@@ -217,6 +217,7 @@ if env['platform'] != 'winddk': | |||
'shader/programopt.c', | |||
'shader/symbol_table.c', | |||
'shader/shader_api.c', | |||
'shader/uniforms.c', | |||
] | |||
slang_sources = [ |
@@ -44,6 +44,7 @@ | |||
#include "shader/prog_statevars.h" | |||
#include "shader/prog_uniform.h" | |||
#include "shader/shader_api.h" | |||
#include "shader/uniforms.h" | |||
#include "shader/slang/slang_compile.h" | |||
#include "shader/slang/slang_link.h" | |||
#include "main/dispatch.h" | |||
@@ -222,7 +223,7 @@ _mesa_lookup_shader_program(GLcontext *ctx, GLuint name) | |||
/** | |||
* As above, but record an error if program is not found. | |||
*/ | |||
static struct gl_shader_program * | |||
struct gl_shader_program * | |||
_mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name, | |||
const char *caller) | |||
{ | |||
@@ -840,77 +841,6 @@ _mesa_sizeof_glsl_type(GLenum type) | |||
} | |||
static GLenum | |||
base_uniform_type(GLenum type) | |||
{ | |||
switch (type) { | |||
#if 0 /* not needed, for now */ | |||
case GL_BOOL: | |||
case GL_BOOL_VEC2: | |||
case GL_BOOL_VEC3: | |||
case GL_BOOL_VEC4: | |||
return GL_BOOL; | |||
#endif | |||
case GL_FLOAT: | |||
case GL_FLOAT_VEC2: | |||
case GL_FLOAT_VEC3: | |||
case GL_FLOAT_VEC4: | |||
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; | |||
} | |||
} | |||
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; | |||
} | |||
} | |||
static GLboolean | |||
is_sampler_type(GLenum type) | |||
{ | |||
switch (type) { | |||
case GL_SAMPLER_1D: | |||
case GL_SAMPLER_2D: | |||
case GL_SAMPLER_3D: | |||
case GL_SAMPLER_CUBE: | |||
case GL_SAMPLER_1D_SHADOW: | |||
case GL_SAMPLER_2D_SHADOW: | |||
case GL_SAMPLER_2D_RECT_ARB: | |||
case GL_SAMPLER_2D_RECT_SHADOW_ARB: | |||
case GL_SAMPLER_1D_ARRAY_EXT: | |||
case GL_SAMPLER_2D_ARRAY_EXT: | |||
case GL_SAMPLER_1D_ARRAY_SHADOW_EXT: | |||
case GL_SAMPLER_2D_ARRAY_SHADOW_EXT: | |||
return GL_TRUE; | |||
default: | |||
return GL_FALSE; | |||
} | |||
} | |||
static void | |||
_mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index, | |||
GLsizei maxLength, GLsizei *length, GLint *size, | |||
@@ -943,93 +873,6 @@ _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index, | |||
} | |||
static struct gl_program_parameter * | |||
get_uniform_parameter(const struct gl_shader_program *shProg, GLuint index) | |||
{ | |||
const struct gl_program *prog = NULL; | |||
GLint progPos; | |||
progPos = shProg->Uniforms->Uniforms[index].VertPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->VertexProgram->Base; | |||
} | |||
else { | |||
progPos = shProg->Uniforms->Uniforms[index].FragPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->FragmentProgram->Base; | |||
} | |||
} | |||
if (!prog || progPos < 0) | |||
return NULL; /* should never happen */ | |||
return &prog->Parameters->Parameters[progPos]; | |||
} | |||
/** | |||
* Called via ctx->Driver.GetActiveUniform(). | |||
*/ | |||
static void | |||
_mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index, | |||
GLsizei maxLength, GLsizei *length, GLint *size, | |||
GLenum *type, GLchar *nameOut) | |||
{ | |||
const struct gl_shader_program *shProg; | |||
const struct gl_program *prog = NULL; | |||
const struct gl_program_parameter *param; | |||
GLint progPos; | |||
shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform"); | |||
if (!shProg) | |||
return; | |||
if (!shProg->Uniforms || index >= shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(index)"); | |||
return; | |||
} | |||
progPos = shProg->Uniforms->Uniforms[index].VertPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->VertexProgram->Base; | |||
} | |||
else { | |||
progPos = shProg->Uniforms->Uniforms[index].FragPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->FragmentProgram->Base; | |||
} | |||
} | |||
if (!prog || progPos < 0) | |||
return; /* should never happen */ | |||
ASSERT(progPos < prog->Parameters->NumParameters); | |||
param = &prog->Parameters->Parameters[progPos]; | |||
if (nameOut) { | |||
_mesa_copy_string(nameOut, maxLength, length, param->Name); | |||
} | |||
if (size) { | |||
GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); | |||
if ((GLint) param->Size > typeSize) { | |||
/* This is an array. | |||
* Array elements are placed on vector[4] boundaries so they're | |||
* a multiple of four floats. We round typeSize up to next multiple | |||
* of four to get the right size below. | |||
*/ | |||
typeSize = (typeSize + 3) & ~3; | |||
} | |||
/* Note that the returned size is in units of the <type>, not bytes */ | |||
*size = param->Size / typeSize; | |||
} | |||
if (type) { | |||
*type = param->DataType; | |||
} | |||
} | |||
/** | |||
* Called via ctx->Driver.GetAttachedShaders(). | |||
*/ | |||
@@ -1209,282 +1052,6 @@ _mesa_get_shader_source(GLcontext *ctx, GLuint shader, GLsizei maxLength, | |||
} | |||
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; | |||
} | |||
} | |||
/** | |||
* 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 */ | |||
if (p->Size <= 4) { | |||
*rows = 1; | |||
*cols = p->Size; | |||
} | |||
else { | |||
*rows = p->Size / 4 + 1; | |||
if (p->Size % 4 == 0) | |||
*cols = 4; | |||
else | |||
*cols = p->Size % 4; | |||
} | |||
} | |||
} | |||
/** | |||
* Helper for get_uniform[fi]v() functions. | |||
* Given a shader program name and uniform location, return a pointer | |||
* to the shader program and return the program parameter position. | |||
*/ | |||
static void | |||
lookup_uniform_parameter(GLcontext *ctx, GLuint program, GLint location, | |||
struct gl_program **progOut, GLint *paramPosOut) | |||
{ | |||
struct gl_shader_program *shProg | |||
= _mesa_lookup_shader_program_err(ctx, program, "glGetUniform[if]v"); | |||
struct gl_program *prog = NULL; | |||
GLint progPos = -1; | |||
/* if shProg is NULL, we'll have already recorded an error */ | |||
if (shProg) { | |||
if (!shProg->Uniforms || | |||
location < 0 || | |||
location >= (GLint) shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(location)"); | |||
} | |||
else { | |||
/* OK, find the gl_program and program parameter location */ | |||
progPos = shProg->Uniforms->Uniforms[location].VertPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->VertexProgram->Base; | |||
} | |||
else { | |||
progPos = shProg->Uniforms->Uniforms[location].FragPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->FragmentProgram->Base; | |||
} | |||
} | |||
} | |||
} | |||
*progOut = prog; | |||
*paramPosOut = progPos; | |||
} | |||
/** | |||
* Called via ctx->Driver.GetUniformfv(). | |||
*/ | |||
static void | |||
_mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, | |||
GLfloat *params) | |||
{ | |||
struct gl_program *prog; | |||
GLint paramPos; | |||
lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); | |||
if (prog) { | |||
const struct gl_program_parameter *p = | |||
&prog->Parameters->Parameters[paramPos]; | |||
GLint rows, cols, i, j, k; | |||
get_uniform_rows_cols(p, &rows, &cols); | |||
k = 0; | |||
for (i = 0; i < rows; i++) { | |||
for (j = 0; j < cols; j++ ) { | |||
params[k++] = prog->Parameters->ParameterValues[paramPos+i][j]; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Called via ctx->Driver.GetUniformiv(). | |||
* \sa _mesa_get_uniformfv, only difference is a cast. | |||
*/ | |||
static void | |||
_mesa_get_uniformiv(GLcontext *ctx, GLuint program, GLint location, | |||
GLint *params) | |||
{ | |||
struct gl_program *prog; | |||
GLint paramPos; | |||
lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); | |||
if (prog) { | |||
const struct gl_program_parameter *p = | |||
&prog->Parameters->Parameters[paramPos]; | |||
GLint rows, cols, i, j, k; | |||
get_uniform_rows_cols(p, &rows, &cols); | |||
k = 0; | |||
for (i = 0; i < rows; i++) { | |||
for (j = 0; j < cols; j++ ) { | |||
params[k++] = (GLint) prog->Parameters->ParameterValues[paramPos+i][j]; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* The value returned by GetUniformLocation actually encodes two things: | |||
* 1. the index into the prog->Uniforms[] array for the uniform | |||
* 2. an offset in the prog->ParameterValues[] array for specifying array | |||
* elements or structure fields. | |||
* This function merges those two values. | |||
*/ | |||
static void | |||
merge_location_offset(GLint *location, GLint offset) | |||
{ | |||
*location = *location | (offset << 16); | |||
} | |||
/** | |||
* Seperate the uniform location and parameter offset. See above. | |||
*/ | |||
static void | |||
split_location_offset(GLint *location, GLint *offset) | |||
{ | |||
*offset = (*location >> 16); | |||
*location = *location & 0xffff; | |||
} | |||
/** | |||
* Called via ctx->Driver.GetUniformLocation(). | |||
* | |||
* The return value will encode two values, the uniform location and an | |||
* offset (used for arrays, structs). | |||
*/ | |||
static GLint | |||
_mesa_get_uniform_location(GLcontext *ctx, GLuint program, const GLchar *name) | |||
{ | |||
GLint offset = 0, location = -1; | |||
struct gl_shader_program *shProg = | |||
_mesa_lookup_shader_program_err(ctx, program, "glGetUniformLocation"); | |||
if (!shProg) | |||
return -1; | |||
if (shProg->LinkStatus == GL_FALSE) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(program)"); | |||
return -1; | |||
} | |||
/* XXX we should return -1 if the uniform was declared, but not | |||
* actually used. | |||
*/ | |||
/* XXX we need to be able to parse uniform names for structs and arrays | |||
* such as: | |||
* mymatrix[1] | |||
* mystruct.field1 | |||
*/ | |||
{ | |||
/* handle 1-dimension arrays here... */ | |||
char *c = strchr(name, '['); | |||
if (c) { | |||
/* truncate name at [ */ | |||
const GLint len = c - name; | |||
GLchar *newName = malloc(len + 1); | |||
if (!newName) | |||
return -1; /* out of mem */ | |||
memcpy(newName, name, len); | |||
newName[len] = 0; | |||
location = _mesa_lookup_uniform(shProg->Uniforms, newName); | |||
if (location >= 0) { | |||
const GLint element = atoi(c + 1); | |||
if (element > 0) { | |||
/* get type of the uniform array element */ | |||
struct gl_program_parameter *p; | |||
p = get_uniform_parameter(shProg, location); | |||
if (p) { | |||
GLint rows, cols; | |||
get_matrix_dims(p->DataType, &rows, &cols); | |||
if (rows < 1) | |||
rows = 1; | |||
offset = element * rows; | |||
} | |||
} | |||
} | |||
free(newName); | |||
} | |||
} | |||
if (location < 0) { | |||
location = _mesa_lookup_uniform(shProg->Uniforms, name); | |||
} | |||
if (location >= 0) { | |||
merge_location_offset(&location, offset); | |||
} | |||
return location; | |||
} | |||
/** | |||
* Called via ctx->Driver.ShaderSource() | |||
*/ | |||
@@ -1691,422 +1258,6 @@ _mesa_update_shader_textures_used(struct gl_program *prog) | |||
} | |||
/** | |||
* 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(GLcontext *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]; | |||
GLuint texUnit = ((GLuint *) values)[i]; | |||
/* check that the sampler (tex unit index) is legal */ | |||
if (texUnit >= ctx->Const.MaxTextureImageUnits) { | |||
_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++) { | |||
GLfloat *uniformVal; | |||
if (offset + k >= slots) { | |||
/* Extra array data is ignored */ | |||
break; | |||
} | |||
/* uniformVal (the destination) is always float[4] */ | |||
uniformVal = program->Parameters->ParameterValues[index + offset + k]; | |||
if (basicType == GL_INT) { | |||
/* convert user's ints to floats */ | |||
const GLint *iValues = ((const GLint *) values) + k * elems; | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = (GLfloat) iValues[i]; | |||
} | |||
} | |||
else if (basicType == GL_UNSIGNED_INT) { | |||
/* convert user's uints to floats */ | |||
const GLuint *iValues = ((const GLuint *) values) + k * elems; | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = (GLfloat) iValues[i]; | |||
} | |||
} | |||
else { | |||
const GLfloat *fValues = ((const GLfloat *) values) + k * elems; | |||
assert(basicType == GL_FLOAT); | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = fValues[i]; | |||
} | |||
} | |||
/* if the uniform is bool-valued, convert to 1.0 or 0.0 */ | |||
if (isUniformBool) { | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Called via ctx->Driver.Uniform(). | |||
*/ | |||
static void | |||
_mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, | |||
const GLvoid *values, GLenum type) | |||
{ | |||
struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; | |||
struct gl_uniform *uniform; | |||
GLint elems, offset; | |||
if (!shProg || !shProg->LinkStatus) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(program not linked)"); | |||
return; | |||
} | |||
if (location == -1) | |||
return; /* The standard specifies this as a no-op */ | |||
if (location < -1) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(location=%d)", | |||
location); | |||
return; | |||
} | |||
split_location_offset(&location, &offset); | |||
if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location=%d)", location); | |||
return; | |||
} | |||
if (count < 0) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniform(count < 0)"); | |||
return; | |||
} | |||
elems = _mesa_sizeof_glsl_type(type); | |||
FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); | |||
uniform = &shProg->Uniforms->Uniforms[location]; | |||
if (ctx->Shader.Flags & GLSL_UNIFORMS) { | |||
const GLenum basicType = base_uniform_type(type); | |||
GLint i; | |||
printf("Mesa: set program %u uniform %s (loc %d) to: ", | |||
shProg->Name, uniform->Name, location); | |||
if (basicType == GL_INT) { | |||
const GLint *v = (const GLint *) values; | |||
for (i = 0; i < count * elems; i++) { | |||
printf("%d ", v[i]); | |||
} | |||
} | |||
else if (basicType == GL_UNSIGNED_INT) { | |||
const GLuint *v = (const GLuint *) values; | |||
for (i = 0; i < count * elems; i++) { | |||
printf("%u ", v[i]); | |||
} | |||
} | |||
else { | |||
const GLfloat *v = (const GLfloat *) values; | |||
assert(basicType == GL_FLOAT); | |||
for (i = 0; i < count * elems; i++) { | |||
printf("%g ", v[i]); | |||
} | |||
} | |||
printf("\n"); | |||
} | |||
/* A uniform var may be used by both a vertex shader and a fragment | |||
* shader. We may need to update one or both shader's uniform here: | |||
*/ | |||
if (shProg->VertexProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->VertPos; | |||
if (index >= 0) { | |||
set_program_uniform(ctx, &shProg->VertexProgram->Base, | |||
index, offset, type, count, elems, values); | |||
} | |||
} | |||
if (shProg->FragmentProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->FragPos; | |||
if (index >= 0) { | |||
set_program_uniform(ctx, &shProg->FragmentProgram->Base, | |||
index, offset, type, count, elems, values); | |||
} | |||
} | |||
uniform->Initialized = GL_TRUE; | |||
} | |||
/** | |||
* Set a matrix-valued program parameter. | |||
*/ | |||
static void | |||
set_program_uniform_matrix(GLcontext *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 = 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 ctx->Driver.UniformMatrix(). | |||
* Note: cols=2, rows=4 ==> array[2] of vec4 | |||
*/ | |||
static void | |||
_mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows, | |||
GLint location, GLsizei count, | |||
GLboolean transpose, const GLfloat *values) | |||
{ | |||
struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; | |||
struct gl_uniform *uniform; | |||
GLint offset; | |||
if (!shProg || !shProg->LinkStatus) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, | |||
"glUniformMatrix(program not linked)"); | |||
return; | |||
} | |||
if (location == -1) | |||
return; /* The standard specifies this as a no-op */ | |||
if (location < -1) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(location)"); | |||
return; | |||
} | |||
split_location_offset(&location, &offset); | |||
if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)"); | |||
return; | |||
} | |||
if (values == NULL) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix"); | |||
return; | |||
} | |||
FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); | |||
uniform = &shProg->Uniforms->Uniforms[location]; | |||
if (shProg->VertexProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->VertPos; | |||
if (index >= 0) { | |||
set_program_uniform_matrix(ctx, &shProg->VertexProgram->Base, | |||
index, offset, | |||
count, rows, cols, transpose, values); | |||
} | |||
} | |||
if (shProg->FragmentProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->FragPos; | |||
if (index >= 0) { | |||
set_program_uniform_matrix(ctx, &shProg->FragmentProgram->Base, | |||
index, offset, | |||
count, rows, cols, transpose, values); | |||
} | |||
} | |||
uniform->Initialized = GL_TRUE; | |||
} | |||
/** | |||
* Validate a program's samplers. | |||
* Specifically, check that there aren't two samplers of different types | |||
@@ -2248,7 +1399,6 @@ _mesa_init_glsl_driver_functions(struct dd_function_table *driver) | |||
driver->DeleteShader = _mesa_delete_shader; | |||
driver->DetachShader = _mesa_detach_shader; | |||
driver->GetActiveAttrib = _mesa_get_active_attrib; | |||
driver->GetActiveUniform = _mesa_get_active_uniform; | |||
driver->GetAttachedShaders = _mesa_get_attached_shaders; | |||
driver->GetAttribLocation = _mesa_get_attrib_location; | |||
driver->GetHandle = _mesa_get_handle; | |||
@@ -2257,15 +1407,12 @@ _mesa_init_glsl_driver_functions(struct dd_function_table *driver) | |||
driver->GetShaderiv = _mesa_get_shaderiv; | |||
driver->GetShaderInfoLog = _mesa_get_shader_info_log; | |||
driver->GetShaderSource = _mesa_get_shader_source; | |||
driver->GetUniformfv = _mesa_get_uniformfv; | |||
driver->GetUniformiv = _mesa_get_uniformiv; | |||
driver->GetUniformLocation = _mesa_get_uniform_location; | |||
driver->IsProgram = _mesa_is_program; | |||
driver->IsShader = _mesa_is_shader; | |||
driver->LinkProgram = _mesa_link_program; | |||
driver->ShaderSource = _mesa_shader_source; | |||
driver->Uniform = _mesa_uniform; | |||
driver->UniformMatrix = _mesa_uniform_matrix; | |||
driver->UseProgram = _mesa_use_program; | |||
driver->ValidateProgram = _mesa_validate_program; | |||
_mesa_init_uniform_functions(driver); | |||
} |
@@ -75,6 +75,10 @@ extern struct gl_shader_program * | |||
_mesa_lookup_shader_program(GLcontext *ctx, GLuint name); | |||
extern struct gl_shader_program * | |||
_mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name, | |||
const char *caller); | |||
extern struct gl_shader * | |||
_mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type); | |||
@@ -0,0 +1,913 @@ | |||
/* | |||
* Mesa 3-D graphics library | |||
* | |||
* Copyright (C) 2004-2008 Brian Paul All Rights Reserved. | |||
* Copyright (C) 2009-2010 VMware, Inc. All Rights Reserved. | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a | |||
* copy of this software and associated documentation files (the "Software"), | |||
* to deal in the Software without restriction, including without limitation | |||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||
* and/or sell copies of the Software, and to permit persons to whom the | |||
* Software is furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included | |||
* in all copies or substantial portions of the Software. | |||
* | |||
* 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. | |||
*/ | |||
/** | |||
* \file uniforms.c | |||
* Functions related to GLSL uniform variables. | |||
* \author Brian Paul | |||
*/ | |||
/** | |||
* XXX things to do: | |||
* 1. Check that the right error code is generated for all _mesa_error() calls. | |||
* 2. Insert FLUSH_VERTICES calls in various places | |||
*/ | |||
#include "main/glheader.h" | |||
#include "main/context.h" | |||
#include "main/hash.h" | |||
#include "shader/program.h" | |||
#include "shader/prog_parameter.h" | |||
#include "shader/prog_statevars.h" | |||
#include "shader/prog_uniform.h" | |||
#include "shader/shader_api.h" | |||
#include "shader/slang/slang_compile.h" | |||
#include "shader/slang/slang_link.h" | |||
#include "main/dispatch.h" | |||
#include "uniforms.h" | |||
static GLenum | |||
base_uniform_type(GLenum type) | |||
{ | |||
switch (type) { | |||
#if 0 /* not needed, for now */ | |||
case GL_BOOL: | |||
case GL_BOOL_VEC2: | |||
case GL_BOOL_VEC3: | |||
case GL_BOOL_VEC4: | |||
return GL_BOOL; | |||
#endif | |||
case GL_FLOAT: | |||
case GL_FLOAT_VEC2: | |||
case GL_FLOAT_VEC3: | |||
case GL_FLOAT_VEC4: | |||
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; | |||
} | |||
} | |||
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; | |||
} | |||
} | |||
static GLboolean | |||
is_sampler_type(GLenum type) | |||
{ | |||
switch (type) { | |||
case GL_SAMPLER_1D: | |||
case GL_SAMPLER_2D: | |||
case GL_SAMPLER_3D: | |||
case GL_SAMPLER_CUBE: | |||
case GL_SAMPLER_1D_SHADOW: | |||
case GL_SAMPLER_2D_SHADOW: | |||
case GL_SAMPLER_2D_RECT_ARB: | |||
case GL_SAMPLER_2D_RECT_SHADOW_ARB: | |||
case GL_SAMPLER_1D_ARRAY_EXT: | |||
case GL_SAMPLER_2D_ARRAY_EXT: | |||
case GL_SAMPLER_1D_ARRAY_SHADOW_EXT: | |||
case GL_SAMPLER_2D_ARRAY_SHADOW_EXT: | |||
return GL_TRUE; | |||
default: | |||
return GL_FALSE; | |||
} | |||
} | |||
static struct gl_program_parameter * | |||
get_uniform_parameter(const struct gl_shader_program *shProg, GLuint index) | |||
{ | |||
const struct gl_program *prog = NULL; | |||
GLint progPos; | |||
progPos = shProg->Uniforms->Uniforms[index].VertPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->VertexProgram->Base; | |||
} | |||
else { | |||
progPos = shProg->Uniforms->Uniforms[index].FragPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->FragmentProgram->Base; | |||
} | |||
} | |||
if (!prog || progPos < 0) | |||
return NULL; /* should never happen */ | |||
return &prog->Parameters->Parameters[progPos]; | |||
} | |||
/** | |||
* Called via ctx->Driver.GetActiveUniform(). | |||
*/ | |||
static void | |||
_mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index, | |||
GLsizei maxLength, GLsizei *length, GLint *size, | |||
GLenum *type, GLchar *nameOut) | |||
{ | |||
const struct gl_shader_program *shProg; | |||
const struct gl_program *prog = NULL; | |||
const struct gl_program_parameter *param; | |||
GLint progPos; | |||
shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform"); | |||
if (!shProg) | |||
return; | |||
if (!shProg->Uniforms || index >= shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(index)"); | |||
return; | |||
} | |||
progPos = shProg->Uniforms->Uniforms[index].VertPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->VertexProgram->Base; | |||
} | |||
else { | |||
progPos = shProg->Uniforms->Uniforms[index].FragPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->FragmentProgram->Base; | |||
} | |||
} | |||
if (!prog || progPos < 0) | |||
return; /* should never happen */ | |||
ASSERT(progPos < prog->Parameters->NumParameters); | |||
param = &prog->Parameters->Parameters[progPos]; | |||
if (nameOut) { | |||
_mesa_copy_string(nameOut, maxLength, length, param->Name); | |||
} | |||
if (size) { | |||
GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); | |||
if ((GLint) param->Size > typeSize) { | |||
/* This is an array. | |||
* Array elements are placed on vector[4] boundaries so they're | |||
* a multiple of four floats. We round typeSize up to next multiple | |||
* of four to get the right size below. | |||
*/ | |||
typeSize = (typeSize + 3) & ~3; | |||
} | |||
/* Note that the returned size is in units of the <type>, not bytes */ | |||
*size = param->Size / typeSize; | |||
} | |||
if (type) { | |||
*type = param->DataType; | |||
} | |||
} | |||
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; | |||
} | |||
} | |||
/** | |||
* 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 */ | |||
if (p->Size <= 4) { | |||
*rows = 1; | |||
*cols = p->Size; | |||
} | |||
else { | |||
*rows = p->Size / 4 + 1; | |||
if (p->Size % 4 == 0) | |||
*cols = 4; | |||
else | |||
*cols = p->Size % 4; | |||
} | |||
} | |||
} | |||
/** | |||
* Helper for get_uniform[fi]v() functions. | |||
* Given a shader program name and uniform location, return a pointer | |||
* to the shader program and return the program parameter position. | |||
*/ | |||
static void | |||
lookup_uniform_parameter(GLcontext *ctx, GLuint program, GLint location, | |||
struct gl_program **progOut, GLint *paramPosOut) | |||
{ | |||
struct gl_shader_program *shProg | |||
= _mesa_lookup_shader_program_err(ctx, program, "glGetUniform[if]v"); | |||
struct gl_program *prog = NULL; | |||
GLint progPos = -1; | |||
/* if shProg is NULL, we'll have already recorded an error */ | |||
if (shProg) { | |||
if (!shProg->Uniforms || | |||
location < 0 || | |||
location >= (GLint) shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(location)"); | |||
} | |||
else { | |||
/* OK, find the gl_program and program parameter location */ | |||
progPos = shProg->Uniforms->Uniforms[location].VertPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->VertexProgram->Base; | |||
} | |||
else { | |||
progPos = shProg->Uniforms->Uniforms[location].FragPos; | |||
if (progPos >= 0) { | |||
prog = &shProg->FragmentProgram->Base; | |||
} | |||
} | |||
} | |||
} | |||
*progOut = prog; | |||
*paramPosOut = progPos; | |||
} | |||
/** | |||
* Called via ctx->Driver.GetUniformfv(). | |||
*/ | |||
static void | |||
_mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, | |||
GLfloat *params) | |||
{ | |||
struct gl_program *prog; | |||
GLint paramPos; | |||
lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); | |||
if (prog) { | |||
const struct gl_program_parameter *p = | |||
&prog->Parameters->Parameters[paramPos]; | |||
GLint rows, cols, i, j, k; | |||
get_uniform_rows_cols(p, &rows, &cols); | |||
k = 0; | |||
for (i = 0; i < rows; i++) { | |||
for (j = 0; j < cols; j++ ) { | |||
params[k++] = prog->Parameters->ParameterValues[paramPos+i][j]; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Called via ctx->Driver.GetUniformiv(). | |||
* \sa _mesa_get_uniformfv, only difference is a cast. | |||
*/ | |||
static void | |||
_mesa_get_uniformiv(GLcontext *ctx, GLuint program, GLint location, | |||
GLint *params) | |||
{ | |||
struct gl_program *prog; | |||
GLint paramPos; | |||
lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); | |||
if (prog) { | |||
const struct gl_program_parameter *p = | |||
&prog->Parameters->Parameters[paramPos]; | |||
GLint rows, cols, i, j, k; | |||
get_uniform_rows_cols(p, &rows, &cols); | |||
k = 0; | |||
for (i = 0; i < rows; i++) { | |||
for (j = 0; j < cols; j++ ) { | |||
params[k++] = (GLint) prog->Parameters->ParameterValues[paramPos+i][j]; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* The value returned by GetUniformLocation actually encodes two things: | |||
* 1. the index into the prog->Uniforms[] array for the uniform | |||
* 2. an offset in the prog->ParameterValues[] array for specifying array | |||
* elements or structure fields. | |||
* This function merges those two values. | |||
*/ | |||
static void | |||
merge_location_offset(GLint *location, GLint offset) | |||
{ | |||
*location = *location | (offset << 16); | |||
} | |||
/** | |||
* Seperate the uniform location and parameter offset. See above. | |||
*/ | |||
static void | |||
split_location_offset(GLint *location, GLint *offset) | |||
{ | |||
*offset = (*location >> 16); | |||
*location = *location & 0xffff; | |||
} | |||
/** | |||
* Called via ctx->Driver.GetUniformLocation(). | |||
* | |||
* The return value will encode two values, the uniform location and an | |||
* offset (used for arrays, structs). | |||
*/ | |||
static GLint | |||
_mesa_get_uniform_location(GLcontext *ctx, GLuint program, const GLchar *name) | |||
{ | |||
GLint offset = 0, location = -1; | |||
struct gl_shader_program *shProg = | |||
_mesa_lookup_shader_program_err(ctx, program, "glGetUniformLocation"); | |||
if (!shProg) | |||
return -1; | |||
if (shProg->LinkStatus == GL_FALSE) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(program)"); | |||
return -1; | |||
} | |||
/* XXX we should return -1 if the uniform was declared, but not | |||
* actually used. | |||
*/ | |||
/* XXX we need to be able to parse uniform names for structs and arrays | |||
* such as: | |||
* mymatrix[1] | |||
* mystruct.field1 | |||
*/ | |||
{ | |||
/* handle 1-dimension arrays here... */ | |||
char *c = strchr(name, '['); | |||
if (c) { | |||
/* truncate name at [ */ | |||
const GLint len = c - name; | |||
GLchar *newName = malloc(len + 1); | |||
if (!newName) | |||
return -1; /* out of mem */ | |||
memcpy(newName, name, len); | |||
newName[len] = 0; | |||
location = _mesa_lookup_uniform(shProg->Uniforms, newName); | |||
if (location >= 0) { | |||
const GLint element = atoi(c + 1); | |||
if (element > 0) { | |||
/* get type of the uniform array element */ | |||
struct gl_program_parameter *p; | |||
p = get_uniform_parameter(shProg, location); | |||
if (p) { | |||
GLint rows, cols; | |||
get_matrix_dims(p->DataType, &rows, &cols); | |||
if (rows < 1) | |||
rows = 1; | |||
offset = element * rows; | |||
} | |||
} | |||
} | |||
free(newName); | |||
} | |||
} | |||
if (location < 0) { | |||
location = _mesa_lookup_uniform(shProg->Uniforms, name); | |||
} | |||
if (location >= 0) { | |||
merge_location_offset(&location, offset); | |||
} | |||
return location; | |||
} | |||
/** | |||
* 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(GLcontext *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]; | |||
GLuint texUnit = ((GLuint *) values)[i]; | |||
/* check that the sampler (tex unit index) is legal */ | |||
if (texUnit >= ctx->Const.MaxTextureImageUnits) { | |||
_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++) { | |||
GLfloat *uniformVal; | |||
if (offset + k >= slots) { | |||
/* Extra array data is ignored */ | |||
break; | |||
} | |||
/* uniformVal (the destination) is always float[4] */ | |||
uniformVal = program->Parameters->ParameterValues[index + offset + k]; | |||
if (basicType == GL_INT) { | |||
/* convert user's ints to floats */ | |||
const GLint *iValues = ((const GLint *) values) + k * elems; | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = (GLfloat) iValues[i]; | |||
} | |||
} | |||
else if (basicType == GL_UNSIGNED_INT) { | |||
/* convert user's uints to floats */ | |||
const GLuint *iValues = ((const GLuint *) values) + k * elems; | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = (GLfloat) iValues[i]; | |||
} | |||
} | |||
else { | |||
const GLfloat *fValues = ((const GLfloat *) values) + k * elems; | |||
assert(basicType == GL_FLOAT); | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = fValues[i]; | |||
} | |||
} | |||
/* if the uniform is bool-valued, convert to 1.0 or 0.0 */ | |||
if (isUniformBool) { | |||
for (i = 0; i < elems; i++) { | |||
uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Called via ctx->Driver.Uniform(). | |||
*/ | |||
static void | |||
_mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, | |||
const GLvoid *values, GLenum type) | |||
{ | |||
struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; | |||
struct gl_uniform *uniform; | |||
GLint elems, offset; | |||
if (!shProg || !shProg->LinkStatus) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(program not linked)"); | |||
return; | |||
} | |||
if (location == -1) | |||
return; /* The standard specifies this as a no-op */ | |||
if (location < -1) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(location=%d)", | |||
location); | |||
return; | |||
} | |||
split_location_offset(&location, &offset); | |||
if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location=%d)", location); | |||
return; | |||
} | |||
if (count < 0) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniform(count < 0)"); | |||
return; | |||
} | |||
elems = _mesa_sizeof_glsl_type(type); | |||
FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); | |||
uniform = &shProg->Uniforms->Uniforms[location]; | |||
if (ctx->Shader.Flags & GLSL_UNIFORMS) { | |||
const GLenum basicType = base_uniform_type(type); | |||
GLint i; | |||
printf("Mesa: set program %u uniform %s (loc %d) to: ", | |||
shProg->Name, uniform->Name, location); | |||
if (basicType == GL_INT) { | |||
const GLint *v = (const GLint *) values; | |||
for (i = 0; i < count * elems; i++) { | |||
printf("%d ", v[i]); | |||
} | |||
} | |||
else if (basicType == GL_UNSIGNED_INT) { | |||
const GLuint *v = (const GLuint *) values; | |||
for (i = 0; i < count * elems; i++) { | |||
printf("%u ", v[i]); | |||
} | |||
} | |||
else { | |||
const GLfloat *v = (const GLfloat *) values; | |||
assert(basicType == GL_FLOAT); | |||
for (i = 0; i < count * elems; i++) { | |||
printf("%g ", v[i]); | |||
} | |||
} | |||
printf("\n"); | |||
} | |||
/* A uniform var may be used by both a vertex shader and a fragment | |||
* shader. We may need to update one or both shader's uniform here: | |||
*/ | |||
if (shProg->VertexProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->VertPos; | |||
if (index >= 0) { | |||
set_program_uniform(ctx, &shProg->VertexProgram->Base, | |||
index, offset, type, count, elems, values); | |||
} | |||
} | |||
if (shProg->FragmentProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->FragPos; | |||
if (index >= 0) { | |||
set_program_uniform(ctx, &shProg->FragmentProgram->Base, | |||
index, offset, type, count, elems, values); | |||
} | |||
} | |||
uniform->Initialized = GL_TRUE; | |||
} | |||
/** | |||
* Set a matrix-valued program parameter. | |||
*/ | |||
static void | |||
set_program_uniform_matrix(GLcontext *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 = 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 ctx->Driver.UniformMatrix(). | |||
* Note: cols=2, rows=4 ==> array[2] of vec4 | |||
*/ | |||
static void | |||
_mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows, | |||
GLint location, GLsizei count, | |||
GLboolean transpose, const GLfloat *values) | |||
{ | |||
struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; | |||
struct gl_uniform *uniform; | |||
GLint offset; | |||
if (!shProg || !shProg->LinkStatus) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, | |||
"glUniformMatrix(program not linked)"); | |||
return; | |||
} | |||
if (location == -1) | |||
return; /* The standard specifies this as a no-op */ | |||
if (location < -1) { | |||
_mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(location)"); | |||
return; | |||
} | |||
split_location_offset(&location, &offset); | |||
if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)"); | |||
return; | |||
} | |||
if (values == NULL) { | |||
_mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix"); | |||
return; | |||
} | |||
FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); | |||
uniform = &shProg->Uniforms->Uniforms[location]; | |||
if (shProg->VertexProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->VertPos; | |||
if (index >= 0) { | |||
set_program_uniform_matrix(ctx, &shProg->VertexProgram->Base, | |||
index, offset, | |||
count, rows, cols, transpose, values); | |||
} | |||
} | |||
if (shProg->FragmentProgram) { | |||
/* convert uniform location to program parameter index */ | |||
GLint index = uniform->FragPos; | |||
if (index >= 0) { | |||
set_program_uniform_matrix(ctx, &shProg->FragmentProgram->Base, | |||
index, offset, | |||
count, rows, cols, transpose, values); | |||
} | |||
} | |||
uniform->Initialized = GL_TRUE; | |||
} | |||
void | |||
_mesa_init_uniform_functions(struct dd_function_table *driver) | |||
{ | |||
driver->GetActiveUniform = _mesa_get_active_uniform; | |||
driver->GetUniformfv = _mesa_get_uniformfv; | |||
driver->GetUniformiv = _mesa_get_uniformiv; | |||
driver->GetUniformLocation = _mesa_get_uniform_location; | |||
driver->Uniform = _mesa_uniform; | |||
driver->UniformMatrix = _mesa_uniform_matrix; | |||
} |
@@ -0,0 +1,33 @@ | |||
/* | |||
* Mesa 3-D graphics library | |||
* | |||
* Copyright (C) 2010 VMware, Inc. All Rights Reserved. | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a | |||
* copy of this software and associated documentation files (the "Software"), | |||
* to deal in the Software without restriction, including without limitation | |||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||
* and/or sell copies of the Software, and to permit persons to whom the | |||
* Software is furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included | |||
* in all copies or substantial portions of the Software. | |||
* | |||
* 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 | |||
* THE AUTHORS 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. | |||
*/ | |||
#ifndef UNIFORMS_H | |||
#define UNIFORMS_H | |||
extern void | |||
_mesa_init_uniform_functions(struct dd_function_table *driver); | |||
#endif /* UNIFORMS_H */ |
@@ -247,7 +247,8 @@ SHADER_SOURCES = \ | |||
shader/prog_uniform.c \ | |||
shader/programopt.c \ | |||
shader/symbol_table.c \ | |||
shader/shader_api.c | |||
shader/shader_api.c \ | |||
shader/uniforms.c | |||
SLANG_SOURCES = \ | |||
shader/slang/slang_builtin.c \ |