4827c8c4cdecfb3ea559880eba8d64d0fc7fd18e
[mesa.git] / src / mesa / shader / slang / slang_link2.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.6
4 *
5 * Copyright (C) 2006 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 /**
26 * \file slang_link.c
27 * slang linker
28 * \author Michal Krol
29 */
30
31 #include "imports.h"
32 #include "context.h"
33 #include "hash.h"
34 #include "macros.h"
35 #include "program.h"
36 #include "prog_instruction.h"
37 #include "prog_parameter.h"
38 #include "prog_print.h"
39 #include "shaderobjects.h"
40 #include "slang_link.h"
41
42
43
44
45 static GLboolean
46 link_varying_vars(struct gl_linked_program *linked, struct gl_program *prog)
47 {
48 GLuint *map, i, firstVarying, newFile;
49 GLbitfield varsWritten, varsRead;
50
51 map = (GLuint *) malloc(prog->Varying->NumParameters * sizeof(GLuint));
52 if (!map)
53 return GL_FALSE;
54
55 for (i = 0; i < prog->Varying->NumParameters; i++) {
56 /* see if this varying is in the linked varying list */
57 const struct gl_program_parameter *var
58 = prog->Varying->Parameters + i;
59
60 GLint j = _mesa_lookup_parameter_index(linked->Varying, -1, var->Name);
61 if (j >= 0) {
62 /* already in list, check size */
63 if (var->Size != linked->Varying->Parameters[j].Size) {
64 /* error */
65 return GL_FALSE;
66 }
67 }
68 else {
69 /* not already in linked list */
70 j = _mesa_add_varying(linked->Varying, var->Name, var->Size);
71 }
72 ASSERT(j >= 0);
73
74 map[i] = j;
75 }
76
77
78 /* Varying variables are treated like other vertex program outputs
79 * (and like other fragment program inputs). The position of the
80 * first varying differs for vertex/fragment programs...
81 * Also, replace File=PROGRAM_VARYING with File=PROGRAM_INPUT/OUTPUT.
82 */
83 if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
84 firstVarying = VERT_RESULT_VAR0;
85 newFile = PROGRAM_OUTPUT;
86 }
87 else {
88 assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
89 firstVarying = FRAG_ATTRIB_VAR0;
90 newFile = PROGRAM_INPUT;
91 }
92
93 /* keep track of which varying vars we read and write */
94 varsWritten = varsRead = 0x0;
95
96 /* OK, now scan the program/shader instructions looking for varying vars,
97 * replacing the old index with the new index.
98 */
99 for (i = 0; i < prog->NumInstructions; i++) {
100 struct prog_instruction *inst = prog->Instructions + i;
101 GLuint j;
102
103 if (inst->DstReg.File == PROGRAM_VARYING) {
104 inst->DstReg.File = newFile;
105 inst->DstReg.Index = map[ inst->DstReg.Index ] + firstVarying;
106 varsWritten |= (1 << inst->DstReg.Index);
107 }
108
109 for (j = 0; j < 3; j++) {
110 if (inst->SrcReg[j].File == PROGRAM_VARYING) {
111 inst->SrcReg[j].File = newFile;
112 inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ] + firstVarying;
113 varsRead |= (1 << inst->SrcReg[j].Index);
114 }
115 }
116 /* XXX update program OutputsWritten, InputsRead */
117 }
118
119 if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
120 prog->OutputsWritten |= varsWritten;
121 }
122 else {
123 assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
124 prog->InputsRead |= varsRead;
125 }
126
127
128 free(map);
129
130 return GL_TRUE;
131 }
132
133
134 static GLboolean
135 is_uniform(enum register_file file)
136 {
137 return (file == PROGRAM_ENV_PARAM ||
138 file == PROGRAM_STATE_VAR ||
139 file == PROGRAM_NAMED_PARAM ||
140 file == PROGRAM_CONSTANT ||
141 file == PROGRAM_UNIFORM);
142 }
143
144
145 static GLboolean
146 link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
147 {
148 GLuint *map, i;
149
150 map = (GLuint *) malloc(prog->Parameters->NumParameters * sizeof(GLuint));
151 if (!map)
152 return GL_FALSE;
153
154 for (i = 0; i < prog->Parameters->NumParameters; i++) {
155 /* see if this uniform is in the linked uniform list */
156 const struct gl_program_parameter *p = prog->Parameters->Parameters + i;
157 const GLfloat *pVals = prog->Parameters->ParameterValues[i];
158 GLint j;
159
160 /* sanity check */
161 assert(is_uniform(p->Type));
162
163 if (p->Name) {
164 j = _mesa_lookup_parameter_index(linked->Uniforms, -1, p->Name);
165 }
166 else {
167 GLuint swizzle;
168 ASSERT(p->Type == PROGRAM_CONSTANT);
169 if (_mesa_lookup_parameter_constant(linked->Uniforms, pVals,
170 p->Size, &j, &swizzle)) {
171 assert(j >= 0);
172 }
173 else {
174 j = -1;
175 }
176 }
177
178 if (j >= 0) {
179 /* already in list, check size XXX check this */
180 assert(p->Size == linked->Uniforms->Parameters[j].Size);
181 }
182 else {
183 /* not already in linked list */
184 switch (p->Type) {
185 case PROGRAM_ENV_PARAM:
186 j = _mesa_add_named_parameter(linked->Uniforms, p->Name, pVals);
187 case PROGRAM_CONSTANT:
188 j = _mesa_add_named_constant(linked->Uniforms, p->Name, pVals, p->Size);
189 break;
190 case PROGRAM_STATE_VAR:
191 j = _mesa_add_state_reference(linked->Uniforms, (const GLint *) p->StateIndexes);
192 break;
193 case PROGRAM_UNIFORM:
194 j = _mesa_add_uniform(linked->Uniforms, p->Name, p->Size);
195 break;
196 default:
197 abort();
198 }
199
200 }
201 ASSERT(j >= 0);
202
203 map[i] = j;
204 }
205
206
207 /* OK, now scan the program/shader instructions looking for varying vars,
208 * replacing the old index with the new index.
209 */
210 for (i = 0; i < prog->NumInstructions; i++) {
211 struct prog_instruction *inst = prog->Instructions + i;
212 GLuint j;
213
214 if (is_uniform(inst->DstReg.File)) {
215 inst->DstReg.Index = map[ inst->DstReg.Index ];
216 }
217
218 for (j = 0; j < 3; j++) {
219 if (is_uniform(inst->SrcReg[j].File)) {
220 inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ];
221 }
222 }
223 /* XXX update program OutputsWritten, InputsRead */
224 }
225
226 free(map);
227
228 return GL_TRUE;
229 }
230
231
232 static void
233 free_linked_program_data(GLcontext *ctx, struct gl_linked_program *linked)
234 {
235 if (linked->VertexProgram) {
236 if (linked->VertexProgram->Base.Parameters == linked->Uniforms) {
237 /* to prevent a double-free in the next call */
238 linked->VertexProgram->Base.Parameters = NULL;
239 }
240 _mesa_delete_program(ctx, &linked->VertexProgram->Base);
241 linked->VertexProgram = NULL;
242 }
243
244 if (linked->FragmentProgram) {
245 if (linked->FragmentProgram->Base.Parameters == linked->Uniforms) {
246 /* to prevent a double-free in the next call */
247 linked->FragmentProgram->Base.Parameters = NULL;
248 }
249 _mesa_delete_program(ctx, &linked->FragmentProgram->Base);
250 linked->FragmentProgram = NULL;
251 }
252
253
254 if (linked->Uniforms) {
255 _mesa_free_parameter_list(linked->Uniforms);
256 linked->Uniforms = NULL;
257 }
258
259 if (linked->Varying) {
260 _mesa_free_parameter_list(linked->Varying);
261 linked->Varying = NULL;
262 }
263 }
264
265
266 /**
267 * Shader linker. Currently:
268 *
269 * 1. The last attached vertex shader and fragment shader are linked.
270 * 2. Varying vars in the two shaders are combined so their locations
271 * agree between the vertex and fragment stages. They're treated as
272 * vertex program output attribs and as fragment program input attribs.
273 * 3. Uniform vars (including state references, constants, etc) from the
274 * vertex and fragment shaders are merged into one group. Recall that
275 * GLSL uniforms are shared by all linked shaders.
276 * 4. The vertex and fragment programs are cloned and modified to update
277 * src/dst register references so they use the new, linked uniform/
278 * varying storage locations.
279 */
280 void
281 _slang_link2(GLcontext *ctx,
282 GLhandleARB programObj,
283 struct gl_linked_program *linked)
284 {
285 struct gl_vertex_program *vertProg;
286 struct gl_fragment_program *fragProg;
287 GLuint i;
288
289 free_linked_program_data(ctx, linked);
290
291 linked->Uniforms = _mesa_new_parameter_list();
292 linked->Varying = _mesa_new_parameter_list();
293
294 /**
295 * Find attached vertex shader, fragment shader
296 */
297 vertProg = NULL;
298 fragProg = NULL;
299 for (i = 0; i < linked->NumShaders; i++) {
300 if (linked->Shaders[i]->Target == GL_VERTEX_PROGRAM_ARB)
301 vertProg = (struct gl_vertex_program *) linked->Shaders[i];
302 else if (linked->Shaders[i]->Target == GL_FRAGMENT_PROGRAM_ARB)
303 fragProg = (struct gl_fragment_program *) linked->Shaders[i];
304 else
305 _mesa_problem(ctx, "unexpected shader target in slang_link2()");
306 }
307 if (!vertProg || !fragProg) {
308 /* XXX is it legal to have one but not the other?? */
309 /* XXX record error */
310 linked->LinkStatus = GL_FALSE;
311 return;
312 }
313
314 /*
315 * Make copies of the vertex/fragment programs now since we'll be
316 * changing src/dst registers after merging the uniforms and varying vars.
317 */
318 linked->VertexProgram = (struct gl_vertex_program *)
319 _mesa_clone_program(ctx, &vertProg->Base);
320 linked->FragmentProgram = (struct gl_fragment_program *)
321 _mesa_clone_program(ctx, &fragProg->Base);
322
323 #if 1
324 printf("************** orig program\n");
325 _mesa_print_program(&fragProg->Base);
326 _mesa_print_program_parameters(ctx, &fragProg->Base);
327 #endif
328
329 link_varying_vars(linked, &linked->VertexProgram->Base);
330 link_varying_vars(linked, &linked->FragmentProgram->Base);
331
332 link_uniform_vars(linked, &linked->VertexProgram->Base);
333 link_uniform_vars(linked, &linked->FragmentProgram->Base);
334
335 /* The vertex and fragment programs share a common set of uniforms now */
336 _mesa_free_parameter_list(linked->VertexProgram->Base.Parameters);
337 _mesa_free_parameter_list(linked->FragmentProgram->Base.Parameters);
338 linked->VertexProgram->Base.Parameters = linked->Uniforms;
339 linked->FragmentProgram->Base.Parameters = linked->Uniforms;
340
341 #if 1
342 printf("************** linked/cloned\n");
343 _mesa_print_program(&linked->FragmentProgram->Base);
344 _mesa_print_program_parameters(ctx, &linked->FragmentProgram->Base);
345 #endif
346
347 linked->LinkStatus = (linked->VertexProgram && linked->FragmentProgram);
348 }
349