push e7552b73e75828caa36d732caa646c0c70d8de9d
[wine/hacks.git] / dlls / wined3d / vertexshader.c
blobfab0dcaa8f79f919a2fbf38e88e04faf537c50aa
1 /*
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
26 #include "config.h"
28 #include <math.h>
29 #include <stdio.h>
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 CONST SHADER_OPCODE IWineD3DVertexShaderImpl_shader_ins[] = {
41 /* This table is not order or position dependent. */
43 /* Arithmetic */
44 {WINED3DSIO_NOP, "nop", 0, 0, WINED3DSIH_NOP, 0, 0 },
45 {WINED3DSIO_MOV, "mov", 1, 2, WINED3DSIH_MOV, 0, 0 },
46 {WINED3DSIO_MOVA, "mova", 1, 2, WINED3DSIH_MOVA, WINED3DVS_VERSION(2,0), -1 },
47 {WINED3DSIO_ADD, "add", 1, 3, WINED3DSIH_ADD, 0, 0 },
48 {WINED3DSIO_SUB, "sub", 1, 3, WINED3DSIH_SUB, 0, 0 },
49 {WINED3DSIO_MAD, "mad", 1, 4, WINED3DSIH_MAD, 0, 0 },
50 {WINED3DSIO_MUL, "mul", 1, 3, WINED3DSIH_MUL, 0, 0 },
51 {WINED3DSIO_RCP, "rcp", 1, 2, WINED3DSIH_RCP, 0, 0 },
52 {WINED3DSIO_RSQ, "rsq", 1, 2, WINED3DSIH_RSQ, 0, 0 },
53 {WINED3DSIO_DP3, "dp3", 1, 3, WINED3DSIH_DP3, 0, 0 },
54 {WINED3DSIO_DP4, "dp4", 1, 3, WINED3DSIH_DP4, 0, 0 },
55 {WINED3DSIO_MIN, "min", 1, 3, WINED3DSIH_MIN, 0, 0 },
56 {WINED3DSIO_MAX, "max", 1, 3, WINED3DSIH_MAX, 0, 0 },
57 {WINED3DSIO_SLT, "slt", 1, 3, WINED3DSIH_SLT, 0, 0 },
58 {WINED3DSIO_SGE, "sge", 1, 3, WINED3DSIH_SGE, 0, 0 },
59 {WINED3DSIO_ABS, "abs", 1, 2, WINED3DSIH_ABS, 0, 0 },
60 {WINED3DSIO_EXP, "exp", 1, 2, WINED3DSIH_EXP, 0, 0 },
61 {WINED3DSIO_LOG, "log", 1, 2, WINED3DSIH_LOG, 0, 0 },
62 {WINED3DSIO_EXPP, "expp", 1, 2, WINED3DSIH_EXPP, 0, 0 },
63 {WINED3DSIO_LOGP, "logp", 1, 2, WINED3DSIH_LOGP, 0, 0 },
64 {WINED3DSIO_LIT, "lit", 1, 2, WINED3DSIH_LIT, 0, 0 },
65 {WINED3DSIO_DST, "dst", 1, 3, WINED3DSIH_DST, 0, 0 },
66 {WINED3DSIO_LRP, "lrp", 1, 4, WINED3DSIH_LRP, 0, 0 },
67 {WINED3DSIO_FRC, "frc", 1, 2, WINED3DSIH_FRC, 0, 0 },
68 {WINED3DSIO_POW, "pow", 1, 3, WINED3DSIH_POW, 0, 0 },
69 {WINED3DSIO_CRS, "crs", 1, 3, WINED3DSIH_CRS, 0, 0 },
70 /* TODO: sng can possibly be performed as
71 RCP tmp, vec
72 MUL out, tmp, vec*/
73 {WINED3DSIO_SGN, "sgn", 1, 2, WINED3DSIH_SGN, 0, 0 },
74 {WINED3DSIO_NRM, "nrm", 1, 2, WINED3DSIH_NRM, 0, 0 },
75 {WINED3DSIO_SINCOS, "sincos", 1, 4, WINED3DSIH_SINCOS, WINED3DVS_VERSION(2,0), WINED3DVS_VERSION(2,1)},
76 {WINED3DSIO_SINCOS, "sincos", 1, 2, WINED3DSIH_SINCOS, WINED3DVS_VERSION(3,0), -1 },
77 /* Matrix */
78 {WINED3DSIO_M4x4, "m4x4", 1, 3, WINED3DSIH_M4x4, 0, 0 },
79 {WINED3DSIO_M4x3, "m4x3", 1, 3, WINED3DSIH_M4x3, 0, 0 },
80 {WINED3DSIO_M3x4, "m3x4", 1, 3, WINED3DSIH_M3x4, 0, 0 },
81 {WINED3DSIO_M3x3, "m3x3", 1, 3, WINED3DSIH_M3x3, 0, 0 },
82 {WINED3DSIO_M3x2, "m3x2", 1, 3, WINED3DSIH_M3x2, 0, 0 },
83 /* Declare registers */
84 {WINED3DSIO_DCL, "dcl", 0, 2, WINED3DSIH_DCL, 0, 0 },
85 /* Constant definitions */
86 {WINED3DSIO_DEF, "def", 1, 5, WINED3DSIH_DEF, 0, 0 },
87 {WINED3DSIO_DEFB, "defb", 1, 2, WINED3DSIH_DEFB, 0, 0 },
88 {WINED3DSIO_DEFI, "defi", 1, 5, WINED3DSIH_DEFI, 0, 0 },
89 /* Flow control - requires GLSL or software shaders */
90 {WINED3DSIO_REP , "rep", 0, 1, WINED3DSIH_REP, WINED3DVS_VERSION(2,0), -1 },
91 {WINED3DSIO_ENDREP, "endrep", 0, 0, WINED3DSIH_ENDREP, WINED3DVS_VERSION(2,0), -1 },
92 {WINED3DSIO_IF, "if", 0, 1, WINED3DSIH_IF, WINED3DVS_VERSION(2,0), -1 },
93 {WINED3DSIO_IFC, "ifc", 0, 2, WINED3DSIH_IFC, WINED3DVS_VERSION(2,1), -1 },
94 {WINED3DSIO_ELSE, "else", 0, 0, WINED3DSIH_ELSE, WINED3DVS_VERSION(2,0), -1 },
95 {WINED3DSIO_ENDIF, "endif", 0, 0, WINED3DSIH_ENDIF, WINED3DVS_VERSION(2,0), -1 },
96 {WINED3DSIO_BREAK, "break", 0, 0, WINED3DSIH_BREAK, WINED3DVS_VERSION(2,1), -1 },
97 {WINED3DSIO_BREAKC, "breakc", 0, 2, WINED3DSIH_BREAKC, WINED3DVS_VERSION(2,1), -1 },
98 {WINED3DSIO_BREAKP, "breakp", 0, 1, WINED3DSIH_BREAKP, 0, 0 },
99 {WINED3DSIO_CALL, "call", 0, 1, WINED3DSIH_CALL, WINED3DVS_VERSION(2,0), -1 },
100 {WINED3DSIO_CALLNZ, "callnz", 0, 2, WINED3DSIH_CALLNZ, WINED3DVS_VERSION(2,0), -1 },
101 {WINED3DSIO_LOOP, "loop", 0, 2, WINED3DSIH_LOOP, WINED3DVS_VERSION(2,0), -1 },
102 {WINED3DSIO_RET, "ret", 0, 0, WINED3DSIH_RET, WINED3DVS_VERSION(2,0), -1 },
103 {WINED3DSIO_ENDLOOP, "endloop", 0, 0, WINED3DSIH_ENDLOOP, WINED3DVS_VERSION(2,0), -1 },
104 {WINED3DSIO_LABEL, "label", 0, 1, WINED3DSIH_LABEL, WINED3DVS_VERSION(2,0), -1 },
106 {WINED3DSIO_SETP, "setp", 1, 3, WINED3DSIH_SETP, 0, 0 },
107 {WINED3DSIO_TEXLDL, "texldl", 1, 3, WINED3DSIH_TEXLDL, WINED3DVS_VERSION(3,0), -1 },
108 {0, NULL, 0, 0, 0, 0, 0 }
111 static void vshader_set_limits(
112 IWineD3DVertexShaderImpl *This) {
114 This->baseShader.limits.texcoord = 0;
115 This->baseShader.limits.attributes = 16;
116 This->baseShader.limits.packed_input = 0;
118 /* Must match D3DCAPS9.MaxVertexShaderConst: at least 256 for vs_2_0 */
119 This->baseShader.limits.constant_float = GL_LIMITS(vshader_constantsF);
121 switch (This->baseShader.reg_maps.shader_version)
123 case WINED3DVS_VERSION(1,0):
124 case WINED3DVS_VERSION(1,1):
125 This->baseShader.limits.temporary = 12;
126 This->baseShader.limits.constant_bool = 0;
127 This->baseShader.limits.constant_int = 0;
128 This->baseShader.limits.address = 1;
129 This->baseShader.limits.packed_output = 0;
130 This->baseShader.limits.sampler = 0;
131 This->baseShader.limits.label = 0;
132 break;
134 case WINED3DVS_VERSION(2,0):
135 case WINED3DVS_VERSION(2,1):
136 This->baseShader.limits.temporary = 12;
137 This->baseShader.limits.constant_bool = 16;
138 This->baseShader.limits.constant_int = 16;
139 This->baseShader.limits.address = 1;
140 This->baseShader.limits.packed_output = 0;
141 This->baseShader.limits.sampler = 0;
142 This->baseShader.limits.label = 16;
143 break;
145 case WINED3DVS_VERSION(3,0):
146 This->baseShader.limits.temporary = 32;
147 This->baseShader.limits.constant_bool = 32;
148 This->baseShader.limits.constant_int = 32;
149 This->baseShader.limits.address = 1;
150 This->baseShader.limits.packed_output = 12;
151 This->baseShader.limits.sampler = 4;
152 This->baseShader.limits.label = 16; /* FIXME: 2048 */
153 break;
155 default: This->baseShader.limits.temporary = 12;
156 This->baseShader.limits.constant_bool = 16;
157 This->baseShader.limits.constant_int = 16;
158 This->baseShader.limits.address = 1;
159 This->baseShader.limits.packed_output = 0;
160 This->baseShader.limits.sampler = 0;
161 This->baseShader.limits.label = 16;
162 FIXME("Unrecognized vertex shader version %#x\n",
163 This->baseShader.reg_maps.shader_version);
167 /* This is an internal function,
168 * used to create fake semantics for shaders
169 * that don't have them - d3d8 shaders where the declaration
170 * stores the register for each input
172 static void vshader_set_input(
173 IWineD3DVertexShaderImpl* This,
174 unsigned int regnum,
175 BYTE usage, BYTE usage_idx) {
177 This->semantics_in[regnum].usage = usage;
178 This->semantics_in[regnum].usage_idx = usage_idx;
179 This->semantics_in[regnum].reg.register_type = WINED3DSPR_INPUT;
180 This->semantics_in[regnum].reg.register_idx = regnum;
181 This->semantics_in[regnum].reg.write_mask = WINED3DSP_WRITEMASK_ALL;
182 This->semantics_in[regnum].reg.modifiers = 0;
183 This->semantics_in[regnum].reg.shift = 0;
184 This->semantics_in[regnum].reg.rel_addr = NULL;
187 static BOOL match_usage(BYTE usage1, BYTE usage_idx1, BYTE usage2, BYTE usage_idx2) {
188 if (usage_idx1 != usage_idx2) return FALSE;
189 if (usage1 == usage2) return TRUE;
190 if (usage1 == WINED3DDECLUSAGE_POSITION && usage2 == WINED3DDECLUSAGE_POSITIONT) return TRUE;
191 if (usage2 == WINED3DDECLUSAGE_POSITION && usage1 == WINED3DDECLUSAGE_POSITIONT) return TRUE;
193 return FALSE;
196 BOOL vshader_get_input(
197 IWineD3DVertexShader* iface,
198 BYTE usage_req, BYTE usage_idx_req,
199 unsigned int* regnum) {
201 IWineD3DVertexShaderImpl* This = (IWineD3DVertexShaderImpl*) iface;
202 int i;
204 for (i = 0; i < MAX_ATTRIBS; i++) {
205 if (!This->baseShader.reg_maps.attributes[i]) continue;
207 if (match_usage(This->semantics_in[i].usage,
208 This->semantics_in[i].usage_idx, usage_req, usage_idx_req))
210 *regnum = i;
211 return TRUE;
214 return FALSE;
217 /* *******************************************
218 IWineD3DVertexShader IUnknown parts follow
219 ******************************************* */
220 static HRESULT WINAPI IWineD3DVertexShaderImpl_QueryInterface(IWineD3DVertexShader *iface, REFIID riid, LPVOID *ppobj) {
221 TRACE("iface %p, riid %s, ppobj %p\n", iface, debugstr_guid(riid), ppobj);
223 if (IsEqualGUID(riid, &IID_IWineD3DVertexShader)
224 || IsEqualGUID(riid, &IID_IWineD3DBaseShader)
225 || IsEqualGUID(riid, &IID_IWineD3DBase)
226 || IsEqualGUID(riid, &IID_IUnknown))
228 IUnknown_AddRef(iface);
229 *ppobj = iface;
230 return S_OK;
233 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));
235 *ppobj = NULL;
236 return E_NOINTERFACE;
239 static ULONG WINAPI IWineD3DVertexShaderImpl_AddRef(IWineD3DVertexShader *iface) {
240 IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
241 ULONG refcount = InterlockedIncrement(&This->baseShader.ref);
243 TRACE("%p increasing refcount to %u\n", This, refcount);
245 return refcount;
248 static ULONG WINAPI IWineD3DVertexShaderImpl_Release(IWineD3DVertexShader *iface) {
249 IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
250 ULONG refcount = InterlockedDecrement(&This->baseShader.ref);
252 TRACE("%p decreasing refcount to %u\n", This, refcount);
254 if (!refcount)
256 shader_cleanup((IWineD3DBaseShader *)iface);
257 HeapFree(GetProcessHeap(), 0, This);
260 return refcount;
263 /* *******************************************
264 IWineD3DVertexShader IWineD3DVertexShader parts follow
265 ******************************************* */
267 static HRESULT WINAPI IWineD3DVertexShaderImpl_GetParent(IWineD3DVertexShader *iface, IUnknown** parent){
268 IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
270 *parent = This->parent;
271 IUnknown_AddRef(*parent);
272 TRACE("(%p) : returning %p\n", This, *parent);
273 return WINED3D_OK;
276 static HRESULT WINAPI IWineD3DVertexShaderImpl_GetDevice(IWineD3DVertexShader* iface, IWineD3DDevice **pDevice){
277 IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
278 IWineD3DDevice_AddRef(This->baseShader.device);
279 *pDevice = This->baseShader.device;
280 TRACE("(%p) returning %p\n", This, *pDevice);
281 return WINED3D_OK;
284 static HRESULT WINAPI IWineD3DVertexShaderImpl_GetFunction(IWineD3DVertexShader* impl, VOID* pData, UINT* pSizeOfData) {
285 IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)impl;
286 TRACE("(%p) : pData(%p), pSizeOfData(%p)\n", This, pData, pSizeOfData);
288 if (NULL == pData) {
289 *pSizeOfData = This->baseShader.functionLength;
290 return WINED3D_OK;
292 if (*pSizeOfData < This->baseShader.functionLength) {
293 /* MSDN claims (for d3d8 at least) that if *pSizeOfData is smaller
294 * than the required size we should write the required size and
295 * return D3DERR_MOREDATA. That's not actually true. */
296 return WINED3DERR_INVALIDCALL;
299 TRACE("(%p) : GetFunction copying to %p\n", This, pData);
300 memcpy(pData, This->baseShader.function, This->baseShader.functionLength);
302 return WINED3D_OK;
305 /* Note that for vertex shaders CompileShader isn't called until the
306 * shader is first used. The reason for this is that we need the vertex
307 * declaration the shader will be used with in order to determine if
308 * the data in a register is of type D3DCOLOR, and needs swizzling. */
309 static HRESULT WINAPI IWineD3DVertexShaderImpl_SetFunction(IWineD3DVertexShader *iface, CONST DWORD *pFunction) {
311 IWineD3DVertexShaderImpl *This =(IWineD3DVertexShaderImpl *)iface;
312 IWineD3DDeviceImpl *deviceImpl = (IWineD3DDeviceImpl *) This->baseShader.device;
313 HRESULT hr;
314 shader_reg_maps *reg_maps = &This->baseShader.reg_maps;
316 TRACE("(%p) : pFunction %p\n", iface, pFunction);
318 /* First pass: trace shader */
319 if (TRACE_ON(d3d_shader)) shader_trace_init(pFunction, This->baseShader.shader_ins);
321 /* Initialize immediate constant lists */
322 list_init(&This->baseShader.constantsF);
323 list_init(&This->baseShader.constantsB);
324 list_init(&This->baseShader.constantsI);
326 /* Second pass: figure out registers used, semantics, etc.. */
327 This->min_rel_offset = GL_LIMITS(vshader_constantsF);
328 This->max_rel_offset = 0;
329 memset(reg_maps, 0, sizeof(shader_reg_maps));
330 hr = shader_get_registers_used((IWineD3DBaseShader*) This, reg_maps,
331 This->semantics_in, This->semantics_out, pFunction);
332 if (hr != WINED3D_OK) return hr;
334 vshader_set_limits(This);
336 This->baseShader.shader_mode = deviceImpl->vs_selected_mode;
338 if(deviceImpl->vs_selected_mode == SHADER_ARB &&
339 (GLINFO_LOCATION).arb_vs_offset_limit &&
340 This->min_rel_offset <= This->max_rel_offset) {
342 if(This->max_rel_offset - This->min_rel_offset > 127) {
343 FIXME("The difference between the minimum and maximum relative offset is > 127\n");
344 FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
345 FIXME("Min: %d, Max: %d\n", This->min_rel_offset, This->max_rel_offset);
346 } else if(This->max_rel_offset - This->min_rel_offset > 63) {
347 This->rel_offset = This->min_rel_offset + 63;
348 } else if(This->max_rel_offset > 63) {
349 This->rel_offset = This->min_rel_offset;
350 } else {
351 This->rel_offset = 0;
354 This->baseShader.load_local_constsF = This->baseShader.reg_maps.usesrelconstF && !list_empty(&This->baseShader.constantsF);
356 /* copy the function ... because it will certainly be released by application */
357 This->baseShader.function = HeapAlloc(GetProcessHeap(), 0, This->baseShader.functionLength);
358 if (!This->baseShader.function) return E_OUTOFMEMORY;
359 memcpy(This->baseShader.function, pFunction, This->baseShader.functionLength);
361 return WINED3D_OK;
364 /* Preload semantics for d3d8 shaders */
365 static void WINAPI IWineD3DVertexShaderImpl_FakeSemantics(IWineD3DVertexShader *iface, IWineD3DVertexDeclaration *vertex_declaration) {
366 IWineD3DVertexShaderImpl *This =(IWineD3DVertexShaderImpl *)iface;
367 IWineD3DVertexDeclarationImpl* vdecl = (IWineD3DVertexDeclarationImpl*)vertex_declaration;
369 unsigned int i;
370 for (i = 0; i < vdecl->element_count; ++i)
372 const struct wined3d_vertex_declaration_element *e = &vdecl->elements[i];
373 vshader_set_input(This, e->output_slot, e->usage, e->usage_idx);
377 /* Set local constants for d3d8 shaders */
378 static HRESULT WINAPI IWIneD3DVertexShaderImpl_SetLocalConstantsF(IWineD3DVertexShader *iface,
379 UINT start_idx, const float *src_data, UINT count) {
380 IWineD3DVertexShaderImpl *This =(IWineD3DVertexShaderImpl *)iface;
381 UINT i, end_idx;
383 TRACE("(%p) : start_idx %u, src_data %p, count %u\n", This, start_idx, src_data, count);
385 end_idx = start_idx + count;
386 if (end_idx > GL_LIMITS(vshader_constantsF)) {
387 WARN("end_idx %u > float constants limit %u\n", end_idx, GL_LIMITS(vshader_constantsF));
388 end_idx = GL_LIMITS(vshader_constantsF);
391 for (i = start_idx; i < end_idx; ++i) {
392 local_constant* lconst = HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant));
393 if (!lconst) return E_OUTOFMEMORY;
395 lconst->idx = i;
396 memcpy(lconst->value, src_data + (i - start_idx) * 4 /* 4 components */, 4 * sizeof(float));
397 list_add_head(&This->baseShader.constantsF, &lconst->entry);
400 return WINED3D_OK;
403 static GLuint vertexshader_compile(IWineD3DVertexShaderImpl *This, const struct vs_compile_args *args) {
404 IWineD3DDeviceImpl *deviceImpl = (IWineD3DDeviceImpl *) This->baseShader.device;
405 SHADER_BUFFER buffer;
406 GLuint ret;
408 /* Generate the HW shader */
409 TRACE("(%p) : Generating hardware program\n", This);
410 shader_buffer_init(&buffer);
411 This->cur_args = args;
412 ret = deviceImpl->shader_backend->shader_generate_vshader((IWineD3DVertexShader *)This, &buffer, args);
413 This->cur_args = NULL;
414 shader_buffer_free(&buffer);
416 return ret;
419 const IWineD3DVertexShaderVtbl IWineD3DVertexShader_Vtbl =
421 /*** IUnknown methods ***/
422 IWineD3DVertexShaderImpl_QueryInterface,
423 IWineD3DVertexShaderImpl_AddRef,
424 IWineD3DVertexShaderImpl_Release,
425 /*** IWineD3DBase methods ***/
426 IWineD3DVertexShaderImpl_GetParent,
427 /*** IWineD3DBaseShader methods ***/
428 IWineD3DVertexShaderImpl_SetFunction,
429 /*** IWineD3DVertexShader methods ***/
430 IWineD3DVertexShaderImpl_GetDevice,
431 IWineD3DVertexShaderImpl_GetFunction,
432 IWineD3DVertexShaderImpl_FakeSemantics,
433 IWIneD3DVertexShaderImpl_SetLocalConstantsF
436 void find_vs_compile_args(IWineD3DVertexShaderImpl *shader, IWineD3DStateBlockImpl *stateblock, struct vs_compile_args *args) {
437 args->fog_src = stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE ? VS_FOG_COORD : VS_FOG_Z;
438 args->swizzle_map = ((IWineD3DDeviceImpl *)shader->baseShader.device)->strided_streams.swizzle_map;
441 static inline BOOL vs_args_equal(const struct vs_compile_args *stored, const struct vs_compile_args *new,
442 const DWORD use_map) {
443 if((stored->swizzle_map & use_map) != new->swizzle_map) return FALSE;
444 return stored->fog_src == new->fog_src;
447 GLuint find_gl_vshader(IWineD3DVertexShaderImpl *shader, const struct vs_compile_args *args)
449 UINT i;
450 DWORD new_size = shader->shader_array_size;
451 struct vs_compiled_shader *new_array;
452 DWORD use_map = ((IWineD3DDeviceImpl *)shader->baseShader.device)->strided_streams.use_map;
454 /* Usually we have very few GL shaders for each d3d shader(just 1 or maybe 2),
455 * so a linear search is more performant than a hashmap or a binary search
456 * (cache coherency etc)
458 for(i = 0; i < shader->num_gl_shaders; i++) {
459 if(vs_args_equal(&shader->gl_shaders[i].args, args, use_map)) {
460 return shader->gl_shaders[i].prgId;
464 TRACE("No matching GL shader found, compiling a new shader\n");
466 if(shader->shader_array_size == shader->num_gl_shaders) {
467 if (shader->num_gl_shaders)
469 new_size = shader->shader_array_size + max(1, shader->shader_array_size / 2);
470 new_array = HeapReAlloc(GetProcessHeap(), 0, shader->gl_shaders,
471 new_size * sizeof(*shader->gl_shaders));
472 } else {
473 new_array = HeapAlloc(GetProcessHeap(), 0, sizeof(*shader->gl_shaders));
474 new_size = 1;
477 if(!new_array) {
478 ERR("Out of memory\n");
479 return 0;
481 shader->gl_shaders = new_array;
482 shader->shader_array_size = new_size;
485 shader->gl_shaders[shader->num_gl_shaders].args = *args;
486 shader->gl_shaders[shader->num_gl_shaders].prgId = vertexshader_compile(shader, args);
487 return shader->gl_shaders[shader->num_gl_shaders++].prgId;