1/* 2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * on the rights to use, copy, modify, merge, publish, distribute, sub 8 * license, and/or sell copies of the Software, and to permit persons to whom 9 * the Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */ 22 23#include "iunknown.h" 24#include "util/u_atomic.h" 25#include "util/u_hash_table.h" 26 27#include "nine_helpers.h" 28#include "nine_pdata.h" 29#include "nine_lock.h" 30 31#define DBG_CHANNEL DBG_UNKNOWN 32 33HRESULT 34NineUnknown_ctor( struct NineUnknown *This, 35 struct NineUnknownParams *pParams ) 36{ 37 This->refs = pParams->container ? 0 : 1; 38 This->bind = 0; 39 This->forward = !This->refs; 40 This->container = pParams->container; 41 This->device = pParams->device; 42 if (This->refs && This->device) 43 NineUnknown_AddRef(NineUnknown(This->device)); 44 45 This->vtable = pParams->vtable; 46 This->vtable_internal = pParams->vtable; 47 This->guids = pParams->guids; 48 This->dtor = pParams->dtor; 49 50 This->pdata = util_hash_table_create(ht_guid_hash, ht_guid_compare); 51 if (!This->pdata) 52 return E_OUTOFMEMORY; 53 54 return D3D_OK; 55} 56 57void 58NineUnknown_dtor( struct NineUnknown *This ) 59{ 60 if (This->refs && This->device) /* Possible only if early exit after a ctor failed */ 61 (void) NineUnknown_Release(NineUnknown(This->device)); 62 63 if (This->pdata) { 64 util_hash_table_foreach(This->pdata, ht_guid_delete, NULL); 65 util_hash_table_destroy(This->pdata); 66 } 67 68 FREE(This); 69} 70 71HRESULT NINE_WINAPI 72NineUnknown_QueryInterface( struct NineUnknown *This, 73 REFIID riid, 74 void **ppvObject ) 75{ 76 unsigned i = 0; 77 char guid_str[64]; 78 79 DBG("This=%p riid=%p id=%s ppvObject=%p\n", 80 This, riid, riid ? GUID_sprintf(guid_str, riid) : "", ppvObject); 81 82 (void)guid_str; 83 84 if (!ppvObject) return E_POINTER; 85 86 do { 87 if (GUID_equal(This->guids[i], riid)) { 88 *ppvObject = This; 89 /* Tests showed that this call succeeds even on objects with 90 * zero refcount. This can happen if the app released all references 91 * but the resource is still bound. 92 */ 93 NineUnknown_AddRef(This); 94 return S_OK; 95 } 96 } while (This->guids[++i]); 97 98 *ppvObject = NULL; 99 return E_NOINTERFACE; 100} 101 102ULONG NINE_WINAPI 103NineUnknown_AddRef( struct NineUnknown *This ) 104{ 105 ULONG r; 106 if (This->forward) 107 return NineUnknown_AddRef(This->container); 108 else 109 r = p_atomic_inc_return(&This->refs); 110 111 if (r == 1) { 112 if (This->device) 113 NineUnknown_AddRef(NineUnknown(This->device)); 114 } 115 return r; 116} 117 118ULONG NINE_WINAPI 119NineUnknown_Release( struct NineUnknown *This ) 120{ 121 if (This->forward) 122 return NineUnknown_Release(This->container); 123 124 ULONG r = p_atomic_dec_return(&This->refs); 125 126 if (r == 0) { 127 if (This->device) { 128 if (NineUnknown_Release(NineUnknown(This->device)) == 0) 129 return r; /* everything's gone */ 130 } 131 /* Containers (here with !forward) take care of item destruction */ 132 if (!This->container && This->bind == 0) { 133 This->dtor(This); 134 } 135 } 136 return r; 137} 138 139/* No need to lock the mutex protecting nine (when D3DCREATE_MULTITHREADED) 140 * for AddRef and Release, except for dtor as some of the dtors require it. */ 141ULONG NINE_WINAPI 142NineUnknown_ReleaseWithDtorLock( struct NineUnknown *This ) 143{ 144 if (This->forward) 145 return NineUnknown_ReleaseWithDtorLock(This->container); 146 147 ULONG r = p_atomic_dec_return(&This->refs); 148 149 if (r == 0) { 150 if (This->device) { 151 if (NineUnknown_ReleaseWithDtorLock(NineUnknown(This->device)) == 0) 152 return r; /* everything's gone */ 153 } 154 /* Containers (here with !forward) take care of item destruction */ 155 if (!This->container && This->bind == 0) { 156 NineLockGlobalMutex(); 157 This->dtor(This); 158 NineUnlockGlobalMutex(); 159 } 160 } 161 return r; 162} 163 164HRESULT NINE_WINAPI 165NineUnknown_GetDevice( struct NineUnknown *This, 166 IDirect3DDevice9 **ppDevice ) 167{ 168 user_assert(ppDevice, E_POINTER); 169 NineUnknown_AddRef(NineUnknown(This->device)); 170 *ppDevice = (IDirect3DDevice9 *)This->device; 171 return D3D_OK; 172} 173 174HRESULT NINE_WINAPI 175NineUnknown_SetPrivateData( struct NineUnknown *This, 176 REFGUID refguid, 177 const void *pData, 178 DWORD SizeOfData, 179 DWORD Flags ) 180{ 181 enum pipe_error err; 182 struct pheader *header; 183 const void *user_data = pData; 184 char guid_str[64]; 185 void *header_data; 186 187 DBG("This=%p GUID=%s pData=%p SizeOfData=%u Flags=%x\n", 188 This, GUID_sprintf(guid_str, refguid), pData, SizeOfData, Flags); 189 190 (void)guid_str; 191 192 if (Flags & D3DSPD_IUNKNOWN) 193 user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL); 194 195 /* data consists of a header and the actual data. avoiding 2 mallocs */ 196 header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData); 197 if (!header) { return E_OUTOFMEMORY; } 198 header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE; 199 200 /* if the refguid already exists, delete it */ 201 NineUnknown_FreePrivateData(This, refguid); 202 203 /* IUnknown special case */ 204 if (header->unknown) { 205 /* here the pointer doesn't point to the data we want, so point at the 206 * pointer making what we eventually copy is the pointer itself */ 207 user_data = &pData; 208 } 209 210 header->size = SizeOfData; 211 header_data = (void *)header + sizeof(*header); 212 memcpy(header_data, user_data, header->size); 213 memcpy(&header->guid, refguid, sizeof(header->guid)); 214 215 err = util_hash_table_set(This->pdata, &header->guid, header); 216 if (err == PIPE_OK) { 217 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); } 218 return D3D_OK; 219 } 220 221 FREE(header); 222 if (err == PIPE_ERROR_OUT_OF_MEMORY) { return E_OUTOFMEMORY; } 223 224 return D3DERR_DRIVERINTERNALERROR; 225} 226 227HRESULT NINE_WINAPI 228NineUnknown_GetPrivateData( struct NineUnknown *This, 229 REFGUID refguid, 230 void *pData, 231 DWORD *pSizeOfData ) 232{ 233 struct pheader *header; 234 DWORD sizeofdata; 235 char guid_str[64]; 236 void *header_data; 237 238 DBG("This=%p GUID=%s pData=%p pSizeOfData=%p\n", 239 This, GUID_sprintf(guid_str, refguid), pData, pSizeOfData); 240 241 (void)guid_str; 242 243 header = util_hash_table_get(This->pdata, refguid); 244 if (!header) { return D3DERR_NOTFOUND; } 245 246 user_assert(pSizeOfData, E_POINTER); 247 sizeofdata = *pSizeOfData; 248 *pSizeOfData = header->size; 249 250 if (!pData) { 251 return D3D_OK; 252 } 253 if (sizeofdata < header->size) { 254 return D3DERR_MOREDATA; 255 } 256 257 header_data = (void *)header + sizeof(*header); 258 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); } 259 memcpy(pData, header_data, header->size); 260 261 return D3D_OK; 262} 263 264HRESULT NINE_WINAPI 265NineUnknown_FreePrivateData( struct NineUnknown *This, 266 REFGUID refguid ) 267{ 268 struct pheader *header; 269 char guid_str[64]; 270 271 DBG("This=%p GUID=%s\n", This, GUID_sprintf(guid_str, refguid)); 272 273 (void)guid_str; 274 275 header = util_hash_table_get(This->pdata, refguid); 276 if (!header) 277 return D3DERR_NOTFOUND; 278 279 ht_guid_delete(NULL, header, NULL); 280 util_hash_table_remove(This->pdata, refguid); 281 282 return D3D_OK; 283} 284