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 "wine/port.h"
27 #include "wined3d_private.h"
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(d3d_decl);
30 
31 static void dump_wined3d_vertex_element(const struct wined3d_vertex_element *element)
32 {
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("       input slot class: %s\n", debug_d3dinput_classification(element->input_slot_class));
38     TRACE("instance data step rate: %u\n", element->instance_data_step_rate);
39     TRACE("                 method: %s (%#x)\n", debug_d3ddeclmethod(element->method), element->method);
40     TRACE("                  usage: %s (%#x)\n", debug_d3ddeclusage(element->usage), element->usage);
41     TRACE("              usage_idx: %u\n", element->usage_idx);
42 }
43 
44 ULONG CDECL wined3d_vertex_declaration_incref(struct wined3d_vertex_declaration *declaration)
45 {
46     ULONG refcount = InterlockedIncrement(&declaration->ref);
47 
48     TRACE("%p increasing refcount to %u.\n", declaration, refcount);
49 
50     return refcount;
51 }
52 
53 static void wined3d_vertex_declaration_destroy_object(void *object)
54 {
55     struct wined3d_vertex_declaration *declaration = object;
56 
57     heap_free(declaration->elements);
58     heap_free(declaration);
59 }
60 
61 ULONG CDECL wined3d_vertex_declaration_decref(struct wined3d_vertex_declaration *declaration)
62 {
63     ULONG refcount = InterlockedDecrement(&declaration->ref);
64 
65     TRACE("%p decreasing refcount to %u.\n", declaration, refcount);
66 
67     if (!refcount)
68     {
69         declaration->parent_ops->wined3d_object_destroyed(declaration->parent);
70         wined3d_cs_destroy_object(declaration->device->cs,
71                 wined3d_vertex_declaration_destroy_object, declaration);
72     }
73 
74     return refcount;
75 }
76 
77 void * CDECL wined3d_vertex_declaration_get_parent(const struct wined3d_vertex_declaration *declaration)
78 {
79     TRACE("declaration %p.\n", declaration);
80 
81     return declaration->parent;
82 }
83 
84 static BOOL declaration_element_valid_ffp(const struct wined3d_vertex_element *element)
85 {
86     switch(element->usage)
87     {
88         case WINED3D_DECL_USAGE_POSITION:
89         case WINED3D_DECL_USAGE_POSITIONT:
90             switch(element->format)
91             {
92                 case WINED3DFMT_R32G32_FLOAT:
93                 case WINED3DFMT_R32G32B32_FLOAT:
94                 case WINED3DFMT_R32G32B32A32_FLOAT:
95                 case WINED3DFMT_R16G16_SINT:
96                 case WINED3DFMT_R16G16B16A16_SINT:
97                 case WINED3DFMT_R16G16_FLOAT:
98                 case WINED3DFMT_R16G16B16A16_FLOAT:
99                     return TRUE;
100                 default:
101                     return FALSE;
102             }
103 
104         case WINED3D_DECL_USAGE_BLEND_WEIGHT:
105             switch(element->format)
106             {
107                 case WINED3DFMT_R32_FLOAT:
108                 case WINED3DFMT_R32G32_FLOAT:
109                 case WINED3DFMT_R32G32B32_FLOAT:
110                 case WINED3DFMT_R32G32B32A32_FLOAT:
111                 case WINED3DFMT_B8G8R8A8_UNORM:
112                 case WINED3DFMT_R8G8B8A8_UINT:
113                 case WINED3DFMT_R16G16_SINT:
114                 case WINED3DFMT_R16G16B16A16_SINT:
115                 case WINED3DFMT_R16G16_FLOAT:
116                 case WINED3DFMT_R16G16B16A16_FLOAT:
117                     return TRUE;
118                 default:
119                     return FALSE;
120             }
121 
122         case WINED3D_DECL_USAGE_BLEND_INDICES:
123             switch(element->format)
124             {
125                 case WINED3DFMT_R8G8B8A8_UINT:
126                     return TRUE;
127                 default:
128                     return FALSE;
129             }
130 
131         case WINED3D_DECL_USAGE_NORMAL:
132             switch(element->format)
133             {
134                 case WINED3DFMT_R32G32B32_FLOAT:
135                 case WINED3DFMT_R32G32B32A32_FLOAT:
136                 case WINED3DFMT_R16G16B16A16_SINT:
137                 case WINED3DFMT_R16G16B16A16_FLOAT:
138                     return TRUE;
139                 default:
140                     return FALSE;
141             }
142 
143         case WINED3D_DECL_USAGE_TEXCOORD:
144             switch(element->format)
145             {
146                 case WINED3DFMT_R32_FLOAT:
147                 case WINED3DFMT_R32G32_FLOAT:
148                 case WINED3DFMT_R32G32B32_FLOAT:
149                 case WINED3DFMT_R32G32B32A32_FLOAT:
150                 case WINED3DFMT_R16G16_SINT:
151                 case WINED3DFMT_R16G16B16A16_SINT:
152                 case WINED3DFMT_R16G16_FLOAT:
153                 case WINED3DFMT_R16G16B16A16_FLOAT:
154                     return TRUE;
155                 default:
156                     return FALSE;
157             }
158 
159         case WINED3D_DECL_USAGE_COLOR:
160             switch(element->format)
161             {
162                 case WINED3DFMT_R32G32B32_FLOAT:
163                 case WINED3DFMT_R32G32B32A32_FLOAT:
164                 case WINED3DFMT_B8G8R8A8_UNORM:
165                 case WINED3DFMT_R8G8B8A8_UINT:
166                 case WINED3DFMT_R16G16B16A16_SINT:
167                 case WINED3DFMT_R8G8B8A8_UNORM:
168                 case WINED3DFMT_R16G16B16A16_SNORM:
169                 case WINED3DFMT_R16G16B16A16_UNORM:
170                 case WINED3DFMT_R16G16B16A16_FLOAT:
171                     return TRUE;
172                 default:
173                     return FALSE;
174             }
175 
176         default:
177             return FALSE;
178     }
179 }
180 
181 static HRESULT vertexdeclaration_init(struct wined3d_vertex_declaration *declaration,
182         struct wined3d_device *device, const struct wined3d_vertex_element *elements, UINT element_count,
183         void *parent, const struct wined3d_parent_ops *parent_ops)
184 {
185     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
186     unsigned int i;
187 
188     if (TRACE_ON(d3d_decl))
189     {
190         for (i = 0; i < element_count; ++i)
191         {
192             dump_wined3d_vertex_element(elements + i);
193         }
194     }
195 
196     declaration->ref = 1;
197     declaration->parent = parent;
198     declaration->parent_ops = parent_ops;
199     declaration->device = device;
200     if (!(declaration->elements = heap_calloc(element_count, sizeof(*declaration->elements))))
201     {
202         ERR("Failed to allocate elements memory.\n");
203         return E_OUTOFMEMORY;
204     }
205     declaration->element_count = element_count;
206 
207     /* Do some static analysis on the elements to make reading the
208      * declaration more comfortable for the drawing code. */
209     for (i = 0; i < element_count; ++i)
210     {
211         struct wined3d_vertex_declaration_element *e = &declaration->elements[i];
212 
213         e->format = wined3d_get_format(gl_info, elements[i].format, 0);
214         e->ffp_valid = declaration_element_valid_ffp(&elements[i]);
215         e->input_slot = elements[i].input_slot;
216         e->offset = elements[i].offset;
217         e->output_slot = elements[i].output_slot;
218         e->input_slot_class = elements[i].input_slot_class;
219         e->instance_data_step_rate = elements[i].instance_data_step_rate;
220         e->method = elements[i].method;
221         e->usage = elements[i].usage;
222         e->usage_idx = elements[i].usage_idx;
223 
224         if (e->usage == WINED3D_DECL_USAGE_POSITIONT)
225             declaration->position_transformed = TRUE;
226 
227         /* Find the streams used in the declaration. The vertex buffers have
228          * to be loaded when drawing, but filter tesselation pseudo streams. */
229         if (e->input_slot >= MAX_STREAMS) continue;
230 
231         if (!e->format->gl_vtx_format)
232         {
233             FIXME("The application tries to use an unsupported format (%s), returning E_FAIL.\n",
234                     debug_d3dformat(elements[i].format));
235             heap_free(declaration->elements);
236             return E_FAIL;
237         }
238 
239         if (e->offset == WINED3D_APPEND_ALIGNED_ELEMENT)
240         {
241             const struct wined3d_vertex_declaration_element *prev;
242             unsigned int j;
243 
244             e->offset = 0;
245             for (j = 1; j <= i; ++j)
246             {
247                 prev = &declaration->elements[i - j];
248                 if (prev->input_slot == e->input_slot)
249                 {
250                     e->offset = (prev->offset + prev->format->byte_count + 3) & ~3;
251                     break;
252                 }
253             }
254         }
255 
256         if (e->offset & 0x3)
257         {
258             WARN("Declaration element %u is not 4 byte aligned(%u), returning E_FAIL.\n", i, e->offset);
259             heap_free(declaration->elements);
260             return E_FAIL;
261         }
262 
263         if (elements[i].format == WINED3DFMT_R16G16_FLOAT || elements[i].format == WINED3DFMT_R16G16B16A16_FLOAT)
264         {
265             if (!gl_info->supported[ARB_HALF_FLOAT_VERTEX]) declaration->half_float_conv_needed = TRUE;
266         }
267     }
268 
269     return WINED3D_OK;
270 }
271 
272 HRESULT CDECL wined3d_vertex_declaration_create(struct wined3d_device *device,
273         const struct wined3d_vertex_element *elements, UINT element_count, void *parent,
274         const struct wined3d_parent_ops *parent_ops, struct wined3d_vertex_declaration **declaration)
275 {
276     struct wined3d_vertex_declaration *object;
277     HRESULT hr;
278 
279     TRACE("device %p, elements %p, element_count %u, parent %p, parent_ops %p, declaration %p.\n",
280             device, elements, element_count, parent, parent_ops, declaration);
281 
282     if (!(object = heap_alloc_zero(sizeof(*object))))
283         return E_OUTOFMEMORY;
284 
285     hr = vertexdeclaration_init(object, device, elements, element_count, parent, parent_ops);
286     if (FAILED(hr))
287     {
288         WARN("Failed to initialize vertex declaration, hr %#x.\n", hr);
289         heap_free(object);
290         return hr;
291     }
292 
293     TRACE("Created vertex declaration %p.\n", object);
294     *declaration = object;
295 
296     return WINED3D_OK;
297 }
298 
299 struct wined3d_fvf_convert_state
300 {
301     const struct wined3d_gl_info *gl_info;
302     struct wined3d_vertex_element *elements;
303     UINT offset;
304     UINT idx;
305 };
306 
307 static void append_decl_element(struct wined3d_fvf_convert_state *state,
308         enum wined3d_format_id format_id, enum wined3d_decl_usage usage, UINT usage_idx)
309 {
310     struct wined3d_vertex_element *elements = state->elements;
311     const struct wined3d_format *format;
312     UINT offset = state->offset;
313     UINT idx = state->idx;
314 
315     elements[idx].format = format_id;
316     elements[idx].input_slot = 0;
317     elements[idx].offset = offset;
318     elements[idx].output_slot = WINED3D_OUTPUT_SLOT_SEMANTIC;
319     elements[idx].input_slot_class = WINED3D_INPUT_PER_VERTEX_DATA;
320     elements[idx].instance_data_step_rate = 0;
321     elements[idx].method = WINED3D_DECL_METHOD_DEFAULT;
322     elements[idx].usage = usage;
323     elements[idx].usage_idx = usage_idx;
324 
325     format = wined3d_get_format(state->gl_info, format_id, 0);
326     state->offset += format->attribute_size;
327     ++state->idx;
328 }
329 
330 static unsigned int convert_fvf_to_declaration(const struct wined3d_gl_info *gl_info,
331         DWORD fvf, struct wined3d_vertex_element **elements)
332 {
333     BOOL has_pos = !!(fvf & WINED3DFVF_POSITION_MASK);
334     BOOL has_blend = (fvf & WINED3DFVF_XYZB5) > WINED3DFVF_XYZRHW;
335     BOOL has_blend_idx = has_blend &&
336        (((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB5) ||
337         (fvf & WINED3DFVF_LASTBETA_D3DCOLOR) ||
338         (fvf & WINED3DFVF_LASTBETA_UBYTE4));
339     BOOL has_normal = !!(fvf & WINED3DFVF_NORMAL);
340     BOOL has_psize = !!(fvf & WINED3DFVF_PSIZE);
341     BOOL has_diffuse = !!(fvf & WINED3DFVF_DIFFUSE);
342     BOOL has_specular = !!(fvf & WINED3DFVF_SPECULAR);
343 
344     DWORD num_textures = (fvf & WINED3DFVF_TEXCOUNT_MASK) >> WINED3DFVF_TEXCOUNT_SHIFT;
345     DWORD texcoords = (fvf & 0xffff0000) >> 16;
346     struct wined3d_fvf_convert_state state;
347     unsigned int size;
348     unsigned int idx;
349     DWORD num_blends = 1 + (((fvf & WINED3DFVF_XYZB5) - WINED3DFVF_XYZB1) >> 1);
350     if (has_blend_idx) num_blends--;
351 
352     /* Compute declaration size */
353     size = has_pos + (has_blend && num_blends > 0) + has_blend_idx + has_normal +
354            has_psize + has_diffuse + has_specular + num_textures;
355 
356     state.gl_info = gl_info;
357     if (!(state.elements = heap_calloc(size, sizeof(*state.elements))))
358         return ~0u;
359     state.offset = 0;
360     state.idx = 0;
361 
362     if (has_pos)
363     {
364         if (!has_blend && (fvf & WINED3DFVF_XYZRHW))
365             append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITIONT, 0);
366         else if ((fvf & WINED3DFVF_XYZW) == WINED3DFVF_XYZW)
367             append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0);
368         else
369             append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0);
370     }
371 
372     if (has_blend && (num_blends > 0))
373     {
374         if ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR))
375             append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
376         else
377         {
378             switch (num_blends)
379             {
380                 case 1:
381                     append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
382                     break;
383                 case 2:
384                     append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
385                     break;
386                 case 3:
387                     append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
388                     break;
389                 case 4:
390                     append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
391                     break;
392                 default:
393                     ERR("Unexpected amount of blend values: %u\n", num_blends);
394             }
395         }
396     }
397 
398     if (has_blend_idx)
399     {
400         if ((fvf & WINED3DFVF_LASTBETA_UBYTE4)
401                 || ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR)))
402             append_decl_element(&state, WINED3DFMT_R8G8B8A8_UINT, WINED3D_DECL_USAGE_BLEND_INDICES, 0);
403         else if (fvf & WINED3DFVF_LASTBETA_D3DCOLOR)
404             append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_INDICES, 0);
405         else
406             append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_INDICES, 0);
407     }
408 
409     if (has_normal)
410         append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_NORMAL, 0);
411     if (has_psize)
412         append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_PSIZE, 0);
413     if (has_diffuse)
414         append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_COLOR, 0);
415     if (has_specular)
416         append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_COLOR, 1);
417 
418     for (idx = 0; idx < num_textures; ++idx)
419     {
420         switch ((texcoords >> (idx * 2)) & 0x03)
421         {
422             case WINED3DFVF_TEXTUREFORMAT1:
423                 append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
424                 break;
425             case WINED3DFVF_TEXTUREFORMAT2:
426                 append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
427                 break;
428             case WINED3DFVF_TEXTUREFORMAT3:
429                 append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
430                 break;
431             case WINED3DFVF_TEXTUREFORMAT4:
432                 append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
433                 break;
434         }
435     }
436 
437     *elements = state.elements;
438     return size;
439 }
440 
441 HRESULT CDECL wined3d_vertex_declaration_create_from_fvf(struct wined3d_device *device,
442         DWORD fvf, void *parent, const struct wined3d_parent_ops *parent_ops,
443         struct wined3d_vertex_declaration **declaration)
444 {
445     struct wined3d_vertex_element *elements;
446     unsigned int size;
447     DWORD hr;
448 
449     TRACE("device %p, fvf %#x, parent %p, parent_ops %p, declaration %p.\n",
450             device, fvf, parent, parent_ops, declaration);
451 
452     size = convert_fvf_to_declaration(&device->adapter->gl_info, fvf, &elements);
453     if (size == ~0U) return E_OUTOFMEMORY;
454 
455     hr = wined3d_vertex_declaration_create(device, elements, size, parent, parent_ops, declaration);
456     heap_free(elements);
457     return hr;
458 }
459