mojoshader.c
changeset 1099 aef946714f39
parent 1098 ef8be3b15633
child 1100 d1c8e5a9dcd0
--- a/mojoshader.c	Wed Apr 18 00:59:12 2012 -0400
+++ b/mojoshader.c	Mon Apr 23 01:19:58 2012 -0400
@@ -152,11 +152,19 @@
     int predicated;
     int uses_pointsize;
     int uses_fog;
-    int glsl_generated_lit_opcode;
+    int glsl_generated_lit_helper;
     int glsl_generated_texldd_setup;
+    int glsl_generated_texm3x3spec_helper;
     int arb1_wrote_position;
     int have_preshader;
     int ignores_ctab;
+    int reset_texmpad;
+    int texm3x2pad_dst0;
+    int texm3x2pad_src0;
+    int texm3x3pad_dst0;
+    int texm3x3pad_src0;
+    int texm3x3pad_dst1;
+    int texm3x3pad_src1;
     MOJOSHADER_preshader *preshader;
 
 #if SUPPORT_PROFILE_ARB1_NV
@@ -2822,10 +2830,10 @@
 {
     const char *maxp = "127.9961"; // value from the dx9 reference.
 
-    if (ctx->glsl_generated_lit_opcode)
+    if (ctx->glsl_generated_lit_helper)
         return;
 
-    ctx->glsl_generated_lit_opcode = 1;
+    ctx->glsl_generated_lit_helper = 1;
 
     push_output(ctx, &ctx->helpers);
     output_line(ctx, "vec4 LIT(const vec4 src)");
@@ -2848,9 +2856,9 @@
 {
     char src0[64]; make_GLSL_srcarg_string_full(ctx, 0, src0, sizeof (src0));
     char code[128];
+    emit_GLSL_LIT_helper(ctx);
     make_GLSL_destarg_assign(ctx, code, sizeof (code), "LIT(%s)", src0);
     output_line(ctx, "%s", code);
-    emit_GLSL_LIT_helper(ctx);
 } // emit_GLSL_LIT
 
 static void emit_GLSL_DST(Context *ctx)
@@ -3229,7 +3237,7 @@
 
         assert(!texldd);
 
-        // Note that this code counts on the register not having swizzles, etc.
+        // !!! FIXME: this code counts on the register not having swizzles, etc.
         get_GLSL_destarg_varname(ctx, dst, sizeof (dst));
         get_GLSL_varname_in_buf(ctx, REG_TYPE_SAMPLER, info->regnum,
                                 sampler, sizeof (sampler));
@@ -3353,7 +3361,7 @@
     char sampler[64];
     char code[512];
 
-    // Note that this code counts on the register not having swizzles, etc.
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
     get_GLSL_varname_in_buf(ctx, REG_TYPE_SAMPLER, info->regnum,
                             sampler, sizeof (sampler));
 
@@ -3370,7 +3378,7 @@
 
 static void emit_GLSL_TEXBEML(Context *ctx)
 {
-    // Note that this code counts on the register not having swizzles, etc.
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
     DestArgInfo *info = &ctx->dest_arg;
     char dst[64]; get_GLSL_destarg_varname(ctx, dst, sizeof (dst));
     char src[64]; get_GLSL_srcarg_varname(ctx, 0, src, sizeof (src));
@@ -3394,12 +3402,207 @@
 
 EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXREG2AR) // !!! FIXME
 EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXREG2GB) // !!! FIXME
-EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X2PAD) // !!! FIXME
-EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X2TEX) // !!! FIXME
-EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3PAD) // !!! FIXME
-EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3TEX) // !!! FIXME
-EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3SPEC) // !!! FIXME
-EMIT_GLSL_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3VSPEC) // !!! FIXME
+
+
+static void emit_GLSL_TEXM3X2PAD(Context *ctx)
+{
+    // no-op ... work happens in emit_GLSL_TEXM3X2TEX().
+} // emit_GLSL_TEXM3X2PAD
+
+static void emit_GLSL_TEXM3X2TEX(Context *ctx)
+{
+    if (ctx->texm3x2pad_src0 == -1)
+        return;
+
+    DestArgInfo *info = &ctx->dest_arg;
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char sampler[64];
+    char code[512];
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_SAMPLER, info->regnum,
+                            sampler, sizeof (sampler));
+
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x2pad_src0,
+                            src0, sizeof (src0));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x2pad_dst0,
+                            src1, sizeof (src1));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src2, sizeof (src2));
+    get_GLSL_destarg_varname(ctx, dst, sizeof (dst));
+
+    make_GLSL_destarg_assign(ctx, code, sizeof (code),
+        "texture2D(%s, vec2(dot(%s.xyz, %s.xyz), dot(%s.xyz, %s.xyz)))",
+        sampler, src0, src1, src2, dst);
+
+    output_line(ctx, "%s", code);
+} // emit_GLSL_TEXM3X2TEX
+
+static void emit_GLSL_TEXM3X3PAD(Context *ctx)
+{
+    // no-op ... work happens in emit_GLSL_TEXM3X3*().
+} // emit_GLSL_TEXM3X3PAD
+
+static void emit_GLSL_TEXM3X3TEX(Context *ctx)
+{
+    if (ctx->texm3x3pad_src1 == -1)
+        return;
+
+    DestArgInfo *info = &ctx->dest_arg;
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char src3[64];
+    char src4[64];
+    char sampler[64];
+    char code[512];
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_SAMPLER, info->regnum,
+                            sampler, sizeof (sampler));
+
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst0,
+                            src0, sizeof (src0));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src0,
+                            src1, sizeof (src1));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst1,
+                            src2, sizeof (src2));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src1,
+                            src3, sizeof (src3));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src4, sizeof (src4));
+    get_GLSL_destarg_varname(ctx, dst, sizeof (dst));
+
+    make_GLSL_destarg_assign(ctx, code, sizeof (code),
+        "texture3D(%s,"
+            " vec3(dot(%s.xyz, %s.xyz),"
+            " dot(%s.xyz, %s.xyz),"
+            " dot(%s.xyz, %s.xyz)))",
+        sampler, src0, src1, src2, src3, dst, src4);
+
+    output_line(ctx, "%s", code);
+} // emit_GLSL_TEXM3X3TEX
+
+static void emit_GLSL_TEXM3X3SPEC_helper(Context *ctx)
+{
+    if (ctx->glsl_generated_texm3x3spec_helper)
+        return;
+
+    ctx->glsl_generated_texm3x3spec_helper = 1;
+
+    push_output(ctx, &ctx->helpers);
+    output_line(ctx, "vec3 TEXM3X3SPEC_reflection(const vec3 normal, const vec3 eyeray)");
+    output_line(ctx, "{"); ctx->indent++;
+    output_line(ctx,   "return (2.0 * ((normal * eyeray) / (normal * normal)) * normal) - eyeray;"); ctx->indent--;
+    output_line(ctx, "}");
+    output_blank_line(ctx);
+    pop_output(ctx);
+} // emit_GLSL_TEXM3X3SPEC_helper
+
+static void emit_GLSL_TEXM3X3SPEC(Context *ctx)
+{
+    if (ctx->texm3x3pad_src1 == -1)
+        return;
+
+    DestArgInfo *info = &ctx->dest_arg;
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char src3[64];
+    char src4[64];
+    char src5[64];
+    char sampler[64];
+    char code[512];
+
+    emit_GLSL_TEXM3X3SPEC_helper(ctx);
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_SAMPLER, info->regnum,
+                            sampler, sizeof (sampler));
+
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst0,
+                            src0, sizeof (src0));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src0,
+                            src1, sizeof (src1));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst1,
+                            src2, sizeof (src2));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src1,
+                            src3, sizeof (src3));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src4, sizeof (src4));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[1].regnum,
+                            src5, sizeof (src5));
+    get_GLSL_destarg_varname(ctx, dst, sizeof (dst));
+
+    make_GLSL_destarg_assign(ctx, code, sizeof (code),
+        "texture3D(%s, "
+            "TEXM3X3SPEC_reflection("
+                "vec3("
+                    "dot(%s.xyz, %s.xyz), "
+                    "dot(%s.xyz, %s.xyz), "
+                    "dot(%s.xyz, %s.xyz)"
+                "),"
+                "%s.xyz,"
+            ")"
+        ")",
+        sampler, src0, src1, src2, src3, dst, src4, src5);
+
+    output_line(ctx, "%s", code);
+} // emit_GLSL_TEXM3X3SPEC
+
+static void emit_GLSL_TEXM3X3VSPEC(Context *ctx)
+{
+    if (ctx->texm3x3pad_src1 == -1)
+        return;
+
+    DestArgInfo *info = &ctx->dest_arg;
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char src3[64];
+    char src4[64];
+    char sampler[64];
+    char code[512];
+
+    emit_GLSL_TEXM3X3SPEC_helper(ctx);
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_SAMPLER, info->regnum,
+                            sampler, sizeof (sampler));
+
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst0,
+                            src0, sizeof (src0));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src0,
+                            src1, sizeof (src1));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst1,
+                            src2, sizeof (src2));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src1,
+                            src3, sizeof (src3));
+    get_GLSL_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src4, sizeof (src4));
+    get_GLSL_destarg_varname(ctx, dst, sizeof (dst));
+
+    make_GLSL_destarg_assign(ctx, code, sizeof (code),
+        "texture3D(%s, "
+            "TEXM3X3SPEC_reflection("
+                "vec3("
+                    "dot(%s.xyz, %s.xyz), "
+                    "dot(%s.xyz, %s.xyz), "
+                    "dot(%s.xyz, %s.xyz)"
+                "), "
+                "vec3(%s.w, %s.w, %s.w)"
+            ")"
+        ")",
+        sampler, src0, src1, src2, src3, dst, src4, src0, src2, dst);
+
+    output_line(ctx, "%s", code);
+} // emit_GLSL_TEXM3X3VSPEC
 
 static void emit_GLSL_EXPP(Context *ctx)
 {
@@ -5281,7 +5484,7 @@
 
 static void arb1_texbem(Context *ctx, const int luminance)
 {
-    // Note that this code counts on the register not having swizzles, etc.
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
     const int stage = ctx->dest_arg.regnum;
     char dst[64]; get_ARB1_destarg_varname(ctx, dst, sizeof (dst));
     char src[64]; get_ARB1_srcarg_varname(ctx, 0, src, sizeof (src));
@@ -5317,12 +5520,170 @@
 
 EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXREG2AR)
 EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXREG2GB)
-EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X2PAD)
-EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X2TEX)
-EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3PAD)
-EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3TEX)
-EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3SPEC)
-EMIT_ARB1_OPCODE_UNIMPLEMENTED_FUNC(TEXM3X3VSPEC)
+
+
+static void emit_ARB1_TEXM3X2PAD(Context *ctx)
+{
+    // no-op ... work happens in emit_ARB1_TEXM3X2TEX().
+} // emit_ARB1_TEXM3X2PAD
+
+static void emit_ARB1_TEXM3X2TEX(Context *ctx)
+{
+    if (ctx->texm3x2pad_src0 == -1)
+        return;
+
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    const int stage = ctx->dest_arg.regnum;
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x2pad_src0,
+                            src0, sizeof (src0));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x2pad_dst0,
+                            src1, sizeof (src1));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src2, sizeof (src2));
+    get_ARB1_destarg_varname(ctx, dst, sizeof (dst));
+
+    output_line(ctx, "DP3 %s.y, %s, %s;", dst, src2, dst);
+    output_line(ctx, "DP3 %s.x, %s, %s;", dst, src0, src1);
+    output_line(ctx, "TEX %s, %s, texture[%d], 2D;", dst, dst, stage);
+    emit_ARB1_dest_modifiers(ctx);
+} // emit_ARB1_TEXM3X2TEX
+
+
+static void emit_ARB1_TEXM3X3PAD(Context *ctx)
+{
+    // no-op ... work happens in emit_ARB1_TEXM3X3*().
+} // emit_ARB1_TEXM3X3PAD
+
+
+static void emit_ARB1_TEXM3X3TEX(Context *ctx)
+{
+    if (ctx->texm3x3pad_src1 == -1)
+        return;
+
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char src3[64];
+    char src4[64];
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    const int stage = ctx->dest_arg.regnum;
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst0,
+                            src0, sizeof (src0));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src0,
+                            src1, sizeof (src1));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst1,
+                            src2, sizeof (src2));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src1,
+                            src3, sizeof (src3));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src4, sizeof (src4));
+    get_ARB1_destarg_varname(ctx, dst, sizeof (dst));
+
+    output_line(ctx, "DP3 %s.z, %s, %s;", dst, dst, src4);
+    output_line(ctx, "DP3 %s.x, %s, %s;", dst, src0, src1);
+    output_line(ctx, "DP3 %s.y, %s, %s;", dst, src2, src3);
+    output_line(ctx, "TEX %s, %s, texture[%d], 3D;", dst, dst, stage);
+    emit_ARB1_dest_modifiers(ctx);
+} // emit_ARB1_TEXM3X3TEX
+
+static void emit_ARB1_TEXM3X3SPEC(Context *ctx)
+{
+    if (ctx->texm3x3pad_src1 == -1)
+        return;
+
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char src3[64];
+    char src4[64];
+    char src5[64];
+    char tmp[64];
+    char tmp2[64];
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    const int stage = ctx->dest_arg.regnum;
+    allocate_ARB1_scratch_reg_name(ctx, tmp, sizeof (tmp));
+    allocate_ARB1_scratch_reg_name(ctx, tmp2, sizeof (tmp2));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst0,
+                            src0, sizeof (src0));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src0,
+                            src1, sizeof (src1));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst1,
+                            src2, sizeof (src2));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src1,
+                            src3, sizeof (src3));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src4, sizeof (src4));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[1].regnum,
+                            src5, sizeof (src5));
+    get_ARB1_destarg_varname(ctx, dst, sizeof (dst));
+
+    output_line(ctx, "DP3 %s.z, %s, %s;", dst, dst, src4);
+    output_line(ctx, "DP3 %s.x, %s, %s;", dst, src0, src1);
+    output_line(ctx, "DP3 %s.y, %s, %s;", dst, src2, src3);
+    output_line(ctx, "MUL %s, %s, %s;", tmp, dst, dst);    // normal * normal
+    output_line(ctx, "MUL %s, %s, %s;", tmp2, dst, src5);  // normal * eyeray
+    output_line(ctx, "DIV %s, %s, %s;", tmp, tmp, tmp2);
+    output_line(ctx, "MUL %s, %s, { 2.0, 2.0, 2.0, 2.0 };", tmp, tmp);
+    output_line(ctx, "MAD %s, %s, %s, -%s;", tmp, tmp, dst, src5);
+    output_line(ctx, "TEX %s, %s, texture[%d], 3D;", dst, tmp, stage);
+    emit_ARB1_dest_modifiers(ctx);
+} // emit_ARB1_TEXM3X3SPEC
+
+static void emit_ARB1_TEXM3X3VSPEC(Context *ctx)
+{
+    if (ctx->texm3x3pad_src1 == -1)
+        return;
+
+    char dst[64];
+    char src0[64];
+    char src1[64];
+    char src2[64];
+    char src3[64];
+    char src4[64];
+    char tmp[64];
+    char tmp2[64];
+    char tmp3[64];
+
+    // !!! FIXME: this code counts on the register not having swizzles, etc.
+    const int stage = ctx->dest_arg.regnum;
+    allocate_ARB1_scratch_reg_name(ctx, tmp, sizeof (tmp));
+    allocate_ARB1_scratch_reg_name(ctx, tmp2, sizeof (tmp2));
+    allocate_ARB1_scratch_reg_name(ctx, tmp3, sizeof (tmp3));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst0,
+                            src0, sizeof (src0));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src0,
+                            src1, sizeof (src1));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_dst1,
+                            src2, sizeof (src2));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->texm3x3pad_src1,
+                            src3, sizeof (src3));
+    get_ARB1_varname_in_buf(ctx, REG_TYPE_TEXTURE, ctx->source_args[0].regnum,
+                            src4, sizeof (src4));
+    get_ARB1_destarg_varname(ctx, dst, sizeof (dst));
+
+    output_line(ctx, "MOV %s.x, %s.w;", tmp3, src0);
+    output_line(ctx, "MOV %s.y, %s.w;", tmp3, src2);
+    output_line(ctx, "MOV %s.z, %s.w;", tmp3, dst);
+    output_line(ctx, "DP3 %s.z, %s, %s;", dst, dst, src4);
+    output_line(ctx, "DP3 %s.x, %s, %s;", dst, src0, src1);
+    output_line(ctx, "DP3 %s.y, %s, %s;", dst, src2, src3);
+    output_line(ctx, "MUL %s, %s, %s;", tmp, dst, dst);    // normal * normal
+    output_line(ctx, "MUL %s, %s, %s;", tmp2, dst, tmp3);  // normal * eyeray
+    output_line(ctx, "DIV %s, %s, %s;", tmp, tmp, tmp2);
+    output_line(ctx, "MUL %s, %s, { 2.0, 2.0, 2.0, 2.0 };", tmp, tmp);
+    output_line(ctx, "MAD %s, %s, %s, -%s;", tmp, tmp, dst, tmp3);
+    output_line(ctx, "TEX %s, %s, texture[%d], 3D;", dst, tmp, stage);
+    emit_ARB1_dest_modifiers(ctx);
+} // emit_ARB1_TEXM3X3VSPEC
 
 static void emit_ARB1_EXPP(Context *ctx) { emit_ARB1_opcode_ds(ctx, "EX2"); }
 static void emit_ARB1_LOGP(Context *ctx) { arb1_log(ctx, "LG2"); }
