*
* 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 <errno.h>
#include <stdbool.h>
+#include <c99_alloca.h>
+
#include "main/glheader.h"
#include "main/context.h"
-#include "main/dispatch.h"
#include "main/enums.h"
+#include "main/glspirv.h"
#include "main/hash.h"
#include "main/mtypes.h"
#include "main/pipelineobj.h"
+#include "main/program_binary.h"
#include "main/shaderapi.h"
#include "main/shaderobj.h"
+#include "main/state.h"
#include "main/transformfeedback.h"
#include "main/uniforms.h"
+#include "compiler/glsl/builtin_functions.h"
#include "compiler/glsl/glsl_parser_extras.h"
#include "compiler/glsl/ir.h"
#include "compiler/glsl/ir_uniform.h"
#include "util/hash_table.h"
#include "util/mesa-sha1.h"
#include "util/crc32.h"
+#include "util/os_file.h"
+#include "util/simple_list.h"
+#include "util/u_string.h"
/**
* Return mask of GLSL_x flags by examining the MESA_GLSL env var.
flags |= GLSL_DUMP;
if (strstr(env, "log"))
flags |= GLSL_LOG;
+ if (strstr(env, "cache_fb"))
+ flags |= GLSL_CACHE_FALLBACK;
+ if (strstr(env, "cache_info"))
+ flags |= GLSL_CACHE_INFO;
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"))
/* 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 (int i = 0; i < MESA_SHADER_STAGES; i++) {
_mesa_reference_program(ctx, &ctx->Shader.CurrentProgram[i], NULL);
+ _mesa_reference_shader_program(ctx,
+ &ctx->Shader.ReferencedPrograms[i],
+ NULL);
+ free(ctx->SubroutineIndex[i].IndexPtr);
+ ctx->SubroutineIndex[i].IndexPtr = 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);
}
* Attach shader to a shader program.
*/
static void
-attach_shader(struct gl_context *ctx, GLuint program, GLuint shader)
+attach_shader(struct gl_context *ctx, struct gl_shader_program *shProg,
+ struct gl_shader *sh)
+{
+ GLuint n = shProg->NumShaders;
+
+ 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 void
+attach_shader_err(struct gl_context *ctx, GLuint program, GLuint shader,
+ const char *caller)
{
struct gl_shader_program *shProg;
struct gl_shader *sh;
const bool same_type_disallowed = _mesa_is_gles(ctx);
- shProg = _mesa_lookup_shader_program_err(ctx, program, "glAttachShader");
+ shProg = _mesa_lookup_shader_program_err(ctx, program, caller);
if (!shProg)
return;
- sh = _mesa_lookup_shader_err(ctx, shader, "glAttachShader");
+ sh = _mesa_lookup_shader_err(ctx, shader, caller);
if (!sh) {
return;
}
* "The error INVALID_OPERATION is generated by AttachObjectARB
* if <obj> is already attached to <containerObj>."
*/
- _mesa_error(ctx, GL_INVALID_OPERATION, "glAttachShader");
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
return;
} else if (same_type_disallowed &&
shProg->Shaders[i]->Stage == sh->Stage) {
* is generated if [...] another shader object of the same type
* as shader is already attached to program."
*/
- _mesa_error(ctx, GL_INVALID_OPERATION, "glAttachShader");
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
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++;
+ attach_shader(ctx, shProg, sh);
}
+static void
+attach_shader_no_error(struct gl_context *ctx, GLuint program, GLuint shader)
+{
+ struct gl_shader_program *shProg;
+ struct gl_shader *sh;
+
+ shProg = _mesa_lookup_shader_program(ctx, program);
+ sh = _mesa_lookup_shader(ctx, shader);
+
+ attach_shader(ctx, shProg, sh);
+}
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));
}
+static GLuint
+create_shader_err(struct gl_context *ctx, GLenum type, const char *caller)
+{
+ if (!_mesa_validate_shader_target(ctx, type)) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "%s(%s)",
+ caller, _mesa_enum_to_string(type));
+ return 0;
+ }
+
+ return create_shader(ctx, type);
+}
+
+
static GLuint
create_shader_program(struct gl_context *ctx)
{
}
-static void
-detach_shader(struct gl_context *ctx, GLuint program, GLuint shader)
+static ALWAYS_INLINE void
+detach_shader(struct gl_context *ctx, GLuint program, GLuint shader,
+ bool no_error)
{
struct gl_shader_program *shProg;
GLuint n;
GLuint i, j;
- shProg = _mesa_lookup_shader_program_err(ctx, program, "glDetachShader");
- if (!shProg)
- return;
+ if (!no_error) {
+ shProg = _mesa_lookup_shader_program_err(ctx, program, "glDetachShader");
+ if (!shProg)
+ return;
+ } else {
+ shProg = _mesa_lookup_shader_program(ctx, program);
+ }
n = shProg->NumShaders;
shProg->Shaders = newList;
shProg->NumShaders = n - 1;
-#ifdef DEBUG
+#ifndef NDEBUG
/* 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 ||
}
/* not found */
- {
+ if (!no_error) {
GLenum err;
if (is_shader(ctx, shader) || is_program(ctx, shader))
err = GL_INVALID_OPERATION;
}
+static void
+detach_shader_error(struct gl_context *ctx, GLuint program, GLuint shader)
+{
+ detach_shader(ctx, program, shader, false);
+}
+
+
+static void
+detach_shader_no_error(struct gl_context *ctx, GLuint program, GLuint shader)
+{
+ detach_shader(ctx, program, shader, true);
+}
+
+
/**
* Return list of shaders attached to shader program.
+ * \param objOut returns GLuint ids
+ * \param handleOut returns GLhandleARB handles
*/
static void
get_attached_shaders(struct gl_context *ctx, GLuint program, GLsizei maxCount,
- GLsizei *count, GLuint *obj)
+ GLsizei *countOut, GLuint *objOut, GLhandleARB *handleOut)
{
struct gl_shader_program *shProg;
if (shProg) {
GLuint i;
for (i = 0; i < (GLuint) maxCount && i < shProg->NumShaders; i++) {
- obj[i] = shProg->Shaders[i]->Name;
+ if (objOut) {
+ objOut[i] = shProg->Shaders[i]->Name;
+ }
+
+ if (handleOut) {
+ handleOut[i] = (GLhandleARB) shProg->Shaders[i]->Name;
+ }
+ }
+ if (countOut) {
+ *countOut = i;
}
- if (count)
- *count = i;
}
}
-
/**
* glGetHandleARB() - return ID/name of currently bound shader program.
*/
return false;
}
+/**
+ * Return the length of a string, or 0 if the pointer passed in is NULL
+ */
+static size_t strlen_or_zero(const char *s)
+{
+ return s ? strlen(s) : 0;
+}
/**
* glGetProgramiv() - get shader program state.
/* 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_gs = _mesa_has_geometry_shaders(ctx);
const bool has_tess = _mesa_has_tessellation(ctx);
/* Are uniform buffer objects available in this context?
case GL_DELETE_STATUS:
*params = shProg->DeletePending;
return;
+ case GL_COMPLETION_STATUS_ARB:
+ if (ctx->Driver.GetShaderProgramCompletionStatus)
+ *params = ctx->Driver.GetShaderProgramCompletionStatus(ctx, shProg);
+ else
+ *params = GL_TRUE;
+ return;
case GL_LINK_STATUS:
- *params = shProg->data->LinkStatus;
+ *params = shProg->data->LinkStatus ? GL_TRUE : GL_FALSE;
return;
case GL_VALIDATE_STATUS:
*params = shProg->data->Validated;
if (shProg->data->UniformStorage[i].is_shader_storage)
continue;
+ /* From ARB_gl_spirv spec:
+ *
+ * "If pname is ACTIVE_UNIFORM_MAX_LENGTH, the length of the
+ * longest active uniform name, including a null terminator, is
+ * returned. If no active uniforms exist, zero is returned. If no
+ * name reflection information is available, one is returned."
+ *
+ * We are setting 0 here, as below it will add 1 for the NUL character.
+ */
+ const GLint base_len =
+ strlen_or_zero(shProg->data->UniformStorage[i].name);
+
/* 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);
+ const GLint len = base_len + 1 +
+ ((shProg->data->UniformStorage[i].array_elements != 0) ? 3 : 0);
if (len > max_len)
max_len = len;
case GL_TRANSFORM_FEEDBACK_VARYINGS:
if (!has_xfb)
break;
- *params = shProg->TransformFeedback.NumVarying;
+
+ /* Check first if there are transform feedback varyings specified in the
+ * shader (ARB_enhanced_layouts). If there isn't any, return the number of
+ * varyings specified using the API.
+ */
+ if (shProg->last_vert_prog &&
+ shProg->last_vert_prog->sh.LinkedTransformFeedback->NumVarying > 0)
+ *params =
+ shProg->last_vert_prog->sh.LinkedTransformFeedback->NumVarying;
+ else
+ *params = shProg->TransformFeedback.NumVarying;
return;
case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: {
unsigned i;
GLint max_len = 0;
+ bool in_shader_varyings;
+ int num_varying;
+
if (!has_xfb)
break;
- for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
- /* Add one for the terminating NUL character.
+ /* Check first if there are transform feedback varyings specified in the
+ * shader (ARB_enhanced_layouts). If there isn't any, use the ones
+ * specified using the API.
+ */
+ in_shader_varyings = shProg->last_vert_prog &&
+ shProg->last_vert_prog->sh.LinkedTransformFeedback->NumVarying > 0;
+
+ num_varying = in_shader_varyings ?
+ shProg->last_vert_prog->sh.LinkedTransformFeedback->NumVarying :
+ shProg->TransformFeedback.NumVarying;
+
+ for (i = 0; i < num_varying; i++) {
+ const char *name = in_shader_varyings ?
+ shProg->last_vert_prog->sh.LinkedTransformFeedback->Varyings[i].Name
+ : shProg->TransformFeedback.VaryingNames[i];
+
+ /* Add one for the terminating NUL character. We have to use
+ * strlen_or_zero, as for shaders constructed from SPIR-V binaries,
+ * it is possible that no name reflection information is available.
*/
- const GLint len =
- strlen(shProg->TransformFeedback.VaryingNames[i]) + 1;
+ const GLint len = strlen_or_zero(name) + 1;
if (len > max_len)
max_len = len;
*params = shProg->TransformFeedback.BufferMode;
return;
case GL_GEOMETRY_VERTICES_OUT:
- if (!has_core_gs)
+ if (!has_gs)
break;
if (check_gs_query(ctx, shProg)) {
*params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
}
return;
case GL_GEOMETRY_SHADER_INVOCATIONS:
- if (!has_core_gs || !ctx->Extensions.ARB_gpu_shader5)
+ if (!has_gs ||
+ (_mesa_is_desktop_gl(ctx) && !ctx->Extensions.ARB_gpu_shader5)) {
break;
+ }
if (check_gs_query(ctx, shProg)) {
*params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
Program->info.gs.invocations;
}
return;
case GL_GEOMETRY_INPUT_TYPE:
- if (!has_core_gs)
+ if (!has_gs)
break;
if (check_gs_query(ctx, shProg)) {
*params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
}
return;
case GL_GEOMETRY_OUTPUT_TYPE:
- if (!has_core_gs)
+ if (!has_gs)
break;
if (check_gs_query(ctx, shProg)) {
*params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
break;
for (i = 0; i < shProg->data->NumUniformBlocks; i++) {
- /* Add one for the terminating NUL character.
+ /* Add one for the terminating NUL character. Name can be NULL, in
+ * that case, from ARB_gl_spirv:
+ * "If pname is ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, the length of
+ * the longest active uniform block name, including the null
+ * terminator, is returned. If no active uniform blocks exist,
+ * zero is returned. If no name reflection information is
+ * available, one is returned."
*/
- const GLint len = strlen(shProg->data->UniformBlocks[i].Name) + 1;
+ const GLint len =
+ strlen_or_zero(shProg->data->UniformBlocks[i].Name) + 1;
if (len > max_len)
max_len = len;
if (!_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx))
break;
- *params = shProg->BinaryRetreivableHint;
+ *params = shProg->BinaryRetrievableHint;
return;
case GL_PROGRAM_BINARY_LENGTH:
- *params = 0;
+ if (ctx->Const.NumProgramBinaryFormats == 0 || !shProg->data->LinkStatus) {
+ *params = 0;
+ } else {
+ _mesa_get_program_binary_length(ctx, shProg, params);
+ }
return;
case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS:
if (!ctx->Extensions.ARB_shader_atomic_counters)
}
case GL_PROGRAM_SEPARABLE:
/* If the program has not been linked, return initial value 0. */
- *params = (shProg->data->LinkStatus == GL_FALSE) ? 0 : shProg->SeparateShader;
+ *params = (shProg->data->LinkStatus == LINKING_FAILURE) ? 0 : shProg->SeparateShader;
return;
/* ARB_tessellation_shader */
case GL_DELETE_STATUS:
*params = shader->DeletePending;
break;
+ case GL_COMPLETION_STATUS_ARB:
+ /* _mesa_glsl_compile_shader is not offloaded to other threads. */
+ *params = GL_TRUE;
+ return;
case GL_COMPILE_STATUS:
- *params = shader->CompileStatus;
+ *params = shader->CompileStatus ? GL_TRUE : GL_FALSE;
break;
case GL_INFO_LOG_LENGTH:
*params = (shader->InfoLog && shader->InfoLog[0] != '\0') ?
case GL_SHADER_SOURCE_LENGTH:
*params = shader->Source ? strlen((char *) shader->Source) + 1 : 0;
break;
+ case GL_SPIR_V_BINARY_ARB:
+ *params = (shader->spirv_data != NULL);
+ break;
default:
_mesa_error(ctx, GL_INVALID_ENUM, "glGetShaderiv(pname)");
return;
* glShaderSource[ARB].
*/
static void
-shader_source(struct gl_shader *sh, const GLchar *source)
+set_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;
+ /* The GL_ARB_gl_spirv spec adds the following to the end of the description
+ * of ShaderSource:
+ *
+ * "If <shader> was previously associated with a SPIR-V module (via the
+ * ShaderBinary command), that association is broken. Upon successful
+ * completion of this command the SPIR_V_BINARY_ARB state of <shader>
+ * is set to FALSE."
+ */
+ _mesa_shader_spirv_data_reference(&sh->spirv_data, NULL);
+
+ if (sh->CompileStatus == COMPILE_SKIPPED && !sh->FallbackSource) {
+ /* If shader was previously compiled back-up the source in case of cache
+ * fallback.
+ */
+ sh->FallbackSource = sh->Source;
+ sh->Source = source;
+ } else {
+ /* 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
}
+static void
+ensure_builtin_types(struct gl_context *ctx)
+{
+ if (!ctx->shader_builtin_ref) {
+ _mesa_glsl_builtin_functions_init_or_ref();
+ ctx->shader_builtin_ref = true;
+ }
+}
/**
* Compile a shader.
if (!sh)
return;
+ /* The GL_ARB_gl_spirv spec says:
+ *
+ * "Add a new error for the CompileShader command:
+ *
+ * An INVALID_OPERATION error is generated if the SPIR_V_BINARY_ARB
+ * state of <shader> is TRUE."
+ */
+ if (sh->spirv_data) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "glCompileShader(SPIR-V)");
+ 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;
+ sh->CompileStatus = COMPILE_FAILURE;
} else {
if (ctx->_Shader->Flags & GLSL_DUMP) {
_mesa_log("GLSL source for %s shader %d:\n",
_mesa_log("%s\n", sh->Source);
}
+ ensure_builtin_types(ctx);
+
/* this call will set the shader->CompileStatus field to indicate if
* compilation was successful.
*/
- _mesa_glsl_compile_shader(ctx, sh, false, false);
+ _mesa_glsl_compile_shader(ctx, sh, false, false, false);
if (ctx->_Shader->Flags & GLSL_LOG) {
_mesa_write_shader_to_file(sh);
}
+struct update_programs_in_pipeline_params
+{
+ struct gl_context *ctx;
+ struct gl_shader_program *shProg;
+};
+
+static void
+update_programs_in_pipeline(GLuint key, void *data, void *userData)
+{
+ struct update_programs_in_pipeline_params *params =
+ (struct update_programs_in_pipeline_params *) userData;
+ struct gl_pipeline_object *obj = (struct gl_pipeline_object *) data;
+
+ for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+ if (obj->CurrentProgram[stage] &&
+ obj->CurrentProgram[stage]->Id == params->shProg->Name) {
+ struct gl_program *prog = params->shProg->_LinkedShaders[stage]->Program;
+ _mesa_use_program(params->ctx, stage, params->shProg, prog, obj);
+ }
+ }
+}
+
+
/**
* Link a program's shaders.
*/
-void
-_mesa_link_program(struct gl_context *ctx, struct gl_shader_program *shProg)
+static ALWAYS_INLINE void
+link_program(struct gl_context *ctx, struct gl_shader_program *shProg,
+ bool no_error)
{
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;
+ if (!no_error) {
+ /* 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;
+ }
}
unsigned programs_in_use = 0;
ctx->_Shader->CurrentProgram[stage]->Id == shProg->Name) {
programs_in_use |= 1 << stage;
}
- }
+ }
+
+ ensure_builtin_types(ctx);
FLUSH_VERTICES(ctx, 0);
_mesa_glsl_link_shader(ctx, shProg);
* the state of any program pipeline for all stages where the program
* is attached."
*/
- if (shProg->data->LinkStatus && programs_in_use) {
+ if (shProg->data->LinkStatus) {
while (programs_in_use) {
const int stage = u_bit_scan(&programs_in_use);
if (shProg->_LinkedShaders[stage])
prog = shProg->_LinkedShaders[stage]->Program;
- _mesa_use_program(ctx, stage, prog, ctx->_Shader);
+ _mesa_use_program(ctx, stage, shProg, prog, ctx->_Shader);
+ }
+
+ if (ctx->Pipeline.Objects) {
+ struct update_programs_in_pipeline_params params = {
+ .ctx = ctx,
+ .shProg = shProg
+ };
+ _mesa_HashWalk(ctx->Pipeline.Objects, update_programs_in_pipeline,
+ ¶ms);
}
}
/* 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",
+ /* Find an unused filename. */
+ FILE *file = NULL;
+ char *filename = NULL;
+ for (unsigned i = 0;; i++) {
+ if (i) {
+ filename = ralloc_asprintf(NULL, "%s/%u-%u.shader_test",
+ capture_path, shProg->Name, i);
+ } else {
+ filename = ralloc_asprintf(NULL, "%s/%u.shader_test",
capture_path, shProg->Name);
- file = fopen(filename, "w");
+ }
+ file = os_file_create_unique(filename, 0644);
+ if (file)
+ break;
+ /* If we are failing for another reason than "this filename already
+ * exists", we are likely to fail again with another filename, so
+ * let's just give up */
+ if (errno != EEXIST)
+ break;
+ ralloc_free(filename);
+ }
if (file) {
fprintf(file, "[require]\nGLSL%s >= %u.%02u\n",
shProg->IsES ? " ES" : "",
ralloc_free(filename);
}
- if (shProg->data->LinkStatus == GL_FALSE &&
+ if (shProg->data->LinkStatus == LINKING_FAILURE &&
(ctx->_Shader->Flags & GLSL_REPORT_ERRORS)) {
_mesa_debug(ctx, "Error linking program %u:\n%s\n",
shProg->Name, shProg->data->InfoLog);
}
+ _mesa_update_vertex_processing_mode(ctx);
+
+ shProg->BinaryRetrievableHint = shProg->BinaryRetrievableHintPending;
+
/* debug code */
if (0) {
GLuint i;
}
+static void
+link_program_error(struct gl_context *ctx, struct gl_shader_program *shProg)
+{
+ link_program(ctx, shProg, false);
+}
+
+
+static void
+link_program_no_error(struct gl_context *ctx, struct gl_shader_program *shProg)
+{
+ link_program(ctx, shProg, true);
+}
+
+
+void
+_mesa_link_program(struct gl_context *ctx, struct gl_shader_program *shProg)
+{
+ link_program_error(ctx, shProg);
+}
+
+
/**
* Print basic shader info (for debug).
*/
}
-static void
-use_program(struct gl_context *ctx, gl_shader_stage stage,
- struct gl_program *new_prog, struct gl_pipeline_object *shTarget)
-{
- struct gl_program **target;
-
- target = &shTarget->CurrentProgram[stage];
- if (new_prog) {
- _mesa_program_init_subroutine_defaults(ctx, new_prog);
- }
-
- if (*target != new_prog) {
- /* 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 == ctx->_Shader->_CurrentFragmentProgram) {
- _mesa_reference_program(ctx,
- &ctx->_Shader->_CurrentFragmentProgram,
- NULL);
- }
- break;
- }
-
- _mesa_reference_program(ctx, target, new_prog);
- return;
- }
-}
-
-
/**
* Use the named shader program for subsequent rendering.
*/
struct gl_program *new_prog = NULL;
if (shProg && shProg->_LinkedShaders[i])
new_prog = shProg->_LinkedShaders[i]->Program;
- use_program(ctx, i, new_prog, &ctx->Shader);
+ _mesa_use_program(ctx, i, shProg, new_prog, &ctx->Shader);
}
_mesa_active_program(ctx, shProg, "glUseProgram");
}
}
+void GLAPIENTRY
+_mesa_AttachObjectARB_no_error(GLhandleARB program, GLhandleARB shader)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ attach_shader_no_error(ctx, program, shader);
+}
+
void GLAPIENTRY
_mesa_AttachObjectARB(GLhandleARB program, GLhandleARB shader)
{
GET_CURRENT_CONTEXT(ctx);
- attach_shader(ctx, program, shader);
+ attach_shader_err(ctx, program, shader, "glAttachObjectARB");
+}
+
+
+void GLAPIENTRY
+_mesa_AttachShader_no_error(GLuint program, GLuint shader)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ attach_shader_no_error(ctx, program, shader);
}
_mesa_AttachShader(GLuint program, GLuint shader)
{
GET_CURRENT_CONTEXT(ctx);
- attach_shader(ctx, program, shader);
+ attach_shader_err(ctx, program, shader, "glAttachShader");
}
}
+GLuint GLAPIENTRY
+_mesa_CreateShader_no_error(GLenum type)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ return create_shader(ctx, type);
+}
+
+
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_err(ctx, type, "glCreateShader");
+}
+
+
+GLhandleARB GLAPIENTRY
+_mesa_CreateShaderObjectARB_no_error(GLenum type)
+{
+ GET_CURRENT_CONTEXT(ctx);
return create_shader(ctx, type);
}
_mesa_CreateShaderObjectARB(GLenum type)
{
GET_CURRENT_CONTEXT(ctx);
- return create_shader(ctx, type);
+ return create_shader_err(ctx, type, "glCreateShaderObjectARB");
}
}
+void GLAPIENTRY
+_mesa_DetachObjectARB_no_error(GLhandleARB program, GLhandleARB shader)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ detach_shader_no_error(ctx, program, shader);
+}
+
+
void GLAPIENTRY
_mesa_DetachObjectARB(GLhandleARB program, GLhandleARB shader)
{
GET_CURRENT_CONTEXT(ctx);
- detach_shader(ctx, program, shader);
+ detach_shader_error(ctx, program, shader);
+}
+
+
+void GLAPIENTRY
+_mesa_DetachShader_no_error(GLuint program, GLuint shader)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ detach_shader_no_error(ctx, program, shader);
}
_mesa_DetachShader(GLuint program, GLuint shader)
{
GET_CURRENT_CONTEXT(ctx);
- detach_shader(ctx, program, shader);
+ detach_shader_error(ctx, program, shader);
}
GLsizei * count, GLhandleARB * obj)
{
GET_CURRENT_CONTEXT(ctx);
- get_attached_shaders(ctx, container, maxCount, count, obj);
+ get_attached_shaders(ctx, (GLuint)container, maxCount, count, NULL, obj);
}
GLsizei *count, GLuint *obj)
{
GET_CURRENT_CONTEXT(ctx);
- get_attached_shaders(ctx, program, maxCount, count, obj);
+ get_attached_shaders(ctx, program, maxCount, count, obj, NULL);
}
}
+void GLAPIENTRY
+_mesa_LinkProgram_no_error(GLuint programObj)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ struct gl_shader_program *shProg =
+ _mesa_lookup_shader_program(ctx, programObj);
+ link_program_no_error(ctx, shProg);
+}
+
+
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"));
+
+ struct gl_shader_program *shProg =
+ _mesa_lookup_shader_program_err(ctx, programObj, "glLinkProgram");
+ link_program_error(ctx, shProg);
}
#ifdef ENABLE_SHADER_CACHE
* following format:
*
* <path>/<stage prefix>_<CHECKSUM>.glsl
+ * <path>/<stage prefix>_<CHECKSUM>.arb
*/
static char *
construct_name(const gl_shader_stage stage, const char *source,
"VS", "TC", "TE", "GS", "FS", "CS",
};
+ const char *format = strncmp(source, "!!ARB", 5) ? "glsl" : "arb";
+
generate_sha1(source, sha);
- return ralloc_asprintf(NULL, "%s/%s_%s.glsl", path, types[stage], sha);
+ return ralloc_asprintf(NULL, "%s/%s_%s.%s", path, types[stage], sha, format);
}
/**
* Write given shader source to a file in MESA_SHADER_DUMP_PATH.
*/
-static void
-dump_shader(const gl_shader_stage stage, const char *source)
+void
+_mesa_dump_shader_source(const gl_shader_stage stage, const char *source)
{
static bool path_exists = true;
char *dump_path;
* 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)
+GLcharARB *
+_mesa_read_shader_source(const gl_shader_stage stage, const char *source)
{
char *read_path;
static bool path_exists = true;
* 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)
+static ALWAYS_INLINE void
+shader_source(struct gl_context *ctx, GLuint shaderObj, GLsizei count,
+ const GLchar *const *string, const GLint *length, bool no_error)
{
- 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 (!no_error) {
+ sh = _mesa_lookup_shader_err(ctx, shaderObj, "glShaderSourceARB");
+ if (!sh)
+ return;
- if (string == NULL) {
- _mesa_error(ctx, GL_INVALID_VALUE, "glShaderSourceARB");
- return;
+ if (string == NULL) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glShaderSourceARB");
+ return;
+ }
+ } else {
+ sh = _mesa_lookup_shader(ctx, shaderObj);
}
/*
}
for (i = 0; i < count; i++) {
- if (string[i] == NULL) {
+ if (!no_error && string[i] == NULL) {
free((GLvoid *) offsets);
_mesa_error(ctx, GL_INVALID_OPERATION,
"glShaderSourceARB(null string)");
source[totalLength - 2] = '\0';
#ifdef ENABLE_SHADER_CACHE
+ GLcharARB *replacement;
+
/* 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);
+ _mesa_dump_shader_source(sh->Stage, source);
- replacement = read_shader(sh->Stage, source);
+ replacement = _mesa_read_shader_source(sh->Stage, source);
if (replacement) {
free(source);
source = replacement;
}
#endif /* ENABLE_SHADER_CACHE */
- shader_source(sh, source);
+ set_shader_source(sh, source);
free(offsets);
}
void GLAPIENTRY
-_mesa_UseProgram(GLuint program)
+_mesa_ShaderSource_no_error(GLuint shaderObj, GLsizei count,
+ const GLchar *const *string, const GLint *length)
{
GET_CURRENT_CONTEXT(ctx);
- struct gl_shader_program *shProg;
+ shader_source(ctx, shaderObj, count, string, length, true);
+}
+
+
+void GLAPIENTRY
+_mesa_ShaderSource(GLuint shaderObj, GLsizei count,
+ const GLchar *const *string, const GLint *length)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ shader_source(ctx, shaderObj, count, string, length, false);
+}
+
+
+static ALWAYS_INLINE void
+use_program(GLuint program, bool no_error)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_shader_program *shProg = NULL;
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 (no_error) {
+ if (program) {
+ shProg = _mesa_lookup_shader_program(ctx, program);
}
- if (!shProg->data->LinkStatus) {
+ } else {
+ if (_mesa_is_xfb_active_and_unpaused(ctx)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
- "glUseProgram(program %u not linked)", program);
+ "glUseProgram(transform feedback active)");
return;
}
- /* debug code */
- if (ctx->_Shader->Flags & GLSL_USE_PROG) {
- print_shader_info(shProg);
+ 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:
*
* object (section 2.14.PPO), the program bound to the appropriate
* stage of the pipeline object is considered current."
*/
- if (program) {
+ if (shProg) {
/* Attach shader state to the binding point */
_mesa_reference_pipeline_object(ctx, &ctx->_Shader, &ctx->Shader);
/* Update the program */
/* Must be done first: detach the progam */
_mesa_use_shader_program(ctx, shProg);
/* Unattach shader_state binding point */
- _mesa_reference_pipeline_object(ctx, &ctx->_Shader, ctx->Pipeline.Default);
+ _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);
+ if (no_error)
+ _mesa_BindProgramPipeline_no_error(ctx->Pipeline.Current->Name);
+ else
+ _mesa_BindProgramPipeline(ctx->Pipeline.Current->Name);
}
}
+
+ _mesa_update_vertex_processing_mode(ctx);
+}
+
+
+void GLAPIENTRY
+_mesa_UseProgram_no_error(GLuint program)
+{
+ use_program(program, true);
+}
+
+
+void GLAPIENTRY
+_mesa_UseProgram(GLuint program)
+{
+ use_program(program, false);
}
void GLAPIENTRY
_mesa_ReleaseShaderCompiler(void)
{
- _mesa_destroy_shader_compiler_caches();
+ GET_CURRENT_CONTEXT(ctx);
+
+ if (ctx->shader_builtin_ref) {
+ _mesa_glsl_builtin_functions_decref();
+ ctx->shader_builtin_ref = false;
+ }
}
const void* binary, GLint length)
{
GET_CURRENT_CONTEXT(ctx);
- (void) shaders;
- (void) binaryformat;
- (void) binary;
+ struct gl_shader **sh;
/* 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:
return;
}
+ /* Get all shader objects at once so we can make the operation
+ * all-or-nothing.
+ */
+ if (n > SIZE_MAX / sizeof(*sh)) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderBinary(count)");
+ return;
+ }
+
+ sh = alloca(sizeof(*sh) * (size_t)n);
+ if (!sh) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glShaderBinary");
+ return;
+ }
+
+ for (int i = 0; i < n; ++i) {
+ sh[i] = _mesa_lookup_shader_err(ctx, shaders[i], "glShaderBinary");
+ if (!sh[i])
+ return;
+ }
+
+ if (binaryformat == GL_SHADER_BINARY_FORMAT_SPIR_V_ARB) {
+ if (!ctx->Extensions.ARB_gl_spirv) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "glShaderBinary(SPIR-V)");
+ } else if (n > 0) {
+ _mesa_spirv_shader_binary(ctx, (unsigned) n, sh, binary,
+ (size_t) length);
+ }
+
+ return;
+ }
+
_mesa_error(ctx, GL_INVALID_ENUM, "glShaderBinary(format)");
}
return;
}
- *length = 0;
- _mesa_error(ctx, GL_INVALID_OPERATION,
- "glGetProgramBinary(driver supports zero binary formats)");
-
- (void) binaryFormat;
- (void) binary;
+ if (ctx->Const.NumProgramBinaryFormats == 0) {
+ *length = 0;
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glGetProgramBinary(driver supports zero binary formats)");
+ } else {
+ _mesa_get_program_binary(ctx, shProg, bufSize, length, binaryFormat,
+ binary);
+ assert(*length == 0 || *binaryFormat == GL_PROGRAM_BINARY_FORMAT_MESA);
+ }
}
void GLAPIENTRY
if (!shProg)
return;
- (void) binaryFormat;
- (void) binary;
+ _mesa_clear_shader_program_data(ctx, shProg);
+ shProg->data = _mesa_create_shader_program_data();
/* Section 2.3.1 (Errors) of the OpenGL 4.5 spec says:
*
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");
+ if (ctx->Const.NumProgramBinaryFormats == 0 ||
+ binaryFormat != GL_PROGRAM_BINARY_FORMAT_MESA) {
+ /* 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 = LINKING_FAILURE;
+ _mesa_error(ctx, GL_INVALID_ENUM, "glProgramBinary");
+ } else {
+ _mesa_program_binary(ctx, shProg, binaryFormat, binary, length);
+ }
}
-void GLAPIENTRY
-_mesa_ProgramParameteri(GLuint program, GLenum pname, GLint value)
+static ALWAYS_INLINE void
+program_parameteri(struct gl_context *ctx, struct gl_shader_program *shProg,
+ GLuint pname, GLint value, bool no_error)
{
- 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
* "An INVALID_VALUE error is generated if the <value> argument to
* ProgramParameteri is not TRUE or FALSE."
*/
- if (value != GL_TRUE && value != GL_FALSE) {
+ if (!no_error && value != GL_TRUE && value != GL_FALSE) {
goto invalid_value;
}
* 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 resolution 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
* changes made to the program before being saved such that when it
* is loaded again a recompile can be avoided."
*/
- shProg->BinaryRetreivableHint = value;
+ shProg->BinaryRetrievableHintPending = 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) {
+ if (!no_error && 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));
+ if (!no_error) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameteri(pname=%s)",
+ _mesa_enum_to_string(pname));
+ }
return;
}
}
+void GLAPIENTRY
+_mesa_ProgramParameteri_no_error(GLuint program, GLenum pname, GLint value)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program);
+ program_parameteri(ctx, shProg, pname, value, true);
+}
+
+
+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;
+
+ program_parameteri(ctx, shProg, pname, value, false);
+}
+
+
void
_mesa_use_program(struct gl_context *ctx, gl_shader_stage stage,
- struct gl_program *prog,
+ struct gl_shader_program *shProg, struct gl_program *prog,
struct gl_pipeline_object *shTarget)
{
- use_program(ctx, stage, prog, shTarget);
+ struct gl_program **target;
+
+ target = &shTarget->CurrentProgram[stage];
+ if (prog) {
+ _mesa_program_init_subroutine_defaults(ctx, prog);
+ }
+
+ if (*target != prog) {
+ /* Program is current, flush it */
+ if (shTarget == ctx->_Shader) {
+ FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS);
+ }
+
+ _mesa_reference_shader_program(ctx,
+ &shTarget->ReferencedPrograms[stage],
+ shProg);
+ _mesa_reference_program(ctx, target, prog);
+ _mesa_update_allow_draw_out_of_order(ctx);
+ if (stage == MESA_SHADER_VERTEX)
+ _mesa_update_vertex_processing_mode(ctx);
+ return;
+ }
+
}
{
GET_CURRENT_CONTEXT(ctx);
- const GLuint shader = create_shader(ctx, type);
+ const GLuint shader = create_shader_err(ctx, type, "glCreateShaderProgramv");
GLuint program = 0;
/*
get_shaderiv(ctx, shader, GL_COMPILE_STATUS, &compiled);
if (compiled) {
- attach_shader(ctx, program, shader);
+ attach_shader_err(ctx, program, shader, "glCreateShaderProgramv");
_mesa_link_program(ctx, shProg);
- detach_shader(ctx, program, shader);
+ detach_shader_error(ctx, program, shader);
#if 0
/* Possibly... */
if (active-user-defined-varyings-in-linked-program) {
append-error-to-info-log;
- shProg->data->LinkStatus = GL_FALSE;
+ shProg->data->LinkStatus = LINKING_FAILURE;
}
#endif
}
/**
* For GL_ARB_tessellation_shader
*/
+void GLAPIENTRY
+_mesa_PatchParameteri_no_error(GLenum pname, GLint value)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ FLUSH_VERTICES(ctx, 0);
+ ctx->TessCtrlProgram.patch_vertices = value;
+}
+
+
extern void GLAPIENTRY
_mesa_PatchParameteri(GLenum pname, GLint value)
{
return;
}
+ FLUSH_VERTICES(ctx, 0);
ctx->TessCtrlProgram.patch_vertices = value;
}
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;
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;
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;
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;
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;
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;
}
i = 0;
+ bool flushed = false;
do {
struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i];
if (uni == NULL) {
continue;
}
+ if (!flushed) {
+ _mesa_flush_vertices_for_uniforms(ctx, uni);
+ flushed = true;
+ }
+
int uni_count = uni->array_elements ? uni->array_elements : 1;
int j, k, f;
}
i += uni_count;
} while(i < count);
-
- FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS);
}
const char *api_name = "glGetUniformSubroutineuiv";
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;
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;
}
}
+/* This is simple list entry that will be used to hold a list of string
+ * tokens of a parsed shader include path.
+ */
+struct sh_incl_path_entry
+{
+ struct sh_incl_path_entry *next;
+ struct sh_incl_path_entry *prev;
+
+ char *path;
+};
+
+/* Nodes of the shader include tree */
+struct sh_incl_path_ht_entry
+{
+ struct hash_table *path;
+ char *shader_source;
+};
+
+struct shader_includes {
+ /* Array to hold include paths given to glCompileShaderIncludeARB() */
+ struct sh_incl_path_entry **include_paths;
+ size_t num_include_paths;
+ size_t relative_path_cursor;
+
+ /* Root hash table holding the shader include tree */
+ struct hash_table *shader_include_tree;
+};
+
+void
+_mesa_init_shader_includes(struct gl_shared_state *shared)
+{
+ shared->ShaderIncludes = calloc(1, sizeof(struct shader_includes));
+ shared->ShaderIncludes->shader_include_tree =
+ _mesa_hash_table_create(NULL, _mesa_hash_string,
+ _mesa_key_string_equal);
+}
+
+size_t
+_mesa_get_shader_include_cursor(struct gl_shared_state *shared)
+{
+ return shared->ShaderIncludes->relative_path_cursor;
+}
+
+void
+_mesa_set_shader_include_cursor(struct gl_shared_state *shared, size_t cursor)
+{
+ shared->ShaderIncludes->relative_path_cursor = cursor;
+}
+
+static void
+destroy_shader_include(struct hash_entry *entry)
+{
+ struct sh_incl_path_ht_entry *sh_incl_ht_entry =
+ (struct sh_incl_path_ht_entry *) entry->data;
+
+ _mesa_hash_table_destroy(sh_incl_ht_entry->path, destroy_shader_include);
+ free(sh_incl_ht_entry->shader_source);
+ free(sh_incl_ht_entry);
+}
+
+void
+_mesa_destroy_shader_includes(struct gl_shared_state *shared)
+{
+ _mesa_hash_table_destroy(shared->ShaderIncludes->shader_include_tree,
+ destroy_shader_include);
+ free(shared->ShaderIncludes);
+}
+
+static bool
+valid_path_format(const char *str, bool relative_path)
+{
+ int i = 0;
+
+ if (!str[i] || (!relative_path && str[i] != '/'))
+ return false;
+
+ i++;
+
+ while (str[i]) {
+ const char c = str[i++];
+ if (('A' <= c && c <= 'Z') ||
+ ('a' <= c && c <= 'z') ||
+ ('0' <= c && c <= '9'))
+ continue;
+
+ if (c == '/') {
+ if (str[i - 2] == '/')
+ return false;
+
+ continue;
+ }
+
+ if (strchr("^. _+*%[](){}|&~=!:;,?-", c) == NULL)
+ return false;
+ }
+
+ if (str[i - 1] == '/')
+ return false;
+
+ return true;
+}
+
+
+static bool
+validate_and_tokenise_sh_incl(struct gl_context *ctx,
+ void *mem_ctx,
+ struct sh_incl_path_entry **path_list,
+ char *full_path, bool error_check)
+{
+ bool relative_path = ctx->Shared->ShaderIncludes->num_include_paths;
+
+ if (!valid_path_format(full_path, relative_path)) {
+ if (error_check) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glNamedStringARB(invalid name %s)", full_path);
+ }
+ return false;
+ }
+
+ char *save_ptr = NULL;
+ char *path_str = strtok_r(full_path, "/", &save_ptr);
+
+ *path_list = rzalloc(mem_ctx, struct sh_incl_path_entry);
+
+ make_empty_list(*path_list);
+
+ while (path_str != NULL) {
+ if (strlen(path_str) == 0) {
+ if (error_check) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glNamedStringARB(invalid name %s)", full_path);
+ }
+
+ return false;
+ }
+
+ if (strcmp(path_str, ".") == 0) {
+ /* Do nothing */
+ } else if (strcmp(path_str, "..") == 0) {
+ struct sh_incl_path_entry *last = last_elem(*path_list);
+ remove_from_list(last);
+ } else {
+ struct sh_incl_path_entry *path =
+ rzalloc(mem_ctx, struct sh_incl_path_entry);
+
+ path->path = strdup(path_str);
+ insert_at_tail(*path_list, path);
+ }
+
+ path_str = strtok_r(NULL, "/", &save_ptr);
+ }
+
+ return true;
+}
+
+static struct sh_incl_path_ht_entry *
+lookup_shader_include(struct gl_context *ctx, char *path,
+ bool error_check)
+{
+ void *mem_ctx = ralloc_context(NULL);
+ struct sh_incl_path_entry *path_list;
+
+ if (!validate_and_tokenise_sh_incl(ctx, mem_ctx, &path_list, path,
+ error_check)) {
+ ralloc_free(mem_ctx);
+ return NULL;
+ }
+
+ struct sh_incl_path_ht_entry *sh_incl_ht_entry = NULL;
+ struct hash_table *path_ht =
+ ctx->Shared->ShaderIncludes->shader_include_tree;
+
+ size_t count = ctx->Shared->ShaderIncludes->num_include_paths;
+ bool relative_path = path[0] != '/';
+
+ size_t i = ctx->Shared->ShaderIncludes->relative_path_cursor;
+ bool use_cursor = ctx->Shared->ShaderIncludes->relative_path_cursor;
+
+ do {
+ struct sh_incl_path_entry *entry;
+
+ if (relative_path) {
+next_relative_path:
+ {
+ struct sh_incl_path_entry *rel_path_list =
+ ctx->Shared->ShaderIncludes->include_paths[i];
+ foreach(entry, rel_path_list) {
+ struct hash_entry *ht_entry =
+ _mesa_hash_table_search(path_ht, entry->path);
+
+ if (!ht_entry) {
+ /* Reset search path and skip to the next include path */
+ path_ht = ctx->Shared->ShaderIncludes->shader_include_tree;
+ sh_incl_ht_entry = NULL;
+ if (use_cursor) {
+ i = 0;
+ use_cursor = false;
+
+ goto next_relative_path;
+ }
+ i++;
+ if (i < count)
+ goto next_relative_path;
+ else
+ break;
+ } else {
+ sh_incl_ht_entry =
+ (struct sh_incl_path_ht_entry *) ht_entry->data;
+ }
+
+ path_ht = sh_incl_ht_entry->path;
+ }
+ }
+ }
+
+ foreach(entry, path_list) {
+ struct hash_entry *ht_entry =
+ _mesa_hash_table_search(path_ht, entry->path);
+
+ if (!ht_entry) {
+ /* Reset search path and skip to the next include path */
+ path_ht = ctx->Shared->ShaderIncludes->shader_include_tree;
+ sh_incl_ht_entry = NULL;
+ if (use_cursor) {
+ i = 0;
+ use_cursor = false;
+
+ break;
+ }
+ i++;
+ break;
+ } else {
+
+ sh_incl_ht_entry =
+ (struct sh_incl_path_ht_entry *) ht_entry->data;
+ }
+
+ path_ht = sh_incl_ht_entry->path;
+ }
+
+ if (i < count &&
+ (sh_incl_ht_entry == NULL || !sh_incl_ht_entry->shader_source))
+ continue;
+
+ /* If we get here then we have found a matching path or exahusted our
+ * relative search paths.
+ */
+ ctx->Shared->ShaderIncludes->relative_path_cursor = i;
+ break;
+ } while (i < count);
+
+ ralloc_free(mem_ctx);
+
+ return sh_incl_ht_entry;
+}
+
+const char *
+_mesa_lookup_shader_include(struct gl_context *ctx, char *path,
+ bool error_check)
+{
+ struct sh_incl_path_ht_entry *shader_include =
+ lookup_shader_include(ctx, path, error_check);
+
+ return shader_include ? shader_include->shader_source : NULL;
+}
+
+static char *
+copy_string(struct gl_context *ctx, const char *str, int str_len,
+ const char *caller)
+{
+ if (!str) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s(NULL string)", caller);
+ return NULL;
+ }
+
+ char *cp;
+ if (str_len == -1)
+ cp = strdup(str);
+ else {
+ cp = calloc(sizeof(char), str_len + 1);
+ memcpy(cp, str, str_len);
+ }
+
+ return cp;
+}
+
+GLvoid GLAPIENTRY
+_mesa_NamedStringARB(GLenum type, GLint namelen, const GLchar *name,
+ GLint stringlen, const GLchar *string)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *caller = "glNamedStringARB";
+
+ if (type != GL_SHADER_INCLUDE_ARB) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid type)", caller);
+ return;
+ }
+
+ char *name_cp = copy_string(ctx, name, namelen, caller);
+ char *string_cp = copy_string(ctx, string, stringlen, caller);
+ if (!name_cp || !string_cp) {
+ free(string_cp);
+ free(name_cp);
+ return;
+ }
+
+ void *mem_ctx = ralloc_context(NULL);
+ struct sh_incl_path_entry *path_list;
+
+ if (!validate_and_tokenise_sh_incl(ctx, mem_ctx, &path_list, name_cp,
+ true)) {
+ free(string_cp);
+ free(name_cp);
+ ralloc_free(mem_ctx);
+ return;
+ }
+
+ mtx_lock(&ctx->Shared->ShaderIncludeMutex);
+
+ struct hash_table *path_ht =
+ ctx->Shared->ShaderIncludes->shader_include_tree;
+
+ struct sh_incl_path_entry *entry;
+ foreach(entry, path_list) {
+ struct hash_entry *ht_entry =
+ _mesa_hash_table_search(path_ht, entry->path);
+
+ struct sh_incl_path_ht_entry *sh_incl_ht_entry;
+ if (!ht_entry) {
+ sh_incl_ht_entry = calloc(1, sizeof(struct sh_incl_path_ht_entry));
+ sh_incl_ht_entry->path =
+ _mesa_hash_table_create(NULL, _mesa_hash_string,
+ _mesa_key_string_equal);
+ _mesa_hash_table_insert(path_ht, entry->path, sh_incl_ht_entry);
+ } else {
+ sh_incl_ht_entry = (struct sh_incl_path_ht_entry *) ht_entry->data;
+ }
+
+ path_ht = sh_incl_ht_entry->path;
+
+ if (last_elem(path_list) == entry) {
+ free(sh_incl_ht_entry->shader_source);
+ sh_incl_ht_entry->shader_source = string_cp;
+ }
+ }
+
+ mtx_unlock(&ctx->Shared->ShaderIncludeMutex);
+
+ free(name_cp);
+ ralloc_free(mem_ctx);
+}
+
+GLvoid GLAPIENTRY
+_mesa_DeleteNamedStringARB(GLint namelen, const GLchar *name)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *caller = "glDeleteNamedStringARB";
+
+ char *name_cp = copy_string(ctx, name, namelen, caller);
+ if (!name_cp)
+ return;
+
+ struct sh_incl_path_ht_entry *shader_include =
+ lookup_shader_include(ctx, name_cp, true);
+
+ if (!shader_include) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "%s(no string associated with path %s)", caller, name_cp);
+ free(name_cp);
+ return;
+ }
+
+ mtx_lock(&ctx->Shared->ShaderIncludeMutex);
+
+ free(shader_include->shader_source);
+ shader_include->shader_source = NULL;
+
+ mtx_unlock(&ctx->Shared->ShaderIncludeMutex);
+
+ free(name_cp);
+}
+
+GLvoid GLAPIENTRY
+_mesa_CompileShaderIncludeARB(GLuint shader, GLsizei count,
+ const GLchar* const *path, const GLint *length)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *caller = "glCompileShaderIncludeARB";
+
+ if (count > 0 && path == NULL) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s(count > 0 && path == NULL)",
+ caller);
+ return;
+ }
+
+ void *mem_ctx = ralloc_context(NULL);
+
+ mtx_lock(&ctx->Shared->ShaderIncludeMutex);
+
+ ctx->Shared->ShaderIncludes->include_paths =
+ ralloc_array_size(mem_ctx, sizeof(struct sh_incl_path_entry *), count);
+
+ for (size_t i = 0; i < count; i++) {
+ char *path_cp = copy_string(ctx, path[i], length ? length[i] : -1,
+ caller);
+ if (!path_cp) {
+ goto exit;
+ }
+
+ struct sh_incl_path_entry *path_list;
+
+ if (!validate_and_tokenise_sh_incl(ctx, mem_ctx, &path_list, path_cp,
+ true)) {
+ free(path_cp);
+ goto exit;
+ }
+
+ ctx->Shared->ShaderIncludes->include_paths[i] = path_list;
+
+ free(path_cp);
+ }
+
+ /* We must set this *after* all calls to validate_and_tokenise_sh_incl()
+ * are done as we use this to decide if we need to check the start of the
+ * path for a '/'
+ */
+ ctx->Shared->ShaderIncludes->num_include_paths = count;
+
+ struct gl_shader *sh = _mesa_lookup_shader(ctx, shader);
+ if (!sh) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s(shader)", caller);
+ goto exit;
+ }
+
+ _mesa_compile_shader(ctx, sh);
+
+exit:
+ ctx->Shared->ShaderIncludes->num_include_paths = 0;
+ ctx->Shared->ShaderIncludes->relative_path_cursor = 0;
+ ctx->Shared->ShaderIncludes->include_paths = NULL;
+
+ mtx_unlock(&ctx->Shared->ShaderIncludeMutex);
+
+ ralloc_free(mem_ctx);
+}
+
+GLboolean GLAPIENTRY
+_mesa_IsNamedStringARB(GLint namelen, const GLchar *name)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ if (!name)
+ return false;
+
+ char *name_cp = copy_string(ctx, name, namelen, "");
+
+ const char *source = _mesa_lookup_shader_include(ctx, name_cp, false);
+ free(name_cp);
+
+ if (!source)
+ return false;
+
+ return true;
+}
+
+GLvoid GLAPIENTRY
+_mesa_GetNamedStringARB(GLint namelen, const GLchar *name, GLsizei bufSize,
+ GLint *stringlen, GLchar *string)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *caller = "glGetNamedStringARB";
+
+ char *name_cp = copy_string(ctx, name, namelen, caller);
+ if (!name_cp)
+ return;
+
+ const char *source = _mesa_lookup_shader_include(ctx, name_cp, true);
+ if (!source) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "%s(no string associated with path %s)", caller, name_cp);
+ free(name_cp);
+ return;
+ }
+
+ size_t size = MIN2(strlen(source), bufSize - 1);
+ memcpy(string, source, size);
+ string[size] = '\0';
+
+ *stringlen = size;
+
+ free(name_cp);
+}
+
+GLvoid GLAPIENTRY
+_mesa_GetNamedStringivARB(GLint namelen, const GLchar *name,
+ GLenum pname, GLint *params)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *caller = "glGetNamedStringivARB";
+
+ char *name_cp = copy_string(ctx, name, namelen, caller);
+ if (!name_cp)
+ return;
+
+ const char *source = _mesa_lookup_shader_include(ctx, name_cp, true);
+ if (!source) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "%s(no string associated with path %s)", caller, name_cp);
+ free(name_cp);
+ return;
+ }
+
+ switch (pname) {
+ case GL_NAMED_STRING_LENGTH_ARB:
+ *params = strlen(source) + 1;
+ break;
+ case GL_NAMED_STRING_TYPE_ARB:
+ *params = GL_SHADER_INCLUDE_ARB;
+ break;
+ default:
+ _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname)", caller);
+ break;
+ }
+
+ free(name_cp);
+}
+
static int
find_compat_subroutine(struct gl_program *p, const struct glsl_type *type)
{