*/
+#include <stdbool.h>
#include "main/glheader.h"
#include "main/context.h"
#include "main/dispatch.h"
#include "main/shaderobj.h"
#include "main/transformfeedback.h"
#include "main/uniforms.h"
+#include "compiler/glsl/glsl_parser_extras.h"
+#include "compiler/glsl/ir.h"
+#include "compiler/glsl/ir_uniform.h"
+#include "compiler/glsl/program.h"
#include "program/program.h"
#include "program/prog_print.h"
#include "program/prog_parameter.h"
#include "util/ralloc.h"
#include "util/hash_table.h"
-#include <stdbool.h>
-#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
-
+#include "util/mesa-sha1.h"
+#include "util/crc32.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"))
return flags;
}
+/**
+ * Memoized version of getenv("MESA_SHADER_CAPTURE_PATH").
+ */
+const char *
+_mesa_get_shader_capture_path(void)
+{
+ static bool read_env_var = false;
+ static const char *path = NULL;
+
+ if (!read_env_var) {
+ path = getenv("MESA_SHADER_CAPTURE_PATH");
+ read_env_var = true;
+ }
+
+ return path;
+}
/**
* Initialize context's shader state.
/* 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;
void
_mesa_free_shader_state(struct gl_context *ctx)
{
- int i;
- for (i = 0; i < MESA_SHADER_STAGES; i++) {
- _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram[i],
- NULL);
+ 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._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);
}
return ctx == NULL || ctx->Extensions.ARB_vertex_shader;
case GL_GEOMETRY_SHADER_ARB:
return ctx == NULL || _mesa_has_geometry_shaders(ctx);
+ case GL_TESS_CONTROL_SHADER:
+ case GL_TESS_EVALUATION_SHADER:
+ return ctx == NULL || _mesa_has_tessellation(ctx);
case GL_COMPUTE_SHADER:
- return ctx == NULL || ctx->Extensions.ARB_compute_shader;
+ return ctx == NULL || _mesa_has_compute_shaders(ctx);
default:
return false;
}
* 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, caller);
return;
} else if (same_type_disallowed &&
- shProg->Shaders[i]->Type == sh->Type) {
+ shProg->Shaders[i]->Stage == sh->Stage) {
/* Shader with the same type is already attached to this program,
* OpenGL ES 2.0 and 3.0 specs say:
*
* 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, 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)
GLuint name;
if (!_mesa_validate_shader_target(ctx, type)) {
- _mesa_error(ctx, GL_INVALID_ENUM, "CreateShader(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 = ctx->Driver.NewShader(ctx, name, type);
- _mesa_HashInsert(ctx->Shared->ShaderObjects, name, sh);
+ sh = _mesa_new_shader(name, _mesa_shader_enum_to_shader_stage(type));
+ sh->Type = type;
+ _mesa_HashInsertLocked(ctx->Shared->ShaderObjects, name, sh);
+ _mesa_HashUnlockMutex(ctx->Shared->ShaderObjects);
return name;
}
GLuint name;
struct gl_shader_program *shProg;
+ _mesa_HashLockMutex(ctx->Shared->ShaderObjects);
+
name = _mesa_HashFindFreeKeyBlock(ctx->Shared->ShaderObjects, 1);
- shProg = ctx->Driver.NewShaderProgram(name);
+ shProg = _mesa_new_shader_program(name);
- _mesa_HashInsert(ctx->Shared->ShaderObjects, name, shProg);
+ _mesa_HashInsertLocked(ctx->Shared->ShaderObjects, name, shProg);
assert(shProg->RefCount == 1);
+ _mesa_HashUnlockMutex(ctx->Shared->ShaderObjects);
+
return name;
}
#ifdef DEBUG
/* 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]->Stage == MESA_SHADER_VERTEX ||
+ shProg->Shaders[j]->Stage == MESA_SHADER_TESS_CTRL ||
+ shProg->Shaders[j]->Stage == MESA_SHADER_TESS_EVAL ||
+ shProg->Shaders[j]->Stage == MESA_SHADER_GEOMETRY ||
+ shProg->Shaders[j]->Stage == MESA_SHADER_FRAGMENT);
assert(shProg->Shaders[j]->RefCount > 0);
}
#endif
static bool
check_gs_query(struct gl_context *ctx, const struct gl_shader_program *shProg)
{
- if (shProg->LinkStatus &&
+ if (shProg->data->LinkStatus &&
shProg->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
return true;
}
static bool
check_tcs_query(struct gl_context *ctx, const struct gl_shader_program *shProg)
{
- if (shProg->LinkStatus &&
+ if (shProg->data->LinkStatus &&
shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL] != NULL) {
return true;
}
static bool
check_tes_query(struct gl_context *ctx, const struct gl_shader_program *shProg)
{
- if (shProg->LinkStatus &&
+ if (shProg->data->LinkStatus &&
shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL] != NULL) {
return true;
}
*params = shProg->DeletePending;
return;
case GL_LINK_STATUS:
- *params = shProg->LinkStatus;
+ *params = shProg->data->LinkStatus ? GL_TRUE : GL_FALSE;
return;
case GL_VALIDATE_STATUS:
- *params = shProg->Validated;
+ *params = shProg->data->Validated;
return;
case GL_INFO_LOG_LENGTH:
- *params = shProg->InfoLog ? strlen(shProg->InfoLog) + 1 : 0;
+ *params = (shProg->data->InfoLog && shProg->data->InfoLog[0] != '\0') ?
+ strlen(shProg->data->InfoLog) + 1 : 0;
return;
case GL_ATTACHED_SHADERS:
*params = shProg->NumShaders;
case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
*params = _mesa_longest_attribute_name_length(shProg);
return;
- case GL_ACTIVE_UNIFORMS:
- *params = shProg->NumUniformStorage - shProg->NumHiddenUniforms;
+ case GL_ACTIVE_UNIFORMS: {
+ unsigned i;
+ const unsigned num_uniforms =
+ shProg->data->NumUniformStorage - shProg->data->NumHiddenUniforms;
+ for (*params = 0, i = 0; i < num_uniforms; i++) {
+ if (!shProg->data->UniformStorage[i].is_shader_storage)
+ (*params)++;
+ }
return;
+ }
case GL_ACTIVE_UNIFORM_MAX_LENGTH: {
unsigned i;
GLint max_len = 0;
const unsigned num_uniforms =
- shProg->NumUniformStorage - shProg->NumHiddenUniforms;
+ shProg->data->NumUniformStorage - shProg->data->NumHiddenUniforms;
for (i = 0; i < num_uniforms; i++) {
+ if (shProg->data->UniformStorage[i].is_shader_storage)
+ continue;
+
/* Add one for the terminating NUL character for a non-array, and
* 4 for the "[0]" and the NUL for an array.
*/
- const GLint len = strlen(shProg->UniformStorage[i].name) + 1 +
- ((shProg->UniformStorage[i].array_elements != 0) ? 3 : 0);
+ const GLint len = strlen(shProg->data->UniformStorage[i].name) + 1 +
+ ((shProg->data->UniformStorage[i].array_elements != 0) ? 3 : 0);
if (len > max_len)
max_len = len;
case GL_GEOMETRY_VERTICES_OUT:
if (!has_core_gs)
break;
- if (check_gs_query(ctx, shProg))
- *params = shProg->Geom.VerticesOut;
+ if (check_gs_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
+ Program->info.gs.vertices_out;
+ }
return;
case GL_GEOMETRY_SHADER_INVOCATIONS:
if (!has_core_gs || !ctx->Extensions.ARB_gpu_shader5)
break;
- if (check_gs_query(ctx, shProg))
- *params = shProg->Geom.Invocations;
+ 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)
break;
- if (check_gs_query(ctx, shProg))
- *params = shProg->Geom.InputType;
+ if (check_gs_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
+ Program->info.gs.input_primitive;
+ }
return;
case GL_GEOMETRY_OUTPUT_TYPE:
if (!has_core_gs)
break;
- if (check_gs_query(ctx, shProg))
- *params = shProg->Geom.OutputType;
+ if (check_gs_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->
+ Program->info.gs.output_primitive;
+ }
return;
case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: {
unsigned i;
if (!has_ubo)
break;
- for (i = 0; i < shProg->NumUniformBlocks; i++) {
+ for (i = 0; i < shProg->data->NumUniformBlocks; i++) {
/* Add one for the terminating NUL character.
*/
- const GLint len = strlen(shProg->UniformBlocks[i].Name) + 1;
+ const GLint len = strlen(shProg->data->UniformBlocks[i].Name) + 1;
if (len > max_len)
max_len = len;
if (!has_ubo)
break;
- *params = shProg->NumUniformBlocks;
+ *params = shProg->data->NumUniformBlocks;
return;
case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
/* This enum isn't part of the OES extension for OpenGL ES 2.0. It is
if (!ctx->Extensions.ARB_shader_atomic_counters)
break;
- *params = shProg->NumAtomicBuffers;
+ *params = shProg->data->NumAtomicBuffers;
return;
case GL_COMPUTE_WORK_GROUP_SIZE: {
int i;
- if (!_mesa_is_desktop_gl(ctx) || !ctx->Extensions.ARB_compute_shader)
+ if (!_mesa_has_compute_shaders(ctx))
break;
- if (!shProg->LinkStatus) {
+ if (!shProg->data->LinkStatus) {
_mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramiv(program not "
"linked)");
return;
return;
}
for (i = 0; i < 3; i++)
- params[i] = shProg->Comp.LocalSize[i];
+ params[i] = shProg->_LinkedShaders[MESA_SHADER_COMPUTE]->
+ Program->info.cs.local_size[i];
return;
}
case GL_PROGRAM_SEPARABLE:
- *params = shProg->SeparateShader;
+ /* If the program has not been linked, return initial value 0. */
+ *params = (shProg->data->LinkStatus == linking_failure) ? 0 : shProg->SeparateShader;
return;
/* ARB_tessellation_shader */
case GL_TESS_CONTROL_OUTPUT_VERTICES:
if (!has_tess)
break;
- if (check_tcs_query(ctx, shProg))
- *params = shProg->TessCtrl.VerticesOut;
+ if (check_tcs_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL]->
+ Program->info.tess.tcs_vertices_out;
+ }
return;
case GL_TESS_GEN_MODE:
if (!has_tess)
break;
- if (check_tes_query(ctx, shProg))
- *params = shProg->TessEval.PrimitiveMode;
+ if (check_tes_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]->
+ Program->info.tess.primitive_mode;
+ }
return;
case GL_TESS_GEN_SPACING:
if (!has_tess)
break;
- if (check_tes_query(ctx, shProg))
- *params = shProg->TessEval.Spacing;
+ if (check_tes_query(ctx, shProg)) {
+ const struct gl_linked_shader *tes =
+ shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL];
+ switch (tes->Program->info.tess.spacing) {
+ case TESS_SPACING_EQUAL:
+ *params = GL_EQUAL;
+ break;
+ case TESS_SPACING_FRACTIONAL_ODD:
+ *params = GL_FRACTIONAL_ODD;
+ break;
+ case TESS_SPACING_FRACTIONAL_EVEN:
+ *params = GL_FRACTIONAL_EVEN;
+ break;
+ case TESS_SPACING_UNSPECIFIED:
+ *params = 0;
+ break;
+ }
+ }
return;
case GL_TESS_GEN_VERTEX_ORDER:
if (!has_tess)
break;
- if (check_tes_query(ctx, shProg))
- *params = shProg->TessEval.VertexOrder;
+ if (check_tes_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]->
+ Program->info.tess.ccw ? GL_CCW : GL_CW;
+ }
return;
case GL_TESS_GEN_POINT_MODE:
if (!has_tess)
break;
- if (check_tes_query(ctx, shProg))
- *params = shProg->TessEval.PointMode;
+ if (check_tes_query(ctx, shProg)) {
+ *params = shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]->
+ Program->info.tess.point_mode ? GL_TRUE : GL_FALSE;
+ }
return;
default:
break;
*params = shader->DeletePending;
break;
case GL_COMPILE_STATUS:
- *params = shader->CompileStatus;
+ *params = shader->CompileStatus ? GL_TRUE : GL_FALSE;
break;
case GL_INFO_LOG_LENGTH:
- *params = shader->InfoLog ? strlen(shader->InfoLog) + 1 : 0;
+ *params = (shader->InfoLog && shader->InfoLog[0] != '\0') ?
+ strlen(shader->InfoLog) + 1 : 0;
break;
case GL_SHADER_SOURCE_LENGTH:
*params = shader->Source ? strlen((char *) shader->Source) + 1 : 0;
return;
}
- _mesa_copy_string(infoLog, bufSize, length, shProg->InfoLog);
+ _mesa_copy_string(infoLog, bufSize, length, shProg->data->InfoLog);
}
* glShaderSource[ARB].
*/
static void
-shader_source(struct gl_context *ctx, GLuint shader, const GLchar *source)
+shader_source(struct gl_shader *sh, const GLchar *source)
{
- struct gl_shader *sh;
+ assert(sh);
- sh = _mesa_lookup_shader_err(ctx, shader, "glShaderSource");
- if (!sh)
- return;
+ 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;
+ }
- /* free old shader source string and install new one */
- free((void *)sh->Source);
- sh->Source = source;
- sh->CompileStatus = GL_FALSE;
#ifdef DEBUG
- sh->SourceChecksum = _mesa_str_checksum(sh->Source);
+ sh->SourceChecksum = util_hash_crc32(sh->Source, strlen(sh->Source));
#endif
}
/**
* Compile a shader.
*/
-static void
-compile_shader(struct gl_context *ctx, GLuint shaderObj)
+void
+_mesa_compile_shader(struct gl_context *ctx, struct gl_shader *sh)
{
- struct gl_shader *sh;
-
- sh = _mesa_lookup_shader_err(ctx, shaderObj, "glCompileShader");
if (!sh)
return;
/* 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",
/* 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);
if (ctx->_Shader->Flags & GLSL_DUMP) {
if (sh->CompileStatus) {
- _mesa_log("GLSL IR for shader %d:\n", sh->Name);
- _mesa_print_ir(_mesa_get_log_file(), sh->ir, NULL);
+ if (sh->ir) {
+ _mesa_log("GLSL IR for shader %d:\n", sh->Name);
+ _mesa_print_ir(_mesa_get_log_file(), sh->ir, NULL);
+ } else {
+ _mesa_log("No GLSL IR for shader %d (shader may be from "
+ "cache)\n", sh->Name);
+ }
_mesa_log("\n\n");
} else {
_mesa_log("GLSL shader %d failed to compile.\n", sh->Name);
/**
* Link a program's shaders.
*/
-static void
-link_program(struct gl_context *ctx, GLuint program)
+void
+_mesa_link_program(struct gl_context *ctx, struct gl_shader_program *shProg)
{
- struct gl_shader_program *shProg;
-
- shProg = _mesa_lookup_shader_program_err(ctx, program, "glLinkProgram");
if (!shProg)
return;
return;
}
- FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+ unsigned programs_in_use = 0;
+ if (ctx->_Shader)
+ for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+ if (ctx->_Shader->CurrentProgram[stage] &&
+ ctx->_Shader->CurrentProgram[stage]->Id == shProg->Name) {
+ programs_in_use |= 1 << stage;
+ }
+ }
+ FLUSH_VERTICES(ctx, 0);
_mesa_glsl_link_shader(ctx, shProg);
- if (shProg->LinkStatus == GL_FALSE &&
+ /* From section 7.3 (Program Objects) of the OpenGL 4.5 spec:
+ *
+ * "If LinkProgram or ProgramBinary successfully re-links a program
+ * object that is active for any shader stage, then the newly generated
+ * executable code will be installed as part of the current rendering
+ * state for all shader stages where the program is active.
+ * Additionally, the newly generated executable code is made part of
+ * the state of any program pipeline for all stages where the program
+ * is attached."
+ */
+ if (shProg->data->LinkStatus && programs_in_use) {
+ while (programs_in_use) {
+ const int stage = u_bit_scan(&programs_in_use);
+
+ struct gl_program *prog = NULL;
+ if (shProg->_LinkedShaders[stage])
+ prog = shProg->_LinkedShaders[stage]->Program;
+
+ _mesa_use_program(ctx, stage, shProg, prog, ctx->_Shader);
+ }
+ }
+
+ /* Capture .shader_test files. */
+ const char *capture_path = _mesa_get_shader_capture_path();
+ if (shProg->Name != 0 && shProg->Name != ~0 && capture_path != NULL) {
+ FILE *file;
+ char *filename = ralloc_asprintf(NULL, "%s/%u.shader_test",
+ capture_path, shProg->Name);
+ file = fopen(filename, "w");
+ if (file) {
+ fprintf(file, "[require]\nGLSL%s >= %u.%02u\n",
+ shProg->IsES ? " ES" : "",
+ shProg->data->Version / 100, shProg->data->Version % 100);
+ if (shProg->SeparateShader)
+ fprintf(file, "GL_ARB_separate_shader_objects\nSSO ENABLED\n");
+ fprintf(file, "\n");
+
+ for (unsigned i = 0; i < shProg->NumShaders; i++) {
+ fprintf(file, "[%s shader]\n%s\n",
+ _mesa_shader_stage_to_string(shProg->Shaders[i]->Stage),
+ shProg->Shaders[i]->Source);
+ }
+ fclose(file);
+ } else {
+ _mesa_warning(ctx, "Failed to open %s", filename);
+ }
+
+ ralloc_free(filename);
+ }
+
+ if (shProg->data->LinkStatus == linking_failure &&
(ctx->_Shader->Flags & GLSL_REPORT_ERRORS)) {
_mesa_debug(ctx, "Error linking program %u:\n%s\n",
- shProg->Name, shProg->InfoLog);
+ shProg->Name, shProg->data->InfoLog);
}
/* debug code */
printf("Link %u shaders in program %u: %s\n",
shProg->NumShaders, shProg->Name,
- shProg->LinkStatus ? "Success" : "Failed");
+ shProg->data->LinkStatus ? "Success" : "Failed");
for (i = 0; i < shProg->NumShaders; i++) {
- printf(" shader %u, type 0x%x\n",
+ printf(" shader %u, stage %u\n",
shProg->Shaders[i]->Name,
- shProg->Shaders[i]->Type);
+ shProg->Shaders[i]->Stage);
}
}
}
printf("Mesa: glUseProgram(%u)\n", shProg->Name);
for (i = 0; i < shProg->NumShaders; i++) {
+#ifdef DEBUG
printf(" %s shader %u, checksum %u\n",
_mesa_shader_stage_to_string(shProg->Shaders[i]->Stage),
shProg->Shaders[i]->Name,
shProg->Shaders[i]->SourceChecksum);
+#else
+ printf(" %s shader %u\n",
+ _mesa_shader_stage_to_string(shProg->Shaders[i]->Stage),
+ shProg->Shaders[i]->Name);
+#endif
}
if (shProg->_LinkedShaders[MESA_SHADER_VERTEX])
printf(" vert prog %u\n",
if (shProg->_LinkedShaders[MESA_SHADER_GEOMETRY])
printf(" geom prog %u\n",
shProg->_LinkedShaders[MESA_SHADER_GEOMETRY]->Program->Id);
+ if (shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL])
+ printf(" tesc prog %u\n",
+ shProg->_LinkedShaders[MESA_SHADER_TESS_CTRL]->Program->Id);
+ if (shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL])
+ printf(" tese prog %u\n",
+ shProg->_LinkedShaders[MESA_SHADER_TESS_EVAL]->Program->Id);
}
_mesa_active_program(struct gl_context *ctx, struct gl_shader_program *shProg,
const char *caller)
{
- if ((shProg != NULL) && !shProg->LinkStatus) {
+ if ((shProg != NULL) && !shProg->data->LinkStatus) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(program %u not linked)", caller, shProg->Name);
return;
}
-static void
-use_shader_program(struct gl_context *ctx, gl_shader_stage stage,
- struct gl_shader_program *shProg,
- struct gl_pipeline_object *shTarget)
-{
- struct gl_shader_program **target;
-
- target = &shTarget->CurrentProgram[stage];
- if ((shProg != NULL) && (shProg->_LinkedShaders[stage] == NULL))
- shProg = NULL;
-
- if (*target != shProg) {
- /* Program is current, flush it */
- if (shTarget == ctx->_Shader) {
- FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS);
- }
-
- /* If the shader is also bound as the current rendering shader, unbind
- * it from that binding point as well. This ensures that the correct
- * semantics of glDeleteProgram are maintained.
- */
- switch (stage) {
- case MESA_SHADER_VERTEX:
- /* Empty for now. */
- break;
- case MESA_SHADER_GEOMETRY:
- /* Empty for now. */
- break;
- case MESA_SHADER_COMPUTE:
- /* Empty for now. */
- break;
- case MESA_SHADER_FRAGMENT:
- if (*target == ctx->_Shader->_CurrentFragmentProgram) {
- _mesa_reference_shader_program(ctx,
- &ctx->_Shader->_CurrentFragmentProgram,
- NULL);
- }
- break;
- }
-
- _mesa_reference_shader_program(ctx, target, shProg);
- return;
- }
-}
-
-
/**
* Use the named shader program for subsequent rendering.
*/
void
-_mesa_use_program(struct gl_context *ctx, struct gl_shader_program *shProg)
+_mesa_use_shader_program(struct gl_context *ctx,
+ struct gl_shader_program *shProg)
{
- int i;
- for (i = 0; i < MESA_SHADER_STAGES; i++)
- use_shader_program(ctx, i, shProg, &ctx->Shader);
+ for (int i = 0; i < MESA_SHADER_STAGES; i++) {
+ struct gl_program *new_prog = NULL;
+ if (shProg && shProg->_LinkedShaders[i])
+ new_prog = shProg->_LinkedShaders[i]->Program;
+ _mesa_use_program(ctx, i, shProg, new_prog, &ctx->Shader);
+ }
_mesa_active_program(ctx, shProg, "glUseProgram");
-
- if (ctx->Driver.UseProgram)
- ctx->Driver.UseProgram(ctx, shProg);
}
validate_shader_program(const struct gl_shader_program *shProg,
char *errMsg)
{
- if (!shProg->LinkStatus) {
+ if (!shProg->data->LinkStatus) {
return GL_FALSE;
}
return;
}
- shProg->Validated = validate_shader_program(shProg, errMsg);
- if (!shProg->Validated) {
+ shProg->data->Validated = validate_shader_program(shProg, errMsg);
+ if (!shProg->data->Validated) {
/* update info log */
- if (shProg->InfoLog) {
- ralloc_free(shProg->InfoLog);
+ if (shProg->data->InfoLog) {
+ ralloc_free(shProg->data->InfoLog);
}
- shProg->InfoLog = ralloc_strdup(shProg, errMsg);
+ shProg->data->InfoLog = ralloc_strdup(shProg->data, errMsg);
}
}
+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");
}
void GLAPIENTRY
-_mesa_CompileShader(GLhandleARB shaderObj)
+_mesa_CompileShader(GLuint shaderObj)
{
GET_CURRENT_CONTEXT(ctx);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glCompileShader %u\n", shaderObj);
- compile_shader(ctx, shaderObj);
+ _mesa_compile_shader(ctx, _mesa_lookup_shader_err(ctx, shaderObj,
+ "glCompileShader"));
}
{
if (MESA_VERBOSE & VERBOSE_API) {
GET_CURRENT_CONTEXT(ctx);
- _mesa_debug(ctx, "glDeleteObjectARB(%u)\n", obj);
+ _mesa_debug(ctx, "glDeleteObjectARB(%lu)\n", (unsigned long)obj);
}
if (obj) {
_mesa_GetObjectParameterfvARB(GLhandleARB object, GLenum pname,
GLfloat *params)
{
- GLint iparams[1]; /* XXX is one element enough? */
+ GLint iparams[1] = {0}; /* XXX is one element enough? */
_mesa_GetObjectParameterivARB(object, pname, iparams);
params[0] = (GLfloat) iparams[0];
}
void GLAPIENTRY
-_mesa_GetShaderSource(GLhandleARB shader, GLsizei maxLength,
- GLsizei *length, GLcharARB *sourceOut)
+_mesa_GetShaderSource(GLuint shader, GLsizei maxLength,
+ GLsizei *length, GLchar *sourceOut)
{
GET_CURRENT_CONTEXT(ctx);
get_shader_source(ctx, shader, maxLength, length, sourceOut);
void GLAPIENTRY
-_mesa_LinkProgram(GLhandleARB programObj)
+_mesa_LinkProgram(GLuint programObj)
{
GET_CURRENT_CONTEXT(ctx);
- link_program(ctx, programObj);
+ if (MESA_VERBOSE & VERBOSE_API)
+ _mesa_debug(ctx, "glLinkProgram %u\n", programObj);
+ _mesa_link_program(ctx, _mesa_lookup_shader_program_err(ctx, programObj,
+ "glLinkProgram"));
+}
+
+#ifdef ENABLE_SHADER_CACHE
+/**
+ * Generate a SHA-1 hash value string for given source string.
+ */
+static void
+generate_sha1(const char *source, char sha_str[64])
+{
+ unsigned char sha[20];
+ _mesa_sha1_compute(source, strlen(source), sha);
+ _mesa_sha1_format(sha_str, sha);
}
+/**
+ * Construct a full path for shader replacement functionality using
+ * following format:
+ *
+ * <path>/<stage prefix>_<CHECKSUM>.glsl
+ */
+static char *
+construct_name(const gl_shader_stage stage, const char *source,
+ const char *path)
+{
+ char sha[64];
+ static const char *types[] = {
+ "VS", "TC", "TE", "GS", "FS", "CS",
+ };
+
+ generate_sha1(source, sha);
+ return ralloc_asprintf(NULL, "%s/%s_%s.glsl", path, types[stage], sha);
+}
+
+/**
+ * Write given shader source to a file in MESA_SHADER_DUMP_PATH.
+ */
+static void
+dump_shader(const gl_shader_stage stage, const char *source)
+{
+ static bool path_exists = true;
+ char *dump_path;
+ FILE *f;
+
+ if (!path_exists)
+ return;
+
+ dump_path = getenv("MESA_SHADER_DUMP_PATH");
+ if (!dump_path) {
+ path_exists = false;
+ return;
+ }
+
+ char *name = construct_name(stage, source, dump_path);
+ f = fopen(name, "w");
+ if (f) {
+ fputs(source, f);
+ fclose(f);
+ } else {
+ GET_CURRENT_CONTEXT(ctx);
+ _mesa_warning(ctx, "could not open %s for dumping shader (%s)", name,
+ strerror(errno));
+ }
+ ralloc_free(name);
+}
/**
* Read shader source code from a file.
* Useful for debugging to override an app's shader.
*/
static GLcharARB *
-read_shader(const char *fname)
+read_shader(const gl_shader_stage stage, const char *source)
{
- int shader_size = 0;
- FILE *f = fopen(fname, "r");
- GLcharARB *buffer, *shader;
- int len;
+ char *read_path;
+ static bool path_exists = true;
+ int len, shader_size = 0;
+ GLcharARB *buffer;
+ FILE *f;
+
+ if (!path_exists)
+ return NULL;
- if (!f) {
+ read_path = getenv("MESA_SHADER_READ_PATH");
+ if (!read_path) {
+ path_exists = false;
return NULL;
}
+ char *name = construct_name(stage, source, read_path);
+ f = fopen(name, "r");
+ ralloc_free(name);
+ if (!f)
+ return NULL;
+
/* allocate enough room for the entire shader */
fseek(f, 0, SEEK_END);
shader_size = ftell(f);
fclose(f);
- shader = strdup(buffer);
- free(buffer);
-
- return shader;
+ return buffer;
}
+#endif /* ENABLE_SHADER_CACHE */
/**
* Called via glShaderSource() and glShaderSourceARB() API functions.
* and pass it to _mesa_shader_source().
*/
void GLAPIENTRY
-_mesa_ShaderSource(GLhandleARB shaderObj, GLsizei count,
- const GLcharARB * const * string, const GLint * length)
+_mesa_ShaderSource(GLuint shaderObj, GLsizei count,
+ const GLchar * const * string, const GLint * length)
{
GET_CURRENT_CONTEXT(ctx);
GLint *offsets;
GLsizei i, totalLength;
GLcharARB *source;
- GLuint checksum;
+ struct gl_shader *sh;
+
+ sh = _mesa_lookup_shader_err(ctx, shaderObj, "glShaderSourceARB");
+ if (!sh)
+ return;
- if (!shaderObj || string == NULL) {
+ if (string == NULL) {
_mesa_error(ctx, GL_INVALID_VALUE, "glShaderSourceARB");
return;
}
source[totalLength - 1] = '\0';
source[totalLength - 2] = '\0';
- if (SHADER_SUBST) {
- /* Compute the shader's source code checksum then try to open a file
- * named newshader_<CHECKSUM>. If it exists, use it in place of the
- * original shader source code. For debugging.
- */
- char filename[100];
- GLcharARB *newSource;
-
- checksum = _mesa_str_checksum(source);
+#ifdef ENABLE_SHADER_CACHE
+ GLcharARB *replacement;
- _mesa_snprintf(filename, sizeof(filename), "newshader_%d", checksum);
+ /* 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);
- newSource = read_shader(filename);
- if (newSource) {
- fprintf(stderr, "Mesa: Replacing shader %u chksum=%d with %s\n",
- shaderObj, checksum, filename);
- free(source);
- source = newSource;
- }
+ replacement = read_shader(sh->Stage, source);
+ if (replacement) {
+ free(source);
+ source = replacement;
}
+#endif /* ENABLE_SHADER_CACHE */
- shader_source(ctx, shaderObj, source);
-
- if (SHADER_SUBST) {
- struct gl_shader *sh = _mesa_lookup_shader(ctx, shaderObj);
- if (sh)
- sh->SourceChecksum = checksum; /* save original checksum */
- }
+ shader_source(sh, source);
free(offsets);
}
-void GLAPIENTRY
-_mesa_UseProgram(GLhandleARB program)
+static ALWAYS_INLINE void
+use_program(GLuint program, bool no_error)
{
GET_CURRENT_CONTEXT(ctx);
- struct gl_shader_program *shProg;
+ struct gl_shader_program *shProg = NULL;
- if (_mesa_is_xfb_active_and_unpaused(ctx)) {
- _mesa_error(ctx, GL_INVALID_OPERATION,
- "glUseProgram(transform feedback active)");
- return;
- }
+ if (MESA_VERBOSE & VERBOSE_API)
+ _mesa_debug(ctx, "glUseProgram %u\n", program);
- 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->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 */
- _mesa_use_program(ctx, shProg);
+ _mesa_use_shader_program(ctx, shProg);
} else {
/* Must be done first: detach the progam */
- _mesa_use_program(ctx, shProg);
+ _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);
}
}
}
void GLAPIENTRY
-_mesa_ValidateProgram(GLhandleARB program)
+_mesa_UseProgram_no_error(GLuint program)
+{
+ use_program(program, true);
+}
+
+
+void GLAPIENTRY
+_mesa_UseProgram(GLuint program)
+{
+ use_program(program, false);
+}
+
+
+void GLAPIENTRY
+_mesa_ValidateProgram(GLuint program)
{
GET_CURRENT_CONTEXT(ctx);
validate_program(ctx, program);
const void* binary, GLint length)
{
GET_CURRENT_CONTEXT(ctx);
- (void) n;
(void) shaders;
(void) binaryformat;
(void) binary;
- (void) length;
- _mesa_error(ctx, GL_INVALID_OPERATION, "glShaderBinary");
+
+ /* Page 68, section 7.2 'Shader Binaries" of the of the OpenGL ES 3.1, and
+ * page 88 of the OpenGL 4.5 specs state:
+ *
+ * "An INVALID_VALUE error is generated if count or length is negative.
+ * An INVALID_ENUM error is generated if binaryformat is not a supported
+ * format returned in SHADER_BINARY_FORMATS."
+ */
+ if (n < 0 || length < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glShaderBinary(count or length < 0)");
+ return;
+ }
+
+ _mesa_error(ctx, GL_INVALID_ENUM, "glShaderBinary(format)");
}
* length is zero, and a call to GetProgramBinary will generate an
* INVALID_OPERATION error.
*/
- if (!shProg->LinkStatus) {
+ if (!shProg->data->LinkStatus) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glGetProgramBinary(program %u not linked)",
shProg->Name);
* Since any value of binaryFormat passed "is not one of those specified as
* allowable for [this] command, an INVALID_ENUM error is generated."
*/
- shProg->LinkStatus = GL_FALSE;
+ shProg->data->LinkStatus = linking_failure;
_mesa_error(ctx, GL_INVALID_ENUM, "glProgramBinary");
}
void
-_mesa_use_shader_program(struct gl_context *ctx, GLenum type,
- struct gl_shader_program *shProg,
- struct gl_pipeline_object *shTarget)
+_mesa_use_program(struct gl_context *ctx, gl_shader_stage stage,
+ struct gl_shader_program *shProg, struct gl_program *prog,
+ struct gl_pipeline_object *shTarget)
{
- gl_shader_stage stage = _mesa_shader_enum_to_shader_stage(type);
- use_shader_program(ctx, stage, shProg, 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);
+ return;
+ }
- if (ctx->Driver.UseProgram)
- ctx->Driver.UseProgram(ctx, shProg);
}
-static GLuint
-_mesa_create_shader_program(struct gl_context* ctx, GLboolean separate,
- GLenum type, GLsizei count, const GLchar* const *strings)
+/**
+ * Copy program-specific data generated by linking from the gl_shader_program
+ * object to the gl_program object referred to by the gl_linked_shader.
+ *
+ * This function expects _mesa_reference_program() to have been previously
+ * called setting the gl_linked_shaders program reference.
+ */
+void
+_mesa_copy_linked_program_data(const struct gl_shader_program *src,
+ struct gl_linked_shader *dst_sh)
{
+ assert(dst_sh->Program);
+
+ struct gl_program *dst = dst_sh->Program;
+
+ dst->info.separate_shader = src->SeparateShader;
+
+ switch (dst_sh->Stage) {
+ case MESA_SHADER_GEOMETRY: {
+ dst->info.gs.vertices_in = src->Geom.VerticesIn;
+ dst->info.gs.uses_end_primitive = src->Geom.UsesEndPrimitive;
+ dst->info.gs.uses_streams = src->Geom.UsesStreams;
+ break;
+ }
+ case MESA_SHADER_FRAGMENT: {
+ dst->info.fs.depth_layout = src->FragDepthLayout;
+ break;
+ }
+ case MESA_SHADER_COMPUTE: {
+ dst->info.cs.shared_size = src->Comp.SharedSize;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/**
+ * ARB_separate_shader_objects: Compile & Link Program
+ */
+GLuint GLAPIENTRY
+_mesa_CreateShaderProgramv(GLenum type, GLsizei count,
+ const GLchar* const *strings)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
const GLuint shader = create_shader(ctx, type);
GLuint program = 0;
+ /*
+ * According to OpenGL 4.5 and OpenGL ES 3.1 standards, section 7.3:
+ * GL_INVALID_VALUE should be generated if count < 0
+ */
+ if (count < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glCreateShaderProgram (count < 0)");
+ return program;
+ }
+
if (shader) {
- _mesa_ShaderSource(shader, count, strings, NULL);
+ struct gl_shader *sh = _mesa_lookup_shader(ctx, shader);
- compile_shader(ctx, shader);
+ _mesa_ShaderSource(shader, count, strings, NULL);
+ _mesa_compile_shader(ctx, sh);
program = create_shader_program(ctx);
if (program) {
struct gl_shader_program *shProg;
- struct gl_shader *sh;
GLint compiled = GL_FALSE;
shProg = _mesa_lookup_shader_program(ctx, program);
- sh = _mesa_lookup_shader(ctx, shader);
- shProg->SeparateShader = separate;
+ shProg->SeparateShader = GL_TRUE;
get_shaderiv(ctx, shader, GL_COMPILE_STATUS, &compiled);
if (compiled) {
- attach_shader(ctx, program, shader);
- link_program(ctx, program);
+ attach_shader_err(ctx, program, shader, "glCreateShaderProgramv");
+ _mesa_link_program(ctx, shProg);
detach_shader(ctx, program, shader);
#if 0
/* Possibly... */
if (active-user-defined-varyings-in-linked-program) {
append-error-to-info-log;
- shProg->LinkStatus = GL_FALSE;
+ shProg->data->LinkStatus = linking_failure;
}
#endif
}
-
- ralloc_strcat(&shProg->InfoLog, sh->InfoLog);
+ if (sh->InfoLog)
+ ralloc_strcat(&shProg->data->InfoLog, sh->InfoLog);
}
delete_shader(ctx, shader);
}
-/**
- * Copy program-specific data generated by linking from the gl_shader_program
- * object to a specific gl_program object.
- */
-void
-_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;
- dst_gp->UsesStreams = src->Geom.UsesStreams;
- }
- break;
- case MESA_SHADER_FRAGMENT: {
- struct gl_fragment_program *dst_fp = (struct gl_fragment_program *) dst;
- dst_fp->FragDepthLayout = src->FragDepthLayout;
- }
- 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;
- }
-}
-
-/**
- * ARB_separate_shader_objects: Compile & Link Program
- */
-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);
-}
-
-
/**
* For GL_ARB_tessellation_shader
*/
}
}
+/**
+ * ARB_shader_subroutine
+ */
+GLint GLAPIENTRY
+_mesa_GetSubroutineUniformLocation(GLuint program, GLenum shadertype,
+ const GLchar *name)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetSubroutineUniformLocation";
+ struct gl_shader_program *shProg;
+ GLenum resource_type;
+ gl_shader_stage stage;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return -1;
+ }
+
+ shProg = _mesa_lookup_shader_program_err(ctx, program, api_name);
+ if (!shProg)
+ return -1;
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ if (!shProg->_LinkedShaders[stage]) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return -1;
+ }
+
+ resource_type = _mesa_shader_stage_to_subroutine_uniform(stage);
+ return _mesa_program_resource_location(shProg, resource_type, name);
+}
+
+GLuint GLAPIENTRY
+_mesa_GetSubroutineIndex(GLuint program, GLenum shadertype,
+ const GLchar *name)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetSubroutineIndex";
+ struct gl_shader_program *shProg;
+ struct gl_program_resource *res;
+ GLenum resource_type;
+ gl_shader_stage stage;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return -1;
+ }
+
+ shProg = _mesa_lookup_shader_program_err(ctx, program, api_name);
+ if (!shProg)
+ return -1;
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ if (!shProg->_LinkedShaders[stage]) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return -1;
+ }
+
+ resource_type = _mesa_shader_stage_to_subroutine(stage);
+ res = _mesa_program_resource_find_name(shProg, resource_type, name, NULL);
+ if (!res) {
+ return -1;
+ }
+
+ return _mesa_program_resource_index(shProg, res);
+}
+
+
+GLvoid GLAPIENTRY
+_mesa_GetActiveSubroutineUniformiv(GLuint program, GLenum shadertype,
+ GLuint index, GLenum pname, GLint *values)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetActiveSubroutineUniformiv";
+ struct gl_shader_program *shProg;
+ struct gl_linked_shader *sh;
+ gl_shader_stage stage;
+ struct gl_program_resource *res;
+ const struct gl_uniform_storage *uni;
+ GLenum resource_type;
+ int count, i, j;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ shProg = _mesa_lookup_shader_program_err(ctx, program, api_name);
+ if (!shProg)
+ return;
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ resource_type = _mesa_shader_stage_to_subroutine_uniform(stage);
+
+ sh = shProg->_LinkedShaders[stage];
+ if (!sh) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ struct gl_program *p = shProg->_LinkedShaders[stage]->Program;
+ if (index >= p->sh.NumSubroutineUniforms) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s: invalid index greater than GL_ACTIVE_SUBROUTINE_UNIFORMS", api_name);
+ return;
+ }
+
+ switch (pname) {
+ case GL_NUM_COMPATIBLE_SUBROUTINES: {
+ res = _mesa_program_resource_find_index(shProg, resource_type, index);
+ if (res) {
+ uni = res->Data;
+ values[0] = uni->num_compatible_subroutines;
+ }
+ break;
+ }
+ case GL_COMPATIBLE_SUBROUTINES: {
+ res = _mesa_program_resource_find_index(shProg, resource_type, index);
+ if (res) {
+ uni = res->Data;
+ count = 0;
+ for (i = 0; i < p->sh.NumSubroutineFunctions; i++) {
+ struct gl_subroutine_function *fn = &p->sh.SubroutineFunctions[i];
+ for (j = 0; j < fn->num_compat_types; j++) {
+ if (fn->types[j] == uni->type) {
+ values[count++] = i;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case GL_UNIFORM_SIZE:
+ res = _mesa_program_resource_find_index(shProg, resource_type, index);
+ if (res) {
+ uni = res->Data;
+ values[0] = uni->array_elements ? uni->array_elements : 1;
+ }
+ break;
+ case GL_UNIFORM_NAME_LENGTH:
+ res = _mesa_program_resource_find_index(shProg, resource_type, index);
+ if (res) {
+ values[0] = strlen(_mesa_program_resource_name(res)) + 1
+ + ((_mesa_program_resource_array_size(res) != 0) ? 3 : 0);
+ }
+ break;
+ default:
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+}
+
+
+GLvoid GLAPIENTRY
+_mesa_GetActiveSubroutineUniformName(GLuint program, GLenum shadertype,
+ GLuint index, GLsizei bufsize,
+ GLsizei *length, GLchar *name)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetActiveSubroutineUniformName";
+ struct gl_shader_program *shProg;
+ GLenum resource_type;
+ gl_shader_stage stage;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ shProg = _mesa_lookup_shader_program_err(ctx, program, api_name);
+ if (!shProg)
+ return;
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ if (!shProg->_LinkedShaders[stage]) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ resource_type = _mesa_shader_stage_to_subroutine_uniform(stage);
+ /* get program resource name */
+ _mesa_get_program_resource_name(shProg, resource_type,
+ index, bufsize,
+ length, name, api_name);
+}
+
+
+GLvoid GLAPIENTRY
+_mesa_GetActiveSubroutineName(GLuint program, GLenum shadertype,
+ GLuint index, GLsizei bufsize,
+ GLsizei *length, GLchar *name)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetActiveSubroutineName";
+ struct gl_shader_program *shProg;
+ GLenum resource_type;
+ gl_shader_stage stage;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ shProg = _mesa_lookup_shader_program_err(ctx, program, api_name);
+ if (!shProg)
+ return;
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ if (!shProg->_LinkedShaders[stage]) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+ resource_type = _mesa_shader_stage_to_subroutine(stage);
+ _mesa_get_program_resource_name(shProg, resource_type,
+ index, bufsize,
+ length, name, api_name);
+}
+
+GLvoid GLAPIENTRY
+_mesa_UniformSubroutinesuiv(GLenum shadertype, GLsizei count,
+ const GLuint *indices)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glUniformSubroutinesuiv";
+ gl_shader_stage stage;
+ int i;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ struct gl_program *p = ctx->_Shader->CurrentProgram[stage];
+ if (!p) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ if (count != p->sh.NumSubroutineUniformRemapTable) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s", api_name);
+ return;
+ }
+
+ i = 0;
+ bool flushed = false;
+ do {
+ struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i];
+ if (uni == NULL) {
+ i++;
+ 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;
+
+ for (j = i; j < i + uni_count; j++) {
+ struct gl_subroutine_function *subfn = NULL;
+ if (indices[j] > p->sh.MaxSubroutineFunctionIndex) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s", api_name);
+ return;
+ }
+
+ for (f = 0; f < p->sh.NumSubroutineFunctions; f++) {
+ if (p->sh.SubroutineFunctions[f].index == indices[j])
+ subfn = &p->sh.SubroutineFunctions[f];
+ }
+
+ if (!subfn) {
+ continue;
+ }
+
+ for (k = 0; k < subfn->num_compat_types; k++) {
+ if (subfn->types[k] == uni->type)
+ break;
+ }
+ if (k == subfn->num_compat_types) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ ctx->SubroutineIndex[p->info.stage].IndexPtr[j] = indices[j];
+ }
+ i += uni_count;
+ } while(i < count);
+}
+
+
+GLvoid GLAPIENTRY
+_mesa_GetUniformSubroutineuiv(GLenum shadertype, GLint location,
+ GLuint *params)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetUniformSubroutineuiv";
+ gl_shader_stage stage;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ struct gl_program *p = ctx->_Shader->CurrentProgram[stage];
+ if (!p) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ if (location >= p->sh.NumSubroutineUniformRemapTable) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s", api_name);
+ return;
+ }
+
+ *params = ctx->SubroutineIndex[p->info.stage].IndexPtr[location];
+}
+
+
+GLvoid GLAPIENTRY
+_mesa_GetProgramStageiv(GLuint program, GLenum shadertype,
+ GLenum pname, GLint *values)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const char *api_name = "glGetProgramStageiv";
+ struct gl_shader_program *shProg;
+ struct gl_linked_shader *sh;
+ gl_shader_stage stage;
+
+ if (!_mesa_validate_shader_target(ctx, shadertype)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ return;
+ }
+
+ shProg = _mesa_lookup_shader_program_err(ctx, program, api_name);
+ if (!shProg)
+ return;
+
+ stage = _mesa_shader_enum_to_shader_stage(shadertype);
+ sh = shProg->_LinkedShaders[stage];
+
+ /* ARB_shader_subroutine doesn't ask the program to be linked, or list any
+ * INVALID_OPERATION in the case of not be linked.
+ *
+ * And for some pnames, like GL_ACTIVE_SUBROUTINE_UNIFORMS, you can ask the
+ * same info using other specs (ARB_program_interface_query), without the
+ * need of the program to be linked, being the value for that case 0.
+ *
+ * But at the same time, some other methods require the program to be
+ * linked for pname related to locations, so it would be inconsistent to
+ * not do the same here. So we are:
+ * * Return GL_INVALID_OPERATION if not linked only for locations.
+ * * Setting a default value of 0, to be returned if not linked.
+ */
+ if (!sh) {
+ values[0] = 0;
+ if (pname == GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s", api_name);
+ }
+ return;
+ }
+
+ struct gl_program *p = sh->Program;
+ switch (pname) {
+ case GL_ACTIVE_SUBROUTINES:
+ values[0] = p->sh.NumSubroutineFunctions;
+ break;
+ case GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS:
+ values[0] = p->sh.NumSubroutineUniformRemapTable;
+ break;
+ case GL_ACTIVE_SUBROUTINE_UNIFORMS:
+ values[0] = p->sh.NumSubroutineUniforms;
+ break;
+ case GL_ACTIVE_SUBROUTINE_MAX_LENGTH:
+ {
+ unsigned i;
+ GLint max_len = 0;
+ GLenum resource_type;
+ struct gl_program_resource *res;
+
+ resource_type = _mesa_shader_stage_to_subroutine(stage);
+ for (i = 0; i < p->sh.NumSubroutineFunctions; i++) {
+ res = _mesa_program_resource_find_index(shProg, resource_type, i);
+ if (res) {
+ const GLint len = strlen(_mesa_program_resource_name(res)) + 1;
+ if (len > max_len)
+ max_len = len;
+ }
+ }
+ values[0] = max_len;
+ break;
+ }
+ case GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH:
+ {
+ unsigned i;
+ GLint max_len = 0;
+ GLenum resource_type;
+ struct gl_program_resource *res;
+
+ resource_type = _mesa_shader_stage_to_subroutine_uniform(stage);
+ for (i = 0; i < p->sh.NumSubroutineUniformRemapTable; i++) {
+ res = _mesa_program_resource_find_index(shProg, resource_type, i);
+ if (res) {
+ const GLint len = strlen(_mesa_program_resource_name(res)) + 1
+ + ((_mesa_program_resource_array_size(res) != 0) ? 3 : 0);
+
+ if (len > max_len)
+ max_len = len;
+ }
+ }
+ values[0] = max_len;
+ break;
+ }
+ default:
+ _mesa_error(ctx, GL_INVALID_ENUM, "%s", api_name);
+ values[0] = -1;
+ break;
+ }
+}
+
+static int
+find_compat_subroutine(struct gl_program *p, const struct glsl_type *type)
+{
+ int i, j;
+
+ for (i = 0; i < p->sh.NumSubroutineFunctions; i++) {
+ struct gl_subroutine_function *fn = &p->sh.SubroutineFunctions[i];
+ for (j = 0; j < fn->num_compat_types; j++) {
+ if (fn->types[j] == type)
+ return i;
+ }
+ }
+ return 0;
+}
+
+static void
+_mesa_shader_write_subroutine_index(struct gl_context *ctx,
+ struct gl_program *p)
+{
+ int i, j;
+
+ if (p->sh.NumSubroutineUniformRemapTable == 0)
+ return;
+
+ i = 0;
+ do {
+ struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i];
+ int uni_count;
+ int val;
+
+ if (!uni) {
+ i++;
+ continue;
+ }
+
+ uni_count = uni->array_elements ? uni->array_elements : 1;
+ for (j = 0; j < uni_count; j++) {
+ val = ctx->SubroutineIndex[p->info.stage].IndexPtr[i + j];
+ memcpy(&uni->storage[j], &val, sizeof(int));
+ }
+
+ _mesa_propagate_uniforms_to_driver_storage(uni, 0, uni_count);
+ i += uni_count;
+ } while(i < p->sh.NumSubroutineUniformRemapTable);
+}
+
+void
+_mesa_shader_write_subroutine_indices(struct gl_context *ctx,
+ gl_shader_stage stage)
+{
+ if (ctx->_Shader->CurrentProgram[stage])
+ _mesa_shader_write_subroutine_index(ctx,
+ ctx->_Shader->CurrentProgram[stage]);
+}
+
+void
+_mesa_program_init_subroutine_defaults(struct gl_context *ctx,
+ struct gl_program *p)
+{
+ assert(p);
+
+ struct gl_subroutine_index_binding *binding = &ctx->SubroutineIndex[p->info.stage];
+ if (binding->NumIndex != p->sh.NumSubroutineUniformRemapTable) {
+ binding->IndexPtr = realloc(binding->IndexPtr,
+ p->sh.NumSubroutineUniformRemapTable * (sizeof(GLuint)));
+ binding->NumIndex = p->sh.NumSubroutineUniformRemapTable;
+ }
+
+ for (int i = 0; i < p->sh.NumSubroutineUniformRemapTable; i++) {
+ struct gl_uniform_storage *uni = p->sh.SubroutineUniformRemapTable[i];
+
+ if (!uni)
+ continue;
+
+ binding->IndexPtr[i] = find_compat_subroutine(p, uni->type);
+ }
+}