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