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 0xFFFF0000 == (token
& 0xFFFF0000) ||
34 0xFFFE0000 == (token
& 0xFFFF0000);
37 inline static BOOL
shader_is_comment_token(DWORD token
) {
38 return D3DSIO_COMMENT
== (token
& D3DSI_OPCODE_MASK
);
42 SHADER_BUFFER
* buffer
,
43 const char *format
, ...) {
45 char* base
= buffer
->buffer
+ buffer
->bsize
;
49 va_start(args
, format
);
50 rc
= vsnprintf(base
, SHADER_PGMSIZE
- 1 - buffer
->bsize
, format
, args
);
53 if (rc
< 0 || /* C89 */
54 rc
> SHADER_PGMSIZE
- 1 - buffer
->bsize
) { /* C99 */
56 ERR("The buffer allocated for the shader program string "
57 "is too small at %d bytes.\n", SHADER_PGMSIZE
);
58 buffer
->bsize
= SHADER_PGMSIZE
- 1;
64 TRACE("GL HW (%u, %u) : %s", buffer
->lineNo
, buffer
->bsize
, base
);
68 const SHADER_OPCODE
* shader_get_opcode(
69 IWineD3DBaseShader
*iface
, const DWORD code
) {
71 IWineD3DBaseShaderImpl
*This
= (IWineD3DBaseShaderImpl
*) iface
;
74 DWORD version
= This
->baseShader
.version
;
75 DWORD hex_version
= This
->baseShader
.hex_version
;
76 const SHADER_OPCODE
*shader_ins
= This
->baseShader
.shader_ins
;
78 /** TODO: use dichotomic search */
79 while (NULL
!= shader_ins
[i
].name
) {
80 if (((code
& D3DSI_OPCODE_MASK
) == shader_ins
[i
].opcode
) &&
81 (((hex_version
>= shader_ins
[i
].min_version
) && (hex_version
<= shader_ins
[i
].max_version
)) ||
82 ((shader_ins
[i
].min_version
== 0) && (shader_ins
[i
].max_version
== 0)))) {
83 return &shader_ins
[i
];
87 FIXME("Unsupported opcode %lx(%ld) masked %lx version %ld\n",
88 code
, code
, code
& D3DSI_OPCODE_MASK
, version
);
92 /* Note: For vertex shaders,
93 * texUsed = addrUsed, and
94 * D3DSPR_TEXTURE = D3DSPR_ADDR.
96 * Also note that this does not count the loop register
97 * as an address register. */
99 void shader_get_registers_used(
100 IWineD3DBaseShader
*iface
,
101 CONST DWORD
* pToken
) {
103 IWineD3DBaseShaderImpl
* This
= (IWineD3DBaseShaderImpl
*) iface
;
104 DWORD
* tempsUsed
= &This
->baseShader
.temps_used
;
105 DWORD
* texUsed
= &This
->baseShader
.textures_used
;
113 while (D3DVS_END() != *pToken
) {
114 CONST SHADER_OPCODE
* curOpcode
;
117 if (shader_is_version_token(*pToken
)) {
122 } else if (shader_is_comment_token(*pToken
)) {
123 DWORD comment_len
= (*pToken
& D3DSI_COMMENTSIZE_MASK
) >> D3DSI_COMMENTSIZE_SHIFT
;
125 pToken
+= comment_len
;
130 curOpcode
= shader_get_opcode(iface
, *pToken
);
133 /* Unhandled opcode, and its parameters */
134 if (NULL
== curOpcode
) {
135 while (*pToken
& 0x80000000)
139 /* Skip declarations (for now) */
140 } else if (D3DSIO_DCL
== curOpcode
->opcode
) {
141 pToken
+= curOpcode
->num_params
;
144 /* Skip definitions (for now) */
145 } else if (D3DSIO_DEF
== curOpcode
->opcode
) {
146 pToken
+= curOpcode
->num_params
;
149 /* Set texture registers, and temporary registers */
153 for (i
= 0; i
< curOpcode
->num_params
; ++i
) {
154 DWORD regtype
= (((*pToken
) & D3DSP_REGTYPE_MASK
) >> D3DSP_REGTYPE_SHIFT
);
155 DWORD reg
= (*pToken
) & D3DSP_REGNUM_MASK
;
156 if (D3DSPR_TEXTURE
== regtype
)
157 *texUsed
|= (1 << reg
);
158 if (D3DSPR_TEMP
== regtype
)
159 *tempsUsed
|= (1 << reg
);
166 void shader_program_dump_decl_usage(
170 DWORD regtype
= shader_get_regtype(param
);
173 if (regtype
== D3DSPR_SAMPLER
) {
174 DWORD ttype
= decl
& D3DSP_TEXTURETYPE_MASK
;
177 case D3DSTT_2D
: TRACE("2d"); break;
178 case D3DSTT_CUBE
: TRACE("cube"); break;
179 case D3DSTT_VOLUME
: TRACE("volume"); break;
180 default: TRACE("unknown_ttype(%08lx)", ttype
);
185 DWORD usage
= decl
& D3DSP_DCL_USAGE_MASK
;
186 DWORD idx
= (decl
& D3DSP_DCL_USAGEINDEX_MASK
) >> D3DSP_DCL_USAGEINDEX_SHIFT
;
189 case D3DDECLUSAGE_POSITION
:
190 TRACE("%s%ld", "position", idx
);
192 case D3DDECLUSAGE_BLENDINDICES
:
193 TRACE("%s", "blend");
195 case D3DDECLUSAGE_BLENDWEIGHT
:
196 TRACE("%s", "weight");
198 case D3DDECLUSAGE_NORMAL
:
199 TRACE("%s%ld", "normal", idx
);
201 case D3DDECLUSAGE_PSIZE
:
202 TRACE("%s", "psize");
204 case D3DDECLUSAGE_COLOR
:
206 TRACE("%s", "color");
208 TRACE("%s%ld", "specular", (idx
- 1));
211 case D3DDECLUSAGE_TEXCOORD
:
212 TRACE("%s%ld", "texture", idx
);
214 case D3DDECLUSAGE_TANGENT
:
215 TRACE("%s", "tangent");
217 case D3DDECLUSAGE_BINORMAL
:
218 TRACE("%s", "binormal");
220 case D3DDECLUSAGE_TESSFACTOR
:
221 TRACE("%s", "tessfactor");
223 case D3DDECLUSAGE_POSITIONT
:
224 TRACE("%s%ld", "positionT", idx
);
226 case D3DDECLUSAGE_FOG
:
229 case D3DDECLUSAGE_DEPTH
:
230 TRACE("%s", "depth");
232 case D3DDECLUSAGE_SAMPLE
:
233 TRACE("%s", "sample");
236 FIXME("unknown_semantics(%08lx)", usage
);
241 /** Shared code in order to generate the bulk of the shader string.
242 Use the shader_header_fct & shader_footer_fct to add strings
243 that are specific to pixel or vertex functions
244 NOTE: A description of how to parse tokens can be found at:
245 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/graphics/hh/graphics/usermodedisplaydriver_shader_cc8e4e05-f5c3-4ec0-8853-8ce07c1551b2.xml.asp */
246 void generate_base_shader(
247 IWineD3DBaseShader
*iface
,
248 SHADER_BUFFER
* buffer
,
249 CONST DWORD
* pFunction
) {
251 IWineD3DBaseShaderImpl
* This
= (IWineD3DBaseShaderImpl
*) iface
;
252 const DWORD
*pToken
= pFunction
;
253 const SHADER_OPCODE
*curOpcode
= NULL
;
256 /* Initialize current parsing state */
257 This
->baseShader
.parse_state
.current_row
= 0;
259 /* First pass: figure out which temporary and texture registers are used */
260 shader_get_registers_used(iface
, pToken
);
261 TRACE("Texture/Address registers used: %#lx, Temp registers used %#lx\n",
262 This
->baseShader
.textures_used
, This
->baseShader
.temps_used
);
264 /* TODO: check register usage against GL/Directx limits, and fail if they're exceeded
265 nUseAddressRegister < = GL_MAX_PROGRAM_ADDRESS_REGISTERS_AR
266 nUseTempRegister <= GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB
269 /* Pre-declare registers */
270 for(i
= 0; i
< This
->baseShader
.limits
.temporary
; i
++) {
271 if (This
->baseShader
.temps_used
& (1 << i
))
272 shader_addline(buffer
, "TEMP R%lu;\n", i
);
275 for (i
= 0; i
< This
->baseShader
.limits
.address
; i
++) {
276 if (This
->baseShader
.textures_used
& (1 << i
))
277 shader_addline(buffer
, "ADDRESS A%ld;\n", i
);
280 for(i
= 0; i
< This
->baseShader
.limits
.texture
; i
++) {
281 if (This
->baseShader
.textures_used
& (1 << i
))
282 shader_addline(buffer
,"TEMP T%lu;\n", i
);
285 /* Texture coordinate registers must be pre-loaded */
286 for (i
= 0; i
< This
->baseShader
.limits
.texture
; i
++) {
287 if (This
->baseShader
.textures_used
& (1 << i
))
288 shader_addline(buffer
, "MOV T%lu, fragment.texcoord[%lu];\n", i
, i
);
291 /* Second pass, process opcodes */
292 if (NULL
!= pToken
) {
293 while (D3DPS_END() != *pToken
) {
295 /* Skip version token */
296 if (shader_is_version_token(*pToken
)) {
301 /* Skip comment tokens */
302 if (shader_is_comment_token(*pToken
)) {
303 DWORD comment_len
= (*pToken
& D3DSI_COMMENTSIZE_MASK
) >> D3DSI_COMMENTSIZE_SHIFT
;
305 TRACE("#%s\n", (char*)pToken
);
306 pToken
+= comment_len
;
311 curOpcode
= shader_get_opcode(iface
, *pToken
);
314 /* Unknown opcode and its parameters */
315 if (NULL
== curOpcode
) {
316 while (*pToken
& 0x80000000) { /* TODO: Think of a sensible name for 0x80000000 */
317 FIXME("unrecognized opcode: %08lx\n", *pToken
);
321 /* Unhandled opcode */
322 } else if (GLNAME_REQUIRE_GLSL
== curOpcode
->glname
) {
324 FIXME("Token %s requires greater functionality than "
325 "Vertex or Fragment_Program_ARB supports\n", curOpcode
->name
);
326 pToken
+= curOpcode
->num_params
;
328 /* If a generator function is set, use it */
329 } else if (curOpcode
->hw_fct
!= NULL
) {
331 SHADER_OPCODE_ARG hw_arg
;
333 hw_arg
.shader
= iface
;
334 hw_arg
.opcode
= curOpcode
;
335 hw_arg
.buffer
= buffer
;
336 if (curOpcode
->num_params
> 0) {
337 hw_arg
.dst
= *pToken
;
339 /* FIXME: this does not account for relative address tokens */
340 for (i
= 1; i
< curOpcode
->num_params
; i
++)
341 hw_arg
.src
[i
-1] = *(pToken
+ i
);
344 curOpcode
->hw_fct(&hw_arg
);
345 pToken
+= curOpcode
->num_params
;
349 TRACE("Found opcode D3D:%s GL:%s, PARAMS:%d, \n",
350 curOpcode
->name
, curOpcode
->glname
, curOpcode
->num_params
);
352 /* Unless we encounter a no-op command, this opcode is unrecognized */
353 if (curOpcode
->opcode
!= D3DSIO_NOP
) {
354 FIXME("Can't handle opcode %s in hwShader\n", curOpcode
->name
);
355 pToken
+= curOpcode
->num_params
;
359 /* TODO: What about result.depth? */
365 /* TODO: Move other shared code here */