vertexdeclaration.c 15.1 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 24
 */

#include "config.h"
#include "d3d9_private.h"

25
WINE_DEFAULT_DEBUG_CHANNEL(d3d9);
26

27 28
typedef struct _D3DDECLTYPE_INFO {
    D3DDECLTYPE d3dType;
29
    enum wined3d_format_id format;
30 31 32 33
    int         size;
    int         typesize;
} D3DDECLTYPE_INFO;

34
static D3DDECLTYPE_INFO const d3d_dtype_lookup[D3DDECLTYPE_UNUSED] = {
35 36 37 38
   {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)},
39
   {D3DDECLTYPE_D3DCOLOR,  WINED3DFMT_B8G8R8A8_UNORM,     4, sizeof(BYTE)},
40 41 42 43 44 45 46 47 48 49 50 51
   {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)},
   {D3DDECLTYPE_UDEC3,     WINED3DFMT_R10G10B10A2_UINT,   3, sizeof(short int)},
   {D3DDECLTYPE_DEC3N,     WINED3DFMT_R10G10B10A2_SNORM,  3, sizeof(short int)},
   {D3DDECLTYPE_FLOAT16_2, WINED3DFMT_R16G16_FLOAT,       2, sizeof(short int)},
   {D3DDECLTYPE_FLOAT16_4, WINED3DFMT_R16G16B16A16_FLOAT, 4, sizeof(short int)}};
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

#define D3D_DECL_SIZE(type)          d3d_dtype_lookup[type].size
#define D3D_DECL_TYPESIZE(type)      d3d_dtype_lookup[type].typesize

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;
70

71 72 73 74
    BOOL has_diffuse = (fvf & D3DFVF_DIFFUSE) != 0;
    BOOL has_specular = (fvf & D3DFVF_SPECULAR) !=0;

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

    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 */
    elements = HeapAlloc(GetProcessHeap(), 0, size * sizeof(D3DVERTEXELEMENT9));
90
    if (!elements) return D3DERR_OUTOFVIDEOMEMORY;
91

92
    elements[size-1] = end_element;
93 94 95 96 97 98
    idx = 0;
    if (has_pos) {
        if (!has_blend && (fvf & D3DFVF_XYZRHW)) {
            elements[idx].Type = D3DDECLTYPE_FLOAT4;
            elements[idx].Usage = D3DDECLUSAGE_POSITIONT;
        }
99 100 101 102
        else if (!has_blend && (fvf & D3DFVF_XYZW) == D3DFVF_XYZW) {
            elements[idx].Type = D3DDECLTYPE_FLOAT4;
            elements[idx].Usage = D3DDECLUSAGE_POSITION;
        }
103 104 105 106 107 108 109 110 111 112
        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;
113 114 115 116 117 118 119 120 121 122
        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);
            }
        }
123 124 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 191 192 193 194 195
        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;
        offset += D3D_DECL_SIZE(elements[idx].Type) * D3D_DECL_TYPESIZE(elements[idx].Type);
    }

    *ppVertexElements = elements;
    return D3D_OK;
}

196
/* IDirect3DVertexDeclaration9 IUnknown parts follow: */
197
static HRESULT WINAPI IDirect3DVertexDeclaration9Impl_QueryInterface(LPDIRECT3DVERTEXDECLARATION9 iface, REFIID riid, LPVOID* ppobj) {
198
    IDirect3DVertexDeclaration9Impl *This = (IDirect3DVertexDeclaration9Impl *)iface;
199

Henri Verbeet's avatar
Henri Verbeet committed
200 201
    TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), ppobj);

202 203
    if (IsEqualGUID(riid, &IID_IUnknown)
        || IsEqualGUID(riid, &IID_IDirect3DVertexDeclaration9)) {
204
        IDirect3DVertexDeclaration9_AddRef(iface);
205
        *ppobj = This;
H. Verbeet's avatar
H. Verbeet committed
206
        return S_OK;
207 208 209
    }

    WARN("(%p)->(%s,%p),not found\n", This, debugstr_guid(riid), ppobj);
