[3/3] d3dx9: Shader assembler <= ps_1_3 support.

Matteo Bruni matteo.mystral at gmail.com
Wed Jul 21 10:20:18 CDT 2010


This patch pretty much completes the D3D shader assembler. Many many
thanks to all the people involved in lengthy code reviews and
especially to Stefan for the original assembler (it wasn't too
different from what is in Wine now).
-------------- next part --------------
From cf9ecf1c38480c92893e529ec54ec672b7258e44 Mon Sep 17 00:00:00 2001
From: Matteo Bruni <matteo.mystral at gmail.com>
Date: Wed, 21 Jul 2010 17:07:24 +0200
Subject: d3dx9: Shader assembler <= ps_1_3 support.

---
 dlls/d3dx9_36/asmparser.c        |  381 ++++++++++++++++++++++++++++++++++----
 dlls/d3dx9_36/asmshader.y        |   12 +-
 dlls/d3dx9_36/bytecodewriter.c   |  314 ++++++++++++++++++++++++++++++-
 dlls/d3dx9_36/d3dx9_36_private.h |   10 +
 dlls/d3dx9_36/tests/asm.c        |   46 +++++-
 5 files changed, 713 insertions(+), 50 deletions(-)

diff --git a/dlls/d3dx9_36/asmparser.c b/dlls/d3dx9_36/asmparser.c
index dfa8362..b6f4fde 100644
--- a/dlls/d3dx9_36/asmparser.c
+++ b/dlls/d3dx9_36/asmparser.c
@@ -226,6 +226,86 @@ static void asmparser_sincos(struct asm_parser *This, DWORD mod, DWORD shift,
     }
 }
 
+static struct shader_reg map_oldps_register(const struct shader_reg *reg, BOOL tex_varying) {
+    struct shader_reg ret;
+    switch(reg->type) {
+        case BWRITERSPR_TEXTURE:
+            if(tex_varying) {
+                ret = *reg;
+                ret.type = BWRITERSPR_INPUT;
+                switch(reg->regnum) {
+                    case 0:     ret.regnum = T0_VARYING; break;
+                    case 1:     ret.regnum = T1_VARYING; break;
+                    case 2:     ret.regnum = T2_VARYING; break;
+                    case 3:     ret.regnum = T3_VARYING; break;
+                    case 4:     ret.regnum = T4_VARYING; break;
+                    case 5:     ret.regnum = T5_VARYING; break;
+                    case 6:     ret.regnum = T6_VARYING; break;
+                    case 7:     ret.regnum = T7_VARYING; break;
+                    default:
+                        FIXME("Unexpected TEXTURE register t%u\n", reg->regnum);
+                        return *reg;
+                }
+                return ret;
+            } else {
+                ret = *reg;
+                ret.type = BWRITERSPR_TEMP;
+                switch(reg->regnum) {
+                    case 0:     ret.regnum = T0_REG; break;
+                    case 1:     ret.regnum = T1_REG; break;
+                    case 2:     ret.regnum = T2_REG; break;
+                    case 3:     ret.regnum = T3_REG; break;
+                    default:
+                        FIXME("Unexpected TEXTURE register t%u\n", reg->regnum);
+                        return *reg;
+                }
+                return ret;
+            }
+
+        /* case BWRITERSPR_INPUT - Identical mapping of 1.x/2.0 color varyings
+           to 3.0 ones */
+
+        default: return *reg;
+    }
+}
+
+static void asmparser_texcoord(struct asm_parser *This, DWORD mod, DWORD shift,
+                               const struct shader_reg *dst,
+                               const struct src_regs *srcs) {
+    struct instruction *instr;
+
+    if(srcs) {
+        asmparser_message(This, "Line %u: Source registers in texcoord instruction\n", This->line_no);
+        set_parse_status(This, PARSE_ERR);
+        return;
+    }
+
+    instr = alloc_instr(1);
+    if(!instr) {
+        ERR("Error allocating memory for the instruction\n");
+        set_parse_status(This, PARSE_ERR);
+        return;
+    }
+
+    /* texcoord copies the texture coord data into a temporary register-like
+     * readable form. In newer shader models this equals a MOV from v0 to r0,
+     * record it as this.
+     */
+    instr->opcode = BWRITERSIO_MOV;
+    instr->dstmod = mod | BWRITERSPDM_SATURATE; /* texcoord clamps to [0;1] */
+    instr->shift = shift;
+    instr->comptype = 0;
+
+    This->funcs->dstreg(This, instr, dst);
+    /* The src reg needs special care */
+    instr->src[0] = map_oldps_register(dst, TRUE);
+
+    if(!add_instruction(This->shader, instr)) {
+        ERR("Out of memory\n");
+        set_parse_status(This, PARSE_ERR);
+    }
+}
+
 static void asmparser_texcrd(struct asm_parser *This, DWORD mod, DWORD shift,
                              const struct shader_reg *dst,
                              const struct src_regs *srcs) {
@@ -259,6 +339,82 @@ static void asmparser_texcrd(struct asm_parser *This, DWORD mod, DWORD shift,
     }
 }
 
