wined3d: Vertex state stateblocks should also record the vertex declaration.
[wine] / dlls / wined3d / vertexdeclaration.c
1 /*
2  * vertex declaration implementation
3  *
4  * Copyright 2002-2005 Raphael Junqueira
5  * Copyright 2004 Jason Edmeades
6  * Copyright 2004 Christian Costa
7  * Copyright 2005 Oliver Stieber
8  * Copyright 2009 Henri Verbeet for CodeWeavers
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26 #include "wined3d_private.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(d3d_decl);
29
30 #define GLINFO_LOCATION This->wineD3DDevice->adapter->gl_info
31
32 static void dump_wined3dvertexelement(const WINED3DVERTEXELEMENT *element) {
33     TRACE("     format: %s (%#x)\n", debug_d3dformat(element->format), element->format);
34     TRACE(" input_slot: %u\n", element->input_slot);
35     TRACE("     offset: %u\n", element->offset);
36     TRACE("output_slot: %u\n", element->output_slot);
37     TRACE("     method: %s (%#x)\n", debug_d3ddeclmethod(element->method), element->method);
38     TRACE("      usage: %s (%#x)\n", debug_d3ddeclusage(element->usage), element->usage);
39     TRACE("  usage_idx: %u\n", element->usage_idx);
40 }
41
42 /* *******************************************
43    IWineD3DVertexDeclaration IUnknown parts follow
44    ******************************************* */
45 static HRESULT WINAPI IWineD3DVertexDeclarationImpl_QueryInterface(IWineD3DVertexDeclaration *iface, REFIID riid, LPVOID *ppobj)
46 {
47     IWineD3DVertexDeclarationImpl *This = (IWineD3DVertexDeclarationImpl *)iface;
48     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
49     if (IsEqualGUID(riid, &IID_IUnknown)
50         || IsEqualGUID(riid, &IID_IWineD3DBase)
51         || IsEqualGUID(riid, &IID_IWineD3DVertexDeclaration)){
52         IUnknown_AddRef(iface);
53         *ppobj = This;
54         return S_OK;
55     }
56     *ppobj = NULL;
57     return E_NOINTERFACE;
58 }
59
60 static ULONG WINAPI IWineD3DVertexDeclarationImpl_AddRef(IWineD3DVertexDeclaration *iface) {
61     IWineD3DVertexDeclarationImpl *This = (IWineD3DVertexDeclarationImpl *)iface;
62     TRACE("(%p) : AddRef increasing from %d\n", This, This->ref);
63     return InterlockedIncrement(&This->ref);
64 }
65
66 static ULONG WINAPI IWineD3DVertexDeclarationImpl_Release(IWineD3DVertexDeclaration *iface) {
67     IWineD3DVertexDeclarationImpl *This = (IWineD3DVertexDeclarationImpl *)iface;
68     ULONG ref;
69     TRACE("(%p) : Releasing from %d\n", This, This->ref);
70     ref = InterlockedDecrement(&This->ref);
71     if (!ref)
72     {
73         HeapFree(GetProcessHeap(), 0, This->elements);
74         This->parent_ops->wined3d_object_destroyed(This->parent);
75         HeapFree(GetProcessHeap(), 0, This);
76     }
77     return ref;
78 }
79
80 /* *******************************************
81    IWineD3DVertexDeclaration parts follow
82    ******************************************* */
83
84 static HRESULT WINAPI IWineD3DVertexDeclarationImpl_GetParent(IWineD3DVertexDeclaration *iface, IUnknown** parent){
85     IWineD3DVertexDeclarationImpl *This = (IWineD3DVertexDeclarationImpl *)iface;
86
87     *parent= This->parent;
88     IUnknown_AddRef(*parent);
89     TRACE("(%p) : returning %p\n", This, *parent);
90     return WINED3D_OK;
91 }
92
93 static HRESULT WINAPI IWineD3DVertexDeclarationImpl_GetDevice(IWineD3DVertexDeclaration *iface, IWineD3DDevice** ppDevice) {
94     IWineD3DVertexDeclarationImpl *This = (IWineD3DVertexDeclarationImpl *)iface;
95     TRACE("(%p) : returning %p\n", This, This->wineD3DDevice);
96
97     *ppDevice = (IWineD3DDevice *) This->wineD3DDevice;
98     IWineD3DDevice_AddRef(*ppDevice);
99
100     return WINED3D_OK;
101 }
102
103 static BOOL declaration_element_valid_ffp(const WINED3DVERTEXELEMENT *element)
104 {
105     switch(element->usage)
106     {
107         case WINED3DDECLUSAGE_POSITION:
108         case WINED3DDECLUSAGE_POSITIONT:
109             switch(element->format)
110             {
111                 case WINED3DFMT_R32G32_FLOAT:
112                 case WINED3DFMT_R32G32B32_FLOAT:
113                 case WINED3DFMT_R32G32B32A32_FLOAT:
114                 case WINED3DFMT_R16G16_SINT:
115                 case WINED3DFMT_R16G16B16A16_SINT:
116                 case WINED3DFMT_R16G16_FLOAT:
117                 case WINED3DFMT_R16G16B16A16_FLOAT:
118                     return TRUE;
119                 default:
120                     return FALSE;
121             }
122
123         case WINED3DDECLUSAGE_BLENDWEIGHT:
124             switch(element->format)
125             {
126                 case WINED3DFMT_R32_FLOAT:
127                 case WINED3DFMT_R32G32_FLOAT:
128                 case WINED3DFMT_R32G32B32_FLOAT:
129                 case WINED3DFMT_R32G32B32A32_FLOAT:
130                 case WINED3DFMT_B8G8R8A8_UNORM:
131                 case WINED3DFMT_R8G8B8A8_UINT:
132                 case WINED3DFMT_R16G16_SINT:
133                 case WINED3DFMT_R16G16B16A16_SINT:
134                 case WINED3DFMT_R16G16_FLOAT:
135                 case WINED3DFMT_R16G16B16A16_FLOAT:
136                     return TRUE;
137                 default:
138                     return FALSE;
139             }
140
141         case WINED3DDECLUSAGE_NORMAL:
142             switch(element->format)
143             {
144                 case WINED3DFMT_R32G32B32_FLOAT:
145                 case WINED3DFMT_R32G32B32A32_FLOAT:
146                 case WINED3DFMT_R16G16B16A16_SINT:
147                 case WINED3DFMT_R16G16B16A16_FLOAT:
148                     return TRUE;
149                 default:
150                     return FALSE;
151             }
152
153         case WINED3DDECLUSAGE_TEXCOORD:
154             switch(element->format)
155             {
156                 case WINED3DFMT_R32_FLOAT:
157                 case WINED3DFMT_R32G32_FLOAT:
158                 case WINED3DFMT_R32G32B32_FLOAT:
159                 case WINED3DFMT_R32G32B32A32_FLOAT:
160                 case WINED3DFMT_R16G16_SINT:
161                 case WINED3DFMT_R16G16B16A16_SINT:
162                 case WINED3DFMT_R16G16_FLOAT:
163                 case WINED3DFMT_R16G16B16A16_FLOAT:
164                     return TRUE;
165                 default:
166                     return FALSE;
167             }
168
169         case WINED3DDECLUSAGE_COLOR:
170             switch(element->format)
171             {
172                 case WINED3DFMT_R32G32B32_FLOAT:
173                 case WINED3DFMT_R32G32B32A32_FLOAT:
174                 case WINED3DFMT_B8G8R8A8_UNORM:
175                 case WINED3DFMT_R8G8B8A8_UINT:
176                 case WINED3DFMT_R16G16B16A16_SINT:
177                 case WINED3DFMT_R8G8B8A8_UNORM:
178                 case WINED3DFMT_R16G16B16A16_SNORM:
179                 case WINED3DFMT_R16G16B16A16_UNORM:
180                 case WINED3DFMT_R16G16B16A16_FLOAT:
181                     return TRUE;
182                 default:
183                     return FALSE;
184             }
185
186         default:
187             return FALSE;
188     }
189 }
190
191 static const IWineD3DVertexDeclarationVtbl IWineD3DVertexDeclaration_Vtbl =
192 {
193     /* IUnknown */
194     IWineD3DVertexDeclarationImpl_QueryInterface,
195     IWineD3DVertexDeclarationImpl_AddRef,
196     IWineD3DVertexDeclarationImpl_Release,
197     /* IWineD3DVertexDeclaration */
198     IWineD3DVertexDeclarationImpl_GetParent,
199     IWineD3DVertexDeclarationImpl_GetDevice,
200 };
201
202 HRESULT vertexdeclaration_init(IWineD3DVertexDeclarationImpl *declaration, IWineD3DDeviceImpl *device,
203         const WINED3DVERTEXELEMENT *elements, UINT element_count,
204         IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
205 {
206     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
207     WORD preloaded = 0; /* MAX_STREAMS, 16 */
208     unsigned int i;
209
210     if (TRACE_ON(d3d_decl))
211     {
212         for (i = 0; i < element_count; ++i)
213         {
214             dump_wined3dvertexelement(elements + i);
215         }
216     }
217
218     declaration->lpVtbl = &IWineD3DVertexDeclaration_Vtbl;
219     declaration->ref = 1;
220     declaration->parent = parent;
221     declaration->parent_ops = parent_ops;
222     declaration->wineD3DDevice = device;
223     declaration->elements = HeapAlloc(GetProcessHeap(), 0, sizeof(*declaration->elements) * element_count);
224     if (!declaration->elements)
225     {
226         ERR("Failed to allocate elements memory.\n");
227         return E_OUTOFMEMORY;
228     }
229     declaration->element_count = element_count;
230
231     /* Do some static analysis on the elements to make reading the
232      * declaration more comfortable for the drawing code. */
233     for (i = 0; i < element_count; ++i)
234     {
235         struct wined3d_vertex_declaration_element *e = &declaration->elements[i];
236
237         e->format_desc = getFormatDescEntry(elements[i].format, gl_info);
238         e->ffp_valid = declaration_element_valid_ffp(&elements[i]);
239         e->input_slot = elements[i].input_slot;
240         e->offset = elements[i].offset;
241         e->output_slot = elements[i].output_slot;
242         e->method = elements[i].method;
243         e->usage = elements[i].usage;
244         e->usage_idx = elements[i].usage_idx;
245
246         if (e->usage == WINED3DDECLUSAGE_POSITIONT) declaration->position_transformed = TRUE;
247
248         /* Find the streams used in the declaration. The vertex buffers have
249          * to be loaded when drawing, but filter tesselation pseudo streams. */
250         if (e->input_slot >= MAX_STREAMS) continue;
251
252         if (!e->format_desc->gl_vtx_format)
253         {
254             FIXME("The application tries to use an unsupported format (%s), returning E_FAIL.\n",
255                     debug_d3dformat(elements[i].format));
256             HeapFree(GetProcessHeap(), 0, declaration->elements);
257             return E_FAIL;
258         }
259
260         if (e->offset & 0x3)
261         {
262             WARN("Declaration element %u is not 4 byte aligned(%u), returning E_FAIL.\n", i, e->offset);
263             HeapFree(GetProcessHeap(), 0, declaration->elements);
264             return E_FAIL;
265         }
266
267         if (!(preloaded & (1 << e->input_slot)))
268         {
269             declaration->streams[declaration->num_streams] = e->input_slot;
270             ++declaration->num_streams;
271             preloaded |= 1 << e->input_slot;
272         }
273
274         if (elements[i].format == WINED3DFMT_R16G16_FLOAT || elements[i].format == WINED3DFMT_R16G16B16A16_FLOAT)
275         {
276             if (!gl_info->supported[ARB_HALF_FLOAT_VERTEX]) declaration->half_float_conv_needed = TRUE;
277         }
278     }
279
280     return WINED3D_OK;
281 }