mesa: Use typed foreach_in_list instead of foreach_list.
[mesa.git] / src / mesa / main / shader_query.cpp
1 /*
2 * Copyright © 2011 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * \file shader_query.cpp
26 * C-to-C++ bridge functions to query GLSL shader data
27 *
28 * \author Ian Romanick <ian.d.romanick@intel.com>
29 */
30
31 #include "main/core.h"
32 #include "glsl_symbol_table.h"
33 #include "ir.h"
34 #include "shaderobj.h"
35 #include "program/hash_table.h"
36 #include "../glsl/program.h"
37
38 extern "C" {
39 #include "shaderapi.h"
40 }
41
42 void GLAPIENTRY
43 _mesa_BindAttribLocation(GLhandleARB program, GLuint index,
44 const GLcharARB *name)
45 {
46 GET_CURRENT_CONTEXT(ctx);
47
48 struct gl_shader_program *const shProg =
49 _mesa_lookup_shader_program_err(ctx, program, "glBindAttribLocation");
50 if (!shProg)
51 return;
52
53 if (!name)
54 return;
55
56 if (strncmp(name, "gl_", 3) == 0) {
57 _mesa_error(ctx, GL_INVALID_OPERATION,
58 "glBindAttribLocation(illegal name)");
59 return;
60 }
61
62 if (index >= ctx->Const.Program[MESA_SHADER_VERTEX].MaxAttribs) {
63 _mesa_error(ctx, GL_INVALID_VALUE, "glBindAttribLocation(index)");
64 return;
65 }
66
67 /* Replace the current value if it's already in the list. Add
68 * VERT_ATTRIB_GENERIC0 because that's how the linker differentiates
69 * between built-in attributes and user-defined attributes.
70 */
71 shProg->AttributeBindings->put(index + VERT_ATTRIB_GENERIC0, name);
72
73 /*
74 * Note that this attribute binding won't go into effect until
75 * glLinkProgram is called again.
76 */
77 }
78
79 static bool
80 is_active_attrib(const ir_variable *var)
81 {
82 if (!var)
83 return false;
84
85 switch (var->data.mode) {
86 case ir_var_shader_in:
87 return var->data.location != -1;
88
89 case ir_var_system_value:
90 /* From GL 4.3 core spec, section 11.1.1 (Vertex Attributes):
91 * "For GetActiveAttrib, all active vertex shader input variables
92 * are enumerated, including the special built-in inputs gl_VertexID
93 * and gl_InstanceID."
94 */
95 return !strcmp(var->name, "gl_VertexID") ||
96 !strcmp(var->name, "gl_InstanceID");
97
98 default:
99 return false;
100 }
101 }
102
103 void GLAPIENTRY
104 _mesa_GetActiveAttrib(GLhandleARB program, GLuint desired_index,
105 GLsizei maxLength, GLsizei * length, GLint * size,
106 GLenum * type, GLcharARB * name)
107 {
108 GET_CURRENT_CONTEXT(ctx);
109 struct gl_shader_program *shProg;
110
111 shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveAttrib");
112 if (!shProg)
113 return;
114
115 if (!shProg->LinkStatus) {
116 _mesa_error(ctx, GL_INVALID_VALUE,
117 "glGetActiveAttrib(program not linked)");
118 return;
119 }
120
121 if (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
122 _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(no vertex shader)");
123 return;
124 }
125
126 exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
127 unsigned current_index = 0;
128
129 foreach_in_list(ir_instruction, node, ir) {
130 const ir_variable *const var = node->as_variable();
131
132 if (!is_active_attrib(var))
133 continue;
134
135 if (current_index == desired_index) {
136 _mesa_copy_string(name, maxLength, length, var->name);
137
138 if (size)
139 *size = (var->type->is_array()) ? var->type->length : 1;
140
141 if (type)
142 *type = var->type->gl_type;
143
144 return;
145 }
146
147 current_index++;
148 }
149
150 /* If the loop did not return early, the caller must have asked for
151 * an index that did not exit. Set an error.
152 */
153 _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(index)");
154 }
155
156 /* Locations associated with shader variables (array or non-array) can be
157 * queried using its base name or using the base name appended with the
158 * valid array index. For example, in case of below vertex shader, valid
159 * queries can be made to know the location of "xyz", "array", "array[0]",
160 * "array[1]", "array[2]" and "array[3]". In this example index reurned
161 * will be 0, 0, 0, 1, 2, 3 respectively.
162 *
163 * [Vertex Shader]
164 * layout(location=0) in vec4 xyz;
165 * layout(location=1) in vec4[4] array;
166 * void main()
167 * { }
168 *
169 * This requirement came up with the addition of ARB_program_interface_query
170 * to OpenGL 4.3 specification. See page 101 (page 122 of the PDF) for details.
171 *
172 * This utility function is used by:
173 * _mesa_GetAttribLocation
174 * _mesa_GetFragDataLocation
175 * _mesa_GetFragDataIndex
176 *
177 * Returns 0:
178 * if the 'name' string matches var->name.
179 * Returns 'matched index':
180 * if the 'name' string matches var->name appended with valid array index.
181 */
182 int static inline
183 get_matching_index(const ir_variable *const var, const char *name) {
184 unsigned idx = 0;
185 const char *const paren = strchr(name, '[');
186 const unsigned len = (paren != NULL) ? paren - name : strlen(name);
187
188 if (paren != NULL) {
189 if (!var->type->is_array())
190 return -1;
191
192 char *endptr;
193 idx = (unsigned) strtol(paren + 1, &endptr, 10);
194 const unsigned idx_len = endptr != (paren + 1) ? endptr - paren - 1 : 0;
195
196 /* Validate the sub string representing index in 'name' string */
197 if ((idx > 0 && paren[1] == '0') /* leading zeroes */
198 || (idx == 0 && idx_len > 1) /* all zeroes */
199 || paren[1] == ' ' /* whitespace */
200 || endptr[0] != ']' /* closing brace */
201 || endptr[1] != '\0' /* null char */
202 || idx_len == 0 /* missing index */
203 || idx >= var->type->length) /* exceeding array bound */
204 return -1;
205 }
206
207 if (strncmp(var->name, name, len) == 0 && var->name[len] == '\0')
208 return idx;
209
210 return -1;
211 }
212
213 GLint GLAPIENTRY
214 _mesa_GetAttribLocation(GLhandleARB program, const GLcharARB * name)
215 {
216 GET_CURRENT_CONTEXT(ctx);
217 struct gl_shader_program *const shProg =
218 _mesa_lookup_shader_program_err(ctx, program, "glGetAttribLocation");
219
220 if (!shProg) {
221 return -1;
222 }
223
224 if (!shProg->LinkStatus) {
225 _mesa_error(ctx, GL_INVALID_OPERATION,
226 "glGetAttribLocation(program not linked)");
227 return -1;
228 }
229
230 if (!name)
231 return -1;
232
233 /* Not having a vertex shader is not an error.
234 */
235 if (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL)
236 return -1;
237
238 exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
239 foreach_in_list(ir_instruction, node, ir) {
240 const ir_variable *const var = node->as_variable();
241
242 /* The extra check against VERT_ATTRIB_GENERIC0 is because
243 * glGetAttribLocation cannot be used on "conventional" attributes.
244 *
245 * From page 95 of the OpenGL 3.0 spec:
246 *
247 * "If name is not an active attribute, if name is a conventional
248 * attribute, or if an error occurs, -1 will be returned."
249 */
250 if (var == NULL
251 || var->data.mode != ir_var_shader_in
252 || var->data.location == -1
253 || var->data.location < VERT_ATTRIB_GENERIC0)
254 continue;
255
256 int index = get_matching_index(var, (const char *) name);
257
258 if (index >= 0)
259 return var->data.location + index - VERT_ATTRIB_GENERIC0;
260 }
261
262 return -1;
263 }
264
265
266 unsigned
267 _mesa_count_active_attribs(struct gl_shader_program *shProg)
268 {
269 if (!shProg->LinkStatus
270 || shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
271 return 0;
272 }
273
274 exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
275 unsigned i = 0;
276
277 foreach_in_list(ir_instruction, node, ir) {
278 const ir_variable *const var = node->as_variable();
279
280 if (!is_active_attrib(var))
281 continue;
282
283 i++;
284 }
285
286 return i;
287 }
288
289
290 size_t
291 _mesa_longest_attribute_name_length(struct gl_shader_program *shProg)
292 {
293 if (!shProg->LinkStatus
294 || shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
295 return 0;
296 }
297
298 exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
299 size_t longest = 0;
300
301 foreach_in_list(ir_instruction, node, ir) {
302 const ir_variable *const var = node->as_variable();
303
304 if (var == NULL
305 || var->data.mode != ir_var_shader_in
306 || var->data.location == -1)
307 continue;
308
309 const size_t len = strlen(var->name);
310 if (len >= longest)
311 longest = len + 1;
312 }
313
314 return longest;
315 }
316
317 void GLAPIENTRY
318 _mesa_BindFragDataLocation(GLuint program, GLuint colorNumber,
319 const GLchar *name)
320 {
321 _mesa_BindFragDataLocationIndexed(program, colorNumber, 0, name);
322 }
323
324 void GLAPIENTRY
325 _mesa_BindFragDataLocationIndexed(GLuint program, GLuint colorNumber,
326 GLuint index, const GLchar *name)
327 {
328 GET_CURRENT_CONTEXT(ctx);
329
330 struct gl_shader_program *const shProg =
331 _mesa_lookup_shader_program_err(ctx, program, "glBindFragDataLocationIndexed");
332 if (!shProg)
333 return;
334
335 if (!name)
336 return;
337
338 if (strncmp(name, "gl_", 3) == 0) {
339 _mesa_error(ctx, GL_INVALID_OPERATION, "glBindFragDataLocationIndexed(illegal name)");
340 return;
341 }
342
343 if (index > 1) {
344 _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(index)");
345 return;
346 }
347
348 if (index == 0 && colorNumber >= ctx->Const.MaxDrawBuffers) {
349 _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(colorNumber)");
350 return;
351 }
352
353 if (index == 1 && colorNumber >= ctx->Const.MaxDualSourceDrawBuffers) {
354 _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(colorNumber)");
355 return;
356 }
357
358 /* Replace the current value if it's already in the list. Add
359 * FRAG_RESULT_DATA0 because that's how the linker differentiates
360 * between built-in attributes and user-defined attributes.
361 */
362 shProg->FragDataBindings->put(colorNumber + FRAG_RESULT_DATA0, name);
363 shProg->FragDataIndexBindings->put(index, name);
364 /*
365 * Note that this binding won't go into effect until
366 * glLinkProgram is called again.
367 */
368
369 }
370
371 GLint GLAPIENTRY
372 _mesa_GetFragDataIndex(GLuint program, const GLchar *name)
373 {
374 GET_CURRENT_CONTEXT(ctx);
375 struct gl_shader_program *const shProg =
376 _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataIndex");
377
378 if (!shProg) {
379 return -1;
380 }
381
382 if (!shProg->LinkStatus) {
383 _mesa_error(ctx, GL_INVALID_OPERATION,
384 "glGetFragDataIndex(program not linked)");
385 return -1;
386 }
387
388 if (!name)
389 return -1;
390
391 if (strncmp(name, "gl_", 3) == 0) {
392 _mesa_error(ctx, GL_INVALID_OPERATION,
393 "glGetFragDataIndex(illegal name)");
394 return -1;
395 }
396
397 /* Not having a fragment shader is not an error.
398 */
399 if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
400 return -1;
401
402 exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
403 foreach_in_list(ir_instruction, node, ir) {
404 const ir_variable *const var = node->as_variable();
405
406 /* The extra check against FRAG_RESULT_DATA0 is because
407 * glGetFragDataLocation cannot be used on "conventional" attributes.
408 *
409 * From page 95 of the OpenGL 3.0 spec:
410 *
411 * "If name is not an active attribute, if name is a conventional
412 * attribute, or if an error occurs, -1 will be returned."
413 */
414 if (var == NULL
415 || var->data.mode != ir_var_shader_out
416 || var->data.location == -1
417 || var->data.location < FRAG_RESULT_DATA0)
418 continue;
419
420 if (get_matching_index(var, (const char *) name) >= 0)
421 return var->data.index;
422 }
423
424 return -1;
425 }
426
427 GLint GLAPIENTRY
428 _mesa_GetFragDataLocation(GLuint program, const GLchar *name)
429 {
430 GET_CURRENT_CONTEXT(ctx);
431 struct gl_shader_program *const shProg =
432 _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataLocation");
433
434 if (!shProg) {
435 return -1;
436 }
437
438 if (!shProg->LinkStatus) {
439 _mesa_error(ctx, GL_INVALID_OPERATION,
440 "glGetFragDataLocation(program not linked)");
441 return -1;
442 }
443
444 if (!name)
445 return -1;
446
447 if (strncmp(name, "gl_", 3) == 0) {
448 _mesa_error(ctx, GL_INVALID_OPERATION,
449 "glGetFragDataLocation(illegal name)");
450 return -1;
451 }
452
453 /* Not having a fragment shader is not an error.
454 */
455 if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
456 return -1;
457
458 exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
459 foreach_in_list(ir_instruction, node, ir) {
460 const ir_variable *const var = node->as_variable();
461
462 /* The extra check against FRAG_RESULT_DATA0 is because
463 * glGetFragDataLocation cannot be used on "conventional" attributes.
464 *
465 * From page 95 of the OpenGL 3.0 spec:
466 *
467 * "If name is not an active attribute, if name is a conventional
468 * attribute, or if an error occurs, -1 will be returned."
469 */
470 if (var == NULL
471 || var->data.mode != ir_var_shader_out
472 || var->data.location == -1
473 || var->data.location < FRAG_RESULT_DATA0)
474 continue;
475
476 int index = get_matching_index(var, (const char *) name);
477
478 if (index >= 0)
479 return var->data.location + index - FRAG_RESULT_DATA0;
480 }
481
482 return -1;
483 }