shader_query.cpp revision e6c314f7d2ed99714376fec6b7509a55535fa3ff
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   GET_CURRENT_CONTEXT(ctx);
243
244   struct gl_shader_program *const shProg =
245      _mesa_lookup_shader_program_err(ctx, program, "glBindFragDataLocation");
246   if (!shProg)
247      return;
248
249   if (!name)
250      return;
251
252   if (strncmp(name, "gl_", 3) == 0) {
253      _mesa_error(ctx, GL_INVALID_OPERATION,
254                  "glBindFragDataLocation(illegal name)");
255      return;
256   }
257
258   if (colorNumber >= ctx->Const.MaxDrawBuffers) {
259      _mesa_error(ctx, GL_INVALID_VALUE, "glBindFragDataLocation(index)");
260      return;
261   }
262
263   /* Replace the current value if it's already in the list.  Add
264    * FRAG_RESULT_DATA0 because that's how the linker differentiates
265    * between built-in attributes and user-defined attributes.
266    */
267   shProg->FragDataBindings->put(colorNumber + FRAG_RESULT_DATA0, name);
268
269   /*
270    * Note that this binding won't go into effect until
271    * glLinkProgram is called again.
272    */
273}
274
275GLint GLAPIENTRY
276_mesa_GetFragDataLocation(GLuint program, const GLchar *name)
277{
278   GET_CURRENT_CONTEXT(ctx);
279   struct gl_shader_program *const shProg =
280      _mesa_lookup_shader_program_err(ctx, program, "glGetFragDataLocation");
281
282   if (!shProg) {
283      return -1;
284   }
285
286   if (!shProg->LinkStatus) {
287      _mesa_error(ctx, GL_INVALID_OPERATION,
288                  "glGetFragDataLocation(program not linked)");
289      return -1;
290   }
291
292   if (!name)
293      return -1;
294
295   if (strncmp(name, "gl_", 3) == 0) {
296      _mesa_error(ctx, GL_INVALID_OPERATION,
297                  "glGetFragDataLocation(illegal name)");
298      return -1;
299   }
300
301   /* Not having a fragment shader is not an error.
302    */
303   if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)
304      return -1;
305
306   exec_list *ir = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir;
307   foreach_list(node, ir) {
308      const ir_variable *const var = ((ir_instruction *) node)->as_variable();
309
310      /* The extra check against FRAG_RESULT_DATA0 is because
311       * glGetFragDataLocation cannot be used on "conventional" attributes.
312       *
313       * From page 95 of the OpenGL 3.0 spec:
314       *
315       *     "If name is not an active attribute, if name is a conventional
316       *     attribute, or if an error occurs, -1 will be returned."
317       */
318      if (var == NULL
319	  || var->mode != ir_var_out
320	  || var->location == -1
321	  || var->location < FRAG_RESULT_DATA0)
322	 continue;
323
324      if (strcmp(var->name, name) == 0)
325	 return var->location - FRAG_RESULT_DATA0;
326   }
327
328   return -1;
329}
330