/** * Return a vertex program which implements the current fixed-function * transform/lighting/texgen operations. */ struct gl_vertex_program * _mesa_get_fixed_func_vertex_program(struct gl_context *ctx) { struct gl_vertex_program *prog; struct state_key key; /* Grab all the relevent state and put it in a single structure: */ make_state_key(ctx, &key); /* Look for an already-prepared program for this state: */ prog = gl_vertex_program( _mesa_search_program_cache(ctx->VertexProgram.Cache, &key, sizeof(key))); if (!prog) { /* OK, we'll have to build a new one */ if (0) printf("Build new TNL program\n"); prog = gl_vertex_program(ctx->Driver.NewProgram(ctx, GL_VERTEX_PROGRAM_ARB, 0)); if (!prog) return NULL; create_new_program( &key, prog, ctx->ShaderCompilerOptions[MESA_SHADER_VERTEX].PreferDP4, ctx->Const.VertexProgram.MaxTemps ); #if 0 if (ctx->Driver.ProgramStringNotify) ctx->Driver.ProgramStringNotify( ctx, GL_VERTEX_PROGRAM_ARB, &prog->Base ); #endif _mesa_program_cache_insert(ctx, ctx->VertexProgram.Cache, &key, sizeof(key), &prog->Base); } return prog; }
/** * Execute a vertex state program. * \note Called from the GL API dispatcher. */ void GLAPIENTRY _mesa_ExecuteProgramNV(GLenum target, GLuint id, const GLfloat *params) { struct gl_vertex_program *vprog; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); if (target != GL_VERTEX_STATE_PROGRAM_NV) { _mesa_error(ctx, GL_INVALID_ENUM, "glExecuteProgramNV"); return; } FLUSH_VERTICES(ctx, _NEW_PROGRAM); vprog = gl_vertex_program(_mesa_lookup_program(ctx, id)); if (!vprog || vprog->Base.Target != GL_VERTEX_STATE_PROGRAM_NV) { _mesa_error(ctx, GL_INVALID_OPERATION, "glExecuteProgramNV"); return; } _mesa_problem(ctx, "glExecuteProgramNV() not supported"); }
/** * Update the ctx->Vertex/Geometry/FragmentProgram._Current pointers to point * to the current/active programs. Then call ctx->Driver.BindProgram() to * tell the driver which programs to use. * * Programs may come from 3 sources: GLSL shaders, ARB/NV_vertex/fragment * programs or programs derived from fixed-function state. * * This function needs to be called after texture state validation in case * we're generating a fragment program from fixed-function texture state. * * \return bitfield which will indicate _NEW_PROGRAM state if a new vertex * or fragment program is being used. */ static GLbitfield update_program(struct gl_context *ctx) { const struct gl_shader_program *vsProg = ctx->Shader.CurrentVertexProgram; const struct gl_shader_program *gsProg = ctx->Shader.CurrentGeometryProgram; struct gl_shader_program *fsProg = ctx->Shader.CurrentFragmentProgram; const struct gl_vertex_program *prevVP = ctx->VertexProgram._Current; const struct gl_fragment_program *prevFP = ctx->FragmentProgram._Current; const struct gl_geometry_program *prevGP = ctx->GeometryProgram._Current; GLbitfield new_state = 0x0; /* * Set the ctx->VertexProgram._Current and ctx->FragmentProgram._Current * pointers to the programs that should be used for rendering. If either * is NULL, use fixed-function code paths. * * These programs may come from several sources. The priority is as * follows: * 1. OpenGL 2.0/ARB vertex/fragment shaders * 2. ARB/NV vertex/fragment programs * 3. Programs derived from fixed-function state. * * Note: it's possible for a vertex shader to get used with a fragment * program (and vice versa) here, but in practice that shouldn't ever * come up, or matter. */ if (fsProg && fsProg->LinkStatus && fsProg->_LinkedShaders[MESA_SHADER_FRAGMENT]) { /* Use GLSL fragment shader */ _mesa_reference_shader_program(ctx, &ctx->Shader._CurrentFragmentProgram, fsProg); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._Current, gl_fragment_program(fsProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program)); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._TexEnvProgram, NULL); } else if (ctx->FragmentProgram._Enabled) { /* Use user-defined fragment program */ _mesa_reference_shader_program(ctx, &ctx->Shader._CurrentFragmentProgram, NULL); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._Current, ctx->FragmentProgram.Current); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._TexEnvProgram, NULL); } else if (ctx->FragmentProgram._MaintainTexEnvProgram) { /* Use fragment program generated from fixed-function state */ struct gl_shader_program *f = _mesa_get_fixed_func_fragment_program(ctx); _mesa_reference_shader_program(ctx, &ctx->Shader._CurrentFragmentProgram, f); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._Current, gl_fragment_program(f->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program)); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._TexEnvProgram, gl_fragment_program(f->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program)); } else { /* No fragment program */ _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._Current, NULL); _mesa_reference_fragprog(ctx, &ctx->FragmentProgram._TexEnvProgram, NULL); } if (gsProg && gsProg->LinkStatus && gsProg->_LinkedShaders[MESA_SHADER_GEOMETRY]) { /* Use GLSL geometry shader */ _mesa_reference_geomprog(ctx, &ctx->GeometryProgram._Current, gl_geometry_program(gsProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->Program)); } else { /* No geometry program */ _mesa_reference_geomprog(ctx, &ctx->GeometryProgram._Current, NULL); } /* Examine vertex program after fragment program as * _mesa_get_fixed_func_vertex_program() needs to know active * fragprog inputs. */ if (vsProg && vsProg->LinkStatus && vsProg->_LinkedShaders[MESA_SHADER_VERTEX]) { /* Use GLSL vertex shader */ _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current, gl_vertex_program(vsProg->_LinkedShaders[MESA_SHADER_VERTEX]->Program)); } else if (ctx->VertexProgram._Enabled) { /* Use user-defined vertex program */ _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current, ctx->VertexProgram.Current); } else if (ctx->VertexProgram._MaintainTnlProgram) { /* Use vertex program generated from fixed-function state */ _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current, _mesa_get_fixed_func_vertex_program(ctx)); _mesa_reference_vertprog(ctx, &ctx->VertexProgram._TnlProgram, ctx->VertexProgram._Current); } else { /* no vertex program */ _mesa_reference_vertprog(ctx, &ctx->VertexProgram._Current, NULL); } /* Let the driver know what's happening: */ if (ctx->FragmentProgram._Current != prevFP) { new_state |= _NEW_PROGRAM; if (ctx->Driver.BindProgram) { ctx->Driver.BindProgram(ctx, GL_FRAGMENT_PROGRAM_ARB, (struct gl_program *) ctx->FragmentProgram._Current); } } if (ctx->GeometryProgram._Current != prevGP) { new_state |= _NEW_PROGRAM; if (ctx->Driver.BindProgram) { ctx->Driver.BindProgram(ctx, MESA_GEOMETRY_PROGRAM, (struct gl_program *) ctx->GeometryProgram._Current); } } if (ctx->VertexProgram._Current != prevVP) { new_state |= _NEW_PROGRAM; if (ctx->Driver.BindProgram) { ctx->Driver.BindProgram(ctx, GL_VERTEX_PROGRAM_ARB, (struct gl_program *) ctx->VertexProgram._Current); } } return new_state; }
/** * Load/parse/compile a program. * \note Called from the GL API dispatcher. */ void GLAPIENTRY _mesa_LoadProgramNV(GLenum target, GLuint id, GLsizei len, const GLubyte *program) { struct gl_program *prog; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); if (!ctx->Extensions.NV_vertex_program && !ctx->Extensions.NV_fragment_program) { _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV()"); return; } if (id == 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glLoadProgramNV(id)"); return; } if (len < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glLoadProgramNV(len)"); return; } FLUSH_VERTICES(ctx, _NEW_PROGRAM); prog = _mesa_lookup_program(ctx, id); if (prog && prog->Target != 0 && prog->Target != target) { _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(target)"); return; } if ((target == GL_VERTEX_PROGRAM_NV || target == GL_VERTEX_STATE_PROGRAM_NV) && ctx->Extensions.NV_vertex_program) { struct gl_vertex_program *vprog = gl_vertex_program(prog); if (!vprog || prog == &_mesa_DummyProgram) { vprog = gl_vertex_program(ctx->Driver.NewProgram(ctx, target, id)); if (!vprog) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV"); return; } _mesa_HashInsert(ctx->Shared->Programs, id, vprog); } if (ctx->Extensions.ARB_vertex_program && (strncmp((char *) program, "!!ARB", 5) == 0)) { _mesa_parse_arb_vertex_program(ctx, target, program, len, vprog); } else { _mesa_parse_nv_vertex_program(ctx, target, program, len, vprog); } } else if (target == GL_FRAGMENT_PROGRAM_NV && ctx->Extensions.NV_fragment_program) { struct gl_fragment_program *fprog = gl_fragment_program(prog); if (!fprog || prog == &_mesa_DummyProgram) { fprog = gl_fragment_program(ctx->Driver.NewProgram(ctx, target, id)); if (!fprog) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV"); return; } _mesa_HashInsert(ctx->Shared->Programs, id, fprog); } _mesa_parse_nv_fragment_program(ctx, target, program, len, fprog); } else if (target == GL_FRAGMENT_PROGRAM_ARB && ctx->Extensions.ARB_fragment_program) { struct gl_fragment_program *fprog = gl_fragment_program(prog); if (!fprog || prog == &_mesa_DummyProgram) { fprog = gl_fragment_program(ctx->Driver.NewProgram(ctx, target, id)); if (!fprog) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV"); return; } _mesa_HashInsert(ctx->Shared->Programs, id, fprog); } _mesa_parse_arb_fragment_program(ctx, target, program, len, fprog); } else { _mesa_error(ctx, GL_INVALID_ENUM, "glLoadProgramNV(target)"); } }
/** * Return a copy of a program. * XXX Problem here if the program object is actually OO-derivation * made by a device driver. */ struct gl_program * _mesa_clone_program(struct gl_context *ctx, const struct gl_program *prog) { struct gl_program *clone; clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id); if (!clone) return NULL; assert(clone->Target == prog->Target); assert(clone->RefCount == 1); clone->String = (GLubyte *) _mesa_strdup((char *) prog->String); clone->Format = prog->Format; clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions); if (!clone->Instructions) { _mesa_reference_program(ctx, &clone, NULL); return NULL; } _mesa_copy_instructions(clone->Instructions, prog->Instructions, prog->NumInstructions); clone->InputsRead = prog->InputsRead; clone->OutputsWritten = prog->OutputsWritten; clone->SamplersUsed = prog->SamplersUsed; clone->ShadowSamplers = prog->ShadowSamplers; memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed)); if (prog->Parameters) clone->Parameters = _mesa_clone_parameter_list(prog->Parameters); memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); clone->IndirectRegisterFiles = prog->IndirectRegisterFiles; clone->NumInstructions = prog->NumInstructions; clone->NumTemporaries = prog->NumTemporaries; clone->NumParameters = prog->NumParameters; clone->NumAttributes = prog->NumAttributes; clone->NumAddressRegs = prog->NumAddressRegs; clone->NumNativeInstructions = prog->NumNativeInstructions; clone->NumNativeTemporaries = prog->NumNativeTemporaries; clone->NumNativeParameters = prog->NumNativeParameters; clone->NumNativeAttributes = prog->NumNativeAttributes; clone->NumNativeAddressRegs = prog->NumNativeAddressRegs; clone->NumAluInstructions = prog->NumAluInstructions; clone->NumTexInstructions = prog->NumTexInstructions; clone->NumTexIndirections = prog->NumTexIndirections; clone->NumNativeAluInstructions = prog->NumNativeAluInstructions; clone->NumNativeTexInstructions = prog->NumNativeTexInstructions; clone->NumNativeTexIndirections = prog->NumNativeTexIndirections; switch (prog->Target) { case GL_VERTEX_PROGRAM_ARB: { const struct gl_vertex_program *vp = gl_vertex_program_const(prog); struct gl_vertex_program *vpc = gl_vertex_program(clone); vpc->IsPositionInvariant = vp->IsPositionInvariant; vpc->IsNVProgram = vp->IsNVProgram; } break; case GL_FRAGMENT_PROGRAM_ARB: { const struct gl_fragment_program *fp = gl_fragment_program_const(prog); struct gl_fragment_program *fpc = gl_fragment_program(clone); fpc->UsesKill = fp->UsesKill; fpc->UsesDFdy = fp->UsesDFdy; fpc->OriginUpperLeft = fp->OriginUpperLeft; fpc->PixelCenterInteger = fp->PixelCenterInteger; } break; case MESA_GEOMETRY_PROGRAM: { const struct gl_geometry_program *gp = gl_geometry_program_const(prog); struct gl_geometry_program *gpc = gl_geometry_program(clone); gpc->VerticesOut = gp->VerticesOut; gpc->InputType = gp->InputType; gpc->OutputType = gp->OutputType; } break; default: _mesa_problem(NULL, "Unexpected target in _mesa_clone_program"); } return clone; }
/** * Bind a program (make it current) * \note Called from the GL API dispatcher by both glBindProgramNV * and glBindProgramARB. */ void GLAPIENTRY _mesa_BindProgramARB(GLenum target, GLuint id) { struct gl_program *curProg, *newProg; GET_CURRENT_CONTEXT(ctx); /* Error-check target and get curProg */ if (target == GL_VERTEX_PROGRAM_ARB && ctx->Extensions.ARB_vertex_program) { curProg = &ctx->VertexProgram.Current->Base; } else if (target == GL_FRAGMENT_PROGRAM_ARB && ctx->Extensions.ARB_fragment_program) { curProg = &ctx->FragmentProgram.Current->Base; } else { _mesa_error(ctx, GL_INVALID_ENUM, "glBindProgramARB(target)"); return; } /* * Get pointer to new program to bind. * NOTE: binding to a non-existant program is not an error. * That's supposed to be caught in glBegin. */ if (id == 0) { /* Bind a default program */ newProg = NULL; if (target == GL_VERTEX_PROGRAM_ARB) newProg = &ctx->Shared->DefaultVertexProgram->Base; else newProg = &ctx->Shared->DefaultFragmentProgram->Base; } else { /* Bind a user program */ newProg = _mesa_lookup_program(ctx, id); if (!newProg || newProg == &_mesa_DummyProgram) { /* allocate a new program now */ newProg = ctx->Driver.NewProgram(ctx, target, id); if (!newProg) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramARB"); return; } _mesa_HashInsert(ctx->Shared->Programs, id, newProg); } else if (newProg->Target != target) { _mesa_error(ctx, GL_INVALID_OPERATION, "glBindProgramARB(target mismatch)"); return; } } /** All error checking is complete now **/ if (curProg->Id == id) { /* binding same program - no change */ return; } /* signal new program (and its new constants) */ FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS); /* bind newProg */ if (target == GL_VERTEX_PROGRAM_ARB) { _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, gl_vertex_program(newProg)); } else if (target == GL_FRAGMENT_PROGRAM_ARB) { _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, gl_fragment_program(newProg)); } /* Never null pointers */ assert(ctx->VertexProgram.Current); assert(ctx->FragmentProgram.Current); if (ctx->Driver.BindProgram) ctx->Driver.BindProgram(ctx, target, newProg); }