shader_query.cpp revision af69d88d
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
38extern "C" {
39#include "shaderapi.h"
40}
41
42void 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
79static bool
80is_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 var->data.location == SYSTEM_VALUE_VERTEX_ID ||
96             var->data.location == SYSTEM_VALUE_VERTEX_ID_ZERO_BASE ||
97             var->data.location == SYSTEM_VALUE_INSTANCE_ID;
98
99   default:
100      return false;
101   }
102}
103
104void GLAPIENTRY
105_mesa_GetActiveAttrib(GLhandleARB program, GLuint desired_index,
106                         GLsizei maxLength, GLsizei * length, GLint * size,
107                         GLenum * type, GLcharARB * name)
108{
109   GET_CURRENT_CONTEXT(ctx);
110   struct gl_shader_program *shProg;
111
112   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveAttrib");
113   if (!shProg)
114      return;
115
116   if (!shProg->LinkStatus) {
117      _mesa_error(ctx, GL_INVALID_VALUE,
118                  "glGetActiveAttrib(program not linked)");
119      return;
120   }
121
122   if (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
123      _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(no vertex shader)");
124      return;
125   }
126
127   exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
128   unsigned current_index = 0;
129
130   foreach_in_list(ir_instruction, node, ir) {
131      const ir_variable *const var = node->as_variable();
132
133      if (!is_active_attrib(var))
134         continue;
135
136      if (current_index == desired_index) {
137         const char *var_name = var->name;
138
139         /* Since gl_VertexID may be lowered to gl_VertexIDMESA, we need to
140          * consider gl_VertexIDMESA as gl_VertexID for purposes of checking
141          * active attributes.
142          */
143         if (var->data.mode == ir_var_system_value &&
144             var->data.location == SYSTEM_VALUE_VERTEX_ID_ZERO_BASE) {
145            var_name = "gl_VertexID";
146         }
147
148	 _mesa_copy_string(name, maxLength, length, var_name);
149
150	 if (size)
151	    *size = (var->type->is_array()) ? var->type->length : 1;
152
153	 if (type)
154	    *type = var->type->gl_type;
155
156	 return;
157      }
158
159      current_index++;
160   }
161
162   /* If the loop did not return early, the caller must have asked for
163    * an index that did not exit.  Set an error.
164    */
165   _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(index)");
166}
167
168/* Locations associated with shader variables (array or non-array) can be
169 * queried using its base name or using the base name appended with the
170 * valid array index. For example, in case of below vertex shader, valid
171 * queries can be made to know the location of "xyz", "array", "array[0]",
172 * "array[1]", "array[2]" and "array[3]". In this example index reurned
173 * will be 0, 0, 0, 1, 2, 3 respectively.
174 *
175 * [Vertex Shader]
176 * layout(location=0) in vec4 xyz;
177 * layout(location=1) in vec4[4] array;
178 * void main()
179 * { }
180 *
181 * This requirement came up with the addition of ARB_program_interface_query
182 * to OpenGL 4.3 specification. See page 101 (page 122 of the PDF) for details.
183 *
184 * This utility function is used by:
185 * _mesa_GetAttribLocation
186 * _mesa_GetFragDataLocation
187 * _mesa_GetFragDataIndex
188 *
189 * Returns 0:
190 *    if the 'name' string matches var->name.
191 * Returns 'matched index':
192 *    if the 'name' string matches var->name appended with valid array index.
193 */
194int static inline
195get_matching_index(const ir_variable *const var, const char *name) {
196   unsigned idx = 0;
197   const char *const paren = strchr(name, '[');
198   const unsigned len = (paren != NULL) ? paren - name : strlen(name);
199
200   if (paren != NULL) {
201      if (!var->type->is_array())
202         return -1;
203
204      char *endptr;
205      idx = (unsigned) strtol(paren + 1, &endptr, 10);
206      const unsigned idx_len = endptr != (paren + 1) ? endptr - paren - 1 : 0;
207
208      /* Validate the sub string representing index in 'name' string */
209      if ((idx > 0 && paren[1] == '0') /* leading zeroes */
210          || (idx == 0 && idx_len > 1) /* all zeroes */
211          || paren[1] == ' ' /* whitespace */
212          || endptr[0] != ']' /* closing brace */
213          || endptr[1] != '\0' /* null char */
214          || idx_len == 0 /* missing index */
215          || idx >= var->type->length) /* exceeding array bound */
216         return -1;
217   }
218
219   if (strncmp(var->name, name, len) == 0 && var->name[len] == '\0')
220      return idx;
221
222   return -1;
223}
224
225GLint GLAPIENTRY
226_mesa_GetAttribLocation(GLhandleARB program, const GLcharARB * name)
227{
228   GET_CURRENT_CONTEXT(ctx);
229   struct gl_shader_program *const shProg =
230      _mesa_lookup_shader_program_err(ctx, program, "glGetAttribLocation");
231
232   if (!shProg) {
233      return -1;
234   }
235
236   if (!shProg->LinkStatus) {
237      _mesa_error(ctx, GL_INVALID_OPERATION,
238                  "glGetAttribLocation(program not linked)");
239      return -1;
240   }
241
242   if (!name)
243      return -1;
244
245   /* Not having a vertex shader is not an error.
246    */
247   if (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL)
248      return -1;
249
250   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
251   foreach_in_list(ir_instruction, node, ir) {
252      const ir_variable *const var = node->as_variable();
253
254      /* The extra check against VERT_ATTRIB_GENERIC0 is because
255       * glGetAttribLocation cannot be used on "conventional" attributes.
256       *
257       * From page 95 of the OpenGL 3.0 spec:
258       *
259       *     "If name is not an active attribute, if name is a conventional
260       *     attribute, or if an error occurs, -1 will be returned."
261       */
262      if (var == NULL
263	  || var->data.mode != ir_var_shader_in
264	  || var->data.location == -1
265	  || var->data.location < VERT_ATTRIB_GENERIC0)
266	 continue;
267
268      int index = get_matching_index(var, (const char *) name);
269
270      if (index >= 0)
271         return var->data.location + index - VERT_ATTRIB_GENERIC0;
272   }
273
274   return -1;
275}
276
277
278unsigned
279_mesa_count_active_attribs(struct gl_shader_program *shProg)
280{
281   if (!shProg->LinkStatus
282       || shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
283      return 0;
284   }
285
286   exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
287   unsigned i = 0;
288
289   foreach_in_list(ir_instruction, node, ir) {
290      const ir_variable *const var = node->as_variable();
291
292      if (!is_active_attrib(var))
293         continue;
294
295      i++;
296   }
297
298   return i;
299}
300
301
302size_t
303_mesa_longest_attribute_name_length(struct gl_shader_program *shProg)
304{
305   if (!shProg->LinkStatus
306       || shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
307      return 0;
308   }
309
310   exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
311   size_t longest = 0;
312
313   foreach_in_list(ir_instruction, node, ir) {
314      const ir_variable *const var = node->as_variable();
315
316      if (var == NULL
317	  || var->data.mode != ir_var_shader_in
318	  || var->data.location == -1)
319	 continue;
320
321      const size_t len = strlen(var->name);
322      if (len >= longest)
323	 longest = len + 1;
324   }
325
326   return longest;
327}
328
329void GLAPIENTRY
330_mesa_BindFragDataLocation(GLuint program, GLuint colorNumber,
331			   const GLchar *name)
332{
333   _mesa_BindFragDataLocationIndexed(program, colorNumber, 0, name);
334}
335
336void GLAPIENTRY
337_mesa_BindFragDataLocationIndexed(GLuint program, GLuint colorNumber,
338                                  GLuint index, const GLchar *name)
339{
340   GET_CURRENT_CONTEXT(ctx);
341
342   struct gl_shader_program *const shProg =
343      _mesa_lookup_shader_program_err(ctx, program, "glBindFragDataLocationIndexed");
344   if (!shProg)
345      return;
346
347   if (!name)
348      return;
349
350   if (strncmp(name, "gl_", 3) == 0) {
351      _mesa_error(ctx, GL_INVALID_OPERATION, "glBindFragDataLocationIndexed(illegal name)");
352      return;
353   }
354
355   if (index > 1) {
356      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(index)");
357      return;
358   }
359
360   if (index == 0 && colorNumber >= ctx->Const.MaxDrawBuffers) {
361      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(colorNumber)");
362      return;
363   }
364
365   if (index == 1 && colorNumber >= ctx->Const.MaxDualSourceDrawBuffers) {
366      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(colorNumber)");
367      return;
368   }
369
370   /* Replace the current value if it's already in the list.  Add
371    * FRAG_RESULT_DATA0 because that's how the linker differentiates
372    * between built-in attributes and user-defined attributes.
373    */
374   shProg->FragDataBindings->put(colorNumber + FRAG_RESULT_DATA0, name);
375   shProg->FragDataIndexBindings->put(index, name);
376   /*
377    * Note that this binding won't go into effect until
378    * glLinkProgram is called again.
379    */
380
381}
382
383GLint GLAPIENTRY
384_mesa_GetFragDataIndex(GLuint program, const GLchar *name)
385{
386   GET_CURRENT_CONTEXT(ctx);
387   struct gl_shader_program *const shProg =
388      _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataIndex");
389
390   if (!shProg) {
391      return -1;
392   }
393
394   if (!shProg->LinkStatus) {
395      _mesa_error(ctx, GL_INVALID_OPERATION,
396                  "glGetFragDataIndex(program not linked)");
397      return -1;
398   }
399
400   if (!name)
401      return -1;
402
403   if (strncmp(name, "gl_", 3) == 0) {
404      _mesa_error(ctx, GL_INVALID_OPERATION,
405                  "glGetFragDataIndex(illegal name)");
406      return -1;
407   }
408
409   /* Not having a fragment shader is not an error.
410    */
411   if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
412      return -1;
413
414   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
415   foreach_in_list(ir_instruction, node, ir) {
416      const ir_variable *const var = node->as_variable();
417
418      /* The extra check against FRAG_RESULT_DATA0 is because
419       * glGetFragDataLocation cannot be used on "conventional" attributes.
420       *
421       * From page 95 of the OpenGL 3.0 spec:
422       *
423       *     "If name is not an active attribute, if name is a conventional
424       *     attribute, or if an error occurs, -1 will be returned."
425       */
426      if (var == NULL
427          || var->data.mode != ir_var_shader_out
428          || var->data.location == -1
429          || var->data.location < FRAG_RESULT_DATA0)
430         continue;
431
432      if (get_matching_index(var, (const char *) name) >= 0)
433         return var->data.index;
434   }
435
436   return -1;
437}
438
439GLint GLAPIENTRY
440_mesa_GetFragDataLocation(GLuint program, const GLchar *name)
441{
442   GET_CURRENT_CONTEXT(ctx);
443   struct gl_shader_program *const shProg =
444      _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataLocation");
445
446   if (!shProg) {
447      return -1;
448   }
449
450   if (!shProg->LinkStatus) {
451      _mesa_error(ctx, GL_INVALID_OPERATION,
452                  "glGetFragDataLocation(program not linked)");
453      return -1;
454   }
455
456   if (!name)
457      return -1;
458
459   if (strncmp(name, "gl_", 3) == 0) {
460      _mesa_error(ctx, GL_INVALID_OPERATION,
461                  "glGetFragDataLocation(illegal name)");
462      return -1;
463   }
464
465   /* Not having a fragment shader is not an error.
466    */
467   if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
468      return -1;
469
470   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
471   foreach_in_list(ir_instruction, node, ir) {
472      const ir_variable *const var = node->as_variable();
473
474      /* The extra check against FRAG_RESULT_DATA0 is because
475       * glGetFragDataLocation cannot be used on "conventional" attributes.
476       *
477       * From page 95 of the OpenGL 3.0 spec:
478       *
479       *     "If name is not an active attribute, if name is a conventional
480       *     attribute, or if an error occurs, -1 will be returned."
481       */
482      if (var == NULL
483	  || var->data.mode != ir_var_shader_out
484	  || var->data.location == -1
485	  || var->data.location < FRAG_RESULT_DATA0)
486	 continue;
487
488      int index = get_matching_index(var, (const char *) name);
489
490      if (index >= 0)
491         return var->data.location + index - FRAG_RESULT_DATA0;
492   }
493
494   return -1;
495}
496