2 * shaders implementation
4 * Copyright 2002-2003 Jason Edmeades
5 * Copyright 2002-2003 Raphael Junqueira
6 * Copyright 2004 Christian Costa
7 * Copyright 2005 Oliver Stieber
8 * Copyright 2006 Ivan Gyurdiev
9 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader
);
35 #define GLINFO_LOCATION ((IWineD3DDeviceImpl *)This->baseShader.device)->adapter->gl_info
37 /* TODO: Vertex and Pixel shaders are almost identical, the only exception being the way that some of the data is looked up or the availability of some of the data i.e. some instructions are only valid for pshaders and some for vshaders
38 because of this the bulk of the software pipeline can be shared between pixel and vertex shaders... and it wouldn't surprise me if the program can be cross compiled using a large body of shared code */
40 #define GLNAME_REQUIRE_GLSL ((const char *)1)
42 CONST SHADER_OPCODE IWineD3DVertexShaderImpl_shader_ins
[] = {
43 /* This table is not order or position dependent. */
46 {WINED3DSIO_NOP
, "nop", "NOP", 0, 0, WINED3DSIH_NOP
, 0, 0 },
47 {WINED3DSIO_MOV
, "mov", "MOV", 1, 2, WINED3DSIH_MOV
, 0, 0 },
48 {WINED3DSIO_MOVA
, "mova", NULL
, 1, 2, WINED3DSIH_MOVA
, WINED3DVS_VERSION(2,0), -1 },
49 {WINED3DSIO_ADD
, "add", "ADD", 1, 3, WINED3DSIH_ADD
, 0, 0 },
50 {WINED3DSIO_SUB
, "sub", "SUB", 1, 3, WINED3DSIH_SUB
, 0, 0 },
51 {WINED3DSIO_MAD
, "mad", "MAD", 1, 4, WINED3DSIH_MAD
, 0, 0 },
52 {WINED3DSIO_MUL
, "mul", "MUL", 1, 3, WINED3DSIH_MUL
, 0, 0 },
53 {WINED3DSIO_RCP
, "rcp", "RCP", 1, 2, WINED3DSIH_RCP
, 0, 0 },
54 {WINED3DSIO_RSQ
, "rsq", "RSQ", 1, 2, WINED3DSIH_RSQ
, 0, 0 },
55 {WINED3DSIO_DP3
, "dp3", "DP3", 1, 3, WINED3DSIH_DP3
, 0, 0 },
56 {WINED3DSIO_DP4
, "dp4", "DP4", 1, 3, WINED3DSIH_DP4
, 0, 0 },
57 {WINED3DSIO_MIN
, "min", "MIN", 1, 3, WINED3DSIH_MIN
, 0, 0 },
58 {WINED3DSIO_MAX
, "max", "MAX", 1, 3, WINED3DSIH_MAX
, 0, 0 },
59 {WINED3DSIO_SLT
, "slt", "SLT", 1, 3, WINED3DSIH_SLT
, 0, 0 },
60 {WINED3DSIO_SGE
, "sge", "SGE", 1, 3, WINED3DSIH_SGE
, 0, 0 },
61 {WINED3DSIO_ABS
, "abs", "ABS", 1, 2, WINED3DSIH_ABS
, 0, 0 },
62 {WINED3DSIO_EXP
, "exp", "EX2", 1, 2, WINED3DSIH_EXP
, 0, 0 },
63 {WINED3DSIO_LOG
, "log", "LG2", 1, 2, WINED3DSIH_LOG
, 0, 0 },
64 {WINED3DSIO_EXPP
, "expp", "EXP", 1, 2, WINED3DSIH_EXPP
, 0, 0 },
65 {WINED3DSIO_LOGP
, "logp", "LOG", 1, 2, WINED3DSIH_LOGP
, 0, 0 },
66 {WINED3DSIO_LIT
, "lit", "LIT", 1, 2, WINED3DSIH_LIT
, 0, 0 },
67 {WINED3DSIO_DST
, "dst", "DST", 1, 3, WINED3DSIH_DST
, 0, 0 },
68 {WINED3DSIO_LRP
, "lrp", "LRP", 1, 4, WINED3DSIH_LRP
, 0, 0 },
69 {WINED3DSIO_FRC
, "frc", "FRC", 1, 2, WINED3DSIH_FRC
, 0, 0 },
70 {WINED3DSIO_POW
, "pow", "POW", 1, 3, WINED3DSIH_POW
, 0, 0 },
71 {WINED3DSIO_CRS
, "crs", "XPD", 1, 3, WINED3DSIH_CRS
, 0, 0 },
72 /* TODO: sng can possibly be performed a s
75 {WINED3DSIO_SGN
, "sgn", NULL
, 1, 2, WINED3DSIH_SGN
, 0, 0 },
76 {WINED3DSIO_NRM
, "nrm", NULL
, 1, 2, WINED3DSIH_NRM
, 0, 0 },
77 {WINED3DSIO_SINCOS
, "sincos", NULL
, 1, 4, WINED3DSIH_SINCOS
, WINED3DVS_VERSION(2,0), WINED3DVS_VERSION(2,1)},
78 {WINED3DSIO_SINCOS
, "sincos", "SCS", 1, 2, WINED3DSIH_SINCOS
, WINED3DVS_VERSION(3,0), -1 },
80 {WINED3DSIO_M4x4
, "m4x4", "undefined", 1, 3, WINED3DSIH_M4x4
, 0, 0 },
81 {WINED3DSIO_M4x3
, "m4x3", "undefined", 1, 3, WINED3DSIH_M4x3
, 0, 0 },
82 {WINED3DSIO_M3x4
, "m3x4", "undefined", 1, 3, WINED3DSIH_M3x4
, 0, 0 },
83 {WINED3DSIO_M3x3
, "m3x3", "undefined", 1, 3, WINED3DSIH_M3x3
, 0, 0 },
84 {WINED3DSIO_M3x2
, "m3x2", "undefined", 1, 3, WINED3DSIH_M3x2
, 0, 0 },
85 /* Declare registers */
86 {WINED3DSIO_DCL
, "dcl", NULL
, 0, 2, WINED3DSIH_DCL
, 0, 0 },
87 /* Constant definitions */
88 {WINED3DSIO_DEF
, "def", NULL
, 1, 5, WINED3DSIH_DEF
, 0, 0 },
89 {WINED3DSIO_DEFB
, "defb", GLNAME_REQUIRE_GLSL
, 1, 2, WINED3DSIH_DEFB
, 0, 0 },
90 {WINED3DSIO_DEFI
, "defi", GLNAME_REQUIRE_GLSL
, 1, 5, WINED3DSIH_DEFI
, 0, 0 },
91 /* Flow control - requires GLSL or software shaders */
92 {WINED3DSIO_REP
, "rep", NULL
, 0, 1, WINED3DSIH_REP
, WINED3DVS_VERSION(2,0), -1 },
93 {WINED3DSIO_ENDREP
, "endrep", NULL
, 0, 0, WINED3DSIH_ENDREP
, WINED3DVS_VERSION(2,0), -1 },
94 {WINED3DSIO_IF
, "if", NULL
, 0, 1, WINED3DSIH_IF
, WINED3DVS_VERSION(2,0), -1 },
95 {WINED3DSIO_IFC
, "ifc", NULL
, 0, 2, WINED3DSIH_IFC
, WINED3DVS_VERSION(2,1), -1 },
96 {WINED3DSIO_ELSE
, "else", NULL
, 0, 0, WINED3DSIH_ELSE
, WINED3DVS_VERSION(2,0), -1 },
97 {WINED3DSIO_ENDIF
, "endif", NULL
, 0, 0, WINED3DSIH_ENDIF
, WINED3DVS_VERSION(2,0), -1 },
98 {WINED3DSIO_BREAK
, "break", NULL
, 0, 0, WINED3DSIH_BREAK
, WINED3DVS_VERSION(2,1), -1 },
99 {WINED3DSIO_BREAKC
, "breakc", NULL
, 0, 2, WINED3DSIH_BREAKC
, WINED3DVS_VERSION(2,1), -1 },
100 {WINED3DSIO_BREAKP
, "breakp", GLNAME_REQUIRE_GLSL
, 0, 1, WINED3DSIH_BREAKP
, 0, 0 },
101 {WINED3DSIO_CALL
, "call", NULL
, 0, 1, WINED3DSIH_CALL
, WINED3DVS_VERSION(2,0), -1 },
102 {WINED3DSIO_CALLNZ
, "callnz", NULL
, 0, 2, WINED3DSIH_CALLNZ
, WINED3DVS_VERSION(2,0), -1 },
103 {WINED3DSIO_LOOP
, "loop", NULL
, 0, 2, WINED3DSIH_LOOP
, WINED3DVS_VERSION(2,0), -1 },
104 {WINED3DSIO_RET
, "ret", NULL
, 0, 0, WINED3DSIH_RET
, WINED3DVS_VERSION(2,0), -1 },
105 {WINED3DSIO_ENDLOOP
, "endloop", NULL
, 0, 0, WINED3DSIH_ENDLOOP
, WINED3DVS_VERSION(2,0), -1 },
106 {WINED3DSIO_LABEL
, "label", NULL
, 0, 1, WINED3DSIH_LABEL
, WINED3DVS_VERSION(2,0), -1 },
108 {WINED3DSIO_SETP
, "setp", GLNAME_REQUIRE_GLSL
, 1, 3, WINED3DSIH_SETP
, 0, 0 },
109 {WINED3DSIO_TEXLDL
, "texldl", NULL
, 1, 3, WINED3DSIH_TEXLDL
, WINED3DVS_VERSION(3,0), -1 },
110 {0, NULL
, NULL
, 0, 0, 0, 0, 0 }
113 static void vshader_set_limits(
114 IWineD3DVertexShaderImpl
*This
) {
116 This
->baseShader
.limits
.texcoord
= 0;
117 This
->baseShader
.limits
.attributes
= 16;
118 This
->baseShader
.limits
.packed_input
= 0;
120 /* Must match D3DCAPS9.MaxVertexShaderConst: at least 256 for vs_2_0 */
121 This
->baseShader
.limits
.constant_float
= GL_LIMITS(vshader_constantsF
);
123 switch (This
->baseShader
.hex_version
) {
124 case WINED3DVS_VERSION(1,0):
125 case WINED3DVS_VERSION(1,1):
126 This
->baseShader
.limits
.temporary
= 12;
127 This
->baseShader
.limits
.constant_bool
= 0;
128 This
->baseShader
.limits
.constant_int
= 0;
129 This
->baseShader
.limits
.address
= 1;
130 This
->baseShader
.limits
.packed_output
= 0;
131 This
->baseShader
.limits
.sampler
= 0;
132 This
->baseShader
.limits
.label
= 0;
135 case WINED3DVS_VERSION(2,0):
136 case WINED3DVS_VERSION(2,1):
137 This
->baseShader
.limits
.temporary
= 12;
138 This
->baseShader
.limits
.constant_bool
= 16;
139 This
->baseShader
.limits
.constant_int
= 16;
140 This
->baseShader
.limits
.address
= 1;
141 This
->baseShader
.limits
.packed_output
= 0;
142 This
->baseShader
.limits
.sampler
= 0;
143 This
->baseShader
.limits
.label
= 16;
146 case WINED3DVS_VERSION(3,0):
147 This
->baseShader
.limits
.temporary
= 32;
148 This
->baseShader
.limits
.constant_bool
= 32;
149 This
->baseShader
.limits
.constant_int
= 32;
150 This
->baseShader
.limits
.address
= 1;
151 This
->baseShader
.limits
.packed_output
= 12;
152 This
->baseShader
.limits
.sampler
= 4;
153 This
->baseShader
.limits
.label
= 16; /* FIXME: 2048 */
156 default: This
->baseShader
.limits
.temporary
= 12;
157 This
->baseShader
.limits
.constant_bool
= 16;
158 This
->baseShader
.limits
.constant_int
= 16;
159 This
->baseShader
.limits
.address
= 1;
160 This
->baseShader
.limits
.packed_output
= 0;
161 This
->baseShader
.limits
.sampler
= 0;
162 This
->baseShader
.limits
.label
= 16;
163 FIXME("Unrecognized vertex shader version %#x\n",
164 This
->baseShader
.hex_version
);
168 /* This is an internal function,
169 * used to create fake semantics for shaders
170 * that don't have them - d3d8 shaders where the declaration
171 * stores the register for each input
173 static void vshader_set_input(
174 IWineD3DVertexShaderImpl
* This
,
176 BYTE usage
, BYTE usage_idx
) {
178 /* Fake usage: set reserved bit, usage, usage_idx */
179 DWORD usage_token
= (0x1 << 31) |
180 (usage
<< WINED3DSP_DCL_USAGE_SHIFT
) | (usage_idx
<< WINED3DSP_DCL_USAGEINDEX_SHIFT
);
182 /* Fake register; set reserved bit, regnum, type: input, wmask: all */
183 DWORD reg_token
= (0x1 << 31) |
184 WINED3DSP_WRITEMASK_ALL
| (WINED3DSPR_INPUT
<< WINED3DSP_REGTYPE_SHIFT
) | regnum
;
186 This
->semantics_in
[regnum
].usage
= usage_token
;
187 This
->semantics_in
[regnum
].reg
= reg_token
;
190 static BOOL
match_usage(BYTE usage1
, BYTE usage_idx1
, BYTE usage2
, BYTE usage_idx2
) {
191 if (usage_idx1
!= usage_idx2
) return FALSE
;
192 if (usage1
== usage2
) return TRUE
;
193 if (usage1
== WINED3DDECLUSAGE_POSITION
&& usage2
== WINED3DDECLUSAGE_POSITIONT
) return TRUE
;
194 if (usage2
== WINED3DDECLUSAGE_POSITION
&& usage1
== WINED3DDECLUSAGE_POSITIONT
) return TRUE
;
199 BOOL
vshader_get_input(
200 IWineD3DVertexShader
* iface
,
201 BYTE usage_req
, BYTE usage_idx_req
,
202 unsigned int* regnum
) {
204 IWineD3DVertexShaderImpl
* This
= (IWineD3DVertexShaderImpl
*) iface
;
207 for (i
= 0; i
< MAX_ATTRIBS
; i
++) {
208 DWORD usage_token
= This
->semantics_in
[i
].usage
;
209 DWORD usage
= (usage_token
& WINED3DSP_DCL_USAGE_MASK
) >> WINED3DSP_DCL_USAGE_SHIFT
;
210 DWORD usage_idx
= (usage_token
& WINED3DSP_DCL_USAGEINDEX_MASK
) >> WINED3DSP_DCL_USAGEINDEX_SHIFT
;
212 if (usage_token
&& match_usage(usage
, usage_idx
, usage_req
, usage_idx_req
)) {
220 BOOL
vshader_input_is_color(
221 IWineD3DVertexShader
* iface
,
222 unsigned int regnum
) {
224 IWineD3DVertexShaderImpl
* This
= (IWineD3DVertexShaderImpl
*) iface
;
226 DWORD usage_token
= This
->semantics_in
[regnum
].usage
;
227 DWORD usage
= (usage_token
& WINED3DSP_DCL_USAGE_MASK
) >> WINED3DSP_DCL_USAGE_SHIFT
;
228 DWORD usage_idx
= (usage_token
& WINED3DSP_DCL_USAGEINDEX_MASK
) >> WINED3DSP_DCL_USAGEINDEX_SHIFT
;
232 for(i
= 0; i
< This
->num_swizzled_attribs
; i
++) {
233 if(This
->swizzled_attribs
[i
].usage
== usage
&&
234 This
->swizzled_attribs
[i
].idx
== usage_idx
) {
241 static inline void find_swizzled_attribs(IWineD3DVertexDeclaration
*declaration
, IWineD3DVertexShaderImpl
*This
) {
243 UINT numoldswizzles
= This
->num_swizzled_attribs
;
244 IWineD3DVertexDeclarationImpl
*decl
= (IWineD3DVertexDeclarationImpl
*) declaration
;
246 DWORD usage_token
, usage
, usage_idx
;
249 attrib_declaration oldswizzles
[sizeof(This
->swizzled_attribs
) / sizeof(This
->swizzled_attribs
[0])];
251 /* Back up the old swizzles to keep attributes that are undefined in the current declaration */
252 memcpy(oldswizzles
, This
->swizzled_attribs
, sizeof(oldswizzles
));
254 memset(This
->swizzled_attribs
, 0, sizeof(This
->swizzled_attribs
[0]) * MAX_ATTRIBS
);
256 for(i
= 0; i
< decl
->num_swizzled_attribs
; i
++) {
257 for(j
= 0; j
< MAX_ATTRIBS
; j
++) {
259 if(!This
->baseShader
.reg_maps
.attributes
[j
]) continue;
261 usage_token
= This
->semantics_in
[j
].usage
;
262 usage
= (usage_token
& WINED3DSP_DCL_USAGE_MASK
) >> WINED3DSP_DCL_USAGE_SHIFT
;
263 usage_idx
= (usage_token
& WINED3DSP_DCL_USAGEINDEX_MASK
) >> WINED3DSP_DCL_USAGEINDEX_SHIFT
;
265 if(decl
->swizzled_attribs
[i
].usage
== usage
&&
266 decl
->swizzled_attribs
[i
].idx
== usage_idx
) {
267 This
->swizzled_attribs
[num
].usage
= usage
;
268 This
->swizzled_attribs
[num
].idx
= usage_idx
;
274 /* Add previously converted attributes back in if they are not defined in the current declaration */
275 for(i
= 0; i
< numoldswizzles
; i
++) {
278 for(j
= 0; j
< decl
->declarationWNumElements
; j
++) {
279 if(oldswizzles
[i
].usage
== decl
->pDeclarationWine
[j
].Usage
&&
280 oldswizzles
[i
].idx
== decl
->pDeclarationWine
[j
].UsageIndex
) {
285 /* This previously converted attribute is declared in the current declaration. Either it is
286 * already in the new array, or it should not be there. Skip it
290 /* We have a previously swizzled attribute that is not defined by the current vertex declaration.
291 * Insert it into the new conversion array to keep it in the old defined state. Otherwise we end up
292 * recompiling if the old decl is used again because undefined attributes are reset to no swizzling.
293 * In the reverse way(attribute was not swizzled and is not declared in new declaration) the attrib
294 * stays unswizzled as well because it isn't found in the oldswizzles array
296 for(j
= 0; j
< num
; j
++) {
297 if(oldswizzles
[i
].usage
> This
->swizzled_attribs
[j
].usage
|| (
298 oldswizzles
[i
].usage
== This
->swizzled_attribs
[j
].usage
&&
299 oldswizzles
[i
].idx
> This
->swizzled_attribs
[j
].idx
)) {
300 memmove(&This
->swizzled_attribs
[j
+ 1], &This
->swizzled_attribs
[j
],
301 sizeof(This
->swizzled_attribs
) - (sizeof(This
->swizzled_attribs
[0]) * (j
+ 1)));
305 This
->swizzled_attribs
[j
].usage
= oldswizzles
[i
].usage
;
306 This
->swizzled_attribs
[j
].idx
= oldswizzles
[i
].idx
;
310 TRACE("New swizzled attributes array\n");
311 for(i
= 0; i
< num
; i
++) {
312 TRACE("%d: %s(%d), %d\n", i
, debug_d3ddeclusage(This
->swizzled_attribs
[i
].usage
),
313 This
->swizzled_attribs
[i
].usage
, This
->swizzled_attribs
[i
].idx
);
315 This
->num_swizzled_attribs
= num
;
317 /** Generate a vertex shader string using either GL_VERTEX_PROGRAM_ARB
318 or GLSL and send it to the card */
319 static VOID
IWineD3DVertexShaderImpl_GenerateShader(
320 IWineD3DVertexShader
*iface
,
321 shader_reg_maps
* reg_maps
,
322 CONST DWORD
*pFunction
) {
324 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
325 IWineD3DVertexDeclaration
*decl
= ((IWineD3DDeviceImpl
*) This
->baseShader
.device
)->stateBlock
->vertexDecl
;
326 SHADER_BUFFER buffer
;
328 find_swizzled_attribs(decl
, This
);
330 #if 0 /* FIXME: Use the buffer that is held by the device, this is ok since fixups will be skipped for software shaders
331 it also requires entering a critical section but cuts down the runtime footprint of wined3d and any memory fragmentation that may occur... */
332 if (This
->device
->fixupVertexBufferSize
< SHADER_PGMSIZE
) {
333 HeapFree(GetProcessHeap(), 0, This
->fixupVertexBuffer
);
334 This
->fixupVertexBuffer
= HeapAlloc(GetProcessHeap() , 0, SHADER_PGMSIZE
);
335 This
->fixupVertexBufferSize
= PGMSIZE
;
336 This
->fixupVertexBuffer
[0] = 0;
338 buffer
.buffer
= This
->device
->fixupVertexBuffer
;
340 buffer
.buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, SHADER_PGMSIZE
);
344 buffer
.newline
= TRUE
;
346 ((IWineD3DDeviceImpl
*)This
->baseShader
.device
)->shader_backend
->shader_generate_vshader(iface
, &buffer
);
348 #if 1 /* if were using the data buffer of device then we don't need to free it */
349 HeapFree(GetProcessHeap(), 0, buffer
.buffer
);
353 /* *******************************************
354 IWineD3DVertexShader IUnknown parts follow
355 ******************************************* */
356 static HRESULT WINAPI
IWineD3DVertexShaderImpl_QueryInterface(IWineD3DVertexShader
*iface
, REFIID riid
, LPVOID
*ppobj
) {
357 return IWineD3DBaseShaderImpl_QueryInterface((IWineD3DBaseShader
*) iface
, riid
, ppobj
);
360 static ULONG WINAPI
IWineD3DVertexShaderImpl_AddRef(IWineD3DVertexShader
*iface
) {
361 return IWineD3DBaseShaderImpl_AddRef((IWineD3DBaseShader
*) iface
);
364 static ULONG WINAPI
IWineD3DVertexShaderImpl_Release(IWineD3DVertexShader
*iface
) {
365 return IWineD3DBaseShaderImpl_Release((IWineD3DBaseShader
*) iface
);
368 /* *******************************************
369 IWineD3DVertexShader IWineD3DVertexShader parts follow
370 ******************************************* */
372 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetParent(IWineD3DVertexShader
*iface
, IUnknown
** parent
){
373 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
375 *parent
= This
->parent
;
376 IUnknown_AddRef(*parent
);
377 TRACE("(%p) : returning %p\n", This
, *parent
);
381 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetDevice(IWineD3DVertexShader
* iface
, IWineD3DDevice
**pDevice
){
382 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
383 IWineD3DDevice_AddRef(This
->baseShader
.device
);
384 *pDevice
= This
->baseShader
.device
;
385 TRACE("(%p) returning %p\n", This
, *pDevice
);
389 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetFunction(IWineD3DVertexShader
* impl
, VOID
* pData
, UINT
* pSizeOfData
) {
390 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)impl
;
391 TRACE("(%p) : pData(%p), pSizeOfData(%p)\n", This
, pData
, pSizeOfData
);
394 *pSizeOfData
= This
->baseShader
.functionLength
;
397 if (*pSizeOfData
< This
->baseShader
.functionLength
) {
398 /* MSDN claims (for d3d8 at least) that if *pSizeOfData is smaller
399 * than the required size we should write the required size and
400 * return D3DERR_MOREDATA. That's not actually true. */
401 return WINED3DERR_INVALIDCALL
;
403 if (NULL
== This
->baseShader
.function
) { /* no function defined */
404 TRACE("(%p) : GetFunction no User Function defined using NULL to %p\n", This
, pData
);
405 (*(DWORD
**) pData
) = NULL
;
407 if(This
->baseShader
.functionLength
== 0){
410 TRACE("(%p) : GetFunction copying to %p\n", This
, pData
);
411 memcpy(pData
, This
->baseShader
.function
, This
->baseShader
.functionLength
);
416 /* Note that for vertex shaders CompileShader isn't called until the
417 * shader is first used. The reason for this is that we need the vertex
418 * declaration the shader will be used with in order to determine if
419 * the data in a register is of type D3DCOLOR, and needs swizzling. */
420 static HRESULT WINAPI
IWineD3DVertexShaderImpl_SetFunction(IWineD3DVertexShader
*iface
, CONST DWORD
*pFunction
) {
422 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
423 IWineD3DDeviceImpl
*deviceImpl
= (IWineD3DDeviceImpl
*) This
->baseShader
.device
;
425 shader_reg_maps
*reg_maps
= &This
->baseShader
.reg_maps
;
427 TRACE("(%p) : pFunction %p\n", iface
, pFunction
);
429 /* First pass: trace shader */
430 shader_trace_init((IWineD3DBaseShader
*) This
, pFunction
);
431 vshader_set_limits(This
);
433 /* Initialize immediate constant lists */
434 list_init(&This
->baseShader
.constantsF
);
435 list_init(&This
->baseShader
.constantsB
);
436 list_init(&This
->baseShader
.constantsI
);
438 /* Second pass: figure out registers used, semantics, etc.. */
439 This
->min_rel_offset
= GL_LIMITS(vshader_constantsF
);
440 This
->max_rel_offset
= 0;
441 memset(reg_maps
, 0, sizeof(shader_reg_maps
));
442 hr
= shader_get_registers_used((IWineD3DBaseShader
*) This
, reg_maps
,
443 This
->semantics_in
, This
->semantics_out
, pFunction
, NULL
);
444 if (hr
!= WINED3D_OK
) return hr
;
446 This
->baseShader
.shader_mode
= deviceImpl
->vs_selected_mode
;
448 if(deviceImpl
->vs_selected_mode
== SHADER_ARB
&&
449 (GLINFO_LOCATION
).arb_vs_offset_limit
&&
450 This
->min_rel_offset
<= This
->max_rel_offset
) {
452 if(This
->max_rel_offset
- This
->min_rel_offset
> 127) {
453 FIXME("The difference between the minimum and maximum relative offset is > 127\n");
454 FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
455 FIXME("Min: %d, Max: %d\n", This
->min_rel_offset
, This
->max_rel_offset
);
456 } else if(This
->max_rel_offset
- This
->min_rel_offset
> 63) {
457 This
->rel_offset
= This
->min_rel_offset
+ 63;
458 } else if(This
->max_rel_offset
> 63) {
459 This
->rel_offset
= This
->min_rel_offset
;
461 This
->rel_offset
= 0;
464 This
->baseShader
.load_local_constsF
= This
->baseShader
.reg_maps
.usesrelconstF
&& !list_empty(&This
->baseShader
.constantsF
);
466 /* copy the function ... because it will certainly be released by application */
467 if (NULL
!= pFunction
) {
470 function
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->baseShader
.functionLength
);
471 if (!function
) return E_OUTOFMEMORY
;
472 memcpy(function
, pFunction
, This
->baseShader
.functionLength
);
473 This
->baseShader
.function
= function
;
475 This
->baseShader
.function
= NULL
;
481 /* Preload semantics for d3d8 shaders */
482 static void WINAPI
IWineD3DVertexShaderImpl_FakeSemantics(IWineD3DVertexShader
*iface
, IWineD3DVertexDeclaration
*vertex_declaration
) {
483 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
484 IWineD3DVertexDeclarationImpl
* vdecl
= (IWineD3DVertexDeclarationImpl
*)vertex_declaration
;
487 for (i
= 0; i
< vdecl
->declarationWNumElements
- 1; ++i
) {
488 WINED3DVERTEXELEMENT
* element
= vdecl
->pDeclarationWine
+ i
;
489 vshader_set_input(This
, element
->Reg
, element
->Usage
, element
->UsageIndex
);
493 /* Set local constants for d3d8 shaders */
494 static HRESULT WINAPI
IWIneD3DVertexShaderImpl_SetLocalConstantsF(IWineD3DVertexShader
*iface
,
495 UINT start_idx
, const float *src_data
, UINT count
) {
496 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
499 TRACE("(%p) : start_idx %u, src_data %p, count %u\n", This
, start_idx
, src_data
, count
);
501 end_idx
= start_idx
+ count
;
502 if (end_idx
> GL_LIMITS(vshader_constantsF
)) {
503 WARN("end_idx %u > float constants limit %u\n", end_idx
, GL_LIMITS(vshader_constantsF
));
504 end_idx
= GL_LIMITS(vshader_constantsF
);
507 for (i
= start_idx
; i
< end_idx
; ++i
) {
508 local_constant
* lconst
= HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant
));
509 if (!lconst
) return E_OUTOFMEMORY
;
512 memcpy(lconst
->value
, src_data
+ (i
- start_idx
) * 4 /* 4 components */, 4 * sizeof(float));
513 list_add_head(&This
->baseShader
.constantsF
, &lconst
->entry
);
519 static inline BOOL
swizzled_attribs_differ(IWineD3DVertexShaderImpl
*This
, IWineD3DVertexDeclarationImpl
*vdecl
) {
527 for(i
= 0; i
< vdecl
->declarationWNumElements
; i
++) {
528 /* Ignore tesselated streams and the termination entry(position0, stream 255, unused) */
529 if(vdecl
->pDeclarationWine
[i
].Stream
>= MAX_STREAMS
||
530 vdecl
->pDeclarationWine
[i
].Type
== WINED3DDECLTYPE_UNUSED
) continue;
532 for(j
= 0; j
< MAX_ATTRIBS
; j
++) {
533 if(!This
->baseShader
.reg_maps
.attributes
[j
]) continue;
535 usage_token
= This
->semantics_in
[j
].usage
;
536 usage
= (usage_token
& WINED3DSP_DCL_USAGE_MASK
) >> WINED3DSP_DCL_USAGE_SHIFT
;
537 usage_idx
= (usage_token
& WINED3DSP_DCL_USAGEINDEX_MASK
) >> WINED3DSP_DCL_USAGEINDEX_SHIFT
;
539 if(vdecl
->pDeclarationWine
[i
].Usage
!= usage
||
540 vdecl
->pDeclarationWine
[i
].UsageIndex
!= usage_idx
) {
545 for(k
= 0; k
< This
->num_swizzled_attribs
; k
++) {
546 if(This
->swizzled_attribs
[k
].usage
== usage
&&
547 This
->swizzled_attribs
[k
].idx
== usage_idx
) {
551 if(!found
&& vdecl
->pDeclarationWine
[i
].Type
== WINED3DDECLTYPE_D3DCOLOR
) {
552 TRACE("Attribute %s%d is D3DCOLOR now but wasn't before\n",
553 debug_d3ddeclusage(usage
), usage_idx
);
556 if( found
&& vdecl
->pDeclarationWine
[i
].Type
!= WINED3DDECLTYPE_D3DCOLOR
) {
557 TRACE("Attribute %s%d was D3DCOLOR before but is not any more\n",
558 debug_d3ddeclusage(usage
), usage_idx
);
566 HRESULT
IWineD3DVertexShaderImpl_CompileShader(IWineD3DVertexShader
*iface
) {
567 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
568 IWineD3DVertexDeclarationImpl
*vdecl
;
569 CONST DWORD
*function
= This
->baseShader
.function
;
570 IWineD3DDeviceImpl
*deviceImpl
= (IWineD3DDeviceImpl
*) This
->baseShader
.device
;
572 TRACE("(%p) : function %p\n", iface
, function
);
574 /* We're already compiled. */
575 if (This
->baseShader
.is_compiled
) {
576 vdecl
= (IWineD3DVertexDeclarationImpl
*) deviceImpl
->stateBlock
->vertexDecl
;
578 if(This
->num_swizzled_attribs
!= vdecl
->num_swizzled_attribs
||
579 memcmp(This
->swizzled_attribs
, vdecl
->swizzled_attribs
, sizeof(vdecl
->swizzled_attribs
[0]) * This
->num_swizzled_attribs
) != 0) {
581 /* The swizzled attributes differ between shader and declaration. This doesn't necessarily mean
582 * we have to recompile, but we have to take a deeper look at see if the attribs that differ
583 * are declared in the decl and used in the shader
585 if(swizzled_attribs_differ(This
, vdecl
)) {
586 WARN("Recompiling vertex shader %p due to D3DCOLOR input changes\n", This
);
589 WARN("Swizzled attribute validation required an expensive comparison\n");
595 if(This
->recompile_count
< 50) {
596 This
->recompile_count
++;
598 FIXME("Vertexshader %p recompiled more than 50 times\n", This
);
601 deviceImpl
->shader_backend
->shader_destroy((IWineD3DBaseShader
*) iface
);
604 /* We don't need to compile */
606 This
->baseShader
.is_compiled
= TRUE
;
610 /* Generate the HW shader */
611 TRACE("(%p) : Generating hardware program\n", This
);
612 IWineD3DVertexShaderImpl_GenerateShader(iface
, &This
->baseShader
.reg_maps
, function
);
614 This
->baseShader
.is_compiled
= TRUE
;
619 const IWineD3DVertexShaderVtbl IWineD3DVertexShader_Vtbl
=
621 /*** IUnknown methods ***/
622 IWineD3DVertexShaderImpl_QueryInterface
,
623 IWineD3DVertexShaderImpl_AddRef
,
624 IWineD3DVertexShaderImpl_Release
,
625 /*** IWineD3DBase methods ***/
626 IWineD3DVertexShaderImpl_GetParent
,
627 /*** IWineD3DBaseShader methods ***/
628 IWineD3DVertexShaderImpl_SetFunction
,
629 /*** IWineD3DVertexShader methods ***/
630 IWineD3DVertexShaderImpl_GetDevice
,
631 IWineD3DVertexShaderImpl_GetFunction
,
632 IWineD3DVertexShaderImpl_FakeSemantics
,
633 IWIneD3DVertexShaderImpl_SetLocalConstantsF