From 95afbeadf5d34310632cc829bcffd2eff447cfba Mon Sep 17 00:00:00 2001 From: Matteo Bruni Date: Sun, 28 Mar 2010 21:16:09 +0200 Subject: [PATCH] d3dx9: Implement D3DXAssembleShader function, really basic shader assembler. Currently it only accepts a minimal subset of the syntax (e.g. just an instruction and two register types supported) and doesn't produce any real output (i.e. shader bytecode). --- .gitignore | 3 + dlls/d3dx9_36/Makefile.in | 6 + dlls/d3dx9_36/asmparser.c | 143 +++++++++++++ dlls/d3dx9_36/asmshader.l | 131 ++++++++++++ dlls/d3dx9_36/asmshader.y | 345 +++++++++++++++++++++++++++++++ dlls/d3dx9_36/asmutils.c | 58 ++++++ dlls/d3dx9_36/bytecodewriter.c | 139 +++++++++++++ dlls/d3dx9_36/d3dx9_36_private.h | 266 ++++++++++++++++++++++++ dlls/d3dx9_36/shader.c | 65 +++++- dlls/d3dx9_36/tests/asm.c | 6 +- 10 files changed, 1159 insertions(+), 3 deletions(-) create mode 100644 dlls/d3dx9_36/asmparser.c create mode 100644 dlls/d3dx9_36/asmshader.l create mode 100644 dlls/d3dx9_36/asmshader.y create mode 100644 dlls/d3dx9_36/asmutils.c create mode 100644 dlls/d3dx9_36/bytecodewriter.c diff --git a/.gitignore b/.gitignore index 264e2de9010..3cf9b2c28dc 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,9 @@ dlls/actxprxy/actxprxy_urlhist_p.c dlls/advapi32/svcctl.h dlls/advapi32/svcctl_c.c dlls/atl/atliface.h +dlls/d3dx9_36/asmshader.tab.c +dlls/d3dx9_36/asmshader.tab.h +dlls/d3dx9_36/asmshader.yy.c dlls/dispex/disp_ex.h dlls/dispex/disp_ex_p.c dlls/dxdiagn/fil_data.h diff --git a/dlls/d3dx9_36/Makefile.in b/dlls/d3dx9_36/Makefile.in index 3d6ef4ec629..d7f558b88e5 100644 --- a/dlls/d3dx9_36/Makefile.in +++ b/dlls/d3dx9_36/Makefile.in @@ -8,6 +8,9 @@ IMPORTS = d3d9 gdi32 user32 kernel32 EXTRALIBS = $(LIBWPP) C_SRCS = \ + asmparser.c \ + asmutils.c \ + bytecodewriter.c \ core.c \ d3dx9_36_main.c \ effect.c \ @@ -20,6 +23,9 @@ C_SRCS = \ texture.c \ util.c +LEX_SRCS = asmshader.l +BISON_SRCS = asmshader.y + RC_SRCS = version.rc @MAKE_DLL_RULES@ diff --git a/dlls/d3dx9_36/asmparser.c b/dlls/d3dx9_36/asmparser.c new file mode 100644 index 00000000000..1af001bb93f --- /dev/null +++ b/dlls/d3dx9_36/asmparser.c @@ -0,0 +1,143 @@ +/* + * Direct3D asm shader parser + * + * Copyright 2008 Stefan Dösinger + * Copyright 2009 Matteo Bruni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +#include "d3dx9_36_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asmshader); +WINE_DECLARE_DEBUG_CHANNEL(parsed_shader); + + +/**************************************************************** + * Common(non-version specific) shader parser control code * + ****************************************************************/ + +static void asmparser_end(struct asm_parser *This) { + TRACE("Finalizing shader\n"); +} + +static void asmparser_instr(struct asm_parser *This, DWORD opcode, + DWORD mod, DWORD shift, + BWRITER_COMPARISON_TYPE comp, + const struct shader_reg *dst, + const struct src_regs *srcs, int expectednsrcs) { + struct instruction *instr; + unsigned int i; + BOOL firstreg = TRUE; + unsigned int src_count = srcs ? srcs->count : 0; + + if(!This->shader) return; + + TRACE_(parsed_shader)("%s ", debug_print_opcode(opcode)); + if(dst) { + TRACE_(parsed_shader)("%s", debug_print_dstreg(dst, This->shader->type)); + firstreg = FALSE; + } + for(i = 0; i < src_count; i++) { + if(!firstreg) TRACE_(parsed_shader)(", "); + else firstreg = FALSE; + TRACE_(parsed_shader)("%s", debug_print_srcreg(&srcs->reg[i], + This->shader->type)); + } + TRACE_(parsed_shader)("\n"); + + if(src_count != expectednsrcs) { + asmparser_message(This, "Line %u: Wrong number of source registers\n", This->line_no); + set_parse_status(This, PARSE_ERR); + return; + } + + instr = alloc_instr(src_count); + if(!instr) { + ERR("Error allocating memory for the instruction\n"); + set_parse_status(This, PARSE_ERR); + return; + } + + instr->opcode = opcode; + instr->dstmod = mod; + instr->shift = shift; + instr->comptype = comp; + if(dst) This->funcs->dstreg(This, instr, dst); + for(i = 0; i < src_count; i++) { + This->funcs->srcreg(This, instr, i, &srcs->reg[i]); + } + + if(!add_instruction(This->shader, instr)) { + ERR("Out of memory\n"); + set_parse_status(This, PARSE_ERR); + } +} + +static void asmparser_srcreg_vs_3(struct asm_parser *This, + struct instruction *instr, int num, + const struct shader_reg *src) { + memcpy(&instr->src[num], src, sizeof(*src)); +} + +static void asmparser_dstreg_vs_3(struct asm_parser *This, + struct instruction *instr, + const struct shader_reg *dst) { + memcpy(&instr->dst, dst, sizeof(*dst)); + instr->has_dst = TRUE; +} + +static void asmparser_predicate_unsupported(struct asm_parser *This, + const struct shader_reg *predicate) { + asmparser_message(This, "Line %u: Predicate not supported in < VS 2.0 or PS 2.x\n", This->line_no); + set_parse_status(This, PARSE_ERR); +} + +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); +} + +static const struct asmparser_backend parser_vs_3 = { + asmparser_dstreg_vs_3, + asmparser_srcreg_vs_3, + + asmparser_predicate_unsupported, + asmparser_coissue_unsupported, + + asmparser_end, + + asmparser_instr, +}; + +void create_vs30_parser(struct asm_parser *ret) { + TRACE_(parsed_shader)("vs_3_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(3, 0); + ret->funcs = &parser_vs_3; +} diff --git a/dlls/d3dx9_36/asmshader.l b/dlls/d3dx9_36/asmshader.l new file mode 100644 index 00000000000..99af3809074 --- /dev/null +++ b/dlls/d3dx9_36/asmshader.l @@ -0,0 +1,131 @@ +/* + * Direct3D shader assembler + * + * Copyright 2008 Stefan Dösinger + * Copyright 2009 Matteo Bruni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +%{ +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +#include "d3dx9_36_private.h" +#include "asmshader.tab.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asmshader); +%} + +%option noyywrap +%option prefix="asmshader_" +%option noinput nounput + +/* Registers */ +REG_TEMP r[0-9]+ +/* for relative addressing in the form o[x], v[x] and c[x] */ +REG_CONSTFLOAT c[0-9]* + +PREPROCESSORDIRECTIVE #[^\n]*\n + +/* Comments */ +DOUBLESLASHCOMMENT "//"[^\n]* +SEMICOLONCOMMENT ";"[^\n]* + +/* Whitespaces are spaces, tabs and newlines */ +WHITESPACE [ \t]+ +NEWLINE (\n)|(\r\n) + +COMMA "," + +IMMVAL \-?(([0-9]+)|([0-9]*\.[0-9]+))(f)? + +ANY (.) + +%% + + /* Common instructions(vertex and pixel shaders) */ +mov {return INSTR_MOV; } + +{REG_TEMP} { + asmshader_lval.regnum = atoi(yytext + 1); + return REG_TEMP; + } +{REG_CONSTFLOAT} { + asmshader_lval.regnum = atoi(yytext + 1); + return REG_CONSTFLOAT; + } + + /* Shader versions. These are important to select the correct + * parser profile. + */ +vs\.1\.0|vs_1_0 {return VER_VS10; } +vs\.1\.1|vs_1_1 {return VER_VS11; } + +vs_2_0 {return VER_VS20; } +vs_2_x {return VER_VS2X; } +vs_3_0 {return VER_VS30; } + +ps\.1\.0|ps_1_0 {return VER_PS10; } +ps\.1\.1|ps_1_1 {return VER_PS11; } +ps\.1\.2|ps_1_2 {return VER_PS12; } +ps\.1\.3|ps_1_3 {return VER_PS13; } +ps\.1\.4|ps_1_4 {return VER_PS14; } + +ps_2_0 {return VER_PS20; } +ps_2_x {return VER_PS2X; } +ps_3_0 {return VER_PS30; } + +{COMMA} {return yytext[0]; } +- {return yytext[0]; } +\( {return yytext[0]; } +\) {return yytext[0]; } + +{PREPROCESSORDIRECTIVE} { + /* TODO: update current line information */ + TRACE("line info update: %s", yytext); + } + + /* Skip comments */ +{DOUBLESLASHCOMMENT} { } +{SEMICOLONCOMMENT} { } + +{WHITESPACE} { /* Do nothing */ } +{NEWLINE} { + asm_ctx.line_no++; + } + +{ANY} { + asmparser_message(&asm_ctx, "Line %u: Unexpected input %s\n", asm_ctx.line_no, yytext); + set_parse_status(&asm_ctx, PARSE_ERR); + } + +%% + +struct bwriter_shader *SlAssembleShader(const char *text, char **messages) { + struct bwriter_shader *ret = NULL; + YY_BUFFER_STATE buffer; + TRACE("%p, %p\n", text, messages); + + buffer = asmshader__scan_string(text); + asmshader__switch_to_buffer(buffer); + + ret = parse_asm_shader(messages); + + asmshader__delete_buffer(buffer); + + return ret; +} diff --git a/dlls/d3dx9_36/asmshader.y b/dlls/d3dx9_36/asmshader.y new file mode 100644 index 00000000000..8bc93c17539 --- /dev/null +++ b/dlls/d3dx9_36/asmshader.y @@ -0,0 +1,345 @@ +/* + * Direct3D shader assembler + * + * Copyright 2008 Stefan Dösinger + * Copyright 2009 Matteo Bruni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +%{ +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +#include "d3dx9_36_private.h" +#include "asmshader.tab.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(asmshader); + +struct asm_parser asm_ctx; + +/* Needed lexer functions declarations */ +void asmshader_error(const char *s); +int asmshader_lex(void); + +void set_rel_reg(struct shader_reg *reg, struct rel_reg *rel) { + reg->rel_reg = NULL; +} + +%} + +%union { + unsigned int regnum; + struct shader_reg reg; + struct { + DWORD swizzle; + DWORD writemask; + } swizzle_wmask; + DWORD writemask; + DWORD swizzle; + struct { + DWORD mod; + DWORD shift; + } modshift; + struct rel_reg rel_reg; + struct src_regs sregs; +} + +/* Common instructions between vertex and pixel shaders */ +%token INSTR_MOV + +/* Registers */ +%token REG_TEMP +%token REG_CONSTFLOAT + +/* Version tokens */ +%token VER_VS10 +%token VER_VS11 +%token VER_VS20 +%token VER_VS2X +%token VER_VS30 + +%token VER_PS10 +%token VER_PS11 +%token VER_PS12 +%token VER_PS13 +%token VER_PS14 +%token VER_PS20 +%token VER_PS2X +%token VER_PS30 + + +%type dreg_name +%type dreg +%type sreg_name +%type sreg +%type swizzle +%type omods +%type rel_reg +%type sregs + +%% + +shader: version_marker instructions + { + asm_ctx.funcs->end(&asm_ctx); + } + +version_marker: VER_VS10 + { + TRACE("Vertex shader 1.0\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_VS11 + { + TRACE("Vertex shader 1.1\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_VS20 + { + TRACE("Vertex shader 2.0\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_VS2X + { + TRACE("Vertex shader 2.x\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_VS30 + { + TRACE("Vertex shader 3.0\n"); + create_vs30_parser(&asm_ctx); + } + | VER_PS10 + { + TRACE("Pixel shader 1.0\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS11 + { + TRACE("Pixel shader 1.1\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS12 + { + TRACE("Pixel shader 1.2\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS13 + { + TRACE("Pixel shader 1.3\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS14 + { + TRACE("Pixel shader 1.4\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS20 + { + TRACE("Pixel shader 2.0\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS2X + { + TRACE("Pixel shader 2.x\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + | VER_PS30 + { + TRACE("Pixel shader 3.0\n"); + set_parse_status(&asm_ctx, PARSE_ERR); + YYABORT; + } + +instructions: /* empty */ + | instructions complexinstr + { + /* Nothing to do */ + } + +complexinstr: instruction + { + + } + +instruction: INSTR_MOV omods dreg ',' sregs + { + TRACE("MOV\n"); + asm_ctx.funcs->instr(&asm_ctx, BWRITERSIO_MOV, $2.mod, $2.shift, 0, &$3, &$5, 1); + } + +dreg: dreg_name rel_reg + { + $$.regnum = $1.regnum; + $$.type = $1.type; + $$.writemask = BWRITERSP_WRITEMASK_ALL; + $$.srcmod = BWRITERSPSM_NONE; + set_rel_reg(&$$, &$2); + } + +dreg_name: REG_TEMP + { + $$.regnum = $1; $$.type = BWRITERSPR_TEMP; + } + +swizzle: /* empty */ + { + $$ = BWRITERVS_NOSWIZZLE; + TRACE("Default swizzle: %08x\n", $$); + } + +omods: /* Empty */ + { + $$.mod = 0; + $$.shift = 0; + } + +sregs: sreg + { + $$.reg[0] = $1; + $$.count = 1; + } + | sregs ',' sreg + { + if($$.count == MAX_SRC_REGS){ + asmparser_message(&asm_ctx, "Line %u: Too many source registers in this instruction\n", + asm_ctx.line_no); + set_parse_status(&asm_ctx, PARSE_ERR); + } + else + $$.reg[$$.count++] = $3; + } + +sreg: sreg_name rel_reg swizzle + { + $$.type = $1.type; + $$.regnum = $1.regnum; + $$.swizzle = $3; + $$.srcmod = BWRITERSPSM_NONE; + set_rel_reg(&$$, &$2); + } + +rel_reg: /* empty */ + { + $$.has_rel_reg = FALSE; + $$.additional_offset = 0; + } + +sreg_name: REG_TEMP + { + $$.regnum = $1; $$.type = BWRITERSPR_TEMP; + } + | REG_CONSTFLOAT + { + $$.regnum = $1; $$.type = BWRITERSPR_CONST; + } + +%% + +void asmshader_error (char const *s) { + asmparser_message(&asm_ctx, "Line %u: Error \"%s\" from bison\n", asm_ctx.line_no, s); + set_parse_status(&asm_ctx, PARSE_ERR); +} + +/* Error reporting function */ +void asmparser_message(struct asm_parser *ctx, const char *fmt, ...) { + va_list args; + char* newbuffer; + int rc, newsize; + + if(ctx->messagecapacity == 0) { + ctx->messages = asm_alloc(MESSAGEBUFFER_INITIAL_SIZE); + if(ctx->messages == NULL) { + ERR("Error allocating memory for parser messages\n"); + return; + } + ctx->messagecapacity = MESSAGEBUFFER_INITIAL_SIZE; + } + + while(1) { + va_start(args, fmt); + rc = vsnprintf(ctx->messages + ctx->messagesize, + ctx->messagecapacity - ctx->messagesize, fmt, args); + va_end(args); + + if (rc < 0 || /* C89 */ + rc >= ctx->messagecapacity - ctx->messagesize) { /* C99 */ + /* Resize the buffer */ + newsize = ctx->messagecapacity * 2; + newbuffer = asm_realloc(ctx->messages, newsize); + if(newbuffer == NULL){ + ERR("Error reallocating memory for parser messages\n"); + return; + } + ctx->messages = newbuffer; + ctx->messagecapacity = newsize; + } else { + ctx->messagesize += rc; + return; + } + } +} + +/* New status is the worst between current status and parameter value */ +void set_parse_status(struct asm_parser *ctx, enum parse_status status) { + if(status == PARSE_ERR) ctx->status = PARSE_ERR; + else if(status == PARSE_WARN && ctx->status == PARSE_SUCCESS) ctx->status = PARSE_WARN; +} + +struct bwriter_shader *parse_asm_shader(char **messages) { + struct bwriter_shader *ret = NULL; + + asm_ctx.shader = NULL; + asm_ctx.status = PARSE_SUCCESS; + asm_ctx.messagesize = asm_ctx.messagecapacity = 0; + asm_ctx.line_no = 1; + + asmshader_parse(); + + if(asm_ctx.status != PARSE_ERR) ret = asm_ctx.shader; + else if(asm_ctx.shader) SlDeleteShader(asm_ctx.shader); + + if(messages) { + if(asm_ctx.messagesize) { + /* Shrink the buffer to the used size */ + *messages = asm_realloc(asm_ctx.messages, asm_ctx.messagesize + 1); + if(!*messages) { + ERR("Out of memory, no messages reported\n"); + asm_free(asm_ctx.messages); + } + } else { + *messages = NULL; + } + } else { + if(asm_ctx.messagecapacity) asm_free(asm_ctx.messages); + } + + return ret; +} diff --git a/dlls/d3dx9_36/asmutils.c b/dlls/d3dx9_36/asmutils.c new file mode 100644 index 00000000000..50000a54fd7 --- /dev/null +++ b/dlls/d3dx9_36/asmutils.c @@ -0,0 +1,58 @@ +/* + * Direct3D shader library utility routines + * + * Copyright 2008 Stefan Dösinger + * Copyright 2009 Matteo Bruni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/debug.h" + +#include "d3dx9_36_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asmshader); + +static const char *get_regname(const struct shader_reg *reg, shader_type st) { + switch(reg->type) { + case BWRITERSPR_TEMP: + return wine_dbg_sprintf("r%u", reg->regnum); + case BWRITERSPR_CONST: + return wine_dbg_sprintf("c%u", reg->regnum); + default: return "unknown regname"; + } +} + +const char *debug_print_dstreg(const struct shader_reg *reg, shader_type st) { + return get_regname(reg, st); +} + +const char *debug_print_srcreg(const struct shader_reg *reg, shader_type st) { + switch(reg->srcmod) { + case BWRITERSPSM_NONE: + return get_regname(reg, st); + } + return "Unknown modifier"; +} + +const char *debug_print_opcode(DWORD opcode) { + switch(opcode){ + case BWRITERSIO_MOV: return "mov"; + + default: return "unknown"; + } +} diff --git a/dlls/d3dx9_36/bytecodewriter.c b/dlls/d3dx9_36/bytecodewriter.c new file mode 100644 index 00000000000..30f7d1ffb32 --- /dev/null +++ b/dlls/d3dx9_36/bytecodewriter.c @@ -0,0 +1,139 @@ +/* + * Direct3D bytecode output functions + * + * Copyright 2008 Stefan Dösinger + * Copyright 2009 Matteo Bruni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +#include "d3dx9_36_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(asmshader); + +/**************************************************************** + * General assembler shader construction helper routines follow * + ****************************************************************/ +/* struct instruction *alloc_instr + * + * Allocates a new instruction structure with srcs registers + * + * Parameters: + * srcs: Number of source registers to allocate + * + * Returns: + * A pointer to the allocated instruction structure + * NULL in case of an allocation failure + */ +struct instruction *alloc_instr(unsigned int srcs) { + struct instruction *ret = asm_alloc(sizeof(*ret)); + if(!ret) { + ERR("Failed to allocate memory for an instruction structure\n"); + return NULL; + } + + if(srcs) { + ret->src = asm_alloc(srcs * sizeof(*ret->src)); + if(!ret->src) { + ERR("Failed to allocate memory for instruction registers\n"); + asm_free(ret); + return NULL; + } + ret->num_srcs = srcs; + } + return ret; +} + +/* void add_instruction + * + * Adds a new instruction to the shader's instructions array and grows the instruction array + * if needed. + * + * The function does NOT copy the instruction structure. Make sure not to release the + * instruction or any of its substructures like registers. + * + * Parameters: + * shader: Shader to add the instruction to + * instr: Instruction to add to the shader + */ +BOOL add_instruction(struct bwriter_shader *shader, struct instruction *instr) { + struct instruction **new_instructions; + + if(!shader) return FALSE; + + if(shader->instr_alloc_size == 0) { + shader->instr = asm_alloc(sizeof(*shader->instr) * INSTRARRAY_INITIAL_SIZE); + if(!shader->instr) { + ERR("Failed to allocate the shader instruction array\n"); + return FALSE; + } + shader->instr_alloc_size = INSTRARRAY_INITIAL_SIZE; + } else if(shader->instr_alloc_size == shader->num_instrs) { + new_instructions = asm_realloc(shader->instr, + sizeof(*shader->instr) * (shader->instr_alloc_size) * 2); + if(!new_instructions) { + ERR("Failed to grow the shader instruction array\n"); + return FALSE; + } + shader->instr = new_instructions; + shader->instr_alloc_size = shader->instr_alloc_size * 2; + } else if(shader->num_instrs > shader->instr_alloc_size) { + ERR("More instructions than allocated. This should not happen\n"); + return FALSE; + } + + shader->instr[shader->num_instrs] = instr; + shader->num_instrs++; + return TRUE; +} + +void SlDeleteShader(struct bwriter_shader *shader) { + unsigned int i, j; + + TRACE("Deleting shader %p\n", shader); + + for(i = 0; i < shader->num_cf; i++) { + asm_free(shader->constF[i]); + } + asm_free(shader->constF); + for(i = 0; i < shader->num_ci; i++) { + asm_free(shader->constI[i]); + } + asm_free(shader->constI); + for(i = 0; i < shader->num_cb; i++) { + asm_free(shader->constB[i]); + } + asm_free(shader->constB); + + asm_free(shader->inputs); + asm_free(shader->outputs); + asm_free(shader->samplers); + + for(i = 0; i < shader->num_instrs; i++) { + for(j = 0; j < shader->instr[i]->num_srcs; j++) { + asm_free(shader->instr[i]->src[j].rel_reg); + } + asm_free(shader->instr[i]->src); + asm_free(shader->instr[i]); + } + asm_free(shader->instr); + + asm_free(shader); +} diff --git a/dlls/d3dx9_36/d3dx9_36_private.h b/dlls/d3dx9_36/d3dx9_36_private.h index 13b84df22d3..90aa747a1c4 100644 --- a/dlls/d3dx9_36/d3dx9_36_private.h +++ b/dlls/d3dx9_36/d3dx9_36_private.h @@ -2,6 +2,8 @@ * Copyright (C) 2002 Raphael Junqueira * Copyright (C) 2008 David Adam * Copyright (C) 2008 Tony Wasserka + * Copyright (C) 2008 Stefan Dösinger + * Copyright (C) 2009 Matteo Bruni * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -128,12 +130,276 @@ typedef struct ID3DXSpriteImpl int allocated_sprites; /* number of (pre-)allocated sprites */ } ID3DXSpriteImpl; +/* Shader assembler definitions */ +typedef enum _shader_type { + ST_VERTEX, + ST_PIXEL, +} shader_type; + +typedef enum BWRITER_COMPARISON_TYPE { + BWRITER_COMPARISON_NONE = 0, +} BWRITER_COMPARISON_TYPE; + +struct shader_reg { + DWORD type; + DWORD regnum; + struct shader_reg *rel_reg; + DWORD srcmod; + union { + DWORD swizzle; + DWORD writemask; + }; +}; + +struct instruction { + DWORD opcode; + DWORD dstmod; + DWORD shift; + BWRITER_COMPARISON_TYPE comptype; + BOOL has_dst; + struct shader_reg dst; + struct shader_reg *src; + unsigned int num_srcs; /* For freeing the rel_regs */ +}; + +struct declaration { + DWORD usage, usage_idx; + DWORD regnum; + DWORD writemask; +}; + +struct samplerdecl { + DWORD type; + DWORD regnum; + unsigned int line_no; /* for error messages */ +}; + +#define INSTRARRAY_INITIAL_SIZE 8 +struct bwriter_shader { + shader_type type; + + /* Shader version selected */ + DWORD version; + + /* Local constants. Every constant that is not defined below is loaded from + * the global constant set at shader runtime + */ + struct constant **constF; + struct constant **constI; + struct constant **constB; + unsigned int num_cf, num_ci, num_cb; + + /* Declared input and output varyings */ + struct declaration *inputs, *outputs; + unsigned int num_inputs, num_outputs; + struct samplerdecl *samplers; + unsigned int num_samplers; + + /* Are special pixel shader 3.0 registers declared? */ + BOOL vPos, vFace; + + /* Array of shader instructions - The shader code itself */ + struct instruction **instr; + unsigned int num_instrs, instr_alloc_size; +}; + +static inline LPVOID asm_alloc(SIZE_T size) { + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); +} + +static inline LPVOID asm_realloc(LPVOID ptr, SIZE_T size) { + return HeapReAlloc(GetProcessHeap(), 0, ptr, size); +} + +static inline BOOL asm_free(LPVOID ptr) { + return HeapFree(GetProcessHeap(), 0, ptr); +} + +struct asm_parser; + +/* This structure is only used in asmshader.y, but since the .l file accesses the semantic types + * too it has to know it as well + */ +struct rel_reg { + BOOL has_rel_reg; + DWORD type; + DWORD additional_offset; + DWORD rel_regnum; + DWORD swizzle; +}; + +#define MAX_SRC_REGS 4 + +struct src_regs { + struct shader_reg reg[MAX_SRC_REGS]; + unsigned int count; +}; + +struct asmparser_backend { + void (*dstreg)(struct asm_parser *This, struct instruction *instr, + const struct shader_reg *dst); + void (*srcreg)(struct asm_parser *This, struct instruction *instr, int num, + const struct shader_reg *src); + + void (*predicate)(struct asm_parser *This, + const struct shader_reg *predicate); + void (*coissue)(struct asm_parser *This); + + void (*end)(struct asm_parser *This); + + void (*instr)(struct asm_parser *This, DWORD opcode, DWORD mod, DWORD shift, + BWRITER_COMPARISON_TYPE comp, const struct shader_reg *dst, + const struct src_regs *srcs, int expectednsrcs); +}; + +struct instruction *alloc_instr(unsigned int srcs); +BOOL add_instruction(struct bwriter_shader *shader, struct instruction *instr); + #define MESSAGEBUFFER_INITIAL_SIZE 256 +struct asm_parser { + /* The function table of the parser implementation */ + const struct asmparser_backend *funcs; + + /* Private data follows */ + struct bwriter_shader *shader; + unsigned int m3x3pad_count; + + enum parse_status { + PARSE_SUCCESS = 0, + PARSE_WARN = 1, + PARSE_ERR = 2 + } status; + char *messages; + unsigned int messagesize; + unsigned int messagecapacity; + unsigned int line_no; +}; + +extern struct asm_parser asm_ctx; + +void create_vs30_parser(struct asm_parser *ret); + +struct bwriter_shader *parse_asm_shader(char **messages); + #ifdef __GNUC__ #define PRINTF_ATTR(fmt,args) __attribute__((format (printf,fmt,args))) #else #define PRINTF_ATTR(fmt,args) #endif +void asmparser_message(struct asm_parser *ctx, const char *fmt, ...) PRINTF_ATTR(2,3); +void set_parse_status(struct asm_parser *ctx, enum parse_status status); + +/* A reasonable value as initial size */ +#define BYTECODEBUFFER_INITIAL_SIZE 32 +struct bytecode_buffer { + DWORD *data; + DWORD size; + DWORD alloc_size; + /* For tracking rare out of memory situations without passing + * return values around everywhere + */ + HRESULT state; +}; + +struct bc_writer; /* Predeclaration for use in vtable parameters */ + +typedef void (*instr_writer)(struct bc_writer *This, + const struct instruction *instr, + struct bytecode_buffer *buffer); + +struct bytecode_backend { + void (*header)(struct bc_writer *This, const struct bwriter_shader *shader, + struct bytecode_buffer *buffer); + void (*end)(struct bc_writer *This, const struct bwriter_shader *shader, + struct bytecode_buffer *buffer); + void (*srcreg)(struct bc_writer *This, const struct shader_reg *reg, + struct bytecode_buffer *buffer); + void (*dstreg)(struct bc_writer *This, const struct shader_reg *reg, + struct bytecode_buffer *buffer, DWORD shift, DWORD mod); + void (*opcode)(struct bc_writer *This, const struct instruction *instr, + DWORD token, struct bytecode_buffer *buffer); + + const struct instr_handler_table { + DWORD opcode; + instr_writer func; + } *instructions; +}; + +/* Bytecode writing stuff */ +struct bc_writer { + const struct bytecode_backend *funcs; + + /* Avoid result checking */ + HRESULT state; + + DWORD version; +}; + +/* Debug utility routines */ +const char *debug_print_dstreg(const struct shader_reg *reg, shader_type st); +const char *debug_print_srcreg(const struct shader_reg *reg, shader_type st); +const char *debug_print_opcode(DWORD opcode); + +/* + Enumerations and defines used in the bytecode writer + intermediate representation +*/ +typedef enum _BWRITERSHADER_INSTRUCTION_OPCODE_TYPE { + BWRITERSIO_MOV = 1, + + BWRITERSIO_COMMENT = 0xfffe, + BWRITERSIO_END = 0Xffff, +} BWRITERSHADER_INSTRUCTION_OPCODE_TYPE; + +typedef enum _BWRITERSHADER_PARAM_REGISTER_TYPE { + BWRITERSPR_TEMP = 0, + BWRITERSPR_CONST = 2, +} BWRITERSHADER_PARAM_REGISTER_TYPE; + +#define BWRITERSP_WRITEMASK_0 0x1 /* .x r */ +#define BWRITERSP_WRITEMASK_1 0x2 /* .y g */ +#define BWRITERSP_WRITEMASK_2 0x4 /* .z b */ +#define BWRITERSP_WRITEMASK_3 0x8 /* .w a */ +#define BWRITERSP_WRITEMASK_ALL 0xf /* all */ + +typedef enum _BWRITERSHADER_PARAM_SRCMOD_TYPE { + BWRITERSPSM_NONE = 0, +} BWRITERSHADER_PARAM_SRCMOD_TYPE; + +#define BWRITER_SM1_VS 0xfffe +#define BWRITER_SM1_PS 0xffff + +#define BWRITERPS_VERSION(major, minor) ((BWRITER_SM1_PS << 16) | ((major) << 8) | (minor)) +#define BWRITERVS_VERSION(major, minor) ((BWRITER_SM1_VS << 16) | ((major) << 8) | (minor)) + +#define BWRITERVS_SWIZZLE_SHIFT 16 +#define BWRITERVS_SWIZZLE_MASK (0xFF << BWRITERVS_SWIZZLE_SHIFT) + +#define BWRITERVS_X_X (0 << BWRITERVS_SWIZZLE_SHIFT) +#define BWRITERVS_X_Y (1 << BWRITERVS_SWIZZLE_SHIFT) +#define BWRITERVS_X_Z (2 << BWRITERVS_SWIZZLE_SHIFT) +#define BWRITERVS_X_W (3 << BWRITERVS_SWIZZLE_SHIFT) + +#define BWRITERVS_Y_X (0 << (BWRITERVS_SWIZZLE_SHIFT + 2)) +#define BWRITERVS_Y_Y (1 << (BWRITERVS_SWIZZLE_SHIFT + 2)) +#define BWRITERVS_Y_Z (2 << (BWRITERVS_SWIZZLE_SHIFT + 2)) +#define BWRITERVS_Y_W (3 << (BWRITERVS_SWIZZLE_SHIFT + 2)) + +#define BWRITERVS_Z_X (0 << (BWRITERVS_SWIZZLE_SHIFT + 4)) +#define BWRITERVS_Z_Y (1 << (BWRITERVS_SWIZZLE_SHIFT + 4)) +#define BWRITERVS_Z_Z (2 << (BWRITERVS_SWIZZLE_SHIFT + 4)) +#define BWRITERVS_Z_W (3 << (BWRITERVS_SWIZZLE_SHIFT + 4)) + +#define BWRITERVS_W_X (0 << (BWRITERVS_SWIZZLE_SHIFT + 6)) +#define BWRITERVS_W_Y (1 << (BWRITERVS_SWIZZLE_SHIFT + 6)) +#define BWRITERVS_W_Z (2 << (BWRITERVS_SWIZZLE_SHIFT + 6)) +#define BWRITERVS_W_W (3 << (BWRITERVS_SWIZZLE_SHIFT + 6)) + +#define BWRITERVS_NOSWIZZLE (BWRITERVS_X_X | BWRITERVS_Y_Y | BWRITERVS_Z_Z | BWRITERVS_W_W) + +struct bwriter_shader *SlAssembleShader(const char *text, char **messages); +void SlDeleteShader(struct bwriter_shader *shader); + #endif /* __WINE_D3DX9_36_PRIVATE_H */ diff --git a/dlls/d3dx9_36/shader.c b/dlls/d3dx9_36/shader.c index b41fee98d57..bb9353fb620 100644 --- a/dlls/d3dx9_36/shader.c +++ b/dlls/d3dx9_36/shader.c @@ -382,6 +382,68 @@ static int wpp_close_output(void) return 1; } +HRESULT assemble_shader(const char *preprocShader, const char *preprocMessages, + LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs) +{ + struct bwriter_shader *shader; + char *messages = NULL; + HRESULT hr; + LPD3DXBUFFER buffer; + int size; + char *pos; + + shader = SlAssembleShader(preprocShader, &messages); + + if(messages || preprocMessages) + { + if(preprocMessages) + { + TRACE("Preprocessor messages:\n"); + TRACE("%s", preprocMessages); + } + if(messages) + { + TRACE("Assembler messages:\n"); + TRACE("%s", messages); + } + + TRACE("Shader source:\n"); + TRACE("%s\n", debugstr_a(preprocShader)); + + size = (messages ? strlen(messages) : 0) + + (preprocMessages ? strlen(preprocMessages) : 0) + 1; + hr = D3DXCreateBuffer(size, &buffer); + if(FAILED(hr)) + { + HeapFree(GetProcessHeap(), 0, messages); + if(shader) SlDeleteShader(shader); + return hr; + } + pos = ID3DXBuffer_GetBufferPointer(buffer); + if(preprocMessages) + { + CopyMemory(pos, preprocMessages, strlen(preprocMessages) + 1); + pos += strlen(preprocMessages); + } + if(messages) + CopyMemory(pos, messages, strlen(messages) + 1); + + *ppErrorMsgs = buffer; + + HeapFree(GetProcessHeap(), 0, messages); + } + + if(shader == NULL) + { + ERR("Asm reading failed\n"); + return D3DXERR_INVALIDDATA; + } + + /* TODO: generate bytecode from the shader */ + SlDeleteShader(shader); + return D3DXERR_INVALIDDATA; +} + HRESULT WINAPI D3DXAssembleShader(LPCSTR data, UINT data_len, CONST D3DXMACRO* defines, @@ -459,8 +521,7 @@ HRESULT WINAPI D3DXAssembleShader(LPCSTR data, goto cleanup; } - FIXME("(%p, %d, %p, %p, %x, %p, %p): stub\n", data, data_len, defines, include, flags, shader, error_messages); - hr = D3DERR_INVALIDCALL; + hr = assemble_shader(wpp_output, wpp_messages, shader, error_messages); cleanup: /* Remove the previously added defines */ diff --git a/dlls/d3dx9_36/tests/asm.c b/dlls/d3dx9_36/tests/asm.c index 5c95a2f5c0c..662239030c2 100644 --- a/dlls/d3dx9_36/tests/asm.c +++ b/dlls/d3dx9_36/tests/asm.c @@ -1327,6 +1327,8 @@ static void assembleshader_test(void) { if(shader) ID3DXBuffer_Release(shader); } else skip("Couldn't create \"shader.vsh\"\n"); + } /* todo_wine */ + /* NULL shader tests */ shader = NULL; messages = NULL; @@ -1340,6 +1342,8 @@ static void assembleshader_test(void) { } if(shader) ID3DXBuffer_Release(shader); + todo_wine { + shader = NULL; messages = NULL; hr = D3DXAssembleShaderFromFileA("nonexistent.vsh", @@ -1403,7 +1407,7 @@ START_TEST(asm) todo_wine vs_3_0_test(); todo_wine ps_3_0_test(); - todo_wine failure_test(); + failure_test(); assembleshader_test(); }