vertexdeclaration.c 16.7 KB
Newer Older
1 2 3 4 5 6
/*
 * vertex declaration implementation
 *
 * Copyright 2002-2005 Raphael Junqueira
 * Copyright 2004 Jason Edmeades
 * Copyright 2004 Christian Costa
7
 * Copyright 2005 Oliver Stieber
8
 * Copyright 2009 Henri Verbeet for CodeWeavers
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
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 28 29 30
#include "wined3d_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(d3d_decl);

31 32
static void dump_wined3d_vertex_element(const struct wined3d_vertex_element *element)
{
33 34 35 36 37 38 39 40 41
    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);
42 43
}

44
ULONG CDECL wined3d_vertex_declaration_incref(struct wined3d_vertex_declaration *declaration)
45
{
46 47 48
    ULONG refcount = InterlockedIncrement(&declaration->ref);

    TRACE("%p increasing refcount to %u.\n", declaration, refcount);
49

50
    return refcount;
51 52
}

53 54 55 56 57 58 59 60
static void wined3d_vertex_declaration_destroy_object(void *object)
{
    struct wined3d_vertex_declaration *declaration = object;

    HeapFree(GetProcessHeap(), 0, declaration->elements);
    HeapFree(GetProcessHeap(), 0, declaration);
}

61 62 63 64 65 66 67
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)
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 75
    return refcount;
}
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 89
        case WINED3D_DECL_USAGE_POSITION:
        case WINED3D_DECL_USAGE_POSITIONT:
90
            switch(element->format)
91
            {
92 93 94 95 96 97 98
                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:
99 100 101 102 103
                    return TRUE;
                default:
                    return FALSE;
            }

104
        case WINED3D_DECL_USAGE_BLEND_WEIGHT:
105
            switch(element->format)
106
            {
107 108 109 110
                case WINED3DFMT_R32_FLOAT:
                case WINED3DFMT_R32G32_FLOAT:
                case WINED3DFMT_R32G32B32_FLOAT:
                case WINED3DFMT_R32G32B32A32_FLOAT:
111
                case WINED3DFMT_B8G8R8A8_UNORM:
112 113 114 115 116
                case WINED3DFMT_R8G8B8A8_UINT:
                case WINED3DFMT_R16G16_SINT:
                case WINED3DFMT_R16G16B16A16_SINT:
                case WINED3DFMT_R16G16_FLOAT:
                case WINED3DFMT_R16G16B16A16_FLOAT:
117 118 119 120 121
                    return TRUE;
                default:
                    return FALSE;
            }

122
        case WINED3D_DECL_USAGE_NORMAL:
123
            switch(element->format)
124
            {
125 126 127 128
                case WINED3DFMT_R32G32B32_FLOAT:
                case WINED3DFMT_R32G32B32A32_FLOAT:
                case WINED3DFMT_R16G16B16A16_SINT:
                case WINED3DFMT_R16G16B16A16_FLOAT:
129 130 131 132 133
                    return TRUE;
                default:
                    return FALSE;
            }

134
        case WINED3D_DECL_USAGE_TEXCOORD:
135
            switch(element->format)
136
            {
137 138 139 140 141 142 143 144
                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:
145 146 147 148 149
                    return TRUE;
                default:
                    return FALSE;
            }

150
        case WINED3D_DECL_USAGE_COLOR:
151
            switch(element->format)
152
            {
153 154
                case WINED3DFMT_R32G32B32_FLOAT:
                case WINED3DFMT_R32G32B32A32_FLOAT:
155
                case WINED3DFMT_B8G8R8A8_UNORM:
156 157 158 159 160 161
                case WINED3DFMT_R8G8B8A8_UINT:
                case WINED3DFMT_R16G16B16A16_SINT:
                case WINED3DFMT_R8G8B8A8_UNORM:
                case WINED3DFMT_R16G16B16A16_SNORM:
                case WINED3DFMT_R16G16B16A16_UNORM:
                case WINED3DFMT_R16G16B16A16_FLOAT:
162 163 164 165 166 167 168 169 170 171
                    return TRUE;
                default:
                    return FALSE;
            }

        default:
            return FALSE;
    }
}

172
static HRESULT vertexdeclaration_init(struct wined3d_vertex_declaration *declaration,
173
        struct wined3d_device *device, const struct wined3d_vertex_element *elements, UINT element_count,
174
        void *parent, const struct wined3d_parent_ops *parent_ops)
175 176 177
{
    const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
    unsigned int i;
178

179 180 181 182
    if (TRACE_ON(d3d_decl))
    {
        for (i = 0; i < element_count; ++i)
        {
183
            dump_wined3d_vertex_element(elements + i);
184
        }
185
    }
186

187 188
    declaration->ref = 1;
    declaration->parent = parent;
189
    declaration->parent_ops = parent_ops;
190
    declaration->device = device;
191
    if (!(declaration->elements = wined3d_calloc(element_count, sizeof(*declaration->elements))))
192
    {
193 194
        ERR("Failed to allocate elements memory.\n");
        return E_OUTOFMEMORY;
195
    }
196
    declaration->element_count = element_count;
197

198 199 200 201 202
    /* 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];
203

204
        e->format = wined3d_get_format(gl_info, elements[i].format, 0);
205
        e->ffp_valid = declaration_element_valid_ffp(&elements[i]);
206 207 208
        e->input_slot = elements[i].input_slot;
        e->offset = elements[i].offset;
        e->output_slot = elements[i].output_slot;
209 210
        e->input_slot_class = elements[i].input_slot_class;
        e->instance_data_step_rate = elements[i].instance_data_step_rate;
211 212 213
        e->method = elements[i].method;
        e->usage = elements[i].usage;
        e->usage_idx = elements[i].usage_idx;
214

215 216
        if (e->usage == WINED3D_DECL_USAGE_POSITIONT)
            declaration->position_transformed = TRUE;
217

218 219
        /* Find the streams used in the declaration. The vertex buffers have
         * to be loaded when drawing, but filter tesselation pseudo streams. */