+static void asmparser_texkill(struct asm_parser *This,
+                              const struct shader_reg *dst) {
+    struct instruction *instr = alloc_instr(0);
+
+    if(!instr) {
+        ERR("Error allocating memory for the instruction\n");
+        set_parse_status(This, PARSE_ERR);
+        return;
+    }
+
+    instr->opcode = BWRITERSIO_TEXKILL;
+    instr->dstmod = 0;
+    instr->shift = 0;
+    instr->comptype = 0;
+
+    /* Do not run the dst register through the normal
+     * register conversion. If used with ps_1_0 to ps_1_3
+     * the texture coordinate from that register is used,
+     * not the temporary register value. In ps_1_4 and
+     * ps_2_0 t0 is always a varying and temporaries can
+     * be used with texkill.
+     */
+    instr->dst = map_oldps_register(dst, TRUE);
+    instr->has_dst = TRUE;
+
+    if(!add_instruction(This->shader, instr)) {
+        ERR("Out of memory\n");
+        set_parse_status(This, PARSE_ERR);
+    }
+}
+
+static void asmparser_texhelper(struct asm_parser *This, DWORD mod, DWORD shift,
+                                const struct shader_reg *dst,
+                                const struct shader_reg *src0) {
+    struct instruction *instr = alloc_instr(2);
+
+    if(!instr) {
+        ERR("Error allocating memory for the instruction\n");
+        set_parse_status(This, PARSE_ERR);
+        return;
+    }
+
+    instr->opcode = BWRITERSIO_TEX;
+    instr->dstmod = mod;
+    instr->shift = shift;
+    instr->comptype = 0;
+    /* The dest register can be mapped normally to a temporary register */
+    This->funcs->dstreg(This, instr, dst);
+    /* Use the src passed as parameter by the specific instruction handler */
+    instr->src[0] = *src0;
+
+    /* The 2nd source register is the sampler register with the
+     * destination's regnum
+     */
+    ZeroMemory(&instr->src[1], sizeof(instr->src[1]));
+    instr->src[1].type = BWRITERSPR_SAMPLER;
+    instr->src[1].regnum = dst->regnum;
+    instr->src[1].swizzle = BWRITERVS_NOSWIZZLE;
+    instr->src[1].srcmod = BWRITERSPSM_NONE;
+    instr->src[1].rel_reg = NULL;
+
+    if(!add_instruction(This->shader, instr)) {
+        ERR("Out of memory\n");
+        set_parse_status(This, PARSE_ERR);
+    }
+}
+
+static void asmparser_tex(struct asm_parser *This, DWORD mod, DWORD shift,
+                          const struct shader_reg *dst) {
+    struct shader_reg src;
+
+    /* The first source register is the varying containing the coordinate */
+    src = map_oldps_register(dst, TRUE);
+    asmparser_texhelper(This, mod, shift, dst, &src);
+}
+
 static void asmparser_texld14(struct asm_parser *This, DWORD mod, DWORD shift,
                               const struct shader_reg *dst,
                               const struct src_regs *srcs) {
@@ -304,6 +460,46 @@ static void asmparser_texld14(struct asm_parser *This, DWORD mod, DWORD shift,
     }
 }
 
