| /* |
| * 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 |
| * 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. |
| */ |
| |
| /** |
| * \file shaderapi.c |
| * \author Brian Paul |
| * |
| * Implementation of GLSL-related API functions. |
| * The glUniform* functions are in uniforms.c |
| * |
| * |
| * 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 <stdbool.h> |
| #include "main/glheader.h" |
| #include "main/context.h" |
| #include "main/dispatch.h" |
| #include "main/enums.h" |
| #include "main/hash.h" |
| #include "main/mtypes.h" |
| #include "main/pipelineobj.h" |
| #include "main/shaderapi.h" |
| #include "main/shaderobj.h" |
| #include "main/transformfeedback.h" |
| #include "main/uniforms.h" |
| #include "compiler/glsl/glsl_parser_extras.h" |
| #include "compiler/glsl/ir.h" |
| #include "compiler/glsl/ir_uniform.h" |
| #include "compiler/glsl/program.h" |
| #include "program/program.h" |
| #include "program/prog_print.h" |
| #include "program/prog_parameter.h" |
| #include "util/ralloc.h" |
| #include "util/hash_table.h" |
| #include "util/mesa-sha1.h" |
| #include "util/crc32.h" |
| |
| /** |
| * Return mask of GLSL_x flags by examining the MESA_GLSL env var. |
| */ |
| GLbitfield |
| _mesa_get_shader_flags(void) |
| { |
| GLbitfield flags = 0x0; |
| const char *env = getenv("MESA_GLSL"); |
| |
| if (env) { |
| if (strstr(env, "dump_on_error")) |
| flags |= GLSL_DUMP_ON_ERROR; |
| else if (strstr(env, "dump")) |
| flags |= GLSL_DUMP; |
| if (strstr(env, "log")) |
| flags |= GLSL_LOG; |
| if (strstr(env, "nopvert")) |
| flags |= GLSL_NOP_VERT; |
| if (strstr(env, "nopfrag")) |
| flags |= GLSL_NOP_FRAG; |
| if (strstr(env, "nopt")) |
| flags |= GLSL_NO_OPT; |
| else if (strstr(env, "opt")) |
| flags |= GLSL_OPT; |
| if (strstr(env, "uniform")) |
| flags |= GLSL_UNIFORMS; |
| if (strstr(env, "useprog")) |
| flags |= GLSL_USE_PROG; |
| if (strstr(env, "errors")) |
| flags |= GLSL_REPORT_ERRORS; |
| } |
| |
| return flags; |
| } |
| |
| /** |
| * Memoized version of getenv("MESA_SHADER_CAPTURE_PATH"). |
| */ |
| const char * |
| _mesa_get_shader_capture_path(void) |
| { |
| static bool read_env_var = false; |
| static const char *path = NULL; |
| |
| if (!read_env_var) { |
| path = getenv("MESA_SHADER_CAPTURE_PATH"); |
| read_env_var = true; |
| } |
| |
| return path; |
| } |
| |
| /** |
| * Initialize context's shader state. |
| */ |
| void |
| _mesa_init_shader_state(struct gl_context *ctx) |
| { |
| /* Device drivers may override these to control what kind of instructions |
| * are generated by the GLSL compiler. |
| */ |
| struct gl_shader_compiler_options options; |
| gl_shader_stage sh; |
| int i; |
| |
| memset(&options, 0, sizeof(options)); |
| options.MaxUnrollIterations = 32; |
| options.MaxIfDepth = UINT_MAX; |
| |
| for (sh = 0; sh < MESA_SHADER_STAGES; ++sh) |
| memcpy(&ctx->Const.ShaderCompilerOptions[sh], &options, sizeof(options)); |
| |
| ctx->Shader.Flags = _mesa_get_shader_flags(); |
| |
| if (ctx->Shader.Flags != 0) |
| ctx->Const.GenerateTemporaryNames = true; |
| |
| /* Extended for ARB_separate_shader_objects */ |
| ctx->Shader.RefCount = 1; |
| mtx_init(&ctx->Shader.Mutex, mtx_plain); |
| |
| ctx->TessCtrlProgram.patch_vertices = 3; |
| for (i = 0; i < 4; ++i) |
| ctx->TessCtrlProgram.patch_default_outer_level[i] = 1.0; |
| for (i = 0; i < 2; ++i) |
| ctx->TessCtrlProgram.patch_default_inner_level[i] = 1.0; |
| } |
| |
| |
| /** |
| * Free the per-context shader-related state. |
| */ |
| void |
| _mesa_free_shader_state(struct gl_context *ctx) |
| { |
| int i; |
| for (i = 0; i < MESA_SHADER_STAGES; i++) { |
| _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram[i], |
| NULL); |
| } |
| _mesa_reference_program(ctx, &ctx->Shader._CurrentFragmentProgram, NULL); |
| _mesa_reference_shader_program(ctx, &ctx->Shader.ActiveProgram, NULL); |
| |
| /* Extended for ARB_separate_shader_objects */ |
| _mesa_reference_pipeline_object(ctx, &ctx->_Shader, NULL); |
| |
| assert(ctx->Shader.RefCount == 1); |
| mtx_destroy(&ctx->Shader.Mutex); |
| } |
| |
| |
| /** |
| * Copy string from <src> to <dst>, up to maxLength characters, returning |
| * length of <dst> in <length>. |
| * \param src the strings source |
| * \param maxLength max chars to copy |
| * \param length returns number of chars copied |
| * \param dst the string destination |
| */ |
| void |
| _mesa_copy_string(GLchar *dst, GLsizei maxLength, |
| GLsizei *length, const GLchar *src) |
| { |
| GLsizei len; |
| for (len = 0; len < maxLength - 1 && src && src[len]; len++) |
| dst[len] = src[len]; |
| if (maxLength > 0) |
| dst[len] = 0; |
| if (length) |
| *length = len; |
| } |
| |
| |
| |
| /** |
| * Confirm that the a shader type is valid and supported by the implementation |
| * |
| * \param ctx Current GL context |
| * \param type Shader target |
| * |
| */ |
| bool |
| _mesa_validate_shader_target(const struct gl_context *ctx, GLenum type) |
| { |
| /* Note: when building built-in GLSL functions, this function may be |
| * invoked with ctx == NULL. In that case, we can only validate that it's |
| * a shader target we recognize, not that it's supported in the current |
| * context. But that's fine--we don't need any further validation than |
| * that when building built-in GLSL functions. |
| */ |
| |
| switch (type) { |
| case GL_FRAGMENT_SHADER: |
| return ctx == NULL || ctx->Extensions.ARB_fragment_shader; |
| case GL_VERTEX_SHADER: |
| return ctx == NULL || ctx->Extensions.ARB_vertex_shader; |
| case GL_GEOMETRY_SHADER_ARB: |
| return ctx == NULL || _mesa_has_geometry_shaders(ctx); |
| case GL_TESS_CONTROL_SHADER: |
| case GL_TESS_EVALUATION_SHADER: |
| return ctx == NULL || _mesa_has_tessellation(ctx); |
| case GL_COMPUTE_SHADER: |
| return ctx == NULL || _mesa_has_compute_shaders(ctx); |
| default: |
| return false; |
| } |
| } |
| |
| |
| static GLboolean |
| is_program(struct gl_context *ctx, GLuint name) |
| { |
| struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, name); |
| return shProg ? GL_TRUE : GL_FALSE; |
| } |
| |
| |
| static GLboolean |
| is_shader(struct gl_context *ctx, GLuint name) |
| { |
| struct gl_shader *shader = _mesa_lookup_shader(ctx, name); |
| return shader ? GL_TRUE : GL_FALSE; |
| } |
| |
| |
| /** |
| * Attach shader to a shader program. |
| */ |
| static void |
| attach_shader(struct gl_context *ctx, GLuint program, GLuint shader) |
| { |
| struct gl_shader_program *shProg; |
| struct gl_shader *sh; |
| GLuint i, n; |
| |
| const bool same_type_disallowed = _mesa_is_gles(ctx); |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, "glAttachShader"); |
| if (!shProg) |
| return; |
| |
| sh = _mesa_lookup_shader_err(ctx, shader, "glAttachShader"); |
| if (!sh) { |
| return; |
| } |
| |
| n = shProg->NumShaders; |
| for (i = 0; i < n; i++) { |
| if (shProg->Shaders[i] == sh) { |
| /* The shader is already attched to this program. The |
| * GL_ARB_shader_objects spec says: |
| * |
| * "The error INVALID_OPERATION is generated by AttachObjectARB |
| * if <obj> is already attached to <containerObj>." |
| */ |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glAttachShader"); |
| return; |
| } else if (same_type_disallowed && |
| shProg->Shaders[i]->Stage == sh->Stage) { |
| /* Shader with the same type is already attached to this program, |
| * OpenGL ES 2.0 and 3.0 specs say: |
| * |
| * "Multiple shader objects of the same type may not be attached |
| * to a single program object. [...] The error INVALID_OPERATION |
| * is generated if [...] another shader object of the same type |
| * as shader is already attached to program." |
| */ |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glAttachShader"); |
| return; |
| } |
| } |
| |
| /* grow list */ |
| shProg->Shaders = realloc(shProg->Shaders, |
| (n + 1) * sizeof(struct gl_shader *)); |
| if (!shProg->Shaders) { |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAttachShader"); |
| return; |
| } |
| |
| /* append */ |
| shProg->Shaders[n] = NULL; /* since realloc() didn't zero the new space */ |
| _mesa_reference_shader(ctx, &shProg->Shaders[n], sh); |
| shProg->NumShaders++; |
| } |
| |
| |
| static GLuint |
| create_shader(struct gl_context *ctx, GLenum type) |
| { |
| struct gl_shader *sh; |
| GLuint name; |
| |
| if (!_mesa_validate_shader_target(ctx, type)) { |
| _mesa_error(ctx, GL_INVALID_ENUM, "CreateShader(%s)", |
| _mesa_enum_to_string(type)); |
| return 0; |
| } |
| |
| _mesa_HashLockMutex(ctx->Shared->ShaderObjects); |
| name = _mesa_HashFindFreeKeyBlock(ctx->Shared->ShaderObjects, 1); |
| sh = _mesa_new_shader(name, _mesa_shader_enum_to_shader_stage(type)); |
| sh->Type = type; |
| _mesa_HashInsertLocked(ctx->Shared->ShaderObjects, name, sh); |
| _mesa_HashUnlockMutex(ctx->Shared->ShaderObjects); |
| |
| return name; |
| } |
| |
| |
| static GLuint |
| create_shader_program(struct gl_context *ctx) |
| { |
| GLuint name; |
| struct gl_shader_program *shProg; |
| |
| _mesa_HashLockMutex(ctx->Shared->ShaderObjects); |
| |
| name = _mesa_HashFindFreeKeyBlock(ctx->Shared->ShaderObjects, 1); |
| |
| shProg = _mesa_new_shader_program(name); |
| |
| _mesa_HashInsertLocked(ctx->Shared->ShaderObjects, name, shProg); |
| |
| assert(shProg->RefCount == 1); |
| |
| _mesa_HashUnlockMutex(ctx->Shared->ShaderObjects); |
| |
| return name; |
| } |
| |
| |
| /** |
| * Delete a shader program. Actually, just decrement the program's |
| * reference count and mark it as DeletePending. |
| * Used to implement glDeleteProgram() and glDeleteObjectARB(). |
| */ |
| static void |
| delete_shader_program(struct gl_context *ctx, GLuint name) |
| { |
| /* |
| * NOTE: deleting shaders/programs works a bit differently than |
| * texture objects (and buffer objects, etc). Shader/program |
| * handles/IDs exist in the hash table until the object is really |
| * deleted (refcount==0). With texture objects, the handle/ID is |
| * removed from the hash table in glDeleteTextures() while the tex |
| * object itself might linger until its refcount goes to zero. |
| */ |
| struct gl_shader_program *shProg; |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, name, "glDeleteProgram"); |
| if (!shProg) |
| return; |
| |
| if (!shProg->DeletePending) { |
| shProg->DeletePending = GL_TRUE; |
| |
| /* effectively, decr shProg's refcount */ |
| _mesa_reference_shader_program(ctx, &shProg, NULL); |
| } |
| } |
| |
| |
| static void |
| delete_shader(struct gl_context *ctx, GLuint shader) |
| { |
| struct gl_shader *sh; |
| |
| sh = _mesa_lookup_shader_err(ctx, shader, "glDeleteShader"); |
| if (!sh) |
| return; |
| |
| if (!sh->DeletePending) { |
| sh->DeletePending = GL_TRUE; |
| |
| /* effectively, decr sh's refcount */ |
| _mesa_reference_shader(ctx, &sh, NULL); |
| } |
| } |
| |
| |
| static void |
| detach_shader(struct gl_context *ctx, GLuint program, GLuint shader) |
| { |
| struct gl_shader_program *shProg; |
| GLuint n; |
| GLuint i, j; |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, "glDetachShader"); |
| if (!shProg) |
| return; |
| |
| n = shProg->NumShaders; |
| |
| for (i = 0; i < n; i++) { |
| if (shProg->Shaders[i]->Name == shader) { |
| /* found it */ |
| struct gl_shader **newList; |
| |
| /* release */ |
| _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL); |
| |
| /* alloc new, smaller array */ |
| newList = malloc((n - 1) * sizeof(struct gl_shader *)); |
| if (!newList) { |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDetachShader"); |
| return; |
| } |
| /* Copy old list entries to new list, skipping removed entry at [i] */ |
| for (j = 0; j < i; j++) { |
| newList[j] = shProg->Shaders[j]; |
| } |
| while (++i < n) { |
| newList[j++] = shProg->Shaders[i]; |
| } |
| |
| /* Free old list and install new one */ |
| free(shProg->Shaders); |
| shProg->Shaders = newList; |
| shProg->NumShaders = n - 1; |
| |
| #ifdef DEBUG |
| /* sanity check - make sure the new list's entries are sensible */ |
| for (j = 0; j < shProg->NumShaders; j++) { |
| assert(shProg->Shaders[j]->Stage == MESA_SHADER_VERTEX || |
| shProg->Shaders[j]->Stage == MESA_SHADER_TESS_CTRL || |
| shProg->Shaders[j]->Stage == MESA_SHADER_TESS_EVAL || |
| shProg->Shaders[j]->Stage == MESA_SHADER_GEOMETRY || |
| shProg->Shaders[j]->Stage == MESA_SHADER_FRAGMENT); |
| assert(shProg->Shaders[j]->RefCount > 0); |
| } |
| #endif |
| |
| return; |
| } |
| } |
| |
| /* not found */ |
| { |
| GLenum err; |
| if (is_shader(ctx, shader) || is_program(ctx, shader)) |
| err = GL_INVALID_OPERATION; |
| else |
| err = GL_INVALID_VALUE; |
| _mesa_error(ctx, err, "glDetachShader(shader)"); |
| return; |
| } |
| } |
| |
| |
| /** |
| * Return list of shaders attached to shader program. |
| */ |
| static void |
| get_attached_shaders(struct gl_context *ctx, GLuint program, GLsizei maxCount, |
| GLsizei *count, GLuint *obj) |
| { |
| struct gl_shader_program *shProg; |
| |
| if (maxCount < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetAttachedShaders(maxCount < 0)"); |
| return; |
| } |
| |
| shProg = |
| _mesa_lookup_shader_program_err(ctx, program, "glGetAttachedShaders"); |
| |
| if (shProg) { |
| GLuint i; |
| for (i = 0; i < (GLuint) maxCount && i < shProg->NumShaders; i++) { |
| obj[i] = shProg->Shaders[i]->Name; |
| } |
| if (count) |
| *count = i; |
| } |
| } |
| |
| |
| /** |
| * glGetHandleARB() - return ID/name of currently bound shader program. |
| */ |
| static GLuint |
| get_handle(struct gl_context *ctx, GLenum pname) |
| { |
| if (pname == GL_PROGRAM_OBJECT_ARB) { |
| if (ctx->_Shader->ActiveProgram) |
| return ctx->_Shader->ActiveProgram->Name; |
| else |
| return 0; |
| } |
| else { |
| _mesa_error(ctx, GL_INVALID_ENUM, "glGetHandleARB"); |
| return 0; |
| } |
| } |
| |
| |
| /** |
| * Check if a geometry shader query is valid at this time. If not, report an |
| * error and return false. |
| * |
| * From GL 3.2 section 6.1.16 (Shader and Program Queries): |
| * |
| * "If GEOMETRY_VERTICES_OUT, GEOMETRY_INPUT_TYPE, or GEOMETRY_OUTPUT_TYPE |
| * are queried for a program which has not been linked successfully, or |
| * which does not contain objects to form a geometry shader, then an |
| * INVALID_OPERATION error is generated." |
| */ |
| static bool |
| check_gs_query(struct gl_context *ctx, const struct gl_shader_program *shProg) |
| { |
| if (shProg->data->LinkStatus && |
| shProg->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { |
| return true; |
| } |
| |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glGetProgramv(linked geometry shader required)"); |
| return false; |
| } |
| |
| |
| /** |
| * Check if a tessellation control shader query is valid at this time. |
| * If not, report an error and return false. |
| * |
| * From GL 4.0 section 6.1.12 (Shader and Program Queries): |
| * |
| * "If TESS_CONTROL_OUTPUT_VERTICES is queried for a program which has |
| * not been linked successfully, or which does not contain objects to |
| * form a tessellation control shader, then an INVALID_OPERATION error is |
| * generated." |
| */ |
| static bool |
| check_tcs_query(struct gl_context *ctx, const struct gl_shader_program *shProg) |
| { |
| if (shProg->data->LinkStatus && |
| shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL] != NULL) { |
| return true; |
| } |
| |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glGetProgramv(linked tessellation control shader required)"); |
| return false; |
| } |
| |
| |
| /** |
| * Check if a tessellation evaluation shader query is valid at this time. |
| * If not, report an error and return false. |
| * |
| * From GL 4.0 section 6.1.12 (Shader and Program Queries): |
| * |
| * "If any of the pname values in this paragraph are queried for a program |
| * which has not been linked successfully, or which does not contain |
| * objects to form a tessellation evaluation shader, then an |
| * INVALID_OPERATION error is generated." |
| * |
| */ |
| static bool |
| check_tes_query(struct gl_context *ctx, const struct gl_shader_program *shProg) |
| { |
| if (shProg->data->LinkStatus && |
| shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL] != NULL) { |
| return true; |
| } |
| |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramv(linked tessellation " |
| "evaluation shader required)"); |
| return false; |
| } |
| |
| |
| /** |
| * glGetProgramiv() - get shader program state. |
| * Note that this is for GLSL shader programs, not ARB vertex/fragment |
| * programs (see glGetProgramivARB). |
| */ |
| static void |
| get_programiv(struct gl_context *ctx, GLuint program, GLenum pname, |
| GLint *params) |
| { |
| struct gl_shader_program *shProg |
| = _mesa_lookup_shader_program_err(ctx, program, "glGetProgramiv(program)"); |
| |
| /* Is transform feedback available in this context? |
| */ |
| const bool has_xfb = |
| (ctx->API == API_OPENGL_COMPAT && ctx->Extensions.EXT_transform_feedback) |
| || ctx->API == API_OPENGL_CORE |
| || _mesa_is_gles3(ctx); |
| |
| /* True if geometry shaders (of the form that was adopted into GLSL 1.50 |
| * and GL 3.2) are available in this context |
| */ |
| const bool has_core_gs = _mesa_has_geometry_shaders(ctx); |
| const bool has_tess = _mesa_has_tessellation(ctx); |
| |
| /* Are uniform buffer objects available in this context? |
| */ |
| const bool has_ubo = |
| (ctx->API == API_OPENGL_COMPAT && |
| ctx->Extensions.ARB_uniform_buffer_object) |
| || ctx->API == API_OPENGL_CORE |
| || _mesa_is_gles3(ctx); |
| |
| if (!shProg) { |
| return; |
| } |
| |
| switch (pname) { |
| case GL_DELETE_STATUS: |
| *params = shProg->DeletePending; |
| return; |
| case GL_LINK_STATUS: |
| *params = shProg->data->LinkStatus; |
| return; |
| case GL_VALIDATE_STATUS: |
| *params = shProg->data->Validated; |
| return; |
| case GL_INFO_LOG_LENGTH: |
| *params = (shProg->data->InfoLog && shProg->data->InfoLog[0] != '\0') ? |
| strlen(shProg->data->InfoLog) + 1 : 0; |
| return; |
| case GL_ATTACHED_SHADERS: |
| *params = shProg->NumShaders; |
| return; |
| case GL_ACTIVE_ATTRIBUTES: |
| *params = _mesa_count_active_attribs(shProg); |
| return; |
| case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: |
| *params = _mesa_longest_attribute_name_length(shProg); |
| return; |
| case GL_ACTIVE_UNIFORMS: { |
| unsigned i; |
| const unsigned num_uniforms = |
| shProg->data->NumUniformStorage - shProg->data->NumHiddenUniforms; |
| for (*params = 0, i = 0; i < num_uniforms; i++) { |
| if (!shProg->data->UniformStorage[i].is_shader_storage) |
| (*params)++; |
| } |
| return; |
| } |
| case GL_ACTIVE_UNIFORM_MAX_LENGTH: { |
| unsigned i; |
| GLint max_len = 0; |
| const unsigned num_uniforms = |
| shProg->data->NumUniformStorage - shProg->data->NumHiddenUniforms; |
| |
| for (i = 0; i < num_uniforms; i++) { |
| if (shProg->data->UniformStorage[i].is_shader_storage) |
| continue; |
| |
| /* Add one for the terminating NUL character for a non-array, and |
| * 4 for the "[0]" and the NUL for an array. |
| */ |
| const GLint len = strlen(shProg->data->UniformStorage[i].name) + 1 + |
| ((shProg->data->UniformStorage[i].array_elements != 0) ? 3 : 0); |
| |
| if (len > max_len) |
| max_len = len; |
| } |
| |
| *params = max_len; |
| return; |
| } |
| case GL_TRANSFORM_FEEDBACK_VARYINGS: |
| if (!has_xfb) |
| break; |
| *params = shProg->TransformFeedback.NumVarying; |
| return; |
| case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: { |
| unsigned i; |
| GLint max_len = 0; |
| if (!has_xfb) |
| break; |
| |
| for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) { |
| /* Add one for the terminating NUL character. |
| */ |
| const GLint len = |
| strlen(shProg->TransformFeedback.VaryingNames[i]) + 1; |
| |
| if (len > max_len) |
| max_len = len; |
| } |
| |
| *params = max_len; |
| return; |
| } |
| case GL_TRANSFORM_FEEDBACK_BUFFER_MODE: |
| if (!has_xfb) |
| break; |
| *params = shProg->TransformFeedback.BufferMode; |
| return; |
| case GL_GEOMETRY_VERTICES_OUT: |
| if (!has_core_gs) |
| break; |
| if (check_gs_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]-> |
| info.Geom.VerticesOut; |
| } |
| return; |
| case GL_GEOMETRY_SHADER_INVOCATIONS: |
| if (!has_core_gs || !ctx->Extensions.ARB_gpu_shader5) |
| break; |
| if (check_gs_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]-> |
| info.Geom.Invocations; |
| } |
| return; |
| case GL_GEOMETRY_INPUT_TYPE: |
| if (!has_core_gs) |
| break; |
| if (check_gs_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]-> |
| info.Geom.InputType; |
| } |
| return; |
| case GL_GEOMETRY_OUTPUT_TYPE: |
| if (!has_core_gs) |
| break; |
| if (check_gs_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]-> |
| info.Geom.OutputType; |
| } |
| return; |
| case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: { |
| unsigned i; |
| GLint max_len = 0; |
| |
| if (!has_ubo) |
| break; |
| |
| for (i = 0; i < shProg->data->NumUniformBlocks; i++) { |
| /* Add one for the terminating NUL character. |
| */ |
| const GLint len = strlen(shProg->data->UniformBlocks[i].Name) + 1; |
| |
| if (len > max_len) |
| max_len = len; |
| } |
| |
| *params = max_len; |
| return; |
| } |
| case GL_ACTIVE_UNIFORM_BLOCKS: |
| if (!has_ubo) |
| break; |
| |
| *params = shProg->data->NumUniformBlocks; |
| return; |
| case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: |
| /* This enum isn't part of the OES extension for OpenGL ES 2.0. It is |
| * only available with desktop OpenGL 3.0+ with the |
| * GL_ARB_get_program_binary extension or OpenGL ES 3.0. |
| * |
| * On desktop, we ignore the 3.0+ requirement because it is silly. |
| */ |
| if (!_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx)) |
| break; |
| |
| *params = shProg->BinaryRetreivableHint; |
| return; |
| case GL_PROGRAM_BINARY_LENGTH: |
| *params = 0; |
| return; |
| case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS: |
| if (!ctx->Extensions.ARB_shader_atomic_counters) |
| break; |
| |
| *params = shProg->data->NumAtomicBuffers; |
| return; |
| case GL_COMPUTE_WORK_GROUP_SIZE: { |
| int i; |
| if (!_mesa_has_compute_shaders(ctx)) |
| break; |
| if (!shProg->data->LinkStatus) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramiv(program not " |
| "linked)"); |
| return; |
| } |
| if (shProg->_LinkedShaders[MESA_SHADER_COMPUTE] == NULL) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramiv(no compute " |
| "shaders)"); |
| return; |
| } |
| for (i = 0; i < 3; i++) |
| params[i] = shProg->Comp.LocalSize[i]; |
| return; |
| } |
| case GL_PROGRAM_SEPARABLE: |
| /* If the program has not been linked, return initial value 0. */ |
| *params = (shProg->data->LinkStatus == GL_FALSE) ? 0 : shProg->SeparateShader; |
| return; |
| |
| /* ARB_tessellation_shader */ |
| case GL_TESS_CONTROL_OUTPUT_VERTICES: |
| if (!has_tess) |
| break; |
| if (check_tcs_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL]-> |
| info.TessCtrl.VerticesOut; |
| } |
| return; |
| case GL_TESS_GEN_MODE: |
| if (!has_tess) |
| break; |
| if (check_tes_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]-> |
| info.TessEval.PrimitiveMode; |
| } |
| return; |
| case GL_TESS_GEN_SPACING: |
| if (!has_tess) |
| break; |
| if (check_tes_query(ctx, shProg)) { |
| const struct gl_linked_shader *tes = |
| shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]; |
| switch (tes->info.TessEval.Spacing) { |
| case TESS_SPACING_EQUAL: |
| *params = GL_EQUAL; |
| break; |
| case TESS_SPACING_FRACTIONAL_ODD: |
| *params = GL_FRACTIONAL_ODD; |
| break; |
| case TESS_SPACING_FRACTIONAL_EVEN: |
| *params = GL_FRACTIONAL_EVEN; |
| break; |
| case TESS_SPACING_UNSPECIFIED: |
| *params = 0; |
| break; |
| } |
| } |
| return; |
| case GL_TESS_GEN_VERTEX_ORDER: |
| if (!has_tess) |
| break; |
| if (check_tes_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]-> |
| info.TessEval.VertexOrder; |
| } |
| return; |
| case GL_TESS_GEN_POINT_MODE: |
| if (!has_tess) |
| break; |
| if (check_tes_query(ctx, shProg)) { |
| *params = shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]-> |
| info.TessEval.PointMode; |
| } |
| return; |
| default: |
| break; |
| } |
| |
| _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramiv(pname=%s)", |
| _mesa_enum_to_string(pname)); |
| } |
| |
| |
| /** |
| * glGetShaderiv() - get GLSL shader state |
| */ |
| static void |
| get_shaderiv(struct gl_context *ctx, GLuint name, GLenum pname, GLint *params) |
| { |
| struct gl_shader *shader = |
| _mesa_lookup_shader_err(ctx, name, "glGetShaderiv"); |
| |
| if (!shader) { |
| return; |
| } |
| |
| switch (pname) { |
| case GL_SHADER_TYPE: |
| *params = shader->Type; |
| break; |
| case GL_DELETE_STATUS: |
| *params = shader->DeletePending; |
| break; |
| case GL_COMPILE_STATUS: |
| *params = shader->CompileStatus; |
| break; |
| case GL_INFO_LOG_LENGTH: |
| *params = (shader->InfoLog && shader->InfoLog[0] != '\0') ? |
| strlen(shader->InfoLog) + 1 : 0; |
| break; |
| case GL_SHADER_SOURCE_LENGTH: |
| *params = shader->Source ? strlen((char *) shader->Source) + 1 : 0; |
| break; |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, "glGetShaderiv(pname)"); |
| return; |
| } |
| } |
| |
| |
| static void |
| get_program_info_log(struct gl_context *ctx, GLuint program, GLsizei bufSize, |
| GLsizei *length, GLchar *infoLog) |
| { |
| struct gl_shader_program *shProg; |
| |
| /* Section 2.5 GL Errors (page 18) of the OpenGL ES 3.0.4 spec and |
| * section 2.3.1 (Errors) of the OpenGL 4.5 spec say: |
| * |
| * "If a negative number is provided where an argument of type sizei or |
| * sizeiptr is specified, an INVALID_VALUE error is generated." |
| */ |
| if (bufSize < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramInfoLog(bufSize < 0)"); |
| return; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, |
| "glGetProgramInfoLog(program)"); |
| if (!shProg) { |
| return; |
| } |
| |
| _mesa_copy_string(infoLog, bufSize, length, shProg->data->InfoLog); |
| } |
| |
| |
| static void |
| get_shader_info_log(struct gl_context *ctx, GLuint shader, GLsizei bufSize, |
| GLsizei *length, GLchar *infoLog) |
| { |
| struct gl_shader *sh; |
| |
| /* Section 2.5 GL Errors (page 18) of the OpenGL ES 3.0.4 spec and |
| * section 2.3.1 (Errors) of the OpenGL 4.5 spec say: |
| * |
| * "If a negative number is provided where an argument of type sizei or |
| * sizeiptr is specified, an INVALID_VALUE error is generated." |
| */ |
| if (bufSize < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetShaderInfoLog(bufSize < 0)"); |
| return; |
| } |
| |
| sh = _mesa_lookup_shader_err(ctx, shader, "glGetShaderInfoLog(shader)"); |
| if (!sh) { |
| return; |
| } |
| |
| _mesa_copy_string(infoLog, bufSize, length, sh->InfoLog); |
| } |
| |
| |
| /** |
| * Return shader source code. |
| */ |
| static void |
| get_shader_source(struct gl_context *ctx, GLuint shader, GLsizei maxLength, |
| GLsizei *length, GLchar *sourceOut) |
| { |
| struct gl_shader *sh; |
| |
| if (maxLength < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetShaderSource(bufSize < 0)"); |
| return; |
| } |
| |
| sh = _mesa_lookup_shader_err(ctx, shader, "glGetShaderSource"); |
| if (!sh) { |
| return; |
| } |
| _mesa_copy_string(sourceOut, maxLength, length, sh->Source); |
| } |
| |
| |
| /** |
| * Set/replace shader source code. A helper function used by |
| * glShaderSource[ARB]. |
| */ |
| static void |
| shader_source(struct gl_shader *sh, const GLchar *source) |
| { |
| assert(sh); |
| |
| /* free old shader source string and install new one */ |
| free((void *)sh->Source); |
| sh->Source = source; |
| #ifdef DEBUG |
| sh->SourceChecksum = util_hash_crc32(sh->Source, strlen(sh->Source)); |
| #endif |
| } |
| |
| |
| /** |
| * Compile a shader. |
| */ |
| void |
| _mesa_compile_shader(struct gl_context *ctx, struct gl_shader *sh) |
| { |
| if (!sh) |
| return; |
| |
| if (!sh->Source) { |
| /* If the user called glCompileShader without first calling |
| * glShaderSource, we should fail to compile, but not raise a GL_ERROR. |
| */ |
| sh->CompileStatus = GL_FALSE; |
| } else { |
| if (ctx->_Shader->Flags & GLSL_DUMP) { |
| _mesa_log("GLSL source for %s shader %d:\n", |
| _mesa_shader_stage_to_string(sh->Stage), sh->Name); |
| _mesa_log("%s\n", sh->Source); |
| } |
| |
| /* this call will set the shader->CompileStatus field to indicate if |
| * compilation was successful. |
| */ |
| _mesa_glsl_compile_shader(ctx, sh, false, false); |
| |
| if (ctx->_Shader->Flags & GLSL_LOG) { |
| _mesa_write_shader_to_file(sh); |
| } |
| |
| if (ctx->_Shader->Flags & GLSL_DUMP) { |
| if (sh->CompileStatus) { |
| if (sh->ir) { |
| _mesa_log("GLSL IR for shader %d:\n", sh->Name); |
| _mesa_print_ir(_mesa_get_log_file(), sh->ir, NULL); |
| } else { |
| _mesa_log("No GLSL IR for shader %d (shader may be from " |
| "cache)\n", sh->Name); |
| } |
| _mesa_log("\n\n"); |
| } else { |
| _mesa_log("GLSL shader %d failed to compile.\n", sh->Name); |
| } |
| if (sh->InfoLog && sh->InfoLog[0] != 0) { |
| _mesa_log("GLSL shader %d info log:\n", sh->Name); |
| _mesa_log("%s\n", sh->InfoLog); |
| } |
| } |
| } |
| |
| if (!sh->CompileStatus) { |
| if (ctx->_Shader->Flags & GLSL_DUMP_ON_ERROR) { |
| _mesa_log("GLSL source for %s shader %d:\n", |
| _mesa_shader_stage_to_string(sh->Stage), sh->Name); |
| _mesa_log("%s\n", sh->Source); |
| _mesa_log("Info Log:\n%s\n", sh->InfoLog); |
| } |
| |
| if (ctx->_Shader->Flags & GLSL_REPORT_ERRORS) { |
| _mesa_debug(ctx, "Error compiling shader %u:\n%s\n", |
| sh->Name, sh->InfoLog); |
| } |
| } |
| } |
| |
| |
| /** |
| * Link a program's shaders. |
| */ |
| void |
| _mesa_link_program(struct gl_context *ctx, struct gl_shader_program *shProg) |
| { |
| if (!shProg) |
| return; |
| |
| /* From the ARB_transform_feedback2 specification: |
| * "The error INVALID_OPERATION is generated by LinkProgram if <program> is |
| * the name of a program being used by one or more transform feedback |
| * objects, even if the objects are not currently bound or are paused." |
| */ |
| if (_mesa_transform_feedback_is_using_program(ctx, shProg)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glLinkProgram(transform feedback is using the program)"); |
| return; |
| } |
| |
| FLUSH_VERTICES(ctx, _NEW_PROGRAM); |
| |
| _mesa_glsl_link_shader(ctx, shProg); |
| |
| /* Capture .shader_test files. */ |
| const char *capture_path = _mesa_get_shader_capture_path(); |
| if (shProg->Name != 0 && shProg->Name != ~0 && capture_path != NULL) { |
| FILE *file; |
| char *filename = ralloc_asprintf(NULL, "%s/%u.shader_test", |
| capture_path, shProg->Name); |
| file = fopen(filename, "w"); |
| if (file) { |
| fprintf(file, "[require]\nGLSL%s >= %u.%02u\n", |
| shProg->IsES ? " ES" : "", |
| shProg->data->Version / 100, shProg->data->Version % 100); |
| if (shProg->SeparateShader) |
| fprintf(file, "GL_ARB_separate_shader_objects\nSSO ENABLED\n"); |
| fprintf(file, "\n"); |
| |
| for (unsigned i = 0; i < shProg->NumShaders; i++) { |
| fprintf(file, "[%s shader]\n%s\n", |
| _mesa_shader_stage_to_string(shProg->Shaders[i]->Stage), |
| shProg->Shaders[i]->Source); |
| } |
| fclose(file); |
| } else { |
| _mesa_warning(ctx, "Failed to open %s", filename); |
| } |
| |
| ralloc_free(filename); |
| } |
| |
| if (shProg->data->LinkStatus == GL_FALSE && |
| (ctx->_Shader->Flags & GLSL_REPORT_ERRORS)) { |
| _mesa_debug(ctx, "Error linking program %u:\n%s\n", |
| shProg->Name, shProg->data->InfoLog); |
| } |
| |
| /* debug code */ |
| if (0) { |
| GLuint i; |
| |
| printf("Link %u shaders in program %u: %s\n", |
| shProg->NumShaders, shProg->Name, |
| shProg->data->LinkStatus ? "Success" : "Failed"); |
| |
| for (i = 0; i < shProg->NumShaders; i++) { |
| printf(" shader %u, stage %u\n", |
| shProg->Shaders[i]->Name, |
| shProg->Shaders[i]->Stage); |
| } |
| } |
| } |
| |
| |
| /** |
| * Print basic shader info (for debug). |
| */ |
| static void |
| print_shader_info(const struct gl_shader_program *shProg) |
| { |
| GLuint i; |
| |
| printf("Mesa: glUseProgram(%u)\n", shProg->Name); |
| for (i = 0; i < shProg->NumShaders; i++) { |
| #ifdef DEBUG |
| printf(" %s shader %u, checksum %u\n", |
| _mesa_shader_stage_to_string(shProg->Shaders[i]->Stage), |
| shProg->Shaders[i]->Name, |
| shProg->Shaders[i]->SourceChecksum); |
| #else |
| printf(" %s shader %u\n", |
| _mesa_shader_stage_to_string(shProg->Shaders[i]->Stage), |
| shProg->Shaders[i]->Name); |
| #endif |
| } |
| if (shProg->_LinkedShaders[MESA_SHADER_VERTEX]) |
| printf(" vert prog %u\n", |
| shProg->_LinkedShaders[MESA_SHADER_VERTEX]->Program->Id); |
| if (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]) |
| printf(" frag prog %u\n", |
| shProg->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program->Id); |
| if (shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]) |
| printf(" geom prog %u\n", |
| shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->Program->Id); |
| if (shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL]) |
| printf(" tesc prog %u\n", |
| shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL]->Program->Id); |
| if (shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]) |
| printf(" tese prog %u\n", |
| shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]->Program->Id); |
| } |
| |
| |
| /** |
| * Use the named shader program for subsequent glUniform calls |
| */ |
| void |
| _mesa_active_program(struct gl_context *ctx, struct gl_shader_program *shProg, |
| const char *caller) |
| { |
| if ((shProg != NULL) && !shProg->data->LinkStatus) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(program %u not linked)", caller, shProg->Name); |
| return; |
| } |
| |
| if (ctx->Shader.ActiveProgram != shProg) { |
| _mesa_reference_shader_program(ctx, &ctx->Shader.ActiveProgram, shProg); |
| } |
| } |
| |
| |
| static void |
| use_shader_program(struct gl_context *ctx, gl_shader_stage stage, |
| struct gl_shader_program *shProg, |
| struct gl_pipeline_object *shTarget) |
| { |
| struct gl_shader_program **target; |
| |
| target = &shTarget->CurrentProgram[stage]; |
| if ((shProg != NULL) && (shProg->_LinkedShaders[stage] == NULL)) |
| shProg = NULL; |
| |
| if (shProg) |
| _mesa_shader_program_init_subroutine_defaults(ctx, shProg); |
| |
| if (*target != shProg) { |
| /* Program is current, flush it */ |
| if (shTarget == ctx->_Shader) { |
| FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS); |
| } |
| |
| /* If the shader is also bound as the current rendering shader, unbind |
| * it from that binding point as well. This ensures that the correct |
| * semantics of glDeleteProgram are maintained. |
| */ |
| switch (stage) { |
| case MESA_SHADER_VERTEX: |
| case MESA_SHADER_TESS_CTRL: |
| case MESA_SHADER_TESS_EVAL: |
| case MESA_SHADER_GEOMETRY: |
| case MESA_SHADER_COMPUTE: |
| /* Empty for now. */ |
| break; |
| case MESA_SHADER_FRAGMENT: |
| if (*target != NULL && |
| ((*target)->_LinkedShaders[MESA_SHADER_FRAGMENT] && |
| (*target)->_LinkedShaders[MESA_SHADER_FRAGMENT]->Program == |
| ctx->_Shader->_CurrentFragmentProgram)) { |
| _mesa_reference_program(ctx, |
| &ctx->_Shader->_CurrentFragmentProgram, |
| NULL); |
| } |
| break; |
| } |
| |
| _mesa_reference_shader_program(ctx, target, shProg); |
| return; |
| } |
| } |
| |
| |
| /** |
| * Use the named shader program for subsequent rendering. |
| */ |
| void |
| _mesa_use_program(struct gl_context *ctx, struct gl_shader_program *shProg) |
| { |
| int i; |
| for (i = 0; i < MESA_SHADER_STAGES; i++) |
| use_shader_program(ctx, i, shProg, &ctx->Shader); |
| _mesa_active_program(ctx, shProg, "glUseProgram"); |
| } |
| |
| |
| /** |
| * Do validation of the given shader program. |
| * \param errMsg returns error message if validation fails. |
| * \return GL_TRUE if valid, GL_FALSE if invalid (and set errMsg) |
| */ |
| static GLboolean |
| validate_shader_program(const struct gl_shader_program *shProg, |
| char *errMsg) |
| { |
| if (!shProg->data->LinkStatus) { |
| return GL_FALSE; |
| } |
| |
| /* From the GL spec, a program is invalid if any of these are true: |
| |
| any two active samplers in the current program object are of |
| different types, but refer to the same texture image unit, |
| |
| any active sampler in the current program object refers to a texture |
| image unit where fixed-function fragment processing accesses a |
| texture target that does not match the sampler type, or |
| |
| the sum of the number of active samplers in the program and the |
| number of texture image units enabled for fixed-function fragment |
| processing exceeds the combined limit on the total number of texture |
| image units allowed. |
| */ |
| |
| /* |
| * Check: any two active samplers in the current program object are of |
| * different types, but refer to the same texture image unit, |
| */ |
| if (!_mesa_sampler_uniforms_are_valid(shProg, errMsg, 100)) |
| return GL_FALSE; |
| |
| return GL_TRUE; |
| } |
| |
| |
| /** |
| * Called via glValidateProgram() |
| */ |
| static void |
| validate_program(struct gl_context *ctx, GLuint program) |
| { |
| struct gl_shader_program *shProg; |
| char errMsg[100] = ""; |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, "glValidateProgram"); |
| if (!shProg) { |
| return; |
| } |
| |
| shProg->data->Validated = validate_shader_program(shProg, errMsg); |
| if (!shProg->data->Validated) { |
| /* update info log */ |
| if (shProg->data->InfoLog) { |
| ralloc_free(shProg->data->InfoLog); |
| } |
| shProg->data->InfoLog = ralloc_strdup(shProg->data, errMsg); |
| } |
| } |
| |
| |
| |
| void GLAPIENTRY |
| _mesa_AttachObjectARB(GLhandleARB program, GLhandleARB shader) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| attach_shader(ctx, program, shader); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_AttachShader(GLuint program, GLuint shader) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| attach_shader(ctx, program, shader); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_CompileShader(GLuint shaderObj) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glCompileShader %u\n", shaderObj); |
| _mesa_compile_shader(ctx, _mesa_lookup_shader_err(ctx, shaderObj, |
| "glCompileShader")); |
| } |
| |
| |
| GLuint GLAPIENTRY |
| _mesa_CreateShader(GLenum type) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glCreateShader %s\n", _mesa_enum_to_string(type)); |
| return create_shader(ctx, type); |
| } |
| |
| |
| GLhandleARB GLAPIENTRY |
| _mesa_CreateShaderObjectARB(GLenum type) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| return create_shader(ctx, type); |
| } |
| |
| |
| GLuint GLAPIENTRY |
| _mesa_CreateProgram(void) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glCreateProgram\n"); |
| return create_shader_program(ctx); |
| } |
| |
| |
| GLhandleARB GLAPIENTRY |
| _mesa_CreateProgramObjectARB(void) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| return create_shader_program(ctx); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_DeleteObjectARB(GLhandleARB obj) |
| { |
| if (MESA_VERBOSE & VERBOSE_API) { |
| GET_CURRENT_CONTEXT(ctx); |
| _mesa_debug(ctx, "glDeleteObjectARB(%lu)\n", (unsigned long)obj); |
| } |
| |
| if (obj) { |
| GET_CURRENT_CONTEXT(ctx); |
| FLUSH_VERTICES(ctx, 0); |
| if (is_program(ctx, obj)) { |
| delete_shader_program(ctx, obj); |
| } |
| else if (is_shader(ctx, obj)) { |
| delete_shader(ctx, obj); |
| } |
| else { |
| /* error? */ |
| } |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_DeleteProgram(GLuint name) |
| { |
| if (name) { |
| GET_CURRENT_CONTEXT(ctx); |
| FLUSH_VERTICES(ctx, 0); |
| delete_shader_program(ctx, name); |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_DeleteShader(GLuint name) |
| { |
| if (name) { |
| GET_CURRENT_CONTEXT(ctx); |
| FLUSH_VERTICES(ctx, 0); |
| delete_shader(ctx, name); |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_DetachObjectARB(GLhandleARB program, GLhandleARB shader) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| detach_shader(ctx, program, shader); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_DetachShader(GLuint program, GLuint shader) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| detach_shader(ctx, program, shader); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetAttachedObjectsARB(GLhandleARB container, GLsizei maxCount, |
| GLsizei * count, GLhandleARB * obj) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_attached_shaders(ctx, container, maxCount, count, obj); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetAttachedShaders(GLuint program, GLsizei maxCount, |
| GLsizei *count, GLuint *obj) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_attached_shaders(ctx, program, maxCount, count, obj); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetInfoLogARB(GLhandleARB object, GLsizei maxLength, GLsizei * length, |
| GLcharARB * infoLog) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| if (is_program(ctx, object)) { |
| get_program_info_log(ctx, object, maxLength, length, infoLog); |
| } |
| else if (is_shader(ctx, object)) { |
| get_shader_info_log(ctx, object, maxLength, length, infoLog); |
| } |
| else { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glGetInfoLogARB"); |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetObjectParameterivARB(GLhandleARB object, GLenum pname, GLint *params) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| /* Implement in terms of GetProgramiv, GetShaderiv */ |
| if (is_program(ctx, object)) { |
| if (pname == GL_OBJECT_TYPE_ARB) { |
| *params = GL_PROGRAM_OBJECT_ARB; |
| } |
| else { |
| get_programiv(ctx, object, pname, params); |
| } |
| } |
| else if (is_shader(ctx, object)) { |
| if (pname == GL_OBJECT_TYPE_ARB) { |
| *params = GL_SHADER_OBJECT_ARB; |
| } |
| else { |
| get_shaderiv(ctx, object, pname, params); |
| } |
| } |
| else { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetObjectParameterivARB"); |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetObjectParameterfvARB(GLhandleARB object, GLenum pname, |
| GLfloat *params) |
| { |
| GLint iparams[1] = {0}; /* XXX is one element enough? */ |
| _mesa_GetObjectParameterivARB(object, pname, iparams); |
| params[0] = (GLfloat) iparams[0]; |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetProgramiv(GLuint program, GLenum pname, GLint *params) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_programiv(ctx, program, pname, params); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetShaderiv(GLuint shader, GLenum pname, GLint *params) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_shaderiv(ctx, shader, pname, params); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetProgramInfoLog(GLuint program, GLsizei bufSize, |
| GLsizei *length, GLchar *infoLog) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_program_info_log(ctx, program, bufSize, length, infoLog); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetShaderInfoLog(GLuint shader, GLsizei bufSize, |
| GLsizei *length, GLchar *infoLog) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_shader_info_log(ctx, shader, bufSize, length, infoLog); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetShaderSource(GLuint shader, GLsizei maxLength, |
| GLsizei *length, GLchar *sourceOut) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| get_shader_source(ctx, shader, maxLength, length, sourceOut); |
| } |
| |
| |
| GLhandleARB GLAPIENTRY |
| _mesa_GetHandleARB(GLenum pname) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| return get_handle(ctx, pname); |
| } |
| |
| |
| GLboolean GLAPIENTRY |
| _mesa_IsProgram(GLuint name) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| return is_program(ctx, name); |
| } |
| |
| |
| GLboolean GLAPIENTRY |
| _mesa_IsShader(GLuint name) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| return is_shader(ctx, name); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_LinkProgram(GLuint programObj) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glLinkProgram %u\n", programObj); |
| _mesa_link_program(ctx, _mesa_lookup_shader_program_err(ctx, programObj, |
| "glLinkProgram")); |
| } |
| |
| #ifdef ENABLE_SHADER_CACHE |
| /** |
| * Generate a SHA-1 hash value string for given source string. |
| */ |
| static void |
| generate_sha1(const char *source, char sha_str[64]) |
| { |
| unsigned char sha[20]; |
| _mesa_sha1_compute(source, strlen(source), sha); |
| _mesa_sha1_format(sha_str, sha); |
| } |
| |
| /** |
| * Construct a full path for shader replacement functionality using |
| * following format: |
| * |
| * <path>/<stage prefix>_<CHECKSUM>.glsl |
| */ |
| static char * |
| construct_name(const gl_shader_stage stage, const char *source, |
| const char *path) |
| { |
| char sha[64]; |
| static const char *types[] = { |
| "VS", "TC", "TE", "GS", "FS", "CS", |
| }; |
| |
| generate_sha1(source, sha); |
| return ralloc_asprintf(NULL, "%s/%s_%s.glsl", path, types[stage], sha); |
| } |
| |
| /** |
| * Write given shader source to a file in MESA_SHADER_DUMP_PATH. |
| */ |
| static void |
| dump_shader(const gl_shader_stage stage, const char *source) |
| { |
| static bool path_exists = true; |
| char *dump_path; |
| FILE *f; |
| |
| if (!path_exists) |
| return; |
| |
| dump_path = getenv("MESA_SHADER_DUMP_PATH"); |
| if (!dump_path) { |
| path_exists = false; |
| return; |
| } |
| |
| char *name = construct_name(stage, source, dump_path); |
| |
| f = fopen(name, "w"); |
| if (f) { |
| fputs(source, f); |
| fclose(f); |
| } else { |
| GET_CURRENT_CONTEXT(ctx); |
| _mesa_warning(ctx, "could not open %s for dumping shader (%s)", name, |
| strerror(errno)); |
| } |
| ralloc_free(name); |
| } |
| |
| /** |
| * Read shader source code from a file. |
| * Useful for debugging to override an app's shader. |
| */ |
| static GLcharARB * |
| read_shader(const gl_shader_stage stage, const char *source) |
| { |
| char *read_path; |
| static bool path_exists = true; |
| int len, shader_size = 0; |
| GLcharARB *buffer; |
| FILE *f; |
| |
| if (!path_exists) |
| return NULL; |
| |
| read_path = getenv("MESA_SHADER_READ_PATH"); |
| if (!read_path) { |
| path_exists = false; |
| return NULL; |
| } |
| |
| char *name = construct_name(stage, source, read_path); |
| f = fopen(name, "r"); |
| ralloc_free(name); |
| if (!f) |
| return NULL; |
| |
| /* allocate enough room for the entire shader */ |
| fseek(f, 0, SEEK_END); |
| shader_size = ftell(f); |
| rewind(f); |
| assert(shader_size); |
| |
| /* add one for terminating zero */ |
| shader_size++; |
| |
| buffer = malloc(shader_size); |
| assert(buffer); |
| |
| len = fread(buffer, 1, shader_size, f); |
| buffer[len] = 0; |
| |
| fclose(f); |
| |
| return buffer; |
| } |
| |
| #endif /* ENABLE_SHADER_CACHE */ |
| |
| /** |
| * Called via glShaderSource() and glShaderSourceARB() API functions. |
| * Basically, concatenate the source code strings into one long string |
| * and pass it to _mesa_shader_source(). |
| */ |
| void GLAPIENTRY |
| _mesa_ShaderSource(GLuint shaderObj, GLsizei count, |
| const GLchar * const * string, const GLint * length) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| GLint *offsets; |
| GLsizei i, totalLength; |
| GLcharARB *source; |
| struct gl_shader *sh; |
| |
| GLcharARB *replacement; |
| |
| sh = _mesa_lookup_shader_err(ctx, shaderObj, "glShaderSourceARB"); |
| if (!sh) |
| return; |
| |
| if (string == NULL) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glShaderSourceARB"); |
| return; |
| } |
| |
| /* |
| * This array holds offsets of where the appropriate string ends, thus the |
| * last element will be set to the total length of the source code. |
| */ |
| offsets = malloc(count * sizeof(GLint)); |
| if (offsets == NULL) { |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderSourceARB"); |
| return; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if (string[i] == NULL) { |
| free((GLvoid *) offsets); |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glShaderSourceARB(null string)"); |
| return; |
| } |
| if (length == NULL || length[i] < 0) |
| offsets[i] = strlen(string[i]); |
| else |
| offsets[i] = length[i]; |
| /* accumulate string lengths */ |
| if (i > 0) |
| offsets[i] += offsets[i - 1]; |
| } |
| |
| /* Total length of source string is sum off all strings plus two. |
| * One extra byte for terminating zero, another extra byte to silence |
| * valgrind warnings in the parser/grammer code. |
| */ |
| totalLength = offsets[count - 1] + 2; |
| source = malloc(totalLength * sizeof(GLcharARB)); |
| if (source == NULL) { |
| free((GLvoid *) offsets); |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderSourceARB"); |
| return; |
| } |
| |
| for (i = 0; i < count; i++) { |
| GLint start = (i > 0) ? offsets[i - 1] : 0; |
| memcpy(source + start, string[i], |
| (offsets[i] - start) * sizeof(GLcharARB)); |
| } |
| source[totalLength - 1] = '\0'; |
| source[totalLength - 2] = '\0'; |
| |
| #ifdef ENABLE_SHADER_CACHE |
| /* Dump original shader source to MESA_SHADER_DUMP_PATH and replace |
| * if corresponding entry found from MESA_SHADER_READ_PATH. |
| */ |
| dump_shader(sh->Stage, source); |
| |
| replacement = read_shader(sh->Stage, source); |
| if (replacement) { |
| free(source); |
| source = replacement; |
| } |
| #endif /* ENABLE_SHADER_CACHE */ |
| |
| shader_source(sh, source); |
| |
| free(offsets); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_UseProgram(GLuint program) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| struct gl_shader_program *shProg; |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glUseProgram %u\n", program); |
| |
| if (_mesa_is_xfb_active_and_unpaused(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glUseProgram(transform feedback active)"); |
| return; |
| } |
| |
| if (program) { |
| shProg = _mesa_lookup_shader_program_err(ctx, program, "glUseProgram"); |
| if (!shProg) { |
| return; |
| } |
| if (!shProg->data->LinkStatus) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glUseProgram(program %u not linked)", program); |
| return; |
| } |
| |
| /* debug code */ |
| if (ctx->_Shader->Flags & GLSL_USE_PROG) { |
| print_shader_info(shProg); |
| } |
| } |
| else { |
| shProg = NULL; |
| } |
| |
| /* The ARB_separate_shader_object spec says: |
| * |
| * "The executable code for an individual shader stage is taken from |
| * the current program for that stage. If there is a current program |
| * object established by UseProgram, that program is considered current |
| * for all stages. Otherwise, if there is a bound program pipeline |
| * object (section 2.14.PPO), the program bound to the appropriate |
| * stage of the pipeline object is considered current." |
| */ |
| if (program) { |
| /* Attach shader state to the binding point */ |
| _mesa_reference_pipeline_object(ctx, &ctx->_Shader, &ctx->Shader); |
| /* Update the program */ |
| _mesa_use_program(ctx, shProg); |
| } else { |
| /* Must be done first: detach the progam */ |
| _mesa_use_program(ctx, shProg); |
| /* Unattach shader_state binding point */ |
| _mesa_reference_pipeline_object(ctx, &ctx->_Shader, ctx->Pipeline.Default); |
| /* If a pipeline was bound, rebind it */ |
| if (ctx->Pipeline.Current) { |
| _mesa_BindProgramPipeline(ctx->Pipeline.Current->Name); |
| } |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_ValidateProgram(GLuint program) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| validate_program(ctx, program); |
| } |
| |
| |
| /** |
| * For OpenGL ES 2.0, GL_ARB_ES2_compatibility |
| */ |
| void GLAPIENTRY |
| _mesa_GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, |
| GLint* range, GLint* precision) |
| { |
| const struct gl_program_constants *limits; |
| const struct gl_precision *p; |
| GET_CURRENT_CONTEXT(ctx); |
| |
| switch (shadertype) { |
| case GL_VERTEX_SHADER: |
| limits = &ctx->Const.Program[MESA_SHADER_VERTEX]; |
| break; |
| case GL_FRAGMENT_SHADER: |
| limits = &ctx->Const.Program[MESA_SHADER_FRAGMENT]; |
| break; |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, |
| "glGetShaderPrecisionFormat(shadertype)"); |
| return; |
| } |
| |
| switch (precisiontype) { |
| case GL_LOW_FLOAT: |
| p = &limits->LowFloat; |
| break; |
| case GL_MEDIUM_FLOAT: |
| p = &limits->MediumFloat; |
| break; |
| case GL_HIGH_FLOAT: |
| p = &limits->HighFloat; |
| break; |
| case GL_LOW_INT: |
| p = &limits->LowInt; |
| break; |
| case GL_MEDIUM_INT: |
| p = &limits->MediumInt; |
| break; |
| case GL_HIGH_INT: |
| p = &limits->HighInt; |
| break; |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, |
| "glGetShaderPrecisionFormat(precisiontype)"); |
| return; |
| } |
| |
| range[0] = p->RangeMin; |
| range[1] = p->RangeMax; |
| precision[0] = p->Precision; |
| } |
| |
| |
| /** |
| * For OpenGL ES 2.0, GL_ARB_ES2_compatibility |
| */ |
| void GLAPIENTRY |
| _mesa_ReleaseShaderCompiler(void) |
| { |
| _mesa_destroy_shader_compiler_caches(); |
| } |
| |
| |
| /** |
| * For OpenGL ES 2.0, GL_ARB_ES2_compatibility |
| */ |
| void GLAPIENTRY |
| _mesa_ShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, |
| const void* binary, GLint length) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) shaders; |
| (void) binaryformat; |
| (void) binary; |
| |
| /* Page 68, section 7.2 'Shader Binaries" of the of the OpenGL ES 3.1, and |
| * page 88 of the OpenGL 4.5 specs state: |
| * |
| * "An INVALID_VALUE error is generated if count or length is negative. |
| * An INVALID_ENUM error is generated if binaryformat is not a supported |
| * format returned in SHADER_BINARY_FORMATS." |
| */ |
| if (n < 0 || length < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glShaderBinary(count or length < 0)"); |
| return; |
| } |
| |
| _mesa_error(ctx, GL_INVALID_ENUM, "glShaderBinary(format)"); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei *length, |
| GLenum *binaryFormat, GLvoid *binary) |
| { |
| struct gl_shader_program *shProg; |
| GLsizei length_dummy; |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (bufSize < 0){ |
| _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramBinary(bufSize < 0)"); |
| return; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetProgramBinary"); |
| if (!shProg) |
| return; |
| |
| /* The ARB_get_program_binary spec says: |
| * |
| * "If <length> is NULL, then no length is returned." |
| * |
| * Ensure that length always points to valid storage to avoid multiple NULL |
| * pointer checks below. |
| */ |
| if (length == NULL) |
| length = &length_dummy; |
| |
| |
| /* The ARB_get_program_binary spec says: |
| * |
| * "When a program object's LINK_STATUS is FALSE, its program binary |
| * length is zero, and a call to GetProgramBinary will generate an |
| * INVALID_OPERATION error. |
| */ |
| if (!shProg->data->LinkStatus) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glGetProgramBinary(program %u not linked)", |
| shProg->Name); |
| *length = 0; |
| return; |
| } |
| |
| *length = 0; |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glGetProgramBinary(driver supports zero binary formats)"); |
| |
| (void) binaryFormat; |
| (void) binary; |
| } |
| |
| void GLAPIENTRY |
| _mesa_ProgramBinary(GLuint program, GLenum binaryFormat, |
| const GLvoid *binary, GLsizei length) |
| { |
| struct gl_shader_program *shProg; |
| GET_CURRENT_CONTEXT(ctx); |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, "glProgramBinary"); |
| if (!shProg) |
| return; |
| |
| (void) binaryFormat; |
| (void) binary; |
| |
| /* Section 2.3.1 (Errors) of the OpenGL 4.5 spec says: |
| * |
| * "If a negative number is provided where an argument of type sizei or |
| * sizeiptr is specified, an INVALID_VALUE error is generated." |
| */ |
| if (length < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glProgramBinary(length < 0)"); |
| return; |
| } |
| |
| /* The ARB_get_program_binary spec says: |
| * |
| * "<binaryFormat> and <binary> must be those returned by a previous |
| * call to GetProgramBinary, and <length> must be the length of the |
| * program binary as returned by GetProgramBinary or GetProgramiv with |
| * <pname> PROGRAM_BINARY_LENGTH. Loading the program binary will fail, |
| * setting the LINK_STATUS of <program> to FALSE, if these conditions |
| * are not met." |
| * |
| * Since any value of binaryFormat passed "is not one of those specified as |
| * allowable for [this] command, an INVALID_ENUM error is generated." |
| */ |
| shProg->data->LinkStatus = GL_FALSE; |
| _mesa_error(ctx, GL_INVALID_ENUM, "glProgramBinary"); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_ProgramParameteri(GLuint program, GLenum pname, GLint value) |
| { |
| struct gl_shader_program *shProg; |
| GET_CURRENT_CONTEXT(ctx); |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, |
| "glProgramParameteri"); |
| if (!shProg) |
| return; |
| |
| switch (pname) { |
| case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: |
| /* This enum isn't part of the OES extension for OpenGL ES 2.0, but it |
| * is part of OpenGL ES 3.0. For the ES2 case, this function shouldn't |
| * even be in the dispatch table, so we shouldn't need to expclicitly |
| * check here. |
| * |
| * On desktop, we ignore the 3.0+ requirement because it is silly. |
| */ |
| |
| /* The ARB_get_program_binary extension spec says: |
| * |
| * "An INVALID_VALUE error is generated if the <value> argument to |
| * ProgramParameteri is not TRUE or FALSE." |
| */ |
| if (value != GL_TRUE && value != GL_FALSE) { |
| goto invalid_value; |
| } |
| |
| /* No need to notify the driver. Any changes will actually take effect |
| * the next time the shader is linked. |
| * |
| * The ARB_get_program_binary extension spec says: |
| * |
| * "To indicate that a program binary is likely to be retrieved, |
| * ProgramParameteri should be called with <pname> |
| * PROGRAM_BINARY_RETRIEVABLE_HINT and <value> TRUE. This setting |
| * will not be in effect until the next time LinkProgram or |
| * ProgramBinary has been called successfully." |
| * |
| * The resloution of issue 9 in the extension spec also says: |
| * |
| * "The application may use the PROGRAM_BINARY_RETRIEVABLE_HINT hint |
| * to indicate to the GL implementation that this program will |
| * likely be saved with GetProgramBinary at some point. This will |
| * give the GL implementation the opportunity to track any state |
| * changes made to the program before being saved such that when it |
| * is loaded again a recompile can be avoided." |
| */ |
| shProg->BinaryRetreivableHint = value; |
| return; |
| |
| case GL_PROGRAM_SEPARABLE: |
| /* Spec imply that the behavior is the same as ARB_get_program_binary |
| * Chapter 7.3 Program Objects |
| */ |
| if (value != GL_TRUE && value != GL_FALSE) { |
| goto invalid_value; |
| } |
| shProg->SeparateShader = value; |
| return; |
| |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameteri(pname=%s)", |
| _mesa_enum_to_string(pname)); |
| return; |
| } |
| |
| invalid_value: |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glProgramParameteri(pname=%s, value=%d): " |
| "value must be 0 or 1.", |
| _mesa_enum_to_string(pname), |
| value); |
| } |
| |
| |
| void |
| _mesa_use_shader_program(struct gl_context *ctx, GLenum type, |
| struct gl_shader_program *shProg, |
| struct gl_pipeline_object *shTarget) |
| { |
| gl_shader_stage stage = _mesa_shader_enum_to_shader_stage(type); |
| use_shader_program(ctx, stage, shProg, shTarget); |
| } |
| |
| |
| /** |
| * Copy program-specific data generated by linking from the gl_shader_program |
| * object to the gl_program object referred to by the gl_linked_shader. |
| * |
| * This function expects _mesa_reference_program() to have been previously |
| * called setting the gl_linked_shaders program reference. |
| */ |
| void |
| _mesa_copy_linked_program_data(const struct gl_shader_program *src, |
| struct gl_linked_shader *dst_sh) |
| { |
| assert(dst_sh->Program); |
| |
| struct gl_program *dst = dst_sh->Program; |
| |
| dst->info.separate_shader = src->SeparateShader; |
| |
| switch (dst_sh->Stage) { |
| case MESA_SHADER_VERTEX: |
| dst->ClipDistanceArraySize = src->Vert.ClipDistanceArraySize; |
| dst->CullDistanceArraySize = src->Vert.CullDistanceArraySize; |
| break; |
| case MESA_SHADER_TESS_CTRL: { |
| dst->info.tess.tcs_vertices_out = dst_sh->info.TessCtrl.VerticesOut; |
| break; |
| } |
| case MESA_SHADER_TESS_EVAL: { |
| dst->info.tess.primitive_mode = dst_sh->info.TessEval.PrimitiveMode; |
| dst->info.tess.spacing = dst_sh->info.TessEval.Spacing; |
| dst->info.tess.ccw = dst_sh->info.TessEval.VertexOrder == GL_CCW; |
| dst->info.tess.point_mode = dst_sh->info.TessEval.PointMode; |
| dst->ClipDistanceArraySize = src->TessEval.ClipDistanceArraySize; |
| dst->CullDistanceArraySize = src->TessEval.CullDistanceArraySize; |
| break; |
| } |
| case MESA_SHADER_GEOMETRY: { |
| dst->info.gs.vertices_in = src->Geom.VerticesIn; |
| dst->info.gs.vertices_out = dst_sh->info.Geom.VerticesOut; |
| dst->info.gs.invocations = dst_sh->info.Geom.Invocations; |
| dst->info.gs.input_primitive = dst_sh->info.Geom.InputType; |
| dst->info.gs.output_primitive = dst_sh->info.Geom.OutputType; |
| dst->ClipDistanceArraySize = src->Geom.ClipDistanceArraySize; |
| dst->CullDistanceArraySize = src->Geom.CullDistanceArraySize; |
| dst->info.gs.uses_end_primitive = src->Geom.UsesEndPrimitive; |
| dst->info.gs.uses_streams = src->Geom.UsesStreams; |
| break; |
| } |
| case MESA_SHADER_FRAGMENT: { |
| dst->info.fs.depth_layout = src->FragDepthLayout; |
| dst->info.fs.early_fragment_tests = dst_sh->info.EarlyFragmentTests; |
| dst->info.fs.inner_coverage = dst_sh->info.InnerCoverage; |
| dst->info.fs.post_depth_coverage = dst_sh->info.PostDepthCoverage; |
| break; |
| } |
| case MESA_SHADER_COMPUTE: { |
| for (int i = 0; i < 3; i++) |
| dst->info.cs.local_size[i] = src->Comp.LocalSize[i]; |
| dst->info.cs.shared_size = src->Comp.SharedSize; |
| dst->info.cs.local_size_variable = src->Comp.LocalSizeVariable; |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * ARB_separate_shader_objects: Compile & Link Program |
| */ |
| GLuint GLAPIENTRY |
| _mesa_CreateShaderProgramv(GLenum type, GLsizei count, |
| const GLchar* const *strings) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| const GLuint shader = create_shader(ctx, type); |
| GLuint program = 0; |
| |
| /* |
| * According to OpenGL 4.5 and OpenGL ES 3.1 standards, section 7.3: |
| * GL_INVALID_VALUE should be generated if count < 0 |
| */ |
| if (count < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glCreateShaderProgram (count < 0)"); |
| return program; |
| } |
| |
| if (shader) { |
| struct gl_shader *sh = _mesa_lookup_shader(ctx, shader); |
| |
| _mesa_ShaderSource(shader, count, strings, NULL); |
| _mesa_compile_shader(ctx, sh); |
| |
| program = create_shader_program(ctx); |
| if (program) { |
| struct gl_shader_program *shProg; |
| GLint compiled = GL_FALSE; |
| |
| shProg = _mesa_lookup_shader_program(ctx, program); |
| |
| shProg->SeparateShader = GL_TRUE; |
| |
| get_shaderiv(ctx, shader, GL_COMPILE_STATUS, &compiled); |
| if (compiled) { |
| attach_shader(ctx, program, shader); |
| _mesa_link_program(ctx, shProg); |
| detach_shader(ctx, program, shader); |
| |
| #if 0 |
| /* Possibly... */ |
| if (active-user-defined-varyings-in-linked-program) { |
| append-error-to-info-log; |
| shProg->data->LinkStatus = GL_FALSE; |
| } |
| #endif |
| } |
| if (sh->InfoLog) |
| ralloc_strcat(&shProg->data->InfoLog, sh->InfoLog); |
| } |
| |
| delete_shader(ctx, shader); |
| } |
| |
| return program; |
| } |
| |
| |
| /** |
| * For GL_ARB_tessellation_shader |
| */ |
| extern void GLAPIENTRY |
| _mesa_PatchParameteri(GLenum pname, GLint value) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (!_mesa_has_tessellation(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glPatchParameteri"); |
| return; |
| } |
| |
| if (pname != GL_PATCH_VERTICES) { |
| _mesa_error(ctx, GL_INVALID_ENUM, "glPatchParameteri"); |
| return; |
| } |
| |
| if (value <= 0 || value > ctx->Const.MaxPatchVertices) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glPatchParameteri"); |
| return; |
| } |
| |
| ctx->TessCtrlProgram.patch_vertices = value; |
| } |
| |
| |
| extern void GLAPIENTRY |
| _mesa_PatchParameterfv(GLenum pname, const GLfloat *values) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (!_mesa_has_tessellation(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glPatchParameterfv"); |
| return; |
| } |
| |
| switch(pname) { |
| case GL_PATCH_DEFAULT_OUTER_LEVEL: |
| FLUSH_VERTICES(ctx, 0); |
| memcpy(ctx->TessCtrlProgram.patch_default_outer_level, values, |
| 4 * sizeof(GLfloat)); |
| ctx->NewDriverState |= ctx->DriverFlags.NewDefaultTessLevels; |
| return; |
| case GL_PATCH_DEFAULT_INNER_LEVEL: |
| FLUSH_VERTICES(ctx, 0); |
| memcpy(ctx->TessCtrlProgram.patch_default_inner_level, values, |
| 2 * sizeof(GLfloat)); |
| ctx->NewDriverState |= ctx->DriverFlags.NewDefaultTessLevels; |
| return; |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, "glPatchParameterfv"); |
| return; |
| } |
| } |
| |
| /** |
| * ARB_shader_subroutine |
| */ |
| GLint GLAPIENTRY |
| _mesa_GetSubroutineUniformLocation(GLuint program, GLenum shadertype, |
| const GLchar *name) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetSubroutineUniformLocation"; |
| struct gl_shader_program *shProg; |
| GLenum resource_type; |
| gl_shader_stage stage; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return -1; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return -1; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, api_name); |
| if (!shProg) |
| return -1; |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| if (!shProg->_LinkedShaders[stage]) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return -1; |
| } |
| |
| resource_type = _mesa_shader_stage_to_subroutine_uniform(stage); |
| return _mesa_program_resource_location(shProg, resource_type, name); |
| } |
| |
| GLuint GLAPIENTRY |
| _mesa_GetSubroutineIndex(GLuint program, GLenum shadertype, |
| const GLchar *name) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetSubroutineIndex"; |
| struct gl_shader_program *shProg; |
| struct gl_program_resource *res; |
| GLenum resource_type; |
| gl_shader_stage stage; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return -1; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return -1; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, api_name); |
| if (!shProg) |
| return -1; |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| if (!shProg->_LinkedShaders[stage]) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return -1; |
| } |
| |
| resource_type = _mesa_shader_stage_to_subroutine(stage); |
| res = _mesa_program_resource_find_name(shProg, resource_type, name, NULL); |
| if (!res) { |
| return -1; |
| } |
| |
| return _mesa_program_resource_index(shProg, res); |
| } |
| |
| |
| GLvoid GLAPIENTRY |
| _mesa_GetActiveSubroutineUniformiv(GLuint program, GLenum shadertype, |
| GLuint index, GLenum pname, GLint *values) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetActiveSubroutineUniformiv"; |
| struct gl_shader_program *shProg; |
| struct gl_linked_shader *sh; |
| gl_shader_stage stage; |
| struct gl_program_resource *res; |
| const struct gl_uniform_storage *uni; |
| GLenum resource_type; |
| int count, i, j; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, api_name); |
| if (!shProg) |
| return; |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| resource_type = _mesa_shader_stage_to_subroutine_uniform(stage); |
| |
| sh = shProg->_LinkedShaders[stage]; |
| if (!sh) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| struct gl_program *p = shProg->_LinkedShaders[stage]->Program; |
| if (index >= p->sh.NumSubroutineUniforms) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "%s: invalid index greater than GL_ACTIVE_SUBROUTINE_UNIFORMS", api_name); |
| return; |
| } |
| |
| switch (pname) { |
| case GL_NUM_COMPATIBLE_SUBROUTINES: { |
| res = _mesa_program_resource_find_index(shProg, resource_type, index); |
| if (res) { |
| uni = res->Data; |
| values[0] = uni->num_compatible_subroutines; |
| } |
| break; |
| } |
| case GL_COMPATIBLE_SUBROUTINES: { |
| res = _mesa_program_resource_find_index(shProg, resource_type, index); |
| if (res) { |
| uni = res->Data; |
| count = 0; |
| for (i = 0; i < p->sh.NumSubroutineFunctions; i++) { |
| struct gl_subroutine_function *fn = &p->sh.SubroutineFunctions[i]; |
| for (j = 0; j < fn->num_compat_types; j++) { |
| if (fn->types[j] == uni->type) { |
| values[count++] = i; |
| break; |
| } |
| } |
| } |
| } |
| break; |
| } |
| case GL_UNIFORM_SIZE: |
| res = _mesa_program_resource_find_index(shProg, resource_type, index); |
| if (res) { |
| uni = res->Data; |
| values[0] = uni->array_elements ? uni->array_elements : 1; |
| } |
| break; |
| case GL_UNIFORM_NAME_LENGTH: |
| res = _mesa_program_resource_find_index(shProg, resource_type, index); |
| if (res) { |
| values[0] = strlen(_mesa_program_resource_name(res)) + 1 |
| + ((_mesa_program_resource_array_size(res) != 0) ? 3 : 0); |
| } |
| break; |
| default: |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| } |
| |
| |
| GLvoid GLAPIENTRY |
| _mesa_GetActiveSubroutineUniformName(GLuint program, GLenum shadertype, |
| GLuint index, GLsizei bufsize, |
| GLsizei *length, GLchar *name) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetActiveSubroutineUniformName"; |
| struct gl_shader_program *shProg; |
| GLenum resource_type; |
| gl_shader_stage stage; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, api_name); |
| if (!shProg) |
| return; |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| if (!shProg->_LinkedShaders[stage]) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| resource_type = _mesa_shader_stage_to_subroutine_uniform(stage); |
| /* get program resource name */ |
| _mesa_get_program_resource_name(shProg, resource_type, |
| index, bufsize, |
| length, name, api_name); |
| } |
| |
| |
| GLvoid GLAPIENTRY |
| _mesa_GetActiveSubroutineName(GLuint program, GLenum shadertype, |
| GLuint index, GLsizei bufsize, |
| GLsizei *length, GLchar *name) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetActiveSubroutineName"; |
| struct gl_shader_program *shProg; |
| GLenum resource_type; |
| gl_shader_stage stage; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, api_name); |
| if (!shProg) |
| return; |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| if (!shProg->_LinkedShaders[stage]) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| resource_type = _mesa_shader_stage_to_subroutine(stage); |
| _mesa_get_program_resource_name(shProg, resource_type, |
| index, bufsize, |
| length, name, api_name); |
| } |
| |
| GLvoid GLAPIENTRY |
| _mesa_UniformSubroutinesuiv(GLenum shadertype, GLsizei count, |
| const GLuint *indices) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glUniformSubroutinesuiv"; |
| struct gl_shader_program *shProg; |
| struct gl_linked_shader *sh; |
| gl_shader_stage stage; |
| int i; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| shProg = ctx->_Shader->CurrentProgram[stage]; |
| if (!shProg) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| sh = shProg->_LinkedShaders[stage]; |
| if (!sh) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| struct gl_program *p = shProg->_LinkedShaders[stage]->Program; |
| if (count != p->sh.NumSubroutineUniformRemapTable) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "%s", api_name); |
| return; |
| } |
| |
| i = 0; |
| do { |
| struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i]; |
| if (uni == NULL) { |
| i++; |
| continue; |
| } |
| |
| int uni_count = uni->array_elements ? uni->array_elements : 1; |
| int j, k, f; |
| |
| for (j = i; j < i + uni_count; j++) { |
| struct gl_subroutine_function *subfn = NULL; |
| if (indices[j] > p->sh.MaxSubroutineFunctionIndex) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "%s", api_name); |
| return; |
| } |
| |
| for (f = 0; f < p->sh.NumSubroutineFunctions; f++) { |
| if (p->sh.SubroutineFunctions[f].index == indices[j]) |
| subfn = &p->sh.SubroutineFunctions[f]; |
| } |
| |
| if (!subfn) { |
| continue; |
| } |
| |
| for (k = 0; k < subfn->num_compat_types; k++) { |
| if (subfn->types[k] == uni->type) |
| break; |
| } |
| if (k == subfn->num_compat_types) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| ctx->SubroutineIndex[p->info.stage].IndexPtr[j] = indices[j]; |
| } |
| i += uni_count; |
| } while(i < count); |
| |
| FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); |
| } |
| |
| |
| GLvoid GLAPIENTRY |
| _mesa_GetUniformSubroutineuiv(GLenum shadertype, GLint location, |
| GLuint *params) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetUniformSubroutineuiv"; |
| struct gl_shader_program *shProg; |
| struct gl_linked_shader *sh; |
| gl_shader_stage stage; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| shProg = ctx->_Shader->CurrentProgram[stage]; |
| if (!shProg) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| sh = shProg->_LinkedShaders[stage]; |
| if (!sh) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| struct gl_program *p = sh->Program; |
| if (location >= p->sh.NumSubroutineUniformRemapTable) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "%s", api_name); |
| return; |
| } |
| |
| *params = ctx->SubroutineIndex[p->info.stage].IndexPtr[location]; |
| } |
| |
| |
| GLvoid GLAPIENTRY |
| _mesa_GetProgramStageiv(GLuint program, GLenum shadertype, |
| GLenum pname, GLint *values) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const char *api_name = "glGetProgramStageiv"; |
| struct gl_shader_program *shProg; |
| struct gl_linked_shader *sh; |
| gl_shader_stage stage; |
| |
| if (!_mesa_has_ARB_shader_subroutine(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| if (!_mesa_validate_shader_target(ctx, shadertype)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| return; |
| } |
| |
| shProg = _mesa_lookup_shader_program_err(ctx, program, api_name); |
| if (!shProg) |
| return; |
| |
| stage = _mesa_shader_enum_to_shader_stage(shadertype); |
| sh = shProg->_LinkedShaders[stage]; |
| |
| /* ARB_shader_subroutine doesn't ask the program to be linked, or list any |
| * INVALID_OPERATION in the case of not be linked. |
| * |
| * And for some pnames, like GL_ACTIVE_SUBROUTINE_UNIFORMS, you can ask the |
| * same info using other specs (ARB_program_interface_query), without the |
| * need of the program to be linked, being the value for that case 0. |
| * |
| * But at the same time, some other methods require the program to be |
| * linked for pname related to locations, so it would be inconsistent to |
| * not do the same here. So we are: |
| * * Return GL_INVALID_OPERATION if not linked only for locations. |
| * * Setting a default value of 0, to be returned if not linked. |
| */ |
| if (!sh) { |
| values[0] = 0; |
| if (pname == GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name); |
| } |
| return; |
| } |
| |
| struct gl_program *p = sh->Program; |
| switch (pname) { |
| case GL_ACTIVE_SUBROUTINES: |
| values[0] = p->sh.NumSubroutineFunctions; |
| break; |
| case GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS: |
| values[0] = p->sh.NumSubroutineUniformRemapTable; |
| break; |
| case GL_ACTIVE_SUBROUTINE_UNIFORMS: |
| values[0] = p->sh.NumSubroutineUniforms; |
| break; |
| case GL_ACTIVE_SUBROUTINE_MAX_LENGTH: |
| { |
| unsigned i; |
| GLint max_len = 0; |
| GLenum resource_type; |
| struct gl_program_resource *res; |
| |
| resource_type = _mesa_shader_stage_to_subroutine(stage); |
| for (i = 0; i < p->sh.NumSubroutineFunctions; i++) { |
| res = _mesa_program_resource_find_index(shProg, resource_type, i); |
| if (res) { |
| const GLint len = strlen(_mesa_program_resource_name(res)) + 1; |
| if (len > max_len) |
| max_len = len; |
| } |
| } |
| values[0] = max_len; |
| break; |
| } |
| case GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH: |
| { |
| unsigned i; |
| GLint max_len = 0; |
| GLenum resource_type; |
| struct gl_program_resource *res; |
| |
| resource_type = _mesa_shader_stage_to_subroutine_uniform(stage); |
| for (i = 0; i < p->sh.NumSubroutineUniformRemapTable; i++) { |
| res = _mesa_program_resource_find_index(shProg, resource_type, i); |
| if (res) { |
| const GLint len = strlen(_mesa_program_resource_name(res)) + 1 |
| + ((_mesa_program_resource_array_size(res) != 0) ? 3 : 0); |
| |
| if (len > max_len) |
| max_len = len; |
| } |
| } |
| values[0] = max_len; |
| break; |
| } |
| default: |
| _mesa_error(ctx, GL_INVALID_ENUM, "%s", api_name); |
| values[0] = -1; |
| break; |
| } |
| } |
| |
| static int |
| find_compat_subroutine(struct gl_program *p, const struct glsl_type *type) |
| { |
| int i, j; |
| |
| for (i = 0; i < p->sh.NumSubroutineFunctions; i++) { |
| struct gl_subroutine_function *fn = &p->sh.SubroutineFunctions[i]; |
| for (j = 0; j < fn->num_compat_types; j++) { |
| if (fn->types[j] == type) |
| return i; |
| } |
| } |
| return 0; |
| } |
| |
| static void |
| _mesa_shader_write_subroutine_index(struct gl_context *ctx, |
| struct gl_program *p) |
| { |
| int i, j; |
| |
| if (p->sh.NumSubroutineUniformRemapTable == 0) |
| return; |
| |
| i = 0; |
| do { |
| struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i]; |
| int uni_count; |
| int val; |
| |
| if (!uni) { |
| i++; |
| continue; |
| } |
| |
| uni_count = uni->array_elements ? uni->array_elements : 1; |
| for (j = 0; j < uni_count; j++) { |
| val = ctx->SubroutineIndex[p->info.stage].IndexPtr[i + j]; |
| memcpy(&uni->storage[j], &val, sizeof(int)); |
| } |
| |
| _mesa_propagate_uniforms_to_driver_storage(uni, 0, uni_count); |
| i += uni_count; |
| } while(i < p->sh.NumSubroutineUniformRemapTable); |
| } |
| |
| void |
| _mesa_shader_write_subroutine_indices(struct gl_context *ctx, |
| gl_shader_stage stage) |
| { |
| if (ctx->_Shader->CurrentProgram[stage] && |
| ctx->_Shader->CurrentProgram[stage]->_LinkedShaders[stage]) |
| _mesa_shader_write_subroutine_index(ctx, |
| ctx->_Shader->CurrentProgram[stage]->_LinkedShaders[stage]->Program); |
| } |
| |
| static void |
| _mesa_program_init_subroutine_defaults(struct gl_context *ctx, |
| struct gl_program *p) |
| { |
| assert(p); |
| |
| struct gl_subroutine_index_binding *binding = &ctx->SubroutineIndex[p->info.stage]; |
| if (binding->NumIndex != p->sh.NumSubroutineUniformRemapTable) { |
| binding->IndexPtr = realloc(binding->IndexPtr, |
| p->sh.NumSubroutineUniformRemapTable * (sizeof(GLuint))); |
| binding->NumIndex = p->sh.NumSubroutineUniformRemapTable; |
| } |
| |
| for (int i = 0; i < p->sh.NumSubroutineUniformRemapTable; i++) { |
| struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i]; |
| |
| if (!uni) |
| continue; |
| |
| binding->IndexPtr[i] = find_compat_subroutine(p, uni->type); |
| } |
| } |
| |
| void |
| _mesa_shader_program_init_subroutine_defaults(struct gl_context *ctx, |
| struct gl_shader_program *shProg) |
| { |
| int i; |
| |
| if (!shProg) |
| return; |
| |
| for (i = 0; i < MESA_SHADER_STAGES; i++) { |
| if (!shProg->_LinkedShaders[i]) |
| continue; |
| |
| _mesa_program_init_subroutine_defaults(ctx, shProg->_LinkedShaders[i]->Program); |
| } |
| } |