The new linker actually does a few things right now:

The varying vars used by the vertex and fragment shader are merged so they
agree.
Similarly, uniforms are merged (along with constants, etc).
The vertex/fragment program instructions are then cloned and rewritten
with the newly resolved uniform/varying locations.
This commit is contained in:
Brian
2006-12-14 13:58:32 -07:00
parent b2a3a8554a
commit d4f7e4cc01
+264 -143
View File
@@ -33,127 +33,248 @@
#include "hash.h"
#include "macros.h"
#include "program.h"
#include "program_instruction.h"
#include "shaderobjects.h"
#include "slang_link.h"
#define RELEASE_GENERIC(x)\
(**x)._unknown.Release ((struct gl2_unknown_intf **) (x))
#define RELEASE_CONTAINER(x)\
(**x)._generic._unknown.Release ((struct gl2_unknown_intf **) (x))
#define RELEASE_PROGRAM(x)\
(**x)._container._generic._unknown.Release ((struct gl2_unknown_intf **) (x))
#define RELEASE_SHADER(x)\
(**x)._generic._unknown.Release ((struct gl2_unknown_intf **) (x))
static struct gl2_unknown_intf **
lookup_handle(GLcontext * ctx, GLhandleARB handle, enum gl2_uiid uiid,
const char *function)
static GLboolean
link_varying_vars(struct gl_linked_program *linked, struct gl_program *prog)
{
struct gl2_unknown_intf **unk;
GLuint *map, i, firstVarying, newFile;
GLbitfield varsWritten, varsRead;
/*
* Note: _mesa_HashLookup() requires non-zero input values, so the
* passed-in handle value must be checked beforehand.
*/
if (handle == 0) {
_mesa_error(ctx, GL_INVALID_VALUE, function);
return NULL;
map = (GLuint *) malloc(prog->Varying->NumParameters * sizeof(GLuint));
if (!map)
return GL_FALSE;
for (i = 0; i < prog->Varying->NumParameters; i++) {
/* see if this varying is in the linked varying list */
const struct gl_program_parameter *var
= prog->Varying->Parameters + i;
GLint j = _mesa_lookup_parameter_index(linked->Varying, -1, var->Name);
if (j >= 0) {
/* already in list, check size */
if (var->Size != linked->Varying->Parameters[j].Size) {
/* error */
return GL_FALSE;
}
}
else {
/* not already in linked list */
j = _mesa_add_varying(linked->Varying, var->Name, var->Size);
}
ASSERT(j >= 0);
map[i] = j;
}
_glthread_LOCK_MUTEX(ctx->Shared->Mutex);
unk = (struct gl2_unknown_intf **) _mesa_HashLookup(ctx->Shared->GL2Objects,
handle);
_glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
if (unk == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE, function);
/* Varying variables are treated like other vertex program outputs
* (and like other fragment program inputs). The position of the
* first varying differs for vertex/fragment programs...
* Also, replace File=PROGRAM_VARYING with File=PROGRAM_INPUT/OUTPUT.
*/
if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
firstVarying = VERT_RESULT_VAR0;
newFile = PROGRAM_OUTPUT;
}
else {
unk = (**unk).QueryInterface(unk, uiid);
if (unk == NULL)
_mesa_error(ctx, GL_INVALID_OPERATION, function);
assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
firstVarying = FRAG_ATTRIB_VAR0;
newFile = PROGRAM_INPUT;
}
return unk;
/* keep track of which varying vars we read and write */
varsWritten = varsRead = 0x0;
/* OK, now scan the program/shader instructions looking for varying vars,
* replacing the old index with the new index.
*/
for (i = 0; i < prog->NumInstructions; i++) {
struct prog_instruction *inst = prog->Instructions + i;
GLuint j;
if (inst->DstReg.File == PROGRAM_VARYING) {
inst->DstReg.File = newFile;
inst->DstReg.Index = map[ inst->DstReg.Index ] + firstVarying;
varsWritten |= (1 << inst->DstReg.Index);
}
for (j = 0; j < 3; j++) {
if (inst->SrcReg[j].File == PROGRAM_VARYING) {
inst->SrcReg[j].File = newFile;
inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ] + firstVarying;
varsRead |= (1 << inst->DstReg.Index);
}
}
/* XXX update program OutputsWritten, InputsRead */
}
if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
prog->OutputsWritten |= varsWritten;
}
else {
assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
prog->InputsRead |= varsRead;
}
free(map);
return GL_TRUE;
}
#define GET_GENERIC(x, handle, function)\
struct gl2_generic_intf **x = (struct gl2_generic_intf **)\
lookup_handle (ctx, handle, UIID_GENERIC, function);
#define GET_CONTAINER(x, handle, function)\
struct gl2_container_intf **x = (struct gl2_container_intf **)\
lookup_handle (ctx, handle, UIID_CONTAINER, function);
#define GET_PROGRAM(x, handle, function)\
struct gl2_program_intf **x = (struct gl2_program_intf **)\
lookup_handle (ctx, handle, UIID_PROGRAM, function);
#define GET_SHADER(x, handle, function)\
struct gl2_shader_intf **x = (struct gl2_shader_intf **)\
lookup_handle (ctx, handle, UIID_SHADER, function);
static void
prelink(GLhandleARB programObj, struct gl_linked_program *linked)
static GLboolean
is_uniform(enum register_file file)
{
GET_CURRENT_CONTEXT(ctx);
return (file == PROGRAM_ENV_PARAM ||
file == PROGRAM_STATE_VAR ||
file == PROGRAM_NAMED_PARAM ||
file == PROGRAM_CONSTANT ||
file == PROGRAM_UNIFORM);
}
linked->VertexProgram = NULL;
linked->FragmentProgram = NULL;
if (programObj != 0) {
GET_PROGRAM(program, programObj, "glUseProgramObjectARB(program)");
static GLboolean
link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
{
GLuint *map, i;
if (program == NULL)
return;
map = (GLuint *) malloc(prog->Parameters->NumParameters * sizeof(GLuint));
if (!map)
return GL_FALSE;
/* XXX terrible hack to find the real vertex/fragment programs */
{
GLuint handle;
GLsizei cnt, i;
cnt = (**program)._container.GetAttachedCount((struct gl2_container_intf **) (program));
for (i = 0; i < prog->Parameters->NumParameters; i++) {
/* see if this uniform is in the linked uniform list */
const struct gl_program_parameter *p = prog->Parameters->Parameters + i;
const GLfloat *pVals = prog->Parameters->ParameterValues[i];
GLint j;
for (i = 0; i < cnt; i++) {
struct gl2_generic_intf **x
= (**program)._container.GetAttached((struct gl2_container_intf **) program, i);
handle = (**x).GetName(x);
{
struct gl_program *prog;
GET_SHADER(sha, handle, "foo");
if (sha && (*sha)->Program) {
prog = (*sha)->Program;
if (prog->Target == GL_VERTEX_PROGRAM_ARB)
linked->VertexProgram = (struct gl_vertex_program *) prog;
else if (prog->Target == GL_FRAGMENT_PROGRAM_ARB)
linked->FragmentProgram = (struct gl_fragment_program *) prog;
}
}
#if 0
if (linked->VertexProgram)
printf("Found vert prog %p %d\n",
linked->VertexProgram,
linked->VertexProgram->Base.NumInstructions);
if (linked->FragmentProgram)
printf("Found frag prog %p %d\n",
linked->FragmentProgram,
linked->FragmentProgram->Base.NumInstructions);
#endif
RELEASE_GENERIC(x);
/* sanity check */
assert(is_uniform(p->Type));
if (p->Name) {
j = _mesa_lookup_parameter_index(linked->Uniforms, -1, p->Name);
}
else {
GLuint swizzle;
ASSERT(p->Type == PROGRAM_CONSTANT);
if (_mesa_lookup_parameter_constant(linked->Uniforms, pVals,
p->Size, &j, &swizzle)) {
assert(j >= 0);
}
else {
j = -1;
}
}
if (j >= 0) {
/* already in list, check size XXX check this */
assert(p->Size == linked->Uniforms->Parameters[j].Size);
}
else {
/* not already in linked list */
switch (p->Type) {
case PROGRAM_ENV_PARAM:
j = _mesa_add_named_parameter(linked->Uniforms, p->Name, pVals);
case PROGRAM_CONSTANT:
j = _mesa_add_named_constant(linked->Uniforms, p->Name, pVals, p->Size);
break;
case PROGRAM_STATE_VAR:
j = _mesa_add_state_reference(linked->Uniforms, (const GLint *) p->StateIndexes);
break;
case PROGRAM_UNIFORM:
j = _mesa_add_uniform(linked->Uniforms, p->Name, p->Size);
break;
default:
abort();
}
}
ASSERT(j >= 0);
map[i] = j;
}
/* OK, now scan the program/shader instructions looking for varying vars,
* replacing the old index with the new index.
*/
for (i = 0; i < prog->NumInstructions; i++) {
struct prog_instruction *inst = prog->Instructions + i;
GLuint j;
if (is_uniform(inst->DstReg.File)) {
inst->DstReg.Index = map[ inst->DstReg.Index ];
}
for (j = 0; j < 3; j++) {
if (is_uniform(inst->SrcReg[j].File)) {
inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ];
}
}
/* XXX update program OutputsWritten, InputsRead */
}
free(map);
return GL_TRUE;
}
static void
free_linked_program_data(GLcontext *ctx, struct gl_linked_program *linked)
{
if (linked->VertexProgram) {
if (linked->VertexProgram->Base.Parameters == linked->Uniforms) {
/* to prevent a double-free in the next call */
linked->VertexProgram->Base.Parameters = NULL;
}
_mesa_delete_program(ctx, &linked->VertexProgram->Base);
linked->VertexProgram = NULL;
}
if (linked->FragmentProgram) {
if (linked->FragmentProgram->Base.Parameters == linked->Uniforms) {
/* to prevent a double-free in the next call */
linked->FragmentProgram->Base.Parameters = NULL;
}
_mesa_delete_program(ctx, &linked->FragmentProgram->Base);
linked->FragmentProgram = NULL;
}
if (linked->Uniforms) {
_mesa_free_parameter_list(linked->Uniforms);
linked->Uniforms = NULL;
}
if (linked->Varying) {
_mesa_free_parameter_list(linked->Varying);
linked->Varying = NULL;
}
}
/**
* Shader linker. Currently:
*
* 1. The last attached vertex shader and fragment shader are linked.
* 2. Varying vars in the two shaders are combined so their locations
* agree between the vertex and fragment stages. They're treated as
* vertex program output attribs and as fragment program input attribs.
* 3. Uniform vars (including state references, constants, etc) from the
* vertex and fragment shaders are merged into one group. Recall that
* GLSL uniforms are shared by all linked shaders.
* 4. The vertex and fragment programs are cloned and modified to update
* src/dst register references so they use the new, linked uniform/
* varying storage locations.
*/
void
_slang_link2(GLcontext *ctx,
GLhandleARB programObj,
@@ -161,66 +282,66 @@ _slang_link2(GLcontext *ctx,
{
struct gl_vertex_program *vertProg;
struct gl_fragment_program *fragProg;
GLuint i;
prelink(programObj, linked);
free_linked_program_data(ctx, linked);
vertProg = linked->VertexProgram;
fragProg = linked->FragmentProgram;
linked->Uniforms = _mesa_new_parameter_list();
linked->Varying = _mesa_new_parameter_list();
/* free old linked data, if any */
if (linked->NumUniforms > 0) {
GLuint i;
for (i = 0; i < linked->NumUniforms; i++) {
_mesa_free((char *) linked->Uniforms[i].Name);
linked->Uniforms[i].Name = NULL;
linked->Uniforms[i].Value = NULL;
}
linked->NumUniforms = 0;
/**
* Find attached vertex shader, fragment shader
*/
vertProg = NULL;
fragProg = NULL;
for (i = 0; i < linked->NumShaders; i++) {
if (linked->Shaders[i]->Target == GL_VERTEX_PROGRAM_ARB)
vertProg = (struct gl_vertex_program *) linked->Shaders[i];
else if (linked->Shaders[i]->Target == GL_FRAGMENT_PROGRAM_ARB)
fragProg = (struct gl_fragment_program *) linked->Shaders[i];
else
_mesa_problem(ctx, "unexpected shader target in slang_link2()");
}
if (!vertProg || !fragProg) {
/* XXX is it legal to have one but not the other?? */
/* XXX record error */
linked->LinkStatus = GL_FALSE;
return;
}
/*
* Find uniforms.
* XXX what about dups?
* Make copies of the vertex/fragment programs now since we'll be
* changing src/dst registers after merging the uniforms and varying vars.
*/
if (vertProg) {
GLuint i;
for (i = 0; i < vertProg->Base.Parameters->NumParameters; i++) {
struct gl_program_parameter *p
= vertProg->Base.Parameters->Parameters + i;
if (p->Name) {
struct gl_uniform *u = linked->Uniforms + linked->NumUniforms;
u->Name = _mesa_strdup(p->Name);
u->Value = &vertProg->Base.Parameters->ParameterValues[i][0];
linked->NumUniforms++;
assert(linked->NumUniforms < MAX_UNIFORMS);
}
}
}
if (fragProg) {
GLuint i;
for (i = 0; i < fragProg->Base.Parameters->NumParameters; i++) {
struct gl_program_parameter *p
= fragProg->Base.Parameters->Parameters + i;
if (p->Name) {
struct gl_uniform *u = linked->Uniforms + linked->NumUniforms;
u->Name = _mesa_strdup(p->Name);
u->Value = &fragProg->Base.Parameters->ParameterValues[i][0];
linked->NumUniforms++;
assert(linked->NumUniforms < MAX_UNIFORMS);
}
}
}
linked->VertexProgram = (struct gl_vertex_program *)
_mesa_clone_program(ctx, &vertProg->Base);
linked->FragmentProgram = (struct gl_fragment_program *)
_mesa_clone_program(ctx, &fragProg->Base);
/* For varying:
* scan both programs for varyings, rewrite programs so they agree
* on locations of varyings.
*/
#if 1
printf("************** orig program\n");
_mesa_print_program(&fragProg->Base);
_mesa_print_program_parameters(ctx, &fragProg->Base);
#endif
/**
* Linking should _copy_ the vertex and fragment shader code,
* rewriting varying references as we go along...
*/
link_varying_vars(linked, &linked->VertexProgram->Base);
link_varying_vars(linked, &linked->FragmentProgram->Base);
linked->LinkStatus = (vertProg && fragProg);
link_uniform_vars(linked, &linked->VertexProgram->Base);
link_uniform_vars(linked, &linked->FragmentProgram->Base);
/* The vertex and fragment programs share a common set of uniforms now */
_mesa_free_parameter_list(linked->VertexProgram->Base.Parameters);
_mesa_free_parameter_list(linked->FragmentProgram->Base.Parameters);
linked->VertexProgram->Base.Parameters = linked->Uniforms;
linked->FragmentProgram->Base.Parameters = linked->Uniforms;
#if 1
printf("************** linked/cloned\n");
_mesa_print_program(&linked->FragmentProgram->Base);
_mesa_print_program_parameters(ctx, &linked->FragmentProgram->Base);
#endif
linked->LinkStatus = (linked->VertexProgram && linked->FragmentProgram);
}