+static void asmparser_texreg2ar(struct asm_parser *This, DWORD mod, DWORD shift,
+                                const struct shader_reg *dst,
+                                const struct shader_reg *src0) {
+    struct shader_reg src;
+
+    src = map_oldps_register(src0, FALSE);
+    /* Supply the correct swizzle */
+    src.swizzle = BWRITERVS_X_W | BWRITERVS_Y_X | BWRITERVS_Z_X | BWRITERVS_W_X;
+    asmparser_texhelper(This, mod, shift, dst, &src);
+}
+
+static void asmparser_texreg2gb(struct asm_parser *This, DWORD mod, DWORD shift,
+                                const struct shader_reg *dst,
+                                const struct shader_reg *src0) {
+    struct shader_reg src;
+
+    src = map_oldps_register(src0, FALSE);
+    /* Supply the correct swizzle */
+    src.swizzle = BWRITERVS_X_Y | BWRITERVS_Y_Z | BWRITERVS_Z_Z | BWRITERVS_W_Z;
+    asmparser_texhelper(This, mod, shift, dst, &src);
+}
+
+static void asmparser_texreg2rgb(struct asm_parser *This, DWORD mod, DWORD shift,
+                                 const struct shader_reg *dst,
+                                 const struct shader_reg *src0) {
+    struct shader_reg src;
+
+    src = map_oldps_register(src0, FALSE);
+    /* Supply the correct swizzle */
+    src.swizzle = BWRITERVS_X_X | BWRITERVS_Y_Y | BWRITERVS_Z_Z | BWRITERVS_W_Z;
+    asmparser_texhelper(This, mod, shift, dst, &src);
+}
+
+/* Complex pixel shader 1.3 instructions like texm3x3tex are tricky - the
+ * bytecode writer works instruction by instruction, so we can't properly
+ * convert these from/to equivalent ps_3_0 instructions. Then simply keep using
+ * the ps_1_3 opcodes and just adapt the registers in the common fashion (i.e.
+ * go through asmparser_instr).
+ */
+
 static void asmparser_instr(struct asm_parser *This, DWORD opcode,
                             DWORD mod, DWORD shift,
                             BWRITER_COMPARISON_TYPE comp,
@@ -345,12 +541,20 @@ ns */
 	    break;
         case BWRITERSIO_TEXCOORD:
             /* texcoord/texcrd are two instructions present only in PS <= 1.3 and PS 1.4 respectively */
-            asmparser_texcrd(This, mod, shift, dst, srcs);
+            if(This->shader->version == BWRITERPS_VERSION(1, 4))
+                asmparser_texcrd(This, mod, shift, dst, srcs);
+            else asmparser_texcoord(This, mod, shift, dst, srcs);
             return;
         case BWRITERSIO_TEX:
             /* this encodes both the tex PS 1.x instruction and the
                texld 1.4/2.0+ instruction */
-            if(This->shader->version == BWRITERPS_VERSION(1, 4)) {
+            if(This->shader->version == BWRITERPS_VERSION(1, 1) ||
+               This->shader->version == BWRITERPS_VERSION(1, 2) ||
+               This->shader->version == BWRITERPS_VERSION(1, 3)) {
+                asmparser_tex(This, mod, shift, dst);
+                return;
+            }
+            else if(This->shader->version == BWRITERPS_VERSION(1, 4)) {
                 asmparser_texld14(This, mod, shift, dst, srcs);
                 return;
             }
@@ -364,6 +568,22 @@ ns */
         return;
     }
 
+    /* Handle PS 1.x instructions, "regularizing" them */
+    switch(opcode) {
+        case BWRITERSIO_TEXKILL:
+            asmparser_texkill(This, dst);
+            return;
+        case BWRITERSIO_TEXREG2AR:
+            asmparser_texreg2ar(This, mod, shift, dst, &srcs->reg[0]);
+            return;
+        case BWRITERSIO_TEXREG2GB:
+            asmparser_texreg2gb(This, mod, shift, dst, &srcs->reg[0]);
+            return;
+        case BWRITERSIO_TEXREG2RGB:
+            asmparser_texreg2rgb(This, mod, shift, dst, &srcs->reg[0]);
+            return;
+    }
+
     instr = alloc_instr(src_count);
     if(!instr) {
         ERR("Error allocating memory for the instruction\n");
@@ -444,39 +664,6 @@ static struct shader_reg map_oldvs_register(const struct shader_reg *reg) {
     }
 }
 
-static struct shader_reg map_oldps_register(const struct shader_reg *reg, BOOL tex_varying) {
-    struct shader_reg ret;
-    switch(reg->type) {
-        case BWRITERSPR_TEXTURE:
-            if(tex_varying) {
-                ret = *reg;
-                ret.type = BWRITERSPR_INPUT;
-                switch(reg->regnum) {
-                    case 0:     ret.regnum = T0_VARYING; break;
-                    case 1:     ret.regnum = T1_VARYING; break;
-                    case 2:     ret.regnum = T2_VARYING; break;
-                    case 3:     ret.regnum = T3_VARYING; break;
-                    case 4:     ret.regnum = T4_VARYING; break;
-                    case 5:     ret.regnum = T5_VARYING; break;
-                    case 6:     ret.regnum = T6_VARYING; break;
-                    case 7:     ret.regnum = T7_VARYING; break;
-                    default:
-                        FIXME("Unexpected TEXTURE register t%u\n", reg->regnum);
-                        return *reg;
-                }
-                return ret;
-            } else {
-                FIXME("TODO: ps_1_x texture register mapping\n");
-                return *reg;
-            }
-
-        /* case BWRITERSPR_INPUT - Identical mapping of 1.x/2.0 color varyings
-           to 3.0 ones */
-
-        default: return *reg;
-    }
-}
-
 /* Checks for unsupported source modifiers in VS (all versions) or
    PS 2.0 and newer */
 static void check_legacy_srcmod(struct asm_parser *This, DWORD srcmod) {
@@ -660,6 +847,30 @@ static void asmparser_srcreg_vs_3(struct asm_parser *This,
     memcpy(&instr->src[num], src, sizeof(*src));
 }
 
+static const struct allowed_reg_type ps_1_0123_reg_allowed[] = {
+    { BWRITERSPR_CONST,     8,  FALSE },
+    { BWRITERSPR_TEMP,      2,  FALSE },
+    { BWRITERSPR_TEXTURE,   4,  FALSE },
+    { BWRITERSPR_INPUT,     2,  FALSE },
+    { ~0U, 0 } /* End tag */
+};
+
+static void asmparser_srcreg_ps_1_0123(struct asm_parser *This,
+                                       struct instruction *instr, int num,
+                                       const struct shader_reg *src) {
+    struct shader_reg reg;
+
+    if(!check_reg_type(src, ps_1_0123_reg_allowed)) {
+        asmparser_message(This, "Line %u: Source register %s not supported in <== PS 1.3\n",
+                          This->line_no,
+                          debug_print_srcreg(src));
+        set_parse_status(This, PARSE_ERR);
+    }
+    check_abs_srcmod(This, src->srcmod);
+    reg = map_oldps_register(src, FALSE);
+    memcpy(&instr->src[num], &reg, sizeof(reg));
+}
+
 static const struct allowed_reg_type ps_1_4_reg_allowed[] = {
     { BWRITERSPR_CONST,     8,  FALSE },
     { BWRITERSPR_TEMP,      6,  FALSE },
@@ -827,6 +1038,22 @@ static void asmparser_dstreg_vs_3(struct asm_parser *This,
     instr->has_dst = TRUE;
 }
 
+static void asmparser_dstreg_ps_1_0123(struct asm_parser *This,
+                                       struct instruction *instr,
+                                       const struct shader_reg *dst) {
+    struct shader_reg reg;
+
+    if(!check_reg_type(dst, ps_1_0123_reg_allowed)) {
+        asmparser_message(This, "Line %u: Destination register %s not supported in PS 1\n",
+                          This->line_no,
+                          debug_print_dstreg(dst));
+        set_parse_status(This, PARSE_ERR);
+    }
+    reg = map_oldps_register(dst, FALSE);
+    memcpy(&instr->dst, &reg, sizeof(reg));
+    instr->has_dst = TRUE;
+}
+
 static void asmparser_dstreg_ps_1_4(struct asm_parser *This,
                                     struct instruction *instr,
                                     const struct shader_reg *dst) {
@@ -981,6 +1208,26 @@ static const struct asmparser_backend parser_vs_3 = {
     asmparser_instr,
 };
 
+static const struct asmparser_backend parser_ps_1_0123 = {
+    asmparser_constF,
+    asmparser_constI,
+    asmparser_constB,
+
+    asmparser_dstreg_ps_1_0123,
+    asmparser_srcreg_ps_1_0123,
+
+    asmparser_predicate_unsupported,
+    asmparser_coissue_supported,
+
+    asmparser_dcl_output_unsupported,
+    asmparser_dcl_input_unsupported,
+    asmparser_dcl_sampler_unsupported,
+
+    asmparser_end,
+
+    asmparser_instr,
+};
+
 static const struct asmparser_backend parser_ps_1_4 = {
     asmparser_constF,
     asmparser_constI,
@@ -1171,6 +1418,70 @@ void create_vs30_parser(struct asm_parser *ret) {
     ret->funcs = &parser_vs_3;
 }
 
+void create_ps10_parser(struct asm_parser *ret) {
+    TRACE_(parsed_shader)("ps_1_0\n");
+
+    ret->shader = asm_alloc(sizeof(*ret->shader));
+    if(!ret->shader) {
+        ERR("Failed to allocate memory for the shader\n");
+        set_parse_status(ret, PARSE_ERR);
+        return;
+    }
+
+    ret->shader->type = ST_PIXEL;
+    ret->shader->version = BWRITERPS_VERSION(1, 0);
+    ret->funcs = &parser_ps_1_0123;
+    gen_oldps_input(ret->shader, 4);
+}
+
+void create_ps11_parser(struct asm_parser *ret) {
+    TRACE_(parsed_shader)("ps_1_1\n");
+
+    ret->shader = asm_alloc(sizeof(*ret->shader));
+    if(!ret->shader) {
+        ERR("Failed to allocate memory for the shader\n");
+        set_parse_status(ret, PARSE_ERR);
+        return;
+    }
+
+    ret->shader->type = ST_PIXEL;
+    ret->shader->version = BWRITERPS_VERSION(1, 1);
+    ret->funcs = &parser_ps_1_0123;
+    gen_oldps_input(ret->shader, 4);
+}
+
+void create_ps12_parser(struct asm_parser *ret) {
+    TRACE_(parsed_shader)("ps_1_2\n");
+
+    ret->shader = asm_alloc(sizeof(*ret->shader));
+    if(!ret->shader) {
+        ERR("Failed to allocate memory for the shader\n");
+        set_parse_status(ret, PARSE_ERR);
+        return;
+    }
+
+    ret->shader->type = ST_PIXEL;
+    ret->shader->version = BWRITERPS_VERSION(1, 2);
+    ret->funcs = &parser_ps_1_0123;
+    gen_oldps_input(ret->shader, 4);
+}
+
+void create_ps13_parser(struct asm_parser *ret) {
+    TRACE_(parsed_shader)("ps_1_3\n");
+
+    ret->shader = asm_alloc(sizeof(*ret->shader));
+    if(!ret->shader) {
+        ERR("Failed to allocate memory for the shader\n");
+        set_parse_status(ret, PARSE_ERR);
+        return;
+    }
+
+    ret->shader->type = ST_PIXEL;
+    ret->shader->version = BWRITERPS_VERSION(1, 3);
+    ret->funcs = &parser_ps_1_0123;
+    gen_oldps_input(ret->shader, 4);
+}
+
 void create_ps14_parser(struct asm_parser *ret) {
     TRACE_(parsed_shader)("ps_1_4\n");
 
diff --git a/dlls/d3dx9_36/asmshader.y b/dlls/d3dx9_36/asmshader.y
index 587f19f..d1a05db 100644
--- a/dlls/d3dx9_36/asmshader.y
+++ b/dlls/d3dx9_36/asmshader.y
@@ -329,26 +329,22 @@ version_marker:       VER_VS10
                     | VER_PS10
                         {
                             TRACE("Pixel  shader 1.0\n");
-                            set_parse_status(&asm_ctx, PARSE_ERR);
-                            YYABORT;
+                            create_ps10_parser(&asm_ctx);
                         }
                     | VER_PS11
                         {
                             TRACE("Pixel  shader 1.1\n");
-                            set_parse_status(&asm_ctx, PARSE_ERR);
-                            YYABORT;
+                            create_ps11_parser(&asm_ctx);
                         }
                     | VER_PS12
                         {
                             TRACE("Pixel  shader 1.2\n");
-                            set_parse_status(&asm_ctx, PARSE_ERR);
-                            YYABORT;
+                            create_ps12_parser(&asm_ctx);
                         }
                     | VER_PS13
                         {
                             TRACE("Pixel  shader 1.3\n");
-                            set_parse_status(&asm_ctx, PARSE_ERR);
-                            YYABORT;
+                            create_ps13_parser(&asm_ctx);
                         }
                     | VER_PS14
                         {
diff --git a/dlls/d3dx9_36/bytecodewriter.c b/dlls/d3dx9_36/bytecodewriter.c
index 5034c7b..07f96e7 100644
--- a/dlls/d3dx9_36/bytecodewriter.c
+++ b/dlls/d3dx9_36/bytecodewriter.c
@@ -578,7 +578,7 @@ static HRESULT find_ps_builtin_semantics(struct bc_writer *This,
     return S_OK;
 }
 
-static void ps_1_4_header(struct bc_writer *This, const struct bwriter_shader *shader, struct bytecode_buffer *buffer) {
+static void ps_1_x_header(struct bc_writer *This, const struct bwriter_shader *shader, struct bytecode_buffer *buffer) {
     HRESULT hr;
 
     /* First check the constants and varyings, and complain if unsupported things are used */
@@ -589,6 +589,27 @@ static void ps_1_4_header(struct bc_writer *This, const struct bwriter_shader *s
         return;
     }
 
+    hr = find_ps_builtin_semantics(This, shader, 4);
+    if(FAILED(hr)) {
+        This->state = hr;
+        return;
+    }
+
+    /* Declare the shader type and version */
+    put_dword(buffer, This->version);
+    write_constF(shader, buffer, TRUE);
+}
+
+static void ps_1_4_header(struct bc_writer *This, const struct bwriter_shader *shader, struct bytecode_buffer *buffer) {
+    HRESULT hr;
+
+    /* First check the constants and varyings, and complain if unsupported things are used */
+    if(shader->num_ci || shader->num_cb) {
+        WARN("Int and bool constants are not supported in shader model 1 shaders\n");
+        WARN("Got %u int and %u boolean constants\n", shader->num_ci, shader->num_cb);
+        This->state = E_INVALIDARG;
+        return;
+    }
     hr = find_ps_builtin_semantics(This, shader, 6);
     if(FAILED(hr)) {
         This->state = hr;
@@ -808,6 +829,27 @@ static void write_srcregs(struct bc_writer *This, const struct instruction *inst
     }
 }
 
+static DWORD map_ps13_temp(struct bc_writer *This, const struct shader_reg *reg) {
+    DWORD token = 0;
+    if(reg->regnum == T0_REG) {
+        token |= (D3DSPR_TEXTURE << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= 0 & D3DSP_REGNUM_MASK; /* No shift */
+    } else if(reg->regnum == T1_REG) {
+        token |= (D3DSPR_TEXTURE << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= 1 & D3DSP_REGNUM_MASK; /* No shift */
+    } else if(reg->regnum == T2_REG) {
+        token |= (D3DSPR_TEXTURE << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= 2 & D3DSP_REGNUM_MASK; /* No shift */
+    } else if(reg->regnum == T3_REG) {
+        token |= (D3DSPR_TEXTURE << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= 3 & D3DSP_REGNUM_MASK; /* No shift */
+    } else {
+        token |= (D3DSPR_TEMP << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= reg->regnum & D3DSP_REGNUM_MASK; /* No shift */
+    }
+    return token;
+}
+
 static DWORD map_ps_input(struct bc_writer *This,
                           const struct shader_reg *reg) {
     DWORD i, token = 0;
@@ -832,6 +874,86 @@ static DWORD map_ps_input(struct bc_writer *This,
     return token;
 }
 
+static void ps_1_0123_srcreg(struct bc_writer *This, const struct shader_reg *reg,
+                             struct bytecode_buffer *buffer) {
+    DWORD token = (1 << 31); /* Bit 31 of registers is 1 */
+    if(reg->rel_reg) {
+        WARN("Relative addressing not supported in <= ps_3_0\n");
+        This->state = E_INVALIDARG;
+        return;
+    }
+
+    switch(reg->type) {
+        case BWRITERSPR_INPUT:
+            token |= map_ps_input(This, reg);
+            break;
+
+            /* Take care about the texture temporaries. There's a problem: They aren't
+             * declared anywhere, so we can only hardcode the values that are used
+             * to map ps_1_3 shaders to the common shader structure
+             */
+        case BWRITERSPR_TEMP:
+            token |= map_ps13_temp(This, reg);
+            break;
+
+        case BWRITERSPR_CONST: /* Can be mapped 1:1 */
+            token |= (reg->type << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= reg->regnum & D3DSP_REGNUM_MASK; /* No shift */
+            break;
+
+        default:
+            WARN("Invalid register type for <= ps_1_3 shader\n");
+            This->state = E_INVALIDARG;
+            return;
+    }
+
+    token |= d3d9_swizzle(reg->swizzle) & D3DVS_SWIZZLE_MASK; /* already shifted */
+
+    if(reg->srcmod == BWRITERSPSM_DZ || reg->srcmod == BWRITERSPSM_DW ||
+       reg->srcmod == BWRITERSPSM_ABS || reg->srcmod == BWRITERSPSM_ABSNEG ||
+       reg->srcmod == BWRITERSPSM_NOT) {
+        WARN("Invalid source modifier %u for <= ps_1_3\n", reg->srcmod);
+        This->state = E_INVALIDARG;
+        return;
+    }
+    token |= d3d9_srcmod(reg->srcmod);
+    put_dword(buffer, token);
+}
+
+static void ps_1_0123_dstreg(struct bc_writer *This, const struct shader_reg *reg,
+                             struct bytecode_buffer *buffer,
+                             DWORD shift, DWORD mod) {
+    DWORD token = (1 << 31); /* Bit 31 of registers is 1 */
+
+    if(reg->rel_reg) {
+        WARN("Relative addressing not supported for destination registers\n");
+        This->state = E_INVALIDARG;
+        return;
+    }
+
+    switch(reg->type) {
+        case BWRITERSPR_TEMP:
+            token |= map_ps13_temp(This, reg);
+            break;
+
+        /* texkill uses the input register as a destination parameter */
+        case BWRITERSPR_INPUT:
+            token |= map_ps_input(This, reg);
+            break;
+
+        default:
+            WARN("Invalid dest register type for 1.x pshader\n");
+            This->state = E_INVALIDARG;
+            return;
+    }
+
+    token |= (shift << D3DSP_DSTSHIFT_SHIFT) & D3DSP_DSTSHIFT_MASK;
+    token |= d3d9_dstmod(mod);
+
+    token |= d3d9_writemask(reg->writemask);
+    put_dword(buffer, token);
+}
+
 /* The length of an instruction consists of the destination register (if any),
  * the number of source registers, the number of address registers used for
  * indirect addressing, and optionally the predicate register
@@ -910,6 +1032,168 @@ static const struct bytecode_backend vs_1_x_backend = {
     vs_1_x_handlers
 };
 
+static void instr_ps_1_0123_texld(struct bc_writer *This,
+                                  const struct instruction *instr,
+                                  struct bytecode_buffer *buffer) {
+    DWORD idx, srcidx;
+    struct shader_reg reg;
+    DWORD swizzlemask;
+
+    if(instr->src[1].type != BWRITERSPR_SAMPLER ||
+       instr->src[1].regnum > 3) {
+        WARN("Unsupported sampler type %u regnum %u\n",
+             instr->src[1].type, instr->src[1].regnum);
+        This->state = E_INVALIDARG;
+        return;
+    } else if(instr->dst.type != BWRITERSPR_TEMP) {
+        WARN("Can only sample into a temp register\n");
+        This->state = E_INVALIDARG;
+        return;
+    }
+
+    idx = instr->src[1].regnum;
+    if((idx == 0 && instr->dst.regnum != T0_REG) ||
+       (idx == 1 && instr->dst.regnum != T1_REG) ||
+       (idx == 2 && instr->dst.regnum != T2_REG) ||
+       (idx == 3 && instr->dst.regnum != T3_REG)) {
+        WARN("Sampling from sampler s%u to register r%u is not possible in ps_1_x\n",
+             idx, instr->dst.regnum);
+        This->state = E_INVALIDARG;
+        return;
+    }
+    if(instr->src[0].type == BWRITERSPR_INPUT) {
+        /* A simple non-dependent read tex instruction */
+        if(instr->src[0].regnum != This->t_regnum[idx]) {
+            WARN("Cannot sample from s%u with texture address data from interpolator %u\n",
+                 idx, instr->src[0].regnum);
+            This->state = E_INVALIDARG;
+            return;
+        }
+        This->funcs->opcode(This, instr, D3DSIO_TEX & D3DSI_OPCODE_MASK, buffer);
+
+        /* map the temp dstreg to the ps_1_3 texture temporary register */
+        This->funcs->dstreg(This, &instr->dst, buffer, instr->shift, instr->dstmod);
+    } else if(instr->src[0].type == BWRITERSPR_TEMP) {
+        if(instr->src[0].regnum == T0_REG) {
+            srcidx = 0;
+        } else if(instr->src[0].regnum == T1_REG) {
+            srcidx = 1;
+        } else if(instr->src[0].regnum == T2_REG) {
+            srcidx = 2;
+        } else if(instr->src[0].regnum == T3_REG) {
+            srcidx = 3;
+        } else {
+            WARN("Invalid address data source register r%u\n", instr->src[0].regnum);
+        }
+
+        swizzlemask = (3 << BWRITERVS_SWIZZLE_SHIFT) |
+            (3 << (BWRITERVS_SWIZZLE_SHIFT + 2)) |
+            (3 << (BWRITERVS_SWIZZLE_SHIFT + 4));
+        if((instr->src[0].swizzle & swizzlemask) == (BWRITERVS_X_X | BWRITERVS_Y_Y | BWRITERVS_Z_Z)) {
+            TRACE("writing texreg2rgb\n");
+            This->funcs->opcode(This, instr, D3DSIO_TEXREG2RGB & D3DSI_OPCODE_MASK, buffer);
+        } else if(instr->src[0].swizzle == (BWRITERVS_X_W | BWRITERVS_Y_X | BWRITERVS_Z_X | BWRITERVS_W_X)) {
+            TRACE("writing texreg2ar\n");
+            This->funcs->opcode(This, instr, D3DSIO_TEXREG2AR & D3DSI_OPCODE_MASK, buffer);
+        } else if(instr->src[0].swizzle == (BWRITERVS_X_Y | BWRITERVS_Y_Z | BWRITERVS_Z_Z | BWRITERVS_W_Z)) {
+            TRACE("writing texreg2gb\n");
+            This->funcs->opcode(This, instr, D3DSIO_TEXREG2GB & D3DSI_OPCODE_MASK, buffer);
+        } else {
+            WARN("Unsupported src addr swizzle in dependent texld: 0x%08x\n", instr->src[0].swizzle);
+            This->state = E_INVALIDARG;
+            return;
+        }
+
+        /* Dst and src reg can be mapped normally. Both registers are temporary registers in the
+         * source shader and have to be mapped to the temporary form of the texture registers. However,
+         * the src reg doesn't have a swizzle
+         */
+        This->funcs->dstreg(This, &instr->dst, buffer, instr->shift, instr->dstmod);
+        reg = instr->src[0];
+        reg.swizzle = BWRITERVS_NOSWIZZLE;
+        This->funcs->srcreg(This, &reg, buffer);
+    } else {
+        WARN("Invalid address data source register\n");
+        This->state = E_INVALIDARG;
+        return;
+    }
+}
+
+static void instr_ps_1_0123_mov(struct bc_writer *This,
+                                const struct instruction *instr,
+                                struct bytecode_buffer *buffer) {
+    DWORD token = D3DSIO_MOV & D3DSI_OPCODE_MASK;
+
+    if(instr->dst.type == BWRITERSPR_TEMP && instr->src[0].type == BWRITERSPR_INPUT) {
+        if((instr->dst.regnum == T0_REG && instr->src[0].regnum == This->t_regnum[0]) ||
+           (instr->dst.regnum == T1_REG && instr->src[0].regnum == This->t_regnum[1]) ||
+           (instr->dst.regnum == T2_REG && instr->src[0].regnum == This->t_regnum[2]) ||
+           (instr->dst.regnum == T3_REG && instr->src[0].regnum == This->t_regnum[3])) {
+            if(instr->dstmod & BWRITERSPDM_SATURATE) {
+                This->funcs->opcode(This, instr, D3DSIO_TEXCOORD & D3DSI_OPCODE_MASK, buffer);
+                /* Remove the SATURATE flag, it's implicit to the instruction */
+                This->funcs->dstreg(This, &instr->dst, buffer, instr->shift, instr->dstmod & (~BWRITERSPDM_SATURATE));
+                return;
+            } else {
+                WARN("A varying -> temp copy is only supported with the SATURATE modifier in <=ps_1_3\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+        } else if(instr->src[0].regnum == This->v_regnum[0] ||
+                  instr->src[0].regnum == This->v_regnum[1]) {
+            /* Handled by the normal mov below. Just drop out of the if condition */
+        } else {
+            WARN("Unsupported varying -> temp mov in <= ps_1_3\n");
+            This->state = E_INVALIDARG;
+            return;
+        }
+    }
+
+    This->funcs->opcode(This, instr, token, buffer);
+    This->funcs->dstreg(This, &instr->dst, buffer, instr->shift, instr->dstmod);
+    This->funcs->srcreg(This, &instr->src[0], buffer);
+}
+
+static const struct instr_handler_table ps_1_0123_handlers[] = {
+    {BWRITERSIO_ADD,            instr_handler},
+    {BWRITERSIO_NOP,            instr_handler},
+    {BWRITERSIO_MOV,            instr_ps_1_0123_mov},
+    {BWRITERSIO_SUB,            instr_handler},
+    {BWRITERSIO_MAD,            instr_handler},
+    {BWRITERSIO_MUL,            instr_handler},
+    {BWRITERSIO_DP3,            instr_handler},
+    {BWRITERSIO_DP4,            instr_handler},
+    {BWRITERSIO_LRP,            instr_handler},
+
+    /* pshader instructions */
+    {BWRITERSIO_CND,            instr_handler},
+    {BWRITERSIO_CMP,            instr_handler},
+    {BWRITERSIO_TEXKILL,        instr_handler},
+    {BWRITERSIO_TEX,            instr_ps_1_0123_texld},
+    {BWRITERSIO_TEXBEM,         instr_handler},
+    {BWRITERSIO_TEXBEML,        instr_handler},
+    {BWRITERSIO_TEXM3x2PAD,     instr_handler},
+    {BWRITERSIO_TEXM3x3PAD,     instr_handler},
+    {BWRITERSIO_TEXM3x3SPEC,    instr_handler},
+    {BWRITERSIO_TEXM3x3VSPEC,   instr_handler},
+    {BWRITERSIO_TEXM3x3TEX,     instr_handler},
+    {BWRITERSIO_TEXM3x3,        instr_handler},
+    {BWRITERSIO_TEXM3x2DEPTH,   instr_handler},
+    {BWRITERSIO_TEXM3x2TEX,     instr_handler},
+    {BWRITERSIO_TEXDP3,         instr_handler},
+    {BWRITERSIO_TEXDP3TEX,      instr_handler},
+    {BWRITERSIO_END,            NULL},
+};
+
+static const struct bytecode_backend ps_1_0123_backend = {
+    ps_1_x_header,
+    end,
+    ps_1_0123_srcreg,
+    ps_1_0123_dstreg,
+    sm_1_x_opcode,
+    ps_1_0123_handlers
+};
+
 static void ps_1_4_srcreg(struct bc_writer *This, const struct shader_reg *reg,
                           struct bytecode_buffer *buffer) {
     DWORD token = (1 << 31); /* Bit 31 of registers is 1 */
@@ -1860,6 +2144,26 @@ static void init_vs30_dx9_writer(struct bc_writer *writer) {
     writer->funcs = &vs_3_backend;
 }
 
+static void init_ps10_dx9_writer(struct bc_writer *writer) {
+    TRACE("Creating DirectX9 pixel shader 1.0 writer\n");
+    writer->funcs = &ps_1_0123_backend;
+}
+
+static void init_ps11_dx9_writer(struct bc_writer *writer) {
+    TRACE("Creating DirectX9 pixel shader 1.1 writer\n");
+    writer->funcs = &ps_1_0123_backend;
+}
+
+static void init_ps12_dx9_writer(struct bc_writer *writer) {
+    TRACE("Creating DirectX9 pixel shader 1.2 writer\n");
+    writer->funcs = &ps_1_0123_backend;
+}
+
+static void init_ps13_dx9_writer(struct bc_writer *writer) {
+    TRACE("Creating DirectX9 pixel shader 1.3 writer\n");
+    writer->funcs = &ps_1_0123_backend;
+}
+
 static void init_ps14_dx9_writer(struct bc_writer *writer) {
     TRACE("Creating DirectX9 pixel shader 1.4 writer\n");
     writer->funcs = &ps_1_4_backend;
@@ -1930,28 +2234,28 @@ static struct bc_writer *create_writer(DWORD version, DWORD dxversion) {
                 WARN("Unsupported dxversion for pixel shader 1.0 requested: %u\n", dxversion);
                 goto fail;
             }
-            /* TODO: Set the appropriate writer backend */
+            init_ps10_dx9_writer(ret);
             break;
         case BWRITERPS_VERSION(1, 1):
             if(dxversion != 9) {
                 WARN("Unsupported dxversion for pixel shader 1.1 requested: %u\n", dxversion);
                 goto fail;
             }
-            /* TODO: Set the appropriate writer backend */
+            init_ps11_dx9_writer(ret);
             break;
         case BWRITERPS_VERSION(1, 2):
             if(dxversion != 9) {
                 WARN("Unsupported dxversion for pixel shader 1.2 requested: %u\n", dxversion);
                 goto fail;
             }
-            /* TODO: Set the appropriate writer backend */
+            init_ps12_dx9_writer(ret);
             break;
         case BWRITERPS_VERSION(1, 3):
             if(dxversion != 9) {
                 WARN("Unsupported dxversion for pixel shader 1.3 requested: %u\n", dxversion);
                 goto fail;
             }
-            /* TODO: Set the appropriate writer backend */
+            init_ps13_dx9_writer(ret);
             break;
         case BWRITERPS_VERSION(1, 4):
             if(dxversion != 9) {
diff --git a/dlls/d3dx9_36/d3dx9_36_private.h b/dlls/d3dx9_36/d3dx9_36_private.h
index c4fa468..7488307 100644
--- a/dlls/d3dx9_36/d3dx9_36_private.h
+++ b/dlls/d3dx9_36/d3dx9_36_private.h
@@ -323,6 +323,10 @@ void create_vs11_parser(struct asm_parser *ret);
 void create_vs20_parser(struct asm_parser *ret);
 void create_vs2x_parser(struct asm_parser *ret);
 void create_vs30_parser(struct asm_parser *ret);
+void create_ps10_parser(struct asm_parser *ret);
+void create_ps11_parser(struct asm_parser *ret);
+void create_ps12_parser(struct asm_parser *ret);
+void create_ps13_parser(struct asm_parser *ret);
 void create_ps14_parser(struct asm_parser *ret);
 void create_ps20_parser(struct asm_parser *ret);
 void create_ps2x_parser(struct asm_parser *ret);
@@ -639,6 +643,12 @@ typedef enum _BWRITERDECLUSAGE {
     BWRITERDECLUSAGE_SAMPLE
 } BWRITERDECLUSAGE;
 
+/* ps 1.x texture registers mappings */
+#define T0_REG          2
+#define T1_REG          3
+#define T2_REG          4
+#define T3_REG          5
+
 struct bwriter_shader *SlAssembleShader(const char *text, char **messages);
 DWORD SlWriteBytecode(const struct bwriter_shader *shader, int dxversion, DWORD **result);
 void SlDeleteShader(struct bwriter_shader *shader);
diff --git a/dlls/d3dx9_36/tests/asm.c b/dlls/d3dx9_36/tests/asm.c
index 7d7d211..5f7e776 100644
--- a/dlls/d3dx9_36/tests/asm.c
+++ b/dlls/d3dx9_36/tests/asm.c
@@ -132,6 +132,11 @@ static void ps_1_1_test(void) {
             {0xffff0101, 0x00000042, 0xb00f0000, 0x00000002, 0x80070000, 0x80e40000,
              0x80e40001, 0x40000001, 0x80080000, 0xb0e40000, 0x0000ffff}
         },
+        {   /* shader 1 */
+            "ps.1.1\n"
+            "mov_d4 r0, r1\n",
+            {0xffff0101, 0x00000001, 0x8e0f0000, 0x80e40001, 0x0000ffff}
+        },
     };
 
     exec_tests("ps_1_1", tests, sizeof(tests) / sizeof(tests[0]));
@@ -478,6 +483,43 @@ static void ps_1_3_test(void) {
             "mov_x4_sat r0.a, -r1_bx2.a\n",
             {0xffff0103, 0x00000001, 0x82180000, 0x85ff0001, 0x0000ffff}
         },
+        {   /* shader 30 */
+            "ps_1_3\n"
+            "texcoord_x2 t0\n",
+            {0xffff0103, 0x00000040, 0xb10f0000, 0x0000ffff}
+        },
+        {   /* shader 31 */
+            "ps_1_3\n"
+            "tex_x2 t0\n",
+            {0xffff0103, 0x00000042, 0xb10f0000, 0x0000ffff}
+        },
+        {   /* shader 32 */
+            "ps_1_3\n"
+            "texreg2ar_x4 t0, t1\n",
+            {0xffff0103, 0x00000045, 0xb20f0000, 0xb0e40001, 0x0000ffff}
+        },
+        {   /* shader 33 */
+            "ps_1_3\n"
+            "texbem_d4 t1, t0\n",
+            {0xffff0103, 0x00000043, 0xbe0f0001, 0xb0e40000, 0x0000ffff}
+        },
+        {   /* shader 34 */
+            "ps_1_3\n"
+            "tex t0\n"
+            "texm3x3pad_x2 t1, t0\n"
+            "texm3x3pad_x2 t2, t0\n"
+            "texm3x3tex_x2 t3, t0\n",
+            {0xffff0103, 0x00000042, 0xb00f0000, 0x00000049, 0xb10f0001, 0xb0e40000,
+	     0x00000049, 0xb10f0002, 0xb0e40000, 0x0000004a, 0xb10f0003, 0xb0e40000,
+	     0x0000ffff}
+        },
+        {   /* shader 35 */
+            "ps_1_3\n"
+            "tex t0\n"
+            "texdp3tex_x8 t1, t0\n",
+            {0xffff0103, 0x00000042, 0xb00f0000, 0x00000053, 0xb30f0001, 0xb0e40000,
+	     0x0000ffff}
+        },
     };
 
     exec_tests("ps_1_3", tests, sizeof(tests) / sizeof(tests[0]));
@@ -1661,9 +1703,9 @@ static void assembleshader_test(void) {
 START_TEST(asm)
 {
     preproc_test();
-    todo_wine ps_1_1_test();
+    ps_1_1_test();
     vs_1_1_test();
-    todo_wine ps_1_3_test();
+    ps_1_3_test();
     ps_1_4_test();
     vs_2_0_test();
     vs_2_x_test();
-- 
1.7.1


More information about the wine-patches mailing list