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_GetFunction(IWineD3DVertexShader
* impl
, VOID
* pData
, UINT
* pSizeOfData
) {
198 IWineD3DVertexShaderImpl
*This
= (IWineD3DVertexShaderImpl
*)impl
;
199 TRACE("(%p) : pData(%p), pSizeOfData(%p)\n", This
, pData
, pSizeOfData
);
202 *pSizeOfData
= This
->baseShader
.functionLength
;
205 if (*pSizeOfData
< This
->baseShader
.functionLength
) {
206 /* MSDN claims (for d3d8 at least) that if *pSizeOfData is smaller
207 * than the required size we should write the required size and
208 * return D3DERR_MOREDATA. That's not actually true. */
209 return WINED3DERR_INVALIDCALL
;
212 TRACE("(%p) : GetFunction copying to %p\n", This
, pData
);
213 memcpy(pData
, This
->baseShader
.function
, This
->baseShader
.functionLength
);
218 static HRESULT
vertexshader_set_function(IWineD3DVertexShaderImpl
*shader
,
219 const DWORD
*byte_code
, const struct wined3d_shader_signature
*output_signature
)
221 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*)shader
->baseShader
.device
;
222 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
223 const struct wined3d_shader_frontend
*fe
;
226 shader_reg_maps
*reg_maps
= &shader
->baseShader
.reg_maps
;
228 TRACE("shader %p, byte_code %p, output_signature %p.\n", shader
, byte_code
, output_signature
);
230 fe
= shader_select_frontend(*byte_code
);
233 FIXME("Unable to find frontend for shader.\n");
234 return WINED3DERR_INVALIDCALL
;
236 shader
->baseShader
.frontend
= fe
;
237 shader
->baseShader
.frontend_data
= fe
->shader_init(byte_code
, output_signature
);
238 if (!shader
->baseShader
.frontend_data
)
240 FIXME("Failed to initialize frontend.\n");
241 return WINED3DERR_INVALIDCALL
;
244 /* First pass: trace shader */
245 if (TRACE_ON(d3d_shader
)) shader_trace_init(fe
, shader
->baseShader
.frontend_data
, byte_code
);
247 /* Initialize immediate constant lists */
248 list_init(&shader
->baseShader
.constantsF
);
249 list_init(&shader
->baseShader
.constantsB
);
250 list_init(&shader
->baseShader
.constantsI
);
252 /* Second pass: figure out registers used, semantics, etc.. */
253 shader
->min_rel_offset
= device
->d3d_vshader_constantF
;
254 shader
->max_rel_offset
= 0;
255 hr
= shader_get_registers_used((IWineD3DBaseShader
*)shader
, fe
,
256 reg_maps
, shader
->attributes
, NULL
, shader
->output_signature
,
257 byte_code
, device
->d3d_vshader_constantF
);
258 if (hr
!= WINED3D_OK
) return hr
;
260 if (output_signature
)
262 for (i
= 0; i
< output_signature
->element_count
; ++i
)
264 struct wined3d_shader_signature_element
*e
= &output_signature
->elements
[i
];
265 reg_maps
->output_registers
|= 1 << e
->register_idx
;
266 shader
->output_signature
[e
->register_idx
] = *e
;
270 vshader_set_limits(shader
);
272 if (device
->vs_selected_mode
== SHADER_ARB
273 && (gl_info
->quirks
& WINED3D_QUIRK_ARB_VS_OFFSET_LIMIT
)
274 && shader
->min_rel_offset
<= shader
->max_rel_offset
)
276 if (shader
->max_rel_offset
- shader
->min_rel_offset
> 127)
278 FIXME("The difference between the minimum and maximum relative offset is > 127\n");
279 FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
280 FIXME("Min: %d, Max: %d\n", shader
->min_rel_offset
, shader
->max_rel_offset
);
282 else if (shader
->max_rel_offset
- shader
->min_rel_offset
> 63)
284 shader
->rel_offset
= shader
->min_rel_offset
+ 63;
286 else if (shader
->max_rel_offset
> 63)
288 shader
->rel_offset
= shader
->min_rel_offset
;
292 shader
->rel_offset
= 0;
295 shader
->baseShader
.load_local_constsF
= shader
->baseShader
.reg_maps
.usesrelconstF
296 && !list_empty(&shader
->baseShader
.constantsF
);
298 /* copy the function ... because it will certainly be released by application */
299 shader
->baseShader
.function
= HeapAlloc(GetProcessHeap(), 0, shader
->baseShader
.functionLength
);
300 if (!shader
->baseShader
.function
) return E_OUTOFMEMORY
;
301 memcpy(shader
->baseShader
.function
, byte_code
, shader
->baseShader
.functionLength
);
306 /* Set local constants for d3d8 shaders */
307 static HRESULT WINAPI
IWIneD3DVertexShaderImpl_SetLocalConstantsF(IWineD3DVertexShader
*iface
,
308 UINT start_idx
, const float *src_data
, UINT count
) {
309 IWineD3DVertexShaderImpl
*This
=(IWineD3DVertexShaderImpl
*)iface
;
310 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*) This
->baseShader
.device
;
313 TRACE("(%p) : start_idx %u, src_data %p, count %u\n", This
, start_idx
, src_data
, count
);
315 end_idx
= start_idx
+ count
;
316 if (end_idx
> device
->d3d_vshader_constantF
)
318 WARN("end_idx %u > float constants limit %u\n", end_idx
, device
->d3d_vshader_constantF
);
319 end_idx
= device
->d3d_vshader_constantF
;
322 for (i
= start_idx
; i
< end_idx
; ++i
) {
323 local_constant
* lconst
= HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant
));
324 if (!lconst
) return E_OUTOFMEMORY
;
327 memcpy(lconst
->value
, src_data
+ (i
- start_idx
) * 4 /* 4 components */, 4 * sizeof(float));
328 list_add_head(&This
->baseShader
.constantsF
, &lconst
->entry
);
334 static const IWineD3DVertexShaderVtbl IWineD3DVertexShader_Vtbl
=
336 /*** IUnknown methods ***/
337 IWineD3DVertexShaderImpl_QueryInterface
,
338 IWineD3DVertexShaderImpl_AddRef
,
339 IWineD3DVertexShaderImpl_Release
,
340 /*** IWineD3DBase methods ***/
341 IWineD3DVertexShaderImpl_GetParent
,
342 /*** IWineD3DBaseShader methods ***/
343 IWineD3DVertexShaderImpl_GetFunction
,
344 /*** IWineD3DVertexShader methods ***/
345 IWIneD3DVertexShaderImpl_SetLocalConstantsF
348 void find_vs_compile_args(IWineD3DVertexShaderImpl
*shader
, IWineD3DStateBlockImpl
*stateblock
, struct vs_compile_args
*args
) {
349 args
->fog_src
= stateblock
->renderState
[WINED3DRS_FOGTABLEMODE
] == WINED3DFOG_NONE
? VS_FOG_COORD
: VS_FOG_Z
;
350 args
->clip_enabled
= stateblock
->renderState
[WINED3DRS_CLIPPING
] && stateblock
->renderState
[WINED3DRS_CLIPPLANEENABLE
];
351 args
->swizzle_map
= ((IWineD3DDeviceImpl
*)shader
->baseShader
.device
)->strided_streams
.swizzle_map
;
354 HRESULT
vertexshader_init(IWineD3DVertexShaderImpl
*shader
, IWineD3DDeviceImpl
*device
,
355 const DWORD
*byte_code
, const struct wined3d_shader_signature
*output_signature
,
356 IUnknown
*parent
, const struct wined3d_parent_ops
*parent_ops
)
360 if (!byte_code
) return WINED3DERR_INVALIDCALL
;
362 shader
->lpVtbl
= &IWineD3DVertexShader_Vtbl
;
363 shader_init(&shader
->baseShader
, device
, parent
, parent_ops
);
365 hr
= vertexshader_set_function(shader
, byte_code
, output_signature
);
368 WARN("Failed to set function, hr %#x.\n", hr
);
369 shader_cleanup((IWineD3DBaseShader
*)shader
);