vertexdeclaration.c 15.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * IDirect3DVertexDeclaration9 implementation
 *
 * Copyright 2002-2003 Raphael Junqueira
 *                     Jason Edmeades
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23
 */

#include "d3d9_private.h"

24
WINE_DEFAULT_DEBUG_CHANNEL(d3d9);
25

26 27
static const struct
{
28
    enum wined3d_format_id format;
29 30
    unsigned int component_count;
    unsigned int component_size;
31 32 33
}
d3d_dtype_lookup[] =
{
34 35 36 37 38 39 40 41 42 43 44 45 46
    /* D3DDECLTYPE_FLOAT1    */ {WINED3DFMT_R32_FLOAT,          1, sizeof(float)},
    /* D3DDECLTYPE_FLOAT2    */ {WINED3DFMT_R32G32_FLOAT,       2, sizeof(float)},
    /* D3DDECLTYPE_FLOAT3    */ {WINED3DFMT_R32G32B32_FLOAT,    3, sizeof(float)},
    /* D3DDECLTYPE_FLOAT4    */ {WINED3DFMT_R32G32B32A32_FLOAT, 4, sizeof(float)},
    /* D3DDECLTYPE_D3DCOLOR  */ {WINED3DFMT_B8G8R8A8_UNORM,     4, sizeof(BYTE)},
    /* D3DDECLTYPE_UBYTE4    */ {WINED3DFMT_R8G8B8A8_UINT,      4, sizeof(BYTE)},
    /* D3DDECLTYPE_SHORT2    */ {WINED3DFMT_R16G16_SINT,        2, sizeof(short int)},
    /* D3DDECLTYPE_SHORT4    */ {WINED3DFMT_R16G16B16A16_SINT,  4, sizeof(short int)},
    /* D3DDECLTYPE_UBYTE4N   */ {WINED3DFMT_R8G8B8A8_UNORM,     4, sizeof(BYTE)},
    /* D3DDECLTYPE_SHORT2N   */ {WINED3DFMT_R16G16_SNORM,       2, sizeof(short int)},
    /* D3DDECLTYPE_SHORT4N   */ {WINED3DFMT_R16G16B16A16_SNORM, 4, sizeof(short int)},
    /* D3DDECLTYPE_USHORT2N  */ {WINED3DFMT_R16G16_UNORM,       2, sizeof(short int)},
    /* D3DDECLTYPE_USHORT4N  */ {WINED3DFMT_R16G16B16A16_UNORM, 4, sizeof(short int)},
47 48
    /* D3DDECLTYPE_UDEC3     */ {WINED3DFMT_R10G10B10X2_UINT,   3, sizeof(short int)},
    /* D3DDECLTYPE_DEC3N     */ {WINED3DFMT_R10G10B10X2_SNORM,  3, sizeof(short int)},
49 50 51
    /* D3DDECLTYPE_FLOAT16_2 */ {WINED3DFMT_R16G16_FLOAT,       2, sizeof(short int)},
    /* D3DDECLTYPE_FLOAT16_4 */ {WINED3DFMT_R16G16B16A16_FLOAT, 4, sizeof(short int)}
};
52

53
static inline struct d3d9_vertex_declaration *impl_from_IDirect3DVertexDeclaration9(IDirect3DVertexDeclaration9 *iface)
54
{
55
    return CONTAINING_RECORD(iface, struct d3d9_vertex_declaration, IDirect3DVertexDeclaration9_iface);
56 57
}

