/************************************************************************** * * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ /* * Authors: * Keith Whitwell */ #include "pipe/p_util.h" #include "draw/draw_context.h" #include "draw/draw_private.h" #include "draw/draw_vbuf.h" #include "draw/draw_vertex.h" #include "draw/draw_pt.h" #include "draw/draw_vs.h" #include "translate/translate.h" struct fetch_shade_emit; struct fse_shader { struct translate_key key; void (*run_linear)( const struct fetch_shade_emit *fse, unsigned start, unsigned count, char *buffer ); }; /* Prototype fetch, shade, emit-hw-verts all in one go. */ struct fetch_shade_emit { struct draw_pt_front_end base; struct draw_context *draw; struct translate_key key; /* Temporaries: */ const float *constants; unsigned pitch[PIPE_MAX_ATTRIBS]; const ubyte *src[PIPE_MAX_ATTRIBS]; unsigned prim; /* Points to one of the three hardwired example shaders, below: */ struct fse_shader *active; /* Temporary: A list of hard-wired shaders. Of course the plan * would be to generate these for a given (vertex-shader, * translate-key) pair... */ struct fse_shader shader[10]; int nr_shaders; }; /* Not quite passthrough yet -- we're still running the 'shader' here, * inlined into the vertex fetch function. */ static void fetch_xyz_rgb_st( const struct fetch_shade_emit *fse, unsigned start, unsigned count, char *buffer ) { unsigned i; const float *m = fse->constants; const float m0 = m[0], m4 = m[4], m8 = m[8], m12 = m[12]; const float m1 = m[1], m5 = m[5], m9 = m[9], m13 = m[13]; const float m2 = m[2], m6 = m[6], m10 = m[10], m14 = m[14]; const float m3 = m[3], m7 = m[7], m11 = m[11], m15 = m[15]; const ubyte *xyz = fse->src[0] + start * fse->pitch[0]; const ubyte *st = fse->src[2] + start * fse->pitch[2]; float *out = (float *)buffer; assert(fse->pitch[1] == 0); /* loop over vertex attributes (vertex shader inputs) */ for (i = 0; i < count; i++) { { const float *in = (const float *)xyz; const float ix = in[0], iy = in[1], iz = in[2]; out[0] = m0 * ix + m4 * iy + m8 * iz + m12; out[1] = m1 * ix + m5 * iy + m9 * iz + m13; out[2] = m2 * ix + m6 * iy + m10 * iz + m14; out[3] = m3 * ix + m7 * iy + m11 * iz + m15; xyz += fse->pitch[0]; } { out[4] = 1.0f; out[5] = 1.0f; out[6] = 1.0f; out[7] = 1.0f; } { const float *in = (const float *)st; st += fse->pitch[2]; out[8] = in[0]; out[9] = in[1]; out[10] = 0.0f; out[11] = 1.0f; } out += 12; } } static void fetch_xyz_rgb( const struct fetch_shade_emit *fse, unsigned start, unsigned count, char *buffer ) { unsigned i; const float *m = (const float *)fse->constants; const float m0 = m[0], m4 = m[4], m8 = m[8], m12 = m[12]; const float m1 = m[1], m5 = m[5], m9 = m[9], m13 = m[13]; const float m2 = m[2], m6 = m[6], m10 = m[10], m14 = m[14]; const float m3 = m[3], m7 = m[7], m11 = m[11], m15 = m[15]; const ubyte *xyz = fse->src[0] + start * fse->pitch[0]; const ubyte *rgb = fse->src[1] + start * fse->pitch[1]; float *out = (float *)buffer; // debug_printf("rgb %f %f %f\n", rgb[0], rgb[1], rgb[2]); for (i = 0; i < count; i++) { { const float *in = (const float *)xyz; const float ix = in[0], iy = in[1], iz = in[2]; out[0] = m0 * ix + m4 * iy + m8 * iz + m12; out[1] = m1 * ix + m5 * iy + m9 * iz + m13; out[2] = m2 * ix + m6 * iy + m10 * iz + m14; out[3] = m3 * ix + m7 * iy + m11 * iz + m15; xyz += fse->pitch[0]; } { const float *in = (const float *)rgb; out[4] = in[0]; out[5] = in[1]; out[6] = in[2]; out[7] = 1.0f; rgb += fse->pitch[1]; } out += 8; } } static void fetch_xyz_rgb_psiz( const struct fetch_shade_emit *fse, unsigned start, unsigned count, char *buffer ) { unsigned i; const float *m = (const float *)fse->constants; const float m0 = m[0], m4 = m[4], m8 = m[8], m12 = m[12]; const float m1 = m[1], m5 = m[5], m9 = m[9], m13 = m[13]; const float m2 = m[2], m6 = m[6], m10 = m[10], m14 = m[14]; const float m3 = m[3], m7 = m[7], m11 = m[11], m15 = m[15]; const ubyte *xyz = fse->src[0] + start * fse->pitch[0]; const float *rgb = (const float *)(fse->src[1] + start * fse->pitch[1]); const float psiz = 1.0; float *out = (float *)buffer; assert(fse->pitch[1] == 0); for (i = 0; i < count; i++) { { const float *in = (const float *)xyz; const float ix = in[0], iy = in[1], iz = in[2]; out[0] = m0 * ix + m4 * iy + m8 * iz + m12; out[1] = m1 * ix + m5 * iy + m9 * iz + m13; out[2] = m2 * ix + m6 * iy + m10 * iz + m14; out[3] = m3 * ix + m7 * iy + m11 * iz + m15; xyz += fse->pitch[0]; } { out[4] = rgb[0]; out[5] = rgb[1]; out[6] = rgb[2]; out[7] = 1.0f; } { out[8] = psiz; } out += 9; } } static boolean set_prim( struct fetch_shade_emit *fse, unsigned prim, unsigned count ) { struct draw_context *draw = fse->draw; fse->prim = prim; switch (prim) { case PIPE_PRIM_LINE_LOOP: if (count > 1024) return FALSE; draw->render->set_primitive( draw->render, PIPE_PRIM_LINE_STRIP ); break; case PIPE_PRIM_TRIANGLE_FAN: case PIPE_PRIM_POLYGON: if (count > 1024) return FALSE; draw->render->set_primitive( draw->render, prim ); break; case PIPE_PRIM_QUADS: case PIPE_PRIM_QUAD_STRIP: draw->render->set_primitive( draw->render, PIPE_PRIM_TRIANGLES ); break; default: draw->render->set_primitive( draw->render, prim ); break; } return TRUE; } static void fse_prepare( struct draw_pt_front_end *fe, unsigned prim, struct draw_pt_middle_end *unused, unsigned opt ) { struct fetch_shade_emit *fse = (struct fetch_shade_emit *)fe; struct draw_context *draw = fse->draw; unsigned num_vs_inputs = draw->vertex_shader->info.num_inputs; unsigned num_vs_outputs = draw->vertex_shader->info.num_outputs; const struct vertex_info *vinfo; unsigned i; boolean need_psize = 0; if (draw->pt.user.elts) { assert(0); return ; } if (!set_prim(fse, prim, /*count*/1022 )) { assert(0); return ; } /* Must do this after set_primitive() above: */ vinfo = draw->render->get_vertex_info(draw->render); fse->key.nr_elements = MAX2(num_vs_outputs, /* outputs - translate to hw format */ num_vs_inputs); /* inputs - fetch from api format */ fse->key.output_stride = vinfo->size * 4; memset(fse->key.element, 0, fse->key.nr_elements * sizeof(fse->key.element[0])); for (i = 0; i < num_vs_inputs; i++) { const struct pipe_vertex_element *src = &draw->pt.vertex_element[i]; fse->key.element[i].input_format = src->src_format; /* Consider ignoring these at this point, ie make generated * programs independent of this state: */ fse->key.element[i].input_buffer = 0; //src->vertex_buffer_index; fse->key.element[i].input_offset = 0; //src->src_offset; } { unsigned dst_offset = 0; for (i = 0; i < vinfo->num_attribs; i++) { unsigned emit_sz = 0; unsigned output_format = PIPE_FORMAT_NONE; unsigned vs_output = vinfo->src_index[i]; switch (vinfo->emit[i]) { case EMIT_4F: output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; emit_sz = 4 * sizeof(float); break; case EMIT_3F: output_format = PIPE_FORMAT_R32G32B32_FLOAT; emit_sz = 3 * sizeof(float); break; case EMIT_2F: output_format = PIPE_FORMAT_R32G32_FLOAT; emit_sz = 2 * sizeof(float); break; case EMIT_1F: output_format = PIPE_FORMAT_R32_FLOAT; emit_sz = 1 * sizeof(float); break; case EMIT_1F_PSIZE: need_psize = 1; output_format = PIPE_FORMAT_R32_FLOAT; emit_sz = 1 * sizeof(float); vs_output = num_vs_outputs + 1; break; default: assert(0); break; } /* The elements in the key correspond to vertex shader output * numbers, not to positions in the hw vertex description -- * that's handled by the output_offset field. */ fse->key.element[vs_output].output_format = output_format; fse->key.element[vs_output].output_offset = dst_offset; dst_offset += emit_sz; assert(fse->key.output_stride >= dst_offset); } } /* To make psize work, really need to tell the vertex shader to * copy that value from input->output. For 'translate' this was * implicit for all elements. */ #if 0 if (need_psize) { unsigned input = num_vs_inputs + 1; const struct pipe_vertex_element *src = &draw->pt.vertex_element[i]; fse->key.element[i].input_format = PIPE_FORMAT_R32_FLOAT; fse->key.element[i].input_buffer = 0; //nr_buffers + 1; fse->key.element[i].input_offset = 0; fse->key.nr_elements += 1; } #endif fse->constants = draw->pt.user.constants; /* Would normally look up a vertex shader and peruse its list of * varients somehow. We omitted that step and put all the * hardcoded "shaders" into an array. We're just making the * assumption that this happens to be a matching shader... ie * you're running isosurf, aren't you? */ fse->active = NULL; for (i = 0; i < fse->nr_shaders; i++) { if (translate_key_compare( &fse->key, &fse->shader[i].key) == 0) fse->active = &fse->shader[i]; } if (!fse->active) { assert(0); return ; } /* Now set buffer pointers: */ for (i = 0; i < num_vs_inputs; i++) { unsigned buf = draw->pt.vertex_element[i].vertex_buffer_index; fse->src[i] = ((const ubyte *) draw->pt.user.vbuffer[buf] + draw->pt.vertex_buffer[buf].buffer_offset + draw->pt.vertex_element[i].src_offset); fse->pitch[i] = draw->pt.vertex_buffer[buf].pitch; } //return TRUE; } static boolean split_prim_inplace(unsigned prim, unsigned *first, unsigned *incr) { switch (prim) { case PIPE_PRIM_POINTS: *first = 1; *incr = 1; return TRUE; case PIPE_PRIM_LINES: *first = 2; *incr = 2; return TRUE; case PIPE_PRIM_LINE_STRIP: *first = 2; *incr = 1; return TRUE; case PIPE_PRIM_TRIANGLES: *first = 3; *incr = 3; return TRUE; case PIPE_PRIM_TRIANGLE_STRIP: *first = 3; *incr = 1; return TRUE; case PIPE_PRIM_QUADS: *first = 4; *incr = 4; return TRUE; case PIPE_PRIM_QUAD_STRIP: *first = 4; *incr = 2; return TRUE; default: *first = 0; *incr = 1; /* set to one so that count % incr works */ return FALSE; } } #define INDEX(i) (start + (i)) static void fse_render_linear( struct vbuf_render *render, unsigned prim, unsigned start, unsigned length ) { ushort *tmp = NULL; unsigned i, j; switch (prim) { case PIPE_PRIM_LINE_LOOP: tmp = MALLOC( sizeof(ushort) * (length + 1) ); for (i = 0; i < length; i++) tmp[i] = INDEX(i); tmp[length] = 0; render->draw( render, tmp, length+1 ); break; case PIPE_PRIM_QUAD_STRIP: tmp = MALLOC( sizeof(ushort) * (length / 2 * 6) ); for (j = i = 0; i + 3 < length; i += 2, j += 6) { tmp[j+0] = INDEX(i+0); tmp[j+1] = INDEX(i+1); tmp[j+2] = INDEX(i+3); tmp[j+3] = INDEX(i+2); tmp[j+4] = INDEX(i+0); tmp[j+5] = INDEX(i+3); } if (j) render->draw( render, tmp, j ); break; case PIPE_PRIM_QUADS: tmp = MALLOC( sizeof(int) * (length / 4 * 6) ); for (j = i = 0; i + 3 < length; i += 4, j += 6) { tmp[j+0] = INDEX(i+0); tmp[j+1] = INDEX(i+1); tmp[j+2] = INDEX(i+3); tmp[j+3] = INDEX(i+1); tmp[j+4] = INDEX(i+2); tmp[j+5] = INDEX(i+3); } if (j) render->draw( render, tmp, j ); break; default: render->draw_arrays( render, start, length ); break; } if (tmp) FREE(tmp); } static boolean do_draw( struct fetch_shade_emit *fse, unsigned start, unsigned count ) { struct draw_context *draw = fse->draw; char *hw_verts = draw->render->allocate_vertices( draw->render, (ushort)fse->key.output_stride, (ushort)count ); if (!hw_verts) return FALSE; /* Single routine to fetch vertices, run shader and emit HW verts. * Clipping and viewport transformation are done on hardware. */ fse->active->run_linear( fse, start, count, hw_verts ); /* Draw arrays path to avoid re-emitting index list again and * again. */ fse_render_linear( draw->render, fse->prim, 0, count ); draw->render->release_vertices( draw->render, hw_verts, fse->key.output_stride, count ); return TRUE; } static void fse_run(struct draw_pt_front_end *fe, pt_elt_func elt_func, const void *elt_ptr, unsigned count) { struct fetch_shade_emit *fse = (struct fetch_shade_emit *)fe; unsigned i = 0; unsigned first, incr; unsigned start = elt_func(elt_ptr, 0); //debug_printf("%s prim %d start %d count %d\n", __FUNCTION__, prim, start, count); split_prim_inplace(fse->prim, &first, &incr); count -= (count - first) % incr; while (i + first <= count) { int nr = MIN2( count - i, 1024 ); /* snap to prim boundary */ nr -= (nr - first) % incr; if (!do_draw( fse, start + i, nr )) { assert(0); return ; } /* increment allowing for repeated vertices */ i += nr - (first - incr); } //return TRUE; } static void fse_finish( struct draw_pt_front_end *frontend ) { } static void fse_destroy( struct draw_pt_front_end *frontend ) { FREE(frontend); } struct draw_pt_front_end *draw_pt_fetch_shade_emit( struct draw_context *draw ) { struct fetch_shade_emit *fse = CALLOC_STRUCT(fetch_shade_emit); if (!fse) return NULL; fse->base.prepare = fse_prepare; fse->base.run = fse_run; fse->base.finish = fse_finish; fse->base.destroy = fse_destroy; fse->draw = draw; fse->shader[0].run_linear = fetch_xyz_rgb_st; fse->shader[0].key.nr_elements = 3; fse->shader[0].key.output_stride = 12 * sizeof(float); fse->shader[0].key.element[0].input_format = PIPE_FORMAT_R32G32B32_FLOAT; fse->shader[0].key.element[0].input_buffer = 0; fse->shader[0].key.element[0].input_offset = 0; fse->shader[0].key.element[0].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[0].key.element[0].output_offset = 0; fse->shader[0].key.element[1].input_format = PIPE_FORMAT_R32G32B32_FLOAT; fse->shader[0].key.element[1].input_buffer = 0; fse->shader[0].key.element[1].input_offset = 0; fse->shader[0].key.element[1].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[0].key.element[1].output_offset = 16; fse->shader[0].key.element[1].input_format = PIPE_FORMAT_R32G32_FLOAT; fse->shader[0].key.element[1].input_buffer = 0; fse->shader[0].key.element[1].input_offset = 0; fse->shader[0].key.element[1].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[0].key.element[1].output_offset = 32; fse->shader[1].run_linear = fetch_xyz_rgb; fse->shader[1].key.nr_elements = 2; fse->shader[1].key.output_stride = 8 * sizeof(float); fse->shader[1].key.element[0].input_format = PIPE_FORMAT_R32G32B32_FLOAT; fse->shader[1].key.element[0].input_buffer = 0; fse->shader[1].key.element[0].input_offset = 0; fse->shader[1].key.element[0].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[1].key.element[0].output_offset = 0; fse->shader[1].key.element[1].input_format = PIPE_FORMAT_R32G32B32_FLOAT; fse->shader[1].key.element[1].input_buffer = 0; fse->shader[1].key.element[1].input_offset = 0; fse->shader[1].key.element[1].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[1].key.element[1].output_offset = 16; fse->shader[2].run_linear = fetch_xyz_rgb_psiz; fse->shader[2].key.nr_elements = 3; fse->shader[2].key.output_stride = 9 * sizeof(float); fse->shader[2].key.element[0].input_format = PIPE_FORMAT_R32G32B32_FLOAT; fse->shader[2].key.element[0].input_buffer = 0; fse->shader[2].key.element[0].input_offset = 0; fse->shader[2].key.element[0].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[2].key.element[0].output_offset = 0; fse->shader[2].key.element[1].input_format = PIPE_FORMAT_R32G32B32_FLOAT; fse->shader[2].key.element[1].input_buffer = 0; fse->shader[2].key.element[1].input_offset = 0; fse->shader[2].key.element[1].output_format = PIPE_FORMAT_R32G32B32A32_FLOAT; fse->shader[2].key.element[1].output_offset = 16; /* psize is special * -- effectively add it here as another input!?! * -- who knows how to add it as a buffer? */ fse->shader[2].key.element[2].input_format = PIPE_FORMAT_R32_FLOAT; fse->shader[2].key.element[2].input_buffer = 0; fse->shader[2].key.element[2].input_offset = 0; fse->shader[2].key.element[2].output_format = PIPE_FORMAT_R32_FLOAT; fse->shader[2].key.element[2].output_offset = 32; fse->nr_shaders = 3; return &fse->base; }