shader_query.cpp revision f8cf79936b42405a8366613b80e3bde21aadaa02
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_BindAttribLocationARB(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.VertexProgram.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
79void GLAPIENTRY
80_mesa_GetActiveAttribARB(GLhandleARB program, GLuint desired_index,
81                         GLsizei maxLength, GLsizei * length, GLint * size,
82                         GLenum * type, GLcharARB * name)
83{
84   GET_CURRENT_CONTEXT(ctx);
85   struct gl_shader_program *shProg;
86
87   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveAttrib");
88   if (!shProg)
89      return;
90
91   if (!shProg->LinkStatus) {
92      _mesa_error(ctx, GL_INVALID_VALUE,
93                  "glGetActiveAttrib(program not linked)");
94      return;
95   }
96
97   if (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
98      _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(no vertex shader)");
99      return;
100   }
101
102   exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
103   unsigned current_index = 0;
104
105   foreach_list(node, ir) {
106      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
107
108      if (var == NULL
109	  || var->mode != ir_var_in
110	  || var->location == -1)
111	 continue;
112
113      if (current_index == desired_index) {
114	 _mesa_copy_string(name, maxLength, length, var->name);
115
116	 if (size)
117	    *size = (var->type->is_array()) ? var->type->length : 1;
118
119	 if (type)
120	    *type = var->type->gl_type;
121
122	 return;
123      }
124
125      current_index++;
126   }
127
128   /* If the loop did not return early, the caller must have asked for
129    * an index that did not exit.  Set an error.
130    */
131   _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(index)");
132}
133
134GLint GLAPIENTRY
135_mesa_GetAttribLocationARB(GLhandleARB program, const GLcharARB * name)
136{
137   GET_CURRENT_CONTEXT(ctx);
138   struct gl_shader_program *const shProg =
139      _mesa_lookup_shader_program_err(ctx, program, "glGetAttribLocation");
140
141   if (!shProg) {
142      return -1;
143   }
144
145   if (!shProg->LinkStatus) {
146      _mesa_error(ctx, GL_INVALID_OPERATION,
147                  "glGetAttribLocation(program not linked)");
148      return -1;
149   }
150
151   if (!name)
152      return -1;
153
154   /* Not having a vertex shader is not an error.
155    */
156   if (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL)
157      return -1;
158
159   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
160   foreach_list(node, ir) {
161      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
162
163      /* The extra check against VERT_ATTRIB_GENERIC0 is because
164       * glGetAttribLocation cannot be used on "conventional" attributes.
165       *
166       * From page 95 of the OpenGL 3.0 spec:
167       *
168       *     "If name is not an active attribute, if name is a conventional
169       *     attribute, or if an error occurs, -1 will be returned."
170       */
171      if (var == NULL
172	  || var->mode != ir_var_in
173	  || var->location == -1
174	  || var->location < VERT_ATTRIB_GENERIC0)
175	 continue;
176
177      if (strcmp(var->name, name) == 0)
178	 return var->location - VERT_ATTRIB_GENERIC0;
179   }
180
181   return -1;
182}
183
184
185unsigned
186_mesa_count_active_attribs(struct gl_shader_program *shProg)
187{
188   if (!shProg->LinkStatus
189       || shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
190      return 0;
191   }
192
193   exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
194   unsigned i = 0;
195
196   foreach_list(node, ir) {
197      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
198
199      if (var == NULL
200	  || var->mode != ir_var_in
201	  || var->location == -1)
202	 continue;
203
204      i++;
205   }
206
207   return i;
208}
209
210
211size_t
212_mesa_longest_attribute_name_length(struct gl_shader_program *shProg)
213{
214   if (!shProg->LinkStatus
215       || shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
216      return 0;
217   }
218
219   exec_list *const ir = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->ir;
220   size_t longest = 0;
221
222   foreach_list(node, ir) {
223      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
224
225      if (var == NULL
226	  || var->mode != ir_var_in
227	  || var->location == -1)
228	 continue;
229
230      const size_t len = strlen(var->name);
231      if (len >= longest)
232	 longest = len + 1;
233   }
234
235   return longest;
236}
237
238void GLAPIENTRY
239_mesa_BindFragDataLocation(GLuint program, GLuint colorNumber,
240			   const GLchar *name)
241{
242   _mesa_BindFragDataLocationIndexed(program, colorNumber, 0, name);
243}
244
245void GLAPIENTRY
246_mesa_BindFragDataLocationIndexed(GLuint program, GLuint colorNumber,
247                                  GLuint index, const GLchar *name)
248{
249   GET_CURRENT_CONTEXT(ctx);
250
251   struct gl_shader_program *const shProg =
252      _mesa_lookup_shader_program_err(ctx, program, "glBindFragDataLocationIndexed");
253   if (!shProg)
254      return;
255
256   if (!name)
257      return;
258
259   if (strncmp(name, "gl_", 3) == 0) {
260      _mesa_error(ctx, GL_INVALID_OPERATION, "glBindFragDataLocationIndexed(illegal name)");
261      return;
262   }
263
264   if (index > 1) {
265      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(index)");
266      return;
267   }
268
269   if (index == 0 && colorNumber >= ctx->Const.MaxDrawBuffers) {
270      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(colorNumber)");
271      return;
272   }
273
274   if (index == 1 && colorNumber >= ctx->Const.MaxDualSourceDrawBuffers) {
275      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocationIndexed(colorNumber)");
276      return;
277   }
278
279   /* Replace the current value if it's already in the list.  Add
280    * FRAG_RESULT_DATA0 because that's how the linker differentiates
281    * between built-in attributes and user-defined attributes.
282    */
283   shProg->FragDataBindings->put(colorNumber + FRAG_RESULT_DATA0, name);
284   shProg->FragDataIndexBindings->put(index, name);
285   /*
286    * Note that this binding won't go into effect until
287    * glLinkProgram is called again.
288    */
289
290}
291
292GLint GLAPIENTRY
293_mesa_GetFragDataIndex(GLuint program, const GLchar *name)
294{
295   GET_CURRENT_CONTEXT(ctx);
296   struct gl_shader_program *const shProg =
297      _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataIndex");
298
299   if (!shProg) {
300      return -1;
301   }
302
303   if (!shProg->LinkStatus) {
304      _mesa_error(ctx, GL_INVALID_OPERATION,
305                  "glGetFragDataIndex(program not linked)");
306      return -1;
307   }
308
309   if (!name)
310      return -1;
311
312   if (strncmp(name, "gl_", 3) == 0) {
313      _mesa_error(ctx, GL_INVALID_OPERATION,
314                  "glGetFragDataIndex(illegal name)");
315      return -1;
316   }
317
318   /* Not having a fragment shader is not an error.
319    */
320   if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
321      return -1;
322
323   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
324   foreach_list(node, ir) {
325      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
326
327      /* The extra check against FRAG_RESULT_DATA0 is because
328       * glGetFragDataLocation cannot be used on "conventional" attributes.
329       *
330       * From page 95 of the OpenGL 3.0 spec:
331       *
332       *     "If name is not an active attribute, if name is a conventional
333       *     attribute, or if an error occurs, -1 will be returned."
334       */
335      if (var == NULL
336          || var->mode != ir_var_out
337          || var->location == -1
338          || var->location < FRAG_RESULT_DATA0)
339         continue;
340
341      if (strcmp(var->name, name) == 0)
342         return var->index;
343   }
344
345   return -1;
346}
347
348GLint GLAPIENTRY
349_mesa_GetFragDataLocation(GLuint program, const GLchar *name)
350{
351   GET_CURRENT_CONTEXT(ctx);
352   struct gl_shader_program *const shProg =
353      _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataLocation");
354
355   if (!shProg) {
356      return -1;
357   }
358
359   if (!shProg->LinkStatus) {
360      _mesa_error(ctx, GL_INVALID_OPERATION,
361                  "glGetFragDataLocation(program not linked)");
362      return -1;
363   }
364
365   if (!name)
366      return -1;
367
368   if (strncmp(name, "gl_", 3) == 0) {
369      _mesa_error(ctx, GL_INVALID_OPERATION,
370                  "glGetFragDataLocation(illegal name)");
371      return -1;
372   }
373
374   /* Not having a fragment shader is not an error.
375    */
376   if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
377      return -1;
378
379   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
380   foreach_list(node, ir) {
381      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
382
383      /* The extra check against FRAG_RESULT_DATA0 is because
384       * glGetFragDataLocation cannot be used on "conventional" attributes.
385       *
386       * From page 95 of the OpenGL 3.0 spec:
387       *
388       *     "If name is not an active attribute, if name is a conventional
389       *     attribute, or if an error occurs, -1 will be returned."
390       */
391      if (var == NULL
392	  || var->mode != ir_var_out
393	  || var->location == -1
394	  || var->location < FRAG_RESULT_DATA0)
395	 continue;
396
397      if (strcmp(var->name, name) == 0)
398	 return var->location - FRAG_RESULT_DATA0;
399   }
400
401   return -1;
402}
403