[4/6] d3dx9: Shader assembler vs_2_x support.

Matteo Bruni matteo.mystral at gmail.com
Fri May 21 10:11:39 CDT 2010


-------------- next part --------------
From c66b88c4aadfdc67fdfe87cd600e49863b3a1377 Mon Sep 17 00:00:00 2001
From: Matteo Bruni <matteo.mystral at gmail.com>
Date: Sun, 16 May 2010 20:57:52 +0200
Subject: d3dx9: Shader assembler vs_2_x support.

---
 dlls/d3dx9_36/asmparser.c        |  259 +++++++++++++++++++++
 dlls/d3dx9_36/asmshader.y        |    6 +-
 dlls/d3dx9_36/bytecodewriter.c   |  473 +++++++++++++++++++++++++++++++++++++-
 dlls/d3dx9_36/d3dx9_36_private.h |   11 +
 dlls/d3dx9_36/tests/asm.c        |   12 +-
 5 files changed, 753 insertions(+), 8 deletions(-)

diff --git a/dlls/d3dx9_36/asmparser.c b/dlls/d3dx9_36/asmparser.c
index dfd0936..0d03a0f 100644
--- a/dlls/d3dx9_36/asmparser.c
+++ b/dlls/d3dx9_36/asmparser.c
@@ -30,6 +30,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(asmshader);
 WINE_DECLARE_DEBUG_CHANNEL(parsed_shader);
 
 
+/* How to map vs 1.0 and 2.0 varyings to 3.0 ones
+ * oTx is mapped to ox, which happens to be an
+ * identical mapping since BWRITERSPR_TEXCRDOUT == BWRITERSPR_OUTPUT
+ * oPos, oFog and point size are mapped to general output regs as well.
+ * the vs 1.x and 2.x parser functions add varying declarations
+ * to the shader, and the 1.x and 2.x output functions check those varyings
+ */
+#define OT0_REG         0
+#define OT1_REG         1
+#define OT2_REG         2
+#define OT3_REG         3
+#define OT4_REG         4
+#define OT5_REG         5
+#define OT6_REG         6
+#define OT7_REG         7
+#define OPOS_REG        8
+#define OFOG_REG        9
+#define OFOG_WRITEMASK  BWRITERSP_WRITEMASK_0
+#define OPTS_REG        9
+#define OPTS_WRITEMASK  BWRITERSP_WRITEMASK_1
+#define OD0_REG         10
+#define OD1_REG         11
+
+
 /****************************************************************
  * Common(non-version specific) shader parser control code      *
  ****************************************************************/
@@ -98,6 +122,40 @@ static void asmparser_dcl_sampler(struct asm_parser *This, DWORD samptype, DWORD
     }
 }
 
