2 * shaders implementation
4 * Copyright 2002-2003 Jason Edmeades
5 * Copyright 2002-2003 Raphael Junqueira
6 * Copyright 2005 Oliver Stieber
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "wined3d_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader
);
30 #define GLNAME_REQUIRE_GLSL ((const char *)1)
32 inline static BOOL
shader_is_version_token(DWORD token
) {
33 return shader_is_pshader_version(token
) ||
34 shader_is_vshader_version(token
);
38 SHADER_BUFFER
* buffer
,
39 const char *format
, ...) {
41 char* base
= buffer
->buffer
+ buffer
->bsize
;
45 va_start(args
, format
);
46 rc
= vsnprintf(base
, SHADER_PGMSIZE
- 1 - buffer
->bsize
, format
, args
);
49 if (rc
< 0 || /* C89 */
50 rc
> SHADER_PGMSIZE
- 1 - buffer
->bsize
) { /* C99 */
52 ERR("The buffer allocated for the shader program string "
53 "is too small at %d bytes.\n", SHADER_PGMSIZE
);
54 buffer
->bsize
= SHADER_PGMSIZE
- 1;
60 TRACE("GL HW (%u, %u) : %s", buffer
->lineNo
, buffer
->bsize
, base
);
64 const SHADER_OPCODE
* shader_get_opcode(
65 IWineD3DBaseShader
*iface
, const DWORD code
) {
67 IWineD3DBaseShaderImpl
*This
= (IWineD3DBaseShaderImpl
*) iface
;
70 DWORD version
= This
->baseShader
.version
;
71 DWORD hex_version
= This
->baseShader
.hex_version
;
72 const SHADER_OPCODE
*shader_ins
= This
->baseShader
.shader_ins
;
74 /** TODO: use dichotomic search */
75 while (NULL
!= shader_ins
[i
].name
) {
76 if (((code
& D3DSI_OPCODE_MASK
) == shader_ins
[i
].opcode
) &&
77 (((hex_version
>= shader_ins
[i
].min_version
) && (hex_version
<= shader_ins
[i
].max_version
)) ||
78 ((shader_ins
[i
].min_version
== 0) && (shader_ins
[i
].max_version
== 0)))) {
79 return &shader_ins
[i
];
83 FIXME("Unsupported opcode %lx(%ld) masked %lx version %ld\n",
84 code
, code
, code
& D3DSI_OPCODE_MASK
, version
);
88 /* Note: For vertex shaders,
89 * texUsed = addrUsed, and
90 * D3DSPR_TEXTURE = D3DSPR_ADDR.
92 * Also note that this does not count the loop register
93 * as an address register. */
95 void shader_get_registers_used(
96 IWineD3DBaseShader
*iface
,
97 CONST DWORD
* pToken
) {
99 IWineD3DBaseShaderImpl
* This
= (IWineD3DBaseShaderImpl
*) iface
;
100 DWORD
* tempsUsed
= &This
->baseShader
.temps_used
;
101 DWORD
* texUsed
= &This
->baseShader
.textures_used
;
109 while (D3DVS_END() != *pToken
) {
110 CONST SHADER_OPCODE
* curOpcode
;
113 if (shader_is_version_token(*pToken
)) {
118 } else if (shader_is_comment(*pToken
)) {
119 DWORD comment_len
= (*pToken
& D3DSI_COMMENTSIZE_MASK
) >> D3DSI_COMMENTSIZE_SHIFT
;
121 pToken
+= comment_len
;
126 curOpcode
= shader_get_opcode(iface
, *pToken
);
129 /* Unhandled opcode, and its parameters */
130 if (NULL
== curOpcode
) {
131 while (*pToken
& 0x80000000)
135 /* Skip declarations (for now) */
136 } else if (D3DSIO_DCL
== curOpcode
->opcode
) {
137 pToken
+= curOpcode
->num_params
;
140 /* Skip definitions (for now) */
141 } else if (D3DSIO_DEF
== curOpcode
->opcode
) {
142 pToken
+= curOpcode
->num_params
;
145 /* Set texture registers, and temporary registers */
149 for (i
= 0; i
< curOpcode
->num_params
; ++i
) {
150 DWORD regtype
= (((*pToken
) & D3DSP_REGTYPE_MASK
) >> D3DSP_REGTYPE_SHIFT
);
151 DWORD reg
= (*pToken
) & D3DSP_REGNUM_MASK
;
152 if (D3DSPR_TEXTURE
== regtype
)
153 *texUsed
|= (1 << reg
);
154 if (D3DSPR_TEMP
== regtype
)
155 *tempsUsed
|= (1 << reg
);
162 void shader_program_dump_decl_usage(
166 DWORD regtype
= shader_get_regtype(param
);
169 if (regtype
== D3DSPR_SAMPLER
) {
170 DWORD ttype
= decl
& D3DSP_TEXTURETYPE_MASK
;
173 case D3DSTT_2D
: TRACE("2d"); break;
174 case D3DSTT_CUBE
: TRACE("cube"); break;
175 case D3DSTT_VOLUME
: TRACE("volume"); break;
176 default: TRACE("unknown_ttype(%08lx)", ttype
);
181 DWORD usage
= decl
& D3DSP_DCL_USAGE_MASK
;
182 DWORD idx
= (decl
& D3DSP_DCL_USAGEINDEX_MASK
) >> D3DSP_DCL_USAGEINDEX_SHIFT
;
185 case D3DDECLUSAGE_POSITION
:
186 TRACE("%s%ld", "position", idx
);
188 case D3DDECLUSAGE_BLENDINDICES
:
189 TRACE("%s", "blend");
191 case D3DDECLUSAGE_BLENDWEIGHT
:
192 TRACE("%s", "weight");
194 case D3DDECLUSAGE_NORMAL
:
195 TRACE("%s%ld", "normal", idx
);
197 case D3DDECLUSAGE_PSIZE
:
198 TRACE("%s", "psize");
200 case D3DDECLUSAGE_COLOR
:
202 TRACE("%s", "color");
204 TRACE("%s%ld", "specular", (idx
- 1));
207 case D3DDECLUSAGE_TEXCOORD
:
208 TRACE("%s%ld", "texture", idx
);
210 case D3DDECLUSAGE_TANGENT
:
211 TRACE("%s", "tangent");
213 case D3DDECLUSAGE_BINORMAL
:
214 TRACE("%s", "binormal");
216 case D3DDECLUSAGE_TESSFACTOR
:
217 TRACE("%s", "tessfactor");
219 case D3DDECLUSAGE_POSITIONT
:
220 TRACE("%s%ld", "positionT", idx
);
222 case D3DDECLUSAGE_FOG
:
225 case D3DDECLUSAGE_DEPTH
:
226 TRACE("%s", "depth");
228 case D3DDECLUSAGE_SAMPLE
:
229 TRACE("%s", "sample");
232 FIXME("unknown_semantics(%08lx)", usage
);
237 void shader_dump_param(
238 IWineD3DBaseShader
*iface
,
242 IWineD3DBaseShaderImpl
* This
= (IWineD3DBaseShaderImpl
*) iface
;
243 static const char* rastout_reg_names
[] = { "oPos", "oFog", "oPts" };
244 char swizzle_reg_chars
[4];
246 DWORD reg
= param
& D3DSP_REGNUM_MASK
;
247 DWORD regtype
= shader_get_regtype(param
);
249 /* There are some minor differences between pixel and vertex shaders */
250 BOOL pshader
= shader_is_pshader_version(This
->baseShader
.hex_version
);
252 /* For one, we'd prefer color components to be shown for pshaders.
253 * FIXME: use the swizzle function for this */
255 swizzle_reg_chars
[0] = pshader
? 'r': 'x';
256 swizzle_reg_chars
[1] = pshader
? 'g': 'y';
257 swizzle_reg_chars
[2] = pshader
? 'b': 'z';
258 swizzle_reg_chars
[3] = pshader
? 'a': 'w';
261 if ( ((param
& D3DSP_SRCMOD_MASK
) == D3DSPSM_NEG
) ||
262 ((param
& D3DSP_SRCMOD_MASK
) == D3DSPSM_BIASNEG
) ||
263 ((param
& D3DSP_SRCMOD_MASK
) == D3DSPSM_SIGNNEG
) ||
264 ((param
& D3DSP_SRCMOD_MASK
) == D3DSPSM_X2NEG
) )
266 else if ((param
& D3DSP_SRCMOD_MASK
) == D3DSPSM_COMP
)
278 TRACE("c%s%lu", (param
& D3DVS_ADDRMODE_RELATIVE
) ? "a0.x + " : "", reg
);
280 case D3DSPR_TEXTURE
: /* vs: case D3DSPR_ADDR */
281 TRACE("%c%lu", (pshader
? 't':'a'), reg
);
284 TRACE("%s", rastout_reg_names
[reg
]);
286 case D3DSPR_COLOROUT
:
289 case D3DSPR_DEPTHOUT
:
295 case D3DSPR_TEXCRDOUT
:
298 case D3DSPR_CONSTINT
:
299 TRACE("i%s%lu", (param
& D3DVS_ADDRMODE_RELATIVE
) ? "a0.x + " : "", reg
);
301 case D3DSPR_CONSTBOOL
:
302 TRACE("b%s%lu", (param
& D3DVS_ADDRMODE_RELATIVE
) ? "a0.x + " : "", reg
);
308 TRACE("aL%s%lu", (param
& D3DVS_ADDRMODE_RELATIVE
) ? "a0.x + " : "", reg
);
314 TRACE("unhandled_rtype(%lx)", regtype
);
319 /* operand output (for modifiers and shift, see dump_ins_modifiers) */
321 if ((param
& D3DSP_WRITEMASK_ALL
) != D3DSP_WRITEMASK_ALL
) {
323 if (param
& D3DSP_WRITEMASK_0
) TRACE("%c", swizzle_reg_chars
[0]);
324 if (param
& D3DSP_WRITEMASK_1
) TRACE("%c", swizzle_reg_chars
[1]);
325 if (param
& D3DSP_WRITEMASK_2
) TRACE("%c", swizzle_reg_chars
[2]);
326 if (param
& D3DSP_WRITEMASK_3
) TRACE("%c", swizzle_reg_chars
[3]);
331 DWORD swizzle
= (param
& D3DSP_SWIZZLE_MASK
) >> D3DSP_SWIZZLE_SHIFT
;
332 DWORD swizzle_r
= swizzle
& 0x03;
333 DWORD swizzle_g
= (swizzle
>> 2) & 0x03;
334 DWORD swizzle_b
= (swizzle
>> 4) & 0x03;
335 DWORD swizzle_a
= (swizzle
>> 6) & 0x03;
337 if (0 != (param
& D3DSP_SRCMOD_MASK
)) {
338 DWORD mask
= param
& D3DSP_SRCMOD_MASK
;
339 /*TRACE("_modifier(0x%08lx) ", mask);*/
341 case D3DSPSM_NONE
: break;
342 case D3DSPSM_NEG
: break;
343 case D3DSPSM_BIAS
: TRACE("_bias"); break;
344 case D3DSPSM_BIASNEG
: TRACE("_bias"); break;
345 case D3DSPSM_SIGN
: TRACE("_bx2"); break;
346 case D3DSPSM_SIGNNEG
: TRACE("_bx2"); break;
347 case D3DSPSM_COMP
: break;
348 case D3DSPSM_X2
: TRACE("_x2"); break;
349 case D3DSPSM_X2NEG
: TRACE("_x2"); break;
350 case D3DSPSM_DZ
: TRACE("_dz"); break;
351 case D3DSPSM_DW
: TRACE("_dw"); break;
353 TRACE("_unknown(0x%08lx)", mask
);
358 * swizzle bits fields:
361 if ((D3DVS_NOSWIZZLE
>> D3DVS_SWIZZLE_SHIFT
) != swizzle
) { /* ! D3DVS_NOSWIZZLE == 0xE4 << D3DVS_SWIZZLE_SHIFT */
362 if (swizzle_r
== swizzle_g
&&
363 swizzle_r
== swizzle_b
&&
364 swizzle_r
== swizzle_a
) {
365 TRACE(".%c", swizzle_reg_chars
[swizzle_r
]);
368 swizzle_reg_chars
[swizzle_r
],
369 swizzle_reg_chars
[swizzle_g
],
370 swizzle_reg_chars
[swizzle_b
],
371 swizzle_reg_chars
[swizzle_a
]);
377 /** Generate the variable & register declarations for the ARB_vertex_program
379 void generate_arb_declarations(IWineD3DBaseShader
*iface
, SHADER_BUFFER
* buffer
) {
381 IWineD3DBaseShaderImpl
* This
= (IWineD3DBaseShaderImpl
*) iface
;
384 for(i
= 0; i
< This
->baseShader
.limits
.temporary
; i
++) {
385 if (This
->baseShader
.temps_used
& (1 << i
))
386 shader_addline(buffer
, "TEMP R%lu;\n", i
);
389 for (i
= 0; i
< This
->baseShader
.limits
.address
; i
++) {
390 if (This
->baseShader
.textures_used
& (1 << i
))
391 shader_addline(buffer
, "ADDRESS A%ld;\n", i
);
394 for(i
= 0; i
< This
->baseShader
.limits
.texture
; i
++) {
395 if (This
->baseShader
.textures_used
& (1 << i
))
396 shader_addline(buffer
,"TEMP T%lu;\n", i
);
399 /* Texture coordinate registers must be pre-loaded */
400 for (i
= 0; i
< This
->baseShader
.limits
.texture
; i
++) {
401 if (This
->baseShader
.textures_used
& (1 << i
))
402 shader_addline(buffer
, "MOV T%lu, fragment.texcoord[%lu];\n", i
, i
);
406 /** Generate the variable & register declarations for the GLSL
408 void generate_glsl_declarations(IWineD3DBaseShader
*iface
, SHADER_BUFFER
* buffer
) {
410 FIXME("GLSL not fully implemented yet.\n");
414 /** Shared code in order to generate the bulk of the shader string.
415 Use the shader_header_fct & shader_footer_fct to add strings
416 that are specific to pixel or vertex functions
417 NOTE: A description of how to parse tokens can be found at:
418 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/graphics/hh/graphics/usermodedisplaydriver_shader_cc8e4e05-f5c3-4ec0-8853-8ce07c1551b2.xml.asp */
419 void generate_base_shader(
420 IWineD3DBaseShader
*iface
,
421 SHADER_BUFFER
* buffer
,
422 CONST DWORD
* pFunction
) {
424 IWineD3DBaseShaderImpl
* This
= (IWineD3DBaseShaderImpl
*) iface
;
425 const DWORD
*pToken
= pFunction
;
426 const SHADER_OPCODE
*curOpcode
= NULL
;
429 /* Initialize current parsing state */
430 This
->baseShader
.parse_state
.current_row
= 0;
432 /* First pass: figure out which temporary and texture registers are used */
433 shader_get_registers_used(iface
, pToken
);
434 TRACE("Texture/Address registers used: %#lx, Temp registers used %#lx\n",
435 This
->baseShader
.textures_used
, This
->baseShader
.temps_used
);
437 /* TODO: check register usage against GL/Directx limits, and fail if they're exceeded
438 nUseAddressRegister < = GL_MAX_PROGRAM_ADDRESS_REGISTERS_AR
439 nUseTempRegister <= GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB
442 /* Pre-declare registers */
444 generate_glsl_declarations(iface
, buffer
);
446 generate_arb_declarations(iface
, buffer
);
448 /* Second pass, process opcodes */
449 if (NULL
!= pToken
) {
450 while (D3DPS_END() != *pToken
) {
452 /* Skip version token */
453 if (shader_is_version_token(*pToken
)) {
458 /* Skip comment tokens */
459 if (shader_is_comment(*pToken
)) {
460 DWORD comment_len
= (*pToken
& D3DSI_COMMENTSIZE_MASK
) >> D3DSI_COMMENTSIZE_SHIFT
;
462 TRACE("#%s\n", (char*)pToken
);
463 pToken
+= comment_len
;
468 curOpcode
= shader_get_opcode(iface
, *pToken
);
471 /* Unknown opcode and its parameters */
472 if (NULL
== curOpcode
) {
473 while (*pToken
& 0x80000000) { /* TODO: Think of a sensible name for 0x80000000 */
474 FIXME("unrecognized opcode: %08lx\n", *pToken
);
478 /* Using GLSL & no generator function exists */
479 } else if (USING_GLSL
&& curOpcode
->hw_glsl_fct
== NULL
) {
481 FIXME("Token %s is not yet implemented with GLSL\n", curOpcode
->name
);
482 pToken
+= curOpcode
->num_params
;
484 /* Unhandled opcode in ARB */
485 } else if ( !USING_GLSL
&& GLNAME_REQUIRE_GLSL
== curOpcode
->glname
) {
487 FIXME("Token %s requires greater functionality than "
488 "Vertex or Fragment_Program_ARB supports\n", curOpcode
->name
);
489 pToken
+= curOpcode
->num_params
;
491 /* If a generator function is set for current shader target, use it */
492 } else if ((!USING_GLSL
&& curOpcode
->hw_fct
!= NULL
) ||
493 (USING_GLSL
&& curOpcode
->hw_glsl_fct
!= NULL
)) {
495 SHADER_OPCODE_ARG hw_arg
;
497 hw_arg
.shader
= iface
;
498 hw_arg
.opcode
= curOpcode
;
499 hw_arg
.buffer
= buffer
;
500 if (curOpcode
->num_params
> 0) {
501 hw_arg
.dst
= *pToken
;
503 /* FIXME: this does not account for relative address tokens */
504 for (i
= 1; i
< curOpcode
->num_params
; i
++)
505 hw_arg
.src
[i
-1] = *(pToken
+ i
);
508 /* Call appropriate function for output target */
510 curOpcode
->hw_glsl_fct(&hw_arg
);
512 curOpcode
->hw_fct(&hw_arg
);
514 pToken
+= curOpcode
->num_params
;
518 TRACE("Found opcode D3D:%s GL:%s, PARAMS:%d,\n",
519 curOpcode
->name
, curOpcode
->glname
, curOpcode
->num_params
);
521 /* Unless we encounter a no-op command, this opcode is unrecognized */
522 if (curOpcode
->opcode
!= D3DSIO_NOP
) {
523 FIXME("Can't handle opcode %s in hwShader\n", curOpcode
->name
);
524 pToken
+= curOpcode
->num_params
;
528 /* TODO: What about result.depth? */
534 void shader_dump_ins_modifiers(const DWORD output
) {
536 DWORD shift
= (output
& D3DSP_DSTSHIFT_MASK
) >> D3DSP_DSTSHIFT_SHIFT
;
537 DWORD mmask
= output
& D3DSP_DSTMOD_MASK
;
541 case 13: TRACE("_d8"); break;
542 case 14: TRACE("_d4"); break;
543 case 15: TRACE("_d2"); break;
544 case 1: TRACE("_x2"); break;
545 case 2: TRACE("_x4"); break;
546 case 3: TRACE("_x8"); break;
547 default: TRACE("_unhandled_shift(%ld)", shift
); break;
551 case D3DSPDM_NONE
: break;
552 case D3DSPDM_SATURATE
: TRACE("_sat"); break;
553 case D3DSPDM_PARTIALPRECISION
: TRACE("_pp"); break;
554 case D3DSPDM_MSAMPCENTROID
: TRACE("_centroid"); break;
555 default: TRACE("_unhandled_modifier(%#lx)", mmask
); break;
559 /* TODO: Move other shared code here */