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/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 if (pParams->container) { 38 This->refs = 0; 39 This->forward = true; 40 This->bind = 0; 41 assert(!pParams->start_with_bind_not_ref); 42 } else if (pParams->start_with_bind_not_ref) { 43 This->refs = 0; 44 This->forward = false; 45 This->bind = 1; 46 } else { 47 This->refs = 1; 48 This->forward = false; 49 This->bind = 0; 50 } 51 52 This->container = pParams->container; 53 This->device = pParams->device; 54 if (This->refs && This->device) 55 NineUnknown_AddRef(NineUnknown(This->device)); 56 57 This->vtable = pParams->vtable; 58 This->vtable_internal = pParams->vtable; 59 This->guids = pParams->guids; 60 This->dtor = pParams->dtor; 61 62 This->pdata = _mesa_hash_table_create(NULL, ht_guid_hash, ht_guid_compare); 63 if (!This->pdata) 64 return E_OUTOFMEMORY; 65 66 return D3D_OK; 67} 68 69void 70NineUnknown_dtor( struct NineUnknown *This ) 71{ 72 if (This->refs && This->device) /* Possible only if early exit after a ctor failed */ 73 (void) NineUnknown_Release(NineUnknown(This->device)); 74 75 if (This->pdata) 76 _mesa_hash_table_destroy(This->pdata, ht_guid_delete); 77 78 FREE(This); 79} 80 81HRESULT NINE_WINAPI 82NineUnknown_QueryInterface( struct NineUnknown *This, 83 REFIID riid, 84 void **ppvObject ) 85{ 86 unsigned i = 0; 87 char guid_str[64]; 88 89 DBG("This=%p riid=%p id=%s ppvObject=%p\n", 90 This, riid, riid ? GUID_sprintf(guid_str, riid) : "", ppvObject); 91 92 (void)guid_str; 93 94 if (!ppvObject) return E_POINTER; 95 96 do { 97 if (GUID_equal(This->guids[i], riid)) { 98 *ppvObject = This; 99 /* Tests showed that this call succeeds even on objects with 100 * zero refcount. This can happen if the app released all references 101 * but the resource is still bound. 102 */ 103 NineUnknown_AddRef(This); 104 return S_OK; 105 } 106 } while (This->guids[++i]); 107 108 *ppvObject = NULL; 109 return E_NOINTERFACE; 110} 111 112ULONG NINE_WINAPI 113NineUnknown_AddRef( struct NineUnknown *This ) 114{ 115 ULONG r; 116 if (This->forward) 117 return NineUnknown_AddRef(This->container); 118 else 119 r = p_atomic_inc_return(&This->refs); 120 121 if (r == 1) { 122 if (This->device) 123 NineUnknown_AddRef(NineUnknown(This->device)); 124 } 125 return r; 126} 127 128ULONG NINE_WINAPI 129NineUnknown_Release( struct NineUnknown *This ) 130{ 131 if (This->forward) 132 return NineUnknown_Release(This->container); 133 134 /* Cannot decrease lower than 0. This is a thing 135 * according to wine tests. It's not just clamping 136 * the result as AddRef after Release while refs is 0 137 * will give 1 */ 138 if (!p_atomic_read(&This->refs)) 139 return 0; 140 141 ULONG r = p_atomic_dec_return(&This->refs); 142 143 if (r == 0) { 144 struct NineDevice9 *device = This->device; 145 /* Containers (here with !forward) take care of item destruction */ 146 147 if (!This->container && This->bind == 0) { 148 This->dtor(This); 149 } 150 if (device) { 151 NineUnknown_Release(NineUnknown(device)); 152 } 153 } 154 return r; 155} 156 157/* No need to lock the mutex protecting nine (when D3DCREATE_MULTITHREADED) 158 * for AddRef and Release, except for dtor as some of the dtors require it. */ 159ULONG NINE_WINAPI 160NineUnknown_ReleaseWithDtorLock( struct NineUnknown *This ) 161{ 162 if (This->forward) 163 return NineUnknown_ReleaseWithDtorLock(This->container); 164 165 ULONG r = p_atomic_dec_return(&This->refs); 166 167 if (r == 0) { 168 struct NineDevice9 *device = This->device; 169 /* Containers (here with !forward) take care of item destruction */ 170 if (!This->container && This->bind == 0) { 171 NineLockGlobalMutex(); 172 This->dtor(This); 173 NineUnlockGlobalMutex(); 174 } 175 if (device) { 176 NineUnknown_ReleaseWithDtorLock(NineUnknown(device)); 177 } 178 } 179 return r; 180} 181 182HRESULT NINE_WINAPI 183NineUnknown_GetDevice( struct NineUnknown *This, 184 IDirect3DDevice9 **ppDevice ) 185{ 186 user_assert(ppDevice, E_POINTER); 187 NineUnknown_AddRef(NineUnknown(This->device)); 188 *ppDevice = (IDirect3DDevice9 *)This->device; 189 return D3D_OK; 190} 191 192HRESULT NINE_WINAPI 193NineUnknown_SetPrivateData( struct NineUnknown *This, 194 REFGUID refguid, 195 const void *pData, 196 DWORD SizeOfData, 197 DWORD Flags ) 198{ 199 struct pheader *header; 200 const void *user_data = pData; 201 char guid_str[64]; 202 void *header_data; 203 204 DBG("This=%p GUID=%s pData=%p SizeOfData=%u Flags=%x\n", 205 This, GUID_sprintf(guid_str, refguid), pData, SizeOfData, Flags); 206 207 (void)guid_str; 208 209 if (Flags & D3DSPD_IUNKNOWN) 210 user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL); 211 212 /* data consists of a header and the actual data. avoiding 2 mallocs */ 213 header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData); 214 if (!header) { DBG("Returning E_OUTOFMEMORY\n"); return E_OUTOFMEMORY; } 215 header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE; 216 217 /* if the refguid already exists, delete it */ 218 NineUnknown_FreePrivateData(This, refguid); 219 220 /* IUnknown special case */ 221 if (header->unknown) { 222 /* here the pointer doesn't point to the data we want, so point at the 223 * pointer making what we eventually copy is the pointer itself */ 224 user_data = &pData; 225 } 226 227 header->size = SizeOfData; 228 header_data = (void *)header + sizeof(*header); 229 memcpy(header_data, user_data, header->size); 230 memcpy(&header->guid, refguid, sizeof(header->guid)); 231 232 DBG("New header %p, size %d\n", header, (int)header->size); 233 _mesa_hash_table_insert(This->pdata, &header->guid, header); 234 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); } 235 return D3D_OK; 236} 237 238HRESULT NINE_WINAPI 239NineUnknown_GetPrivateData( struct NineUnknown *This, 240 REFGUID refguid, 241 void *pData, 242 DWORD *pSizeOfData ) 243{ 244 struct hash_entry *entry; 245 struct pheader *header; 246 DWORD sizeofdata; 247 char guid_str[64]; 248 void *header_data; 249 250 DBG("This=%p GUID=%s pData=%p pSizeOfData=%p\n", 251 This, GUID_sprintf(guid_str, refguid), pData, pSizeOfData); 252 253 (void)guid_str; 254 255 entry = _mesa_hash_table_search(This->pdata, refguid); 256 if (!entry) { DBG("Returning D3DERR_NOTFOUND\n"); return D3DERR_NOTFOUND; } 257 258 header = entry->data; 259 260 user_assert(pSizeOfData, E_POINTER); 261 sizeofdata = *pSizeOfData; 262 *pSizeOfData = header->size; 263 DBG("Found header %p, size %d. Requested max %d\n", header, (int)header->size, (int)sizeofdata); 264 265 if (!pData) { 266 DBG("Returning early D3D_OK\n"); 267 return D3D_OK; 268 } 269 if (sizeofdata < header->size) { 270 DBG("Returning D3DERR_MOREDATA\n"); 271 return D3DERR_MOREDATA; 272 } 273 274 header_data = (void *)header + sizeof(*header); 275 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); } 276 memcpy(pData, header_data, header->size); 277 DBG("Returning D3D_OK\n"); 278 279 return D3D_OK; 280} 281 282HRESULT NINE_WINAPI 283NineUnknown_FreePrivateData( struct NineUnknown *This, 284 REFGUID refguid ) 285{ 286 struct hash_entry *entry; 287 char guid_str[64]; 288 289 DBG("This=%p GUID=%s\n", This, GUID_sprintf(guid_str, refguid)); 290 291 (void)guid_str; 292 293 entry = _mesa_hash_table_search(This->pdata, refguid); 294 if (!entry) { 295 DBG("Nothing to free\n"); 296 return D3DERR_NOTFOUND; 297 } 298 299 DBG("Freeing %p\n", entry->data); 300 ht_guid_delete(entry); 301 _mesa_hash_table_remove(This->pdata, entry); 302 303 return D3D_OK; 304} 305