+static void asmparser_sincos(struct asm_parser *This, DWORD mod, DWORD shift,
+                             const struct shader_reg *dst,
+                             const struct src_regs *srcs) {
+    struct instruction *instr;
+
+    if(!srcs || srcs->count != 3) {
+        asmparser_message(This, "Line %u: sincos (vs 2) has an incorrect number of source registers\n", This->line_no);
+        set_parse_status(This, PARSE_ERR);
+        return;
+    }
+
+    instr = alloc_instr(3);
+    if(!instr) {
+        ERR("Error allocating memory for the instruction\n");
+        set_parse_status(This, PARSE_ERR);
+        return;
+    }
+
+    instr->opcode = BWRITERSIO_SINCOS;
+    instr->dstmod = mod;
+    instr->shift = shift;
+    instr->comptype = 0;
+
+    This->funcs->dstreg(This, instr, dst);
+    This->funcs->srcreg(This, instr, 0, &srcs->reg[0]);
+    This->funcs->srcreg(This, instr, 1, &srcs->reg[1]);
+    This->funcs->srcreg(This, instr, 2, &srcs->reg[2]);
+
+    if(!add_instruction(This->shader, instr)) {
+        ERR("Out of memory\n");
+        set_parse_status(This, PARSE_ERR);
+    }
+}
+
 static void asmparser_instr(struct asm_parser *This, DWORD opcode,
                             DWORD mod, DWORD shift,
                             BWRITER_COMPARISON_TYPE comp,
@@ -125,6 +183,20 @@ static void asmparser_instr(struct asm_parser *This, DWORD opcode,
     }
     TRACE_(parsed_shader)("\n");
 
+ /* Check for instructions with different syntaxes in different shader versio
+ns */
+    switch(opcode) {
+        case BWRITERSIO_SINCOS:
+            /* The syntax changes between vs 2 and the other shader versions */
+            if(This->shader->version == BWRITERVS_VERSION(2, 0) ||
+               This->shader->version == BWRITERVS_VERSION(2, 1)) {
+                asmparser_sincos(This, mod, shift, dst, srcs);
+                return;
+            }
+            /* Use the default handling */
+            break;
+    }
+
     if(src_count != expectednsrcs) {
         asmparser_message(This, "Line %u: Wrong number of source registers\n", This->line_no);
         set_parse_status(This, PARSE_ERR);
@@ -153,6 +225,64 @@ static void asmparser_instr(struct asm_parser *This, DWORD opcode,
     }
 }
 
+static struct shader_reg map_oldvs_register(const struct shader_reg *reg) {
+    struct shader_reg ret;
+    switch(reg->type) {
+        case BWRITERSPR_RASTOUT:
+            ret = *reg;
+            ret.type = BWRITERSPR_OUTPUT;
+            switch(reg->regnum) {
+                case BWRITERSRO_POSITION:
+                    ret.regnum = OPOS_REG;
+                    break;
+                case BWRITERSRO_FOG:
+                    ret.regnum = OFOG_REG;
+                    ret.writemask = OFOG_WRITEMASK;
+                    break;
+                case BWRITERSRO_POINT_SIZE:
+                    ret.regnum = OPTS_REG;
+                    ret.writemask = OPTS_WRITEMASK;
+                    break;
+                default:
+                    FIXME("Unhandled RASTOUT register %u\n", reg->regnum);
+                    return *reg;
+            }
+            return ret;
+
+        case BWRITERSPR_TEXCRDOUT:
+            ret = *reg;
+            ret.type = BWRITERSPR_OUTPUT;
+            switch(reg->regnum) {
+                case 0: ret.regnum = OT0_REG; break;
+                case 1: ret.regnum = OT1_REG; break;
+                case 2: ret.regnum = OT2_REG; break;
+                case 3: ret.regnum = OT3_REG; break;
+                case 4: ret.regnum = OT4_REG; break;
+                case 5: ret.regnum = OT5_REG; break;
+                case 6: ret.regnum = OT6_REG; break;
+                case 7: ret.regnum = OT7_REG; break;
+                default:
+                    FIXME("Unhandled TEXCRDOUT regnum %u\n", reg->regnum);
+                    return *reg;
+            }
+            return ret;
+
+        case BWRITERSPR_ATTROUT:
+            ret = *reg;
+            ret.type = BWRITERSPR_OUTPUT;
+            switch(reg->regnum) {
+                case 0: ret.regnum = OD0_REG; break;
+                case 1: ret.regnum = OD1_REG; break;
+                default:
+                    FIXME("Unhandled ATTROUT regnum %u\n", reg->regnum);
+                    return *reg;
+            }
+            return ret;
+
+        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) {
@@ -168,6 +298,15 @@ static void check_legacy_srcmod(struct asm_parser *This, DWORD srcmod) {
     }
 }
 
+static void check_abs_srcmod(struct asm_parser *This, DWORD srcmod) {
+    if(srcmod == BWRITERSPSM_ABS || srcmod == BWRITERSPSM_ABSNEG) {
+        asmparser_message(This, "Line %u: Source modifier %s not supported in this shader version\n",
+                          This->line_no,
+                          debug_print_srcmod(srcmod));
+        set_parse_status(This, PARSE_ERR);
+    }
+}
+
 static void check_loop_swizzle(struct asm_parser *This,
                                const struct shader_reg *src) {
     if((src->type == BWRITERSPR_LOOP && src->swizzle != BWRITERVS_NOSWIZZLE) ||
@@ -219,6 +358,40 @@ static BOOL check_reg_type(const struct shader_reg *reg,
 }
 
 /* Native assembler doesn't do separate checks for src and dst registers */
+static const struct allowed_reg_type vs_2_reg_allowed[] = {
+    { BWRITERSPR_TEMP,      12 },
+    { BWRITERSPR_INPUT,     16 },
+    { BWRITERSPR_CONST,    ~0U },
+    { BWRITERSPR_ADDR,       1 },
+    { BWRITERSPR_CONSTBOOL, 16 },
+    { BWRITERSPR_CONSTINT,  16 },
+    { BWRITERSPR_LOOP,       1 },
+    { BWRITERSPR_LABEL,   2048 },
+    { BWRITERSPR_PREDICATE,  1 },
+    { BWRITERSPR_RASTOUT,    3 }, /* oPos, oFog and oPts */
+    { BWRITERSPR_ATTROUT,    2 },
+    { BWRITERSPR_TEXCRDOUT,  8 },
+    { ~0U, 0 } /* End tag */
+};
+
+static void asmparser_srcreg_vs_2(struct asm_parser *This,
+                                  struct instruction *instr, int num,
+                                  const struct shader_reg *src) {
+    struct shader_reg reg;
+
+    if(!check_reg_type(src, vs_2_reg_allowed)) {
+        asmparser_message(This, "Line %u: Source register %s not supported in VS 2\n",
+                          This->line_no,
+                          debug_print_srcreg(src, ST_VERTEX));
+        set_parse_status(This, PARSE_ERR);
+    }
+    check_loop_swizzle(This, src);
+    check_legacy_srcmod(This, src->srcmod);
+    check_abs_srcmod(This, src->srcmod);
+    reg = map_oldvs_register(src);
+    memcpy(&instr->src[num], &reg, sizeof(reg));
+}
+
 static const struct allowed_reg_type vs_3_reg_allowed[] = {
     { BWRITERSPR_TEMP,         32 },
     { BWRITERSPR_INPUT,        16 },
@@ -278,6 +451,24 @@ static void asmparser_srcreg_ps_3(struct asm_parser *This,
     memcpy(&instr->src[num], src, sizeof(*src));
 }
 
+static void asmparser_dstreg_vs_2(struct asm_parser *This,
+                                  struct instruction *instr,
+                                  const struct shader_reg *dst) {
+    struct shader_reg reg;
+
+    if(!check_reg_type(dst, vs_2_reg_allowed)) {
+        asmparser_message(This, "Line %u: Destination register %s not supported in VS 2.0\n",
+                          This->line_no,
+                          debug_print_dstreg(dst, ST_VERTEX));
+        set_parse_status(This, PARSE_ERR);
+    }
+    check_ps_dstmod(This, instr->dstmod);
+    check_shift_dstmod(This, instr->shift);
+    reg = map_oldvs_register(dst);
+    memcpy(&instr->dst, &reg, sizeof(reg));
+    instr->has_dst = TRUE;
+}
+
 static void asmparser_dstreg_vs_3(struct asm_parser *This,
                                   struct instruction *instr,
                                   const struct shader_reg *dst) {
@@ -329,6 +520,26 @@ static void asmparser_coissue_unsupported(struct asm_parser *This) {
     set_parse_status(This, PARSE_ERR);
 }
 
+static const struct asmparser_backend parser_vs_2 = {
+    asmparser_constF,
+    asmparser_constI,
+    asmparser_constB,
+
+    asmparser_dstreg_vs_2,
+    asmparser_srcreg_vs_2,
+
+    asmparser_predicate_supported,
+    asmparser_coissue_unsupported,
+
+    asmparser_dcl_output,
+    asmparser_dcl_input,
+    asmparser_dcl_sampler,
+
+    asmparser_end,
+
+    asmparser_instr,
+};
+
 static const struct asmparser_backend parser_vs_3 = {
     asmparser_constF,
     asmparser_constI,
@@ -369,6 +580,54 @@ static const struct asmparser_backend parser_ps_3 = {
     asmparser_instr,
 };
 
+static void gen_oldvs_output(struct bwriter_shader *shader) {
+    record_declaration(shader, BWRITERDECLUSAGE_POSITION, 0, TRUE, OPOS_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 0, TRUE, OT0_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 1, TRUE, OT1_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 2, TRUE, OT2_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 3, TRUE, OT3_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 4, TRUE, OT4_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 5, TRUE, OT5_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 6, TRUE, OT6_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_TEXCOORD, 7, TRUE, OT7_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_FOG, 0, TRUE, OFOG_REG, OFOG_WRITEMASK);
+    record_declaration(shader, BWRITERDECLUSAGE_PSIZE, 0, TRUE, OPTS_REG, OPTS_WRITEMASK);
+    record_declaration(shader, BWRITERDECLUSAGE_COLOR, 0, TRUE, OD0_REG, BWRITERSP_WRITEMASK_ALL);
+    record_declaration(shader, BWRITERDECLUSAGE_COLOR, 1, TRUE, OD1_REG, BWRITERSP_WRITEMASK_ALL);
+}
+
+void create_vs20_parser(struct asm_parser *ret) {
+    TRACE_(parsed_shader)("vs_2_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_VERTEX;
+    ret->shader->version = BWRITERVS_VERSION(2, 0);
+    ret->funcs = &parser_vs_2;
+    gen_oldvs_output(ret->shader);
+}
+
+void create_vs2x_parser(struct asm_parser *ret) {
+    TRACE_(parsed_shader)("vs_2_x\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_VERTEX;
+    ret->shader->version = BWRITERVS_VERSION(2, 1);
+    ret->funcs = &parser_vs_2;
+    gen_oldvs_output(ret->shader);
+}
+
 void create_vs30_parser(struct asm_parser *ret) {
     TRACE_(parsed_shader)("vs_3_0\n");
 
diff --git a/dlls/d3dx9_36/asmshader.y b/dlls/d3dx9_36/asmshader.y
index 8be8328..ffdccd2 100644
--- a/dlls/d3dx9_36/asmshader.y
+++ b/dlls/d3dx9_36/asmshader.y
@@ -283,14 +283,12 @@ version_marker:       VER_VS10
                     | VER_VS20
                         {
                             TRACE("Vertex shader 2.0\n");
-                            set_parse_status(&asm_ctx, PARSE_ERR);
-                            YYABORT;
+                            create_vs20_parser(&asm_ctx);
                         }
                     | VER_VS2X
                         {
                             TRACE("Vertex shader 2.x\n");
-                            set_parse_status(&asm_ctx, PARSE_ERR);
-                            YYABORT;
+                            create_vs2x_parser(&asm_ctx);
                         }
                     | VER_VS30
                         {
diff --git a/dlls/d3dx9_36/bytecodewriter.c b/dlls/d3dx9_36/bytecodewriter.c
index a579ae8..a3e4118 100644
--- a/dlls/d3dx9_36/bytecodewriter.c
+++ b/dlls/d3dx9_36/bytecodewriter.c
@@ -400,10 +400,220 @@ static void write_constF(const struct bwriter_shader *shader, struct bytecode_bu
     write_const(shader->constF, shader->num_cf, D3DSIO_DEF, D3DSPR_CONST, buffer, len);
 }
 
+static HRESULT vs_find_builtin_varyings(struct bc_writer *This, const struct bwriter_shader *shader) {
+    DWORD i;
+    DWORD usage, usage_idx, writemask, regnum;
+
+    for(i = 0; i < shader->num_outputs; i++) {
+        usage = shader->outputs[i].usage;
+        usage_idx = shader->outputs[i].usage_idx;
+        writemask = shader->outputs[i].writemask;
+        regnum = shader->outputs[i].regnum;
+
+        switch(usage) {
+            case BWRITERDECLUSAGE_POSITION:
+            case BWRITERDECLUSAGE_POSITIONT:
+                if(usage_idx > 0) {
+                    WARN("dcl_position%u not supported in sm 1/2 shaders\n", usage_idx);
+                    return E_INVALIDARG;
+                }
+                TRACE("o%u is oPos\n", regnum);
+                This->oPos_regnum = regnum;
+                break;
+
+            case BWRITERDECLUSAGE_COLOR:
+                if(usage_idx > 1) {
+                    WARN("dcl_color%u not supported in sm 1/2 shaders\n", usage_idx);
+                    return E_INVALIDARG;
+                }
+                if(writemask != BWRITERSP_WRITEMASK_ALL) {
+                    WARN("Only WRITEMASK_ALL is supported on color in sm 1/2\n");
+                    return E_INVALIDARG;
+                }
+                TRACE("o%u is oD%u\n", regnum, usage_idx);
+                This->oD_regnum[usage_idx] = regnum;
+                break;
+
+            case BWRITERDECLUSAGE_TEXCOORD:
+                if(usage_idx > 8) {
+                    WARN("dcl_color%u not supported in sm 1/2 shaders\n", usage_idx);
+                    return E_INVALIDARG;
+                }
+                if(writemask != (BWRITERSP_WRITEMASK_0) &&
+                   writemask != (BWRITERSP_WRITEMASK_0 | BWRITERSP_WRITEMASK_1) &&
+                   writemask != (BWRITERSP_WRITEMASK_0 | BWRITERSP_WRITEMASK_1 | BWRITERSP_WRITEMASK_2) &&
+                   writemask != (BWRITERSP_WRITEMASK_ALL)) {
+                    WARN("Partial writemasks not supported on texture coordinates in sm 1 and 2\n");
+                    return E_INVALIDARG;
+                }
+                TRACE("o%u is oT%u\n", regnum, usage_idx);
+                This->oT_regnum[usage_idx] = regnum;
+                break;
+
+            case BWRITERDECLUSAGE_PSIZE:
+                if(usage_idx > 0) {
+                    WARN("dcl_psize%u not supported in sm 1/2 shaders\n", usage_idx);
+                    return E_INVALIDARG;
+                }
+                TRACE("o%u writemask 0x%08x is oPts\n", regnum, writemask);
+                This->oPts_regnum = regnum;
+                This->oPts_mask = writemask;
+                break;
+
+            case BWRITERDECLUSAGE_FOG:
+                if(usage_idx > 0) {
+                    WARN("dcl_fog%u not supported in sm 1 shaders\n", usage_idx);
+                    return E_INVALIDARG;
+                }
+                if(writemask != BWRITERSP_WRITEMASK_0 && writemask != BWRITERSP_WRITEMASK_1 &&
+                   writemask != BWRITERSP_WRITEMASK_2 && writemask != BWRITERSP_WRITEMASK_3) {
+                    WARN("Unsupported fog writemask\n");
+                    return E_INVALIDARG;
+                }
+                TRACE("o%u writemask 0x%08x is oFog\n", regnum, writemask);
+                This->oFog_regnum = regnum;
+                This->oFog_mask = writemask;
+                break;
+
+            default:
+                WARN("Varying type %u is not supported in shader model 1.x\n", usage);
+                return E_INVALIDARG;
+        }
+    }
+
+    return S_OK;
+}
+
 static void end(struct bc_writer *This, const struct bwriter_shader *shader, struct bytecode_buffer *buffer) {
     put_dword(buffer, D3DSIO_END);
 }
 
+static DWORD map_vs_output(struct bc_writer *This, DWORD regnum, DWORD mask, DWORD *has_components) {
+    DWORD token = 0;
+    DWORD i;
+
+    *has_components = TRUE;
+    if(regnum == This->oPos_regnum) {
+        token |= (D3DSPR_RASTOUT << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= D3DSRO_POSITION & D3DSP_REGNUM_MASK; /* No shift */
+        return token;
+    } 
+    if(regnum == This->oFog_regnum && mask == This->oFog_mask) {
+        token |= (D3DSPR_RASTOUT << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= D3DSRO_FOG & D3DSP_REGNUM_MASK; /* No shift */
+        token |= D3DSP_WRITEMASK_ALL;
+        *has_components = FALSE;
+        return token;
+    }
+    if(regnum == This->oPts_regnum && mask == This->oPts_mask) {
+        token |= (D3DSPR_RASTOUT << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+        token |= D3DSRO_POINT_SIZE & D3DSP_REGNUM_MASK; /* No shift */
+        token |= D3DSP_WRITEMASK_ALL;
+        *has_components = FALSE;
+        return token;
+    }
+    for(i = 0; i < 2; i++) {
+        if(regnum == This->oD_regnum[i]) {
+            token |= (D3DSPR_ATTROUT << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= i & D3DSP_REGNUM_MASK; /* No shift */
+            return token;
+        }
+    }
+    for(i = 0; i < 8; i++) {
+        if(regnum == This->oT_regnum[i]) {
+            token |= (D3DSPR_TEXCRDOUT << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= i & D3DSP_REGNUM_MASK; /* No shift */
+            return token;
+        }
+    }
+
+    /* The varying must be undeclared - if an unsupported varying was declared,
+     * the vs_find_builtin_varyings function would have caught it and this code
+     * would not run */
+    WARN("Undeclared varying %u\n", regnum);
+    This->state = E_INVALIDARG;
+    return -1;
+}
+
+static void vs_12_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 */
+    DWORD has_wmask;
+
+    if(reg->rel_reg) {
+        WARN("Relative addressing not supported for destination registers\n");
+        This->state = E_INVALIDARG;
+        return;
+    }
+
+    switch(reg->type) {
+        case BWRITERSPR_OUTPUT:
+            token |= map_vs_output(This, reg->regnum, reg->writemask, &has_wmask);
+            break;
+
+        case BWRITERSPR_RASTOUT:
+        case BWRITERSPR_ATTROUT:
+            /* These registers are mapped to input and output regs. They can be encoded in the bytecode,
+            * but are unexpected. If we hit this path it might be due to an error.
+            */
+            FIXME("Unexpected register type %u\n", reg->type);
+            /* drop through */
+        case BWRITERSPR_INPUT:
+        case BWRITERSPR_TEMP:
+        case BWRITERSPR_CONST:
+            token |= (reg->type << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= reg->regnum & D3DSP_REGNUM_MASK; /* No shift */
+            has_wmask = TRUE;
+            break;
+
+        case BWRITERSPR_ADDR:
+            if(reg->regnum != 0) {
+                WARN("Only a0 exists\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+            token |= (D3DSPR_ADDR << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= 0 & D3DSP_REGNUM_MASK; /* No shift */
+            has_wmask = TRUE;
+            break;
+
+        case BWRITERSPR_PREDICATE:
+            if(This->version != BWRITERVS_VERSION(2, 1)){
+                WARN("Predicate register is allowed only in vs_2_x\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+            if(reg->regnum != 0) {
+                WARN("Only predicate register p0 exists\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+            token |= (D3DSPR_PREDICATE << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= (D3DSPR_PREDICATE << D3DSP_REGTYPE_SHIFT2) & D3DSP_REGTYPE_MASK2;
+            token |= 0 & D3DSP_REGNUM_MASK; /* No shift */
+            has_wmask = TRUE;
+            break;
+
+        default:
+            WARN("Invalid register type for 1.x-2.x vertex shader\n");
+            This->state = E_INVALIDARG;
+            return;
+    }
+
+    /* strictly speaking there are no modifiers in vs_2_0 and vs_1_x, but they can be written
+     * into the bytecode and since the compiler doesn't do such checks write them
+     * (the checks are done by the undocumented shader validator)
+     */
+    token |= (shift << D3DSP_DSTSHIFT_SHIFT) & D3DSP_DSTSHIFT_MASK;
+    token |= d3d9_dstmod(mod);
+
+    if(has_wmask) {
+        token |= d3d9_writemask(reg->writemask);
+    }
+    put_dword(buffer, token);
+}
+
 static void write_srcregs(struct bc_writer *This, const struct instruction *instr,
                           struct bytecode_buffer *buffer){
     unsigned int i;
@@ -451,6 +661,129 @@ static void write_constI(const struct bwriter_shader *shader, struct bytecode_bu
     write_const(shader->constI, shader->num_ci, D3DSIO_DEFI, D3DSPR_CONSTINT, buffer, len);
 }
 
+static void vs_2_header(struct bc_writer *This,
+                        const struct bwriter_shader *shader,
+                        struct bytecode_buffer *buffer) {
+    HRESULT hr;
+
+    hr = vs_find_builtin_varyings(This, shader);
+    if(FAILED(hr)) {
+        This->state = hr;
+        return;
+    }
+
+    /* Declare the shader type and version */
+    put_dword(buffer, This->version);
+
+    write_declarations(buffer, TRUE, shader->inputs, shader->num_inputs, D3DSPR_INPUT);
+    write_constF(shader, buffer, TRUE);
+    write_constB(shader, buffer, TRUE);
+    write_constI(shader, buffer, TRUE);
+    return;
+}
+
+static void vs_2_srcreg(struct bc_writer *This,
+                        const struct shader_reg *reg,
+                        struct bytecode_buffer *buffer) {
+    DWORD token = (1 << 31); /* Bit 31 of registers is 1 */
+    DWORD has_swizzle;
+    DWORD component;
+    DWORD d3d9reg;
+
+    switch(reg->type) {
+        case BWRITERSPR_OUTPUT:
+            /* Map the swizzle to a writemask, the format expected
+               by map_vs_output
+             */
+            switch(reg->swizzle) {
+                case BWRITERVS_SWIZZLE_X:
+                    component = BWRITERSP_WRITEMASK_0;
+                    break;
+                case BWRITERVS_SWIZZLE_Y:
+                    component = BWRITERSP_WRITEMASK_1;
+                    break;
+                case BWRITERVS_SWIZZLE_Z:
+                    component = BWRITERSP_WRITEMASK_2;
+                    break;
+                case BWRITERVS_SWIZZLE_W:
+                    component = BWRITERSP_WRITEMASK_3;
+                    break;
+                default:
+                    component = 0;
+            }
+            token |= map_vs_output(This, reg->regnum, component, &has_swizzle);
+            break;
+
+        case BWRITERSPR_RASTOUT:
+        case BWRITERSPR_ATTROUT:
+            /* These registers are mapped to input and output regs. They can be encoded in the bytecode,
+             * but are unexpected. If we hit this path it might be due to an error.
+             */
+            FIXME("Unexpected register type %u\n", reg->type);
+            /* drop through */
+        case BWRITERSPR_INPUT:
+        case BWRITERSPR_TEMP:
+        case BWRITERSPR_CONST:
+        case BWRITERSPR_ADDR:
+        case BWRITERSPR_CONSTINT:
+        case BWRITERSPR_CONSTBOOL:
+        case BWRITERSPR_LABEL:
+            d3d9reg = d3d9_register(reg->type);
+            token |= (d3d9reg << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= (d3d9reg << D3DSP_REGTYPE_SHIFT2) & D3DSP_REGTYPE_MASK2;
+            token |= reg->regnum & D3DSP_REGNUM_MASK; /* No shift */
+            break;
+
+        case BWRITERSPR_LOOP:
+            if(reg->regnum != 0) {
+                WARN("Only regnum 0 is supported for the loop index register in vs_2_0\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+            token |= (D3DSPR_LOOP << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= (D3DSPR_LOOP << D3DSP_REGTYPE_SHIFT2) & D3DSP_REGTYPE_MASK2;
+            token |= 0 & D3DSP_REGNUM_MASK; /* No shift */
+            break;
+
+        case BWRITERSPR_PREDICATE:
+            if(This->version != BWRITERVS_VERSION(2, 1)){
+                WARN("Predicate register is allowed only in vs_2_x\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+            if(reg->regnum > 0) {
+                WARN("Only predicate register 0 is supported\n");
+                This->state = E_INVALIDARG;
+                return;
+            }
+            token |= (D3DSPR_PREDICATE << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK;
+            token |= (D3DSPR_PREDICATE << D3DSP_REGTYPE_SHIFT2) & D3DSP_REGTYPE_MASK2;
+            token |= 0 & D3DSP_REGNUM_MASK; /* No shift */
+
+            break;
+
+        default:
+            WARN("Invalid register type for 2.0 vshader\n");
+            This->state = E_INVALIDARG;
+            return;
+    }
+
+    token |= d3d9_swizzle(reg->swizzle) & D3DVS_SWIZZLE_MASK; /* already shifted */
+
+    token |= d3d9_srcmod(reg->srcmod);
+
+    if(reg->rel_reg)
+        token |= D3DVS_ADDRMODE_RELATIVE & D3DVS_ADDRESSMODE_MASK;
+
+    put_dword(buffer, token);
+
+    /* vs_2_0 and newer write the register containing the index explicitly in the
+     * binary code
+     */
+    if(token & D3DVS_ADDRMODE_RELATIVE)
+        vs_2_srcreg(This, reg->rel_reg, buffer);
+}
+
 static void sm_2_opcode(struct bc_writer *This,
                         const struct instruction *instr,
                         DWORD token, struct bytecode_buffer *buffer) {
@@ -464,6 +797,132 @@ static void sm_2_opcode(struct bc_writer *This,
     put_dword(buffer,token);
 }
 
+static const struct instr_handler_table vs_2_0_handlers[] = {
+    {BWRITERSIO_ADD,            instr_handler},
+    {BWRITERSIO_NOP,            instr_handler},
+    {BWRITERSIO_MOV,            instr_handler},
+    {BWRITERSIO_SUB,            instr_handler},
+    {BWRITERSIO_MAD,            instr_handler},
+    {BWRITERSIO_MUL,            instr_handler},
+    {BWRITERSIO_RCP,            instr_handler},
+    {BWRITERSIO_RSQ,            instr_handler},
+    {BWRITERSIO_DP3,            instr_handler},
+    {BWRITERSIO_DP4,            instr_handler},
+    {BWRITERSIO_MIN,            instr_handler},
+    {BWRITERSIO_MAX,            instr_handler},
+    {BWRITERSIO_SLT,            instr_handler},
+    {BWRITERSIO_SGE,            instr_handler},
+    {BWRITERSIO_ABS,            instr_handler},
+    {BWRITERSIO_EXP,            instr_handler},
+    {BWRITERSIO_LOG,            instr_handler},
+    {BWRITERSIO_EXPP,           instr_handler},
+    {BWRITERSIO_LOGP,           instr_handler},
+    {BWRITERSIO_DST,            instr_handler},
+    {BWRITERSIO_LRP,            instr_handler},
+    {BWRITERSIO_FRC,            instr_handler},
+    {BWRITERSIO_CRS,            instr_handler},
+    {BWRITERSIO_SGN,            instr_handler},
+    {BWRITERSIO_NRM,            instr_handler},
+    {BWRITERSIO_SINCOS,         instr_handler},
+    {BWRITERSIO_M4x4,           instr_handler},
+    {BWRITERSIO_M4x3,           instr_handler},
+    {BWRITERSIO_M3x4,           instr_handler},
+    {BWRITERSIO_M3x3,           instr_handler},
+    {BWRITERSIO_M3x2,           instr_handler},
+    {BWRITERSIO_LIT,            instr_handler},
+    {BWRITERSIO_POW,            instr_handler},
+    {BWRITERSIO_MOVA,           instr_handler},
+
+    {BWRITERSIO_CALL,           instr_handler},
+    {BWRITERSIO_CALLNZ,         instr_handler},
+    {BWRITERSIO_REP,            instr_handler},
+    {BWRITERSIO_ENDREP,         instr_handler},
+    {BWRITERSIO_IF,             instr_handler},
+    {BWRITERSIO_LABEL,          instr_handler},
+    {BWRITERSIO_ELSE,           instr_handler},
+    {BWRITERSIO_ENDIF,          instr_handler},
+    {BWRITERSIO_LOOP,           instr_handler},
+    {BWRITERSIO_RET,            instr_handler},
+    {BWRITERSIO_ENDLOOP,        instr_handler},
+
+    {BWRITERSIO_END,            NULL},
+};
+
+static const struct bytecode_backend vs_2_0_backend = {
+    vs_2_header,
+    end,
+    vs_2_srcreg,
+    vs_12_dstreg,
+    sm_2_opcode,
+    vs_2_0_handlers
+};
+
+static const struct instr_handler_table vs_2_x_handlers[] = {
+    {BWRITERSIO_ADD,            instr_handler},
+    {BWRITERSIO_NOP,            instr_handler},
+    {BWRITERSIO_MOV,            instr_handler},
+    {BWRITERSIO_SUB,            instr_handler},
+    {BWRITERSIO_MAD,            instr_handler},
+    {BWRITERSIO_MUL,            instr_handler},
+    {BWRITERSIO_RCP,            instr_handler},
+    {BWRITERSIO_RSQ,            instr_handler},
+    {BWRITERSIO_DP3,            instr_handler},
+    {BWRITERSIO_DP4,            instr_handler},
+    {BWRITERSIO_MIN,            instr_handler},
+    {BWRITERSIO_MAX,            instr_handler},
+    {BWRITERSIO_SLT,            instr_handler},
+    {BWRITERSIO_SGE,            instr_handler},
+    {BWRITERSIO_ABS,            instr_handler},
+    {BWRITERSIO_EXP,            instr_handler},
+    {BWRITERSIO_LOG,            instr_handler},
+    {BWRITERSIO_EXPP,           instr_handler},
+    {BWRITERSIO_LOGP,           instr_handler},
+    {BWRITERSIO_DST,            instr_handler},
+    {BWRITERSIO_LRP,            instr_handler},
+    {BWRITERSIO_FRC,            instr_handler},
+    {BWRITERSIO_CRS,            instr_handler},
+    {BWRITERSIO_SGN,            instr_handler},
+    {BWRITERSIO_NRM,            instr_handler},
+    {BWRITERSIO_SINCOS,         instr_handler},
+    {BWRITERSIO_M4x4,           instr_handler},
+    {BWRITERSIO_M4x3,           instr_handler},
+    {BWRITERSIO_M3x4,           instr_handler},
+    {BWRITERSIO_M3x3,           instr_handler},
+    {BWRITERSIO_M3x2,           instr_handler},
+    {BWRITERSIO_LIT,            instr_handler},
+    {BWRITERSIO_POW,            instr_handler},
+    {BWRITERSIO_MOVA,           instr_handler},
+
+    {BWRITERSIO_CALL,           instr_handler},
+    {BWRITERSIO_CALLNZ,         instr_handler},
+    {BWRITERSIO_REP,            instr_handler},
+    {BWRITERSIO_ENDREP,         instr_handler},
+    {BWRITERSIO_IF,             instr_handler},
+    {BWRITERSIO_LABEL,          instr_handler},
+    {BWRITERSIO_IFC,            instr_handler},
+    {BWRITERSIO_ELSE,           instr_handler},
+    {BWRITERSIO_ENDIF,          instr_handler},
+    {BWRITERSIO_BREAK,          instr_handler},
+    {BWRITERSIO_BREAKC,         instr_handler},
+    {BWRITERSIO_LOOP,           instr_handler},
+    {BWRITERSIO_RET,            instr_handler},
+    {BWRITERSIO_ENDLOOP,        instr_handler},
+
+    {BWRITERSIO_SETP,           instr_handler},
+    {BWRITERSIO_BREAKP,         instr_handler},
+
+    {BWRITERSIO_END,            NULL},
+};
+
+static const struct bytecode_backend vs_2_x_backend = {
+    vs_2_header,
+    end,
+    vs_2_srcreg,
+    vs_12_dstreg,
+    sm_2_opcode,
+    vs_2_x_handlers
+};
+
 static void write_samplers(const struct bwriter_shader *shader, struct bytecode_buffer *buffer) {
     DWORD i;
     DWORD instr_dcl = D3DSIO_DCL | (2 << D3DSI_INSTLENGTH_SHIFT);
@@ -712,6 +1171,16 @@ static const struct bytecode_backend ps_3_backend = {
     ps_3_handlers
 };
 
+static void init_vs20_dx9_writer(struct bc_writer *writer) {
+    TRACE("Creating DirectX9 vertex shader 2.0 writer\n");
+    writer->funcs = &vs_2_0_backend;
+}
+
+static void init_vs2x_dx9_writer(struct bc_writer *writer) {
+    TRACE("Creating DirectX9 vertex shader 2.x writer\n");
+    writer->funcs = &vs_2_x_backend;
+}
+
 static void init_vs30_dx9_writer(struct bc_writer *writer) {
     TRACE("Creating DirectX9 vertex shader 3.0 writer\n");
     writer->funcs = &vs_3_backend;
@@ -750,14 +1219,14 @@ static struct bc_writer *create_writer(DWORD version, DWORD dxversion) {
                 WARN("Unsupported dxversion for vertex shader 2.0 requested: %u\n", dxversion);
                 goto fail;
             }
-            /* TODO: Set the appropriate writer backend */
+            init_vs20_dx9_writer(ret);
             break;
         case BWRITERVS_VERSION(2, 1):
             if(dxversion != 9) {
                 WARN("Unsupported dxversion for vertex shader 2.x requested: %u\n", dxversion);
                 goto fail;
             }
-            /* TODO: Set the appropriate writer backend */
+            init_vs2x_dx9_writer(ret);
             break;
         case BWRITERVS_VERSION(3, 0):
             if(dxversion != 9) {
diff --git a/dlls/d3dx9_36/d3dx9_36_private.h b/dlls/d3dx9_36/d3dx9_36_private.h
index 248503d..7ad8688 100644
--- a/dlls/d3dx9_36/d3dx9_36_private.h
+++ b/dlls/d3dx9_36/d3dx9_36_private.h
@@ -311,6 +311,8 @@ struct asm_parser {
 
 extern struct asm_parser asm_ctx;
 
+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_ps30_parser(struct asm_parser *ret);
 
@@ -369,6 +371,15 @@ struct bc_writer {
     HRESULT                       state;
 
     DWORD                         version;
+
+    /* Vertex shader varying mapping */
+    DWORD                         oPos_regnum;
+    DWORD                         oD_regnum[2];
+    DWORD                         oT_regnum[8];
+    DWORD                         oFog_regnum;
+    DWORD                         oFog_mask;
+    DWORD                         oPts_regnum;
+    DWORD                         oPts_mask;
 };
 
 /* Debug utility routines */
diff --git a/dlls/d3dx9_36/tests/asm.c b/dlls/d3dx9_36/tests/asm.c
index 0b8cd3e..68d634f 100644
--- a/dlls/d3dx9_36/tests/asm.c
+++ b/dlls/d3dx9_36/tests/asm.c
@@ -739,6 +739,14 @@ static void vs_2_0_test(void) {
             "endif\n",
             {0xfffe0200, 0x01000028, 0xede40800, 0x0000002a, 0x0000002b, 0x0000ffff}
         },
+        {   /* shader 28 */
+            "vs_2_0\n"
+            "call l3\n"
+            "ret\n"
+            "label l3\n"
+            "ret\n",
+            {0xfffe0200, 0x01000019, 0xa0e41003, 0x0000001c, 0x0100001e, 0xa0e41003, 0x0000001c, 0x0000ffff}
+        },
     };
 
     exec_tests("vs_2_0", tests, sizeof(tests) / sizeof(tests[0]));
@@ -1467,8 +1475,8 @@ START_TEST(asm)
     todo_wine vs_1_1_test();
     todo_wine ps_1_3_test();
     todo_wine ps_1_4_test();
-    todo_wine vs_2_0_test();
-    todo_wine vs_2_x_test();
+    vs_2_0_test();
+    vs_2_x_test();
     todo_wine ps_2_0_test();
     todo_wine ps_2_x_test();
     vs_3_0_test();
-- 
1.6.4.4


More information about the wine-patches mailing list