H. Verbeet's avatar
H. Verbeet committed
210
    *ppobj = NULL;
211 212 213
    return E_NOINTERFACE;
}

214
static ULONG WINAPI IDirect3DVertexDeclaration9Impl_AddRef(LPDIRECT3DVERTEXDECLARATION9 iface) {
215
    IDirect3DVertexDeclaration9Impl *This = (IDirect3DVertexDeclaration9Impl *)iface;
216 217
    ULONG ref = InterlockedIncrement(&This->ref);

Henri Verbeet's avatar
Henri Verbeet committed
218
    TRACE("%p increasing refcount to %u.\n", iface, ref);
219

220
    if(ref == 1) {
221
        IDirect3DDevice9Ex_AddRef(This->parentDevice);
222 223 224
        if (!This->convFVF)
        {
            wined3d_mutex_lock();
225
            wined3d_vertex_declaration_incref(This->wineD3DVertexDeclaration);
226 227
            wined3d_mutex_unlock();
        }
228 229
    }

230
    return ref;
231 232
}

233 234 235 236 237 238 239
void IDirect3DVertexDeclaration9Impl_Destroy(LPDIRECT3DVERTEXDECLARATION9 iface) {
    IDirect3DVertexDeclaration9Impl *This = (IDirect3DVertexDeclaration9Impl *)iface;

    if(This->ref != 0) {
        /* Should not happen unless wine has a bug or the application releases references it does not own */
        ERR("Destroying vdecl with ref != 0\n");
    }
240 241

    wined3d_mutex_lock();
242
    wined3d_vertex_declaration_decref(This->wineD3DVertexDeclaration);
243
    wined3d_mutex_unlock();
244 245
}

246
static ULONG WINAPI IDirect3DVertexDeclaration9Impl_Release(LPDIRECT3DVERTEXDECLARATION9 iface) {
247
    IDirect3DVertexDeclaration9Impl *This = (IDirect3DVertexDeclaration9Impl *)iface;
248 249
    ULONG ref = InterlockedDecrement(&This->ref);

Henri Verbeet's avatar
Henri Verbeet committed
250
    TRACE("%p decreasing refcount to %u.\n", iface, ref);
251

252
    if (ref == 0) {
253 254
        IDirect3DDevice9Ex *parentDevice = This->parentDevice;

255
        if(!This->convFVF) {
256
            IDirect3DVertexDeclaration9Impl_Destroy(iface);
257
        }
258 259 260

        /* Release the device last, as it may cause the device to be destroyed. */
        IDirect3DDevice9Ex_Release(parentDevice);
261 262 263 264 265
    }
    return ref;
}

/* IDirect3DVertexDeclaration9 Interface follow: */
266 267 268
static HRESULT WINAPI IDirect3DVertexDeclaration9Impl_GetDevice(IDirect3DVertexDeclaration9 *iface,
        IDirect3DDevice9 **device)
{
269
    IDirect3DVertexDeclaration9Impl *This = (IDirect3DVertexDeclaration9Impl *)iface;
270

271
    TRACE("iface %p, device %p.\n", iface, device);
272

273 274
    *device = (IDirect3DDevice9 *)This->parentDevice;
    IDirect3DDevice9_AddRef(*device);
275

276 277 278
    TRACE("Returning device %p.\n", *device);

    return D3D_OK;
279 280
}

281
static HRESULT WINAPI IDirect3DVertexDeclaration9Impl_GetDeclaration(LPDIRECT3DVERTEXDECLARATION9 iface, D3DVERTEXELEMENT9* pDecl, UINT* pNumElements) {
282
    IDirect3DVertexDeclaration9Impl *This = (IDirect3DVertexDeclaration9Impl *)iface;
283

Henri Verbeet's avatar
Henri Verbeet committed
284
    TRACE("iface %p, elements %p, element_count %p.\n", iface, pDecl, pNumElements);
285 286 287 288 289 290 291 292 293 294 295 296 297

    *pNumElements = This->element_count;

    /* Passing a NULL pDecl is used to just retrieve the number of elements */
    if (!pDecl) {
        TRACE("NULL pDecl passed. Returning D3D_OK.\n");
        return D3D_OK;
    }

    TRACE("Copying %p to %p\n", This->elements, pDecl);
    CopyMemory(pDecl, This->elements, This->element_count * sizeof(D3DVERTEXELEMENT9));

    return D3D_OK;
298
}
299