58 59 60 61 62 63 64 65 66 67 68 69 70 71
HRESULT vdecl_convert_fvf(
    DWORD fvf,
    D3DVERTEXELEMENT9** ppVertexElements) {

    unsigned int idx, idx2;
    unsigned int offset;
    BOOL has_pos = (fvf & D3DFVF_POSITION_MASK) != 0;
    BOOL has_blend = (fvf & D3DFVF_XYZB5) > D3DFVF_XYZRHW;
    BOOL has_blend_idx = has_blend &&
       (((fvf & D3DFVF_XYZB5) == D3DFVF_XYZB5) ||
        (fvf & D3DFVF_LASTBETA_D3DCOLOR) ||
        (fvf & D3DFVF_LASTBETA_UBYTE4));
    BOOL has_normal = (fvf & D3DFVF_NORMAL) != 0;
    BOOL has_psize = (fvf & D3DFVF_PSIZE) != 0;
72

73 74 75 76
    BOOL has_diffuse = (fvf & D3DFVF_DIFFUSE) != 0;
    BOOL has_specular = (fvf & D3DFVF_SPECULAR) !=0;

    DWORD num_textures = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
77
    DWORD texcoords = (fvf & 0xFFFF0000) >> 16;
78 79 80 81 82 83 84 85 86 87 88 89 90

    D3DVERTEXELEMENT9 end_element = D3DDECL_END();
    D3DVERTEXELEMENT9 *elements = NULL;

    unsigned int size;
    DWORD num_blends = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_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 + 1;

    /* convert the declaration */
91 92
    if (!(elements = heap_alloc(size * sizeof(*elements))))
        return D3DERR_OUTOFVIDEOMEMORY;
93

94
    elements[size-1] = end_element;
95 96 97 98 99 100
    idx = 0;
    if (has_pos) {
        if (!has_blend && (fvf & D3DFVF_XYZRHW)) {
            elements[idx].Type = D3DDECLTYPE_FLOAT4;
            elements[idx].Usage = D3DDECLUSAGE_POSITIONT;
        }
101 102 103 104
        else if (!has_blend && (fvf & D3DFVF_XYZW) == D3DFVF_XYZW) {
            elements[idx].Type = D3DDECLTYPE_FLOAT4;
            elements[idx].Usage = D3DDECLUSAGE_POSITION;
        }
105 106 107 108 109 110 111 112 113 114
        else {
            elements[idx].Type = D3DDECLTYPE_FLOAT3;
            elements[idx].Usage = D3DDECLUSAGE_POSITION;
        }
        elements[idx].UsageIndex = 0;
        idx++;
    }
    if (has_blend && (num_blends > 0)) {
        if (((fvf & D3DFVF_XYZB5) == D3DFVF_XYZB2) && (fvf & D3DFVF_LASTBETA_D3DCOLOR))
            elements[idx].Type = D3DDECLTYPE_D3DCOLOR;
115 116 117 118 119 120 121 122 123 124
        else {
            switch(num_blends) {
                case 1: elements[idx].Type = D3DDECLTYPE_FLOAT1; break;
                case 2: elements[idx].Type = D3DDECLTYPE_FLOAT2; break;
                case 3: elements[idx].Type = D3DDECLTYPE_FLOAT3; break;
                case 4: elements[idx].Type = D3DDECLTYPE_FLOAT4; break;
                default:
                    ERR("Unexpected amount of blend values: %u\n", num_blends);
            }
        }
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
        elements[idx].Usage = D3DDECLUSAGE_BLENDWEIGHT;
        elements[idx].UsageIndex = 0;
        idx++;
    }
    if (has_blend_idx) {
        if (fvf & D3DFVF_LASTBETA_UBYTE4 ||
            (((fvf & D3DFVF_XYZB5) == D3DFVF_XYZB2) && (fvf & D3DFVF_LASTBETA_D3DCOLOR)))
            elements[idx].Type = D3DDECLTYPE_UBYTE4;
        else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
            elements[idx].Type = D3DDECLTYPE_D3DCOLOR;
        else
            elements[idx].Type = D3DDECLTYPE_FLOAT1;
        elements[idx].Usage = D3DDECLUSAGE_BLENDINDICES;
        elements[idx].UsageIndex = 0;
        idx++;
    }
    if (has_normal) {
        elements[idx].Type = D3DDECLTYPE_FLOAT3;
        elements[idx].Usage = D3DDECLUSAGE_NORMAL;
        elements[idx].UsageIndex = 0;
        idx++;
    }
    if (has_psize) {
        elements[idx].Type = D3DDECLTYPE_FLOAT1;
        elements[idx].Usage = D3DDECLUSAGE_PSIZE;
        elements[idx].UsageIndex = 0;
        idx++;
    }
    if (has_diffuse) {
        elements[idx].Type = D3DDECLTYPE_D3DCOLOR;
        elements[idx].Usage = D3DDECLUSAGE_COLOR;
        elements[idx].UsageIndex = 0;
        idx++;
    }
    if (has_specular) {
        elements[idx].Type = D3DDECLTYPE_D3DCOLOR;
        elements[idx].Usage = D3DDECLUSAGE_COLOR;
        elements[idx].UsageIndex = 1;
        idx++;
    }
    for (idx2 = 0; idx2 < num_textures; idx2++) {
        unsigned int numcoords = (texcoords >> (idx2*2)) & 0x03;
        switch (numcoords) {
            case D3DFVF_TEXTUREFORMAT1:
                elements[idx].Type = D3DDECLTYPE_FLOAT1;
                break;
            case D3DFVF_TEXTUREFORMAT2:
                elements[idx].Type = D3DDECLTYPE_FLOAT2;
                break;
            case D3DFVF_TEXTUREFORMAT3:
                elements[idx].Type = D3DDECLTYPE_FLOAT3;
                break;
            case D3DFVF_TEXTUREFORMAT4:
                elements[idx].Type = D3DDECLTYPE_FLOAT4;
                break;
        }
        elements[idx].Usage = D3DDECLUSAGE_TEXCOORD;
        elements[idx].UsageIndex = idx2;
        idx++;
    }

    /* Now compute offsets, and initialize the rest of the fields */
    for (idx = 0, offset = 0; idx < size-1; idx++) {
        elements[idx].Stream = 0;
        elements[idx].Method = D3DDECLMETHOD_DEFAULT;
        elements[idx].Offset = offset;
191 192
        offset += d3d_dtype_lookup[elements[idx].Type].component_count
                * d3d_dtype_lookup[elements[idx].Type].component_size;
193 194 195 196 197 198
    }

    *ppVertexElements = elements;
    return D3D_OK;
}