220
        if (e->input_slot >= MAX_STREAMS) continue;
221

222
        if (!e->format->gl_vtx_format)
223
        {
224
            FIXME("The application tries to use an unsupported format (%s), returning E_FAIL.\n",
225
                    debug_d3dformat(elements[i].format));
226
            HeapFree(GetProcessHeap(), 0, declaration->elements);
227 228 229
            return E_FAIL;
        }

230 231
        if (e->offset == WINED3D_APPEND_ALIGNED_ELEMENT)
        {
232 233 234 235 236
            const struct wined3d_vertex_declaration_element *prev;
            unsigned int j;

            e->offset = 0;
            for (j = 1; j <= i; ++j)
237
            {
238 239 240 241 242 243
                prev = &declaration->elements[i - j];
                if (prev->input_slot == e->input_slot)
                {
                    e->offset = (prev->offset + prev->format->byte_count + 3) & ~3;
                    break;
                }
244 245 246
            }
        }

247 248
        if (e->offset & 0x3)
        {
249 250
            WARN("Declaration element %u is not 4 byte aligned(%u), returning E_FAIL.\n", i, e->offset);
            HeapFree(GetProcessHeap(), 0, declaration->elements);
251 252 253
            return E_FAIL;
        }

254
        if (elements[i].format == WINED3DFMT_R16G16_FLOAT || elements[i].format == WINED3DFMT_R16G16B16A16_FLOAT)
255
        {
256
            if (!gl_info->supported[ARB_HALF_FLOAT_VERTEX]) declaration->half_float_conv_needed = TRUE;
257
        }
258 259
    }

260
    return WINED3D_OK;
261
}
262

263
HRESULT CDECL wined3d_vertex_declaration_create(struct wined3d_device *device,
264
        const struct wined3d_vertex_element *elements, UINT element_count, void *parent,
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
        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;
294
    struct wined3d_vertex_element *elements;
295 296 297 298 299
    UINT offset;
    UINT idx;
};

static void append_decl_element(struct wined3d_fvf_convert_state *state,
300
        enum wined3d_format_id format_id, enum wined3d_decl_usage usage, UINT usage_idx)
