program_resource: add subroutine support (v3.1)
[mesa.git] / src / mesa / main / program_resource.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2015 Intel Corporation. All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 */
25
26 #include "main/enums.h"
27 #include "main/macros.h"
28 #include "main/mtypes.h"
29 #include "main/shaderapi.h"
30 #include "main/shaderobj.h"
31 #include "main/context.h"
32 #include "program_resource.h"
33 #include "ir_uniform.h"
34 static bool
35 supported_interface_enum(struct gl_context *ctx, GLenum iface)
36 {
37 switch (iface) {
38 case GL_UNIFORM:
39 case GL_UNIFORM_BLOCK:
40 case GL_PROGRAM_INPUT:
41 case GL_PROGRAM_OUTPUT:
42 case GL_TRANSFORM_FEEDBACK_VARYING:
43 case GL_ATOMIC_COUNTER_BUFFER:
44 return true;
45 case GL_VERTEX_SUBROUTINE:
46 case GL_FRAGMENT_SUBROUTINE:
47 case GL_VERTEX_SUBROUTINE_UNIFORM:
48 case GL_FRAGMENT_SUBROUTINE_UNIFORM:
49 return _mesa_has_shader_subroutine(ctx);
50 case GL_GEOMETRY_SUBROUTINE:
51 case GL_GEOMETRY_SUBROUTINE_UNIFORM:
52 return _mesa_has_geometry_shaders(ctx) && _mesa_has_shader_subroutine(ctx);
53 case GL_COMPUTE_SUBROUTINE:
54 case GL_COMPUTE_SUBROUTINE_UNIFORM:
55 return _mesa_has_compute_shaders(ctx) && _mesa_has_shader_subroutine(ctx);
56 case GL_TESS_CONTROL_SUBROUTINE:
57 case GL_TESS_EVALUATION_SUBROUTINE:
58 case GL_TESS_CONTROL_SUBROUTINE_UNIFORM:
59 case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM:
60 return _mesa_has_tessellation(ctx) && _mesa_has_shader_subroutine(ctx);
61 case GL_BUFFER_VARIABLE:
62 case GL_SHADER_STORAGE_BLOCK:
63 default:
64 return false;
65 }
66 }
67
68 void GLAPIENTRY
69 _mesa_GetProgramInterfaceiv(GLuint program, GLenum programInterface,
70 GLenum pname, GLint *params)
71 {
72 GET_CURRENT_CONTEXT(ctx);
73 unsigned i;
74 struct gl_shader_program *shProg =
75 _mesa_lookup_shader_program_err(ctx, program,
76 "glGetProgramInterfaceiv");
77 if (!shProg)
78 return;
79
80 if (!params) {
81 _mesa_error(ctx, GL_INVALID_OPERATION,
82 "glGetProgramInterfaceiv(params NULL)");
83 return;
84 }
85
86 /* Validate interface. */
87 if (!supported_interface_enum(ctx, programInterface)) {
88 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramInterfaceiv(%s)",
89 _mesa_enum_to_string(programInterface));
90 return;
91 }
92
93 /* Validate pname against interface. */
94 switch(pname) {
95 case GL_ACTIVE_RESOURCES:
96 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++)
97 if (shProg->ProgramResourceList[i].Type == programInterface)
98 (*params)++;
99 break;
100 case GL_MAX_NAME_LENGTH:
101 if (programInterface == GL_ATOMIC_COUNTER_BUFFER) {
102 _mesa_error(ctx, GL_INVALID_OPERATION,
103 "glGetProgramInterfaceiv(%s pname %s)",
104 _mesa_enum_to_string(programInterface),
105 _mesa_enum_to_string(pname));
106 return;
107 }
108 /* Name length consists of base name, 3 additional chars '[0]' if
109 * resource is an array and finally 1 char for string terminator.
110 */
111 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
112 if (shProg->ProgramResourceList[i].Type != programInterface)
113 continue;
114 const char *name =
115 _mesa_program_resource_name(&shProg->ProgramResourceList[i]);
116 unsigned array_size =
117 _mesa_program_resource_array_size(&shProg->ProgramResourceList[i]);
118 *params = MAX2(*params, strlen(name) + (array_size ? 3 : 0) + 1);
119 }
120 break;
121 case GL_MAX_NUM_ACTIVE_VARIABLES:
122 switch (programInterface) {
123 case GL_UNIFORM_BLOCK:
124 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
125 if (shProg->ProgramResourceList[i].Type == programInterface) {
126 struct gl_uniform_block *block =
127 (struct gl_uniform_block *)
128 shProg->ProgramResourceList[i].Data;
129 *params = MAX2(*params, block->NumUniforms);
130 }
131 }
132 break;
133 case GL_ATOMIC_COUNTER_BUFFER:
134 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
135 if (shProg->ProgramResourceList[i].Type == programInterface) {
136 struct gl_active_atomic_buffer *buffer =
137 (struct gl_active_atomic_buffer *)
138 shProg->ProgramResourceList[i].Data;
139 *params = MAX2(*params, buffer->NumUniforms);
140 }
141 }
142 break;
143 default:
144 _mesa_error(ctx, GL_INVALID_OPERATION,
145 "glGetProgramInterfaceiv(%s pname %s)",
146 _mesa_enum_to_string(programInterface),
147 _mesa_enum_to_string(pname));
148 };
149 break;
150 case GL_MAX_NUM_COMPATIBLE_SUBROUTINES:
151 switch (programInterface) {
152 case GL_VERTEX_SUBROUTINE_UNIFORM:
153 case GL_FRAGMENT_SUBROUTINE_UNIFORM:
154 case GL_GEOMETRY_SUBROUTINE_UNIFORM:
155 case GL_COMPUTE_SUBROUTINE_UNIFORM:
156 case GL_TESS_CONTROL_SUBROUTINE_UNIFORM:
157 case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM: {
158 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
159 if (shProg->ProgramResourceList[i].Type == programInterface) {
160 struct gl_uniform_storage *uni =
161 (struct gl_uniform_storage *)
162 shProg->ProgramResourceList[i].Data;
163 *params = MAX2(*params, uni->num_compatible_subroutines);
164 }
165 }
166 break;
167 }
168
169 default:
170 _mesa_error(ctx, GL_INVALID_OPERATION,
171 "glGetProgramInterfaceiv(%s pname %s)",
172 _mesa_enum_to_string(programInterface),
173 _mesa_enum_to_string(pname));
174 }
175 break;
176 default:
177 _mesa_error(ctx, GL_INVALID_OPERATION,
178 "glGetProgramInterfaceiv(pname %s)",
179 _mesa_enum_to_string(pname));
180 }
181 }
182
183 static bool
184 is_xfb_marker(const char *str)
185 {
186 static const char *markers[] = {
187 "gl_NextBuffer",
188 "gl_SkipComponents1",
189 "gl_SkipComponents2",
190 "gl_SkipComponents3",
191 "gl_SkipComponents4",
192 NULL
193 };
194 const char **m = markers;
195
196 if (strncmp(str, "gl_", 3) != 0)
197 return false;
198
199 for (; *m; m++)
200 if (strcmp(*m, str) == 0)
201 return true;
202
203 return false;
204 }
205
206 /**
207 * Checks if given name index is legal for GetProgramResourceIndex,
208 * check is written to be compatible with GL_ARB_array_of_arrays.
209 */
210 static bool
211 valid_program_resource_index_name(const GLchar *name)
212 {
213 const char *array = strstr(name, "[");
214 const char *close = strrchr(name, ']');
215
216 /* Not array, no need for the check. */
217 if (!array)
218 return true;
219
220 /* Last array index has to be zero. */
221 if (!close || *--close != '0')
222 return false;
223
224 return true;
225 }
226
227 GLuint GLAPIENTRY
228 _mesa_GetProgramResourceIndex(GLuint program, GLenum programInterface,
229 const GLchar *name)
230 {
231 GET_CURRENT_CONTEXT(ctx);
232 struct gl_program_resource *res;
233 struct gl_shader_program *shProg =
234 _mesa_lookup_shader_program_err(ctx, program,
235 "glGetProgramResourceIndex");
236 if (!shProg || !name)
237 return GL_INVALID_INDEX;
238
239 if (!supported_interface_enum(ctx, programInterface)) {
240 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceIndex(%s)",
241 _mesa_enum_to_string(programInterface));
242 return GL_INVALID_INDEX;
243 }
244 /*
245 * For the interface TRANSFORM_FEEDBACK_VARYING, the value INVALID_INDEX
246 * should be returned when querying the index assigned to the special names
247 * "gl_NextBuffer", "gl_SkipComponents1", "gl_SkipComponents2",
248 * "gl_SkipComponents3", and "gl_SkipComponents4".
249 */
250 if (programInterface == GL_TRANSFORM_FEEDBACK_VARYING &&
251 is_xfb_marker(name))
252 return GL_INVALID_INDEX;
253
254 switch (programInterface) {
255 case GL_TESS_CONTROL_SUBROUTINE:
256 case GL_TESS_CONTROL_SUBROUTINE_UNIFORM:
257 case GL_TESS_EVALUATION_SUBROUTINE:
258 case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM:
259 case GL_COMPUTE_SUBROUTINE:
260 case GL_COMPUTE_SUBROUTINE_UNIFORM:
261 case GL_GEOMETRY_SUBROUTINE:
262 case GL_GEOMETRY_SUBROUTINE_UNIFORM:
263 case GL_VERTEX_SUBROUTINE:
264 case GL_FRAGMENT_SUBROUTINE:
265 case GL_VERTEX_SUBROUTINE_UNIFORM:
266 case GL_FRAGMENT_SUBROUTINE_UNIFORM:
267 case GL_PROGRAM_INPUT:
268 case GL_PROGRAM_OUTPUT:
269 case GL_UNIFORM:
270 case GL_TRANSFORM_FEEDBACK_VARYING:
271 /* Validate name syntax for array variables */
272 if (!valid_program_resource_index_name(name))
273 return GL_INVALID_INDEX;
274 /* fall-through */
275 case GL_UNIFORM_BLOCK:
276 res = _mesa_program_resource_find_name(shProg, programInterface, name);
277 if (!res)
278 return GL_INVALID_INDEX;
279
280 return _mesa_program_resource_index(shProg, res);
281 case GL_ATOMIC_COUNTER_BUFFER:
282 default:
283 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceIndex(%s)",
284 _mesa_enum_to_string(programInterface));
285 }
286
287 return GL_INVALID_INDEX;
288 }
289
290 void GLAPIENTRY
291 _mesa_GetProgramResourceName(GLuint program, GLenum programInterface,
292 GLuint index, GLsizei bufSize, GLsizei *length,
293 GLchar *name)
294 {
295 GET_CURRENT_CONTEXT(ctx);
296 struct gl_shader_program *shProg =
297 _mesa_lookup_shader_program_err(ctx, program,
298 "glGetProgramResourceName");
299
300 /* Set user friendly return values in case of errors. */
301 if (name)
302 *name = '\0';
303 if (length)
304 *length = 0;
305
306 if (!shProg || !name)
307 return;
308
309 if (programInterface == GL_ATOMIC_COUNTER_BUFFER ||
310 !supported_interface_enum(ctx, programInterface)) {
311 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceName(%s)",
312 _mesa_enum_to_string(programInterface));
313 return;
314 }
315
316 _mesa_get_program_resource_name(shProg, programInterface, index, bufSize,
317 length, name, "glGetProgramResourceName");
318 }
319
320 void GLAPIENTRY
321 _mesa_GetProgramResourceiv(GLuint program, GLenum programInterface,
322 GLuint index, GLsizei propCount,
323 const GLenum *props, GLsizei bufSize,
324 GLsizei *length, GLint *params)
325 {
326 GET_CURRENT_CONTEXT(ctx);
327 struct gl_shader_program *shProg =
328 _mesa_lookup_shader_program_err(ctx, program, "glGetProgramResourceiv");
329
330 if (!shProg || !params)
331 return;
332
333 /* The error INVALID_VALUE is generated if <propCount> is zero.
334 * Note that we check < 0 here because it makes sense to bail early.
335 */
336 if (propCount <= 0) {
337 _mesa_error(ctx, GL_INVALID_VALUE,
338 "glGetProgramResourceiv(propCount <= 0)");
339 return;
340 }
341
342 /* No need to write any properties, user requested none. */
343 if (bufSize == 0)
344 return;
345
346 _mesa_get_program_resourceiv(shProg, programInterface, index,
347 propCount, props, bufSize, length, params);
348 }
349
350 /**
351 * Function verifies syntax of given name for GetProgramResourceLocation
352 * and GetProgramResourceLocationIndex for the following cases:
353 *
354 * "array element portion of a string passed to GetProgramResourceLocation
355 * or GetProgramResourceLocationIndex must not have, a "+" sign, extra
356 * leading zeroes, or whitespace".
357 *
358 * Check is written to be compatible with GL_ARB_array_of_arrays.
359 */
360 static bool
361 invalid_array_element_syntax(const GLchar *name)
362 {
363 char *first = strchr(name, '[');
364 char *last = strrchr(name, '[');
365
366 if (!first)
367 return false;
368
369 /* No '+' or ' ' allowed anywhere. */
370 if (strchr(first, '+') || strchr(first, ' '))
371 return true;
372
373 /* Check that last array index is 0. */
374 if (last[1] == '0' && last[2] != ']')
375 return true;
376
377 return false;
378 }
379
380 static struct gl_shader_program *
381 lookup_linked_program(GLuint program, const char *caller)
382 {
383 GET_CURRENT_CONTEXT(ctx);
384 struct gl_shader_program *prog =
385 _mesa_lookup_shader_program_err(ctx, program, caller);
386
387 if (!prog)
388 return NULL;
389
390 if (prog->LinkStatus == GL_FALSE) {
391 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)",
392 caller);
393 return NULL;
394 }
395 return prog;
396 }
397
398 GLint GLAPIENTRY
399 _mesa_GetProgramResourceLocation(GLuint program, GLenum programInterface,
400 const GLchar *name)
401 {
402 GET_CURRENT_CONTEXT(ctx);
403 struct gl_shader_program *shProg =
404 lookup_linked_program(program, "glGetProgramResourceLocation");
405
406 if (!shProg || !name || invalid_array_element_syntax(name))
407 return -1;
408
409 /* Validate programInterface. */
410 switch (programInterface) {
411 case GL_UNIFORM:
412 case GL_PROGRAM_INPUT:
413 case GL_PROGRAM_OUTPUT:
414 break;
415
416 case GL_VERTEX_SUBROUTINE_UNIFORM:
417 case GL_FRAGMENT_SUBROUTINE_UNIFORM:
418 if (!_mesa_has_shader_subroutine(ctx))
419 goto fail;
420 break;
421 case GL_GEOMETRY_SUBROUTINE_UNIFORM:
422 if (!_mesa_has_geometry_shaders(ctx) || !_mesa_has_shader_subroutine(ctx))
423 goto fail;
424 break;
425 case GL_COMPUTE_SUBROUTINE_UNIFORM:
426 if (!_mesa_has_compute_shaders(ctx) || !_mesa_has_shader_subroutine(ctx))
427 goto fail;
428 break;
429 case GL_TESS_CONTROL_SUBROUTINE_UNIFORM:
430 case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM:
431 if (!_mesa_has_tessellation(ctx) || !_mesa_has_shader_subroutine(ctx))
432 goto fail;
433 break;
434 default:
435 goto fail;
436 }
437
438 return _mesa_program_resource_location(shProg, programInterface, name);
439 fail:
440 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceLocation(%s %s)",
441 _mesa_enum_to_string(programInterface), name);
442 return -1;
443 }
444
445 /**
446 * Returns output index for dual source blending.
447 */
448 GLint GLAPIENTRY
449 _mesa_GetProgramResourceLocationIndex(GLuint program, GLenum programInterface,
450 const GLchar *name)
451 {
452 GET_CURRENT_CONTEXT(ctx);
453 struct gl_shader_program *shProg =
454 lookup_linked_program(program, "glGetProgramResourceLocationIndex");
455
456 if (!shProg || !name || invalid_array_element_syntax(name))
457 return -1;
458
459 /* From the GL_ARB_program_interface_query spec:
460 *
461 * "For GetProgramResourceLocationIndex, <programInterface> must be
462 * PROGRAM_OUTPUT."
463 */
464 if (programInterface != GL_PROGRAM_OUTPUT) {
465 _mesa_error(ctx, GL_INVALID_ENUM,
466 "glGetProgramResourceLocationIndex(%s)",
467 _mesa_enum_to_string(programInterface));
468 return -1;
469 }
470
471 return _mesa_program_resource_location_index(shProg, GL_PROGRAM_OUTPUT,
472 name);
473 }