300
static const IDirect3DVertexDeclaration9Vtbl Direct3DVertexDeclaration9_Vtbl =
301
{
302
    /* IUnknown */
303 304 305
    IDirect3DVertexDeclaration9Impl_QueryInterface,
    IDirect3DVertexDeclaration9Impl_AddRef,
    IDirect3DVertexDeclaration9Impl_Release,
306
    /* IDirect3DVertexDeclaration9 */
307 308 309 310
    IDirect3DVertexDeclaration9Impl_GetDevice,
    IDirect3DVertexDeclaration9Impl_GetDeclaration
};

311 312 313 314 315 316 317 318 319 320 321 322
static void STDMETHODCALLTYPE d3d9_vertexdeclaration_wined3d_object_destroyed(void *parent)
{
    IDirect3DVertexDeclaration9Impl *declaration = parent;
    HeapFree(GetProcessHeap(), 0, declaration->elements);
    HeapFree(GetProcessHeap(), 0, declaration);
}

static const struct wined3d_parent_ops d3d9_vertexdeclaration_wined3d_parent_ops =
{
    d3d9_vertexdeclaration_wined3d_object_destroyed,
};

323 324 325
static HRESULT convert_to_wined3d_declaration(const D3DVERTEXELEMENT9* d3d9_elements,
        WINED3DVERTEXELEMENT **wined3d_elements, UINT *element_count)
{
326
    const D3DVERTEXELEMENT9* element;
327
    UINT count = 1;
328
    UINT i;
329 330 331 332

    TRACE("d3d9_elements %p, wined3d_elements %p\n", d3d9_elements, wined3d_elements);

    element = d3d9_elements;
333
    while (element++->Stream != 0xff && count++ < 128);
334

335
    if (count == 128) return E_FAIL;
336

337
    /* Skip the END element */
338
    --count;
339

340
    *wined3d_elements = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WINED3DVERTEXELEMENT));
341 342
    if (!*wined3d_elements) {
        FIXME("Memory allocation failed\n");
343
        return D3DERR_OUTOFVIDEOMEMORY;
344 345
    }

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    for (i = 0; i < count; ++i)
    {
        if (d3d9_elements[i].Type >= (sizeof(d3d_dtype_lookup) / sizeof(*d3d_dtype_lookup)))
        {
            WARN("Invalid element type %#x.\n", d3d9_elements[i].Type);
            HeapFree(GetProcessHeap(), 0, *wined3d_elements);
            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;
        (*wined3d_elements)[i].output_slot = ~0U;
        (*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;
361 362
    }

363 364 365
    *element_count = count;

    return D3D_OK;
366
}
367

368 369
HRESULT vertexdeclaration_init(IDirect3DVertexDeclaration9Impl *declaration,
        IDirect3DDevice9Impl *device, const D3DVERTEXELEMENT9 *elements)
370
{
371
    WINED3DVERTEXELEMENT *wined3d_elements;
372
    UINT wined3d_element_count;
373
    UINT element_count;
374
    HRESULT hr;
375

376
    hr = convert_to_wined3d_declaration(elements, &wined3d_elements, &wined3d_element_count);
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->lpVtbl = &Direct3DVertexDeclaration9_Vtbl;
    declaration->ref = 1;
385

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

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

409
    declaration->parentDevice = &device->IDirect3DDevice9Ex_iface;
410 411 412
    IDirect3DDevice9Ex_AddRef(declaration->parentDevice);

    return D3D_OK;
413
}