/* * Copyright (C) 2006 Ben Skeggs. * * 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 (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 NONINFRINGEMENT. * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. * */ /* * Authors: * Ben Skeggs */ #include "glheader.h" #include "macros.h" #include "enums.h" #include "extensions.h" #include "shader/program.h" #include "shader/prog_instruction.h" /*#include "shader/arbprogparse.h"*/ #include "tnl/tnl.h" #include "nouveau_context.h" #include "nouveau_shader.h" /***************************************************************************** * Mesa entry points */ static void nouveauBindProgram(GLcontext *ctx, GLenum target, struct gl_program *prog) { NVSDBG("target=%s, prog=%p\n", _mesa_lookup_enum_by_nr(target), prog); } static struct gl_program * nouveauNewProgram(GLcontext *ctx, GLenum target, GLuint id) { nouveauShader *nvs; NVSDBG("target=%s, id=%d\n", _mesa_lookup_enum_by_nr(target), id); nvs = CALLOC_STRUCT(_nouveauShader); NVSDBG("prog=%p\n", nvs); switch (target) { case GL_VERTEX_PROGRAM_ARB: return _mesa_init_vertex_program(ctx, &nvs->mesa.vp, target, id); case GL_FRAGMENT_PROGRAM_ARB: return _mesa_init_fragment_program(ctx, &nvs->mesa.fp, target, id); default: _mesa_problem(ctx, "Unsupported shader target"); break; } FREE(nvs); return NULL; } static void nouveauDeleteProgram(GLcontext *ctx, struct gl_program *prog) { nouveauShader *nvs = (nouveauShader *)prog; NVSDBG("prog=%p\n", prog); if (nvs->translated) FREE(nvs->program); _mesa_delete_program(ctx, prog); } static void nouveauProgramStringNotify(GLcontext *ctx, GLenum target, struct gl_program *prog) { nouveauShader *nvs = (nouveauShader *)prog; NVSDBG("target=%s, prog=%p\n", _mesa_lookup_enum_by_nr(target), prog); if (nvs->translated) FREE(nvs->program); nvs->error = GL_FALSE; nvs->translated = GL_FALSE; _tnl_program_string(ctx, target, prog); } static GLboolean nouveauIsProgramNative(GLcontext * ctx, GLenum target, struct gl_program *prog) { nouveauShader *nvs = (nouveauShader *)prog; NVSDBG("target=%s, prog=%p\n", _mesa_lookup_enum_by_nr(target), prog); return nvs->translated; } GLboolean nvsUpdateShader(GLcontext *ctx, nouveauShader *nvs) { nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx); struct gl_program_parameter_list *plist; int i; NVSDBG("prog=%p\n", nvs); /* Translate to HW format now if necessary */ if (!nvs->translated) { /* Mesa ASM shader -> nouveauShader */ if (!nouveau_shader_pass0(ctx, nvs)) return GL_FALSE; /* Basic dead code elimination + register usage info */ if (!nouveau_shader_pass1(nvs)) return GL_FALSE; /* nouveauShader -> HW bytecode, HW register alloc */ if (!nouveau_shader_pass2(nvs)) return GL_FALSE; assert(nvs->translated); assert(nvs->program); } /* Update state parameters */ plist = nvs->mesa.vp.Base.Parameters; _mesa_load_state_parameters(ctx, plist); for (i=0; iparam_high; i++) { if (!nvs->params[i].in_use) continue; if (!nvs->on_hardware) { /* if we've been kicked off the hardware there's no guarantee our * consts are still there.. reupload them all */ nvs->func->UpdateConst(ctx, nvs, i); } else if (nvs->params[i].source_val) { /* update any changed state parameters */ if (!TEST_EQ_4V(nvs->params[i].val, nvs->params[i].source_val)) nvs->func->UpdateConst(ctx, nvs, i); } } /* Upload program to hardware, this must come after state param update * as >=NV30 fragprogs inline consts into the bytecode. */ if (!nvs->on_hardware) { nouveauShader **current; if (nvs->mesa.vp.Base.Target == GL_VERTEX_PROGRAM_ARB) current = &nmesa->current_vertprog; else current = &nmesa->current_fragprog; if (*current) (*current)->on_hardware = 0; nvs->func->UploadToHW(ctx, nvs); nvs->on_hardware = 1; *current = nvs; } return GL_TRUE; } nouveauShader * nvsBuildTextShader(GLcontext *ctx, GLenum target, const char *text) { nouveauShader *nvs; nvs = CALLOC_STRUCT(_nouveauShader); if (!nvs) return NULL; if (target == GL_VERTEX_PROGRAM_ARB) { _mesa_init_vertex_program(ctx, &nvs->mesa.vp, GL_VERTEX_PROGRAM_ARB, 0); _mesa_parse_arb_vertex_program(ctx, GL_VERTEX_PROGRAM_ARB, text, strlen(text), &nvs->mesa.vp); } else if (target == GL_FRAGMENT_PROGRAM_ARB) { _mesa_init_fragment_program(ctx, &nvs->mesa.fp, GL_FRAGMENT_PROGRAM_ARB, 0); _mesa_parse_arb_fragment_program(ctx, GL_FRAGMENT_PROGRAM_ARB, text, strlen(text), &nvs->mesa.fp); } nouveau_shader_pass0(ctx, nvs); nouveau_shader_pass1(nvs); nouveau_shader_pass2(nvs); return nvs; } static void nvsBuildPassthroughVP(GLcontext *ctx) { nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx); const char *vp_text = "!!ARBvp1.0\n" "OPTION ARB_position_invariant;" "" "MOV result.color, vertex.color;\n" "MOV result.texcoord[0], vertex.texcoord[0];\n" "MOV result.texcoord[1], vertex.texcoord[1];\n" "MOV result.texcoord[2], vertex.texcoord[2];\n" "MOV result.texcoord[3], vertex.texcoord[3];\n" "MOV result.texcoord[4], vertex.texcoord[4];\n" "MOV result.texcoord[5], vertex.texcoord[5];\n" "MOV result.texcoord[6], vertex.texcoord[6];\n" "MOV result.texcoord[7], vertex.texcoord[7];\n" "END"; nmesa->passthrough_vp = nvsBuildTextShader(ctx, GL_VERTEX_PROGRAM_ARB, vp_text); } static void nvsBuildPassthroughFP(GLcontext *ctx) { nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx); const char *fp_text = "!!ARBfp1.0\n" "MOV result.color, fragment.color;\n" "END"; nmesa->passthrough_fp = nvsBuildTextShader(ctx, GL_FRAGMENT_PROGRAM_ARB, fp_text); } void nouveauShaderInitFuncs(GLcontext * ctx) { nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx); switch (nmesa->screen->card->type) { case NV_20: NV20VPInitShaderFuncs(&nmesa->VPfunc); break; case NV_30: NV30VPInitShaderFuncs(&nmesa->VPfunc); NV30FPInitShaderFuncs(&nmesa->FPfunc); break; case NV_40: case NV_44: NV40VPInitShaderFuncs(&nmesa->VPfunc); NV40FPInitShaderFuncs(&nmesa->FPfunc); break; case NV_50: default: return; } /* Build a vertex program that simply passes through all attribs. * Needed to do swtcl on nv40 */ if (nmesa->screen->card->type >= NV_40) nvsBuildPassthroughVP(ctx); /* Needed on NV30, even when using swtcl, if you want to get colours */ if (nmesa->screen->card->type >= NV_30) nvsBuildPassthroughFP(ctx); ctx->Const.VertexProgram.MaxNativeInstructions = nmesa->VPfunc.MaxInst; ctx->Const.VertexProgram.MaxNativeAluInstructions = nmesa->VPfunc.MaxInst; ctx->Const.VertexProgram.MaxNativeTexInstructions = nmesa->VPfunc.MaxInst; ctx->Const.VertexProgram.MaxNativeTexIndirections = ctx->Const.VertexProgram.MaxNativeTexInstructions; ctx->Const.VertexProgram.MaxNativeAttribs = nmesa->VPfunc.MaxAttrib; ctx->Const.VertexProgram.MaxNativeTemps = nmesa->VPfunc.MaxTemp; ctx->Const.VertexProgram.MaxNativeAddressRegs = nmesa->VPfunc.MaxAddress; ctx->Const.VertexProgram.MaxNativeParameters = nmesa->VPfunc.MaxConst; if (nmesa->screen->card->type >= NV_30) { ctx->Const.FragmentProgram.MaxNativeInstructions = nmesa->FPfunc.MaxInst; ctx->Const.FragmentProgram.MaxNativeAluInstructions = nmesa->FPfunc.MaxInst; ctx->Const.FragmentProgram.MaxNativeTexInstructions = nmesa->FPfunc.MaxInst; ctx->Const.FragmentProgram.MaxNativeTexIndirections = ctx->Const.FragmentProgram.MaxNativeTexInstructions; ctx->Const.FragmentProgram.MaxNativeAttribs = nmesa->FPfunc.MaxAttrib; ctx->Const.FragmentProgram.MaxNativeTemps = nmesa->FPfunc.MaxTemp; ctx->Const.FragmentProgram.MaxNativeAddressRegs = nmesa->FPfunc.MaxAddress; ctx->Const.FragmentProgram.MaxNativeParameters = nmesa->FPfunc.MaxConst; } ctx->Driver.NewProgram = nouveauNewProgram; ctx->Driver.BindProgram = nouveauBindProgram; ctx->Driver.DeleteProgram = nouveauDeleteProgram; ctx->Driver.ProgramStringNotify = nouveauProgramStringNotify; ctx->Driver.IsProgramNative = nouveauIsProgramNative; } /***************************************************************************** * Disassembly support structs */ #define CHECK_RANGE(idx, arr) ((idx)= (sizeof(ops) / sizeof(struct _opcode_info))) return NULL; if (ops[op].name == NULL) return NULL; return &ops[op]; } static const char *_SFR_STRING[] = { [NVS_FR_POSITION] = "position", [NVS_FR_WEIGHT] = "weight", [NVS_FR_NORMAL] = "normal", [NVS_FR_COL0] = "color", [NVS_FR_COL1] = "color.secondary", [NVS_FR_BFC0] = "bfc", [NVS_FR_BFC1] = "bfc.secondary", [NVS_FR_FOGCOORD] = "fogcoord", [NVS_FR_POINTSZ] = "pointsize", [NVS_FR_TEXCOORD0] = "texcoord[0]", [NVS_FR_TEXCOORD1] = "texcoord[1]", [NVS_FR_TEXCOORD2] = "texcoord[2]", [NVS_FR_TEXCOORD3] = "texcoord[3]", [NVS_FR_TEXCOORD4] = "texcoord[4]", [NVS_FR_TEXCOORD5] = "texcoord[5]", [NVS_FR_TEXCOORD6] = "texcoord[6]", [NVS_FR_TEXCOORD7] = "texcoord[7]", [NVS_FR_FRAGDATA0] = "data[0]", [NVS_FR_FRAGDATA1] = "data[1]", [NVS_FR_FRAGDATA2] = "data[2]", [NVS_FR_FRAGDATA3] = "data[3]", [NVS_FR_CLIP0] = "clip_plane[0]", [NVS_FR_CLIP1] = "clip_plane[1]", [NVS_FR_CLIP2] = "clip_plane[2]", [NVS_FR_CLIP3] = "clip_plane[3]", [NVS_FR_CLIP4] = "clip_plane[4]", [NVS_FR_CLIP5] = "clip_plane[5]", [NVS_FR_CLIP6] = "clip_plane[6]", [NVS_FR_FACING] = "facing", }; #define SFR_STRING(idx) CHECK_RANGE((idx), SFR_STRING) static const char *_SWZ_STRING[] = { [NVS_SWZ_X] = "x", [NVS_SWZ_Y] = "y", [NVS_SWZ_Z] = "z", [NVS_SWZ_W] = "w" }; #define SWZ_STRING(idx) CHECK_RANGE((idx), SWZ_STRING) static const char *_NVS_PREC_STRING[] = { [NVS_PREC_FLOAT32] = "R", [NVS_PREC_FLOAT16] = "H", [NVS_PREC_FIXED12] = "X", [NVS_PREC_UNKNOWN] = "?" }; #define NVS_PREC_STRING(idx) CHECK_RANGE((idx), NVS_PREC_STRING) static const char *_NVS_COND_STRING[] = { [NVS_COND_FL] = "FL", [NVS_COND_LT] = "LT", [NVS_COND_EQ] = "EQ", [NVS_COND_LE] = "LE", [NVS_COND_GT] = "GT", [NVS_COND_NE] = "NE", [NVS_COND_GE] = "GE", [NVS_COND_TR] = "TR", [NVS_COND_UNKNOWN] = "??" }; #define NVS_COND_STRING(idx) CHECK_RANGE((idx), NVS_COND_STRING) /***************************************************************************** * ShaderFragment dumping */ static void nvsDumpIndent(int lvl) { while (lvl--) printf(" "); } static void nvsDumpSwizzle(nvsSwzComp *swz) { printf(".%s%s%s%s", SWZ_STRING(swz[0]), SWZ_STRING(swz[1]), SWZ_STRING(swz[2]), SWZ_STRING(swz[3]) ); } static void nvsDumpReg(nvsInstruction * inst, nvsRegister * reg) { if (reg->negate) printf("-"); if (reg->abs) printf("abs("); switch (reg->file) { case NVS_FILE_TEMP: printf("R%d", reg->index); nvsDumpSwizzle(reg->swizzle); break; case NVS_FILE_ATTRIB: printf("attrib.%s", SFR_STRING(reg->index)); nvsDumpSwizzle(reg->swizzle); break; case NVS_FILE_ADDRESS: printf("A%d", reg->index); break; case NVS_FILE_CONST: if (reg->indexed) printf("const[A%d.%s + %d]", reg->addr_reg, SWZ_STRING(reg->addr_comp), reg->index); else printf("const[%d]", reg->index); nvsDumpSwizzle(reg->swizzle); break; default: printf("UNKNOWN_FILE"); break; } if (reg->abs) printf(")"); } static void nvsDumpInstruction(nvsInstruction * inst, int slot, int lvl) { struct _opcode_info *opr = &ops[inst->op]; int i; nvsDumpIndent(lvl); printf("%s ", opr->name); if (!opr->flags & NODS) { switch (inst->dest.file) { case NVS_FILE_RESULT: printf("result.%s", SFR_STRING(inst->dest.index)); break; case NVS_FILE_TEMP: printf("R%d", inst->dest.index); break; case NVS_FILE_ADDRESS: printf("A%d", inst->dest.index); break; default: printf("UNKNOWN_DST_FILE"); break; } if (inst->mask != SMASK_ALL) { printf("."); if (inst->mask & SMASK_X) printf("x"); if (inst->mask & SMASK_Y) printf("y"); if (inst->mask & SMASK_Z) printf("z"); if (inst->mask & SMASK_W) printf("w"); } if (opr->numsrc) printf(", "); } for (i = 0; i < opr->numsrc; i++) { nvsDumpReg(inst, &inst->src[i]); if (i != opr->numsrc - 1) printf(", "); } if (opr->flags & TI_UNIT) printf(", texture[%d]", inst->tex_unit); printf("\n"); } void nvsDumpFragmentList(nvsFragmentHeader *f, int lvl) { while (f) { switch (f->type) { case NVS_INSTRUCTION: nvsDumpInstruction((nvsInstruction*)f, 0, lvl); break; default: fprintf(stderr, "%s: Only NVS_INSTRUCTION fragments can be in" "nvsFragmentList!\n", __func__); return; } f = f->next; } } /***************************************************************************** * HW shader disassembly */ static void nvsDisasmHWShaderOp(nvsFunc * shader, int merged) { struct _opcode_info *opi; nvsOpcode op; nvsRegFile file; nvsSwzComp swz[4]; int i; op = shader->GetOpcode(shader, merged); opi = _get_op_info(op); if (!opi) { printf("NO OPINFO!"); return; } printf("%s", opi->name); if (shader->GetPrecision && (!(opi->flags & BRANCH_ALL)) && (!(opi->flags * NODS)) && (op != NVS_OP_NOP)) printf("%s", NVS_PREC_STRING(shader->GetPrecision(shader))); if (shader->SupportsConditional && shader->SupportsConditional(shader)) { if (shader->GetConditionUpdate(shader)) { printf("C%d", shader->GetCondRegID(shader)); } } if (shader->GetSaturate && shader->GetSaturate(shader)) printf("_SAT"); if (!(opi->flags & NODS)) { int mask = shader->GetDestMask(shader, merged); switch (shader->GetDestFile(shader, merged)) { case NVS_FILE_ADDRESS: printf(" A%d", shader->GetDestID(shader, merged)); break; case NVS_FILE_TEMP: printf(" R%d", shader->GetDestID(shader, merged)); break; case NVS_FILE_RESULT: printf(" result.%s", (SFR_STRING(shader->GetDestID(shader, merged)))); break; default: printf(" BAD_RESULT_FILE"); break; } if (mask != SMASK_ALL) { printf("."); if (mask & SMASK_X) printf("x"); if (mask & SMASK_Y) printf("y"); if (mask & SMASK_Z) printf("z"); if (mask & SMASK_W) printf("w"); } } if (shader->SupportsConditional && shader->SupportsConditional(shader) && shader->GetConditionTest(shader)) { shader->GetCondRegSwizzle(shader, swz); printf(" (%s%d.%s%s%s%s)", NVS_COND_STRING(shader->GetCondition(shader)), shader->GetCondRegID(shader), SWZ_STRING(swz[NVS_SWZ_X]), SWZ_STRING(swz[NVS_SWZ_Y]), SWZ_STRING(swz[NVS_SWZ_Z]), SWZ_STRING(swz[NVS_SWZ_W]) ); } /* looping */ if (opi->flags & COUNT_ALL) { printf(" { "); if (opi->flags & COUNT_NUM) { printf("%d", shader->GetLoopCount(shader)); } if (opi->flags & COUNT_IND) { printf(", %d", shader->GetLoopInitial(shader)); } if (opi->flags & COUNT_INC) { printf(", %d", shader->GetLoopIncrement(shader)); } printf(" }"); } /* branching */ if (opi->flags & BRANCH_TR) printf(" %d", shader->GetBranch(shader)); if (opi->flags & BRANCH_EL) printf(" ELSE %d", shader->GetBranchElse(shader)); if (opi->flags & BRANCH_EN) printf(" END %d", shader->GetBranchEnd(shader)); if (!(opi->flags & NODS) && opi->numsrc) printf(","); printf(" "); for (i = 0; i < opi->numsrc; i++) { if (shader->GetSourceAbs(shader, merged, i)) printf("abs("); if (shader->GetSourceNegate(shader, merged, i)) printf("-"); file = shader->GetSourceFile(shader, merged, i); switch (file) { case NVS_FILE_TEMP: printf("R%d", shader->GetSourceID(shader, merged, i)); break; case NVS_FILE_CONST: if (shader->GetSourceIndexed(shader, merged, i)) { printf("c[A%d.%s + 0x%x]", shader->GetRelAddressRegID(shader), SWZ_STRING(shader->GetRelAddressSwizzle(shader)), shader->GetSourceID(shader, merged, i) ); } else { float val[4]; if (shader->GetSourceConstVal) { shader->GetSourceConstVal(shader, merged, i, val); printf("{ %.02f, %.02f, %.02f, %.02f }", val[0], val[1], val[2], val[3]); } else { printf("c[0x%x]", shader->GetSourceID(shader, merged, i)); } } break; case NVS_FILE_ATTRIB: if (shader->GetSourceIndexed(shader, merged, i)) { printf("attrib[A%d.%s + %d]", shader->GetRelAddressRegID(shader), SWZ_STRING(shader->GetRelAddressSwizzle(shader)), shader->GetSourceID(shader, merged, i) ); } else { printf("attrib.%s", SFR_STRING(shader->GetSourceID(shader, merged, i)) ); } break; case NVS_FILE_ADDRESS: printf("A%d", shader->GetRelAddressRegID(shader)); break; default: printf("UNKNOWN_SRC_FILE"); break; } shader->GetSourceSwizzle(shader, merged, i, swz); if (file != NVS_FILE_ADDRESS && (swz[NVS_SWZ_X] != NVS_SWZ_X || swz[NVS_SWZ_Y] != NVS_SWZ_Y || swz[NVS_SWZ_Z] != NVS_SWZ_Z || swz[NVS_SWZ_W] != NVS_SWZ_W)) { printf(".%s%s%s%s", SWZ_STRING(swz[NVS_SWZ_X]), SWZ_STRING(swz[NVS_SWZ_Y]), SWZ_STRING(swz[NVS_SWZ_Z]), SWZ_STRING(swz[NVS_SWZ_W])); } if (shader->GetSourceAbs(shader, merged, i)) printf(")"); if (shader->GetSourceScale) { int scale = shader->GetSourceScale(shader, merged, i); if (scale > 1) printf("{scaled %dx}", scale); } if (i < (opi->numsrc - 1)) printf(", "); } if (shader->IsLastInst(shader)) printf(" + END"); } void nvsDisasmHWShader(nvsPtr nvs) { nvsFunc *shader = nvs->func; unsigned int iaddr = 0; if (!nvs->program) { fprintf(stderr, "No HW program present"); return; } shader->inst = nvs->program; while (1) { if (shader->inst >= (nvs->program + nvs->program_size)) { fprintf(stderr, "Reached end of program, but HW inst has no END"); break; } printf("\t0x%08x:\n", shader->inst[0]); printf("\t0x%08x:\n", shader->inst[1]); printf("\t0x%08x:\n", shader->inst[2]); printf("\t0x%08x:", shader->inst[3]); printf("\n\t\tINST %d.0: ", iaddr); nvsDisasmHWShaderOp(shader, 0); if (shader->HasMergedInst(shader)) { printf("\n\t\tINST %d.1: ", iaddr); nvsDisasmHWShaderOp(shader, 1); } printf("\n"); if (shader->IsLastInst(shader)) break; shader->inst += shader->GetOffsetNext(shader); iaddr++; } printf("\n"); }