@@ -5578,7 +5939,7 @@
 {
     if (!shader_version_atleast(ctx, 1, 4))
     {
-        // Note that this code counts on the register not having swizzles, etc.
+        // !!! FIXME: this code counts on the register not having swizzles, etc.
         DestArgInfo *info = &ctx->dest_arg;
         char dst[64]; get_ARB1_destarg_varname(ctx, dst, sizeof (dst));
         output_line(ctx, "TEX %s, %s, texture[%d], 2D;",
@@ -6980,6 +7341,36 @@
     // !!! FIXME: there are further limitations in ps_1_3 and earlier.
 } // state_TEXKILL
 
+// Some rules that apply to some of the fruity ps_1_1 texture opcodes...
+static void state_texops(Context *ctx, const char *opcode,
+                         const int dims, const int texbem)
+{
+    const DestArgInfo *dst = &ctx->dest_arg;
+    const SourceArgInfo *src = &ctx->source_args[0];
+    if (dst->regtype != REG_TYPE_TEXTURE)
+        failf(ctx, "%s destination must be a texture register", opcode);
+    if (src->regtype != REG_TYPE_TEXTURE)
+        failf(ctx, "%s source must be a texture register", opcode);
+    if (src->regnum >= dst->regnum)  // so says MSDN.
+        failf(ctx, "%s dest must be a higher register than source", opcode);
+
+    if (dims)
+    {
+        TextureType ttyp = (dims == 2) ? TEXTURE_TYPE_2D : TEXTURE_TYPE_VOLUME;
+        add_sampler(ctx, REG_TYPE_SAMPLER, dst->regnum, ttyp, texbem);
+    } // if
+
+    add_attribute_register(ctx, REG_TYPE_TEXTURE, dst->regnum,
+                           MOJOSHADER_USAGE_TEXCOORD, dst->regnum, 0xF, 0);
+
+    // Strictly speaking, there should be a TEX opcode prior to this call that
+    //  should fill in this metadata, but I'm not sure that's required for the
+    //  shader to assemble in D3D, so we'll do this so we don't fail with a
+    //  cryptic error message even if the developer didn't do the TEX.
+    add_attribute_register(ctx, REG_TYPE_TEXTURE, src->regnum,
+                           MOJOSHADER_USAGE_TEXCOORD, src->regnum, 0xF, 0);
+} // state_texops
+
 static void state_texbem(Context *ctx, const char *opcode)
 {
     // The TEXBEM equasion, according to MSDN:
@@ -7007,26 +7398,7 @@
     // !!! FIXME:  or texbeml instruction cannot be read later,
     // !!! FIXME:  except by another texbem or texbeml.
 
-    const DestArgInfo *dst = &ctx->dest_arg;
-    const SourceArgInfo *src = &ctx->source_args[0];
-    if (dst->regtype != REG_TYPE_TEXTURE)
-        failf(ctx, "%s destination must be a texture register", opcode);
-    if (src->regtype != REG_TYPE_TEXTURE)
-        failf(ctx, "%s source must be a texture register", opcode);
-    if (src->regnum >= dst->regnum)  // so says MSDN.
-        failf(ctx, "%s dest must be a higher register than source", opcode);
-
-    add_sampler(ctx, REG_TYPE_SAMPLER, dst->regnum, TEXTURE_TYPE_2D, 1);
-    add_attribute_register(ctx, REG_TYPE_TEXTURE, dst->regnum,
-                           MOJOSHADER_USAGE_TEXCOORD, dst->regnum, 0xF, 0);
-
-    // Strictly speaking, there should be a TEX opcode prior to this call that
-    //  should fill in this metadata, but I'm not sure that's required for the
-    //  shader to assemble in D3D, so we'll do this so we don't fail with a
-    //  cryptic error message even if the developer didn't do the TEX.
-    add_sampler(ctx, REG_TYPE_SAMPLER, src->regnum, TEXTURE_TYPE_2D, 0);
-    add_attribute_register(ctx, REG_TYPE_TEXTURE, src->regnum,
-                           MOJOSHADER_USAGE_TEXCOORD, src->regnum, 0xF, 0);
+    state_texops(ctx, opcode, 2, 1);
 } // state_texbem
 
 static void state_TEXBEM(Context *ctx)
@@ -7039,6 +7411,82 @@
     state_texbem(ctx, "TEXBEML");
 } // state_TEXBEML
 
+static void state_TEXM3X2PAD(Context *ctx)
+{
+    if (shader_version_atleast(ctx, 1, 4))
+        fail(ctx, "TEXM3X2PAD opcode not available after Shader Model 1.3");
+    state_texops(ctx, "TEXM3X2PAD", 0, 0);
+    // !!! FIXME: check for correct opcode existance and order more rigorously?
+    ctx->texm3x2pad_src0 = ctx->source_args[0].regnum;
+    ctx->texm3x2pad_dst0 = ctx->dest_arg.regnum;
+} // state_TEXM3X2PAD
+
+static void state_TEXM3X2TEX(Context *ctx)
+{
+    if (shader_version_atleast(ctx, 1, 4))
+        fail(ctx, "TEXM3X2TEX opcode not available after Shader Model 1.3");
+    if (ctx->texm3x2pad_dst0 == -1)
+        fail(ctx, "TEXM3X2TEX opcode without matching TEXM3X2PAD");
+    // !!! FIXME: check for correct opcode existance and order more rigorously?
+    state_texops(ctx, "TEXM3X2TEX", 2, 0);
+    ctx->reset_texmpad = 1;
+} // state_TEXM3X2TEX
+
+static void state_TEXM3X3PAD(Context *ctx)
+{
+    if (shader_version_atleast(ctx, 1, 4))
+        fail(ctx, "TEXM3X2TEX opcode not available after Shader Model 1.3");
+    state_texops(ctx, "TEXM3X3PAD", 0, 0);
+
+    // !!! FIXME: check for correct opcode existance and order more rigorously?
+    if (ctx->texm3x3pad_dst0 == -1)
+    {
+        ctx->texm3x3pad_src0 = ctx->source_args[0].regnum;
+        ctx->texm3x3pad_dst0 = ctx->dest_arg.regnum;
+    } // if
+    else if (ctx->texm3x3pad_dst1 == -1)
+    {
+        ctx->texm3x3pad_src1 = ctx->source_args[0].regnum;
+        ctx->texm3x3pad_dst1 = ctx->dest_arg.regnum;
+    } // else
+} // state_TEXM3X3PAD
+
+static void state_texm3x3(Context *ctx, const char *opcode)
+{
+    // !!! FIXME: check for correct opcode existance and order more rigorously?
+    if (shader_version_atleast(ctx, 1, 4))
+        failf(ctx, "%s opcode not available after Shader Model 1.3", opcode);
+    if (ctx->texm3x3pad_dst1 == -1)
+        failf(ctx, "%s opcode without matching TEXM3X3PADs", opcode);
+    state_texops(ctx, opcode, 3, 0);
+    ctx->reset_texmpad = 1;
+} // state_texm3x3
+
+static void state_TEXM3X3(Context *ctx)
+{
+    if (!shader_version_atleast(ctx, 1, 2))
+        fail(ctx, "TEXM3X3 opcode not available in Shader Model 1.1");
+    state_texm3x3(ctx, "TEXM3X3");
+} // state_TEXM3X3
+
+static void state_TEXM3X3TEX(Context *ctx)
+{
+    state_texm3x3(ctx, "TEXM3X3TEX");
+} // state_TEXM3X3TEX
+
+static void state_TEXM3X3SPEC(Context *ctx)
+{
+    state_texm3x3(ctx, "TEXM3X3SPEC");
+    if (ctx->source_args[1].regtype != REG_TYPE_CONST)
+        fail(ctx, "TEXM3X3SPEC final arg must be a constant register");
+} // state_TEXM3X3SPEC
+
+static void state_TEXM3X3VSPEC(Context *ctx)
+{
+    state_texm3x3(ctx, "TEXM3X3VSPEC");
+} // state_TEXM3X3VSPEC
+
+
 static void state_TEXLD(Context *ctx)
 {
     if (shader_version_atleast(ctx, 2, 0))
@@ -7229,6 +7677,17 @@
     if (!isfail(ctx))
         emitter(ctx);  // call the profile's emitter.
 
+    if (ctx->reset_texmpad)
+    {
+        ctx->texm3x2pad_dst0 = -1;
+        ctx->texm3x2pad_src0 = -1;
+        ctx->texm3x3pad_dst0 = -1;
+        ctx->texm3x3pad_src0 = -1;
+        ctx->texm3x3pad_dst1 = -1;
+        ctx->texm3x3pad_src1 = -1;
+        ctx->reset_texmpad = 0;
+    } // if
+
     ctx->previous_opcode = opcode;
     ctx->scratch_registers = 0;  // reset after every instruction.
 
@@ -7972,6 +8431,12 @@
     ctx->endline_len = strlen(ctx->endline);
     ctx->last_address_reg_component = -1;
     ctx->current_position = MOJOSHADER_POSITION_BEFORE;
+    ctx->texm3x2pad_dst0 = -1;
+    ctx->texm3x2pad_src0 = -1;
+    ctx->texm3x3pad_dst0 = -1;
+    ctx->texm3x3pad_src0 = -1;
+    ctx->texm3x3pad_dst1 = -1;
+    ctx->texm3x3pad_src1 = -1;
 
     ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);
     if (ctx->errors == NULL)