X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Funiform_query.cpp;h=82d7628e8d8aa89abd3524b593a8ffc8a337ddb4;hb=917db0bc3dfdec1536018d96aab261e485b8d872;hp=496f60416bfd654cbeea6819a9acc148142d6ec0;hpb=719909698c67c287a393d2380278e7b7495ae018;p=mesa.git diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp index 496f60416bf..82d7628e8d8 100644 --- a/src/mesa/main/uniform_query.cpp +++ b/src/mesa/main/uniform_query.cpp @@ -18,11 +18,14 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. */ + #include + #include "main/core.h" #include "main/context.h" #include "ir.h" @@ -30,15 +33,14 @@ #include "program/hash_table.h" #include "../glsl/program.h" #include "../glsl/ir_uniform.h" - -extern "C" { +#include "../glsl/glsl_parser_extras.h" #include "main/shaderapi.h" #include "main/shaderobj.h" #include "uniforms.h" -} + extern "C" void GLAPIENTRY -_mesa_GetActiveUniformARB(GLhandleARB program, GLuint index, +_mesa_GetActiveUniform(GLhandleARB program, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *nameOut) { @@ -57,7 +59,7 @@ _mesa_GetActiveUniformARB(GLhandleARB program, GLuint index, const struct gl_uniform_storage *const uni = &shProg->UniformStorage[index]; if (nameOut) { - _mesa_copy_string(nameOut, maxLength, length, uni->name); + _mesa_get_uniform_name(uni, maxLength, length, nameOut); } if (size) { @@ -72,265 +74,101 @@ _mesa_GetActiveUniformARB(GLhandleARB program, GLuint index, } } -static GLenum -base_uniform_type(GLenum type) +extern "C" void GLAPIENTRY +_mesa_GetActiveUniformsiv(GLuint program, + GLsizei uniformCount, + const GLuint *uniformIndices, + GLenum pname, + GLint *params) { - switch (type) { - case GL_BOOL: - case GL_BOOL_VEC2: - case GL_BOOL_VEC3: - case GL_BOOL_VEC4: - return GL_BOOL; - case GL_FLOAT: - case GL_FLOAT_VEC2: - case GL_FLOAT_VEC3: - case GL_FLOAT_VEC4: - case GL_FLOAT_MAT2: - case GL_FLOAT_MAT2x3: - case GL_FLOAT_MAT2x4: - case GL_FLOAT_MAT3x2: - case GL_FLOAT_MAT3: - case GL_FLOAT_MAT3x4: - case GL_FLOAT_MAT4x2: - case GL_FLOAT_MAT4x3: - case GL_FLOAT_MAT4: - return GL_FLOAT; - case GL_UNSIGNED_INT: - case GL_UNSIGNED_INT_VEC2: - case GL_UNSIGNED_INT_VEC3: - case GL_UNSIGNED_INT_VEC4: - return GL_UNSIGNED_INT; - case GL_INT: - case GL_INT_VEC2: - case GL_INT_VEC3: - case GL_INT_VEC4: - return GL_INT; - default: - _mesa_problem(NULL, "Invalid type in base_uniform_type()"); - return GL_FLOAT; - } -} + GET_CURRENT_CONTEXT(ctx); + struct gl_shader_program *shProg; + GLsizei i; -static GLboolean -is_boolean_type(GLenum type) -{ - switch (type) { - case GL_BOOL: - case GL_BOOL_VEC2: - case GL_BOOL_VEC3: - case GL_BOOL_VEC4: - return GL_TRUE; - default: - return GL_FALSE; - } -} + shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform"); + if (!shProg) + return; -static GLboolean -is_sampler_type(GLenum type) -{ - switch (type) { - case GL_SAMPLER_1D: - case GL_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_SAMPLER_2D: - case GL_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_SAMPLER_1D_SHADOW: - case GL_SAMPLER_2D_SHADOW: - case GL_SAMPLER_CUBE_SHADOW: - case GL_SAMPLER_2D_RECT_ARB: - case GL_INT_SAMPLER_2D_RECT: - case GL_UNSIGNED_INT_SAMPLER_2D_RECT: - case GL_SAMPLER_2D_RECT_SHADOW_ARB: - case GL_SAMPLER_1D_ARRAY_EXT: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - case GL_SAMPLER_2D_ARRAY_EXT: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW_EXT: - case GL_SAMPLER_2D_ARRAY_SHADOW_EXT: - case GL_SAMPLER_CUBE_MAP_ARRAY: - case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: - case GL_SAMPLER_BUFFER: - case GL_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_SAMPLER_EXTERNAL_OES: - return GL_TRUE; - default: - return GL_FALSE; + if (uniformCount < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glGetUniformIndices(uniformCount < 0)"); + return; } -} -/** - * Given a uniform index, return the vertex/geometry/fragment program - * that has that parameter, plus the position of the parameter in the - * parameter/constant buffer. - * \param shProg the shader program - * \param index the uniform index in [0, NumUniforms-1] - * \param progOut returns containing program - * \param posOut returns position of the uniform in the param/const buffer - * \return GL_TRUE for success, GL_FALSE for invalid index - */ -static GLboolean -find_uniform_parameter_pos(struct gl_shader_program *shProg, GLint index, - struct gl_program **progOut, GLint *posOut) -{ - struct gl_program *prog = NULL; - GLint pos; + for (i = 0; i < uniformCount; i++) { + GLuint index = uniformIndices[i]; - if (!shProg->Uniforms || - index < 0 || - index >= (GLint) shProg->Uniforms->NumUniforms) { - return GL_FALSE; - } - - pos = shProg->Uniforms->Uniforms[index].VertPos; - if (pos >= 0) { - prog = shProg->_LinkedShaders[MESA_SHADER_VERTEX]->Program; - } - else { - pos = shProg->Uniforms->Uniforms[index].FragPos; - if (pos >= 0) { - prog = shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program; - } - else { - pos = shProg->Uniforms->Uniforms[index].GeomPos; - if (pos >= 0) { - prog = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->Program; - } + if (index >= shProg->NumUserUniformStorage) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniformsiv(index)"); + return; } } - if (!prog || pos < 0) - return GL_FALSE; /* should really never happen */ + for (i = 0; i < uniformCount; i++) { + GLuint index = uniformIndices[i]; + const struct gl_uniform_storage *uni = &shProg->UniformStorage[index]; - *progOut = prog; - *posOut = pos; + switch (pname) { + case GL_UNIFORM_TYPE: + params[i] = uni->type->gl_type; + break; - return GL_TRUE; -} + case GL_UNIFORM_SIZE: + /* array_elements is zero for non-arrays, but the API requires that 1 be + * returned. + */ + params[i] = MAX2(1, uni->array_elements); + break; -/** - * Return pointer to a gl_program_parameter which corresponds to a uniform. - * \param shProg the shader program - * \param index the uniform index in [0, NumUniforms-1] - * \return gl_program_parameter point or NULL if index is invalid - */ -const struct gl_program_parameter * -get_uniform_parameter(struct gl_shader_program *shProg, GLint index) -{ - struct gl_program *prog; - GLint progPos; + case GL_UNIFORM_NAME_LENGTH: + params[i] = strlen(uni->name) + 1; - if (find_uniform_parameter_pos(shProg, index, &prog, &progPos)) - return &prog->Parameters->Parameters[progPos]; - else - return NULL; -} + /* Page 61 (page 73 of the PDF) in section 2.11 of the OpenGL ES 3.0 + * spec says: + * + * "If the active uniform is an array, the uniform name returned + * in name will always be the name of the uniform array appended + * with "[0]"." + */ + if (uni->array_elements != 0) + params[i] += 3; + break; -static unsigned -get_vector_elements(GLenum type) -{ - switch (type) { - case GL_FLOAT: - case GL_INT: - case GL_BOOL: - case GL_UNSIGNED_INT: - default: /* Catch all the various sampler types. */ - return 1; + case GL_UNIFORM_BLOCK_INDEX: + params[i] = uni->block_index; + break; - case GL_FLOAT_VEC2: - case GL_INT_VEC2: - case GL_BOOL_VEC2: - case GL_UNSIGNED_INT_VEC2: - return 2; + case GL_UNIFORM_OFFSET: + params[i] = uni->offset; + break; - case GL_FLOAT_VEC3: - case GL_INT_VEC3: - case GL_BOOL_VEC3: - case GL_UNSIGNED_INT_VEC3: - return 3; + case GL_UNIFORM_ARRAY_STRIDE: + params[i] = uni->array_stride; + break; - case GL_FLOAT_VEC4: - case GL_INT_VEC4: - case GL_BOOL_VEC4: - case GL_UNSIGNED_INT_VEC4: - return 4; - } -} + case GL_UNIFORM_MATRIX_STRIDE: + params[i] = uni->matrix_stride; + break; -static void -get_matrix_dims(GLenum type, GLint *rows, GLint *cols) -{ - switch (type) { - case GL_FLOAT_MAT2: - *rows = *cols = 2; - break; - case GL_FLOAT_MAT2x3: - *rows = 3; - *cols = 2; - break; - case GL_FLOAT_MAT2x4: - *rows = 4; - *cols = 2; - break; - case GL_FLOAT_MAT3: - *rows = 3; - *cols = 3; - break; - case GL_FLOAT_MAT3x2: - *rows = 2; - *cols = 3; - break; - case GL_FLOAT_MAT3x4: - *rows = 4; - *cols = 3; - break; - case GL_FLOAT_MAT4: - *rows = 4; - *cols = 4; - break; - case GL_FLOAT_MAT4x2: - *rows = 2; - *cols = 4; - break; - case GL_FLOAT_MAT4x3: - *rows = 3; - *cols = 4; - break; - default: - *rows = *cols = 0; - } -} + case GL_UNIFORM_IS_ROW_MAJOR: + params[i] = uni->row_major; + break; -/** - * Determine the number of rows and columns occupied by a uniform - * according to its datatype. For non-matrix types (such as GL_FLOAT_VEC4), - * the number of rows = 1 and cols = number of elements in the vector. - */ -static void -get_uniform_rows_cols(const struct gl_program_parameter *p, - GLint *rows, GLint *cols) -{ - get_matrix_dims(p->DataType, rows, cols); - if (*rows == 0 && *cols == 0) { - /* not a matrix type, probably a float or vector */ - *rows = 1; - *cols = get_vector_elements(p->DataType); + case GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX: + if (!ctx->Extensions.ARB_shader_atomic_counters) + goto invalid_enum; + params[i] = uni->atomic_buffer_index; + break; + + default: + goto invalid_enum; + } } + + return; + + invalid_enum: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetActiveUniformsiv(pname)"); } static bool @@ -399,6 +237,8 @@ validate_uniform_parameters(struct gl_context *ctx, * * - if no variable with a location of location exists in the * program object currently in use and location is not -1, + * - if count is greater than one, and the uniform declared in the + * shader is not an array variable, */ if (location < -1) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)", @@ -406,7 +246,7 @@ validate_uniform_parameters(struct gl_context *ctx, return false; } - _mesa_uniform_split_location_offset(location, loc, array_index); + _mesa_uniform_split_location_offset(shProg, location, loc, array_index); if (*loc >= shProg->NumUserUniformStorage) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)", @@ -414,11 +254,21 @@ validate_uniform_parameters(struct gl_context *ctx, return false; } - /* This case should be impossible. The implication is that a call like - * glGetUniformLocation(prog, "foo[8]") was successful but "foo" is not an - * array. + if (shProg->UniformStorage[*loc].array_elements == 0 && count > 1) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(count > 1 for non-array, location=%d)", + caller, location); + return false; + } + + /* If the uniform is an array, check that array_index is in bounds. + * If not an array, check that array_index is zero. + * array_index is unsigned so no need to check for less than zero. */ - if (*array_index != 0 && shProg->UniformStorage[*loc].array_elements == 0) { + unsigned limit = shProg->UniformStorage[*loc].array_elements; + if (limit == 0) + limit = 1; + if (*array_index >= limit) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)", caller, location); return false; @@ -455,10 +305,18 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location, const union gl_constant_value *const src = &uni->storage[offset * elements]; - unsigned bytes = sizeof(uni->storage[0]) * elements; - if (bytes > bufSize) { - elements = bufSize / sizeof(uni->storage[0]); - bytes = bufSize; + assert(returnType == GLSL_TYPE_FLOAT || returnType == GLSL_TYPE_INT || + returnType == GLSL_TYPE_UINT); + /* The three (currently) supported types all have the same size, + * which is of course the same as their union. That'll change + * with glGetUniformdv()... + */ + unsigned bytes = sizeof(src[0]) * elements; + if (bufSize < 0 || bytes > (unsigned) bufSize) { + _mesa_error( ctx, GL_INVALID_OPERATION, + "glGetnUniform*vARB(out of bounds: bufSize is %d," + " but %u bytes are required)", bufSize, bytes ); + return; } /* If the return type and the uniform's native type are "compatible," @@ -587,20 +445,14 @@ log_uniform(const void *values, enum glsl_base_type basicType, static void log_program_parameters(const struct gl_shader_program *shProg) { - static const char *stages[] = { - "vertex", "fragment", "geometry" - }; - - assert(Elements(stages) == MESA_SHADER_TYPES); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { if (shProg->_LinkedShaders[i] == NULL) continue; const struct gl_program *const prog = shProg->_LinkedShaders[i]->Program; printf("Program %d %s shader parameters:\n", - shProg->Name, stages[i]); + shProg->Name, _mesa_shader_stage_to_string(i)); for (unsigned j = 0; j < prog->Parameters->NumParameters; j++) { printf("%s: %p %f %f %f %f\n", prog->Parameters->Parameters[j].Name, @@ -615,207 +467,6 @@ log_program_parameters(const struct gl_shader_program *shProg) } #endif -/** - * Check if the type given by userType is allowed to set a uniform of the - * target type. Generally, equivalence is required, but setting Boolean - * uniforms can be done with glUniformiv or glUniformfv. - */ -static GLboolean -compatible_types(GLenum userType, GLenum targetType) -{ - if (userType == targetType) - return GL_TRUE; - - if (targetType == GL_BOOL && (userType == GL_FLOAT || - userType == GL_UNSIGNED_INT || - userType == GL_INT)) - return GL_TRUE; - - if (targetType == GL_BOOL_VEC2 && (userType == GL_FLOAT_VEC2 || - userType == GL_UNSIGNED_INT_VEC2 || - userType == GL_INT_VEC2)) - return GL_TRUE; - - if (targetType == GL_BOOL_VEC3 && (userType == GL_FLOAT_VEC3 || - userType == GL_UNSIGNED_INT_VEC3 || - userType == GL_INT_VEC3)) - return GL_TRUE; - - if (targetType == GL_BOOL_VEC4 && (userType == GL_FLOAT_VEC4 || - userType == GL_UNSIGNED_INT_VEC4 || - userType == GL_INT_VEC4)) - return GL_TRUE; - - if (is_sampler_type(targetType) && userType == GL_INT) - return GL_TRUE; - - return GL_FALSE; -} - -/** - * Set the value of a program's uniform variable. - * \param program the program whose uniform to update - * \param index the index of the program parameter for the uniform - * \param offset additional parameter slot offset (for arrays) - * \param type the incoming datatype of 'values' - * \param count the number of uniforms to set - * \param elems number of elements per uniform (1, 2, 3 or 4) - * \param values the new values, of datatype 'type' - */ -static void -set_program_uniform(struct gl_context *ctx, struct gl_program *program, - GLint index, GLint offset, - GLenum type, GLsizei count, GLint elems, - const void *values) -{ - const struct gl_program_parameter *param = - &program->Parameters->Parameters[index]; - - assert(offset >= 0); - assert(elems >= 1); - assert(elems <= 4); - - if (!compatible_types(type, param->DataType)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)"); - return; - } - - if (index + offset > (GLint) program->Parameters->Size) { - /* out of bounds! */ - return; - } - - if (param->Type == PROGRAM_SAMPLER) { - /* This controls which texture unit which is used by a sampler */ - GLboolean changed = GL_FALSE; - GLint i; - - /* this should have been caught by the compatible_types() check */ - ASSERT(type == GL_INT); - - /* loop over number of samplers to change */ - for (i = 0; i < count; i++) { - GLuint sampler = (GLuint) - program->Parameters->ParameterValues[index+offset + i][0].f; - GLuint texUnit = ((GLuint *) values)[i]; - - /* check that the sampler (tex unit index) is legal */ - if (texUnit >= ctx->Const.MaxCombinedTextureImageUnits) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glUniform1(invalid sampler/tex unit index for '%s')", - param->Name); - return; - } - - /* This maps a sampler to a texture unit: */ - if (sampler < MAX_SAMPLERS) { -#if 0 - printf("Set program %p sampler %d '%s' to unit %u\n", - program, sampler, param->Name, texUnit); -#endif - if (program->SamplerUnits[sampler] != texUnit) { - program->SamplerUnits[sampler] = texUnit; - changed = GL_TRUE; - } - } - } - - if (changed) { - /* When a sampler's value changes it usually requires rewriting - * a GPU program's TEX instructions since there may not be a - * sampler->texture lookup table. We signal this with the - * ProgramStringNotify() callback. - */ - FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM); - _mesa_update_shader_textures_used(program); - /* Do we need to care about the return value here? - * This should not be the first time the driver was notified of - * this program. - */ - (void) ctx->Driver.ProgramStringNotify(ctx, program->Target, program); - } - } - else { - /* ordinary uniform variable */ - const GLboolean isUniformBool = is_boolean_type(param->DataType); - const GLenum basicType = base_uniform_type(type); - const GLint slots = (param->Size + 3) / 4; - const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); - GLsizei k, i; - - if ((GLint) param->Size > typeSize) { - /* an array */ - /* we'll ignore extra data below */ - } - else { - /* non-array: count must be at most one; count == 0 is handled - * by the loop below - */ - if (count > 1) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glUniform(uniform '%s' is not an array)", - param->Name); - return; - } - } - - /* loop over number of array elements */ - for (k = 0; k < count; k++) { - gl_constant_value *uniformVal; - - if (offset + k >= slots) { - /* Extra array data is ignored */ - break; - } - - /* uniformVal (the destination) is always gl_constant_value[4] */ - uniformVal = program->Parameters->ParameterValues[index + offset + k]; - - if (basicType == GL_INT) { - const GLint *iValues = ((const GLint *) values) + k * elems; - for (i = 0; i < elems; i++) { - if (!ctx->Const.NativeIntegers) - uniformVal[i].f = (GLfloat) iValues[i]; - else - uniformVal[i].i = iValues[i]; - } - } - else if (basicType == GL_UNSIGNED_INT) { - const GLuint *iValues = ((const GLuint *) values) + k * elems; - for (i = 0; i < elems; i++) { - if (!ctx->Const.NativeIntegers) - uniformVal[i].f = (GLfloat)(GLuint) iValues[i]; - else - uniformVal[i].u = iValues[i]; - } - } - else { - const GLfloat *fValues = ((const GLfloat *) values) + k * elems; - assert(basicType == GL_FLOAT); - for (i = 0; i < elems; i++) { - uniformVal[i].f = fValues[i]; - } - } - - /* if the uniform is bool-valued, convert to 1 or 0 */ - if (isUniformBool) { - for (i = 0; i < elems; i++) { - if (basicType == GL_FLOAT) - uniformVal[i].b = uniformVal[i].f != 0.0f ? 1 : 0; - else - uniformVal[i].b = uniformVal[i].u ? 1 : 0; - - if (ctx->Const.NativeIntegers) - uniformVal[i].u = - uniformVal[i].b ? ctx->Const.UniformBooleanTrue : 0; - else - uniformVal[i].f = uniformVal[i].b ? 1.0f : 0.0f; - } - } - } - } -} - /** * Propagate some values from uniform backing storage to driver storage * @@ -943,12 +594,9 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, unsigned loc, offset; unsigned components; unsigned src_components; - unsigned i; enum glsl_base_type basicType; struct gl_uniform_storage *uni; - ASSERT_OUTSIDE_BEGIN_END(ctx); - if (!validate_uniform_parameters(ctx, shProg, location, count, &loc, &offset, "glUniform", false)) return; @@ -1071,6 +719,8 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, * GL_INVALID_VALUE error and ignore the command. */ if (uni->type->is_sampler()) { + int i; + for (i = 0; i < count; i++) { const unsigned texUnit = ((unsigned *) values)[i]; @@ -1097,10 +747,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, * will have already generated an error. */ if (uni->array_elements != 0) { - if (offset >= uni->array_elements) - return; - - count = MIN2(count, (uni->array_elements - offset)); + count = MIN2(count, (int) (uni->array_elements - offset)); } FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); @@ -1115,6 +762,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, (const union gl_constant_value *) values; union gl_constant_value *dst = &uni->storage[components * offset]; const unsigned elems = components * count; + unsigned i; for (i = 0; i < elems; i++) { if (basicType == GLSL_TYPE_FLOAT) { @@ -1133,117 +781,64 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, * the changes through. */ if (uni->type->is_sampler()) { - for (i = 0; i < count; i++) { - shProg->SamplerUnits[uni->sampler + offset + i] = - ((unsigned *) values)[i]; - } + int i; bool flushed = false; - for (i = 0; i < MESA_SHADER_TYPES; i++) { - struct gl_program *prog; + for (i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_shader *const sh = shProg->_LinkedShaders[i]; + int j; - if (shProg->_LinkedShaders[i] == NULL) + /* If the shader stage doesn't use the sampler uniform, skip this. + */ + if (sh == NULL || !uni->sampler[i].active) continue; - prog = shProg->_LinkedShaders[i]->Program; + for (j = 0; j < count; j++) { + sh->SamplerUnits[uni->sampler[i].index + offset + j] = + ((unsigned *) values)[j]; + } + + struct gl_program *const prog = sh->Program; - assert(sizeof(prog->SamplerUnits) == sizeof(shProg->SamplerUnits)); + assert(sizeof(prog->SamplerUnits) == sizeof(sh->SamplerUnits)); - if (memcmp(prog->SamplerUnits, - shProg->SamplerUnits, - sizeof(shProg->SamplerUnits)) != 0) { + /* Determine if any of the samplers used by this shader stage have + * been modified. + */ + bool changed = false; + for (unsigned j = 0; j < Elements(prog->SamplerUnits); j++) { + if ((sh->active_samplers & (1U << j)) != 0 + && (prog->SamplerUnits[j] != sh->SamplerUnits[j])) { + changed = true; + break; + } + } + + if (changed) { if (!flushed) { FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM); flushed = true; } memcpy(prog->SamplerUnits, - shProg->SamplerUnits, - sizeof(shProg->SamplerUnits)); + sh->SamplerUnits, + sizeof(sh->SamplerUnits)); - _mesa_update_shader_textures_used(prog); - (void) ctx->Driver.ProgramStringNotify(ctx, prog->Target, prog); + _mesa_update_shader_textures_used(shProg, prog); + if (ctx->Driver.SamplerUniformChange) + ctx->Driver.SamplerUniformChange(ctx, prog->Target, prog); } } } } -/** - * Set a matrix-valued program parameter. - */ -static void -set_program_uniform_matrix(struct gl_context *ctx, struct gl_program *program, - GLuint index, GLuint offset, - GLuint count, GLuint rows, GLuint cols, - GLboolean transpose, const GLfloat *values) -{ - GLuint mat, row, col; - GLuint src = 0; - const struct gl_program_parameter *param = - &program->Parameters->Parameters[index]; - const GLuint slots = (param->Size + 3) / 4; - const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); - GLint nr, nc; - - /* check that the number of rows, columns is correct */ - get_matrix_dims(param->DataType, &nr, &nc); - if (rows != nr || cols != nc) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glUniformMatrix(matrix size mismatch)"); - return; - } - - if ((GLint) param->Size <= typeSize) { - /* non-array: count must be at most one; count == 0 is handled - * by the loop below - */ - if (count > 1) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glUniformMatrix(uniform is not an array)"); - return; - } - } - - /* - * Note: the _columns_ of a matrix are stored in program registers, not - * the rows. So, the loops below look a little funny. - * XXX could optimize this a bit... - */ - - /* loop over matrices */ - for (mat = 0; mat < count; mat++) { - - /* each matrix: */ - for (col = 0; col < cols; col++) { - GLfloat *v; - if (offset >= slots) { - /* Ignore writes beyond the end of (the used part of) an array */ - return; - } - v = (GLfloat *) program->Parameters->ParameterValues[index + offset]; - for (row = 0; row < rows; row++) { - if (transpose) { - v[row] = values[src + row * cols + col]; - } - else { - v[row] = values[src + col * rows + row]; - } - } - - offset++; - } - - src += rows * cols; /* next matrix */ - } -} - /** * Called by glUniformMatrix*() functions. * Note: cols=2, rows=4 ==> array[2] of vec4 */ extern "C" void _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, - GLint cols, GLint rows, + GLuint cols, GLuint rows, GLint location, GLsizei count, GLboolean transpose, const GLfloat *values) { @@ -1253,8 +848,6 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, unsigned elements; struct gl_uniform_storage *uni; - ASSERT_OUTSIDE_BEGIN_END(ctx); - if (!validate_uniform_parameters(ctx, shProg, location, count, &loc, &offset, "glUniformMatrix", false)) return; @@ -1279,6 +872,17 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, return; } + /* GL_INVALID_VALUE is generated if `transpose' is not GL_FALSE. + * http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml */ + if (ctx->API == API_OPENGLES + || (ctx->API == API_OPENGLES2 && ctx->Version < 30)) { + if (transpose) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glUniformMatrix(matrix transpose is not GL_FALSE)"); + return; + } + } + if (ctx->Shader.Flags & GLSL_UNIFORMS) { log_uniform(values, GLSL_TYPE_FLOAT, components, vectors, count, bool(transpose), shProg, location, uni); @@ -1296,10 +900,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, * will have already generated an error. */ if (uni->array_elements != 0) { - if (offset >= uni->array_elements) - return; - - count = MIN2(count, (uni->array_elements - offset)); + count = MIN2(count, (int) (uni->array_elements - offset)); } FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); @@ -1317,7 +918,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, const float *src = values; float *dst = &uni->storage[elements * offset].f; - for (unsigned i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { for (unsigned r = 0; r < rows; r++) { for (unsigned c = 0; c < cols; c++) { dst[(c * components) + r] = src[c + (r * vectors)]; @@ -1334,85 +935,54 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); } + /** * Called via glGetUniformLocation(). * - * The return value will encode two values, the uniform location and an - * offset (used for arrays, structs). + * Returns the uniform index into UniformStorage (also the + * glGetActiveUniformsiv uniform index), and stores the referenced + * array offset in *offset, or GL_INVALID_INDEX (-1). Those two + * return values can be encoded into a uniform location for + * glUniform* using _mesa_uniform_merge_location_offset(index, offset). */ -extern "C" GLint +extern "C" unsigned _mesa_get_uniform_location(struct gl_context *ctx, struct gl_shader_program *shProg, - const GLchar *name) + const GLchar *name, + unsigned *out_offset) { - const size_t len = strlen(name); - long offset; - bool array_lookup; - char *name_copy; - - /* If the name ends with a ']', assume that it refers to some element of an - * array. Malformed array references will fail the hash table look up - * below, so it doesn't matter that they are not caught here. This code - * only wants to catch the "leaf" array references so that arrays of - * structures containing arrays will be handled correctly. + /* Page 80 (page 94 of the PDF) of the OpenGL 2.1 spec says: + * + * "The first element of a uniform array is identified using the + * name of the uniform array appended with "[0]". Except if the last + * part of the string name indicates a uniform array, then the + * location of the first element of that array can be retrieved by + * either using the name of the uniform array, or the name of the + * uniform array appended with "[0]"." + * + * Note: since uniform names are not allowed to use whitespace, and array + * indices within uniform names are not allowed to use "+", "-", or leading + * zeros, it follows that each uniform has a unique name up to the possible + * ambiguity with "[0]" noted above. Therefore we don't need to worry + * about mal-formed inputs--they will properly fail when we try to look up + * the uniform name in shProg->UniformHash. */ - if (name[len-1] == ']') { - unsigned i; - - /* Walk backwards over the string looking for a non-digit character. - * This had better be the opening bracket for an array index. - * - * Initially, i specifies the location of the ']'. Since the string may - * contain only the ']' charcater, walk backwards very carefully. - */ - for (i = len - 1; (i > 0) && isdigit(name[i-1]); --i) - /* empty */ ; - - /* Page 80 (page 94 of the PDF) of the OpenGL 2.1 spec says: - * - * "The first element of a uniform array is identified using the - * name of the uniform array appended with "[0]". Except if the last - * part of the string name indicates a uniform array, then the - * location of the first element of that array can be retrieved by - * either using the name of the uniform array, or the name of the - * uniform array appended with "[0]"." - * - * Page 79 (page 93 of the PDF) of the OpenGL 2.1 spec says: - * - * "name must be a null terminated string, without white space." - * - * Return an error if there is no opening '[' to match the closing ']'. - * An error will also be returned if there is intervening white space - * (or other non-digit characters) before the opening '['. - */ - if ((i == 0) || name[i-1] != '[') - return -1; - - /* Return an error if there are no digits between the opening '[' to - * match the closing ']'. - */ - if (i == (len - 1)) - return -1; - /* Make a new string that is a copy of the old string up to (but not - * including) the '[' character. - */ - name_copy = (char *) malloc(i); - memcpy(name_copy, name, i - 1); - name_copy[i-1] = '\0'; - - offset = strtol(&name[i], NULL, 10); - if (offset < 0) - return -1; + const GLchar *base_name_end; + long offset = parse_program_resource_name(name, &base_name_end); + bool array_lookup = offset >= 0; + char *name_copy; - array_lookup = true; + if (array_lookup) { + name_copy = (char *) malloc(base_name_end - name + 1); + memcpy(name_copy, name, base_name_end - name); + name_copy[base_name_end - name] = '\0'; } else { name_copy = (char *) name; offset = 0; - array_lookup = false; } - unsigned location; + unsigned location = 0; const bool found = shProg->UniformHash->get(location, name_copy); assert(!found @@ -1424,14 +994,61 @@ _mesa_get_uniform_location(struct gl_context *ctx, free(name_copy); if (!found) - return -1; + return GL_INVALID_INDEX; - /* Since array_elements is 0 for non-arrays, this causes look-ups of 'a[0]' - * to (correctly) fail if 'a' is not an array. + /* If the uniform is an array, fail if the index is out of bounds. + * (A negative index is caught above.) This also fails if the uniform + * is not an array, but the user is trying to index it, because + * array_elements is zero and offset >= 0. */ - if (array_lookup && shProg->UniformStorage[location].array_elements == 0) { - return -1; + if (array_lookup + && offset >= (long) shProg->UniformStorage[location].array_elements) { + return GL_INVALID_INDEX; } - return _mesa_uniform_merge_location_offset(location, offset); + *out_offset = offset; + return location; +} + +extern "C" bool +_mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg, + char *errMsg, size_t errMsgLength) +{ + const glsl_type *unit_types[MAX_COMBINED_TEXTURE_IMAGE_UNITS]; + + memset(unit_types, 0, sizeof(unit_types)); + + for (unsigned i = 0; i < shProg->NumUserUniformStorage; i++) { + const struct gl_uniform_storage *const storage = + &shProg->UniformStorage[i]; + const glsl_type *const t = (storage->type->is_array()) + ? storage->type->fields.array : storage->type; + + if (!t->is_sampler()) + continue; + + const unsigned count = MAX2(1, storage->type->array_size()); + for (unsigned j = 0; j < count; j++) { + const unsigned unit = storage->storage[j].i; + + /* The types of the samplers associated with a particular texture + * unit must be an exact match. Page 74 (page 89 of the PDF) of the + * OpenGL 3.3 core spec says: + * + * "It is not allowed to have variables of different sampler + * types pointing to the same texture image unit within a program + * object." + */ + if (unit_types[unit] == NULL) { + unit_types[unit] = t; + } else if (unit_types[unit] != t) { + _mesa_snprintf(errMsg, errMsgLength, + "Texture unit %d is accessed both as %s and %s", + unit, unit_types[unit]->name, t->name); + return false; + } + } + } + + return true; }