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 static void vshader_set_limits(IWineD3DVertexShaderImpl
*This
)
39 DWORD shader_version
= WINED3D_SHADER_VERSION(This
->baseShader
.reg_maps
.shader_version
.major
,
40 This
->baseShader
.reg_maps
.shader_version
.minor
);
42 This
->baseShader
.limits
.texcoord
= 0;
43 This
->baseShader
.limits
.attributes
= 16;
44 This
->baseShader
.limits
.packed_input
= 0;
46 switch (shader_version
)
48 case WINED3D_SHADER_VERSION(1,0):
49 case WINED3D_SHADER_VERSION(1,1):
50 This
->baseShader
.limits
.temporary
= 12;
51 This
->baseShader
.limits
.constant_bool
= 0;
52 This
->baseShader
.limits
.constant_int
= 0;
53 This
->baseShader
.limits
.address
= 1;
54 This
->baseShader
.limits
.packed_output
= 0;
55 This
->baseShader
.limits
.sampler
= 0;
56 This
->baseShader
.limits
.label
= 0;
57 /* TODO: vs_1_1 has a minimum of 96 constants. What happens if a vs_1_1 shader is used
58 * on a vs_3_0 capable card that has 256 constants? */
59 This
->baseShader
.limits
.constant_float
= min(256, GL_LIMITS(vshader_constantsF
));
62 case WINED3D_SHADER_VERSION(2,0):
63 case WINED3D_SHADER_VERSION(2,1):
64 This
->baseShader
.limits
.temporary
= 12;
65 This
->baseShader
.limits
.constant_bool
= 16;
66 This
->baseShader
.limits
.constant_int
= 16;
67 This
->baseShader
.limits
.address
= 1;
68 This
->baseShader
.limits
.packed_output
= 0;
69 This
->baseShader
.limits
.sampler
= 0;
70 This
->baseShader
.limits
.label
= 16;
71 This
->baseShader
.limits
.constant_float
= min(256, GL_LIMITS(vshader_constantsF
));
74 case WINED3D_SHADER_VERSION(4,0):
75 FIXME("Using 3.0 limits for 4.0 shader\n");
78 case WINED3D_SHADER_VERSION(3,0):
79 This
->baseShader
.limits
.temporary
= 32;
80 This
->baseShader
.limits
.constant_bool
= 32;
81 This
->baseShader
.limits
.constant_int
= 32;
82 This
->baseShader
.limits
.address
= 1;
83 This
->baseShader
.limits
.packed_output
= 12;
84 This
->baseShader
.limits
.sampler
= 4;
85 This
->baseShader
.limits
.label
= 16; /* FIXME: 2048 */
86 /* DX10 cards on Windows advertise a d3d9 constant limit of 256 even though they are capable
87 * of supporting much more(GL drivers advertise 1024). d3d9.dll and d3d8.dll clamp the
88 * wined3d-advertised maximum. Clamp the constant limit for <= 3.0 shaders to 256.s
89 * use constant buffers */
90 This
->baseShader
.limits
.constant_float
= min(256, GL_LIMITS(vshader_constantsF
));
94 This
->baseShader
.limits
.temporary
= 12;
95 This
->baseShader
.limits
.constant_bool
= 16;
96 This
->baseShader
.limits
.constant_int
= 16;
97 This
->baseShader
.limits
.address
= 1;
98 This
->baseShader
.limits
.packed_output
= 0;
99 This
->baseShader
.limits
.sampler
= 0;
100 This
->baseShader
.limits
.label
= 16;
101 This
->baseShader
.limits
.constant_float
= min(256, GL_LIMITS(vshader_constantsF
));
102 FIXME("Unrecognized vertex shader version %u.%u\n",
103 This
->baseShader
.reg_maps
.shader_version
.major
,
104 This
->baseShader
.reg_maps
.shader_version
.minor
);
108 static BOOL
match_usage(BYTE usage1
, BYTE usage_idx1
, BYTE usage2
, BYTE usage_idx2
) {
109 if (usage_idx1
!= usage_idx2
) return FALSE
;
110 if (usage1
== usage2
) return TRUE
;
111 if (usage1
== WINED3DDECLUSAGE_POSITION
&& usage2
== WINED3DDECLUSAGE_POSITIONT
) return TRUE
;
112 if (usage2
== WINED3DDECLUSAGE_POSITION
&& usage1
== WINED3DDECLUSAGE_POSITIONT
) return TRUE
;
117 BOOL
vshader_get_input(IWineD3DVertexShader
* iface
, BYTE usage_req
, BYTE usage_idx_req
, unsigned int *regnum
)
119 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
120 WORD map
= This
->baseShader
.reg_maps
.input_registers
;
123 for (i
= 0; map
; map
>>= 1, ++i
)
125 if (!(map
& 1)) continue;
127 if (match_usage(This
->attributes
[i
].usage
,
128 This
->attributes
[i
].usage_idx
, usage_req
, usage_idx_req
))
137 /* *******************************************
138 IWineD3DVertexShader IUnknown parts follow
139 ******************************************* */
140 static HRESULT WINAPI
IWineD3DVertexShaderImpl_QueryInterface(IWineD3DVertexShader
*iface
, REFIID riid
, LPVOID
*ppobj
) {
141 TRACE("iface %p, riid %s, ppobj %p\n", iface
, debugstr_guid(riid
), ppobj
);
143 if (IsEqualGUID(riid
, &IID_IWineD3DVertexShader
)
144 || IsEqualGUID(riid
, &IID_IWineD3DBaseShader
)
145 || IsEqualGUID(riid
, &IID_IWineD3DBase
)
146 || IsEqualGUID(riid
, &IID_IUnknown
))
148 IUnknown_AddRef(iface
);
153 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid
));
156 return E_NOINTERFACE
;
159 static ULONG WINAPI
IWineD3DVertexShaderImpl_AddRef(IWineD3DVertexShader
*iface
) {
160 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
161 ULONG refcount
= InterlockedIncrement(&This
->baseShader
.ref
);
163 TRACE("%p increasing refcount to %u\n", This
, refcount
);
168 static ULONG WINAPI
IWineD3DVertexShaderImpl_Release(IWineD3DVertexShader
*iface
) {
169 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
170 ULONG refcount
= InterlockedDecrement(&This
->baseShader
.ref
);
172 TRACE("%p decreasing refcount to %u\n", This
, refcount
);
176 shader_cleanup((IWineD3DBaseShader
*)iface
);
177 HeapFree(GetProcessHeap(), 0, This
);
183 /* *******************************************
184 IWineD3DVertexShader IWineD3DVertexShader parts follow
185 ******************************************* */
187 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetParent(IWineD3DVertexShader
*iface
, IUnknown
** parent
){
188 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
190 *parent
= This
->parent
;
191 IUnknown_AddRef(*parent
);
192 TRACE("(%p) : returning %p\n", This
, *parent
);
196 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetDevice(IWineD3DVertexShader
* iface
, IWineD3DDevice
**pDevice
){
197 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
198 IWineD3DDevice_AddRef(This
->baseShader
.device
);
199 *pDevice
= This
->baseShader
.device
;
200 TRACE("(%p) returning %p\n", This
, *pDevice
);
204 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetFunction(IWineD3DVertexShader
* impl
, VOID
* pData
, UINT
* pSizeOfData
) {
205 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)impl
;
206 TRACE("(%p) : pData(%p), pSizeOfData(%p)\n", This
, pData
, pSizeOfData
);
209 *pSizeOfData
= This
->baseShader
.functionLength
;
212 if (*pSizeOfData
< This
->baseShader
.functionLength
) {
213 /* MSDN claims (for d3d8 at least) that if *pSizeOfData is smaller
214 * than the required size we should write the required size and
215 * return D3DERR_MOREDATA. That's not actually true. */
216 return WINED3DERR_INVALIDCALL
;
219 TRACE("(%p) : GetFunction copying to %p\n", This
, pData
);
220 memcpy(pData
, This
->baseShader
.function
, This
->baseShader
.functionLength
);
225 /* Note that for vertex shaders CompileShader isn't called until the
226 * shader is first used. The reason for this is that we need the vertex
227 * declaration the shader will be used with in order to determine if
228 * the data in a register is of type D3DCOLOR, and needs swizzling. */
229 static HRESULT WINAPI
IWineD3DVertexShaderImpl_SetFunction(IWineD3DVertexShader
*iface
,
230 const DWORD
*pFunction
, const struct wined3d_shader_signature
*output_signature
)
232 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
233 IWineD3DDeviceImpl
*deviceImpl
= (IWineD3DDeviceImpl
*) This
->baseShader
.device
;
234 const struct wined3d_shader_frontend
*fe
;
237 shader_reg_maps
*reg_maps
= &This
->baseShader
.reg_maps
;
239 TRACE("(%p) : pFunction %p\n", iface
, pFunction
);
241 fe
= shader_select_frontend(*pFunction
);
244 FIXME("Unable to find frontend for shader.\n");
245 return WINED3DERR_INVALIDCALL
;
247 This
->baseShader
.frontend
= fe
;
248 This
->baseShader
.frontend_data
= fe
->shader_init(pFunction
, output_signature
);
249 if (!This
->baseShader
.frontend_data
)
251 FIXME("Failed to initialize frontend.\n");
252 return WINED3DERR_INVALIDCALL
;
255 /* First pass: trace shader */
256 if (TRACE_ON(d3d_shader
)) shader_trace_init(fe
, This
->baseShader
.frontend_data
, pFunction
);
258 /* Initialize immediate constant lists */
259 list_init(&This
->baseShader
.constantsF
);
260 list_init(&This
->baseShader
.constantsB
);
261 list_init(&This
->baseShader
.constantsI
);
263 /* Second pass: figure out registers used, semantics, etc.. */
264 This
->min_rel_offset
= GL_LIMITS(vshader_constantsF
);
265 This
->max_rel_offset
= 0;
266 hr
= shader_get_registers_used((IWineD3DBaseShader
*) This
, fe
,
267 reg_maps
, This
->attributes
, NULL
, This
->output_signature
,
268 pFunction
, GL_LIMITS(vshader_constantsF
));
269 if (hr
!= WINED3D_OK
) return hr
;
271 if (output_signature
)
273 for (i
= 0; i
< output_signature
->element_count
; ++i
)
275 struct wined3d_shader_signature_element
*e
= &output_signature
->elements
[i
];
276 reg_maps
->output_registers
|= 1 << e
->register_idx
;
277 This
->output_signature
[e
->register_idx
] = *e
;
281 vshader_set_limits(This
);
283 if (deviceImpl
->vs_selected_mode
== SHADER_ARB
284 && ((GLINFO_LOCATION
).quirks
& WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT
)
285 && This
->min_rel_offset
<= This
->max_rel_offset
)
287 if(This
->max_rel_offset
- This
->min_rel_offset
> 127) {
288 FIXME("The difference between the minimum and maximum relative offset is > 127\n");
289 FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
290 FIXME("Min: %d, Max: %d\n", This
->min_rel_offset
, This
->max_rel_offset
);
291 } else if(This
->max_rel_offset
- This
->min_rel_offset
> 63) {
292 This
->rel_offset
= This
->min_rel_offset
+ 63;
293 } else if(This
->max_rel_offset
> 63) {
294 This
->rel_offset
= This
->min_rel_offset
;
296 This
->rel_offset
= 0;
299 This
->baseShader
.load_local_constsF
= This
->baseShader
.reg_maps
.usesrelconstF
&& !list_empty(&This
->baseShader
.constantsF
);
301 /* copy the function ... because it will certainly be released by application */
302 This
->baseShader
.function
= HeapAlloc(GetProcessHeap(), 0, This
->baseShader
.functionLength
);
303 if (!This
->baseShader
.function
) return E_OUTOFMEMORY
;
304 memcpy(This
->baseShader
.function
, pFunction
, This
->baseShader
.functionLength
);
309 /* Set local constants for d3d8 shaders */
310 static HRESULT WINAPI
IWIneD3DVertexShaderImpl_SetLocalConstantsF(IWineD3DVertexShader
*iface
,
311 UINT start_idx
, const float *src_data
, UINT count
) {
312 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
315 TRACE("(%p) : start_idx %u, src_data %p, count %u\n", This
, start_idx
, src_data
, count
);
317 end_idx
= start_idx
+ count
;
318 if (end_idx
> GL_LIMITS(vshader_constantsF
)) {
319 WARN("end_idx %u > float constants limit %u\n", end_idx
, GL_LIMITS(vshader_constantsF
));
320 end_idx
= GL_LIMITS(vshader_constantsF
);
323 for (i
= start_idx
; i
< end_idx
; ++i
) {
324 local_constant
* lconst
= HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant
));
325 if (!lconst
) return E_OUTOFMEMORY
;
328 memcpy(lconst
->value
, src_data
+ (i
- start_idx
) * 4 /* 4 components */, 4 * sizeof(float));
329 list_add_head(&This
->baseShader
.constantsF
, &lconst
->entry
);
335 const IWineD3DVertexShaderVtbl IWineD3DVertexShader_Vtbl
=
337 /*** IUnknown methods ***/
338 IWineD3DVertexShaderImpl_QueryInterface
,
339 IWineD3DVertexShaderImpl_AddRef
,
340 IWineD3DVertexShaderImpl_Release
,
341 /*** IWineD3DBase methods ***/
342 IWineD3DVertexShaderImpl_GetParent
,
343 /*** IWineD3DBaseShader methods ***/
344 IWineD3DVertexShaderImpl_SetFunction
,
345 /*** IWineD3DVertexShader methods ***/
346 IWineD3DVertexShaderImpl_GetDevice
,
347 IWineD3DVertexShaderImpl_GetFunction
,
348 IWIneD3DVertexShaderImpl_SetLocalConstantsF
351 void find_vs_compile_args(IWineD3DVertexShaderImpl
*shader
, IWineD3DStateBlockImpl
*stateblock
, struct vs_compile_args
*args
) {
352 args
->fog_src
= stateblock
->renderState
[WINED3DRS_FOGTABLEMODE
] == WINED3DFOG_NONE
? VS_FOG_COORD
: VS_FOG_Z
;
353 args
->swizzle_map
= ((IWineD3DDeviceImpl
*)shader
->baseShader
.device
)->strided_streams
.swizzle_map
;