-#include "program/prog_parameter.h"
-#include "program/prog_statevars.h"
-#include "program/prog_uniform.h"
-#include "program/prog_instruction.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;
- }
-}
-
-
-/**
- * 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;
-
- if (!shProg->Uniforms ||
- index < 0 ||
- index >= (GLint) shProg->Uniforms->NumUniforms) {
- return GL_FALSE;
- }
-
- pos = shProg->Uniforms->Uniforms[index].VertPos;
- if (pos >= 0) {
- prog = &shProg->VertexProgram->Base;
- }
- else {
- pos = shProg->Uniforms->Uniforms[index].FragPos;
- if (pos >= 0) {
- prog = &shProg->FragmentProgram->Base;
- }
- else {
- pos = shProg->Uniforms->Uniforms[index].GeomPos;
- if (pos >= 0) {
- prog = &shProg->GeometryProgram->Base;
- }
- }
- }
-
- if (!prog || pos < 0)
- return GL_FALSE; /* should really never happen */
-
- *progOut = prog;
- *posOut = pos;
-
- return GL_TRUE;
-}
-
-
-/**
- * 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
- */
-static const struct gl_program_parameter *
-get_uniform_parameter(struct gl_shader_program *shProg, GLint index)
-{
- struct gl_program *prog;
- GLint progPos;
-
- if (find_uniform_parameter_pos(shProg, index, &prog, &progPos))
- return &prog->Parameters->Parameters[progPos];
- else
- return NULL;
-}
-
-
-/**
- * Called by glGetActiveUniform().
- */
-static void
-_mesa_get_active_uniform(struct gl_context *ctx, GLuint program, GLuint index,
- GLsizei maxLength, GLsizei *length, GLint *size,
- GLenum *type, GLchar *nameOut)
-{
- struct gl_shader_program *shProg =
- _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
- const struct gl_program_parameter *param;
-
- if (!shProg)
- return;
-
- if (!shProg->Uniforms || index >= shProg->Uniforms->NumUniforms) {
- _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(index)");
- return;
- }
-
- param = get_uniform_parameter(shProg, index);
- if (!param)
- return;
-
- 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 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_FLOAT_VEC2:
- case GL_INT_VEC2:
- case GL_BOOL_VEC2:
- case GL_UNSIGNED_INT_VEC2:
- return 2;
-
- case GL_FLOAT_VEC3:
- case GL_INT_VEC3:
- case GL_BOOL_VEC3:
- case GL_UNSIGNED_INT_VEC3:
- return 3;
-
- case GL_FLOAT_VEC4:
- case GL_INT_VEC4:
- case GL_BOOL_VEC4:
- case GL_UNSIGNED_INT_VEC4:
- return 4;
- }
-}
-
-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 */
- *rows = 1;
- *cols = get_vector_elements(p->DataType);
- }
-}
-
-
-/**
- * GLSL uniform arrays and structs require special handling.
- *
- * The GL_ARB_shader_objects spec says that if you use
- * glGetUniformLocation to get the location of an array, you CANNOT
- * access other elements of the array by adding an offset to the
- * returned location. For example, you must call
- * glGetUniformLocation("foo[16]") if you want to set the 16th element
- * of the array with glUniform().
- *
- * HOWEVER, some other OpenGL drivers allow accessing array elements
- * by adding an offset to the returned array location. And some apps
- * seem to depend on that behaviour.
- *
- * Mesa's gl_uniform_list doesn't directly support this since each
- * entry in the list describes one uniform variable, not one uniform
- * element. We could insert dummy entries in the list for each array
- * element after [0] but that causes complications elsewhere.
- *
- * We solve this problem by encoding two values in the location that's
- * returned by glGetUniformLocation():
- * a) index into gl_uniform_list::Uniforms[] for the uniform
- * b) an array/field offset (0 for simple types)
- *
- * These two values are encoded in the high and low halves of a GLint.
- * By putting the uniform number in the high part and the offset in the
- * low part, we can support the unofficial ability to index into arrays
- * by adding offsets to the location value.
- */
-static void
-merge_location_offset(GLint *location, GLint offset)
-{
- *location = (*location << 16) | offset;
-}
-
-
-/**
- * Separate the uniform location and parameter offset. See above.
- */
-static void
-split_location_offset(GLint *location, GLint *offset)
-{
- *offset = *location & 0xffff;
- *location = *location >> 16;
-}
-
-
-
-/**
- * Called via glGetUniform[fiui]v() to get the current value of a uniform.
- */
-static void
-get_uniform(struct gl_context *ctx, GLuint program, GLint location,
- GLsizei bufSize, GLenum returnType, GLvoid *paramsOut)
-{
- struct gl_shader_program *shProg =
- _mesa_lookup_shader_program_err(ctx, program, "glGetUniformfv");
- struct gl_program *prog;
- GLint paramPos, offset;
-
- if (!shProg)
- return;
-
- split_location_offset(&location, &offset);
-
- if (!find_uniform_parameter_pos(shProg, location, &prog, ¶mPos)) {
- _mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(location)");
- }
- else {
- const struct gl_program_parameter *p =
- &prog->Parameters->Parameters[paramPos];
- GLint rows, cols, i, j, k;
- GLsizei numBytes;
-
- get_uniform_rows_cols(p, &rows, &cols);
-
- numBytes = rows * cols * _mesa_sizeof_type(returnType);
- if (bufSize < numBytes) {
- _mesa_error( ctx, GL_INVALID_OPERATION,
- "glGetnUniformfvARB(out of bounds: bufSize is %d,"
- " but %d bytes are required)", bufSize, numBytes );
- return;
- }
-
- switch (returnType) {
- case GL_FLOAT:
- {
- GLfloat *params = (GLfloat *) paramsOut;
- k = 0;
- for (i = 0; i < rows; i++) {
- const int base = paramPos + offset + i;
- for (j = 0; j < cols; j++ ) {
- params[k++] = prog->Parameters->ParameterValues[base][j];
- }
- }
- }
- break;
- case GL_DOUBLE:
- {
- GLfloat *params = (GLfloat *) paramsOut;
- k = 0;
- for (i = 0; i < rows; i++) {
- const int base = paramPos + offset + i;
- for (j = 0; j < cols; j++ ) {
- params[k++] = (GLdouble)
- prog->Parameters->ParameterValues[base][j];
- }
- }
- }
- break;
- case GL_INT:
- {
- GLint *params = (GLint *) paramsOut;
- k = 0;
- for (i = 0; i < rows; i++) {
- const int base = paramPos + offset + i;
- for (j = 0; j < cols; j++ ) {
- params[k++] = (GLint)
- prog->Parameters->ParameterValues[base][j];
- }
- }
- }
- break;
- case GL_UNSIGNED_INT:
- {
- GLuint *params = (GLuint *) paramsOut;
- k = 0;
- for (i = 0; i < rows; i++) {
- const int base = paramPos + offset + i;
- for (j = 0; j < cols; j++ ) {
- params[k++] = (GLuint)
- prog->Parameters->ParameterValues[base][j];
- }
- }
- }
- break;
- default:
- _mesa_problem(ctx, "bad returnType in get_uniform()");
- }
- }
-}
-
-
-/**
- * Called via glGetUniformLocation().
- *
- * The return value will encode two values, the uniform location and an
- * offset (used for arrays, structs).
- */
-GLint
-_mesa_get_uniform_location(struct gl_context *ctx,
- struct gl_shader_program *shProg,
- const GLchar *name)
-{
- GLint offset = 0, location = -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 */
- const struct gl_program_parameter *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;
-}
-
-