301
{
302
    struct wined3d_vertex_element *elements = state->elements;
303 304 305 306 307 308 309
    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;
310
    elements[idx].output_slot = WINED3D_OUTPUT_SLOT_SEMANTIC;
311 312
    elements[idx].input_slot_class = WINED3D_INPUT_PER_VERTEX_DATA;
    elements[idx].instance_data_step_rate = 0;
313
    elements[idx].method = WINED3D_DECL_METHOD_DEFAULT;
314 315 316
    elements[idx].usage = usage;
    elements[idx].usage_idx = usage_idx;

317
    format = wined3d_get_format(state->gl_info, format_id, 0);
318
    state->offset += format->attribute_size;
319 320 321 322
    ++state->idx;
}

static unsigned int convert_fvf_to_declaration(const struct wined3d_gl_info *gl_info,
323
        DWORD fvf, struct wined3d_vertex_element **elements)
324 325 326 327 328 329 330 331 332 333 334 335 336
{
    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;
337
    DWORD texcoords = (fvf & 0xffff0000) >> 16;
338 339 340 341 342 343 344 345 346 347 348
    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;
349 350
    if (!(state.elements = wined3d_calloc(size, sizeof(*state.elements))))
        return ~0u;
351 352 353 354 355 356
    state.offset = 0;
    state.idx = 0;

    if (has_pos)
    {
        if (!has_blend && (fvf & WINED3DFVF_XYZRHW))
357
            append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITIONT, 0);
358
        else if ((fvf & WINED3DFVF_XYZW) == WINED3DFVF_XYZW)
359
            append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0);
360
        else
361
            append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_POSITION, 0);
362 363 364 365 366
    }

    if (has_blend && (num_blends > 0))
    {
        if ((fvf & WINED3DFVF_XYZB5) == WINED3DFVF_XYZB2 && (fvf & WINED3DFVF_LASTBETA_D3DCOLOR))
367
            append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
368 369 370 371 372
        else
        {
            switch (num_blends)
            {
                case 1:
373
                    append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
374 375
                    break;
                case 2:
376
                    append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
377 378
                    break;
                case 3:
379
                    append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
380 381
                    break;
                case 4:
382
                    append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_BLEND_WEIGHT, 0);
383 384 385 386 387 388 389 390 391 392 393
                    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)))
394
            append_decl_element(&state, WINED3DFMT_R8G8B8A8_UINT, WINED3D_DECL_USAGE_BLEND_INDICES, 0);
395
        else if (fvf & WINED3DFVF_LASTBETA_D3DCOLOR)
396
            append_decl_element(&state, WINED3DFMT_B8G8R8A8_UNORM, WINED3D_DECL_USAGE_BLEND_INDICES, 0);
397
        else
398
            append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_BLEND_INDICES, 0);
399 400
    }

401 402 403 404 405 406 407 408
    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);
409 410 411 412 413 414

    for (idx = 0; idx < num_textures; ++idx)
    {
        switch ((texcoords >> (idx * 2)) & 0x03)
        {
            case WINED3DFVF_TEXTUREFORMAT1:
415
                append_decl_element(&state, WINED3DFMT_R32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
416 417
                break;
            case WINED3DFVF_TEXTUREFORMAT2:
418
                append_decl_element(&state, WINED3DFMT_R32G32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
419 420
                break;
            case WINED3DFVF_TEXTUREFORMAT3:
421
                append_decl_element(&state, WINED3DFMT_R32G32B32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
422 423
                break;
            case WINED3DFVF_TEXTUREFORMAT4:
424
                append_decl_element(&state, WINED3DFMT_R32G32B32A32_FLOAT, WINED3D_DECL_USAGE_TEXCOORD, idx);
425 426 427 428 429 430 431 432
                break;
        }
    }

    *elements = state.elements;
    return size;
}

433
HRESULT CDECL wined3d_vertex_declaration_create_from_fvf(struct wined3d_device *device,
434 435 436
        DWORD fvf, void *parent, const struct wined3d_parent_ops *parent_ops,
        struct wined3d_vertex_declaration **declaration)
{
437
    struct wined3d_vertex_element *elements;
438 439 440 441 442 443 444 445 446
    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;

447
    hr = wined3d_vertex_declaration_create(device, elements, size, parent, parent_ops, declaration);
448 449 450
    HeapFree(GetProcessHeap(), 0, elements);
    return hr;
}