wined3d: Fix regression from register maps switch for ARB shaders.
[wine] / dlls / wined3d / baseshader.c
1 /*
2  * shaders implementation
3  *
4  * Copyright 2002-2003 Jason Edmeades
5  * Copyright 2002-2003 Raphael Junqueira
6  * Copyright 2005 Oliver Stieber
7  * Copyright 2006 Ivan Gyurdiev
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include <string.h>
26 #include <stdio.h>
27 #include "wined3d_private.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader);
30
31 #define GLNAME_REQUIRE_GLSL  ((const char *)1)
32
33 inline static BOOL shader_is_version_token(DWORD token) {
34     return shader_is_pshader_version(token) ||
35            shader_is_vshader_version(token);
36 }
37
38 int shader_addline(
39     SHADER_BUFFER* buffer,  
40     const char *format, ...) {
41
42     char* base = buffer->buffer + buffer->bsize;
43     int rc;
44
45     va_list args;
46     va_start(args, format);
47     rc = vsnprintf(base, SHADER_PGMSIZE - 1 - buffer->bsize, format, args);
48     va_end(args);
49
50     if (rc < 0 ||                                   /* C89 */ 
51         rc > SHADER_PGMSIZE - 1 - buffer->bsize) {  /* C99 */
52
53         ERR("The buffer allocated for the shader program string "
54             "is too small at %d bytes.\n", SHADER_PGMSIZE);
55         buffer->bsize = SHADER_PGMSIZE - 1;
56         return -1;
57     }
58
59     buffer->bsize += rc;
60     buffer->lineNo++;
61     TRACE("GL HW (%u, %u) : %s", buffer->lineNo, buffer->bsize, base); 
62     return 0;
63 }
64
65 const SHADER_OPCODE* shader_get_opcode(
66     IWineD3DBaseShader *iface, const DWORD code) {
67
68     IWineD3DBaseShaderImpl *This = (IWineD3DBaseShaderImpl*) iface;
69
70     DWORD i = 0;
71     DWORD version = This->baseShader.version;
72     DWORD hex_version = This->baseShader.hex_version;
73     const SHADER_OPCODE *shader_ins = This->baseShader.shader_ins;
74
75     /** TODO: use dichotomic search */
76     while (NULL != shader_ins[i].name) {
77         if (((code & D3DSI_OPCODE_MASK) == shader_ins[i].opcode) &&
78             (((hex_version >= shader_ins[i].min_version) && (hex_version <= shader_ins[i].max_version)) ||
79             ((shader_ins[i].min_version == 0) && (shader_ins[i].max_version == 0)))) {
80             return &shader_ins[i];
81         }
82         ++i;
83     }
84     FIXME("Unsupported opcode %lx(%ld) masked %lx version %ld\n", 
85        code, code, code & D3DSI_OPCODE_MASK, version);
86     return NULL;
87 }
88
89 /* Read a parameter opcode from the input stream,
90  * and possibly a relative addressing token.
91  * Return the number of tokens read */
92 int shader_get_param(
93     IWineD3DBaseShader* iface,
94     const DWORD* pToken,
95     DWORD* param,
96     DWORD* addr_token) {
97
98     /* PS >= 3.0 have relative addressing (with token)
99      * VS >= 2.0 have relative addressing (with token)
100      * VS >= 1.0 < 2.0 have relative addressing (without token)
101      * The version check below should work in general */
102
103     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
104     char rel_token = D3DSHADER_VERSION_MAJOR(This->baseShader.hex_version) >= 2 &&
105         ((*pToken & D3DSHADER_ADDRESSMODE_MASK) == D3DSHADER_ADDRMODE_RELATIVE);
106
107     *param = *pToken;
108     *addr_token = rel_token? *(pToken + 1): 0;
109     return rel_token? 2:1;
110 }
111
112 /* Return the number of parameters to skip for an opcode */
113 static inline int shader_skip_opcode(
114     IWineD3DBaseShaderImpl* This,
115     const SHADER_OPCODE* curOpcode,
116     DWORD opcode_token) {
117
118    /* Shaders >= 2.0 may contain address tokens, but fortunately they
119     * have a useful legnth mask - use it here. Shaders 1.0 contain no such tokens */
120
121     return (D3DSHADER_VERSION_MAJOR(This->baseShader.hex_version) >= 2)?
122         ((opcode_token & D3DSI_INSTLENGTH_MASK) >> D3DSI_INSTLENGTH_SHIFT):
123         curOpcode->num_params;
124 }
125
126 /* Read the parameters of an unrecognized opcode from the input stream
127  * Return the number of tokens read. 
128  * 
129  * Note: This function assumes source or destination token format.
130  * It will not work with specially-formatted tokens like DEF or DCL, 
131  * but hopefully those would be recognized */
132
133 int shader_skip_unrecognized(
134     IWineD3DBaseShader* iface,
135     const DWORD* pToken) {
136
137     int tokens_read = 0;
138     int i = 0;
139
140     /* TODO: Think of a good name for 0x80000000 and replace it with a constant */
141     while (*pToken & 0x80000000) {
142
143         DWORD param, addr_token;
144         tokens_read += shader_get_param(iface, pToken, &param, &addr_token);
145         pToken += tokens_read;
146
147         FIXME("Unrecognized opcode param: token=%08lX "
148             "addr_token=%08lX name=", param, addr_token);
149         shader_dump_param(iface, param, addr_token, i);
150         FIXME("\n");
151         ++i;
152     }
153     return tokens_read;
154 }
155
156 /* Convert floating point offset relative
157  * to a register file to an absolute offset for float constants */
158
159 unsigned int shader_get_float_offset(const DWORD reg) {
160
161      unsigned int regnum = reg & D3DSP_REGNUM_MASK;
162      int regtype = shader_get_regtype(reg);
163
164      switch (regtype) {
165         case D3DSPR_CONST: return regnum;
166         case D3DSPR_CONST2: return 2048 + regnum;
167         case D3DSPR_CONST3: return 4096 + regnum;
168         case D3DSPR_CONST4: return 6144 + regnum;
169         default:
170             FIXME("Unsupported register type: %d\n", regtype);
171             return regnum;
172      }
173 }
174
175 /* Note that this does not count the loop register
176  * as an address register. */
177
178 static void shader_get_registers_used(
179     IWineD3DBaseShader *iface,
180     shader_reg_maps* reg_maps,
181     CONST DWORD* pToken) {
182
183     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
184
185     if (pToken == NULL)
186         return;
187
188     reg_maps->temporary = 0;
189     reg_maps->texcoord = 0;
190     reg_maps->address = 0;
191
192     while (D3DVS_END() != *pToken) {
193         CONST SHADER_OPCODE* curOpcode;
194         DWORD opcode_token;
195
196         /* Skip version */
197         if (shader_is_version_token(*pToken)) {
198              ++pToken;
199              continue;
200
201         /* Skip comments */
202         } else if (shader_is_comment(*pToken)) {
203              DWORD comment_len = (*pToken & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
204              ++pToken;
205              pToken += comment_len;
206              continue;
207         }
208
209         /* Fetch opcode */
210         opcode_token = *pToken++;
211         curOpcode = shader_get_opcode(iface, opcode_token);
212
213         /* Unhandled opcode, and its parameters */
214         if (NULL == curOpcode) {
215            while (*pToken & 0x80000000)
216                ++pToken;
217            continue;
218
219         /* Skip declarations (for now) */
220         } else if (D3DSIO_DCL == curOpcode->opcode) {
221             pToken += curOpcode->num_params;
222             continue;
223
224         /* Skip definitions (for now) */
225         } else if (D3DSIO_DEF == curOpcode->opcode) {
226             pToken += curOpcode->num_params;
227             continue;
228
229         /* Set texture registers, and temporary registers */
230         } else {
231             int i, limit;
232
233             /* This will loop over all the registers and try to
234              * make a bitmask of the ones we're interested in. 
235              *
236              * Relative addressing tokens are ignored, but that's 
237              * okay, since we'll catch any address registers when 
238              * they are initialized (required by spec) */
239
240             limit = (opcode_token & D3DSHADER_INSTRUCTION_PREDICATED)?
241                 curOpcode->num_params + 1: curOpcode->num_params;
242
243             for (i = 0; i < limit; ++i) {
244
245                 DWORD param, addr_token, reg, regtype;
246                 pToken += shader_get_param(iface, pToken, &param, &addr_token);
247
248                 regtype = (param & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT;
249                 reg = param & D3DSP_REGNUM_MASK;
250
251                 if (D3DSPR_TEXTURE == regtype) { /* vs: D3DSPR_ADDR */
252
253                     if (shader_is_pshader_version(This->baseShader.hex_version))
254                         reg_maps->texcoord |= (1 << reg);
255                     else
256                         reg_maps->address |= (1 << reg);
257                 }
258
259                 if (D3DSPR_TEMP == regtype)
260                     reg_maps->temporary |= (1 << reg);
261              }
262         }
263     }
264 }
265
266 void shader_program_dump_decl_usage(
267     DWORD decl, 
268     DWORD param) {
269
270     DWORD regtype = shader_get_regtype(param);
271     TRACE("dcl_");
272
273     if (regtype == D3DSPR_SAMPLER) {
274         DWORD ttype = decl & D3DSP_TEXTURETYPE_MASK;
275
276         switch (ttype) {
277             case D3DSTT_2D: TRACE("2d"); break;
278             case D3DSTT_CUBE: TRACE("cube"); break;
279             case D3DSTT_VOLUME: TRACE("volume"); break;
280             default: TRACE("unknown_ttype(%08lx)", ttype); 
281        }
282
283     } else { 
284
285         DWORD usage = decl & D3DSP_DCL_USAGE_MASK;
286         DWORD idx = (decl & D3DSP_DCL_USAGEINDEX_MASK) >> D3DSP_DCL_USAGEINDEX_SHIFT;
287
288         switch(usage) {
289         case D3DDECLUSAGE_POSITION:
290             TRACE("%s%ld", "position", idx);
291             break;
292         case D3DDECLUSAGE_BLENDINDICES:
293             TRACE("%s", "blend");
294             break;
295         case D3DDECLUSAGE_BLENDWEIGHT:
296             TRACE("%s", "weight");
297             break;
298         case D3DDECLUSAGE_NORMAL:
299             TRACE("%s%ld", "normal", idx);
300             break;
301         case D3DDECLUSAGE_PSIZE:
302             TRACE("%s", "psize");
303             break;
304         case D3DDECLUSAGE_COLOR:
305             if(idx == 0)  {
306                 TRACE("%s", "color");
307             } else {
308                 TRACE("%s%ld", "specular", (idx - 1));
309             }
310             break;
311         case D3DDECLUSAGE_TEXCOORD:
312             TRACE("%s%ld", "texture", idx);
313             break;
314         case D3DDECLUSAGE_TANGENT:
315             TRACE("%s", "tangent");
316             break;
317         case D3DDECLUSAGE_BINORMAL:
318             TRACE("%s", "binormal");
319             break;
320         case D3DDECLUSAGE_TESSFACTOR:
321             TRACE("%s", "tessfactor");
322             break;
323         case D3DDECLUSAGE_POSITIONT:
324             TRACE("%s%ld", "positionT", idx);
325             break;
326         case D3DDECLUSAGE_FOG:
327             TRACE("%s", "fog");
328             break;
329         case D3DDECLUSAGE_DEPTH:
330             TRACE("%s", "depth");
331             break;
332         case D3DDECLUSAGE_SAMPLE:
333             TRACE("%s", "sample");
334             break;
335         default:
336             FIXME("unknown_semantics(%08lx)", usage);
337         }
338     }
339 }
340
341 static void shader_dump_arr_entry(
342     IWineD3DBaseShader *iface,
343     const DWORD param,
344     const DWORD addr_token,
345     unsigned int reg,
346     int input) {
347
348     char relative =
349         ((param & D3DSHADER_ADDRESSMODE_MASK) == D3DSHADER_ADDRMODE_RELATIVE);
350
351     if (relative) {
352         TRACE("[");
353         if (addr_token)
354             shader_dump_param(iface, addr_token, 0, input);
355         else
356             TRACE("a0.x");
357         TRACE(" + ");
358      }
359      TRACE("%u", reg);
360      if (relative)
361          TRACE("]");
362 }
363
364 void shader_dump_param(
365     IWineD3DBaseShader *iface,
366     const DWORD param, 
367     const DWORD addr_token,
368     int input) {
369
370     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
371     static const char* rastout_reg_names[] = { "oPos", "oFog", "oPts" };
372     char swizzle_reg_chars[4];
373
374     DWORD reg = param & D3DSP_REGNUM_MASK;
375     DWORD regtype = shader_get_regtype(param);
376
377     /* There are some minor differences between pixel and vertex shaders */
378     BOOL pshader = shader_is_pshader_version(This->baseShader.hex_version);
379
380     /* For one, we'd prefer color components to be shown for pshaders.
381      * FIXME: use the swizzle function for this */
382
383     swizzle_reg_chars[0] = pshader? 'r': 'x';
384     swizzle_reg_chars[1] = pshader? 'g': 'y';
385     swizzle_reg_chars[2] = pshader? 'b': 'z';
386     swizzle_reg_chars[3] = pshader? 'a': 'w';
387
388     if (input) {
389         if ( ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_NEG) ||
390              ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_BIASNEG) ||
391              ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_SIGNNEG) ||
392              ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_X2NEG) )
393             TRACE("-");
394         else if ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_COMP)
395             TRACE("1-");
396         else if ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_NOT)
397             TRACE("!");
398     }
399
400     switch (regtype) {
401         case D3DSPR_TEMP:
402             TRACE("r%lu", reg);
403             break;
404         case D3DSPR_INPUT:
405             TRACE("v");
406             shader_dump_arr_entry(iface, param, addr_token, reg, input);
407             break;
408         case D3DSPR_CONST:
409         case D3DSPR_CONST2:
410         case D3DSPR_CONST3:
411         case D3DSPR_CONST4:
412             TRACE("c");
413             shader_dump_arr_entry(iface, param, addr_token, shader_get_float_offset(param), input);
414             break;
415         case D3DSPR_TEXTURE: /* vs: case D3DSPR_ADDR */
416             TRACE("%c%lu", (pshader? 't':'a'), reg);
417             break;        
418         case D3DSPR_RASTOUT:
419             TRACE("%s", rastout_reg_names[reg]);
420             break;
421         case D3DSPR_COLOROUT:
422             TRACE("oC%lu", reg);
423             break;
424         case D3DSPR_DEPTHOUT:
425             TRACE("oDepth");
426             break;
427         case D3DSPR_ATTROUT:
428             TRACE("oD%lu", reg);
429             break;
430         case D3DSPR_TEXCRDOUT: 
431
432             /* Vertex shaders >= 3.0 use general purpose output registers
433              * (D3DSPR_OUTPUT), which can include an address token */
434
435             if (D3DSHADER_VERSION_MAJOR(This->baseShader.hex_version) >= 3) {
436                 TRACE("o");
437                 shader_dump_arr_entry(iface, param, addr_token, reg, input);
438             }
439             else 
440                TRACE("oT%lu", reg);
441             break;
442         case D3DSPR_CONSTINT:
443             TRACE("i");
444             shader_dump_arr_entry(iface, param, addr_token, reg, input);
445             break;
446         case D3DSPR_CONSTBOOL:
447             TRACE("b");
448             shader_dump_arr_entry(iface, param, addr_token, reg, input);
449             break;
450         case D3DSPR_LABEL:
451             TRACE("l%lu", reg);
452             break;
453         case D3DSPR_LOOP:
454             TRACE("aL");
455             break;
456         case D3DSPR_SAMPLER:
457             TRACE("s%lu", reg);
458             break;
459         case D3DSPR_PREDICATE:
460             TRACE("p%lu", reg);
461             break;
462         default:
463             TRACE("unhandled_rtype(%#lx)", regtype);
464             break;
465    }
466
467    if (!input) {
468        /* operand output (for modifiers and shift, see dump_ins_modifiers) */
469
470        if ((param & D3DSP_WRITEMASK_ALL) != D3DSP_WRITEMASK_ALL) {
471            TRACE(".");
472            if (param & D3DSP_WRITEMASK_0) TRACE("%c", swizzle_reg_chars[0]);
473            if (param & D3DSP_WRITEMASK_1) TRACE("%c", swizzle_reg_chars[1]);
474            if (param & D3DSP_WRITEMASK_2) TRACE("%c", swizzle_reg_chars[2]);
475            if (param & D3DSP_WRITEMASK_3) TRACE("%c", swizzle_reg_chars[3]);
476        }
477
478    } else {
479         /** operand input */
480         DWORD swizzle = (param & D3DSP_SWIZZLE_MASK) >> D3DSP_SWIZZLE_SHIFT;
481         DWORD swizzle_r = swizzle & 0x03;
482         DWORD swizzle_g = (swizzle >> 2) & 0x03;
483         DWORD swizzle_b = (swizzle >> 4) & 0x03;
484         DWORD swizzle_a = (swizzle >> 6) & 0x03;
485
486         if (0 != (param & D3DSP_SRCMOD_MASK)) {
487             DWORD mask = param & D3DSP_SRCMOD_MASK;
488             switch (mask) {
489                 case D3DSPSM_NONE:    break;
490                 case D3DSPSM_NEG:     break;
491                 case D3DSPSM_NOT:     break;
492                 case D3DSPSM_BIAS:    TRACE("_bias"); break;
493                 case D3DSPSM_BIASNEG: TRACE("_bias"); break;
494                 case D3DSPSM_SIGN:    TRACE("_bx2"); break;
495                 case D3DSPSM_SIGNNEG: TRACE("_bx2"); break;
496                 case D3DSPSM_COMP:    break;
497                 case D3DSPSM_X2:      TRACE("_x2"); break;
498                 case D3DSPSM_X2NEG:   TRACE("_x2"); break;
499                 case D3DSPSM_DZ:      TRACE("_dz"); break;
500                 case D3DSPSM_DW:      TRACE("_dw"); break;
501                 default:
502                     TRACE("_unknown_modifier(%#lx)", mask >> D3DSP_SRCMOD_SHIFT);
503             }
504         }
505
506         /**
507         * swizzle bits fields:
508         *  RRGGBBAA
509         */
510         if ((D3DVS_NOSWIZZLE >> D3DVS_SWIZZLE_SHIFT) != swizzle) { /* ! D3DVS_NOSWIZZLE == 0xE4 << D3DVS_SWIZZLE_SHIFT */
511             if (swizzle_r == swizzle_g &&
512                 swizzle_r == swizzle_b &&
513                 swizzle_r == swizzle_a) {
514                     TRACE(".%c", swizzle_reg_chars[swizzle_r]);
515             } else {
516                 TRACE(".%c%c%c%c",
517                 swizzle_reg_chars[swizzle_r],
518                 swizzle_reg_chars[swizzle_g],
519                 swizzle_reg_chars[swizzle_b],
520                 swizzle_reg_chars[swizzle_a]);
521             }
522         }
523     }
524 }
525
526 /** Generate the variable & register declarations for the ARB_vertex_program
527     output target */
528 void generate_arb_declarations(
529     IWineD3DBaseShader *iface,
530     shader_reg_maps* reg_maps,
531     SHADER_BUFFER* buffer) {
532
533     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
534     DWORD i;
535
536     for(i = 0; i < This->baseShader.limits.temporary; i++) {
537         if (reg_maps->temporary & (1 << i))
538             shader_addline(buffer, "TEMP R%lu;\n", i);
539     }
540
541     for (i = 0; i < This->baseShader.limits.address; i++) {
542         if (reg_maps->address & (1 << i))
543             shader_addline(buffer, "ADDRESS A%ld;\n", i);
544     }
545
546     for(i = 0; i < This->baseShader.limits.texture; i++) {
547         if (reg_maps->texcoord & (1 << i))
548             shader_addline(buffer,"TEMP T%lu;\n", i);
549     }
550
551     /* Texture coordinate registers must be pre-loaded */
552     for (i = 0; i < This->baseShader.limits.texture; i++) {
553         if (reg_maps->texcoord & (1 << i))
554             shader_addline(buffer, "MOV T%lu, fragment.texcoord[%lu];\n", i, i);
555     }
556
557     /* Need to PARAM the environment parameters (constants) so we can use relative addressing */
558     shader_addline(buffer, "PARAM C[%d] = { program.env[0..%d] };\n",
559                    This->baseShader.limits.constant_float,
560                    This->baseShader.limits.constant_float - 1);
561 }
562
563 /** Generate the variable & register declarations for the GLSL
564     output target */
565 void generate_glsl_declarations(
566     IWineD3DBaseShader *iface,
567     shader_reg_maps* reg_maps,
568     SHADER_BUFFER* buffer) {
569
570     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
571     int i;
572
573     FIXME("GLSL not fully implemented yet.\n");
574
575     /* Declare the constants (aka uniforms) */
576     shader_addline(buffer, "uniform vec4 C[%u];\n", This->baseShader.limits.constant_float);
577
578     /* Declare texture samplers 
579      * TODO: Make this work for textures other than 2D */
580     for (i = 0; i < This->baseShader.limits.texture; i++) {
581         shader_addline(buffer, "uniform sampler2D mytex%lu;\n", i);
582     }
583     
584     /* Declare address variables */
585     for (i = 0; i < This->baseShader.limits.address; i++) {
586         if (reg_maps->address & (1 << i))
587             shader_addline(buffer, "ivec4 A%ld;\n", i);
588     }
589
590     /* Declare texture temporaries */
591     for (i = 0; i < This->baseShader.limits.texture; i++) {
592         shader_addline(buffer, "vec4 T%lu = gl_TexCoord[%lu];\n", i, i);
593     }
594
595     /* Declare temporary variables */
596     for(i = 0; i < This->baseShader.limits.temporary; i++) {
597         if (reg_maps->temporary & (1 << i))
598             shader_addline(buffer, "vec4 R%lu;\n", i);
599     }
600
601     /* Declare all named attributes (TODO: Add this to the reg_maps
602      * and only declare those that are needed) */
603     for (i = 0; i < This->baseShader.limits.attributes; i++) {
604         shader_addline(buffer, "attribute vec4 attrib%i;\n", i);
605     }
606
607     /* Temporary variables for matrix operations */
608     shader_addline(buffer, "vec4 tmp0;\n");
609     shader_addline(buffer, "vec4 tmp1;\n");
610
611     /* Start the main program */
612     shader_addline(buffer, "void main() {\n");
613 }
614
615 /** Shared code in order to generate the bulk of the shader string.
616     Use the shader_header_fct & shader_footer_fct to add strings
617     that are specific to pixel or vertex functions
618     NOTE: A description of how to parse tokens can be found at:
619           http://msdn.microsoft.com/library/default.asp?url=/library/en-us/graphics/hh/graphics/usermodedisplaydriver_shader_cc8e4e05-f5c3-4ec0-8853-8ce07c1551b2.xml.asp */
620 void generate_base_shader(
621     IWineD3DBaseShader *iface,
622     SHADER_BUFFER* buffer,
623     CONST DWORD* pFunction) {
624
625     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
626     const DWORD *pToken = pFunction;
627     const SHADER_OPCODE *curOpcode = NULL;
628     SHADER_HANDLER hw_fct = NULL;
629     DWORD opcode_token;
630     DWORD i;
631     shader_reg_maps reg_maps;
632     SHADER_OPCODE_ARG hw_arg;
633
634     memset(&reg_maps, 0, sizeof(shader_reg_maps));
635
636     /* Initialize current parsing state */
637     hw_arg.shader = iface;
638     hw_arg.buffer = buffer;
639     hw_arg.reg_maps = &reg_maps;
640     This->baseShader.parse_state.current_row = 0;
641
642     /* First pass: figure out which temporary and texture registers are used */
643     shader_get_registers_used(iface, &reg_maps, pToken);
644
645     /* TODO: check register usage against GL/Directx limits, and fail if they're exceeded
646         nUseAddressRegister < = GL_MAX_PROGRAM_ADDRESS_REGISTERS_AR
647         nUseTempRegister    <=  GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB
648     */
649
650     /* Pre-declare registers */
651     if (wined3d_settings.shader_mode == SHADER_GLSL) {
652         generate_glsl_declarations(iface, &reg_maps, buffer);
653     } else {
654         generate_arb_declarations(iface, &reg_maps, buffer);
655     }
656
657     /* Second pass, process opcodes */
658     if (NULL != pToken) {
659         while (D3DPS_END() != *pToken) {
660
661             /* Skip version token */
662             if (shader_is_version_token(*pToken)) {
663                 ++pToken;
664                 continue;
665             }
666
667             /* Skip comment tokens */
668             if (shader_is_comment(*pToken)) {
669                 DWORD comment_len = (*pToken & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
670                 ++pToken;
671                 TRACE("#%s\n", (char*)pToken);
672                 pToken += comment_len;
673                 continue;
674             }
675
676             /* Read opcode */
677             opcode_token = *pToken++;
678             curOpcode = shader_get_opcode(iface, opcode_token);
679             hw_fct = (curOpcode == NULL)? NULL:
680                      (wined3d_settings.shader_mode == SHADER_GLSL)?
681                          curOpcode->hw_glsl_fct : curOpcode->hw_fct;
682
683             /* Unknown opcode and its parameters */
684             if (NULL == curOpcode) {
685                 FIXME("Unrecognized opcode: token=%08lX\n", opcode_token);
686                 pToken += shader_skip_unrecognized(iface, pToken); 
687
688             /* If a generator function is set for current shader target, use it */
689             } else if (hw_fct != NULL) {
690
691                 hw_arg.opcode = curOpcode;
692
693                 if (curOpcode->num_params > 0) {
694
695                     DWORD param, addr_token = 0;
696
697                     /* DCL instruction has usage dst parameter, not register */
698                     if (curOpcode->opcode == D3DSIO_DCL)
699                         param = *pToken++;
700                     else
701                         pToken += shader_get_param(iface, pToken, &param, &addr_token);
702
703                     hw_arg.dst = param;
704                     hw_arg.dst_addr = addr_token;
705
706                     if (opcode_token & D3DSHADER_INSTRUCTION_PREDICATED) 
707                         hw_arg.predicate = *pToken++;
708
709                     for (i = 1; i < curOpcode->num_params; i++) {
710                         /* DEF* instructions have constant src parameters, not registers */
711                         if (curOpcode->opcode == D3DSIO_DEF || 
712                             curOpcode->opcode == D3DSIO_DEFI || 
713                             curOpcode->opcode == D3DSIO_DEFB) {
714                             param = *pToken++;
715
716                         } else
717                             pToken += shader_get_param(iface, pToken, &param, &addr_token);
718
719                         hw_arg.src[i-1] = param;
720                         hw_arg.src_addr[i-1] = addr_token;
721                     }
722                 }
723
724                 /* Call appropriate function for output target */
725                 hw_fct(&hw_arg);
726
727             } else {
728
729                 /* Unless we encounter a no-op command, this opcode is unrecognized */
730                 if (curOpcode->opcode != D3DSIO_NOP) {
731                     FIXME("Can't handle opcode %s in hwShader\n", curOpcode->name);
732                     pToken += shader_skip_opcode(This, curOpcode, opcode_token);
733                 }
734             }
735         }
736         /* TODO: What about result.depth? */
737
738     }
739 }
740
741 void shader_dump_ins_modifiers(const DWORD output) {
742
743     DWORD shift = (output & D3DSP_DSTSHIFT_MASK) >> D3DSP_DSTSHIFT_SHIFT;
744     DWORD mmask = output & D3DSP_DSTMOD_MASK;
745
746     switch (shift) {
747         case 0: break;
748         case 13: TRACE("_d8"); break;
749         case 14: TRACE("_d4"); break;
750         case 15: TRACE("_d2"); break;
751         case 1: TRACE("_x2"); break;
752         case 2: TRACE("_x4"); break;
753         case 3: TRACE("_x8"); break;
754         default: TRACE("_unhandled_shift(%ld)", shift); break;
755     }
756
757     if (mmask & D3DSPDM_SATURATE)         TRACE("_sat");
758     if (mmask & D3DSPDM_PARTIALPRECISION) TRACE("_pp");
759     if (mmask & D3DSPDM_MSAMPCENTROID)    TRACE("_centroid");
760
761     mmask &= ~(D3DSPDM_SATURATE | D3DSPDM_PARTIALPRECISION | D3DSPDM_MSAMPCENTROID);
762     if (mmask)
763         FIXME("_unrecognized_modifier(%#lx)", mmask >> D3DSP_DSTMOD_SHIFT);
764 }
765
766 /** Process the D3DSIO_DCL opcode into an ARB string - creates a local vec4
767  * float constant, and stores it's usage on the regmaps. */
768 void shader_hw_def(SHADER_OPCODE_ARG* arg) {
769
770     DWORD reg = arg->dst & D3DSP_REGNUM_MASK;
771
772     shader_addline(arg->buffer, 
773                    "PARAM C%lu = { %f, %f, %f, %f };\n", reg,
774                    *((const float *)(arg->src + 0)),
775                    *((const float *)(arg->src + 1)),
776                    *((const float *)(arg->src + 2)),
777                    *((const float *)(arg->src + 3)) );
778
779     arg->reg_maps->constantsF[reg] = 1;
780 }
781
782 /* TODO: Move other shared code here */