vertex_program struct.
Allow switching between regular and vertex_program implementations
of fixed function TNL with the MESA_TNL_PROG environment var
(previously this required recompilation).
Ensure program compilation only references program data, not the
wider context. This means that compiled programs only need to be
invalidated when the program string changes, not on other state
changes.
clean:
-rm -f */*.o
+ -rm -f */*/*.o
-rm -f depend depend.bak mesa.a
-rm -f drivers/*/*.o
(cd drivers/dri ; $(MAKE) clean)
#if defined(XFree86LOADER) && defined(IN_MODULE)
xf86printf("%s", s);
#else
- printf("%s", s);
+ fprintf(stderr,"%s", s);
#endif
}
GLuint InputsRead; /* Bitmask of which input regs are read */
GLuint OutputsWritten; /* Bitmask of which output regs are written to */
struct program_parameter_list *Parameters; /**< array [NumParameters] */
+ void *TnlData; /* should probably use Base.DriverData */
};
_tnl_save_init( ctx );
_tnl_array_init( ctx );
_tnl_vtx_init( ctx );
- _tnl_install_pipeline( ctx, _tnl_default_pipeline );
+
+ if (ctx->_MaintainTnlProgram)
+ _tnl_install_pipeline( ctx, _tnl_vp_pipeline );
+ else
+ _tnl_install_pipeline( ctx, _tnl_default_pipeline );
/* Initialize the arrayelt helper
*/
_tnl_destroy_pipeline( ctx );
_ae_destroy_context( ctx );
+ _tnl_ProgramCacheDestroy( ctx );
+
FREE(tnl);
ctx->swtnl_context = NULL;
}
};
+
+struct tnl_cache {
+ GLuint hash;
+ void *key;
+ void *data;
+ struct tnl_cache *next;
+};
+
+
struct tnl_device_driver
{
/***
GLvertexformat exec_vtxfmt;
GLvertexformat save_vtxfmt;
+ struct tnl_cache *vp_cache;
+
} TNLcontext;
* (ie const or non-const).
*/
if (check_input_changes( ctx ) || tnl->pipeline.new_state) {
-#if TNL_FIXED_FUNCTION_PROGRAM
- _tnl_UpdateFixedFunctionProgram( ctx );
-#endif
+ if (ctx->_MaintainTnlProgram)
+ _tnl_UpdateFixedFunctionProgram( ctx );
for (i = 0; i < tnl->pipeline.nr_stages ; i++) {
struct tnl_pipeline_stage *s = &tnl->pipeline.stages[i];
* case, if it becomes necessary to do so.
*/
const struct tnl_pipeline_stage *_tnl_default_pipeline[] = {
-#if TNL_FIXED_FUNCTION_PROGRAM
- &_tnl_arb_vertex_program_stage,
-#else
&_tnl_vertex_transform_stage,
&_tnl_normal_transform_stage,
&_tnl_lighting_stage,
&_tnl_texture_transform_stage,
&_tnl_point_attenuation_stage,
#if defined(FEATURE_NV_vertex_program) || defined(FEATURE_ARB_vertex_program)
- &_tnl_vertex_program_stage,
-#endif
+ &_tnl_arb_vertex_program_stage,
+ &_tnl_vertex_program_stage,
#endif
&_tnl_render_stage,
NULL
};
+
+const struct tnl_pipeline_stage *_tnl_vp_pipeline[] = {
+ &_tnl_arb_vertex_program_stage,
+ &_tnl_render_stage,
+ NULL
+};
/* Shorthand to plug in the default pipeline:
*/
extern const struct tnl_pipeline_stage *_tnl_default_pipeline[];
+extern const struct tnl_pipeline_stage *_tnl_vp_pipeline[];
/* Convenience routines provided by t_vb_render.c:
struct compilation {
GLuint reg_active;
union instruction *csr;
- struct vertex_buffer *VB; /* for input sizes! */
};
_mesa_printf("ARG%d", reg - REG_ARG0);
else if (reg >= REG_TMP0 && reg <= REG_TMP11)
_mesa_printf("TMP%d", reg - REG_TMP0);
- else if (reg >= REG_IN0 && reg <= REG_IN15)
+ else if (reg >= REG_IN0 && reg <= REG_IN31)
_mesa_printf("IN%d", reg - REG_IN0);
else if (reg >= REG_OUT0 && reg <= REG_OUT14)
_mesa_printf("OUT%d", reg - REG_OUT0);
}
}
+static void free_tnl_data( struct vertex_program *program )
+{
+ struct tnl_compiled_program *p = program->TnlData;
+ if (p->compiled_func) free((void *)p->compiled_func);
+ free(p);
+ program->TnlData = NULL;
+}
-static void compile_vertex_program( struct arb_vp_machine *m,
- const struct vertex_program *program )
+static void compile_vertex_program( struct vertex_program *program,
+ GLboolean try_codegen )
{
struct compilation cp;
+ struct tnl_compiled_program *p = CALLOC_STRUCT(tnl_compiled_program);
GLuint i;
- /* Initialize cp:
+ _mesa_printf("%s\n", __FUNCTION__);
+
+ if (program->TnlData)
+ free_tnl_data( program );
+
+ program->TnlData = p;
+
+ /* Initialize cp. Note that ctx and VB aren't used in compilation
+ * so we don't have to worry about statechanges:
*/
memset(&cp, 0, sizeof(cp));
- cp.VB = m->VB;
- cp.csr = m->store;
+ cp.csr = p->instructions;
/* Compile instructions:
*/
/* Finish up:
*/
- m->instructions = m->store;
- m->nr_instructions = cp.csr - m->store;
-
+ p->nr_instructions = cp.csr - p->instructions;
/* Print/disassemble:
*/
if (DISASSEM) {
- for (i = 0; i < m->nr_instructions; i++) {
- _tnl_disassem_vba_insn(m->instructions[i]);
+ for (i = 0; i < p->nr_instructions; i++) {
+ _tnl_disassem_vba_insn(p->instructions[i]);
}
_mesa_printf("\n\n");
}
#ifdef USE_SSE_ASM
- /* TODO: check if anything changed...
- */
- if (m->try_codegen)
- _tnl_sse_codegen_vertex_program(m);
+ if (try_codegen)
+ _tnl_sse_codegen_vertex_program(p);
#endif
}
{
GLuint p;
- for (p = 0; p < ctx->Const.MaxClipPlanes; p++)
+ for (p = 0; p < ctx->Const.MaxClipPlanes; p++) {
if (ctx->Transform.ClipPlanesEnabled & (1 << p)) {
GLuint nr, i;
const GLfloat a = ctx->Transform._ClipUserPlane[p][0];
}
}
}
+ }
}
}
-static void call_func( struct arb_vp_machine *m )
+static INLINE void call_func( struct tnl_compiled_program *p,
+ struct arb_vp_machine *m )
{
- m->func(m);
+ p->compiled_func(m);
}
/**
ctx->_TnlProgram);
struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
struct arb_vp_machine *m = ARB_VP_MACHINE(stage);
- GLuint i, j, outputs = program->OutputsWritten;
+ struct tnl_compiled_program *p;
+ GLuint i, j, outputs;
+
+ if (!program || program->IsNVProgram)
+ return GL_TRUE;
if (program->Parameters) {
_mesa_load_state_parameters(ctx, program->Parameters);
}
+ p = (struct tnl_compiled_program *)program->TnlData;
+ assert(p);
/* Initialize regs where necessary:
*/
m->nr_inputs = m->nr_outputs = 0;
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < _TNL_ATTRIB_MAX; i++) {
if (program->InputsRead & (1<<i)) {
GLuint j = m->nr_inputs++;
m->input[j].idx = i;
- m->input[j].data = m->VB->AttribPtr[i]->data;
+ m->input[j].data = (GLfloat *)m->VB->AttribPtr[i]->data;
m->input[j].stride = m->VB->AttribPtr[i]->stride;
m->input[j].size = m->VB->AttribPtr[i]->size;
ASSIGN_4V(m->File[0][REG_IN0 + i], 0, 0, 0, 1);
if (program->OutputsWritten & (1<<i)) {
GLuint j = m->nr_outputs++;
m->output[j].idx = i;
- m->output[j].data = m->attribs[i].data;
+ m->output[j].data = (GLfloat *)m->attribs[i].data;
}
}
STRIDE_F(m->input[j].data, m->input[j].stride);
}
- if (m->func) {
- call_func( m );
+ if (p->compiled_func) {
+ call_func( p, m );
}
else {
- for (j = 0; j < m->nr_instructions; j++) {
- union instruction inst = m->instructions[j];
+ for (j = 0; j < p->nr_instructions; j++) {
+ union instruction inst = p->instructions[j];
opcode_func[inst.alu.opcode]( m, inst );
}
}
VB->ClipPtr = &m->attribs[VERT_RESULT_HPOS];
VB->ClipPtr->count = VB->Count;
+ outputs = program->OutputsWritten;
+
if (outputs & (1<<VERT_RESULT_COL0)) {
VB->ColorPtr[0] = &m->attribs[VERT_RESULT_COL0];
VB->AttribPtr[VERT_ATTRIB_COLOR0] = VB->ColorPtr[0];
struct vertex_program *program =
(ctx->VertexProgram._Enabled ? ctx->VertexProgram.Current : 0);
-#if TNL_FIXED_FUNCTION_PROGRAM
- if (!program) {
+ if (!program && ctx->_MaintainTnlProgram) {
program = ctx->_TnlProgram;
}
-#endif
if (program) {
- compile_vertex_program( m, program );
+ if (!program->TnlData)
+ compile_vertex_program( program, m->try_codegen );
/* Grab the state GL state and put into registers:
*/
if (_mesa_getenv("MESA_EXPERIMENTAL"))
m->try_codegen = 1;
- _mesa_printf("try_codegen %d\n", m->try_codegen);
-
/* Allocate arrays of vertex output values */
for (i = 0; i < VERT_RESULT_MAX; i++) {
_mesa_vector4f_alloc( &m->attribs[i], 0, size, 32 );
_mesa_vector4f_alloc( &m->ndcCoords, 0, size, 32 );
m->clipmask = (GLubyte *) ALIGN_MALLOC(sizeof(GLubyte)*size, 32 );
-
-#if TNL_FIXED_FUNCTION_PROGRAM
- _mesa_allow_light_in_model( ctx, GL_FALSE );
-#endif
-
+ if (ctx->_MaintainTnlProgram)
+ _mesa_allow_light_in_model( ctx, GL_FALSE );
return GL_TRUE;
}
#define REG_OUT0 17
#define REG_OUT14 31
#define REG_IN0 32
-#define REG_IN15 47
-#define REG_ID 48 /* 0,0,0,1 */
-#define REG_ONES 49 /* 1,1,1,1 */
-#define REG_SWZ 50 /* -1,1,0,0 */
-#define REG_NEG 51 /* -1,-1,-1,-1 */
+#define REG_IN31 63
+#define REG_ID 64 /* 0,0,0,1 */
+#define REG_ONES 65 /* 1,1,1,1 */
+#define REG_SWZ 66 /* -1,1,0,0 */
+#define REG_NEG 67 /* -1,-1,-1,-1 */
#define REG_UNDEF 127 /* special case - never used */
#define REG_MAX 128
#define REG_INVALID ~0
GLfloat *data;
};
+
+
/*--------------------------------------------------------------------------- */
/*!
*/
struct arb_vp_machine {
GLfloat (*File[4])[4]; /* All values referencable from the program. */
- GLint AddressReg;
- struct input input[16];
+ struct input input[_TNL_ATTRIB_MAX];
GLuint nr_inputs;
struct output output[15];
GLuint nr_outputs;
- union instruction store[1024];
- union instruction *instructions;
- GLint nr_instructions;
-
GLvector4f attribs[VERT_RESULT_MAX]; /**< result vectors. */
GLvector4f ndcCoords; /**< normalized device coords */
GLubyte *clipmask; /**< clip flags */
GLuint vtx_nr; /**< loop counter */
- void (*func)( struct arb_vp_machine * ); /**< codegen'd program? */
-
struct vertex_buffer *VB;
GLcontext *ctx;
GLboolean try_codegen;
};
+struct tnl_compiled_program {
+ union instruction instructions[1024];
+ GLint nr_instructions;
+ void (*compiled_func)( struct arb_vp_machine * ); /**< codegen'd program */
+};
+
+void _tnl_program_string_change( struct vertex_program * );
+void _tnl_program_destroy( struct vertex_program * );
+
void _tnl_disassem_vba_insn( union instruction op );
+GLboolean _tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p);
+
#endif
struct compilation {
struct x86_function func;
- struct arb_vp_machine *m;
-
+ struct tnl_compiled_program *p;
GLuint insn_counter;
struct {
static GLboolean build_vertex_program( struct compilation *cp )
{
+ struct arb_vp_machine *m = NULL;
GLuint j;
struct x86_reg regEAX = x86_make_reg(file_REG32, reg_AX);
x86_mov(&cp->func, regEAX, x86_fn_arg(&cp->func, 1));
x86_mov(&cp->func, parmECX, regEAX);
- x86_mov(&cp->func, regEAX, x86_make_disp(regEAX, get_offset(cp->m, cp->m->File + FILE_REG)));
- x86_mov(&cp->func, parmECX, x86_make_disp(parmECX, get_offset(cp->m, cp->m->File + FILE_STATE_PARAM)));
+ x86_mov(&cp->func, regEAX, x86_make_disp(regEAX, get_offset(m, m->File + FILE_REG)));
+ x86_mov(&cp->func, parmECX, x86_make_disp(parmECX, get_offset(m, m->File + FILE_STATE_PARAM)));
- for (j = 0; j < cp->m->nr_instructions; j++) {
- union instruction inst = cp->m->instructions[j];
+ for (j = 0; j < cp->p->nr_instructions; j++) {
+ union instruction inst = cp->p->instructions[j];
cp->insn_counter = j+1; /* avoid zero */
if (DISASSEM) {
* struct arb_vertex_machine.
*/
GLboolean
-_tnl_sse_codegen_vertex_program(struct arb_vp_machine *m)
+_tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p)
{
struct compilation cp;
memset(&cp, 0, sizeof(cp));
- cp.m = m;
+ cp.p = p;
cp.have_sse2 = 1;
- if (m->func) {
- free((void *)m->func);
- m->func = NULL;
+ if (p->compiled_func) {
+ free((void *)p->compiled_func);
+ p->compiled_func = NULL;
}
x86_init_func(&cp.func);
+ /* Note ctx state is not referenced in building the function, so it
+ * depends only on the list of instructions:
+ */
if (!build_vertex_program(&cp)) {
x86_release_func( &cp.func );
return GL_FALSE;
}
- m->func = (void (*)(struct arb_vp_machine *))x86_get_func( &cp.func );
+ p->compiled_func = (void (*)(struct arb_vp_machine *))x86_get_func( &cp.func );
return GL_TRUE;
}
#else
GLboolean
-_tnl_sse_codegen_vertex_program( GLcontext *ctx )
+_tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p)
{
/* Dummy version for when USE_SSE_ASM not defined */
return GL_FALSE;
struct vertex_program *program = ctx->VertexProgram.Current;
GLuint i;
- if (!ctx->VertexProgram._Enabled)
+ if (!ctx->VertexProgram._Enabled ||
+ !program->IsNVProgram)
return GL_TRUE;
/* load program parameter registers (they're read-only) */
#include "mtypes.h"
-/* Define to 1 to test fixed-function execution via vertex programs:
- */
-#define TNL_FIXED_FUNCTION_PROGRAM 0
-
extern void _tnl_UpdateFixedFunctionProgram( GLcontext *ctx );
#endif