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
10 * Copyright 2009 Henri Verbeet for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wined3d_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader
);
36 static void vshader_set_limits(IWineD3DVertexShaderImpl
*This
)
38 DWORD shader_version
= WINED3D_SHADER_VERSION(This
->baseShader
.reg_maps
.shader_version
.major
,
39 This
->baseShader
.reg_maps
.shader_version
.minor
);
40 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*) This
->baseShader
.device
;
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, device
->d3d_vshader_constantF
);
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, device
->d3d_vshader_constantF
);
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, device
->d3d_vshader_constantF
);
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, device
->d3d_vshader_constantF
);
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 This
->baseShader
.parent_ops
->wined3d_object_destroyed(This
->baseShader
.parent
);
178 HeapFree(GetProcessHeap(), 0, This
);
184 /* *******************************************
185 IWineD3DVertexShader IWineD3DVertexShader parts follow
186 ******************************************* */
188 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetParent(IWineD3DVertexShader
*iface
, IUnknown
** parent
){
189 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
191 *parent
= This
->baseShader
.parent
;
192 IUnknown_AddRef(*parent
);
193 TRACE("(%p) : returning %p\n", This
, *parent
);
197 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetDevice(IWineD3DVertexShader
* iface
, IWineD3DDevice
**pDevice
){
198 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)iface
;
199 IWineD3DDevice_AddRef(This
->baseShader
.device
);
200 *pDevice
= This
->baseShader
.device
;
201 TRACE("(%p) returning %p\n", This
, *pDevice
);
205 static HRESULT WINAPI
IWineD3DVertexShaderImpl_GetFunction(IWineD3DVertexShader
* impl
, VOID
* pData
, UINT
* pSizeOfData
) {
206 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)impl
;
207 TRACE("(%p) : pData(%p), pSizeOfData(%p)\n", This
, pData
, pSizeOfData
);
210 *pSizeOfData
= This
->baseShader
.functionLength
;
213 if (*pSizeOfData
< This
->baseShader
.functionLength
) {
214 /* MSDN claims (for d3d8 at least) that if *pSizeOfData is smaller
215 * than the required size we should write the required size and
216 * return D3DERR_MOREDATA. That's not actually true. */
217 return WINED3DERR_INVALIDCALL
;
220 TRACE("(%p) : GetFunction copying to %p\n", This
, pData
);
221 memcpy(pData
, This
->baseShader
.function
, This
->baseShader
.functionLength
);
226 static HRESULT
vertexshader_set_function(IWineD3DVertexShaderImpl
*shader
,
227 const DWORD
*byte_code
, const struct wined3d_shader_signature
*output_signature
)
229 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*)shader
->baseShader
.device
;
230 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
231 const struct wined3d_shader_frontend
*fe
;
234 shader_reg_maps
*reg_maps
= &shader
->baseShader
.reg_maps
;
236 TRACE("shader %p, byte_code %p, output_signature %p.\n", shader
, byte_code
, output_signature
);
238 fe
= shader_select_frontend(*byte_code
);
241 FIXME("Unable to find frontend for shader.\n");
242 return WINED3DERR_INVALIDCALL
;
244 shader
->baseShader
.frontend
= fe
;
245 shader
->baseShader
.frontend_data
= fe
->shader_init(byte_code
, output_signature
);
246 if (!shader
->baseShader
.frontend_data
)
248 FIXME("Failed to initialize frontend.\n");
249 return WINED3DERR_INVALIDCALL
;
252 /* First pass: trace shader */
253 if (TRACE_ON(d3d_shader
)) shader_trace_init(fe
, shader
->baseShader
.frontend_data
, byte_code
);
255 /* Initialize immediate constant lists */
256 list_init(&shader
->baseShader
.constantsF
);
257 list_init(&shader
->baseShader
.constantsB
);
258 list_init(&shader
->baseShader
.constantsI
);
260 /* Second pass: figure out registers used, semantics, etc.. */
261 shader
->min_rel_offset
= device
->d3d_vshader_constantF
;
262 shader
->max_rel_offset
= 0;
263 hr
= shader_get_registers_used((IWineD3DBaseShader
*)shader
, fe
,
264 reg_maps
, shader
->attributes
, NULL
, shader
->output_signature
,
265 byte_code
, device
->d3d_vshader_constantF
);
266 if (hr
!= WINED3D_OK
) return hr
;
268 if (output_signature
)
270 for (i
= 0; i
< output_signature
->element_count
; ++i
)
272 struct wined3d_shader_signature_element
*e
= &output_signature
->elements
[i
];
273 reg_maps
->output_registers
|= 1 << e
->register_idx
;
274 shader
->output_signature
[e
->register_idx
] = *e
;
278 vshader_set_limits(shader
);
280 if (device
->vs_selected_mode
== SHADER_ARB
281 && (gl_info
->quirks
& WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT
)
282 && shader
->min_rel_offset
<= shader
->max_rel_offset
)
284 if (shader
->max_rel_offset
- shader
->min_rel_offset
> 127)
286 FIXME("The difference between the minimum and maximum relative offset is > 127\n");
287 FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
288 FIXME("Min: %d, Max: %d\n", shader
->min_rel_offset
, shader
->max_rel_offset
);
290 else if (shader
->max_rel_offset
- shader
->min_rel_offset
> 63)
292 shader
->rel_offset
= shader
->min_rel_offset
+ 63;
294 else if (shader
->max_rel_offset
> 63)
296 shader
->rel_offset
= shader
->min_rel_offset
;
300 shader
->rel_offset
= 0;
303 shader
->baseShader
.load_local_constsF
= shader
->baseShader
.reg_maps
.usesrelconstF
304 && !list_empty(&shader
->baseShader
.constantsF
);
306 /* copy the function ... because it will certainly be released by application */
307 shader
->baseShader
.function
= HeapAlloc(GetProcessHeap(), 0, shader
->baseShader
.functionLength
);
308 if (!shader
->baseShader
.function
) return E_OUTOFMEMORY
;
309 memcpy(shader
->baseShader
.function
, byte_code
, shader
->baseShader
.functionLength
);
314 /* Set local constants for d3d8 shaders */
315 static HRESULT WINAPI
IWIneD3DVertexShaderImpl_SetLocalConstantsF(IWineD3DVertexShader
*iface
,
316 UINT start_idx
, const float *src_data
, UINT count
) {
317 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
318 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*) This
->baseShader
.device
;
321 TRACE("(%p) : start_idx %u, src_data %p, count %u\n", This
, start_idx
, src_data
, count
);
323 end_idx
= start_idx
+ count
;
324 if (end_idx
> device
->d3d_vshader_constantF
)
326 WARN("end_idx %u > float constants limit %u\n", end_idx
, device
->d3d_vshader_constantF
);
327 end_idx
= device
->d3d_vshader_constantF
;
330 for (i
= start_idx
; i
< end_idx
; ++i
) {
331 local_constant
* lconst
= HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant
));
332 if (!lconst
) return E_OUTOFMEMORY
;
335 memcpy(lconst
->value
, src_data
+ (i
- start_idx
) * 4 /* 4 components */, 4 * sizeof(float));
336 list_add_head(&This
->baseShader
.constantsF
, &lconst
->entry
);
342 static const IWineD3DVertexShaderVtbl IWineD3DVertexShader_Vtbl
=
344 /*** IUnknown methods ***/
345 IWineD3DVertexShaderImpl_QueryInterface
,
346 IWineD3DVertexShaderImpl_AddRef
,
347 IWineD3DVertexShaderImpl_Release
,
348 /*** IWineD3DBase methods ***/
349 IWineD3DVertexShaderImpl_GetParent
,
350 /*** IWineD3DBaseShader methods ***/
351 IWineD3DVertexShaderImpl_GetDevice
,
352 IWineD3DVertexShaderImpl_GetFunction
,
353 /*** IWineD3DVertexShader methods ***/
354 IWIneD3DVertexShaderImpl_SetLocalConstantsF
357 void find_vs_compile_args(IWineD3DVertexShaderImpl
*shader
, IWineD3DStateBlockImpl
*stateblock
, struct vs_compile_args
*args
) {
358 args
->fog_src
= stateblock
->renderState
[WINED3DRS_FOGTABLEMODE
] == WINED3DFOG_NONE
? VS_FOG_COORD
: VS_FOG_Z
;
359 args
->clip_enabled
= stateblock
->renderState
[WINED3DRS_CLIPPING
] && stateblock
->renderState
[WINED3DRS_CLIPPLANEENABLE
];
360 args
->swizzle_map
= ((IWineD3DDeviceImpl
*)shader
->baseShader
.device
)->strided_streams
.swizzle_map
;
363 HRESULT
vertexshader_init(IWineD3DVertexShaderImpl
*shader
, IWineD3DDeviceImpl
*device
,
364 const DWORD
*byte_code
, const struct wined3d_shader_signature
*output_signature
,
365 IUnknown
*parent
, const struct wined3d_parent_ops
*parent_ops
)
369 if (!byte_code
) return WINED3DERR_INVALIDCALL
;
371 shader
->lpVtbl
= &IWineD3DVertexShader_Vtbl
;
372 shader_init(&shader
->baseShader
, device
, parent
, parent_ops
);
374 hr
= vertexshader_set_function(shader
, byte_code
, output_signature
);
377 WARN("Failed to set function, hr %#x.\n", hr
);
378 shader_cleanup((IWineD3DBaseShader
*)shader
);