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