/** * Translate fragment program if needed. */ struct st_fp_variant * st_get_fp_variant(struct st_context *st, struct st_fragment_program *stfp, const struct st_fp_variant_key *key) { struct st_fp_variant *fpv; /* Search for existing variant */ for (fpv = stfp->variants; fpv; fpv = fpv->next) { if (memcmp(&fpv->key, key, sizeof(*key)) == 0) { break; } } if (!fpv) { /* create new */ fpv = st_translate_fragment_program(st, stfp, key); if (fpv) { /* insert into list */ fpv->next = stfp->variants; stfp->variants = fpv; } } return fpv; }
/** * Create fragment shader that does a TEX() instruction to get a Z * value, then writes to FRAG_RESULT_DEPTH. * Pass fragment color through as-is. */ static struct st_fragment_program * make_fragment_shader_z(struct st_context *st) { GLcontext *ctx = st->ctx; struct gl_program *p; GLuint ic = 0; if (st->drawpix.z_shader) { return st->drawpix.z_shader; } /* * Create shader now */ p = ctx->Driver.NewProgram(ctx, GL_FRAGMENT_PROGRAM_ARB, 0); if (!p) return NULL; p->NumInstructions = 3; p->Instructions = _mesa_alloc_instructions(p->NumInstructions); if (!p->Instructions) { ctx->Driver.DeleteProgram(ctx, p); return NULL; } _mesa_init_instructions(p->Instructions, p->NumInstructions); /* TEX result.depth, fragment.texcoord[0], texture[0], 2D; */ p->Instructions[ic].Opcode = OPCODE_TEX; p->Instructions[ic].DstReg.File = PROGRAM_OUTPUT; p->Instructions[ic].DstReg.Index = FRAG_RESULT_DEPTH; p->Instructions[ic].DstReg.WriteMask = WRITEMASK_Z; p->Instructions[ic].SrcReg[0].File = PROGRAM_INPUT; p->Instructions[ic].SrcReg[0].Index = FRAG_ATTRIB_TEX0; p->Instructions[ic].TexSrcUnit = 0; p->Instructions[ic].TexSrcTarget = TEXTURE_2D_INDEX; ic++; /* MOV result.color, fragment.color */ p->Instructions[ic].Opcode = OPCODE_MOV; p->Instructions[ic].DstReg.File = PROGRAM_OUTPUT; p->Instructions[ic].DstReg.Index = FRAG_RESULT_COLOR; p->Instructions[ic].SrcReg[0].File = PROGRAM_INPUT; p->Instructions[ic].SrcReg[0].Index = FRAG_ATTRIB_COL0; ic++; /* END; */ p->Instructions[ic++].Opcode = OPCODE_END; assert(ic == p->NumInstructions); p->InputsRead = FRAG_BIT_TEX0 | FRAG_BIT_COL0; p->OutputsWritten = (1 << FRAG_RESULT_COLOR) | (1 << FRAG_RESULT_DEPTH); p->SamplersUsed = 0x1; /* sampler 0 (bit 0) is used */ st->drawpix.z_shader = (struct st_fragment_program *) p; st_translate_fragment_program(st, st->drawpix.z_shader, NULL); return st->drawpix.z_shader; }
/** * Called via ctx->Driver.ProgramStringNotify() * Called when the program's text/code is changed. We have to free * all shader variants and corresponding gallium shaders when this happens. */ static GLboolean st_program_string_notify( struct gl_context *ctx, GLenum target, struct gl_program *prog ) { struct st_context *st = st_context(ctx); gl_shader_stage stage = _mesa_program_enum_to_shader_stage(target); if (target == GL_FRAGMENT_PROGRAM_ARB) { struct st_fragment_program *stfp = (struct st_fragment_program *) prog; st_release_fp_variants(st, stfp); if (!st_translate_fragment_program(st, stfp)) return false; if (st->fp == stfp) st->dirty.st |= ST_NEW_FRAGMENT_PROGRAM; } else if (target == GL_GEOMETRY_PROGRAM_NV) { struct st_geometry_program *stgp = (struct st_geometry_program *) prog; st_release_basic_variants(st, stgp->Base.Base.Target, &stgp->variants, &stgp->tgsi); if (!st_translate_geometry_program(st, stgp)) return false; if (st->gp == stgp) st->dirty.st |= ST_NEW_GEOMETRY_PROGRAM; } else if (target == GL_VERTEX_PROGRAM_ARB) { struct st_vertex_program *stvp = (struct st_vertex_program *) prog; st_release_vp_variants(st, stvp); if (!st_translate_vertex_program(st, stvp)) return false; if (st->vp == stvp) st->dirty.st |= ST_NEW_VERTEX_PROGRAM; } else if (target == GL_TESS_CONTROL_PROGRAM_NV) { struct st_tessctrl_program *sttcp = (struct st_tessctrl_program *) prog; st_release_basic_variants(st, sttcp->Base.Base.Target, &sttcp->variants, &sttcp->tgsi); if (!st_translate_tessctrl_program(st, sttcp)) return false; if (st->tcp == sttcp) st->dirty.st |= ST_NEW_TESSCTRL_PROGRAM; } else if (target == GL_TESS_EVALUATION_PROGRAM_NV) { struct st_tesseval_program *sttep = (struct st_tesseval_program *) prog; st_release_basic_variants(st, sttep->Base.Base.Target, &sttep->variants, &sttep->tgsi); if (!st_translate_tesseval_program(st, sttep)) return false; if (st->tep == sttep) st->dirty.st |= ST_NEW_TESSEVAL_PROGRAM; } else if (target == GL_COMPUTE_PROGRAM_NV) { struct st_compute_program *stcp = (struct st_compute_program *) prog; st_release_cp_variants(st, stcp); if (!st_translate_compute_program(st, stcp)) return false; if (st->cp == stcp) st->dirty_cp.st |= ST_NEW_COMPUTE_PROGRAM; } else if (target == GL_FRAGMENT_SHADER_ATI) { assert(prog); struct st_fragment_program *stfp = (struct st_fragment_program *) prog; assert(stfp->ati_fs); assert(stfp->ati_fs->Program == prog); st_init_atifs_prog(ctx, prog); st_release_fp_variants(st, stfp); if (!st_translate_fragment_program(st, stfp)) return false; if (st->fp == stfp) st->dirty.st |= ST_NEW_FRAGMENT_PROGRAM; } if (ST_DEBUG & DEBUG_PRECOMPILE || st->shader_has_one_variant[stage]) st_precompile_shader_variant(st, prog); return GL_TRUE; }
/** * Find a translated vertex program that corresponds to stvp and * has outputs matched to stfp's inputs. * This performs vertex and fragment translation (to TGSI) when needed. */ static struct translated_vertex_program * find_translated_vp(struct st_context *st, struct st_vertex_program *stvp, struct st_fragment_program *stfp) { static const GLuint UNUSED = ~0; struct translated_vertex_program *xvp; const GLbitfield fragInputsRead = stfp->Base.Base.InputsRead; /* * Translate fragment program if needed. */ if (!stfp->state.tokens) { GLuint inAttr, numIn = 0; for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) { if (fragInputsRead & (1 << inAttr)) { stfp->input_to_slot[inAttr] = numIn; numIn++; } else { stfp->input_to_slot[inAttr] = UNUSED; } } stfp->num_input_slots = numIn; assert(stfp->Base.Base.NumInstructions > 1); st_translate_fragment_program(st, stfp, stfp->input_to_slot); } /* See if we've got a translated vertex program whose outputs match * the fragment program's inputs. * XXX This could be a hash lookup, using InputsRead as the key. */ for (xvp = stfp->vertex_programs; xvp; xvp = xvp->next) { if (xvp->master == stvp && xvp->frag_inputs == fragInputsRead) { break; } } /* No? Allocate translated vp object now */ if (!xvp) { xvp = ST_CALLOC_STRUCT(translated_vertex_program); xvp->frag_inputs = fragInputsRead; xvp->master = stvp; xvp->next = stfp->vertex_programs; stfp->vertex_programs = xvp; } /* See if we need to translate vertex program to TGSI form */ if (xvp->serialNo != stvp->serialNo) { GLuint outAttr; const GLbitfield outputsWritten = stvp->Base.Base.OutputsWritten; GLuint numVpOuts = 0; GLboolean emitPntSize = GL_FALSE, emitBFC0 = GL_FALSE, emitBFC1 = GL_FALSE; GLbitfield usedGenerics = 0x0; GLbitfield usedOutputSlots = 0x0; /* Compute mapping of vertex program outputs to slots, which depends * on the fragment program's input->slot mapping. */ for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) { /* set defaults: */ xvp->output_to_slot[outAttr] = UNUSED; xvp->output_to_semantic_name[outAttr] = TGSI_SEMANTIC_COUNT; xvp->output_to_semantic_index[outAttr] = 99; if (outAttr == VERT_RESULT_HPOS) { /* always put xformed position into slot zero */ GLuint slot = 0; xvp->output_to_slot[VERT_RESULT_HPOS] = slot; xvp->output_to_semantic_name[outAttr] = TGSI_SEMANTIC_POSITION; xvp->output_to_semantic_index[outAttr] = 0; numVpOuts++; usedOutputSlots |= (1 << slot); } else if (outputsWritten & (1 << outAttr)) { /* see if the frag prog wants this vert output */ GLint fpInAttrib = vp_out_to_fp_in(outAttr); if (fpInAttrib >= 0) { GLuint fpInSlot = stfp->input_to_slot[fpInAttrib]; if (fpInSlot != ~0) { /* match this vp output to the fp input */ GLuint vpOutSlot = stfp->input_map[fpInSlot]; xvp->output_to_slot[outAttr] = vpOutSlot; xvp->output_to_semantic_name[outAttr] = stfp->input_semantic_name[fpInSlot]; xvp->output_to_semantic_index[outAttr] = stfp->input_semantic_index[fpInSlot]; numVpOuts++; usedOutputSlots |= (1 << vpOutSlot); } else { #if 0 /*debug*/ printf("VP output %d not used by FP\n", outAttr); #endif } } else if (outAttr == VERT_RESULT_PSIZ) emitPntSize = GL_TRUE; else if (outAttr == VERT_RESULT_BFC0) emitBFC0 = GL_TRUE; else if (outAttr == VERT_RESULT_BFC1) emitBFC1 = GL_TRUE; } #if 0 /*debug*/ printf("assign vp output_to_slot[%d] = %d\n", outAttr, xvp->output_to_slot[outAttr]); #endif } /* must do these last */ if (emitPntSize) { GLuint slot = numVpOuts++; xvp->output_to_slot[VERT_RESULT_PSIZ] = slot; xvp->output_to_semantic_name[VERT_RESULT_PSIZ] = TGSI_SEMANTIC_PSIZE; xvp->output_to_semantic_index[VERT_RESULT_PSIZ] = 0; usedOutputSlots |= (1 << slot); } if (emitBFC0) { GLuint slot = numVpOuts++; xvp->output_to_slot[VERT_RESULT_BFC0] = slot; xvp->output_to_semantic_name[VERT_RESULT_BFC0] = TGSI_SEMANTIC_COLOR; xvp->output_to_semantic_index[VERT_RESULT_BFC0] = 0; usedOutputSlots |= (1 << slot); } if (emitBFC1) { GLuint slot = numVpOuts++; xvp->output_to_slot[VERT_RESULT_BFC1] = slot; xvp->output_to_semantic_name[VERT_RESULT_BFC1] = TGSI_SEMANTIC_COLOR; xvp->output_to_semantic_index[VERT_RESULT_BFC1] = 1; usedOutputSlots |= (1 << slot); } /* build usedGenerics mask */ usedGenerics = 0x0; for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) { if (xvp->output_to_semantic_name[outAttr] == TGSI_SEMANTIC_GENERIC) { usedGenerics |= (1 << xvp->output_to_semantic_index[outAttr]); } } /* For each vertex program output that doesn't match up to a fragment * program input, map the vertex program output to a free slot and * free generic attribute. */ for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) { if (outputsWritten & (1 << outAttr)) { if (xvp->output_to_slot[outAttr] == UNUSED) { GLint freeGeneric = _mesa_ffs(~usedGenerics) - 1; GLint freeSlot = _mesa_ffs(~usedOutputSlots) - 1; usedGenerics |= (1 << freeGeneric); usedOutputSlots |= (1 << freeSlot); xvp->output_to_slot[outAttr] = freeSlot; xvp->output_to_semantic_name[outAttr] = TGSI_SEMANTIC_GENERIC; xvp->output_to_semantic_index[outAttr] = freeGeneric; } } #if 0 /*debug*/ printf("vp output_to_slot[%d] = %d\n", outAttr, xvp->output_to_slot[outAttr]); #endif } assert(stvp->Base.Base.NumInstructions > 1); st_translate_vertex_program(st, stvp, xvp->output_to_slot, xvp->output_to_semantic_name, xvp->output_to_semantic_index); xvp->vp = stvp; /* translated VP is up to date now */ xvp->serialNo = stvp->serialNo; } return xvp; }
/** * Make fragment shader for glDraw/CopyPixels. This shader is made * by combining the pixel transfer shader with the user-defined shader. */ static struct st_fragment_program * combined_drawpix_fragment_program(GLcontext *ctx) { struct st_context *st = ctx->st; struct st_fragment_program *stfp; if (st->pixel_xfer.program->serialNo == st->pixel_xfer.xfer_prog_sn && st->fp->serialNo == st->pixel_xfer.user_prog_sn) { /* the pixel tranfer program has not changed and the user-defined * program has not changed, so re-use the combined program. */ stfp = st->pixel_xfer.combined_prog; } else { /* Concatenate the pixel transfer program with the current user- * defined program. */ if (is_passthrough_program(&st->fp->Base)) { stfp = (struct st_fragment_program *) _mesa_clone_program(ctx, &st->pixel_xfer.program->Base.Base); } else { #if 0 printf("Base program:\n"); _mesa_print_program(&st->fp->Base.Base); printf("DrawPix program:\n"); _mesa_print_program(&st->pixel_xfer.program->Base.Base); #endif stfp = (struct st_fragment_program *) _mesa_combine_programs(ctx, &st->pixel_xfer.program->Base.Base, &st->fp->Base.Base); } #if 0 { struct gl_program *p = &stfp->Base.Base; printf("Combined DrawPixels program:\n"); _mesa_print_program(p); printf("InputsRead: 0x%x\n", p->InputsRead); printf("OutputsWritten: 0x%x\n", p->OutputsWritten); _mesa_print_parameter_list(p->Parameters); } #endif /* translate to TGSI tokens */ st_translate_fragment_program(st, stfp, NULL); /* save new program, update serial numbers */ st->pixel_xfer.xfer_prog_sn = st->pixel_xfer.program->serialNo; st->pixel_xfer.user_prog_sn = st->fp->serialNo; st->pixel_xfer.combined_prog_sn = stfp->serialNo; /* can't reference new program directly, already have a reference on it */ st_reference_fragprog(st, &st->pixel_xfer.combined_prog, NULL); st->pixel_xfer.combined_prog = stfp; } /* Ideally we'd have updated the pipe constants during the normal * st/atom mechanism. But we can't since this is specific to glDrawPixels. */ st_upload_constants(st, stfp->Base.Base.Parameters, PIPE_SHADER_FRAGMENT); return stfp; }