199
static HRESULT WINAPI d3d9_vertex_declaration_QueryInterface(IDirect3DVertexDeclaration9 *iface,
200 201 202 203 204 205 206
        REFIID riid, void **out)
{
    TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);

    if (IsEqualGUID(riid, &IID_IDirect3DVertexDeclaration9)
            || IsEqualGUID(riid, &IID_IUnknown))
    {
207
        IDirect3DVertexDeclaration9_AddRef(iface);
208
        *out = iface;
H. Verbeet's avatar
H. Verbeet committed
209
        return S_OK;
210 211
    }

212 213 214
    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));

    *out = NULL;
215 216 217
    return E_NOINTERFACE;
}

218
static ULONG WINAPI d3d9_vertex_declaration_AddRef(IDirect3DVertexDeclaration9 *iface)
219
{
220 221
    struct d3d9_vertex_declaration *declaration = impl_from_IDirect3DVertexDeclaration9(iface);
    ULONG refcount = InterlockedIncrement(&declaration->refcount);
222

223
    TRACE("%p increasing refcount to %u.\n", iface, refcount);
224

225
    if (refcount == 1)
226
    {
227 228
        IDirect3DDevice9Ex_AddRef(declaration->parent_device);
        wined3d_vertex_declaration_incref(declaration->wined3d_declaration);
229 230
    }

231
    return refcount;
232 233
}

234
static ULONG WINAPI d3d9_vertex_declaration_Release(IDirect3DVertexDeclaration9 *iface)
235
{
236 237
    struct d3d9_vertex_declaration *declaration = impl_from_IDirect3DVertexDeclaration9(iface);
    ULONG refcount = InterlockedDecrement(&declaration->refcount);
238

239
    TRACE("%p decreasing refcount to %u.\n", iface, refcount);
240

241
    if (!refcount)
242
    {
243 244
        IDirect3DDevice9Ex *parent_device = declaration->parent_device;
        wined3d_vertex_declaration_decref(declaration->wined3d_declaration);
245 246

        /* Release the device last, as it may cause the device to be destroyed. */
247
        IDirect3DDevice9Ex_Release(parent_device);
248
    }
249 250

    return refcount;
251 252
}

