423 lines
12 KiB
C
423 lines
12 KiB
C
![]() |
/**************************************************************************
|
||
|
*
|
||
|
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
|
||
|
* 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, sub license, 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 (including the
|
||
|
* next paragraph) 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 NON-INFRINGEMENT.
|
||
|
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
|
||
|
*
|
||
|
**************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* State validation for vertex/fragment shaders.
|
||
|
* Note that we have to delay most vertex/fragment shader translation
|
||
|
* until rendering time since the linkage between the vertex outputs and
|
||
|
* fragment inputs can vary depending on the pairing of shaders.
|
||
|
*
|
||
|
* Authors:
|
||
|
* Brian Paul
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
#include "main/imports.h"
|
||
|
#include "main/mtypes.h"
|
||
|
|
||
|
#include "pipe/p_context.h"
|
||
|
#include "pipe/tgsi/mesa/mesa_to_tgsi.h"
|
||
|
#include "pipe/tgsi/exec/tgsi_core.h"
|
||
|
|
||
|
#include "st_context.h"
|
||
|
#include "st_cache.h"
|
||
|
#include "st_atom.h"
|
||
|
#include "st_program.h"
|
||
|
#include "st_atom_shader.h"
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Structure to describe a (vertex program, fragment program) pair
|
||
|
* which is linked together (used together to render something). This
|
||
|
* linkage basically servers the same purpose as the OpenGL Shading
|
||
|
* Language linker, but also applies to ARB programs and Mesa's
|
||
|
* fixed-function-generated programs.
|
||
|
*
|
||
|
* More background:
|
||
|
*
|
||
|
* The translation from Mesa programs to TGSI programs depends on the
|
||
|
* linkage between the vertex program and the fragment program. This is
|
||
|
* because we tightly pack the inputs and outputs of shaders into
|
||
|
* consecutive "slots".
|
||
|
*
|
||
|
* Suppose an app uses one vertex program "VP" (outputting pos, color and tex0)
|
||
|
* and two fragment programs:
|
||
|
* FP1: uses tex0 input only (input slot 0)
|
||
|
* FP2: uses color input only (input slot 0)
|
||
|
*
|
||
|
* When VP is used with FP1 we want VP.output[2] to match FP1.input[0], but
|
||
|
* when VP is used with FP2 we want VP.output[1] to match FP1.input[0].
|
||
|
*
|
||
|
* We don't want to re-translate the vertex and/or fragment programs
|
||
|
* each time the VP/FP bindings/linkings change. The solution is this
|
||
|
* structure which stores the translated TGSI shaders on a per-linkage
|
||
|
* basis.
|
||
|
*
|
||
|
*/
|
||
|
struct linked_program_pair
|
||
|
{
|
||
|
struct st_vertex_program *vprog; /**< never changes */
|
||
|
struct st_fragment_program *fprog; /**< never changes */
|
||
|
|
||
|
struct tgsi_token vs_tokens[ST_FP_MAX_TOKENS];
|
||
|
struct tgsi_token fs_tokens[ST_FP_MAX_TOKENS];
|
||
|
|
||
|
const struct cso_vertex_shader *vs;
|
||
|
const struct cso_fragment_shader *fs;
|
||
|
|
||
|
GLuint vertSerialNo, fragSerialNo;
|
||
|
|
||
|
/** maps a Mesa VERT_ATTRIB_x to a packed TGSI input index */
|
||
|
GLuint vp_input_to_index[MAX_VERTEX_PROGRAM_ATTRIBS];
|
||
|
/** maps a TGSI input index back to a Mesa VERT_ATTRIB_x */
|
||
|
GLuint vp_index_to_input[MAX_VERTEX_PROGRAM_ATTRIBS];
|
||
|
|
||
|
GLuint vp_result_to_slot[VERT_RESULT_MAX];
|
||
|
|
||
|
struct linked_program_pair *next;
|
||
|
};
|
||
|
|
||
|
|
||
|
/** XXX temporary - use some kind of hash table instead */
|
||
|
static struct linked_program_pair *Pairs = NULL;
|
||
|
|
||
|
|
||
|
static void
|
||
|
find_and_remove(struct gl_program *prog)
|
||
|
{
|
||
|
struct linked_program_pair *pair, *prev = NULL, *next;
|
||
|
for (pair = Pairs; pair; pair = next) {
|
||
|
next = pair->next;
|
||
|
if (pair->vprog == (struct st_vertex_program *) prog ||
|
||
|
pair->fprog == (struct st_fragment_program *) prog) {
|
||
|
/* unlink */
|
||
|
if (prev)
|
||
|
prev->next = next;
|
||
|
else
|
||
|
Pairs = next;
|
||
|
/* delete pair->vs */
|
||
|
/* delete pair->fs */
|
||
|
free(pair);
|
||
|
}
|
||
|
else {
|
||
|
prev = pair;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Delete any known program pairs that use the given vertex program.
|
||
|
*/
|
||
|
void
|
||
|
st_remove_vertex_program(struct st_context *st, struct st_vertex_program *stvp)
|
||
|
{
|
||
|
find_and_remove(&stvp->Base.Base);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Delete any known program pairs that use the given fragment program.
|
||
|
*/
|
||
|
void
|
||
|
st_remove_fragment_program(struct st_context *st,
|
||
|
struct st_fragment_program *stfp)
|
||
|
{
|
||
|
find_and_remove(&stfp->Base.Base);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Given a vertex program output attribute, return the corresponding
|
||
|
* fragment program input attribute.
|
||
|
* \return -1 for vertex outputs that have no corresponding fragment input
|
||
|
*/
|
||
|
static GLint
|
||
|
vp_out_to_fp_in(GLuint vertResult)
|
||
|
{
|
||
|
if (vertResult >= VERT_RESULT_TEX0 &&
|
||
|
vertResult < VERT_RESULT_TEX0 + MAX_TEXTURE_COORD_UNITS)
|
||
|
return FRAG_ATTRIB_TEX0 + (vertResult - VERT_RESULT_TEX0);
|
||
|
|
||
|
if (vertResult >= VERT_RESULT_VAR0 &&
|
||
|
vertResult < VERT_RESULT_VAR0 + MAX_VARYING)
|
||
|
return FRAG_ATTRIB_VAR0 + (vertResult - VERT_RESULT_VAR0);
|
||
|
|
||
|
switch (vertResult) {
|
||
|
case VERT_RESULT_HPOS:
|
||
|
return FRAG_ATTRIB_WPOS;
|
||
|
case VERT_RESULT_COL0:
|
||
|
return FRAG_ATTRIB_COL0;
|
||
|
case VERT_RESULT_COL1:
|
||
|
return FRAG_ATTRIB_COL1;
|
||
|
case VERT_RESULT_FOGC:
|
||
|
return FRAG_ATTRIB_FOGC;
|
||
|
default:
|
||
|
/* Back-face colors, edge flags, etc */
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Examine the outputs written by a vertex program and the inputs read
|
||
|
* by a fragment program to determine which match up and where they
|
||
|
* should be mapped into the generic shader output/input slots.
|
||
|
* \param vert_output_map returns the vertex output register mapping
|
||
|
* \param frag_input_map returns the fragment input register mapping
|
||
|
*/
|
||
|
static GLuint
|
||
|
link_outputs_to_inputs(GLbitfield outputsWritten,
|
||
|
GLbitfield inputsRead,
|
||
|
GLuint vert_output_map[],
|
||
|
GLuint frag_input_map[])
|
||
|
{
|
||
|
static const GLuint UNUSED = ~0;
|
||
|
GLint vert_slot_to_attr[50], frag_slot_to_attr[50];
|
||
|
GLuint outAttr, inAttr;
|
||
|
GLuint numIn = 0, dummySlot;
|
||
|
|
||
|
for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) {
|
||
|
if (inputsRead & (1 << inAttr)) {
|
||
|
frag_input_map[inAttr] = numIn;
|
||
|
frag_slot_to_attr[numIn] = inAttr;
|
||
|
numIn++;
|
||
|
}
|
||
|
else {
|
||
|
frag_input_map[inAttr] = UNUSED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) {
|
||
|
if (outputsWritten & (1 << outAttr)) {
|
||
|
/* see if the frag prog wants this vert output */
|
||
|
GLint fpIn = vp_out_to_fp_in(outAttr);
|
||
|
|
||
|
if (fpIn >= 0) {
|
||
|
GLuint frag_slot = frag_input_map[fpIn];
|
||
|
vert_output_map[outAttr] = frag_slot;
|
||
|
vert_slot_to_attr[frag_slot] = outAttr;
|
||
|
}
|
||
|
else {
|
||
|
vert_output_map[outAttr] = UNUSED;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
vert_output_map[outAttr] = UNUSED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* We'll map all unused vertex program outputs to this slot.
|
||
|
* We'll also map all undefined fragment program inputs to this slot.
|
||
|
*/
|
||
|
dummySlot = numIn;
|
||
|
|
||
|
/* Map vert program outputs that aren't used to the dummy slot */
|
||
|
for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) {
|
||
|
if (outputsWritten & (1 << outAttr)) {
|
||
|
if (vert_output_map[outAttr] == UNUSED)
|
||
|
vert_output_map[outAttr] = dummySlot;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Map frag program inputs that aren't defined to the dummy slot */
|
||
|
for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) {
|
||
|
if (inputsRead & (1 << inAttr)) {
|
||
|
if (frag_input_map[inAttr] == UNUSED)
|
||
|
frag_input_map[inAttr] = dummySlot;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
printf("vOut W slot\n");
|
||
|
for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) {
|
||
|
printf("%4d %c %4d\n", outAttr,
|
||
|
" *"[(outputsWritten >> outAttr) & 1],
|
||
|
vert_output_map[outAttr]);
|
||
|
}
|
||
|
printf("vIn R slot\n");
|
||
|
for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) {
|
||
|
printf("%3d %c %4d\n", inAttr,
|
||
|
" *"[(inputsRead >> inAttr) & 1],
|
||
|
frag_input_map[inAttr]);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return numIn;
|
||
|
}
|
||
|
|
||
|
|
||
|
static struct linked_program_pair *
|
||
|
lookup_program_pair(struct st_context *st,
|
||
|
struct st_vertex_program *vprog,
|
||
|
struct st_fragment_program *fprog)
|
||
|
{
|
||
|
struct linked_program_pair *pair;
|
||
|
|
||
|
/* search */
|
||
|
for (pair = Pairs; pair; pair = pair->next) {
|
||
|
if (pair->vprog == vprog && pair->fprog == fprog) {
|
||
|
/* found it */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Examine the outputs of the vertex shader and the inputs of the
|
||
|
* fragment shader to determine how to match both to a common set
|
||
|
* of slots.
|
||
|
*/
|
||
|
if (!pair) {
|
||
|
pair = CALLOC_STRUCT(linked_program_pair);
|
||
|
if (pair) {
|
||
|
pair->vprog = vprog;
|
||
|
pair->fprog = fprog;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pair;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
link_shaders(struct st_context *st, struct linked_program_pair *pair)
|
||
|
{
|
||
|
struct st_vertex_program *vprog = pair->vprog;
|
||
|
struct st_fragment_program *fprog = pair->fprog;
|
||
|
|
||
|
assert(vprog);
|
||
|
assert(fprog);
|
||
|
|
||
|
if (pair->vertSerialNo != vprog->serialNo ||
|
||
|
pair->fragSerialNo != fprog->serialNo) {
|
||
|
/* re-link and re-translate */
|
||
|
GLuint vert_output_mapping[VERT_RESULT_MAX];
|
||
|
GLuint frag_input_mapping[FRAG_ATTRIB_MAX];
|
||
|
|
||
|
link_outputs_to_inputs(vprog->Base.Base.OutputsWritten,
|
||
|
fprog->Base.Base.InputsRead | FRAG_BIT_WPOS,
|
||
|
vert_output_mapping,
|
||
|
frag_input_mapping);
|
||
|
|
||
|
/* xlate vp to vs + vs tokens */
|
||
|
st_translate_vertex_program(st, vprog,
|
||
|
vert_output_mapping,
|
||
|
pair->vs_tokens, ST_FP_MAX_TOKENS);
|
||
|
|
||
|
pair->vprog = vprog;
|
||
|
/* temp hacks */
|
||
|
pair->vs = vprog->vs;
|
||
|
vprog->vs = NULL;
|
||
|
|
||
|
|
||
|
/* xlate fp to fs + fs tokens */
|
||
|
st_translate_fragment_program(st, fprog,
|
||
|
frag_input_mapping,
|
||
|
pair->fs_tokens, ST_FP_MAX_TOKENS);
|
||
|
pair->fprog = fprog;
|
||
|
/* temp hacks */
|
||
|
pair->fs = fprog->fs;
|
||
|
fprog->fs = NULL;
|
||
|
|
||
|
/* save pair */
|
||
|
pair->next = Pairs;
|
||
|
Pairs = pair;
|
||
|
|
||
|
pair->vertSerialNo = vprog->serialNo;
|
||
|
pair->fragSerialNo = fprog->serialNo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
update_linkage( struct st_context *st )
|
||
|
{
|
||
|
struct linked_program_pair *pair;
|
||
|
struct st_vertex_program *stvp;
|
||
|
struct st_fragment_program *stfp;
|
||
|
|
||
|
/* find active shader and params -- Should be covered by
|
||
|
* ST_NEW_VERTEX_PROGRAM
|
||
|
*/
|
||
|
if (st->ctx->Shader.CurrentProgram &&
|
||
|
st->ctx->Shader.CurrentProgram->LinkStatus &&
|
||
|
st->ctx->Shader.CurrentProgram->VertexProgram) {
|
||
|
struct gl_vertex_program *f
|
||
|
= st->ctx->Shader.CurrentProgram->VertexProgram;
|
||
|
stvp = st_vertex_program(f);
|
||
|
}
|
||
|
else {
|
||
|
assert(st->ctx->VertexProgram._Current);
|
||
|
stvp = st_vertex_program(st->ctx->VertexProgram._Current);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (st->ctx->Shader.CurrentProgram &&
|
||
|
st->ctx->Shader.CurrentProgram->LinkStatus &&
|
||
|
st->ctx->Shader.CurrentProgram->FragmentProgram) {
|
||
|
struct gl_fragment_program *f
|
||
|
= st->ctx->Shader.CurrentProgram->FragmentProgram;
|
||
|
stfp = st_fragment_program(f);
|
||
|
}
|
||
|
else {
|
||
|
assert(st->ctx->FragmentProgram._Current);
|
||
|
stfp = st_fragment_program(st->ctx->FragmentProgram._Current);
|
||
|
}
|
||
|
|
||
|
|
||
|
pair = lookup_program_pair(st, stvp, stfp);
|
||
|
assert(pair);
|
||
|
link_shaders(st, pair);
|
||
|
|
||
|
|
||
|
/* Bind the vertex program and TGSI shader */
|
||
|
st->vp = stvp;
|
||
|
st->state.vs = pair->vs;
|
||
|
st->pipe->bind_vs_state(st->pipe, st->state.vs->data);
|
||
|
|
||
|
/* Bind the fragment program and TGSI shader */
|
||
|
st->fp = stfp;
|
||
|
st->state.fs = pair->fs;
|
||
|
st->pipe->bind_fs_state(st->pipe, st->state.fs->data);
|
||
|
|
||
|
st->vertex_result_to_slot = pair->vp_result_to_slot;
|
||
|
}
|
||
|
|
||
|
|
||
|
const struct st_tracked_state st_update_shader = {
|
||
|
.name = "st_update_shader",
|
||
|
.dirty = {
|
||
|
.mesa = 0,
|
||
|
.st = ST_NEW_LINKAGE
|
||
|
},
|
||
|
.update = update_linkage
|
||
|
};
|