/********************************************************** * Copyright 2008-2009 VMware, Inc. 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 * SVGA Shader Dump Facilities * * @author Michal Krol <michal@vmware.com> */ #include <assert.h> #include <string.h> #include "svga_shader.h" #include "svga_shader_dump.h" #include "svga_shader_op.h" #include "util/u_debug.h" #include "../svga_hw_reg.h" #include "svga3d_shaderdefs.h" struct dump_info { uint32 version; boolean is_ps; int indent; }; #define DUMP_MAX_OP_SRC 4 struct dump_op { struct sh_op op; struct sh_dstreg dst; struct sh_srcreg dstind; struct sh_srcreg src[DUMP_MAX_OP_SRC]; struct sh_srcreg srcind[DUMP_MAX_OP_SRC]; struct sh_srcreg p0; }; static void dump_indent(int indent) { int i; for (i = 0; i < indent; ++i) { _debug_printf(" "); } } static void dump_op( struct sh_op op, const char *mnemonic ) { assert( op.is_reg == 0 ); if (op.predicated) { _debug_printf("(p0) "); } if (op.coissue) _debug_printf( "+" ); _debug_printf( "%s", mnemonic ); switch (op.opcode) { case SVGA3DOP_TEX: switch (op.control) { case 0: break; case 1 /* PROJECT */: _debug_printf("p"); break; case 2 /* BIAS */: _debug_printf("b"); break; default: assert(0); } break; case SVGA3DOP_IFC: case SVGA3DOP_BREAKC: case SVGA3DOP_SETP: switch (op.control) { case SVGA3DOPCOMP_GT: _debug_printf("_gt"); break; case SVGA3DOPCOMP_EQ: _debug_printf("_eq"); break; case SVGA3DOPCOMP_GE: _debug_printf("_ge"); break; case SVGA3DOPCOMP_LT: _debug_printf("_lt"); break; case SVGA3DOPCOMPC_NE: _debug_printf("_ne"); break; case SVGA3DOPCOMP_LE: _debug_printf("_le"); break; default: assert(0); } break; default: assert(op.control == 0); } } static void format_reg(const char *name, const struct sh_reg reg, const struct sh_srcreg *indreg) { if (reg.relative) { assert(indreg); if (sh_srcreg_type(*indreg) == SVGA3DREG_LOOP) { _debug_printf("%s[aL+%u]", name, reg.number); } else { _debug_printf("%s[a%u.x+%u]", name, indreg->number, reg.number); } } else { _debug_printf("%s%u", name, reg.number); } } static void dump_reg( struct sh_reg reg, struct sh_srcreg *indreg, const struct dump_info *di ) { assert( reg.is_reg == 1 ); switch (sh_reg_type( reg )) { case SVGA3DREG_TEMP: format_reg("r", reg, NULL); break; case SVGA3DREG_INPUT: format_reg("v", reg, indreg); break; case SVGA3DREG_CONST: format_reg("c", reg, indreg); break; case SVGA3DREG_ADDR: /* VS */ /* SVGA3DREG_TEXTURE */ /* PS */ assert(!reg.relative); if (di->is_ps) { format_reg("t", reg, NULL); } else { format_reg("a", reg, NULL); } break; case SVGA3DREG_RASTOUT: assert(!reg.relative); switch (reg.number) { case 0 /*POSITION*/: _debug_printf( "oPos" ); break; case 1 /*FOG*/: _debug_printf( "oFog" ); break; case 2 /*POINT_SIZE*/: _debug_printf( "oPts" ); break; default: assert( 0 ); _debug_printf( "???" ); } break; case SVGA3DREG_ATTROUT: assert( reg.number < 2 ); format_reg("oD", reg, NULL); break; case SVGA3DREG_TEXCRDOUT: /* VS */ /* SVGA3DREG_OUTPUT */ /* VS3.0+ */ if (!di->is_ps && di->version >= SVGA3D_VS_30) { format_reg("o", reg, indreg); } else { format_reg("oT", reg, NULL); } break; case SVGA3DREG_COLOROUT: format_reg("oC", reg, NULL); break; case SVGA3DREG_DEPTHOUT: assert(!reg.relative); assert(reg.number == 0); _debug_printf("oDepth"); break; case SVGA3DREG_SAMPLER: format_reg("s", reg, NULL); break; case SVGA3DREG_CONSTBOOL: format_reg("b", reg, NULL); break; case SVGA3DREG_CONSTINT: format_reg("i", reg, NULL); break; case SVGA3DREG_LOOP: assert(!reg.relative); assert( reg.number == 0 ); _debug_printf( "aL" ); break; case SVGA3DREG_MISCTYPE: assert(!reg.relative); switch (reg.number) { case SVGA3DMISCREG_POSITION: _debug_printf("vPos"); break; case SVGA3DMISCREG_FACE: _debug_printf("vFace"); break; default: assert(0); _debug_printf("???"); } break; case SVGA3DREG_LABEL: format_reg("l", reg, NULL); break; case SVGA3DREG_PREDICATE: format_reg("p", reg, NULL); break; default: assert( 0 ); _debug_printf( "???" ); } } static void dump_cdata( struct sh_cdata cdata ) { _debug_printf( "%f, %f, %f, %f", cdata.xyzw[0], cdata.xyzw[1], cdata.xyzw[2], cdata.xyzw[3] ); } static void dump_idata( struct sh_idata idata ) { _debug_printf( "%d, %d, %d, %d", idata.xyzw[0], idata.xyzw[1], idata.xyzw[2], idata.xyzw[3] ); } static void dump_bdata( boolean bdata ) { _debug_printf( bdata ? "TRUE" : "FALSE" ); } static void dump_sampleinfo(struct sh_sampleinfo sampleinfo) { assert( sampleinfo.is_reg == 1 ); switch (sampleinfo.texture_type) { case SVGA3DSAMP_2D: _debug_printf( "_2d" ); break; case SVGA3DSAMP_CUBE: _debug_printf( "_cube" ); break; case SVGA3DSAMP_VOLUME: _debug_printf( "_volume" ); break; default: assert( 0 ); } } static void dump_semantic(uint usage, uint usage_index) { switch (usage) { case SVGA3D_DECLUSAGE_POSITION: _debug_printf("_position"); break; case SVGA3D_DECLUSAGE_BLENDWEIGHT: _debug_printf("_blendweight"); break; case SVGA3D_DECLUSAGE_BLENDINDICES: _debug_printf("_blendindices"); break; case SVGA3D_DECLUSAGE_NORMAL: _debug_printf("_normal"); break; case SVGA3D_DECLUSAGE_PSIZE: _debug_printf("_psize"); break; case SVGA3D_DECLUSAGE_TEXCOORD: _debug_printf("_texcoord"); break; case SVGA3D_DECLUSAGE_TANGENT: _debug_printf("_tangent"); break; case SVGA3D_DECLUSAGE_BINORMAL: _debug_printf("_binormal"); break; case SVGA3D_DECLUSAGE_TESSFACTOR: _debug_printf("_tessfactor"); break; case SVGA3D_DECLUSAGE_POSITIONT: _debug_printf("_positiont"); break; case SVGA3D_DECLUSAGE_COLOR: _debug_printf("_color"); break; case SVGA3D_DECLUSAGE_FOG: _debug_printf("_fog"); break; case SVGA3D_DECLUSAGE_DEPTH: _debug_printf("_depth"); break; case SVGA3D_DECLUSAGE_SAMPLE: _debug_printf("_sample"); break; default: assert(!"Unknown usage"); _debug_printf("_???"); } if (usage_index) { _debug_printf("%u", usage_index); } } static void dump_dstreg(struct sh_dstreg dstreg, struct sh_srcreg *indreg, const struct dump_info *di) { union { struct sh_reg reg; struct sh_dstreg dstreg; } u; memset(&u, 0, sizeof(u)); assert( (dstreg.modifier & (SVGA3DDSTMOD_SATURATE | SVGA3DDSTMOD_PARTIALPRECISION)) == dstreg.modifier ); if (dstreg.modifier & SVGA3DDSTMOD_SATURATE) _debug_printf( "_sat" ); if (dstreg.modifier & SVGA3DDSTMOD_PARTIALPRECISION) _debug_printf( "_pp" ); switch (dstreg.shift_scale) { case 0: break; case 1: _debug_printf( "_x2" ); break; case 2: _debug_printf( "_x4" ); break; case 3: _debug_printf( "_x8" ); break; case 13: _debug_printf( "_d8" ); break; case 14: _debug_printf( "_d4" ); break; case 15: _debug_printf( "_d2" ); break; default: assert( 0 ); } _debug_printf( " " ); u.dstreg = dstreg; dump_reg( u.reg, indreg, di); if (dstreg.write_mask != SVGA3DWRITEMASK_ALL) { _debug_printf( "." ); if (dstreg.write_mask & SVGA3DWRITEMASK_0) _debug_printf( "x" ); if (dstreg.write_mask & SVGA3DWRITEMASK_1) _debug_printf( "y" ); if (dstreg.write_mask & SVGA3DWRITEMASK_2) _debug_printf( "z" ); if (dstreg.write_mask & SVGA3DWRITEMASK_3) _debug_printf( "w" ); } } static void dump_srcreg( struct sh_srcreg srcreg, struct sh_srcreg *indreg, const struct dump_info *di ) { struct sh_reg srcreg_sh = {0}; /* bit-fields carefully aligned, ensure they stay that way. */ STATIC_ASSERT(sizeof(struct sh_reg) == sizeof(struct sh_srcreg)); memcpy(&srcreg_sh, &srcreg, sizeof(srcreg_sh)); switch (srcreg.modifier) { case SVGA3DSRCMOD_NEG: case SVGA3DSRCMOD_BIASNEG: case SVGA3DSRCMOD_SIGNNEG: case SVGA3DSRCMOD_X2NEG: case SVGA3DSRCMOD_ABSNEG: _debug_printf( "-" ); break; case SVGA3DSRCMOD_COMP: _debug_printf( "1-" ); break; case SVGA3DSRCMOD_NOT: _debug_printf( "!" ); } dump_reg(srcreg_sh, indreg, di ); switch (srcreg.modifier) { case SVGA3DSRCMOD_NONE: case SVGA3DSRCMOD_NEG: case SVGA3DSRCMOD_COMP: case SVGA3DSRCMOD_NOT: break; case SVGA3DSRCMOD_BIAS: case SVGA3DSRCMOD_BIASNEG: _debug_printf( "_bias" ); break; case SVGA3DSRCMOD_SIGN: case SVGA3DSRCMOD_SIGNNEG: _debug_printf( "_bx2" ); break; case SVGA3DSRCMOD_X2: case SVGA3DSRCMOD_X2NEG: _debug_printf( "_x2" ); break; case SVGA3DSRCMOD_DZ: _debug_printf( "_dz" ); break; case SVGA3DSRCMOD_DW: _debug_printf( "_dw" ); break; case SVGA3DSRCMOD_ABS: case SVGA3DSRCMOD_ABSNEG: _debug_printf("_abs"); break; default: assert( 0 ); } if (srcreg.swizzle_x != 0 || srcreg.swizzle_y != 1 || srcreg.swizzle_z != 2 || srcreg.swizzle_w != 3) { _debug_printf( "." ); if (srcreg.swizzle_x == srcreg.swizzle_y && srcreg.swizzle_y == srcreg.swizzle_z && srcreg.swizzle_z == srcreg.swizzle_w) { _debug_printf( "%c", "xyzw"[srcreg.swizzle_x] ); } else { _debug_printf( "%c", "xyzw"[srcreg.swizzle_x] ); _debug_printf( "%c", "xyzw"[srcreg.swizzle_y] ); _debug_printf( "%c", "xyzw"[srcreg.swizzle_z] ); _debug_printf( "%c", "xyzw"[srcreg.swizzle_w] ); } } } static void parse_op(struct dump_info *di, const uint **token, struct dump_op *op, uint num_dst, uint num_src) { uint i; assert(num_dst <= 1); assert(num_src <= DUMP_MAX_OP_SRC); op->op = *(struct sh_op *)*token; *token += sizeof(struct sh_op) / sizeof(uint); if (num_dst >= 1) { op->dst = *(struct sh_dstreg *)*token; *token += sizeof(struct sh_dstreg) / sizeof(uint); if (op->dst.relative && (!di->is_ps && di->version >= SVGA3D_VS_30)) { op->dstind = *(struct sh_srcreg *)*token; *token += sizeof(struct sh_srcreg) / sizeof(uint); } } if (op->op.predicated) { op->p0 = *(struct sh_srcreg *)*token; *token += sizeof(struct sh_srcreg) / sizeof(uint); } for (i = 0; i < num_src; ++i) { op->src[i] = *(struct sh_srcreg *)*token; *token += sizeof(struct sh_srcreg) / sizeof(uint); if (op->src[i].relative && ((!di->is_ps && di->version >= SVGA3D_VS_20) || (di->is_ps && di->version >= SVGA3D_PS_30))) { op->srcind[i] = *(struct sh_srcreg *)*token; *token += sizeof(struct sh_srcreg) / sizeof(uint); } } } static void dump_inst(struct dump_info *di, const unsigned **assem, struct sh_op op, const struct sh_opcode_info *info) { struct dump_op dop; boolean not_first_arg = FALSE; uint i; assert(info->num_dst <= 1); di->indent -= info->pre_dedent; dump_indent(di->indent); di->indent += info->post_indent; dump_op(op, info->mnemonic); parse_op(di, assem, &dop, info->num_dst, info->num_src); if (info->num_dst > 0) { dump_dstreg(dop.dst, &dop.dstind, di); not_first_arg = TRUE; } for (i = 0; i < info->num_src; i++) { if (not_first_arg) { _debug_printf(", "); } else { _debug_printf(" "); } dump_srcreg(dop.src[i], &dop.srcind[i], di); not_first_arg = TRUE; } _debug_printf("\n"); } void svga_shader_dump( const unsigned *assem, unsigned dwords, unsigned do_binary ) { boolean finished = FALSE; struct dump_info di; di.version = *assem++; di.is_ps = (di.version & 0xFFFF0000) == 0xFFFF0000; di.indent = 0; _debug_printf( "%s_%u_%u\n", di.is_ps ? "ps" : "vs", (di.version >> 8) & 0xff, di.version & 0xff ); while (!finished) { struct sh_op op = *(struct sh_op *) assem; switch (op.opcode) { case SVGA3DOP_DCL: { struct sh_dcl dcl = *(struct sh_dcl *) assem; _debug_printf( "dcl" ); switch (sh_dstreg_type(dcl.reg)) { case SVGA3DREG_INPUT: if ((di.is_ps && di.version >= SVGA3D_PS_30) || (!di.is_ps && di.version >= SVGA3D_VS_30)) { dump_semantic(dcl.u.semantic.usage, dcl.u.semantic.usage_index); } break; case SVGA3DREG_TEXCRDOUT: if (!di.is_ps && di.version >= SVGA3D_VS_30) { dump_semantic(dcl.u.semantic.usage, dcl.u.semantic.usage_index); } break; case SVGA3DREG_SAMPLER: dump_sampleinfo( dcl.u.sampleinfo ); break; } dump_dstreg(dcl.reg, NULL, &di); _debug_printf( "\n" ); assem += sizeof( struct sh_dcl ) / sizeof( unsigned ); } break; case SVGA3DOP_DEFB: { struct sh_defb defb = *(struct sh_defb *) assem; _debug_printf( "defb " ); dump_reg( defb.reg, NULL, &di ); _debug_printf( ", " ); dump_bdata( defb.data ); _debug_printf( "\n" ); assem += sizeof( struct sh_defb ) / sizeof( unsigned ); } break; case SVGA3DOP_DEFI: { struct sh_defi defi = *(struct sh_defi *) assem; _debug_printf( "defi " ); dump_reg( defi.reg, NULL, &di ); _debug_printf( ", " ); dump_idata( defi.idata ); _debug_printf( "\n" ); assem += sizeof( struct sh_defi ) / sizeof( unsigned ); } break; case SVGA3DOP_TEXCOORD: { struct sh_opcode_info info = *svga_opcode_info(op.opcode); assert(di.is_ps); if (di.version > SVGA3D_PS_13) { assert(info.num_src == 0); info.num_src = 1; } dump_inst(&di, &assem, op, &info); } break; case SVGA3DOP_TEX: { struct sh_opcode_info info = *svga_opcode_info(op.opcode); assert(di.is_ps); if (di.version > SVGA3D_PS_13) { assert(info.num_src == 0); if (di.version > SVGA3D_PS_14) { info.num_src = 2; info.mnemonic = "texld"; } else { info.num_src = 1; } } dump_inst(&di, &assem, op, &info); } break; case SVGA3DOP_DEF: { struct sh_def def = *(struct sh_def *) assem; _debug_printf( "def " ); dump_reg( def.reg, NULL, &di ); _debug_printf( ", " ); dump_cdata( def.cdata ); _debug_printf( "\n" ); assem += sizeof( struct sh_def ) / sizeof( unsigned ); } break; case SVGA3DOP_SINCOS: { struct sh_opcode_info info = *svga_opcode_info(op.opcode); if ((di.is_ps && di.version >= SVGA3D_PS_30) || (!di.is_ps && di.version >= SVGA3D_VS_30)) { assert(info.num_src == 3); info.num_src = 1; } dump_inst(&di, &assem, op, &info); } break; case SVGA3DOP_PHASE: _debug_printf( "phase\n" ); assem += sizeof( struct sh_op ) / sizeof( unsigned ); break; case SVGA3DOP_COMMENT: { struct sh_comment comment = *(struct sh_comment *)assem; /* Ignore comment contents. */ assem += sizeof(struct sh_comment) / sizeof(unsigned) + comment.size; } break; case SVGA3DOP_END: finished = TRUE; break; default: { const struct sh_opcode_info *info = svga_opcode_info(op.opcode); dump_inst(&di, &assem, op, info); } } } }