| /* | |
| * vertex declaration implementation | |
| * | |
| * Copyright 2002-2005 Raphael Junqueira | |
| * Copyright 2004 Jason Edmeades | |
| * Copyright 2004 Christian Costa | |
| * Copyright 2005 Oliver Stieber | |
| * Copyright 2009 Henri Verbeet for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include "wined3d_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(d3d_decl); | |
| static void dump_wined3d_vertex_element(const struct wined3d_vertex_element *element) | |
| { | |
| TRACE(" format: %s (%#x)\n", debug_d3dformat(element->format), element->format); | |
| TRACE(" input_slot: %u\n", element->input_slot); | |
| TRACE(" offset: %u\n", element->offset); | |
| TRACE(" output_slot: %u\n", element->output_slot); | |
| TRACE(" input slot class: %s\n", debug_d3dinput_classification(element->input_slot_class)); | |
| TRACE("instance data step rate: %u\n", element->instance_data_step_rate); | |
| TRACE(" method: %s (%#x)\n", debug_d3ddeclmethod(element->method), element->method); | |
| TRACE(" usage: %s (%#x)\n", debug_d3ddeclusage(element->usage), element->usage); | |
| TRACE(" usage_idx: %u\n", element->usage_idx); | |
| } | |
| ULONG CDECL wined3d_vertex_declaration_incref(struct wined3d_vertex_declaration *declaration) | |
| { | |
| ULONG refcount = InterlockedIncrement(&declaration->ref); | |
| TRACE("%p increasing refcount to %u.\n", declaration, refcount); | |
| return refcount; | |
| } | |
| static void wined3d_vertex_declaration_destroy_object(void *object) | |
| { | |
| struct wined3d_vertex_declaration *declaration = object; | |
| HeapFree(GetProcessHeap(), 0, declaration->elements); | |
| HeapFree(GetProcessHeap(), 0, declaration); | |
| } | |
| ULONG CDECL wined3d_vertex_declaration_decref(struct wined3d_vertex_declaration *declaration) | |
| { | |
| ULONG refcount = InterlockedDecrement(&declaration->ref); | |
| TRACE("%p decreasing refcount to %u.\n", declaration, refcount); | |
| if (!refcount) | |
| { | |
| declaration->parent_ops->wined3d_object_destroyed(declaration->parent); | |
| wined3d_cs_destroy_object(declaration->device->cs, | |
| wined3d_vertex_declaration_destroy_object, declaration); | |
| } | |
| return refcount; | |
| } | |
| void * CDECL wined3d_vertex_declaration_get_parent(const struct wined3d_vertex_declaration *declaration) | |
| { | |
| TRACE("declaration %p.\n", declaration); | |
| return declaration->parent; | |
| } | |
| static BOOL declaration_element_valid_ffp(const struct wined3d_vertex_element *element) | |
| { | |
| switch(element->usage) | |
| { | |
| case WINED3D_DECL_USAGE_POSITION: | |
| case WINED3D_DECL_USAGE_POSITIONT: | |
| switch(element->format) | |
| { | |
| case WINED3DFMT_R32G32_FLOAT: | |
| case WINED3DFMT_R32G32B32_FLOAT: | |
| case WINED3DFMT_R32G32B32A32_FLOAT: | |
| case WINED3DFMT_R16G16_SINT: | |
| case WINED3DFMT_R16G16B16A16_SINT: | |
| case WINED3DFMT_R16G16_FLOAT: | |
| case WINED3DFMT_R16G16B16A16_FLOAT: | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| case WINED3D_DECL_USAGE_BLEND_WEIGHT: | |
| switch(element->format) | |
| { | |
| case WINED3DFMT_R32_FLOAT: | |
| case WINED3DFMT_R32G32_FLOAT: | |
| case WINED3DFMT_R32G32B32_FLOAT: | |
| case WINED3DFMT_R32G32B32A32_FLOAT: | |
| case WINED3DFMT_B8G8R8A8_UNORM: | |
| case WINED3DFMT_R8G8B8A8_UINT: | |
| case WINED3DFMT_R16G16_SINT: | |
| case WINED3DFMT_R16G16B16A16_SINT: | |
| case WINED3DFMT_R16G16_FLOAT: | |
| case WINED3DFMT_R16G16B16A16_FLOAT: | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| case WINED3D_DECL_USAGE_NORMAL: | |
| switch(element->format) | |
| { | |
| case WINED3DFMT_R32G32B32_FLOAT: | |
| case WINED3DFMT_R32G32B32A32_FLOAT: | |
| case WINED3DFMT_R16G16B16A16_SINT: | |
| case WINED3DFMT_R16G16B16A16_FLOAT: | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| case WINED3D_DECL_USAGE_TEXCOORD: | |
| switch(element->format) | |
| { | |
| case WINED3DFMT_R32_FLOAT: | |
| case WINED3DFMT_R32G32_FLOAT: | |
| case WINED3DFMT_R32G32B32_FLOAT: | |
| case WINED3DFMT_R32G32B32A32_FLOAT: | |
| case WINED3DFMT_R16G16_SINT: | |
| case WINED3DFMT_R16G16B16A16_SINT: | |
| case WINED3DFMT_R16G16_FLOAT: | |
| case WINED3DFMT_R16G16B16A16_FLOAT: | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| case WINED3D_DECL_USAGE_COLOR: | |
| switch(element->format) | |
| { | |
| case WINED3DFMT_R32G32B32_FLOAT: | |
| case WINED3DFMT_R32G32B32A32_FLOAT: | |
| case WINED3DFMT_B8G8R8A8_UNORM: | |
| case WINED3DFMT_R8G8B8A8_UINT: | |
| case WINED3DFMT_R16G16B16A16_SINT: | |
| case WINED3DFMT_R8G8B8A8_UNORM: | |
| case WINED3DFMT_R16G16B16A16_SNORM: | |
| case WINED3DFMT_R16G16B16A16_UNORM: | |
| case WINED3DFMT_R16G16B16A16_FLOAT: | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| default: | |
| return FALSE; | |
| } | |
| } | |
| static HRESULT vertexdeclaration_init(struct wined3d_vertex_declaration *declaration, | |
| struct wined3d_device *device, const struct wined3d_vertex_element *elements, UINT element_count, | |
| void *parent, const struct wined3d_parent_ops *parent_ops) | |
| { | |
| const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; | |
| unsigned int i; | |
| if (TRACE_ON(d3d_decl)) | |
| { | |
| for (i = 0; i < element_count; ++i) | |
| { | |
| dump_wined3d_vertex_element(elements + i); | |
| } | |
| } | |
| declaration->ref = 1; | |
| declaration->parent = parent; | |
| declaration->parent_ops = parent_ops; | |
| declaration->device = device; | |
| if (!(declaration->elements = wined3d_calloc(element_count, sizeof(*declaration->elements)))) | |
| { | |
| ERR("Failed to allocate elements memory.\n"); | |
| return E_OUTOFMEMORY; | |
| } | |
| declaration->element_count = element_count; | |
| /* Do some static analysis on the elements to make reading the | |
| * declaration more comfortable for the drawing code. */ | |
| for (i = 0; i < element_count; ++i) | |
| { | |
| struct wined3d_vertex_declaration_element *e = &declaration->elements[i]; | |
| e->format = wined3d_get_format(gl_info, elements[i].format, 0); | |
| e->ffp_valid = declaration_element_valid_ffp(&elements[i]); | |
| e->input_slot = elements[i].input_slot; | |
| e->offset = elements[i].offset; | |
| e->output_slot = elements[i].output_slot; | |
| e->input_slot_class = elements[i].input_slot_class; | |
| e->instance_data_step_rate = elements[i].instance_data_step_rate; | |
| e->method = elements[i].method; | |
| e->usage = elements[i].usage; | |
| e->usage_idx = elements[i].usage_idx; | |
| if (e->usage == WINED3D_DECL_USAGE_POSITIONT) | |
| declaration->position_transformed = TRUE; | |
| /* Find the streams used in the declaration. The vertex buffers have | |
| * to be loaded when drawing, but filter tesselation pseudo streams. */ | |
| if (e->input_slot >= MAX_STREAMS) continue; | |
| if (!e->format->gl_vtx_format) | |
| { | |
| FIXME("The application tries to use an unsupported format (%s), returning E_FAIL.\n", | |
| debug_d3dformat(elements[i].format)); | |
| HeapFree(GetProcessHeap(), 0, declaration->elements); | |
| return E_FAIL; | |
| } | |
| if (e->offset == WINED3D_APPEND_ALIGNED_ELEMENT) | |
| { | |
| const struct wined3d_vertex_declaration_element *prev; | |
| unsigned int j; | |
| e->offset = 0; | |
| for (j = 1; j <= i; ++j) | |
| { | |
| prev = &declaration->elements[i - j]; | |
| if (prev->input_slot == e->input_slot) | |
| { | |
| e->offset = (prev->offset + prev->format->byte_count + 3) & ~3; | |
| break; | |
| } | |
| } | |
| } | |
| if (e->offset & 0x3) | |
| { | |
| WARN("Declaration element %u is not 4 byte aligned(%u), returning E_FAIL.\n", i, e->offset); | |
| HeapFree(GetProcessHeap(), 0, declaration->elements); | |
| return E_FAIL; | |
| } | |
| if (elements[i].format == WINED3DFMT_R16G16_FLOAT || elements[i].format == WINED3DFMT_R16G16B16A16_FLOAT) | |
| { | |
| if (!gl_info->supported[ARB_HALF_FLOAT_VERTEX]) declaration->half_float_conv_needed = TRUE; | |
| } | |
| } | |
| return WINED3D_OK; | |
| } | |
| HRESULT CDECL wined3d_vertex_declaration_create(struct wined3d_device *device, | |
| const struct wined3d_vertex_element *elements, UINT element_count, void *parent, | |
| const struct wined3d_parent_ops *parent_ops, struct wined3d_vertex_declaration **declaration) | |
| { | |
| struct wined3d_vertex_declaration *object; | |
| HRESULT hr; | |
| TRACE("device %p, elements %p, element_count %u, parent %p, parent_ops %p, declaration %p.\n", | |
| device, elements, element_count, parent, parent_ops, declaration); | |
| object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); | |
| if(!object) | |
| return E_OUTOFMEMORY; | |
| hr = vertexdeclaration_init(object, device, elements, element_count, parent, parent_ops); | |
| if (FAILED(hr)) | |
| { | |
| WARN("Failed to initialize vertex declaration, hr %#x.\n", hr); | |
| HeapFree(GetProcessHeap(), 0, object); | |
| return hr; | |
| } | |
| TRACE("Created vertex declaration %p.\n", object); | |
| *declaration = object; | |
| return WINED3D_OK; | |
| } | |
| struct wined3d_fvf_convert_state | |
| { | |
| const struct wined3d_gl_info *gl_info; | |
| struct wined3d_vertex_element *elements; | |
| UINT offset; | |
| UINT idx; | |
| }; | |
| static void append_decl_element(struct wined3d_fvf_convert_state *state, | |
| enum wined3d_format_id format_id, enum wined3d_decl_usage usage, UINT usage_idx) | |
| { | |
| struct wined3d_vertex_element *elements = state->elements; | |
| const struct wined3d_format *format; | |
| UINT offset = state->offset; | |
| UINT idx = state->idx; | |
| elements[idx].format = format_id; | |
| elements[idx].input_slot = 0; | |
| elements[idx].offset = offset; | |
| elements[idx].output_slot = WINED3D_OUTPUT_SLOT_SEMANTIC; | |
| elements[idx].input_slot_class = WINED3D_INPUT_PER_VERTEX_DATA; | |
| elements[idx].instance_data_step_rate = 0; | |
| elements[idx].method = WINED3D_DECL_METHOD_DEFAULT; | |
| elements[idx].usage = usage; | |
| elements[idx].usage_idx = usage_idx; | |
| format = wined3d_get_format(state->gl_info, format_id, 0); | |
| state->offset += format->attribute_size; | |
| ++state->idx; | |
| } | |
| static unsigned int convert_fvf_to_declaration(const struct wined3d_gl_info *gl_info, | |
| DWORD fvf, struct wined3d_vertex_element **elements) | |
| { | |
| BOOL has_pos = !!(fvf & WINED3DFVF_POSITION_MASK); | |
| BOOL has_blend = (fvf & WINED3DFVF_XYZB5) > WINED3DFVF_XYZRHW; | |
| BOOL has_blend_idx = has_blend && | |
| (((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB5) || | |
| (fvf & WINED3DFVF_LASTBETA_D3DCOLOR) || | |
| (fvf & WINED3DFVF_LASTBETA_UBYTE4)); | |
| BOOL has_normal = !!(fvf & WINED3DFVF_NORMAL); | |
| BOOL has_psize = !!(fvf & WINED3DFVF_PSIZE); | |
| BOOL has_diffuse = !!(fvf & WINED3DFVF_DIFFUSE); | |
| BOOL has_specular = !!(fvf & WINED3DFVF_SPECULAR); | |
| DWORD num_textures = (fvf & WINED3DFVF_TEXCOUNT_MASK) >> WINED3DFVF_TEXCOUNT_SHIFT; | |
| DWORD texcoords = (fvf & 0xffff0000) >> 16; | |
| struct wined3d_fvf_convert_state state; | |
| unsigned int size; | |
| unsigned int idx; | |
| DWORD num_blends = 1 + (((fvf & WINED3DFVF_XYZB5) - WINED3DFVF_XYZB1) >> 1); | |
| if (has_blend_idx) num_blends--; | |
| /* Compute declaration size */ | |
| size = has_pos + (has_blend && num_blends > 0) + has_blend_idx + has_normal + | |
| has_psize + has_diffuse + has_specular + num_textures; | |
| state.gl_info = gl_info; | |
| if (!(state.elements = wined3d_calloc(size, sizeof(*state.elements)))) | |
| return ~0u; | |
| state.offset = 0; | |
| state.idx = 0; | |
| if (has_pos) | |
| { | |
| if (!has_blend && (fvf & WINED3DFVF_XYZRHW)) | |
| append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITIONT, 0); | |
| else if ((fvf & WINED3DFVF_XYZW) == WINED3DFVF_XYZW) | |
| append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0); | |
| else | |
| append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0); | |
| } | |
| if (has_blend && (num_blends > 0)) | |
| { | |
| if ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR)) | |
| append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); | |
| else | |
| { | |
| switch (num_blends) | |
| { | |
| case 1: | |
| append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); | |
| break; | |
| case 2: | |
| append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); | |
| break; | |
| case 3: | |
| append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); | |
| break; | |
| case 4: | |
| append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0); | |
| break; | |
| default: | |
| ERR("Unexpected amount of blend values: %u\n", num_blends); | |
| } | |
| } | |
| } | |
| if (has_blend_idx) | |
| { | |
| if ((fvf & WINED3DFVF_LASTBETA_UBYTE4) | |
| || ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR))) | |
| append_decl_element(&state, WINED3DFMT_R8G8B8A8_UINT, WINED3D_DECL_USAGE_BLEND_INDICES, 0); | |
| else if (fvf & WINED3DFVF_LASTBETA_D3DCOLOR) | |
| append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_INDICES, 0); | |
| else | |
| append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_INDICES, 0); | |
| } | |
| if (has_normal) | |
| append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_NORMAL, 0); | |
| if (has_psize) | |
| append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_PSIZE, 0); | |
| if (has_diffuse) | |
| append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_COLOR, 0); | |
| if (has_specular) | |
| append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_COLOR, 1); | |
| for (idx = 0; idx < num_textures; ++idx) | |
| { | |
| switch ((texcoords >> (idx * 2)) & 0x03) | |
| { | |
| case WINED3DFVF_TEXTUREFORMAT1: | |
| append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); | |
| break; | |
| case WINED3DFVF_TEXTUREFORMAT2: | |
| append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); | |
| break; | |
| case WINED3DFVF_TEXTUREFORMAT3: | |
| append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); | |
| break; | |
| case WINED3DFVF_TEXTUREFORMAT4: | |
| append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx); | |
| break; | |
| } | |
| } | |
| *elements = state.elements; | |
| return size; | |
| } | |
| HRESULT CDECL wined3d_vertex_declaration_create_from_fvf(struct wined3d_device *device, | |
| DWORD fvf, void *parent, const struct wined3d_parent_ops *parent_ops, | |
| struct wined3d_vertex_declaration **declaration) | |
| { | |
| struct wined3d_vertex_element *elements; | |
| unsigned int size; | |
| DWORD hr; | |
| TRACE("device %p, fvf %#x, parent %p, parent_ops %p, declaration %p.\n", | |
| device, fvf, parent, parent_ops, declaration); | |
| size = convert_fvf_to_declaration(&device->adapter->gl_info, fvf, &elements); | |
| if (size == ~0U) return E_OUTOFMEMORY; | |
| hr = wined3d_vertex_declaration_create(device, elements, size, parent, parent_ops, declaration); | |
| HeapFree(GetProcessHeap(), 0, elements); | |
| return hr; | |
| } |