From bfa3999284dfd2e764a224d61e2e89df7b5bb38a Mon Sep 17 00:00:00 2001 From: Matteo Bruni Date: Tue, 20 Jul 2010 15:01:16 +0200 Subject: [PATCH] d3dx9: Shader assembler ps_1_4 support. --- dlls/d3dx9_36/asmparser.c | 183 +++++++++++++++++++++++++++++ dlls/d3dx9_36/asmshader.y | 8 +- dlls/d3dx9_36/bytecodewriter.c | 191 ++++++++++++++++++++++++++++++- dlls/d3dx9_36/d3dx9_36_private.h | 1 + dlls/d3dx9_36/tests/asm.c | 29 ++++- 5 files changed, 408 insertions(+), 4 deletions(-) diff --git a/dlls/d3dx9_36/asmparser.c b/dlls/d3dx9_36/asmparser.c index e1a32ca7a54..10188a01af5 100644 --- a/dlls/d3dx9_36/asmparser.c +++ b/dlls/d3dx9_36/asmparser.c @@ -161,6 +161,12 @@ static void asmparser_dcl_input_ps_2(struct asm_parser *This, DWORD usage, DWORD } } +static void asmparser_dcl_input_unsupported(struct asm_parser *This, DWORD usage, DWORD num, + DWORD mod, const struct shader_reg *reg) { + asmparser_message(This, "Line %u: Input declaration unsupported in this shader version\n", This->line_no); + set_parse_status(This, PARSE_ERR); +} + static void asmparser_dcl_sampler(struct asm_parser *This, DWORD samptype, DWORD mod, DWORD regnum, unsigned int line_no) { @@ -220,6 +226,84 @@ static void asmparser_sincos(struct asm_parser *This, DWORD mod, DWORD shift, } } +static void asmparser_texcrd(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 != 1) { + asmparser_message(This, "Line %u: Wrong number of source registers in texcrd 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; + } + + /* The job of texcrd is done by mov in later shader versions */ + instr->opcode = BWRITERSIO_MOV; + instr->dstmod = mod; + instr->shift = shift; + instr->comptype = 0; + + This->funcs->dstreg(This, instr, dst); + This->funcs->srcreg(This, instr, 0, &srcs->reg[0]); + + if(!add_instruction(This->shader, instr)) { + ERR("Out of memory\n"); + set_parse_status(This, PARSE_ERR); + } +} + +static void asmparser_texld14(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 != 1) { + asmparser_message(This, "Line %u: texld (PS 1.4) has a wrong number of source registers\n", This->line_no); + set_parse_status(This, PARSE_ERR); + return; + } + + instr = alloc_instr(2); + if(!instr) { + ERR("Error allocating memory for the instruction\n"); + set_parse_status(This, PARSE_ERR); + return; + } + + /* This code is recording a texld instruction, not tex. However, + * texld borrows the opcode of tex + */ + instr->opcode = BWRITERSIO_TEX; + instr->dstmod = mod; + instr->shift = shift; + instr->comptype = 0; + + This->funcs->dstreg(This, instr, dst); + This->funcs->srcreg(This, instr, 0, &srcs->reg[0]); + + /* 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_instr(struct asm_parser *This, DWORD opcode, DWORD mod, DWORD shift, BWRITER_COMPARISON_TYPE comp, @@ -258,6 +342,19 @@ ns */ return; } /* Use the default handling */ + 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); + 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)) { + asmparser_texld14(This, mod, shift, dst, srcs); + return; + } + /* else fallback to the standard behavior */ break; } @@ -563,6 +660,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_4_reg_allowed[] = { + { BWRITERSPR_CONST, 8, FALSE }, + { BWRITERSPR_TEMP, 6, FALSE }, + { BWRITERSPR_TEXTURE, 6, FALSE }, + { BWRITERSPR_INPUT, 2, FALSE }, + { ~0U, 0 } /* End tag */ +}; + +static void asmparser_srcreg_ps_1_4(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_4_reg_allowed)) { + asmparser_message(This, "Line %u: Source register %s not supported in PS 1.4\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, TRUE); + memcpy(&instr->src[num], ®, sizeof(reg)); +} + static const struct allowed_reg_type ps_2_0_reg_allowed[] = { { BWRITERSPR_INPUT, 2, FALSE }, { BWRITERSPR_TEMP, 32, FALSE }, @@ -706,6 +827,22 @@ static void asmparser_dstreg_vs_3(struct asm_parser *This, instr->has_dst = TRUE; } +static void asmparser_dstreg_ps_1_4(struct asm_parser *This, + struct instruction *instr, + const struct shader_reg *dst) { + struct shader_reg reg; + + if(!check_reg_type(dst, ps_1_4_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, ®, sizeof(reg)); + instr->has_dst = TRUE; +} + static void asmparser_dstreg_ps_2(struct asm_parser *This, struct instruction *instr, const struct shader_reg *dst) { @@ -769,6 +906,16 @@ static void asmparser_predicate_unsupported(struct asm_parser *This, set_parse_status(This, PARSE_ERR); } +static void asmparser_coissue_supported(struct asm_parser *This) { + /* this sets the coissue flag of the last instruction added to the shader */ + if(!This->shader) return; + if(This->shader->num_instrs == 0){ + asmparser_message(This, "Line %u: Coissue flag on the first shader instruction\n", This->line_no); + set_parse_status(This, PARSE_ERR); + } + This->shader->instr[This->shader->num_instrs-1]->coissue = TRUE; +} + static void asmparser_coissue_unsupported(struct asm_parser *This) { asmparser_message(This, "Line %u: Coissue is only supported in pixel shaders versions <= 1.4\n", This->line_no); set_parse_status(This, PARSE_ERR); @@ -834,6 +981,26 @@ static const struct asmparser_backend parser_vs_3 = { asmparser_instr, }; +static const struct asmparser_backend parser_ps_1_4 = { + asmparser_constF, + asmparser_constI, + asmparser_constB, + + asmparser_dstreg_ps_1_4, + asmparser_srcreg_ps_1_4, + + 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_2 = { asmparser_constF, asmparser_constI, @@ -1004,6 +1171,22 @@ void create_vs30_parser(struct asm_parser *ret) { ret->funcs = &parser_vs_3; } +void create_ps14_parser(struct asm_parser *ret) { + TRACE_(parsed_shader)("ps_1_4\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, 4); + ret->funcs = &parser_ps_1_4; + gen_oldps_input(ret->shader, 6); +} + void create_ps20_parser(struct asm_parser *ret) { TRACE_(parsed_shader)("ps_2_0\n"); diff --git a/dlls/d3dx9_36/asmshader.y b/dlls/d3dx9_36/asmshader.y index 3ed2128de73..a961af40c7e 100644 --- a/dlls/d3dx9_36/asmshader.y +++ b/dlls/d3dx9_36/asmshader.y @@ -336,8 +336,7 @@ version_marker: VER_VS10 | VER_PS14 { TRACE("Pixel shader 1.4\n"); - set_parse_status(&asm_ctx, PARSE_ERR); - YYABORT; + create_ps14_parser(&asm_ctx); } | VER_PS20 { @@ -370,6 +369,11 @@ complexinstr: instruction TRACE("predicate\n"); asm_ctx.funcs->predicate(&asm_ctx, &$1); } + | '+' instruction + { + TRACE("coissue\n"); + asm_ctx.funcs->coissue(&asm_ctx); + } instruction: INSTR_ADD omods dreg ',' sregs { diff --git a/dlls/d3dx9_36/bytecodewriter.c b/dlls/d3dx9_36/bytecodewriter.c index 9fe2bfcdb7a..5034c7b841a 100644 --- a/dlls/d3dx9_36/bytecodewriter.c +++ b/dlls/d3dx9_36/bytecodewriter.c @@ -578,6 +578,28 @@ 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) { + 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; + return; + } + + /* Declare the shader type and version */ + put_dword(buffer, This->version); + write_constF(shader, buffer, TRUE); +} + static void end(struct bc_writer *This, const struct bwriter_shader *shader, struct bytecode_buffer *buffer) { put_dword(buffer, D3DSIO_END); } @@ -888,6 +910,168 @@ static const struct bytecode_backend vs_1_x_backend = { vs_1_x_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 */ + 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; + + /* Can be mapped 1:1 */ + case BWRITERSPR_TEMP: + case BWRITERSPR_CONST: + 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_4 shader\n"); + This->state = E_INVALIDARG; + return; + } + + token |= d3d9_swizzle(reg->swizzle) & D3DVS_SWIZZLE_MASK; /* already shifted */ + + if(reg->srcmod == BWRITERSPSM_ABS || reg->srcmod == BWRITERSPSM_ABSNEG || + reg->srcmod == BWRITERSPSM_NOT) { + WARN("Invalid source modifier %u for ps_1_4\n", reg->srcmod); + This->state = E_INVALIDARG; + return; + } + token |= d3d9_srcmod(reg->srcmod); + put_dword(buffer, token); +} + +static void ps_1_4_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: /* 1:1 mapping */ + token |= (reg->type << D3DSP_REGTYPE_SHIFT) & D3DSP_REGTYPE_MASK; + token |= reg->regnum & D3DSP_REGNUM_MASK; /* No shift */ + break; + + /* For texkill */ + 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); +} + +static void instr_ps_1_4_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->src[0].regnum == This->t_regnum[0] || + instr->src[0].regnum == This->t_regnum[1] || + instr->src[0].regnum == This->t_regnum[2] || + instr->src[0].regnum == This->t_regnum[3] || + instr->src[0].regnum == This->t_regnum[4] || + instr->src[0].regnum == This->t_regnum[5]) { + /* Similar to a regular mov, but a different opcode */ + token = D3DSIO_TEXCOORD & D3DSI_OPCODE_MASK; + } 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_4\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 void instr_ps_1_4_texld(struct bc_writer *This, + const struct instruction *instr, + struct bytecode_buffer *buffer) { + if(instr->src[1].type != BWRITERSPR_SAMPLER || + instr->src[1].regnum > 5) { + 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; + } + + if(instr->src[1].regnum != instr->dst.regnum) { + WARN("Sampling from sampler s%u to register r%u is not possible in ps_1_4\n", + instr->src[1].regnum, instr->dst.regnum); + This->state = E_INVALIDARG; + return; + } + + This->funcs->opcode(This, instr, D3DSIO_TEX & D3DSI_OPCODE_MASK, 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_4_handlers[] = { + {BWRITERSIO_ADD, instr_handler}, + {BWRITERSIO_NOP, instr_handler}, + {BWRITERSIO_MOV, instr_ps_1_4_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_4_texld}, + {BWRITERSIO_TEXDEPTH, instr_handler}, + {BWRITERSIO_BEM, instr_handler}, + + {BWRITERSIO_PHASE, instr_handler}, + {BWRITERSIO_END, NULL}, +}; + +static const struct bytecode_backend ps_1_4_backend = { + ps_1_4_header, + end, + ps_1_4_srcreg, + ps_1_4_dstreg, + sm_1_x_opcode, + ps_1_4_handlers +}; + static void write_constB(const struct bwriter_shader *shader, struct bytecode_buffer *buffer, BOOL len) { write_const(shader->constB, shader->num_cb, D3DSIO_DEFB, D3DSPR_CONSTBOOL, buffer, len); } @@ -1676,6 +1860,11 @@ static void init_vs30_dx9_writer(struct bc_writer *writer) { writer->funcs = &vs_3_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; +} + static void init_ps20_dx9_writer(struct bc_writer *writer) { TRACE("Creating DirectX9 pixel shader 2.0 writer\n"); writer->funcs = &ps_2_0_backend; @@ -1769,7 +1958,7 @@ static struct bc_writer *create_writer(DWORD version, DWORD dxversion) { WARN("Unsupported dxversion for pixel shader 1.4 requested: %u\n", dxversion); goto fail; } - /* TODO: Set the appropriate writer backend */ + init_ps14_dx9_writer(ret); break; case BWRITERPS_VERSION(2, 0): diff --git a/dlls/d3dx9_36/d3dx9_36_private.h b/dlls/d3dx9_36/d3dx9_36_private.h index 6e7d970b5d7..4be1d582f2f 100644 --- a/dlls/d3dx9_36/d3dx9_36_private.h +++ b/dlls/d3dx9_36/d3dx9_36_private.h @@ -323,6 +323,7 @@ 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_ps14_parser(struct asm_parser *ret); void create_ps20_parser(struct asm_parser *ret); void create_ps2x_parser(struct asm_parser *ret); void create_ps30_parser(struct asm_parser *ret); diff --git a/dlls/d3dx9_36/tests/asm.c b/dlls/d3dx9_36/tests/asm.c index 0b8b77782b2..fe3eee06023 100644 --- a/dlls/d3dx9_36/tests/asm.c +++ b/dlls/d3dx9_36/tests/asm.c @@ -561,6 +561,33 @@ static void ps_1_4_test(void) { "texdepth r5\n", {0xffff0104, 0x00000057, 0x800f0005, 0x0000ffff} }, + { /* shader 15 */ + "ps_1_4\n" + "add r0, r1, r2_bx2\n", + {0xffff0104, 0x00000002, 0x800f0000, 0x80e40001, 0x84e40002, 0x0000ffff} + }, + { /* shader 16 */ + "ps_1_4\n" + "add_x4 r0, r1, r2\n", + {0xffff0104, 0x00000002, 0x820f0000, 0x80e40001, 0x80e40002, 0x0000ffff} + }, + { /* shader 17 */ + "ps_1_4\n" + "add r0.rgb, r1, r2\n" + "+add r0.a, r1, r2\n", + {0xffff0104, 0x00000002, 0x80070000, 0x80e40001, 0x80e40002, 0x40000002, + 0x80080000, 0x80e40001, 0x80e40002, 0x0000ffff} + }, + { /* shader 18 */ + "ps_1_4\n" + "texdepth_x2 r5\n", + {0xffff0104, 0x00000057, 0x810f0005, 0x0000ffff} + }, + { /* shader 18 */ + "ps_1_4\n" + "bem_d2 r1, c0, r0\n", + {0xffff0104, 0x00000059, 0x8f0f0001, 0xa0e40000, 0x80e40000, 0x0000ffff} + }, }; exec_tests("ps_1_4", tests, sizeof(tests) / sizeof(tests[0])); @@ -1627,7 +1654,7 @@ START_TEST(asm) todo_wine ps_1_1_test(); vs_1_1_test(); todo_wine ps_1_3_test(); - todo_wine ps_1_4_test(); + ps_1_4_test(); vs_2_0_test(); vs_2_x_test(); ps_2_0_test();