d3dx9: Shader assembler ps_3_0 support.
[wine] / dlls / d3dx9_36 / asmparser.c
1 /*
2  * Direct3D asm shader parser
3  *
4  * Copyright 2008 Stefan Dösinger
5  * Copyright 2009 Matteo Bruni
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25 #include "wine/debug.h"
26
27 #include "d3dx9_36_private.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(asmshader);
30 WINE_DECLARE_DEBUG_CHANNEL(parsed_shader);
31
32
33 /****************************************************************
34  * Common(non-version specific) shader parser control code      *
35  ****************************************************************/
36
37 static void asmparser_end(struct asm_parser *This) {
38     TRACE("Finalizing shader\n");
39 }
40
41 static void asmparser_constF(struct asm_parser *This, DWORD reg, float x, float y, float z, float w) {
42     if(!This->shader) return;
43     TRACE("Adding float constant %u at pos %u\n", reg, This->shader->num_cf);
44     TRACE_(parsed_shader)("def c%u, %f, %f, %f, %f\n", reg, x, y, z, w);
45     if(!add_constF(This->shader, reg, x, y, z, w)) {
46         ERR("Out of memory\n");
47         set_parse_status(This, PARSE_ERR);
48     }
49 }
50
51 static void asmparser_constB(struct asm_parser *This, DWORD reg, BOOL x) {
52     if(!This->shader) return;
53     TRACE("Adding boolean constant %u at pos %u\n", reg, This->shader->num_cb);
54     TRACE_(parsed_shader)("def b%u, %s\n", reg, x ? "true" : "false");
55     if(!add_constB(This->shader, reg, x)) {
56         ERR("Out of memory\n");
57         set_parse_status(This, PARSE_ERR);
58     }
59 }
60
61 static void asmparser_constI(struct asm_parser *This, DWORD reg, INT x, INT y, INT z, INT w) {
62     if(!This->shader) return;
63     TRACE("Adding integer constant %u at pos %u\n", reg, This->shader->num_ci);
64     TRACE_(parsed_shader)("def i%u, %d, %d, %d, %d\n", reg, x, y, z, w);
65     if(!add_constI(This->shader, reg, x, y, z, w)) {
66         ERR("Out of memory\n");
67         set_parse_status(This, PARSE_ERR);
68     }
69 }
70
71 static void asmparser_dcl_output(struct asm_parser *This, DWORD usage, DWORD num,
72                                  const struct shader_reg *reg) {
73     if(!This->shader) return;
74     if(This->shader->type == ST_PIXEL) {
75         asmparser_message(This, "Line %u: Output register declared in a pixel shader\n", This->line_no);
76         set_parse_status(This, PARSE_ERR);
77     }
78     if(!record_declaration(This->shader, usage, num, TRUE, reg->regnum, reg->writemask)) {
79         ERR("Out of memory\n");
80         set_parse_status(This, PARSE_ERR);
81     }
82 }
83
84 static void asmparser_dcl_input(struct asm_parser *This, DWORD usage, DWORD num,
85                                 const struct shader_reg *reg) {
86     if(!This->shader) return;
87     if(!record_declaration(This->shader, usage, num, FALSE, reg->regnum, reg->writemask)) {
88         ERR("Out of memory\n");
89         set_parse_status(This, PARSE_ERR);
90     }
91 }
92
93 static void asmparser_dcl_sampler(struct asm_parser *This, DWORD samptype, DWORD regnum, unsigned int line_no) {
94     if(!This->shader) return;
95     if(!record_sampler(This->shader, samptype, regnum)) {
96         ERR("Out of memory\n");
97         set_parse_status(This, PARSE_ERR);
98     }
99 }
100
101 static void asmparser_instr(struct asm_parser *This, DWORD opcode,
102                             DWORD mod, DWORD shift,
103                             BWRITER_COMPARISON_TYPE comp,
104                             const struct shader_reg *dst,
105                             const struct src_regs *srcs, int expectednsrcs) {
106     struct instruction *instr;
107     unsigned int i;
108     BOOL firstreg = TRUE;
109     unsigned int src_count = srcs ? srcs->count : 0;
110
111     if(!This->shader) return;
112
113     TRACE_(parsed_shader)("%s%s%s ", debug_print_opcode(opcode),
114                           debug_print_dstmod(mod),
115                           debug_print_comp(comp));
116     if(dst) {
117         TRACE_(parsed_shader)("%s", debug_print_dstreg(dst, This->shader->type));
118         firstreg = FALSE;
119     }
120     for(i = 0; i < src_count; i++) {
121         if(!firstreg) TRACE_(parsed_shader)(", ");
122         else firstreg = FALSE;
123         TRACE_(parsed_shader)("%s", debug_print_srcreg(&srcs->reg[i],
124                                                        This->shader->type));
125     }
126     TRACE_(parsed_shader)("\n");
127
128     if(src_count != expectednsrcs) {
129         asmparser_message(This, "Line %u: Wrong number of source registers\n", This->line_no);
130         set_parse_status(This, PARSE_ERR);
131         return;
132     }
133
134     instr = alloc_instr(src_count);
135     if(!instr) {
136         ERR("Error allocating memory for the instruction\n");
137         set_parse_status(This, PARSE_ERR);
138         return;
139     }
140
141     instr->opcode = opcode;
142     instr->dstmod = mod;
143     instr->shift = shift;
144     instr->comptype = comp;
145     if(dst) This->funcs->dstreg(This, instr, dst);
146     for(i = 0; i < src_count; i++) {
147         This->funcs->srcreg(This, instr, i, &srcs->reg[i]);
148     }
149
150     if(!add_instruction(This->shader, instr)) {
151         ERR("Out of memory\n");
152         set_parse_status(This, PARSE_ERR);
153     }
154 }
155
156 /* Checks for unsupported source modifiers in VS (all versions) or
157    PS 2.0 and newer */
158 static void check_legacy_srcmod(struct asm_parser *This, DWORD srcmod) {
159     if(srcmod == BWRITERSPSM_BIAS || srcmod == BWRITERSPSM_BIASNEG ||
160        srcmod == BWRITERSPSM_SIGN || srcmod == BWRITERSPSM_SIGNNEG ||
161        srcmod == BWRITERSPSM_COMP || srcmod == BWRITERSPSM_X2 ||
162        srcmod == BWRITERSPSM_X2NEG || srcmod == BWRITERSPSM_DZ ||
163        srcmod == BWRITERSPSM_DW) {
164         asmparser_message(This, "Line %u: Source modifier %s not supported in this shader version\n",
165                           This->line_no,
166                           debug_print_srcmod(srcmod));
167         set_parse_status(This, PARSE_ERR);
168     }
169 }
170
171 static void check_loop_swizzle(struct asm_parser *This,
172                                const struct shader_reg *src) {
173     if((src->type == BWRITERSPR_LOOP && src->swizzle != BWRITERVS_NOSWIZZLE) ||
174        (src->rel_reg && src->rel_reg->type == BWRITERSPR_LOOP &&
175         src->rel_reg->swizzle != BWRITERVS_NOSWIZZLE)) {
176         asmparser_message(This, "Line %u: Swizzle not allowed on aL register\n", This->line_no);
177         set_parse_status(This, PARSE_ERR);
178     }
179 }
180
181 static void check_shift_dstmod(struct asm_parser *This, DWORD shift) {
182     if(shift != 0) {
183         asmparser_message(This, "Line %u: Shift modifiers not supported in this shader version\n",
184                           This->line_no);
185         set_parse_status(This, PARSE_ERR);
186     }
187 }
188
189 static void check_ps_dstmod(struct asm_parser *This, DWORD dstmod) {
190     if(dstmod == BWRITERSPDM_PARTIALPRECISION ||
191        dstmod == BWRITERSPDM_MSAMPCENTROID) {
192         asmparser_message(This, "Line %u: Instruction modifier %s not supported in this shader version\n",
193                           This->line_no,
194                           debug_print_dstmod(dstmod));
195         set_parse_status(This, PARSE_ERR);
196     }
197 }
198
199 struct allowed_reg_type {
200     DWORD type;
201     DWORD count;
202 };
203
204 static BOOL check_reg_type(const struct shader_reg *reg,
205                            const struct allowed_reg_type *allowed) {
206     unsigned int i = 0;
207
208     while(allowed[i].type != ~0U) {
209         if(reg->type == allowed[i].type) {
210             if(reg->rel_reg) return TRUE; /* The relative addressing register
211                                              can have a negative value, we
212                                              can't check the register index */
213             if(reg->regnum < allowed[i].count) return TRUE;
214             return FALSE;
215         }
216         i++;
217     }
218     return FALSE;
219 }
220
221 /* Native assembler doesn't do separate checks for src and dst registers */
222 static const struct allowed_reg_type vs_3_reg_allowed[] = {
223     { BWRITERSPR_TEMP,         32 },
224     { BWRITERSPR_INPUT,        16 },
225     { BWRITERSPR_CONST,       ~0U },
226     { BWRITERSPR_ADDR,          1 },
227     { BWRITERSPR_CONSTBOOL,    16 },
228     { BWRITERSPR_CONSTINT,     16 },
229     { BWRITERSPR_LOOP,          1 },
230     { BWRITERSPR_LABEL,      2048 },
231     { BWRITERSPR_PREDICATE,     1 },
232     { BWRITERSPR_SAMPLER,       4 },
233     { BWRITERSPR_OUTPUT,       12 },
234     { ~0U, 0 } /* End tag */
235 };
236
237 static void asmparser_srcreg_vs_3(struct asm_parser *This,
238                                   struct instruction *instr, int num,
239                                   const struct shader_reg *src) {
240     if(!check_reg_type(src, vs_3_reg_allowed)) {
241         asmparser_message(This, "Line %u: Source register %s not supported in VS 3.0\n",
242                           This->line_no,
243                           debug_print_srcreg(src, ST_VERTEX));
244         set_parse_status(This, PARSE_ERR);
245     }
246     check_loop_swizzle(This, src);
247     check_legacy_srcmod(This, src->srcmod);
248     memcpy(&instr->src[num], src, sizeof(*src));
249 }
250
251 static const struct allowed_reg_type ps_3_reg_allowed[] = {
252     { BWRITERSPR_INPUT,        10 },
253     { BWRITERSPR_TEMP,         32 },
254     { BWRITERSPR_CONST,       224 },
255     { BWRITERSPR_CONSTINT,     16 },
256     { BWRITERSPR_CONSTBOOL,    16 },
257     { BWRITERSPR_PREDICATE,     1 },
258     { BWRITERSPR_SAMPLER,      16 },
259     { BWRITERSPR_MISCTYPE,      2 }, /* vPos and vFace */
260     { BWRITERSPR_LOOP,          1 },
261     { BWRITERSPR_LABEL,      2048 },
262     { BWRITERSPR_COLOROUT,    ~0U },
263     { BWRITERSPR_DEPTHOUT,      1 },
264     { ~0U, 0 } /* End tag */
265 };
266
267 static void asmparser_srcreg_ps_3(struct asm_parser *This,
268                                   struct instruction *instr, int num,
269                                   const struct shader_reg *src) {
270     if(!check_reg_type(src, ps_3_reg_allowed)) {
271         asmparser_message(This, "Line %u: Source register %s not supported in PS 3.0\n",
272                           This->line_no,
273                           debug_print_srcreg(src, ST_PIXEL));
274         set_parse_status(This, PARSE_ERR);
275     }
276     check_loop_swizzle(This, src);
277     check_legacy_srcmod(This, src->srcmod);
278     memcpy(&instr->src[num], src, sizeof(*src));
279 }
280
281 static void asmparser_dstreg_vs_3(struct asm_parser *This,
282                                   struct instruction *instr,
283                                   const struct shader_reg *dst) {
284     if(!check_reg_type(dst, vs_3_reg_allowed)) {
285         asmparser_message(This, "Line %u: Destination register %s not supported in VS 3.0\n",
286                           This->line_no,
287                           debug_print_dstreg(dst, ST_VERTEX));
288         set_parse_status(This, PARSE_ERR);
289     }
290     check_ps_dstmod(This, instr->dstmod);
291     check_shift_dstmod(This, instr->shift);
292     memcpy(&instr->dst, dst, sizeof(*dst));
293     instr->has_dst = TRUE;
294 }
295
296 static void asmparser_dstreg_ps_3(struct asm_parser *This,
297                                   struct instruction *instr,
298                                   const struct shader_reg *dst) {
299     if(!check_reg_type(dst, ps_3_reg_allowed)) {
300         asmparser_message(This, "Line %u: Destination register %s not supported in PS 3.0\n",
301                           This->line_no,
302                           debug_print_dstreg(dst, ST_PIXEL));
303         set_parse_status(This, PARSE_ERR);
304     }
305     check_shift_dstmod(This, instr->shift);
306     memcpy(&instr->dst, dst, sizeof(*dst));
307     instr->has_dst = TRUE;
308 }
309
310 static void asmparser_predicate_supported(struct asm_parser *This,
311                                           const struct shader_reg *predicate) {
312     /* this sets the predicate of the last instruction added to the shader */
313     if(!This->shader) return;
314     if(This->shader->num_instrs == 0) ERR("Predicate without an instruction\n");
315     This->shader->instr[This->shader->num_instrs - 1]->has_predicate = TRUE;
316     memcpy(&This->shader->instr[This->shader->num_instrs - 1]->predicate, predicate, sizeof(*predicate));
317 }
318
319 #if 0
320 static void asmparser_predicate_unsupported(struct asm_parser *This,
321                                             const struct shader_reg *predicate) {
322     asmparser_message(This, "Line %u: Predicate not supported in < VS 2.0 or PS 2.x\n", This->line_no);
323     set_parse_status(This, PARSE_ERR);
324 }
325 #endif
326
327 static void asmparser_coissue_unsupported(struct asm_parser *This) {
328     asmparser_message(This, "Line %u: Coissue is only supported in pixel shaders versions <= 1.4\n", This->line_no);
329     set_parse_status(This, PARSE_ERR);
330 }
331
332 static const struct asmparser_backend parser_vs_3 = {
333     asmparser_constF,
334     asmparser_constI,
335     asmparser_constB,
336
337     asmparser_dstreg_vs_3,
338     asmparser_srcreg_vs_3,
339
340     asmparser_predicate_supported,
341     asmparser_coissue_unsupported,
342
343     asmparser_dcl_output,
344     asmparser_dcl_input,
345     asmparser_dcl_sampler,
346
347     asmparser_end,
348
349     asmparser_instr,
350 };
351
352 static const struct asmparser_backend parser_ps_3 = {
353     asmparser_constF,
354     asmparser_constI,
355     asmparser_constB,
356
357     asmparser_dstreg_ps_3,
358     asmparser_srcreg_ps_3,
359
360     asmparser_predicate_supported,
361     asmparser_coissue_unsupported,
362
363     asmparser_dcl_output,
364     asmparser_dcl_input,
365     asmparser_dcl_sampler,
366
367     asmparser_end,
368
369     asmparser_instr,
370 };
371
372 void create_vs30_parser(struct asm_parser *ret) {
373     TRACE_(parsed_shader)("vs_3_0\n");
374
375     ret->shader = asm_alloc(sizeof(*ret->shader));
376     if(!ret->shader) {
377         ERR("Failed to allocate memory for the shader\n");
378         set_parse_status(ret, PARSE_ERR);
379         return;
380     }
381
382     ret->shader->type = ST_VERTEX;
383     ret->shader->version = BWRITERVS_VERSION(3, 0);
384     ret->funcs = &parser_vs_3;
385 }
386
387 void create_ps30_parser(struct asm_parser *ret) {
388     TRACE_(parsed_shader)("ps_3_0\n");
389
390     ret->shader = asm_alloc(sizeof(*ret->shader));
391     if(!ret->shader) {
392         ERR("Failed to allocate memory for the shader\n");
393         set_parse_status(ret, PARSE_ERR);
394         return;
395     }
396
397     ret->shader->type = ST_PIXEL;
398     ret->shader->version = BWRITERPS_VERSION(3, 0);
399     ret->funcs = &parser_ps_3;
400 }