/** * 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, GLuint cols, GLuint rows, GLint location, GLsizei count, GLboolean transpose, const GLvoid *values, enum glsl_base_type basicType) { unsigned offset; unsigned vectors; unsigned components; unsigned elements; int size_mul; struct gl_uniform_storage *const uni = validate_uniform_parameters(ctx, shProg, location, count, &offset, "glUniformMatrix"); if (uni == NULL) return; if (!uni->type->is_matrix()) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(non-matrix uniform)"); return; } assert(basicType == GLSL_TYPE_FLOAT || basicType == GLSL_TYPE_DOUBLE); size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1; assert(!uni->type->is_sampler()); vectors = uni->type->matrix_columns; components = uni->type->vector_elements; /* Verify that the types are compatible. This is greatly simplified for * matrices because they can only have a float base type. */ if (vectors != cols || components != rows) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(matrix size mismatch)"); return; } /* GL_INVALID_VALUE is generated if `transpose' is not GL_FALSE. * http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml */ if (transpose) { if (ctx->API == API_OPENGLES2 && ctx->Version < 30) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(matrix transpose is not GL_FALSE)"); return; } } /* Section 2.11.7 (Uniform Variables) of the OpenGL 4.2 Core Profile spec * says: * * "If any of the following conditions occur, an INVALID_OPERATION * error is generated by the Uniform* commands, and no uniform values * are changed: * * ... * * - if the uniform declared in the shader is not of type boolean and * the type indicated in the name of the Uniform* command used does * not match the type of the uniform" * * There are no Boolean matrix types, so we do not need to allow * GLSL_TYPE_BOOL here (as _mesa_uniform does). */ if (uni->type->base_type != basicType) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix%ux%u(\"%s\"@%d is %s, not %s)", cols, rows, uni->name, location, glsl_type_name(uni->type->base_type), glsl_type_name(basicType)); return; } if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) { log_uniform(values, uni->type->base_type, components, vectors, count, bool(transpose), shProg, location, uni); } /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says: * * "When loading N elements starting at an arbitrary position k in a * uniform declared as an array, elements k through k + N - 1 in the * array will be replaced with the new values. Values for any array * element that exceeds the highest array element index used, as * reported by GetActiveUniform, will be ignored by the GL." * * Clamp 'count' to a valid value. Note that for non-arrays a count > 1 * will have already generated an error. */ if (uni->array_elements != 0) { count = MIN2(count, (int) (uni->array_elements - offset)); } FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); /* Store the data in the "actual type" backing storage for the uniform. */ elements = components * vectors; if (!transpose) { memcpy(&uni->storage[elements * offset], values, sizeof(uni->storage[0]) * elements * count * size_mul); } else if (basicType == GLSL_TYPE_FLOAT) { /* Copy and transpose the matrix. */ const float *src = (const float *)values; float *dst = &uni->storage[elements * offset].f; 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)]; } } dst += elements; src += elements; } } else { assert(basicType == GLSL_TYPE_DOUBLE); const double *src = (const double *)values; double *dst = (double *)&uni->storage[elements * offset].f; 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)]; } } dst += elements; src += elements; } } uni->initialized = true; _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); }
/** * 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, GLuint cols, GLuint rows, GLint location, GLsizei count, GLboolean transpose, const GLfloat *values) { unsigned loc, offset; unsigned vectors; unsigned components; 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; uni = &shProg->UniformStorage[loc]; if (!uni->type->is_matrix()) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(non-matrix uniform)"); return; } assert(!uni->type->is_sampler()); vectors = uni->type->matrix_columns; components = uni->type->vector_elements; /* Verify that the types are compatible. This is greatly simplified for * matrices because they can only have a float base type. */ if (vectors != cols || components != rows) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(matrix size mismatch)"); return; } if (ctx->Shader.Flags & GLSL_UNIFORMS) { log_uniform(values, GLSL_TYPE_FLOAT, components, vectors, count, bool(transpose), shProg, location, uni); } /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says: * * "When loading N elements starting at an arbitrary position k in a * uniform declared as an array, elements k through k + N - 1 in the * array will be replaced with the new values. Values for any array * element that exceeds the highest array element index used, as * reported by GetActiveUniform, will be ignored by the GL." * * Clamp 'count' to a valid value. Note that for non-arrays a count > 1 * will have already generated an error. */ if (uni->array_elements != 0) { if (offset >= uni->array_elements) return; count = MIN2(count, (uni->array_elements - offset)); } FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); /* Store the data in the "actual type" backing storage for the uniform. */ elements = components * vectors; if (!transpose) { memcpy(&uni->storage[elements * offset], values, sizeof(uni->storage[0]) * elements * count); } else { /* Copy and transpose the matrix. */ const float *src = values; float *dst = &uni->storage[elements * offset].f; 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)]; } } dst += elements; src += elements; } } uni->initialized = true; _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); }
/** * Called via glUniform*() functions. */ extern "C" void _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, GLint location, GLsizei count, const GLvoid *values, enum glsl_base_type basicType, unsigned src_components) { unsigned offset; int size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1; struct gl_uniform_storage *const uni = validate_uniform_parameters(ctx, shProg, location, count, &offset, "glUniform"); if (uni == NULL) return; if (uni->type->is_matrix()) { /* Can't set matrix uniforms (like mat4) with glUniform */ _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform%u(uniform \"%s\"@%d is matrix)", src_components, uni->name, location); return; } /* Verify that the types are compatible. */ const unsigned components = uni->type->is_sampler() ? 1 : uni->type->vector_elements; if (components != src_components) { /* glUniformN() must match float/vecN type */ _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform%u(\"%s\"@%u has %u components, not %u)", src_components, uni->name, location, components, src_components); return; } bool match; switch (uni->type->base_type) { case GLSL_TYPE_BOOL: match = (basicType != GLSL_TYPE_DOUBLE); break; case GLSL_TYPE_SAMPLER: match = (basicType == GLSL_TYPE_INT); break; case GLSL_TYPE_IMAGE: match = (basicType == GLSL_TYPE_INT && _mesa_is_desktop_gl(ctx)); break; default: match = (basicType == uni->type->base_type); break; } if (!match) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform%u(\"%s\"@%d is %s, not %s)", src_components, uni->name, location, glsl_type_name(uni->type->base_type), glsl_type_name(basicType)); return; } if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) { log_uniform(values, basicType, components, 1, count, false, shProg, location, uni); } /* Page 100 (page 116 of the PDF) of the OpenGL 3.0 spec says: * * "Setting a sampler's value to i selects texture image unit number * i. The values of i range from zero to the implementation- dependent * maximum supported number of texture image units." * * In addition, table 2.3, "Summary of GL errors," on page 17 (page 33 of * the PDF) says: * * "Error Description Offending command * ignored? * ... * INVALID_VALUE Numeric argument out of range Yes" * * Based on that, when an invalid sampler is specified, we generate a * GL_INVALID_VALUE error and ignore the command. */ if (uni->type->is_sampler()) { for (int i = 0; i < count; i++) { const unsigned texUnit = ((unsigned *) values)[i]; /* check that the sampler (tex unit index) is legal */ if (texUnit >= ctx->Const.MaxCombinedTextureImageUnits) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniform1i(invalid sampler/tex unit index for " "uniform %d)", location); return; } } } if (uni->type->is_image()) { for (int i = 0; i < count; i++) { const int unit = ((GLint *) values)[i]; /* check that the image unit is legal */ if (unit < 0 || unit >= (int)ctx->Const.MaxImageUnits) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniform1i(invalid image unit index for uniform %d)", location); return; } } } /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says: * * "When loading N elements starting at an arbitrary position k in a * uniform declared as an array, elements k through k + N - 1 in the * array will be replaced with the new values. Values for any array * element that exceeds the highest array element index used, as * reported by GetActiveUniform, will be ignored by the GL." * * Clamp 'count' to a valid value. Note that for non-arrays a count > 1 * will have already generated an error. */ if (uni->array_elements != 0) { count = MIN2(count, (int) (uni->array_elements - offset)); } FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); /* Store the data in the "actual type" backing storage for the uniform. */ if (!uni->type->is_boolean()) { memcpy(&uni->storage[size_mul * components * offset], values, sizeof(uni->storage[0]) * components * count * size_mul); } else { const union gl_constant_value *src = (const union gl_constant_value *) values; union gl_constant_value *dst = &uni->storage[components * offset]; const unsigned elems = components * count; for (unsigned i = 0; i < elems; i++) { if (basicType == GLSL_TYPE_FLOAT) { dst[i].i = src[i].f != 0.0f ? ctx->Const.UniformBooleanTrue : 0; } else { dst[i].i = src[i].i != 0 ? ctx->Const.UniformBooleanTrue : 0; } } } uni->initialized = true; _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); /* If the uniform is a sampler, do the extra magic necessary to propagate * the changes through. */ if (uni->type->is_sampler()) { bool flushed = false; for (int i = 0; i < MESA_SHADER_STAGES; i++) { struct gl_shader *const sh = shProg->_LinkedShaders[i]; /* If the shader stage doesn't use the sampler uniform, skip this. */ if (sh == NULL || !uni->sampler[i].active) continue; for (int 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(sh->SamplerUnits)); /* Determine if any of the samplers used by this shader stage have * been modified. */ bool changed = false; for (unsigned j = 0; j < ARRAY_SIZE(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, sh->SamplerUnits, sizeof(sh->SamplerUnits)); _mesa_update_shader_textures_used(shProg, prog); if (ctx->Driver.SamplerUniformChange) ctx->Driver.SamplerUniformChange(ctx, prog->Target, prog); } } } /* If the uniform is an image, update the mapping from image * uniforms to image units present in the shader data structure. */ if (uni->type->is_image()) { for (int i = 0; i < MESA_SHADER_STAGES; i++) { if (uni->image[i].active) { struct gl_shader *sh = shProg->_LinkedShaders[i]; for (int j = 0; j < count; j++) sh->ImageUnits[uni->image[i].index + offset + j] = ((GLint *) values)[j]; } } ctx->NewDriverState |= ctx->DriverFlags.NewImageUnits; } }
/** * Called via glUniform*() functions. */ extern "C" void _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, GLint location, GLsizei count, const GLvoid *values, GLenum type) { unsigned loc, offset; unsigned components; unsigned src_components; 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; uni = &shProg->UniformStorage[loc]; /* Verify that the types are compatible. */ switch (type) { case GL_FLOAT: basicType = GLSL_TYPE_FLOAT; src_components = 1; break; case GL_FLOAT_VEC2: basicType = GLSL_TYPE_FLOAT; src_components = 2; break; case GL_FLOAT_VEC3: basicType = GLSL_TYPE_FLOAT; src_components = 3; break; case GL_FLOAT_VEC4: basicType = GLSL_TYPE_FLOAT; src_components = 4; break; case GL_UNSIGNED_INT: basicType = GLSL_TYPE_UINT; src_components = 1; break; case GL_UNSIGNED_INT_VEC2: basicType = GLSL_TYPE_UINT; src_components = 2; break; case GL_UNSIGNED_INT_VEC3: basicType = GLSL_TYPE_UINT; src_components = 3; break; case GL_UNSIGNED_INT_VEC4: basicType = GLSL_TYPE_UINT; src_components = 4; break; case GL_INT: basicType = GLSL_TYPE_INT; src_components = 1; break; case GL_INT_VEC2: basicType = GLSL_TYPE_INT; src_components = 2; break; case GL_INT_VEC3: basicType = GLSL_TYPE_INT; src_components = 3; break; case GL_INT_VEC4: basicType = GLSL_TYPE_INT; src_components = 4; break; case GL_BOOL: case GL_BOOL_VEC2: case GL_BOOL_VEC3: case GL_BOOL_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: default: _mesa_problem(NULL, "Invalid type in %s", __func__); return; } if (uni->type->is_sampler()) { components = 1; } else { components = uni->type->vector_elements; } bool match; switch (uni->type->base_type) { case GLSL_TYPE_BOOL: match = true; break; case GLSL_TYPE_SAMPLER: match = (basicType == GLSL_TYPE_INT); break; default: match = (basicType == uni->type->base_type); break; } if (uni->type->is_matrix() || components != src_components || !match) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)"); return; } if (ctx->Shader.Flags & GLSL_UNIFORMS) { log_uniform(values, basicType, components, 1, count, false, shProg, location, uni); } /* Page 100 (page 116 of the PDF) of the OpenGL 3.0 spec says: * * "Setting a sampler's value to i selects texture image unit number * i. The values of i range from zero to the implementation- dependent * maximum supported number of texture image units." * * In addition, table 2.3, "Summary of GL errors," on page 17 (page 33 of * the PDF) says: * * "Error Description Offending command * ignored? * ... * INVALID_VALUE Numeric argument out of range Yes" * * Based on that, when an invalid sampler is specified, we generate a * 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]; /* check that the sampler (tex unit index) is legal */ if (texUnit >= ctx->Const.MaxCombinedTextureImageUnits) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniform1i(invalid sampler/tex unit index for " "uniform %d)", location); return; } } } /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says: * * "When loading N elements starting at an arbitrary position k in a * uniform declared as an array, elements k through k + N - 1 in the * array will be replaced with the new values. Values for any array * element that exceeds the highest array element index used, as * reported by GetActiveUniform, will be ignored by the GL." * * Clamp 'count' to a valid value. Note that for non-arrays a count > 1 * will have already generated an error. */ if (uni->array_elements != 0) { if (offset >= uni->array_elements) return; count = MIN2(count, (uni->array_elements - offset)); } FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); /* Store the data in the "actual type" backing storage for the uniform. */ if (!uni->type->is_boolean()) { memcpy(&uni->storage[components * offset], values, sizeof(uni->storage[0]) * components * count); } else { const union gl_constant_value *src = (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) { dst[i].i = src[i].f != 0.0f ? 1 : 0; } else { dst[i].i = src[i].i != 0 ? 1 : 0; } } } uni->initialized = true; _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); /* If the uniform is a sampler, do the extra magic necessary to propagate * the changes through. */ if (uni->type->is_sampler()) { int i; for (i = 0; i < count; i++) { shProg->SamplerUnits[uni->sampler + offset + i] = ((unsigned *) values)[i]; } bool flushed = false; for (i = 0; i < MESA_SHADER_TYPES; i++) { struct gl_program *prog; if (shProg->_LinkedShaders[i] == NULL) continue; prog = shProg->_LinkedShaders[i]->Program; /* If the shader stage doesn't use any samplers, don't bother * checking if any samplers have changed. */ if (prog->SamplersUsed == 0) continue; assert(sizeof(prog->SamplerUnits) == sizeof(shProg->SamplerUnits)); /* 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 ((prog->SamplersUsed & (1U << j)) != 0 && (prog->SamplerUnits[j] != shProg->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)); _mesa_update_shader_textures_used(prog); (void) ctx->Driver.ProgramStringNotify(ctx, prog->Target, prog); } } } }