253
static HRESULT WINAPI d3d9_vertex_declaration_GetDevice(IDirect3DVertexDeclaration9 *iface, IDirect3DDevice9 **device)
254
{
255
    struct d3d9_vertex_declaration *declaration = impl_from_IDirect3DVertexDeclaration9(iface);
256

257
    TRACE("iface %p, device %p.\n", iface, device);
258

259
    *device = (IDirect3DDevice9 *)declaration->parent_device;
260
    IDirect3DDevice9_AddRef(*device);
261

262 263 264
    TRACE("Returning device %p.\n", *device);

    return D3D_OK;
265 266
}

267
static HRESULT WINAPI d3d9_vertex_declaration_GetDeclaration(IDirect3DVertexDeclaration9 *iface,
268 269
        D3DVERTEXELEMENT9 *elements, UINT *element_count)
{
270
    struct d3d9_vertex_declaration *declaration = impl_from_IDirect3DVertexDeclaration9(iface);
271

272
    TRACE("iface %p, elements %p, element_count %p.\n", iface, elements, element_count);
273

274
    *element_count = declaration->element_count;
275

276 277
    /* Passing a NULL elements is used to just retrieve the number of elements */
    if (!elements)
278 279
        return D3D_OK;

280 281
    TRACE("Copying %p to %p.\n", declaration->elements, elements);
    memcpy(elements, declaration->elements, sizeof(*declaration->elements) * declaration->element_count);
282 283

    return D3D_OK;
284
}
285

286
static const struct IDirect3DVertexDeclaration9Vtbl d3d9_vertex_declaration_vtbl =
287
{
288
    /* IUnknown */
289 290 291
    d3d9_vertex_declaration_QueryInterface,
    d3d9_vertex_declaration_AddRef,
    d3d9_vertex_declaration_Release,
292
    /* IDirect3DVertexDeclaration9 */
293 294
    d3d9_vertex_declaration_GetDevice,
    d3d9_vertex_declaration_GetDeclaration,
295 296
};

297
struct d3d9_vertex_declaration *unsafe_impl_from_IDirect3DVertexDeclaration9(IDirect3DVertexDeclaration9 *iface)
298 299 300
{
    if (!iface)
        return NULL;
301 302
    assert(iface->lpVtbl == &d3d9_vertex_declaration_vtbl);
    return CONTAINING_RECORD(iface, struct d3d9_vertex_declaration, IDirect3DVertexDeclaration9_iface);
303 304
}

305 306
static void STDMETHODCALLTYPE d3d9_vertexdeclaration_wined3d_object_destroyed(void *parent)
{
307
    struct d3d9_vertex_declaration *declaration = parent;
308 309
    heap_free(declaration->elements);
    heap_free(declaration);
310 311 312 313 314 315 316
}

static const struct wined3d_parent_ops d3d9_vertexdeclaration_wined3d_parent_ops =
{
    d3d9_vertexdeclaration_wined3d_object_destroyed,
};

317
static HRESULT convert_to_wined3d_declaration(const D3DVERTEXELEMENT9 *d3d9_elements,
318
        struct wined3d_vertex_element **wined3d_elements, UINT *element_count, DWORD *stream_map)
319
{
320
    const D3DVERTEXELEMENT9* element;
321
    UINT count = 1;
322
    UINT i;
323

Andrey Gusev's avatar
Andrey Gusev committed
324
    TRACE("d3d9_elements %p, wined3d_elements %p, element_count %p\n", d3d9_elements, wined3d_elements, element_count);
325

326 327
    *stream_map = 0;

328
    element = d3d9_elements;
329
    while (element++->Stream != 0xff && count++ < 128);
330

331
    if (count == 128) return E_FAIL;
332

333
    /* Skip the END element */
334
    --count;
335

336 337
    if (!(*wined3d_elements = heap_alloc(count * sizeof(**wined3d_elements))))
    {
338
        FIXME("Memory allocation failed\n");
339
        return D3DERR_OUTOFVIDEOMEMORY;
340 341
    }

342 343
    for (i = 0; i < count; ++i)
    {
344
        if (d3d9_elements[i].Type >= ARRAY_SIZE(d3d_dtype_lookup))
345 346
        {
            WARN("Invalid element type %#x.\n", d3d9_elements[i].Type);
347
            heap_free(*wined3d_elements);
348 349 350 351 352
            return E_FAIL;
        }
        (*wined3d_elements)[i].format = d3d_dtype_lookup[d3d9_elements[i].Type].format;
        (*wined3d_elements)[i].input_slot = d3d9_elements[i].Stream;
        (*wined3d_elements)[i].offset = d3d9_elements[i].Offset;
353
        (*wined3d_elements)[i].output_slot = WINED3D_OUTPUT_SLOT_SEMANTIC;
354 355
        (*wined3d_elements)[i].input_slot_class = WINED3D_INPUT_PER_VERTEX_DATA;
        (*wined3d_elements)[i].instance_data_step_rate = 0;
356 357 358
        (*wined3d_elements)[i].method = d3d9_elements[i].Method;
        (*wined3d_elements)[i].usage = d3d9_elements[i].Usage;
        (*wined3d_elements)[i].usage_idx = d3d9_elements[i].UsageIndex;
359
        *stream_map |= 1u << d3d9_elements[i].Stream;
360 361
    }

362 363 364
    *element_count = count;

    return D3D_OK;
365
}
366

