*
* 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.
{
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_shader_program(ctx, &ctx->Shader.ActiveProgram, NULL);
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 ||
/**
* 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 ? GL_TRUE : GL_FALSE;
return;
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 == linking_failure) ? 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 ? GL_TRUE : GL_FALSE;
break;
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);
- if (sh->CompileStatus == compile_skipped && !sh->FallbackSource) {
+ /* 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.
*/
#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 = compile_failure;
+ 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.
*/
}
+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);
_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 == linking_failure &&
+ 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).
*/
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;
- 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)");
/* 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_ShaderSource_no_error(GLuint shaderObj, GLsizei count,
+ const GLchar *const *string, const GLint *length)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ 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)
{
_mesa_BindProgramPipeline(ctx->Pipeline.Current->Name);
}
}
+
+ _mesa_update_vertex_processing_mode(ctx);
}
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 = linking_failure;
- _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);
+ }
}
* 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:
&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;
}
/* Possibly... */
if (active-user-defined-varyings-in-linked-program) {
append-error-to-info-log;
- shProg->data->LinkStatus = linking_failure;
+ shProg->data->LinkStatus = LINKING_FAILURE;
}
#endif
}
_mesa_PatchParameteri_no_error(GLenum pname, GLint value)
{
GET_CURRENT_CONTEXT(ctx);
+ FLUSH_VERTICES(ctx, 0);
ctx->TessCtrlProgram.patch_vertices = value;
}
return;
}
+ FLUSH_VERTICES(ctx, 0);
ctx->TessCtrlProgram.patch_vertices = value;
}
}
}
+/* 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)
{