X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Fshaderapi.c;h=f0d345a143bb590a843e367f6b87c2411f35ba18;hb=b2bddaf7a000bf9830a7947b18d8e31fb25353ae;hp=72a65d78e5cf2b2eec8a7e0b70a1919001942472;hpb=9f37b405a3de8668a5f74c9681829688475ac3b7;p=mesa.git diff --git a/src/mesa/main/shaderapi.c b/src/mesa/main/shaderapi.c index 72a65d78e5c..f0d345a143b 100644 --- a/src/mesa/main/shaderapi.c +++ b/src/mesa/main/shaderapi.c @@ -17,9 +17,10 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. */ /** @@ -41,17 +42,22 @@ #include "main/dispatch.h" #include "main/enums.h" #include "main/hash.h" -#include "main/mfeatures.h" +#include "main/hash_table.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 "program/program.h" +#include "program/prog_print.h" #include "program/prog_parameter.h" #include "ralloc.h" #include #include "../glsl/glsl_parser_extras.h" +#include "../glsl/ir.h" #include "../glsl/ir_uniform.h" +#include "../glsl/program.h" /** Define this to enable shader substitution (see below) */ #define SHADER_SUBST 0 @@ -60,14 +66,16 @@ /** * Return mask of GLSL_x flags by examining the MESA_GLSL env var. */ -static GLbitfield -get_shader_flags(void) +GLbitfield +_mesa_get_shader_flags(void) { GLbitfield flags = 0x0; const char *env = _mesa_getenv("MESA_GLSL"); if (env) { - if (strstr(env, "dump")) + 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; @@ -101,18 +109,23 @@ _mesa_init_shader_state(struct gl_context *ctx) * are generated by the GLSL compiler. */ struct gl_shader_compiler_options options; - gl_shader_type sh; + gl_shader_stage sh; memset(&options, 0, sizeof(options)); options.MaxUnrollIterations = 32; + options.MaxIfDepth = UINT_MAX; /* Default pragma settings */ options.DefaultPragmas.Optimize = GL_TRUE; - for (sh = 0; sh < MESA_SHADER_TYPES; ++sh) + for (sh = 0; sh < MESA_SHADER_STAGES; ++sh) memcpy(&ctx->ShaderCompilerOptions[sh], &options, sizeof(options)); - ctx->Shader.Flags = get_shader_flags(); + ctx->Shader.Flags = _mesa_get_shader_flags(); + + /* Extended for ARB_separate_shader_objects */ + ctx->Shader.RefCount = 1; + mtx_init(&ctx->Shader.Mutex, mtx_plain); } @@ -122,14 +135,20 @@ _mesa_init_shader_state(struct gl_context *ctx) void _mesa_free_shader_state(struct gl_context *ctx) { - _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentVertexProgram, NULL); - _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentGeometryProgram, - NULL); - _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentFragmentProgram, - NULL); + int i; + for (i = 0; i < MESA_SHADER_STAGES; i++) { + _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram[i], + NULL); + } _mesa_reference_shader_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); } @@ -163,41 +182,31 @@ _mesa_copy_string(GLchar *dst, GLsizei maxLength, * \param type Shader target * */ -static bool -validate_shader_target(const struct gl_context *ctx, GLenum type) -{ +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->Extensions.ARB_fragment_shader; + return ctx == NULL || ctx->Extensions.ARB_fragment_shader; case GL_VERTEX_SHADER: - return ctx->Extensions.ARB_vertex_shader; + return ctx == NULL || ctx->Extensions.ARB_vertex_shader; case GL_GEOMETRY_SHADER_ARB: - return _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_geometry_shader4; + return ctx == NULL || _mesa_has_geometry_shaders(ctx); + case GL_COMPUTE_SHADER: + return ctx == NULL || ctx->Extensions.ARB_compute_shader; default: return false; } } -/** - * Find the length of the longest transform feedback varying name - * which was specified with glTransformFeedbackVaryings(). - */ -static GLint -longest_feedback_varying_name(const struct gl_shader_program *shProg) -{ - GLuint i; - GLint max = 0; - for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) { - GLint len = strlen(shProg->TransformFeedback.VaryingNames[i]); - if (len > max) - max = len; - } - return max; -} - - - static GLboolean is_program(struct gl_context *ctx, GLuint name) { @@ -224,6 +233,8 @@ attach_shader(struct gl_context *ctx, GLuint program, GLuint shader) 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; @@ -244,6 +255,18 @@ attach_shader(struct gl_context *ctx, GLuint program, GLuint shader) */ _mesa_error(ctx, GL_INVALID_OPERATION, "glAttachShader"); return; + } else if (same_type_disallowed && + shProg->Shaders[i]->Type == sh->Type) { + /* 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; } } @@ -270,7 +293,7 @@ create_shader(struct gl_context *ctx, GLenum type) struct gl_shader *sh; GLuint name; - if (!validate_shader_target(ctx, type)) { + if (!_mesa_validate_shader_target(ctx, type)) { _mesa_error(ctx, GL_INVALID_ENUM, "CreateShader(type)"); return 0; } @@ -371,30 +394,31 @@ detach_shader(struct gl_context *ctx, GLuint program, GLuint shader) _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL); /* alloc new, smaller array */ - newList = - malloc((n - 1) * sizeof(struct gl_shader *)); + 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) + while (++i < n) { newList[j++] = shProg->Shaders[i]; - free(shProg->Shaders); + } + /* Free old list and install new one */ + free(shProg->Shaders); shProg->Shaders = newList; shProg->NumShaders = n - 1; #ifdef DEBUG - /* sanity check */ - { - for (j = 0; j < shProg->NumShaders; j++) { - assert(shProg->Shaders[j]->Type == GL_VERTEX_SHADER || - shProg->Shaders[j]->Type == GL_FRAGMENT_SHADER); - assert(shProg->Shaders[j]->RefCount > 0); - } + /* sanity check - make sure the new list's entries are sensible */ + for (j = 0; j < shProg->NumShaders; j++) { + assert(shProg->Shaders[j]->Type == GL_VERTEX_SHADER || + shProg->Shaders[j]->Type == GL_GEOMETRY_SHADER || + shProg->Shaders[j]->Type == GL_FRAGMENT_SHADER); + assert(shProg->Shaders[j]->RefCount > 0); } #endif @@ -411,7 +435,7 @@ detach_shader(struct gl_context *ctx, GLuint program, GLuint shader) err = GL_INVALID_OPERATION; else err = GL_INVALID_VALUE; - _mesa_error(ctx, err, "glDetachProgram(shader)"); + _mesa_error(ctx, err, "glDetachShader(shader)"); return; } } @@ -456,6 +480,31 @@ get_handle(struct gl_context *ctx, GLenum pname) } +/** + * 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->LinkStatus && + shProg->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { + return true; + } + + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetProgramv(linked geometry shader required)"); + return false; +} + + /** * glGetProgramiv() - get shader program state. * Note that this is for GLSL shader programs, not ARB vertex/fragment @@ -470,19 +519,19 @@ get_programiv(struct gl_context *ctx, GLuint program, GLenum pname, GLint *param /* Is transform feedback available in this context? */ const bool has_xfb = - (ctx->API == API_OPENGL && ctx->Extensions.EXT_transform_feedback) + (ctx->API == API_OPENGL_COMPAT && ctx->Extensions.EXT_transform_feedback) || ctx->API == API_OPENGL_CORE || _mesa_is_gles3(ctx); - /* Are geometry shaders available in this context? + /* 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_gs = - _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_geometry_shader4; + const bool has_core_gs = _mesa_is_desktop_gl(ctx) && ctx->Version >= 32; /* Are uniform buffer objects available in this context? */ const bool has_ubo = - (ctx->API == API_OPENGL && ctx->Extensions.ARB_uniform_buffer_object) + (ctx->API == API_OPENGL_COMPAT && ctx->Extensions.ARB_uniform_buffer_object) || ctx->API == API_OPENGL_CORE || _mesa_is_gles3(ctx); @@ -521,9 +570,11 @@ get_programiv(struct gl_context *ctx, GLuint program, GLenum pname, GLint *param GLint max_len = 0; for (i = 0; i < shProg->NumUserUniformStorage; i++) { - /* Add one for the terminating NUL character. + /* 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->UniformStorage[i].name) + 1; + const GLint len = strlen(shProg->UniformStorage[i].name) + 1 + + ((shProg->UniformStorage[i].array_elements != 0) ? 3 : 0); if (len > max_len) max_len = len; @@ -537,30 +588,52 @@ get_programiv(struct gl_context *ctx, GLuint program, GLenum pname, GLint *param break; *params = shProg->TransformFeedback.NumVarying; return; - case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: + case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: { + unsigned i; + GLint max_len = 0; if (!has_xfb) break; - *params = longest_feedback_varying_name(shProg) + 1; + + 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_ARB: - if (!has_gs) + case GL_GEOMETRY_VERTICES_OUT: + if (!has_core_gs) break; - *params = shProg->Geom.VerticesOut; + if (check_gs_query(ctx, shProg)) + *params = shProg->Geom.VerticesOut; return; - case GL_GEOMETRY_INPUT_TYPE_ARB: - if (!has_gs) + case GL_GEOMETRY_SHADER_INVOCATIONS: + if (!has_core_gs || !ctx->Extensions.ARB_gpu_shader5) break; - *params = shProg->Geom.InputType; + if (check_gs_query(ctx, shProg)) + *params = shProg->Geom.Invocations; return; - case GL_GEOMETRY_OUTPUT_TYPE_ARB: - if (!has_gs) + case GL_GEOMETRY_INPUT_TYPE: + if (!has_core_gs) break; - *params = shProg->Geom.OutputType; + if (check_gs_query(ctx, shProg)) + *params = shProg->Geom.InputType; + return; + case GL_GEOMETRY_OUTPUT_TYPE: + if (!has_core_gs) + break; + if (check_gs_query(ctx, shProg)) + *params = shProg->Geom.OutputType; return; case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: { unsigned i; @@ -587,6 +660,51 @@ get_programiv(struct gl_context *ctx, GLuint program, GLenum pname, GLint *param *params = shProg->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->NumAtomicBuffers; + return; + case GL_COMPUTE_WORK_GROUP_SIZE: { + int i; + if (!_mesa_is_desktop_gl(ctx) || !ctx->Extensions.ARB_compute_shader) + break; + if (!shProg->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 (!ctx->Extensions.ARB_separate_shader_objects) + break; + + *params = shProg->SeparateShader; + return; default: break; } @@ -711,20 +829,63 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj) if (!sh) return; - options = &ctx->ShaderCompilerOptions[_mesa_shader_type_to_index(sh->Type)]; + options = &ctx->ShaderCompilerOptions[sh->Stage]; /* set default pragma state for shader */ sh->Pragmas = options->DefaultPragmas; - /* this call will set the sh->CompileStatus field to indicate if - * compilation was successful. - */ - _mesa_glsl_compile_shader(ctx, sh); + 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) { + fprintf(stderr, "GLSL source for %s shader %d:\n", + _mesa_shader_stage_to_string(sh->Stage), sh->Name); + fprintf(stderr, "%s\n", sh->Source); + fflush(stderr); + } + + /* 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) { + fprintf(stderr, "GLSL IR for shader %d:\n", sh->Name); + _mesa_print_ir(stderr, sh->ir, NULL); + fprintf(stderr, "\n\n"); + } else { + fprintf(stderr, "GLSL shader %d failed to compile.\n", sh->Name); + } + if (sh->InfoLog && sh->InfoLog[0] != 0) { + fprintf(stderr, "GLSL shader %d info log:\n", sh->Name); + fprintf(stderr, "%s\n", sh->InfoLog); + } + fflush(stderr); + } - if (sh->CompileStatus == GL_FALSE && - (ctx->Shader.Flags & GLSL_REPORT_ERRORS)) { - _mesa_debug(ctx, "Error compiling shader %u:\n%s\n", - sh->Name, sh->InfoLog); + } + + if (!sh->CompileStatus) { + if (ctx->Shader.Flags & GLSL_DUMP_ON_ERROR) { + fprintf(stderr, "GLSL source for %s shader %d:\n", + _mesa_shader_stage_to_string(sh->Stage), sh->Name); + fprintf(stderr, "%s\n", sh->Source); + fprintf(stderr, "Info Log:\n%s\n", sh->InfoLog); + fflush(stderr); + } + + if (ctx->Shader.Flags & GLSL_REPORT_ERRORS) { + _mesa_debug(ctx, "Error compiling shader %u:\n%s\n", + sh->Name, sh->InfoLog); + } } } @@ -736,19 +897,19 @@ static void link_program(struct gl_context *ctx, GLuint program) { struct gl_shader_program *shProg; - struct gl_transform_feedback_object *obj = - ctx->TransformFeedback.CurrentObject; shProg = _mesa_lookup_shader_program_err(ctx, program, "glLinkProgram"); if (!shProg) return; - if (obj->Active - && (shProg == ctx->Shader.CurrentVertexProgram - || shProg == ctx->Shader.CurrentGeometryProgram - || shProg == ctx->Shader.CurrentFragmentProgram)) { + /* From the ARB_transform_feedback2 specification: + * "The error INVALID_OPERATION is generated by LinkProgram if 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 active)"); + "glLinkProgram(transform feedback is using the program)"); return; } @@ -789,21 +950,8 @@ print_shader_info(const struct gl_shader_program *shProg) printf("Mesa: glUseProgram(%u)\n", shProg->Name); for (i = 0; i < shProg->NumShaders; i++) { - const char *s; - switch (shProg->Shaders[i]->Type) { - case GL_VERTEX_SHADER: - s = "vertex"; - break; - case GL_FRAGMENT_SHADER: - s = "fragment"; - break; - case GL_GEOMETRY_SHADER: - s = "geometry"; - break; - default: - s = ""; - } - printf(" %s shader %u, checksum %u\n", s, + printf(" %s shader %u, checksum %u\n", + _mesa_shader_stage_to_string(shProg->Shaders[i]->Stage), shProg->Shaders[i]->Name, shProg->Shaders[i]->SourceChecksum); } @@ -839,37 +987,16 @@ _mesa_active_program(struct gl_context *ctx, struct gl_shader_program *shProg, /** */ -static bool +static void use_shader_program(struct gl_context *ctx, GLenum type, struct gl_shader_program *shProg) { struct gl_shader_program **target; + gl_shader_stage stage = _mesa_shader_enum_to_shader_stage(type); - switch (type) { - case GL_VERTEX_SHADER: - target = &ctx->Shader.CurrentVertexProgram; - if ((shProg == NULL) - || (shProg->_LinkedShaders[MESA_SHADER_VERTEX] == NULL)) { - shProg = NULL; - } - break; - case GL_GEOMETRY_SHADER_ARB: - target = &ctx->Shader.CurrentGeometryProgram; - if ((shProg == NULL) - || (shProg->_LinkedShaders[MESA_SHADER_GEOMETRY] == NULL)) { - shProg = NULL; - } - break; - case GL_FRAGMENT_SHADER: - target = &ctx->Shader.CurrentFragmentProgram; - if ((shProg == NULL) - || (shProg->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL)) { - shProg = NULL; - } - break; - default: - return false; - } + target = &ctx->Shader.CurrentProgram[stage]; + if ((shProg == NULL) || (shProg->_LinkedShaders[stage] == NULL)) + shProg = NULL; if (*target != shProg) { FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS); @@ -885,6 +1012,9 @@ use_shader_program(struct gl_context *ctx, GLenum type, case GL_GEOMETRY_SHADER_ARB: /* Empty for now. */ break; + case GL_COMPUTE_SHADER: + /* Empty for now. */ + break; case GL_FRAGMENT_SHADER: if (*target == ctx->Shader._CurrentFragmentProgram) { _mesa_reference_shader_program(ctx, @@ -895,10 +1025,8 @@ use_shader_program(struct gl_context *ctx, GLenum type, } _mesa_reference_shader_program(ctx, target, shProg); - return true; + return; } - - return false; } /** @@ -910,6 +1038,7 @@ _mesa_use_program(struct gl_context *ctx, struct gl_shader_program *shProg) use_shader_program(ctx, GL_VERTEX_SHADER, shProg); use_shader_program(ctx, GL_GEOMETRY_SHADER_ARB, shProg); use_shader_program(ctx, GL_FRAGMENT_SHADER, shProg); + use_shader_program(ctx, GL_COMPUTE_SHADER, shProg); _mesa_active_program(ctx, shProg, "glUseProgram"); if (ctx->Driver.UseProgram) @@ -1000,7 +1129,7 @@ _mesa_AttachShader(GLuint program, GLuint shader) void GLAPIENTRY -_mesa_CompileShaderARB(GLhandleARB shaderObj) +_mesa_CompileShader(GLhandleARB shaderObj) { GET_CURRENT_CONTEXT(ctx); if (MESA_VERBOSE & VERBOSE_API) @@ -1214,7 +1343,7 @@ _mesa_GetShaderInfoLog(GLuint shader, GLsizei bufSize, void GLAPIENTRY -_mesa_GetShaderSourceARB(GLhandleARB shader, GLsizei maxLength, +_mesa_GetShaderSource(GLhandleARB shader, GLsizei maxLength, GLsizei *length, GLcharARB *sourceOut) { GET_CURRENT_CONTEXT(ctx); @@ -1247,7 +1376,7 @@ _mesa_IsShader(GLuint name) void GLAPIENTRY -_mesa_LinkProgramARB(GLhandleARB programObj) +_mesa_LinkProgram(GLhandleARB programObj) { GET_CURRENT_CONTEXT(ctx); link_program(ctx, programObj); @@ -1290,8 +1419,8 @@ read_shader(const char *fname) * and pass it to _mesa_shader_source(). */ void GLAPIENTRY -_mesa_ShaderSourceARB(GLhandleARB shaderObj, GLsizei count, - const GLcharARB ** string, const GLint * length) +_mesa_ShaderSource(GLhandleARB shaderObj, GLsizei count, + const GLcharARB * const * string, const GLint * length) { GET_CURRENT_CONTEXT(ctx); GLint *offsets; @@ -1384,16 +1513,12 @@ _mesa_ShaderSourceARB(GLhandleARB shaderObj, GLsizei count, void GLAPIENTRY -_mesa_UseProgramObjectARB(GLhandleARB program) +_mesa_UseProgram(GLhandleARB program) { GET_CURRENT_CONTEXT(ctx); struct gl_shader_program *shProg; - struct gl_transform_feedback_object *obj = - ctx->TransformFeedback.CurrentObject; - - ASSERT_OUTSIDE_BEGIN_END(ctx); - if (obj->Active && !obj->Paused) { + if (_mesa_is_xfb_active_and_unpaused(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUseProgram(transform feedback active)"); return; @@ -1419,17 +1544,45 @@ _mesa_UseProgramObjectARB(GLhandleARB program) shProg = NULL; } - _mesa_use_program(ctx, shProg); + /* The "Dependencies on EXT_separate_shader_objects" section of 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 for any shader stage or for uniform updates established by + * UseProgram, UseShaderProgramEXT, or ActiveProgramEXT, the current + * program for that stage (if any) is considered current. Otherwise, + * if there is a bound program pipeline object ..." + */ + 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_ValidateProgramARB(GLhandleARB program) +_mesa_ValidateProgram(GLhandleARB 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) @@ -1440,10 +1593,10 @@ _mesa_GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, switch (shadertype) { case GL_VERTEX_SHADER: - limits = &ctx->Const.VertexProgram; + limits = &ctx->Const.Program[MESA_SHADER_VERTEX]; break; case GL_FRAGMENT_SHADER: - limits = &ctx->Const.FragmentProgram; + limits = &ctx->Const.Program[MESA_SHADER_FRAGMENT]; break; default: _mesa_error(ctx, GL_INVALID_ENUM, @@ -1482,6 +1635,9 @@ _mesa_GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, } +/** + * For OpenGL ES 2.0, GL_ARB_ES2_compatibility + */ void GLAPIENTRY _mesa_ReleaseShaderCompiler(void) { @@ -1489,6 +1645,9 @@ _mesa_ReleaseShaderCompiler(void) } +/** + * 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) @@ -1504,12 +1663,62 @@ _mesa_ShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, void GLAPIENTRY -_mesa_ProgramParameteriARB(GLuint program, GLenum pname, GLint value) +_mesa_GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei *length, + GLenum *binaryFormat, GLvoid *binary) +{ + struct gl_shader_program *shProg; + GET_CURRENT_CONTEXT(ctx); + + shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetProgramBinary"); + if (!shProg) + return; + + if (!shProg->LinkStatus) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetProgramBinary(program %u not linked)", + shProg->Name); + return; + } + + if (bufSize < 0){ + _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramBinary(bufSize < 0)"); + return; + } + + /* The ARB_get_program_binary spec says: + * + * "If is NULL, then no length is returned." + */ + if (length != NULL) + *length = 0; + + (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); - ASSERT_OUTSIDE_BEGIN_END(ctx); + shProg = _mesa_lookup_shader_program_err(ctx, program, "glProgramBinary"); + if (!shProg) + return; + + (void) binaryFormat; + (void) binary; + (void) length; + _mesa_error(ctx, GL_INVALID_OPERATION, __FUNCTION__); +} + + +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"); @@ -1517,51 +1726,76 @@ _mesa_ProgramParameteriARB(GLuint program, GLenum pname, GLint value) return; switch (pname) { - case GL_GEOMETRY_VERTICES_OUT_ARB: - if (value < 1 || - (unsigned) value > ctx->Const.MaxGeometryOutputVertices) { + 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 argument to + * ProgramParameteri is not TRUE or FALSE." + */ + if (value != GL_TRUE && value != GL_FALSE) { _mesa_error(ctx, GL_INVALID_VALUE, - "glProgramParameteri(GL_GEOMETRY_VERTICES_OUT_ARB=%d", + "glProgramParameteri(pname=%s, value=%d): " + "value must be 0 or 1.", + _mesa_lookup_enum_by_nr(pname), value); return; } - shProg->Geom.VerticesOut = value; - break; - case GL_GEOMETRY_INPUT_TYPE_ARB: - switch (value) { - case GL_POINTS: - case GL_LINES: - case GL_LINES_ADJACENCY_ARB: - case GL_TRIANGLES: - case GL_TRIANGLES_ADJACENCY_ARB: - shProg->Geom.InputType = value; - break; - default: - _mesa_error(ctx, GL_INVALID_VALUE, - "glProgramParameteri(geometry input type = %s", - _mesa_lookup_enum_by_nr(value)); - return; - } - break; - case GL_GEOMETRY_OUTPUT_TYPE_ARB: - switch (value) { - case GL_POINTS: - case GL_LINE_STRIP: - case GL_TRIANGLE_STRIP: - shProg->Geom.OutputType = 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 + * PROGRAM_BINARY_RETRIEVABLE_HINT and 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: + if (!ctx->Extensions.ARB_separate_shader_objects) break; - default: + + /* 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) { _mesa_error(ctx, GL_INVALID_VALUE, - "glProgramParameteri(geometry output type = %s", - _mesa_lookup_enum_by_nr(value)); + "glProgramParameteri(pname=%s, value=%d): " + "value must be 0 or 1.", + _mesa_lookup_enum_by_nr(pname), + value); return; } - break; + shProg->SeparateShader = value; + return; + default: - _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameteriARB(pname=%s)", - _mesa_lookup_enum_by_nr(pname)); break; } + + _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameteri(pname=%s)", + _mesa_lookup_enum_by_nr(pname)); } void @@ -1584,15 +1818,12 @@ _mesa_UseShaderProgramEXT(GLenum type, GLuint program) GET_CURRENT_CONTEXT(ctx); struct gl_shader_program *shProg = NULL; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!validate_shader_target(ctx, type)) { + if (!_mesa_validate_shader_target(ctx, type)) { _mesa_error(ctx, GL_INVALID_ENUM, "glUseShaderProgramEXT(type)"); return; } - if (ctx->TransformFeedback.CurrentObject->Active && - !ctx->TransformFeedback.CurrentObject->Paused) { + if (_mesa_is_xfb_active_and_unpaused(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUseShaderProgramEXT(transform feedback is active)"); return; @@ -1611,7 +1842,41 @@ _mesa_UseShaderProgramEXT(GLenum type, GLuint program) } } - _mesa_use_shader_program(ctx, type, shProg); + /* The "Dependencies on EXT_separate_shader_objects" section of 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 for any shader stage or for uniform updates established by + * UseProgram, UseShaderProgramEXT, or ActiveProgramEXT, the current + * program for that stage (if any) is considered current. Otherwise, + * if there is a bound program pipeline object ..." + */ + if (program) { + /* Attach shader state to the binding point */ + _mesa_reference_pipeline_object(ctx, &ctx->_Shader, &ctx->Shader); + /* Update the program */ + _mesa_use_shader_program(ctx, type, shProg); + } else { + /* Must be done first: detach the progam */ + _mesa_use_shader_program(ctx, type, shProg); + + /* Nothing remains current */ + if (!ctx->Shader.CurrentVertexProgram && + !ctx->Shader.CurrentGeometryProgram && + !ctx->Shader.CurrentFragmentProgram && + !ctx->Shader.ActiveProgram) { + + /* 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); + } + } + } } @@ -1626,23 +1891,50 @@ _mesa_ActiveProgramEXT(GLuint program) ? _mesa_lookup_shader_program_err(ctx, program, "glActiveProgramEXT") : NULL; - _mesa_active_program(ctx, shProg, "glActiveProgramEXT"); + /* The "Dependencies on EXT_separate_shader_objects" section of 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 for any shader stage or for uniform updates established by + * UseProgram, UseShaderProgramEXT, or ActiveProgramEXT, the current + * program for that stage (if any) is considered current. Otherwise, + * if there is a bound program pipeline object ..." + */ + if (shProg != NULL) { + /* Attach shader state to the binding point */ + _mesa_reference_pipeline_object(ctx, &ctx->_Shader, &ctx->Shader); + _mesa_active_program(ctx, shProg, "glActiveProgramEXT"); + } else { + /* Must be done first: unset the current active progam */ + _mesa_active_program(ctx, shProg, "glActiveProgramEXT"); + + /* Nothing remains current */ + if (!ctx->Shader.CurrentVertexProgram && !ctx->Shader.CurrentGeometryProgram && + !ctx->Shader.CurrentFragmentProgram && !ctx->Shader.ActiveProgram) { + + /* 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); + } + } + } + return; } - -/** - * For GL_EXT_separate_shader_objects - */ -GLuint GLAPIENTRY -_mesa_CreateShaderProgramEXT(GLenum type, const GLchar *string) +static GLuint +_mesa_create_shader_program(struct gl_context* ctx, GLboolean separate, + GLenum type, GLsizei count, const GLchar* const *strings) { - GET_CURRENT_CONTEXT(ctx); const GLuint shader = create_shader(ctx, type); GLuint program = 0; if (shader) { - shader_source(ctx, shader, _mesa_strdup(string)); + _mesa_ShaderSource(shader, count, strings, NULL); + compile_shader(ctx, shader); program = create_shader_program(ctx); @@ -1654,6 +1946,8 @@ _mesa_CreateShaderProgramEXT(GLenum type, const GLchar *string) shProg = _mesa_lookup_shader_program(ctx, program); sh = _mesa_lookup_shader(ctx, shader); + shProg->SeparateShader = separate; + get_shaderiv(ctx, shader, GL_COMPILE_STATUS, &compiled); if (compiled) { attach_shader(ctx, program, shader); @@ -1678,68 +1972,66 @@ _mesa_CreateShaderProgramEXT(GLenum type, const GLchar *string) return program; } + /** - * Plug in shader-related functions into API dispatch table. + * Copy program-specific data generated by linking from the gl_shader_program + * object to a specific gl_program object. */ void -_mesa_init_shader_dispatch(struct _glapi_table *exec) -{ -#if FEATURE_GL - /* GL_ARB_vertex/fragment_shader */ - SET_DeleteObjectARB(exec, _mesa_DeleteObjectARB); - SET_GetHandleARB(exec, _mesa_GetHandleARB); - SET_DetachObjectARB(exec, _mesa_DetachObjectARB); - SET_CreateShaderObjectARB(exec, _mesa_CreateShaderObjectARB); - SET_ShaderSourceARB(exec, _mesa_ShaderSourceARB); - SET_CompileShaderARB(exec, _mesa_CompileShaderARB); - SET_CreateProgramObjectARB(exec, _mesa_CreateProgramObjectARB); - SET_AttachObjectARB(exec, _mesa_AttachObjectARB); - SET_LinkProgramARB(exec, _mesa_LinkProgramARB); - SET_UseProgramObjectARB(exec, _mesa_UseProgramObjectARB); - SET_ValidateProgramARB(exec, _mesa_ValidateProgramARB); - SET_GetObjectParameterfvARB(exec, _mesa_GetObjectParameterfvARB); - SET_GetObjectParameterivARB(exec, _mesa_GetObjectParameterivARB); - SET_GetInfoLogARB(exec, _mesa_GetInfoLogARB); - SET_GetAttachedObjectsARB(exec, _mesa_GetAttachedObjectsARB); - SET_GetShaderSourceARB(exec, _mesa_GetShaderSourceARB); - - /* OpenGL 2.0 */ - SET_AttachShader(exec, _mesa_AttachShader); - SET_CreateProgram(exec, _mesa_CreateProgram); - SET_CreateShader(exec, _mesa_CreateShader); - SET_DeleteProgram(exec, _mesa_DeleteProgram); - SET_DeleteShader(exec, _mesa_DeleteShader); - SET_DetachShader(exec, _mesa_DetachShader); - SET_GetAttachedShaders(exec, _mesa_GetAttachedShaders); - SET_GetProgramiv(exec, _mesa_GetProgramiv); - SET_GetProgramInfoLog(exec, _mesa_GetProgramInfoLog); - SET_GetShaderiv(exec, _mesa_GetShaderiv); - SET_GetShaderInfoLog(exec, _mesa_GetShaderInfoLog); - SET_IsProgram(exec, _mesa_IsProgram); - SET_IsShader(exec, _mesa_IsShader); - - /* GL_ARB_vertex_shader */ - SET_BindAttribLocationARB(exec, _mesa_BindAttribLocationARB); - SET_GetActiveAttribARB(exec, _mesa_GetActiveAttribARB); - SET_GetAttribLocationARB(exec, _mesa_GetAttribLocationARB); - - SET_ProgramParameteriARB(exec, _mesa_ProgramParameteriARB); - - SET_UseShaderProgramEXT(exec, _mesa_UseShaderProgramEXT); - SET_ActiveProgramEXT(exec, _mesa_ActiveProgramEXT); - SET_CreateShaderProgramEXT(exec, _mesa_CreateShaderProgramEXT); - - /* GL_EXT_gpu_shader4 / GL 3.0 */ - SET_BindFragDataLocationEXT(exec, _mesa_BindFragDataLocation); - SET_GetFragDataLocationEXT(exec, _mesa_GetFragDataLocation); - - /* GL_ARB_ES2_compatibility */ - SET_ReleaseShaderCompiler(exec, _mesa_ReleaseShaderCompiler); - SET_GetShaderPrecisionFormat(exec, _mesa_GetShaderPrecisionFormat); - - /* GL_ARB_blend_func_extended */ - SET_BindFragDataLocationIndexed(exec, _mesa_BindFragDataLocationIndexed); - SET_GetFragDataIndex(exec, _mesa_GetFragDataIndex); -#endif /* FEATURE_GL */ +_mesa_copy_linked_program_data(gl_shader_stage type, + const struct gl_shader_program *src, + struct gl_program *dst) +{ + switch (type) { + case MESA_SHADER_VERTEX: + dst->UsesClipDistanceOut = src->Vert.UsesClipDistance; + break; + case MESA_SHADER_GEOMETRY: { + struct gl_geometry_program *dst_gp = (struct gl_geometry_program *) dst; + dst_gp->VerticesIn = src->Geom.VerticesIn; + dst_gp->VerticesOut = src->Geom.VerticesOut; + dst_gp->Invocations = src->Geom.Invocations; + dst_gp->InputType = src->Geom.InputType; + dst_gp->OutputType = src->Geom.OutputType; + dst->UsesClipDistanceOut = src->Geom.UsesClipDistance; + dst_gp->UsesEndPrimitive = src->Geom.UsesEndPrimitive; + } + break; + case MESA_SHADER_COMPUTE: { + struct gl_compute_program *dst_cp = (struct gl_compute_program *) dst; + int i; + for (i = 0; i < 3; i++) + dst_cp->LocalSize[i] = src->Comp.LocalSize[i]; + } + break; + default: + break; + } } + +/** + * For GL_EXT_separate_shader_objects + */ +GLuint GLAPIENTRY +_mesa_CreateShaderProgramEXT(GLenum type, const GLchar *string) +{ + GET_CURRENT_CONTEXT(ctx); + + return _mesa_create_shader_program(ctx, GL_FALSE, type, 1, &string); +} + +/** + * ARB_separate_shader_objects: Compile & Link Program + * + * Basically the same as _mesa_CreateShaderProgramEXT but with support of + * multiple strings and sets the SeparateShader flag to true. + */ +GLuint GLAPIENTRY +_mesa_CreateShaderProgramv(GLenum type, GLsizei count, + const GLchar* const *strings) +{ + GET_CURRENT_CONTEXT(ctx); + + return _mesa_create_shader_program(ctx, GL_TRUE, type, count, strings); +}