wined3d: Shaders: share dump_param function, version functions.
[wine/gsoc_dplay.git] / dlls / wined3d / baseshader.c
blob92db8ab7e3ebbcb086bdceadbdead880a105c0ad
1 /*
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
23 #include "config.h"
24 #include <string.h>
25 #include <stdio.h>
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);
37 int shader_addline(
38 SHADER_BUFFER* buffer,
39 const char *format, ...) {
41 char* base = buffer->buffer + buffer->bsize;
42 int rc;
44 va_list args;
45 va_start(args, format);
46 rc = vsnprintf(base, SHADER_PGMSIZE - 1 - buffer->bsize, format, args);
47 va_end(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;
55 return -1;
58 buffer->bsize += rc;
59 buffer->lineNo++;
60 TRACE("GL HW (%u, %u) : %s", buffer->lineNo, buffer->bsize, base);
61 return 0;
64 const SHADER_OPCODE* shader_get_opcode(
65 IWineD3DBaseShader *iface, const DWORD code) {
67 IWineD3DBaseShaderImpl *This = (IWineD3DBaseShaderImpl*) iface;
69 DWORD i = 0;
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];
81 ++i;
83 FIXME("Unsupported opcode %lx(%ld) masked %lx version %ld\n",
84 code, code, code & D3DSI_OPCODE_MASK, version);
85 return NULL;
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;
103 if (pToken == NULL)
104 return;
106 *tempsUsed = 0;
107 *texUsed = 0;
109 while (D3DVS_END() != *pToken) {
110 CONST SHADER_OPCODE* curOpcode;
112 /* Skip version */
113 if (shader_is_version_token(*pToken)) {
114 ++pToken;
115 continue;
117 /* Skip comments */
118 } else if (shader_is_comment(*pToken)) {
119 DWORD comment_len = (*pToken & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
120 ++pToken;
121 pToken += comment_len;
122 continue;
125 /* Fetch opcode */
126 curOpcode = shader_get_opcode(iface, *pToken);
127 ++pToken;
129 /* Unhandled opcode, and its parameters */
130 if (NULL == curOpcode) {
131 while (*pToken & 0x80000000)
132 ++pToken;
133 continue;
135 /* Skip declarations (for now) */
136 } else if (D3DSIO_DCL == curOpcode->opcode) {
137 pToken += curOpcode->num_params;
138 continue;
140 /* Skip definitions (for now) */
141 } else if (D3DSIO_DEF == curOpcode->opcode) {
142 pToken += curOpcode->num_params;
143 continue;
145 /* Set texture registers, and temporary registers */
146 } else {
147 int i;
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);
156 ++pToken;
162 void shader_program_dump_decl_usage(
163 DWORD decl,
164 DWORD param) {
166 DWORD regtype = shader_get_regtype(param);
167 TRACE("dcl_");
169 if (regtype == D3DSPR_SAMPLER) {
170 DWORD ttype = decl & D3DSP_TEXTURETYPE_MASK;
172 switch (ttype) {
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);
179 } else {
181 DWORD usage = decl & D3DSP_DCL_USAGE_MASK;
182 DWORD idx = (decl & D3DSP_DCL_USAGEINDEX_MASK) >> D3DSP_DCL_USAGEINDEX_SHIFT;
184 switch(usage) {
185 case D3DDECLUSAGE_POSITION:
186 TRACE("%s%ld", "position", idx);
187 break;
188 case D3DDECLUSAGE_BLENDINDICES:
189 TRACE("%s", "blend");
190 break;
191 case D3DDECLUSAGE_BLENDWEIGHT:
192 TRACE("%s", "weight");
193 break;
194 case D3DDECLUSAGE_NORMAL:
195 TRACE("%s%ld", "normal", idx);
196 break;
197 case D3DDECLUSAGE_PSIZE:
198 TRACE("%s", "psize");
199 break;
200 case D3DDECLUSAGE_COLOR:
201 if(idx == 0) {
202 TRACE("%s", "color");
203 } else {
204 TRACE("%s%ld", "specular", (idx - 1));
206 break;
207 case D3DDECLUSAGE_TEXCOORD:
208 TRACE("%s%ld", "texture", idx);
209 break;
210 case D3DDECLUSAGE_TANGENT:
211 TRACE("%s", "tangent");
212 break;
213 case D3DDECLUSAGE_BINORMAL:
214 TRACE("%s", "binormal");
215 break;
216 case D3DDECLUSAGE_TESSFACTOR:
217 TRACE("%s", "tessfactor");
218 break;
219 case D3DDECLUSAGE_POSITIONT:
220 TRACE("%s%ld", "positionT", idx);
221 break;
222 case D3DDECLUSAGE_FOG:
223 TRACE("%s", "fog");
224 break;
225 case D3DDECLUSAGE_DEPTH:
226 TRACE("%s", "depth");
227 break;
228 case D3DDECLUSAGE_SAMPLE:
229 TRACE("%s", "sample");
230 break;
231 default:
232 FIXME("unknown_semantics(%08lx)", usage);
237 void shader_dump_param(
238 IWineD3DBaseShader *iface,
239 const DWORD param,
240 int input) {
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';
260 if (input) {
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) )
265 TRACE("-");
266 else if ((param & D3DSP_SRCMOD_MASK) == D3DSPSM_COMP)
267 TRACE("1-");
270 switch (regtype) {
271 case D3DSPR_TEMP:
272 TRACE("r%lu", reg);
273 break;
274 case D3DSPR_INPUT:
275 TRACE("v%lu", reg);
276 break;
277 case D3DSPR_CONST:
278 TRACE("c%s%lu", (param & D3DVS_ADDRMODE_RELATIVE) ? "a0.x + " : "", reg);
279 break;
280 case D3DSPR_TEXTURE: /* vs: case D3DSPR_ADDR */
281 TRACE("%c%lu", (pshader? 't':'a'), reg);
282 break;
283 case D3DSPR_RASTOUT:
284 TRACE("%s", rastout_reg_names[reg]);
285 break;
286 case D3DSPR_COLOROUT:
287 TRACE("oC%lu", reg);
288 break;
289 case D3DSPR_DEPTHOUT:
290 TRACE("oDepth");
291 break;
292 case D3DSPR_ATTROUT:
293 TRACE("oD%lu", reg);
294 break;
295 case D3DSPR_TEXCRDOUT:
296 TRACE("oT%lu", reg);
297 break;
298 case D3DSPR_CONSTINT:
299 TRACE("i%s%lu", (param & D3DVS_ADDRMODE_RELATIVE) ? "a0.x + " : "", reg);
300 break;
301 case D3DSPR_CONSTBOOL:
302 TRACE("b%s%lu", (param & D3DVS_ADDRMODE_RELATIVE) ? "a0.x + " : "", reg);
303 break;
304 case D3DSPR_LABEL:
305 TRACE("l%lu", reg);
306 break;
307 case D3DSPR_LOOP:
308 TRACE("aL%s%lu", (param & D3DVS_ADDRMODE_RELATIVE) ? "a0.x + " : "", reg);
309 break;
310 case D3DSPR_SAMPLER:
311 TRACE("s%lu", reg);
312 break;
313 default:
314 TRACE("unhandled_rtype(%lx)", regtype);
315 break;
318 if (!input) {
319 /* operand output (for modifiers and shift, see dump_ins_modifiers) */
321 if ((param & D3DSP_WRITEMASK_ALL) != D3DSP_WRITEMASK_ALL) {
322 TRACE(".");
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]);
329 } else {
330 /** operand input */
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);*/
340 switch (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;
352 default:
353 TRACE("_unknown(0x%08lx)", mask);
358 * swizzle bits fields:
359 * RRGGBBAA
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]);
366 } else {
367 TRACE(".%c%c%c%c",
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
378 output target */
379 void generate_arb_declarations(IWineD3DBaseShader *iface, SHADER_BUFFER* buffer) {
381 IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
382 DWORD i;
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
407 output target */
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;
427 DWORD i;
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 */
443 if (USING_GLSL)
444 generate_glsl_declarations(iface, buffer);
445 else
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)) {
454 ++pToken;
455 continue;
458 /* Skip comment tokens */
459 if (shader_is_comment(*pToken)) {
460 DWORD comment_len = (*pToken & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
461 ++pToken;
462 TRACE("#%s\n", (char*)pToken);
463 pToken += comment_len;
464 continue;
467 /* Read opcode */
468 curOpcode = shader_get_opcode(iface, *pToken);
469 ++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);
475 ++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 */
509 if (USING_GLSL)
510 curOpcode->hw_glsl_fct(&hw_arg);
511 else
512 curOpcode->hw_fct(&hw_arg);
514 pToken += curOpcode->num_params;
516 } else {
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;
539 switch (shift) {
540 case 0: break;
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;
550 switch(mmask) {
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 */