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