367
static HRESULT vertexdeclaration_init(struct d3d9_vertex_declaration *declaration,
368
        struct d3d9_device *device, const D3DVERTEXELEMENT9 *elements)
369
{
370
    struct wined3d_vertex_element *wined3d_elements;
371
    UINT wined3d_element_count;
372
    UINT element_count;
373
    HRESULT hr;
374

375 376
    hr = convert_to_wined3d_declaration(elements, &wined3d_elements, &wined3d_element_count,
            &declaration->stream_map);
377
    if (FAILED(hr))
378
    {
379
        WARN("Failed to create wined3d vertex declaration elements, hr %#x.\n", hr);
380
        return hr;
381 382
    }

383 384
    declaration->IDirect3DVertexDeclaration9_iface.lpVtbl = &d3d9_vertex_declaration_vtbl;
    declaration->refcount = 1;
385

386
    element_count = wined3d_element_count + 1;
387
    if (!(declaration->elements = heap_alloc(element_count * sizeof(*declaration->elements))))
388
    {
389
        heap_free(wined3d_elements);
390
        ERR("Failed to allocate vertex declaration elements memory.\n");
391 392
        return D3DERR_OUTOFVIDEOMEMORY;
    }
393 394
    memcpy(declaration->elements, elements, element_count * sizeof(*elements));
    declaration->element_count = element_count;
395

396
    wined3d_mutex_lock();
397
    hr = wined3d_vertex_declaration_create(device->wined3d_device, wined3d_elements, wined3d_element_count,
398
            declaration, &d3d9_vertexdeclaration_wined3d_parent_ops, &declaration->wined3d_declaration);
399
    wined3d_mutex_unlock();
400
    heap_free(wined3d_elements);
401 402
    if (FAILED(hr))
    {
403
        heap_free(declaration->elements);
404
        WARN("Failed to create wined3d vertex declaration, hr %#x.\n", hr);
405 406
        if (hr == E_INVALIDARG)
            hr = E_FAIL;
407
        return hr;
408
    }
409

410 411
    declaration->parent_device = &device->IDirect3DDevice9Ex_iface;
    IDirect3DDevice9Ex_AddRef(declaration->parent_device);
412 413

    return D3D_OK;
414
}
415

416
HRESULT d3d9_vertex_declaration_create(struct d3d9_device *device,
417
        const D3DVERTEXELEMENT9 *elements, struct d3d9_vertex_declaration **declaration)
418
{
419
    struct d3d9_vertex_declaration *object;
420 421
    HRESULT hr;

422
    if (!(object = heap_alloc_zero(sizeof(*object))))
423 424 425 426 427 428
        return E_OUTOFMEMORY;

    hr = vertexdeclaration_init(object, device, elements);
    if (FAILED(hr))
    {
        WARN("Failed to initialize vertex declaration, hr %#x.\n", hr);
429
        heap_free(object);
430 431 432 433 434 435 436 437
        return hr;
    }

    TRACE("Created vertex declaration %p.\n", object);
    *declaration = object;

    return D3D_OK;
}