+ * Translate a geometry program to create a new variant.
+ */
+bool
+st_translate_geometry_program(struct st_context *st,
+ struct st_geometry_program *stgp)
+{
+ struct ureg_program *ureg;
+
+ ureg = ureg_create_with_screen(TGSI_PROCESSOR_GEOMETRY, st->pipe->screen);
+ if (ureg == NULL)
+ return false;
+
+ ureg_property(ureg, TGSI_PROPERTY_GS_INPUT_PRIM, stgp->Base.InputType);
+ ureg_property(ureg, TGSI_PROPERTY_GS_OUTPUT_PRIM, stgp->Base.OutputType);
+ ureg_property(ureg, TGSI_PROPERTY_GS_MAX_OUTPUT_VERTICES,
+ stgp->Base.VerticesOut);
+ ureg_property(ureg, TGSI_PROPERTY_GS_INVOCATIONS, stgp->Base.Invocations);
+
+ st_translate_program_common(st, &stgp->Base.Base, stgp->glsl_to_tgsi, ureg,
+ TGSI_PROCESSOR_GEOMETRY, &stgp->tgsi);
+
+ free_glsl_to_tgsi_visitor(stgp->glsl_to_tgsi);
+ stgp->glsl_to_tgsi = NULL;
+ return true;
+}
+
+
+/**
+ * Get/create a basic program variant.
+ */
+struct st_basic_variant *
+st_get_basic_variant(struct st_context *st,
+ unsigned pipe_shader,
+ struct pipe_shader_state *tgsi,
+ struct st_basic_variant **variants)
+{
+ struct pipe_context *pipe = st->pipe;
+ struct st_basic_variant *v;
+ struct st_basic_variant_key key;
+
+ memset(&key, 0, sizeof(key));
+ key.st = st->has_shareable_shaders ? NULL : st;
+
+ /* Search for existing variant */
+ for (v = *variants; v; v = v->next) {
+ if (memcmp(&v->key, &key, sizeof(key)) == 0) {
+ break;
+ }
+ }
+
+ if (!v) {
+ /* create new */
+ v = CALLOC_STRUCT(st_basic_variant);
+ if (v) {
+ /* fill in new variant */
+ switch (pipe_shader) {
+ case PIPE_SHADER_TESS_CTRL:
+ v->driver_shader = pipe->create_tcs_state(pipe, tgsi);
+ break;
+ case PIPE_SHADER_TESS_EVAL:
+ v->driver_shader = pipe->create_tes_state(pipe, tgsi);
+ break;
+ case PIPE_SHADER_GEOMETRY:
+ v->driver_shader = pipe->create_gs_state(pipe, tgsi);
+ break;
+ default:
+ assert(!"unhandled shader type");
+ free(v);
+ return NULL;
+ }
+
+ v->key = key;
+
+ /* insert into list */
+ v->next = *variants;
+ *variants = v;
+ }
+ }
+
+ return v;
+}
+
+
+/**
+ * Translate a tessellation control program to create a new variant.
+ */
+bool
+st_translate_tessctrl_program(struct st_context *st,
+ struct st_tessctrl_program *sttcp)
+{
+ struct ureg_program *ureg;
+
+ ureg = ureg_create_with_screen(TGSI_PROCESSOR_TESS_CTRL, st->pipe->screen);
+ if (ureg == NULL)
+ return false;
+
+ ureg_property(ureg, TGSI_PROPERTY_TCS_VERTICES_OUT,
+ sttcp->Base.VerticesOut);
+
+ st_translate_program_common(st, &sttcp->Base.Base, sttcp->glsl_to_tgsi,
+ ureg, TGSI_PROCESSOR_TESS_CTRL, &sttcp->tgsi);
+
+ free_glsl_to_tgsi_visitor(sttcp->glsl_to_tgsi);
+ sttcp->glsl_to_tgsi = NULL;
+ return true;
+}
+
+
+/**
+ * Translate a tessellation evaluation program to create a new variant.
+ */
+bool
+st_translate_tesseval_program(struct st_context *st,
+ struct st_tesseval_program *sttep)
+{
+ struct ureg_program *ureg;
+
+ ureg = ureg_create_with_screen(TGSI_PROCESSOR_TESS_EVAL, st->pipe->screen);
+ if (ureg == NULL)
+ return false;
+
+ if (sttep->Base.PrimitiveMode == GL_ISOLINES)
+ ureg_property(ureg, TGSI_PROPERTY_TES_PRIM_MODE, GL_LINES);
+ else
+ ureg_property(ureg, TGSI_PROPERTY_TES_PRIM_MODE, sttep->Base.PrimitiveMode);
+
+ switch (sttep->Base.Spacing) {
+ case GL_EQUAL:
+ ureg_property(ureg, TGSI_PROPERTY_TES_SPACING, PIPE_TESS_SPACING_EQUAL);
+ break;
+ case GL_FRACTIONAL_EVEN:
+ ureg_property(ureg, TGSI_PROPERTY_TES_SPACING,
+ PIPE_TESS_SPACING_FRACTIONAL_EVEN);
+ break;
+ case GL_FRACTIONAL_ODD:
+ ureg_property(ureg, TGSI_PROPERTY_TES_SPACING,
+ PIPE_TESS_SPACING_FRACTIONAL_ODD);
+ break;
+ default:
+ assert(0);
+ }
+
+ ureg_property(ureg, TGSI_PROPERTY_TES_VERTEX_ORDER_CW,
+ sttep->Base.VertexOrder == GL_CW);
+ ureg_property(ureg, TGSI_PROPERTY_TES_POINT_MODE, sttep->Base.PointMode);
+
+ st_translate_program_common(st, &sttep->Base.Base, sttep->glsl_to_tgsi,
+ ureg, TGSI_PROCESSOR_TESS_EVAL, &sttep->tgsi);
+
+ free_glsl_to_tgsi_visitor(sttep->glsl_to_tgsi);
+ sttep->glsl_to_tgsi = NULL;
+ return true;
+}
+
+
+/**
+ * Translate a compute program to create a new variant.
+ */
+bool
+st_translate_compute_program(struct st_context *st,
+ struct st_compute_program *stcp)
+{
+ struct ureg_program *ureg;
+ struct pipe_shader_state prog;
+
+ ureg = ureg_create_with_screen(TGSI_PROCESSOR_COMPUTE, st->pipe->screen);
+ if (ureg == NULL)
+ return false;
+
+ st_translate_program_common(st, &stcp->Base.Base, stcp->glsl_to_tgsi, ureg,
+ TGSI_PROCESSOR_COMPUTE, &prog);
+
+ stcp->tgsi.ir_type = PIPE_SHADER_IR_TGSI;
+ stcp->tgsi.prog = prog.tokens;
+ stcp->tgsi.req_local_mem = stcp->Base.SharedSize;
+ stcp->tgsi.req_private_mem = 0;
+ stcp->tgsi.req_input_mem = 0;
+
+ free_glsl_to_tgsi_visitor(stcp->glsl_to_tgsi);
+ stcp->glsl_to_tgsi = NULL;
+ return true;
+}
+
+
+/**
+ * Get/create compute program variant.
+ */
+struct st_basic_variant *
+st_get_cp_variant(struct st_context *st,
+ struct pipe_compute_state *tgsi,
+ struct st_basic_variant **variants)
+{
+ struct pipe_context *pipe = st->pipe;
+ struct st_basic_variant *v;
+ struct st_basic_variant_key key;
+
+ memset(&key, 0, sizeof(key));
+ key.st = st->has_shareable_shaders ? NULL : st;
+
+ /* Search for existing variant */
+ for (v = *variants; v; v = v->next) {
+ if (memcmp(&v->key, &key, sizeof(key)) == 0) {
+ break;
+ }
+ }
+
+ if (!v) {
+ /* create new */
+ v = CALLOC_STRUCT(st_basic_variant);
+ if (v) {
+ /* fill in new variant */
+ v->driver_shader = pipe->create_compute_state(pipe, tgsi);
+ v->key = key;
+
+ /* insert into list */
+ v->next = *variants;
+ *variants = v;
+ }
+ }
+
+ return v;
+}
+
+
+/**
+ * Vert/Geom/Frag programs have per-context variants. Free all the
+ * variants attached to the given program which match the given context.
+ */
+static void
+destroy_program_variants(struct st_context *st, struct gl_program *target)
+{
+ if (!target || target == &_mesa_DummyProgram)
+ return;
+
+ switch (target->Target) {
+ case GL_VERTEX_PROGRAM_ARB:
+ {
+ struct st_vertex_program *stvp = (struct st_vertex_program *) target;
+ struct st_vp_variant *vpv, **prevPtr = &stvp->variants;
+
+ for (vpv = stvp->variants; vpv; ) {
+ struct st_vp_variant *next = vpv->next;
+ if (vpv->key.st == st) {
+ /* unlink from list */
+ *prevPtr = next;
+ /* destroy this variant */
+ delete_vp_variant(st, vpv);
+ }
+ else {
+ prevPtr = &vpv->next;
+ }
+ vpv = next;
+ }
+ }
+ break;
+ case GL_FRAGMENT_PROGRAM_ARB:
+ {
+ struct st_fragment_program *stfp =
+ (struct st_fragment_program *) target;
+ struct st_fp_variant *fpv, **prevPtr = &stfp->variants;
+
+ for (fpv = stfp->variants; fpv; ) {
+ struct st_fp_variant *next = fpv->next;
+ if (fpv->key.st == st) {
+ /* unlink from list */
+ *prevPtr = next;
+ /* destroy this variant */
+ delete_fp_variant(st, fpv);
+ }
+ else {
+ prevPtr = &fpv->next;
+ }
+ fpv = next;
+ }
+ }
+ break;
+ case GL_GEOMETRY_PROGRAM_NV:
+ case GL_TESS_CONTROL_PROGRAM_NV:
+ case GL_TESS_EVALUATION_PROGRAM_NV:
+ case GL_COMPUTE_PROGRAM_NV:
+ {
+ struct st_geometry_program *gp = (struct st_geometry_program*)target;
+ struct st_tessctrl_program *tcp = (struct st_tessctrl_program*)target;
+ struct st_tesseval_program *tep = (struct st_tesseval_program*)target;
+ struct st_compute_program *cp = (struct st_compute_program*)target;
+ struct st_basic_variant **variants =
+ target->Target == GL_GEOMETRY_PROGRAM_NV ? &gp->variants :
+ target->Target == GL_TESS_CONTROL_PROGRAM_NV ? &tcp->variants :
+ target->Target == GL_TESS_EVALUATION_PROGRAM_NV ? &tep->variants :
+ target->Target == GL_COMPUTE_PROGRAM_NV ? &cp->variants :
+ NULL;
+ struct st_basic_variant *v, **prevPtr = variants;
+
+ for (v = *variants; v; ) {
+ struct st_basic_variant *next = v->next;
+ if (v->key.st == st) {
+ /* unlink from list */
+ *prevPtr = next;
+ /* destroy this variant */
+ delete_basic_variant(st, v, target->Target);
+ }
+ else {
+ prevPtr = &v->next;
+ }
+ v = next;
+ }
+ }
+ break;
+ default:
+ _mesa_problem(NULL, "Unexpected program target 0x%x in "
+ "destroy_program_variants_cb()", target->Target);
+ }
+}
+
+
+/**
+ * Callback for _mesa_HashWalk. Free all the shader's program variants
+ * which match the given context.
+ */
+static void
+destroy_shader_program_variants_cb(GLuint key, void *data, void *userData)
+{
+ struct st_context *st = (struct st_context *) userData;
+ struct gl_shader *shader = (struct gl_shader *) data;
+
+ switch (shader->Type) {
+ case GL_SHADER_PROGRAM_MESA:
+ {
+ struct gl_shader_program *shProg = (struct gl_shader_program *) data;
+ GLuint i;
+
+ for (i = 0; i < shProg->NumShaders; i++) {
+ destroy_program_variants(st, shProg->Shaders[i]->Program);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(shProg->_LinkedShaders); i++) {
+ if (shProg->_LinkedShaders[i])
+ destroy_program_variants(st, shProg->_LinkedShaders[i]->Program);
+ }
+ }
+ break;
+ case GL_VERTEX_SHADER:
+ case GL_FRAGMENT_SHADER:
+ case GL_GEOMETRY_SHADER:
+ case GL_TESS_CONTROL_SHADER:
+ case GL_TESS_EVALUATION_SHADER:
+ case GL_COMPUTE_SHADER:
+ {
+ destroy_program_variants(st, shader->Program);
+ }
+ break;
+ default:
+ assert(0);
+ }
+}
+
+
+/**
+ * Callback for _mesa_HashWalk. Free all the program variants which match
+ * the given context.
+ */
+static void
+destroy_program_variants_cb(GLuint key, void *data, void *userData)
+{
+ struct st_context *st = (struct st_context *) userData;
+ struct gl_program *program = (struct gl_program *) data;
+ destroy_program_variants(st, program);
+}
+
+
+/**
+ * Walk over all shaders and programs to delete any variants which
+ * belong to the given context.
+ * This is called during context tear-down.
+ */
+void
+st_destroy_program_variants(struct st_context *st)
+{
+ /* If shaders can be shared with other contexts, the last context will
+ * call DeleteProgram on all shaders, releasing everything.
+ */
+ if (st->has_shareable_shaders)
+ return;
+
+ /* ARB vert/frag program */
+ _mesa_HashWalk(st->ctx->Shared->Programs,
+ destroy_program_variants_cb, st);
+
+ /* GLSL vert/frag/geom shaders */
+ _mesa_HashWalk(st->ctx->Shared->ShaderObjects,
+ destroy_shader_program_variants_cb, st);
+}
+
+
+/**
+ * For debugging, print/dump the current vertex program.