/* * Mesa 3-D graphics library * * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * \file program.c * Vertex and fragment program support functions. * \author Brian Paul */ #include "util/glheader.h" #include "main/context.h" #include "main/framebuffer.h" #include "main/hash.h" #include "main/macros.h" #include "main/shaderobj.h" #include "main/state.h" #include "program.h" #include "prog_cache.h" #include "prog_parameter.h" #include "prog_instruction.h" #include "util/bitscan.h" #include "util/ralloc.h" #include "util/u_atomic.h" #include "state_tracker/st_program.h" #include "state_tracker/st_context.h" /** * A pointer to this dummy program is put into the hash table when * glGenPrograms is called. */ struct gl_program _mesa_DummyProgram; /** * Init context's vertex/fragment program state */ void _mesa_init_program(struct gl_context *ctx) { /* * If this assertion fails, we need to increase the field * size for register indexes (see INST_INDEX_BITS). */ assert(ctx->Const.Program[MESA_SHADER_VERTEX].MaxUniformComponents / 4 <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_FRAGMENT].MaxUniformComponents / 4 <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_VERTEX].MaxTemps <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_VERTEX].MaxLocalParams <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_FRAGMENT].MaxTemps <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_FRAGMENT].MaxLocalParams <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_VERTEX].MaxUniformComponents <= 4 * MAX_UNIFORMS); assert(ctx->Const.Program[MESA_SHADER_FRAGMENT].MaxUniformComponents <= 4 * MAX_UNIFORMS); assert(ctx->Const.Program[MESA_SHADER_VERTEX].MaxAddressOffset <= (1 << INST_INDEX_BITS)); assert(ctx->Const.Program[MESA_SHADER_FRAGMENT].MaxAddressOffset <= (1 << INST_INDEX_BITS)); /* If this fails, increase prog_instruction::TexSrcUnit size */ STATIC_ASSERT(MAX_TEXTURE_UNITS <= (1 << 5)); /* If this fails, increase prog_instruction::TexSrcTarget size */ STATIC_ASSERT(NUM_TEXTURE_TARGETS <= (1 << 4)); ctx->Program.ErrorPos = -1; ctx->Program.ErrorString = strdup(""); ctx->VertexProgram._VaryingInputs = VERT_BIT_ALL; ctx->VertexProgram.Enabled = GL_FALSE; ctx->VertexProgram.PointSizeEnabled = _mesa_is_gles2(ctx) ? GL_TRUE : GL_FALSE; ctx->VertexProgram.TwoSideEnabled = GL_FALSE; _mesa_reference_program(ctx, &ctx->VertexProgram.Current, ctx->Shared->DefaultVertexProgram); assert(ctx->VertexProgram.Current); ctx->VertexProgram.Cache = _mesa_new_program_cache(); ctx->FragmentProgram.Enabled = GL_FALSE; _mesa_reference_program(ctx, &ctx->FragmentProgram.Current, ctx->Shared->DefaultFragmentProgram); assert(ctx->FragmentProgram.Current); ctx->FragmentProgram.Cache = _mesa_new_program_cache(); _mesa_reset_vertex_processing_mode(ctx); /* XXX probably move this stuff */ ctx->ATIFragmentShader.Enabled = GL_FALSE; ctx->ATIFragmentShader.Current = ctx->Shared->DefaultFragmentShader; assert(ctx->ATIFragmentShader.Current); ctx->ATIFragmentShader.Current->RefCount++; } /** * Free a context's vertex/fragment program state */ void _mesa_free_program_data(struct gl_context *ctx) { _mesa_reference_program(ctx, &ctx->VertexProgram.Current, NULL); _mesa_delete_program_cache(ctx, ctx->VertexProgram.Cache); _mesa_reference_program(ctx, &ctx->FragmentProgram.Current, NULL); _mesa_delete_program_cache(ctx, ctx->FragmentProgram.Cache); /* XXX probably move this stuff */ if (ctx->ATIFragmentShader.Current) { ctx->ATIFragmentShader.Current->RefCount--; if (ctx->ATIFragmentShader.Current->RefCount <= 0) { free(ctx->ATIFragmentShader.Current); } } free((void *) ctx->Program.ErrorString); } /** * Update the default program objects in the given context to reference those * specified in the shared state and release those referencing the old * shared state. */ void _mesa_update_default_objects_program(struct gl_context *ctx) { _mesa_reference_program(ctx, &ctx->VertexProgram.Current, ctx->Shared->DefaultVertexProgram); assert(ctx->VertexProgram.Current); _mesa_reference_program(ctx, &ctx->FragmentProgram.Current, ctx->Shared->DefaultFragmentProgram); assert(ctx->FragmentProgram.Current); /* XXX probably move this stuff */ if (ctx->ATIFragmentShader.Current) { ctx->ATIFragmentShader.Current->RefCount--; if (ctx->ATIFragmentShader.Current->RefCount <= 0) { free(ctx->ATIFragmentShader.Current); } } ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader; assert(ctx->ATIFragmentShader.Current); ctx->ATIFragmentShader.Current->RefCount++; } /** * Set the vertex/fragment program error state (position and error string). * This is generally called from within the parsers. */ void _mesa_set_program_error(struct gl_context *ctx, GLint pos, const char *string) { ctx->Program.ErrorPos = pos; free((void *) ctx->Program.ErrorString); if (!string) string = ""; ctx->Program.ErrorString = strdup(string); } /** * Initialize a new gl_program object. */ struct gl_program * _mesa_init_gl_program(struct gl_program *prog, gl_shader_stage stage, GLuint id, bool is_arb_asm) { if (!prog) return NULL; memset(prog, 0, sizeof(*prog)); prog->Id = id; prog->Target = _mesa_shader_stage_to_program(stage); prog->RefCount = 1; prog->Format = GL_PROGRAM_FORMAT_ASCII_ARB; prog->info.stage = stage; prog->info.use_legacy_math_rules = is_arb_asm; /* Uniforms that lack an initializer in the shader code have an initial * value of zero. This includes sampler uniforms. * * Page 24 (page 30 of the PDF) of the GLSL 1.20 spec says: * * "The link time initial value is either the value of the variable's * initializer, if present, or 0 if no initializer is present. Sampler * types cannot have initializers." * * So we only initialise ARB assembly style programs. */ if (is_arb_asm) { /* default mapping from samplers to texture units */ for (unsigned i = 0; i < MAX_SAMPLERS; i++) prog->SamplerUnits[i] = i; } return prog; } struct gl_program * _mesa_new_program(struct gl_context *ctx, gl_shader_stage stage, GLuint id, bool is_arb_asm) { struct gl_program *prog; switch (stage) { case MESA_SHADER_VERTEX: prog = (struct gl_program*)rzalloc(NULL, struct gl_vertex_program); break; default: prog = rzalloc(NULL, struct gl_program); break; } return _mesa_init_gl_program(prog, stage, id, is_arb_asm); } /** * Delete a program and remove it from the hash table, ignoring the * reference count. */ void _mesa_delete_program(struct gl_context *ctx, struct gl_program *prog) { struct st_context *st = st_context(ctx); assert(prog); assert(prog->RefCount==0); st_release_variants(st, prog); free(prog->serialized_nir); free(prog->base_serialized_nir); if (prog == &_mesa_DummyProgram) return; if (prog->Parameters) { _mesa_free_parameter_list(prog->Parameters); } if (prog->nir) { ralloc_free(prog->nir); } if (prog->sh.BindlessSamplers) { ralloc_free(prog->sh.BindlessSamplers); } if (prog->sh.BindlessImages) { ralloc_free(prog->sh.BindlessImages); } if (prog->driver_cache_blob) { ralloc_free(prog->driver_cache_blob); } ralloc_free(prog); } /** * Return the gl_program object for a given ID. * Basically just a wrapper for _mesa_HashLookup() to avoid a lot of * casts elsewhere. */ struct gl_program * _mesa_lookup_program(struct gl_context *ctx, GLuint id) { if (id) return (struct gl_program *) _mesa_HashLookup(&ctx->Shared->Programs, id); else return NULL; } /** * Reference counting for vertex/fragment programs * This is normally only called from the _mesa_reference_program() macro * when there's a real pointer change. */ void _mesa_reference_program_(struct gl_context *ctx, struct gl_program **ptr, struct gl_program *prog) { #ifndef NDEBUG assert(ptr); if (*ptr && prog) { /* sanity check */ if ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB) assert(prog->Target == GL_VERTEX_PROGRAM_ARB); else if ((*ptr)->Target == GL_FRAGMENT_PROGRAM_ARB) assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB || prog->Target == GL_FRAGMENT_PROGRAM_NV); else if ((*ptr)->Target == GL_GEOMETRY_PROGRAM_NV) assert(prog->Target == GL_GEOMETRY_PROGRAM_NV); } #endif if (*ptr) { struct gl_program *oldProg = *ptr; assert(oldProg->RefCount > 0); if (p_atomic_dec_zero(&oldProg->RefCount)) { assert(ctx); _mesa_reference_shader_program_data(&oldProg->sh.data, NULL); _mesa_delete_program(ctx, oldProg); } *ptr = NULL; } assert(!*ptr); if (prog) { p_atomic_inc(&prog->RefCount); } *ptr = prog; } /* Gets the minimum number of shader invocations per fragment. * This function is useful to determine if we need to do per * sample shading or per fragment shading. */ GLint _mesa_get_min_invocations_per_fragment(struct gl_context *ctx, const struct gl_program *prog) { /* From ARB_sample_shading specification: * "Using gl_SampleID in a fragment shader causes the entire shader * to be evaluated per-sample." * * "Using gl_SamplePosition in a fragment shader causes the entire * shader to be evaluated per-sample." * * "If MULTISAMPLE or SAMPLE_SHADING_ARB is disabled, sample shading * has no effect." */ if (ctx->Multisample.Enabled) { /* The ARB_gpu_shader5 specification says: * * "Use of the "sample" qualifier on a fragment shader input * forces per-sample shading" */ if (prog->info.fs.uses_sample_qualifier || BITSET_TEST(prog->info.system_values_read, SYSTEM_VALUE_SAMPLE_ID) || BITSET_TEST(prog->info.system_values_read, SYSTEM_VALUE_SAMPLE_POS)) return MAX2(_mesa_geometric_samples(ctx->DrawBuffer), 1); else if (ctx->Multisample.SampleShading) return MAX2(ceilf(ctx->Multisample.MinSampleShadingValue * _mesa_geometric_samples(ctx->DrawBuffer)), 1); else return 1; } return 1; } GLbitfield gl_external_samplers(const struct gl_program *prog) { GLbitfield external_samplers = 0; GLbitfield mask = prog->SamplersUsed; while (mask) { int idx = u_bit_scan(&mask); if (prog->sh.SamplerTargets[idx] == TEXTURE_EXTERNAL_INDEX) external_samplers |= (1 << idx); } return external_samplers; } static int compare_state_var(const void *a1, const void *a2) { const struct gl_program_parameter *p1 = (const struct gl_program_parameter *)a1; const struct gl_program_parameter *p2 = (const struct gl_program_parameter *)a2; for (unsigned i = 0; i < STATE_LENGTH; i++) { if (p1->StateIndexes[i] != p2->StateIndexes[i]) return p1->StateIndexes[i] - p2->StateIndexes[i]; } return 0; } void _mesa_add_separate_state_parameters(struct gl_program *prog, struct gl_program_parameter_list *state_params) { unsigned num_state_params = state_params->NumParameters; if (num_state_params == 0) return; /* All state parameters should be vec4s. */ for (unsigned i = 0; i < num_state_params; i++) { assert(state_params->Parameters[i].Type == PROGRAM_STATE_VAR); assert(state_params->Parameters[i].Size == 4); assert(state_params->Parameters[i].ValueOffset == i * 4); } /* Sort state parameters to facilitate better parameter merging. */ qsort(state_params->Parameters, num_state_params, sizeof(state_params->Parameters[0]), compare_state_var); unsigned *remap = malloc(num_state_params * sizeof(unsigned)); /* Add state parameters to the end of the parameter list. */ for (unsigned i = 0; i < num_state_params; i++) { unsigned old_index = state_params->Parameters[i].ValueOffset / 4; remap[old_index] = _mesa_add_parameter(prog->Parameters, PROGRAM_STATE_VAR, state_params->Parameters[i].Name, state_params->Parameters[i].Size, GL_NONE, NULL, state_params->Parameters[i].StateIndexes, state_params->Parameters[i].Padded); prog->Parameters->StateFlags |= _mesa_program_state_flags(state_params->Parameters[i].StateIndexes); } /* Fix up state parameter offsets in instructions. */ int num_instr = prog->arb.NumInstructions; struct prog_instruction *instrs = prog->arb.Instructions; /* Fix src indices after sorting. */ for (unsigned i = 0; i < num_instr; i++) { struct prog_instruction *inst = instrs + i; unsigned num_src = _mesa_num_inst_src_regs(inst->Opcode); for (unsigned j = 0; j < num_src; j++) { if (inst->SrcReg[j].File == PROGRAM_STATE_VAR) inst->SrcReg[j].Index = remap[inst->SrcReg[j].Index]; } } free(remap); }