/** * MojoShader; generate shader programs from bytecode of compiled * Direct3D shaders. * * Please see the file LICENSE.txt in the source's root directory. * * This file written by Ryan C. Gordon. */ #define __MOJOSHADER_INTERNAL__ 1 #include "mojoshader_profile.h" #pragma GCC visibility push(hidden) #if SUPPORT_PROFILE_D3D const char *make_D3D_srcarg_string_in_buf(Context *ctx, const SourceArgInfo *arg, char *buf, size_t buflen) { const char *premod_str = ""; const char *postmod_str = ""; switch (arg->src_mod) { case SRCMOD_NEGATE: premod_str = "-"; break; case SRCMOD_BIASNEGATE: premod_str = "-"; // fall through. case SRCMOD_BIAS: postmod_str = "_bias"; break; case SRCMOD_SIGNNEGATE: premod_str = "-"; // fall through. case SRCMOD_SIGN: postmod_str = "_bx2"; break; case SRCMOD_COMPLEMENT: premod_str = "1-"; break; case SRCMOD_X2NEGATE: premod_str = "-"; // fall through. case SRCMOD_X2: postmod_str = "_x2"; break; case SRCMOD_DZ: postmod_str = "_dz"; break; case SRCMOD_DW: postmod_str = "_dw"; break; case SRCMOD_ABSNEGATE: premod_str = "-"; // fall through. case SRCMOD_ABS: postmod_str = "_abs"; break; case SRCMOD_NOT: premod_str = "!"; break; case SRCMOD_NONE: case SRCMOD_TOTAL: break; // stop compiler whining. } // switch char regnum_str[16]; const char *regtype_str = get_D3D_register_string(ctx, arg->regtype, arg->regnum, regnum_str, sizeof (regnum_str)); if (regtype_str == NULL) { fail(ctx, "Unknown source register type."); *buf = '\0'; return buf; } // if const char *rel_lbracket = ""; const char *rel_rbracket = ""; char rel_swizzle[4] = { '\0' }; char rel_regnum_str[16] = { '\0' }; const char *rel_regtype_str = ""; if (arg->relative) { if (arg->relative_regtype == REG_TYPE_LOOP) { rel_swizzle[0] = '\0'; rel_swizzle[1] = '\0'; rel_swizzle[2] = '\0'; } // if else { rel_swizzle[0] = '.'; rel_swizzle[1] = swizzle_channels[arg->relative_component]; rel_swizzle[2] = '\0'; } // else rel_lbracket = "["; rel_rbracket = "]"; rel_regtype_str = get_D3D_register_string(ctx, arg->relative_regtype, arg->relative_regnum, rel_regnum_str, sizeof (rel_regnum_str)); if (regtype_str == NULL) { fail(ctx, "Unknown relative source register type."); *buf = '\0'; return buf; } // if } // if char swizzle_str[6]; size_t i = 0; const int scalar = isscalar(ctx, ctx->shader_type, arg->regtype, arg->regnum); if (!scalar && !no_swizzle(arg->swizzle)) { swizzle_str[i++] = '.'; swizzle_str[i++] = swizzle_channels[arg->swizzle_x]; swizzle_str[i++] = swizzle_channels[arg->swizzle_y]; swizzle_str[i++] = swizzle_channels[arg->swizzle_z]; swizzle_str[i++] = swizzle_channels[arg->swizzle_w]; // .xyzz is the same as .xyz, .z is the same as .zzzz, etc. while (swizzle_str[i-1] == swizzle_str[i-2]) i--; } // if swizzle_str[i] = '\0'; assert(i < sizeof (swizzle_str)); // !!! FIXME: c12[a0.x] actually needs to be c[a0.x + 12] snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s", premod_str, regtype_str, regnum_str, postmod_str, rel_lbracket, rel_regtype_str, rel_regnum_str, rel_swizzle, rel_rbracket, swizzle_str); // !!! FIXME: make sure the scratch buffer was large enough. return buf; } // make_D3D_srcarg_string_in_buf const char *make_D3D_destarg_string(Context *ctx, char *buf, const size_t buflen) { const DestArgInfo *arg = &ctx->dest_arg; const char *result_shift_str = ""; switch (arg->result_shift) { case 0x1: result_shift_str = "_x2"; break; case 0x2: result_shift_str = "_x4"; break; case 0x3: result_shift_str = "_x8"; break; case 0xD: result_shift_str = "_d8"; break; case 0xE: result_shift_str = "_d4"; break; case 0xF: result_shift_str = "_d2"; break; } // switch const char *sat_str = (arg->result_mod & MOD_SATURATE) ? "_sat" : ""; const char *pp_str = (arg->result_mod & MOD_PP) ? "_pp" : ""; const char *cent_str = (arg->result_mod & MOD_CENTROID) ? "_centroid" : ""; char regnum_str[16]; const char *regtype_str = get_D3D_register_string(ctx, arg->regtype, arg->regnum, regnum_str, sizeof (regnum_str)); if (regtype_str == NULL) { fail(ctx, "Unknown destination register type."); *buf = '\0'; return buf; } // if char writemask_str[6]; size_t i = 0; const int scalar = isscalar(ctx, ctx->shader_type, arg->regtype, arg->regnum); if (!scalar && !writemask_xyzw(arg->writemask)) { writemask_str[i++] = '.'; if (arg->writemask0) writemask_str[i++] = 'x'; if (arg->writemask1) writemask_str[i++] = 'y'; if (arg->writemask2) writemask_str[i++] = 'z'; if (arg->writemask3) writemask_str[i++] = 'w'; } // if writemask_str[i] = '\0'; assert(i < sizeof (writemask_str)); const char *pred_left = ""; const char *pred_right = ""; char pred[32] = { '\0' }; if (ctx->predicated) { pred_left = "("; pred_right = ") "; make_D3D_srcarg_string_in_buf(ctx, &ctx->predicate_arg, pred, sizeof (pred)); } // if // may turn out something like "_x2_sat_pp_centroid (!p0.x) r0.xyzw" ... snprintf(buf, buflen, "%s%s%s%s %s%s%s%s%s%s", result_shift_str, sat_str, pp_str, cent_str, pred_left, pred, pred_right, regtype_str, regnum_str, writemask_str); // !!! FIXME: make sure the scratch buffer was large enough. return buf; } // make_D3D_destarg_string const char *make_D3D_srcarg_string(Context *ctx, const size_t idx, char *buf, size_t buflen) { if (idx >= STATICARRAYLEN(ctx->source_args)) { fail(ctx, "Too many source args"); *buf = '\0'; return buf; } // if const SourceArgInfo *arg = &ctx->source_args[idx]; return make_D3D_srcarg_string_in_buf(ctx, arg, buf, buflen); } // make_D3D_srcarg_string const char *get_D3D_const_array_varname(Context *ctx, int base, int size) { char buf[64]; snprintf(buf, sizeof (buf), "c_array_%d_%d", base, size); return StrDup(ctx, buf); } // get_D3D_const_array_varname void emit_D3D_start(Context *ctx, const char *profilestr) { const uint major = (uint) ctx->major_ver; const uint minor = (uint) ctx->minor_ver; char minor_str[16]; ctx->ignores_ctab = 1; if (minor == 0xFF) strcpy(minor_str, "sw"); else if ((major > 1) && (minor == 1)) strcpy(minor_str, "x"); // for >= SM2, apparently this is "x". Weird. else snprintf(minor_str, sizeof (minor_str), "%u", (uint) minor); output_line(ctx, "%s_%u_%s", ctx->shader_type_str, major, minor_str); } // emit_D3D_start void emit_D3D_end(Context *ctx) { output_line(ctx, "end"); } // emit_D3D_end void emit_D3D_phase(Context *ctx) { output_line(ctx, "phase"); } // emit_D3D_phase void emit_D3D_finalize(Context *ctx) { // no-op. } // emit_D3D_finalize void emit_D3D_global(Context *ctx, RegisterType regtype, int regnum) { // no-op. } // emit_D3D_global void emit_D3D_array(Context *ctx, VariableList *var) { // no-op. } // emit_D3D_array void emit_D3D_const_array(Context *ctx, const ConstantsList *clist, int base, int size) { // no-op. } // emit_D3D_const_array void emit_D3D_uniform(Context *ctx, RegisterType regtype, int regnum, const VariableList *var) { // no-op. } // emit_D3D_uniform void emit_D3D_sampler(Context *ctx, int s, TextureType ttype, int tb) { // no-op. } // emit_D3D_sampler void emit_D3D_attribute(Context *ctx, RegisterType regtype, int regnum, MOJOSHADER_usage usage, int index, int wmask, int flags) { // no-op. } // emit_D3D_attribute void emit_D3D_RESERVED(Context *ctx) { // do nothing; fails in the state machine. } // emit_D3D_RESERVED // Generic D3D opcode emitters. A list of macros generate all the entry points // that call into these... char *lowercase(char *dst, const char *src) { int i = 0; do { const char ch = src[i]; dst[i] = (((ch >= 'A') && (ch <= 'Z')) ? (ch - ('A' - 'a')) : ch); } while (src[i++]); return dst; } // lowercase void emit_D3D_opcode_d(Context *ctx, const char *opcode) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s%s", ctx->coissue ? "+" : "", opcode, dst); } // emit_D3D_opcode_d void emit_D3D_opcode_s(Context *ctx, const char *opcode) { char src0[64]; make_D3D_srcarg_string(ctx, 0, src0, sizeof (src0)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s %s", ctx->coissue ? "+" : "", opcode, src0); } // emit_D3D_opcode_s void emit_D3D_opcode_ss(Context *ctx, const char *opcode) { char src0[64]; make_D3D_srcarg_string(ctx, 0, src0, sizeof (src0)); char src1[64]; make_D3D_srcarg_string(ctx, 1, src1, sizeof (src1)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s %s, %s", ctx->coissue ? "+" : "", opcode, src0, src1); } // emit_D3D_opcode_ss void emit_D3D_opcode_ds(Context *ctx, const char *opcode) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); char src0[64]; make_D3D_srcarg_string(ctx, 0, src0, sizeof (src0)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s%s, %s", ctx->coissue ? "+" : "", opcode, dst, src0); } // emit_D3D_opcode_ds void emit_D3D_opcode_dss(Context *ctx, const char *opcode) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); char src0[64]; make_D3D_srcarg_string(ctx, 0, src0, sizeof (src0)); char src1[64]; make_D3D_srcarg_string(ctx, 1, src1, sizeof (src1)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s%s, %s, %s", ctx->coissue ? "+" : "", opcode, dst, src0, src1); } // emit_D3D_opcode_dss void emit_D3D_opcode_dsss(Context *ctx, const char *opcode) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); char src0[64]; make_D3D_srcarg_string(ctx, 0, src0, sizeof (src0)); char src1[64]; make_D3D_srcarg_string(ctx, 1, src1, sizeof (src1)); char src2[64]; make_D3D_srcarg_string(ctx, 2, src2, sizeof (src2)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s%s, %s, %s, %s", ctx->coissue ? "+" : "", opcode, dst, src0, src1, src2); } // emit_D3D_opcode_dsss void emit_D3D_opcode_dssss(Context *ctx, const char *opcode) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); char src0[64]; make_D3D_srcarg_string(ctx, 0, src0, sizeof (src0)); char src1[64]; make_D3D_srcarg_string(ctx, 1, src1, sizeof (src1)); char src2[64]; make_D3D_srcarg_string(ctx, 2, src2, sizeof (src2)); char src3[64]; make_D3D_srcarg_string(ctx, 3, src3, sizeof (src3)); opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx,"%s%s%s, %s, %s, %s, %s", ctx->coissue ? "+" : "", opcode, dst, src0, src1, src2, src3); } // emit_D3D_opcode_dssss void emit_D3D_opcode(Context *ctx, const char *opcode) { opcode = lowercase((char *) alloca(strlen(opcode) + 1), opcode); output_line(ctx, "%s%s", ctx->coissue ? "+" : "", opcode); } // emit_D3D_opcode #define EMIT_D3D_OPCODE_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode(ctx, #op); \ } #define EMIT_D3D_OPCODE_D_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_d(ctx, #op); \ } #define EMIT_D3D_OPCODE_S_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_s(ctx, #op); \ } #define EMIT_D3D_OPCODE_SS_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_ss(ctx, #op); \ } #define EMIT_D3D_OPCODE_DS_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_ds(ctx, #op); \ } #define EMIT_D3D_OPCODE_DSS_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_dss(ctx, #op); \ } #define EMIT_D3D_OPCODE_DSSS_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_dsss(ctx, #op); \ } #define EMIT_D3D_OPCODE_DSSSS_FUNC(op) \ void emit_D3D_##op(Context *ctx) { \ emit_D3D_opcode_dssss(ctx, #op); \ } EMIT_D3D_OPCODE_FUNC(NOP) EMIT_D3D_OPCODE_DS_FUNC(MOV) EMIT_D3D_OPCODE_DSS_FUNC(ADD) EMIT_D3D_OPCODE_DSS_FUNC(SUB) EMIT_D3D_OPCODE_DSSS_FUNC(MAD) EMIT_D3D_OPCODE_DSS_FUNC(MUL) EMIT_D3D_OPCODE_DS_FUNC(RCP) EMIT_D3D_OPCODE_DS_FUNC(RSQ) EMIT_D3D_OPCODE_DSS_FUNC(DP3) EMIT_D3D_OPCODE_DSS_FUNC(DP4) EMIT_D3D_OPCODE_DSS_FUNC(MIN) EMIT_D3D_OPCODE_DSS_FUNC(MAX) EMIT_D3D_OPCODE_DSS_FUNC(SLT) EMIT_D3D_OPCODE_DSS_FUNC(SGE) EMIT_D3D_OPCODE_DS_FUNC(EXP) EMIT_D3D_OPCODE_DS_FUNC(LOG) EMIT_D3D_OPCODE_DS_FUNC(LIT) EMIT_D3D_OPCODE_DSS_FUNC(DST) EMIT_D3D_OPCODE_DSSS_FUNC(LRP) EMIT_D3D_OPCODE_DS_FUNC(FRC) EMIT_D3D_OPCODE_DSS_FUNC(M4X4) EMIT_D3D_OPCODE_DSS_FUNC(M4X3) EMIT_D3D_OPCODE_DSS_FUNC(M3X4) EMIT_D3D_OPCODE_DSS_FUNC(M3X3) EMIT_D3D_OPCODE_DSS_FUNC(M3X2) EMIT_D3D_OPCODE_S_FUNC(CALL) EMIT_D3D_OPCODE_SS_FUNC(CALLNZ) EMIT_D3D_OPCODE_SS_FUNC(LOOP) EMIT_D3D_OPCODE_FUNC(RET) EMIT_D3D_OPCODE_FUNC(ENDLOOP) EMIT_D3D_OPCODE_S_FUNC(LABEL) EMIT_D3D_OPCODE_DSS_FUNC(POW) EMIT_D3D_OPCODE_DSS_FUNC(CRS) EMIT_D3D_OPCODE_DSSS_FUNC(SGN) EMIT_D3D_OPCODE_DS_FUNC(ABS) EMIT_D3D_OPCODE_DS_FUNC(NRM) EMIT_D3D_OPCODE_S_FUNC(REP) EMIT_D3D_OPCODE_FUNC(ENDREP) EMIT_D3D_OPCODE_S_FUNC(IF) EMIT_D3D_OPCODE_FUNC(ELSE) EMIT_D3D_OPCODE_FUNC(ENDIF) EMIT_D3D_OPCODE_FUNC(BREAK) EMIT_D3D_OPCODE_DS_FUNC(MOVA) EMIT_D3D_OPCODE_D_FUNC(TEXKILL) EMIT_D3D_OPCODE_DS_FUNC(TEXBEM) EMIT_D3D_OPCODE_DS_FUNC(TEXBEML) EMIT_D3D_OPCODE_DS_FUNC(TEXREG2AR) EMIT_D3D_OPCODE_DS_FUNC(TEXREG2GB) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X2PAD) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X2TEX) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X3PAD) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X3TEX) EMIT_D3D_OPCODE_DSS_FUNC(TEXM3X3SPEC) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X3VSPEC) EMIT_D3D_OPCODE_DS_FUNC(EXPP) EMIT_D3D_OPCODE_DS_FUNC(LOGP) EMIT_D3D_OPCODE_DSSS_FUNC(CND) EMIT_D3D_OPCODE_DS_FUNC(TEXREG2RGB) EMIT_D3D_OPCODE_DS_FUNC(TEXDP3TEX) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X2DEPTH) EMIT_D3D_OPCODE_DS_FUNC(TEXDP3) EMIT_D3D_OPCODE_DS_FUNC(TEXM3X3) EMIT_D3D_OPCODE_D_FUNC(TEXDEPTH) EMIT_D3D_OPCODE_DSSS_FUNC(CMP) EMIT_D3D_OPCODE_DSS_FUNC(BEM) EMIT_D3D_OPCODE_DSSS_FUNC(DP2ADD) EMIT_D3D_OPCODE_DS_FUNC(DSX) EMIT_D3D_OPCODE_DS_FUNC(DSY) EMIT_D3D_OPCODE_DSSSS_FUNC(TEXLDD) EMIT_D3D_OPCODE_DSS_FUNC(TEXLDL) EMIT_D3D_OPCODE_S_FUNC(BREAKP) // special cases for comparison opcodes... const char *get_D3D_comparison_string(Context *ctx) { const char *comps[] = { "", "_gt", "_eq", "_ge", "_lt", "_ne", "_le" }; if (ctx->instruction_controls >= STATICARRAYLEN(comps)) { fail(ctx, "unknown comparison control"); return ""; } // if return comps[ctx->instruction_controls]; } // get_D3D_comparison_string void emit_D3D_BREAKC(Context *ctx) { char op[16]; snprintf(op, sizeof (op), "break%s", get_D3D_comparison_string(ctx)); emit_D3D_opcode_ss(ctx, op); } // emit_D3D_BREAKC void emit_D3D_IFC(Context *ctx) { char op[16]; snprintf(op, sizeof (op), "if%s", get_D3D_comparison_string(ctx)); emit_D3D_opcode_ss(ctx, op); } // emit_D3D_IFC void emit_D3D_SETP(Context *ctx) { char op[16]; snprintf(op, sizeof (op), "setp%s", get_D3D_comparison_string(ctx)); emit_D3D_opcode_dss(ctx, op); } // emit_D3D_SETP void emit_D3D_DEF(Context *ctx) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); const float *val = (const float *) ctx->dwords; // !!! FIXME: could be int? char val0[32]; char val1[32]; char val2[32]; char val3[32]; floatstr(ctx, val0, sizeof (val0), val[0], 0); floatstr(ctx, val1, sizeof (val1), val[1], 0); floatstr(ctx, val2, sizeof (val2), val[2], 0); floatstr(ctx, val3, sizeof (val3), val[3], 0); output_line(ctx, "def%s, %s, %s, %s, %s", dst, val0, val1, val2, val3); } // emit_D3D_DEF void emit_D3D_DEFI(Context *ctx) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); const int32 *x = (const int32 *) ctx->dwords; output_line(ctx, "defi%s, %d, %d, %d, %d", dst, (int) x[0], (int) x[1], (int) x[2], (int) x[3]); } // emit_D3D_DEFI void emit_D3D_DEFB(Context *ctx) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); output_line(ctx, "defb%s, %s", dst, ctx->dwords[0] ? "true" : "false"); } // emit_D3D_DEFB static const char *usagestrs[] = { "_position", "_blendweight", "_blendindices", "_normal", "_psize", "_texcoord", "_tangent", "_binormal", "_tessfactor", "_positiont", "_color", "_fog", "_depth", "_sample" }; void emit_D3D_DCL(Context *ctx) { char dst[64]; make_D3D_destarg_string(ctx, dst, sizeof (dst)); const DestArgInfo *arg = &ctx->dest_arg; const char *usage_str = ""; char index_str[16] = { '\0' }; if (arg->regtype == REG_TYPE_SAMPLER) { switch ((const TextureType) ctx->dwords[0]) { case TEXTURE_TYPE_2D: usage_str = "_2d"; break; case TEXTURE_TYPE_CUBE: usage_str = "_cube"; break; case TEXTURE_TYPE_VOLUME: usage_str = "_volume"; break; default: fail(ctx, "unknown sampler texture type"); return; } // switch } // if else if (arg->regtype == REG_TYPE_MISCTYPE) { switch ((const MiscTypeType) arg->regnum) { case MISCTYPE_TYPE_POSITION: case MISCTYPE_TYPE_FACE: usage_str = ""; // just become "dcl vFace" or whatever. break; default: fail(ctx, "unknown misc register type"); return; } // switch } // else if else { const uint32 usage = ctx->dwords[0]; const uint32 index = ctx->dwords[1]; usage_str = usagestrs[usage]; if (index != 0) snprintf(index_str, sizeof (index_str), "%u", (uint) index); } // else output_line(ctx, "dcl%s%s%s", usage_str, index_str, dst); } // emit_D3D_DCL void emit_D3D_TEXCRD(Context *ctx) { // this opcode looks and acts differently depending on the shader model. if (shader_version_atleast(ctx, 1, 4)) emit_D3D_opcode_ds(ctx, "texcrd"); else emit_D3D_opcode_d(ctx, "texcoord"); } // emit_D3D_TEXCOORD void emit_D3D_TEXLD(Context *ctx) { // this opcode looks and acts differently depending on the shader model. if (shader_version_atleast(ctx, 2, 0)) { if (ctx->instruction_controls == CONTROL_TEXLD) emit_D3D_opcode_dss(ctx, "texld"); else if (ctx->instruction_controls == CONTROL_TEXLDP) emit_D3D_opcode_dss(ctx, "texldp"); else if (ctx->instruction_controls == CONTROL_TEXLDB) emit_D3D_opcode_dss(ctx, "texldb"); } // if else if (shader_version_atleast(ctx, 1, 4)) { emit_D3D_opcode_ds(ctx, "texld"); } // else if else { emit_D3D_opcode_d(ctx, "tex"); } // else } // emit_D3D_TEXLD void emit_D3D_SINCOS(Context *ctx) { // this opcode needs extra registers for sm2 and lower. if (!shader_version_atleast(ctx, 3, 0)) emit_D3D_opcode_dsss(ctx, "sincos"); else emit_D3D_opcode_ds(ctx, "sincos"); } // emit_D3D_SINCOS #endif // SUPPORT_PROFILE_D3D #pragma GCC visibility pop