17ec681f3Smrg/*
27ec681f3Smrg * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
87ec681f3Smrg * license, and/or sell copies of the Software, and to permit persons to whom
97ec681f3Smrg * the Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
197ec681f3Smrg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
207ec681f3Smrg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
217ec681f3Smrg * USE OR OTHER DEALINGS IN THE SOFTWARE. */
227ec681f3Smrg
237ec681f3Smrg#include "iunknown.h"
247ec681f3Smrg#include "util/u_atomic.h"
257ec681f3Smrg#include "util/hash_table.h"
267ec681f3Smrg
277ec681f3Smrg#include "nine_helpers.h"
287ec681f3Smrg#include "nine_pdata.h"
297ec681f3Smrg#include "nine_lock.h"
307ec681f3Smrg
317ec681f3Smrg#define DBG_CHANNEL DBG_UNKNOWN
327ec681f3Smrg
337ec681f3SmrgHRESULT
347ec681f3SmrgNineUnknown_ctor( struct NineUnknown *This,
357ec681f3Smrg                  struct NineUnknownParams *pParams )
367ec681f3Smrg{
377ec681f3Smrg    if (pParams->container) {
387ec681f3Smrg        This->refs = 0;
397ec681f3Smrg        This->forward = true;
407ec681f3Smrg        This->bind = 0;
417ec681f3Smrg        assert(!pParams->start_with_bind_not_ref);
427ec681f3Smrg    } else if (pParams->start_with_bind_not_ref) {
437ec681f3Smrg        This->refs = 0;
447ec681f3Smrg        This->forward = false;
457ec681f3Smrg        This->bind = 1;
467ec681f3Smrg    } else {
477ec681f3Smrg        This->refs = 1;
487ec681f3Smrg        This->forward = false;
497ec681f3Smrg        This->bind = 0;
507ec681f3Smrg    }
517ec681f3Smrg
527ec681f3Smrg    This->container = pParams->container;
537ec681f3Smrg    This->device = pParams->device;
547ec681f3Smrg    if (This->refs && This->device)
557ec681f3Smrg        NineUnknown_AddRef(NineUnknown(This->device));
567ec681f3Smrg
577ec681f3Smrg    This->vtable = pParams->vtable;
587ec681f3Smrg    This->vtable_internal = pParams->vtable;
597ec681f3Smrg    This->guids = pParams->guids;
607ec681f3Smrg    This->dtor = pParams->dtor;
617ec681f3Smrg
627ec681f3Smrg    This->pdata = _mesa_hash_table_create(NULL, ht_guid_hash, ht_guid_compare);
637ec681f3Smrg    if (!This->pdata)
647ec681f3Smrg        return E_OUTOFMEMORY;
657ec681f3Smrg
667ec681f3Smrg    return D3D_OK;
677ec681f3Smrg}
687ec681f3Smrg
697ec681f3Smrgvoid
707ec681f3SmrgNineUnknown_dtor( struct NineUnknown *This )
717ec681f3Smrg{
727ec681f3Smrg    if (This->refs && This->device) /* Possible only if early exit after a ctor failed */
737ec681f3Smrg        (void) NineUnknown_Release(NineUnknown(This->device));
747ec681f3Smrg
757ec681f3Smrg    if (This->pdata)
767ec681f3Smrg        _mesa_hash_table_destroy(This->pdata, ht_guid_delete);
777ec681f3Smrg
787ec681f3Smrg    FREE(This);
797ec681f3Smrg}
807ec681f3Smrg
817ec681f3SmrgHRESULT NINE_WINAPI
827ec681f3SmrgNineUnknown_QueryInterface( struct NineUnknown *This,
837ec681f3Smrg                            REFIID riid,
847ec681f3Smrg                            void **ppvObject )
857ec681f3Smrg{
867ec681f3Smrg    unsigned i = 0;
877ec681f3Smrg    char guid_str[64];
887ec681f3Smrg
897ec681f3Smrg    DBG("This=%p riid=%p id=%s ppvObject=%p\n",
907ec681f3Smrg        This, riid, riid ? GUID_sprintf(guid_str, riid) : "", ppvObject);
917ec681f3Smrg
927ec681f3Smrg    (void)guid_str;
937ec681f3Smrg
947ec681f3Smrg    if (!ppvObject) return E_POINTER;
957ec681f3Smrg
967ec681f3Smrg    do {
977ec681f3Smrg        if (GUID_equal(This->guids[i], riid)) {
987ec681f3Smrg            *ppvObject = This;
997ec681f3Smrg            /* Tests showed that this call succeeds even on objects with
1007ec681f3Smrg             * zero refcount. This can happen if the app released all references
1017ec681f3Smrg             * but the resource is still bound.
1027ec681f3Smrg             */
1037ec681f3Smrg            NineUnknown_AddRef(This);
1047ec681f3Smrg            return S_OK;
1057ec681f3Smrg        }
1067ec681f3Smrg    } while (This->guids[++i]);
1077ec681f3Smrg
1087ec681f3Smrg    *ppvObject = NULL;
1097ec681f3Smrg    return E_NOINTERFACE;
1107ec681f3Smrg}
1117ec681f3Smrg
1127ec681f3SmrgULONG NINE_WINAPI
1137ec681f3SmrgNineUnknown_AddRef( struct NineUnknown *This )
1147ec681f3Smrg{
1157ec681f3Smrg    ULONG r;
1167ec681f3Smrg    if (This->forward)
1177ec681f3Smrg        return NineUnknown_AddRef(This->container);
1187ec681f3Smrg    else
1197ec681f3Smrg        r = p_atomic_inc_return(&This->refs);
1207ec681f3Smrg
1217ec681f3Smrg    if (r == 1) {
1227ec681f3Smrg        if (This->device)
1237ec681f3Smrg            NineUnknown_AddRef(NineUnknown(This->device));
1247ec681f3Smrg    }
1257ec681f3Smrg    return r;
1267ec681f3Smrg}
1277ec681f3Smrg
1287ec681f3SmrgULONG NINE_WINAPI
1297ec681f3SmrgNineUnknown_Release( struct NineUnknown *This )
1307ec681f3Smrg{
1317ec681f3Smrg    if (This->forward)
1327ec681f3Smrg        return NineUnknown_Release(This->container);
1337ec681f3Smrg
1347ec681f3Smrg    /* Cannot decrease lower than 0. This is a thing
1357ec681f3Smrg     * according to wine tests. It's not just clamping
1367ec681f3Smrg     * the result as AddRef after Release while refs is 0
1377ec681f3Smrg     * will give 1 */
1387ec681f3Smrg    if (!p_atomic_read(&This->refs))
1397ec681f3Smrg        return 0;
1407ec681f3Smrg
1417ec681f3Smrg    ULONG r = p_atomic_dec_return(&This->refs);
1427ec681f3Smrg
1437ec681f3Smrg    if (r == 0) {
1447ec681f3Smrg        struct NineDevice9 *device = This->device;
1457ec681f3Smrg        /* Containers (here with !forward) take care of item destruction */
1467ec681f3Smrg
1477ec681f3Smrg        if (!This->container && This->bind == 0) {
1487ec681f3Smrg            This->dtor(This);
1497ec681f3Smrg        }
1507ec681f3Smrg        if (device) {
1517ec681f3Smrg            NineUnknown_Release(NineUnknown(device));
1527ec681f3Smrg        }
1537ec681f3Smrg    }
1547ec681f3Smrg    return r;
1557ec681f3Smrg}
1567ec681f3Smrg
1577ec681f3Smrg/* No need to lock the mutex protecting nine (when D3DCREATE_MULTITHREADED)
1587ec681f3Smrg * for AddRef and Release, except for dtor as some of the dtors require it. */
1597ec681f3SmrgULONG NINE_WINAPI
1607ec681f3SmrgNineUnknown_ReleaseWithDtorLock( struct NineUnknown *This )
1617ec681f3Smrg{
1627ec681f3Smrg    if (This->forward)
1637ec681f3Smrg        return NineUnknown_ReleaseWithDtorLock(This->container);
1647ec681f3Smrg
1657ec681f3Smrg    ULONG r = p_atomic_dec_return(&This->refs);
1667ec681f3Smrg
1677ec681f3Smrg    if (r == 0) {
1687ec681f3Smrg        struct NineDevice9 *device = This->device;
1697ec681f3Smrg        /* Containers (here with !forward) take care of item destruction */
1707ec681f3Smrg        if (!This->container && This->bind == 0) {
1717ec681f3Smrg            NineLockGlobalMutex();
1727ec681f3Smrg            This->dtor(This);
1737ec681f3Smrg            NineUnlockGlobalMutex();
1747ec681f3Smrg        }
1757ec681f3Smrg        if (device) {
1767ec681f3Smrg            NineUnknown_ReleaseWithDtorLock(NineUnknown(device));
1777ec681f3Smrg        }
1787ec681f3Smrg    }
1797ec681f3Smrg    return r;
1807ec681f3Smrg}
1817ec681f3Smrg
1827ec681f3SmrgHRESULT NINE_WINAPI
1837ec681f3SmrgNineUnknown_GetDevice( struct NineUnknown *This,
1847ec681f3Smrg                       IDirect3DDevice9 **ppDevice )
1857ec681f3Smrg{
1867ec681f3Smrg    user_assert(ppDevice, E_POINTER);
1877ec681f3Smrg    NineUnknown_AddRef(NineUnknown(This->device));
1887ec681f3Smrg    *ppDevice = (IDirect3DDevice9 *)This->device;
1897ec681f3Smrg    return D3D_OK;
1907ec681f3Smrg}
1917ec681f3Smrg
1927ec681f3SmrgHRESULT NINE_WINAPI
1937ec681f3SmrgNineUnknown_SetPrivateData( struct NineUnknown *This,
1947ec681f3Smrg                            REFGUID refguid,
1957ec681f3Smrg                            const void *pData,
1967ec681f3Smrg                            DWORD SizeOfData,
1977ec681f3Smrg                            DWORD Flags )
1987ec681f3Smrg{
1997ec681f3Smrg    struct pheader *header;
2007ec681f3Smrg    const void *user_data = pData;
2017ec681f3Smrg    char guid_str[64];
2027ec681f3Smrg    void *header_data;
2037ec681f3Smrg
2047ec681f3Smrg    DBG("This=%p GUID=%s pData=%p SizeOfData=%u Flags=%x\n",
2057ec681f3Smrg        This, GUID_sprintf(guid_str, refguid), pData, SizeOfData, Flags);
2067ec681f3Smrg
2077ec681f3Smrg    (void)guid_str;
2087ec681f3Smrg
2097ec681f3Smrg    if (Flags & D3DSPD_IUNKNOWN)
2107ec681f3Smrg        user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL);
2117ec681f3Smrg
2127ec681f3Smrg    /* data consists of a header and the actual data. avoiding 2 mallocs */
2137ec681f3Smrg    header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData);
2147ec681f3Smrg    if (!header) { DBG("Returning E_OUTOFMEMORY\n"); return E_OUTOFMEMORY; }
2157ec681f3Smrg    header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE;
2167ec681f3Smrg
2177ec681f3Smrg    /* if the refguid already exists, delete it */
2187ec681f3Smrg    NineUnknown_FreePrivateData(This, refguid);
2197ec681f3Smrg
2207ec681f3Smrg    /* IUnknown special case */
2217ec681f3Smrg    if (header->unknown) {
2227ec681f3Smrg        /* here the pointer doesn't point to the data we want, so point at the
2237ec681f3Smrg         * pointer making what we eventually copy is the pointer itself */
2247ec681f3Smrg        user_data = &pData;
2257ec681f3Smrg    }
2267ec681f3Smrg
2277ec681f3Smrg    header->size = SizeOfData;
2287ec681f3Smrg    header_data = (void *)header + sizeof(*header);
2297ec681f3Smrg    memcpy(header_data, user_data, header->size);
2307ec681f3Smrg    memcpy(&header->guid, refguid, sizeof(header->guid));
2317ec681f3Smrg
2327ec681f3Smrg    DBG("New header %p, size %d\n", header, (int)header->size);
2337ec681f3Smrg    _mesa_hash_table_insert(This->pdata, &header->guid, header);
2347ec681f3Smrg    if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); }
2357ec681f3Smrg    return D3D_OK;
2367ec681f3Smrg}
2377ec681f3Smrg
2387ec681f3SmrgHRESULT NINE_WINAPI
2397ec681f3SmrgNineUnknown_GetPrivateData( struct NineUnknown *This,
2407ec681f3Smrg                            REFGUID refguid,
2417ec681f3Smrg                            void *pData,
2427ec681f3Smrg                            DWORD *pSizeOfData )
2437ec681f3Smrg{
2447ec681f3Smrg    struct hash_entry *entry;
2457ec681f3Smrg    struct pheader *header;
2467ec681f3Smrg    DWORD sizeofdata;
2477ec681f3Smrg    char guid_str[64];
2487ec681f3Smrg    void *header_data;
2497ec681f3Smrg
2507ec681f3Smrg    DBG("This=%p GUID=%s pData=%p pSizeOfData=%p\n",
2517ec681f3Smrg        This, GUID_sprintf(guid_str, refguid), pData, pSizeOfData);
2527ec681f3Smrg
2537ec681f3Smrg    (void)guid_str;
2547ec681f3Smrg
2557ec681f3Smrg    entry = _mesa_hash_table_search(This->pdata, refguid);
2567ec681f3Smrg    if (!entry) { DBG("Returning D3DERR_NOTFOUND\n"); return D3DERR_NOTFOUND; }
2577ec681f3Smrg
2587ec681f3Smrg    header = entry->data;
2597ec681f3Smrg
2607ec681f3Smrg    user_assert(pSizeOfData, E_POINTER);
2617ec681f3Smrg    sizeofdata = *pSizeOfData;
2627ec681f3Smrg    *pSizeOfData = header->size;
2637ec681f3Smrg    DBG("Found header %p, size %d. Requested max %d\n", header, (int)header->size, (int)sizeofdata);
2647ec681f3Smrg
2657ec681f3Smrg    if (!pData) {
2667ec681f3Smrg        DBG("Returning early D3D_OK\n");
2677ec681f3Smrg        return D3D_OK;
2687ec681f3Smrg    }
2697ec681f3Smrg    if (sizeofdata < header->size) {
2707ec681f3Smrg        DBG("Returning D3DERR_MOREDATA\n");
2717ec681f3Smrg        return D3DERR_MOREDATA;
2727ec681f3Smrg    }
2737ec681f3Smrg
2747ec681f3Smrg    header_data = (void *)header + sizeof(*header);
2757ec681f3Smrg    if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); }
2767ec681f3Smrg    memcpy(pData, header_data, header->size);
2777ec681f3Smrg    DBG("Returning D3D_OK\n");
2787ec681f3Smrg
2797ec681f3Smrg    return D3D_OK;
2807ec681f3Smrg}
2817ec681f3Smrg
2827ec681f3SmrgHRESULT NINE_WINAPI
2837ec681f3SmrgNineUnknown_FreePrivateData( struct NineUnknown *This,
2847ec681f3Smrg                             REFGUID refguid )
2857ec681f3Smrg{
2867ec681f3Smrg    struct hash_entry *entry;
2877ec681f3Smrg    char guid_str[64];
2887ec681f3Smrg
2897ec681f3Smrg    DBG("This=%p GUID=%s\n", This, GUID_sprintf(guid_str, refguid));
2907ec681f3Smrg
2917ec681f3Smrg    (void)guid_str;
2927ec681f3Smrg
2937ec681f3Smrg    entry = _mesa_hash_table_search(This->pdata, refguid);
2947ec681f3Smrg    if (!entry) {
2957ec681f3Smrg        DBG("Nothing to free\n");
2967ec681f3Smrg        return D3DERR_NOTFOUND;
2977ec681f3Smrg    }
2987ec681f3Smrg
2997ec681f3Smrg    DBG("Freeing %p\n", entry->data);
3007ec681f3Smrg    ht_guid_delete(entry);
3017ec681f3Smrg    _mesa_hash_table_remove(This->pdata, entry);
3027ec681f3Smrg
3037ec681f3Smrg    return D3D_OK;
3047ec681f3Smrg}
305