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 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 = util_hash_table_create(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 util_hash_table_foreach(This->pdata, ht_guid_delete, NULL); 77 util_hash_table_destroy(This->pdata); 78 } 79 80 FREE(This); 81} 82 83HRESULT NINE_WINAPI 84NineUnknown_QueryInterface( struct NineUnknown *This, 85 REFIID riid, 86 void **ppvObject ) 87{ 88 unsigned i = 0; 89 char guid_str[64]; 90 91 DBG("This=%p riid=%p id=%s ppvObject=%p\n", 92 This, riid, riid ? GUID_sprintf(guid_str, riid) : "", ppvObject); 93 94 (void)guid_str; 95 96 if (!ppvObject) return E_POINTER; 97 98 do { 99 if (GUID_equal(This->guids[i], riid)) { 100 *ppvObject = This; 101 /* Tests showed that this call succeeds even on objects with 102 * zero refcount. This can happen if the app released all references 103 * but the resource is still bound. 104 */ 105 NineUnknown_AddRef(This); 106 return S_OK; 107 } 108 } while (This->guids[++i]); 109 110 *ppvObject = NULL; 111 return E_NOINTERFACE; 112} 113 114ULONG NINE_WINAPI 115NineUnknown_AddRef( struct NineUnknown *This ) 116{ 117 ULONG r; 118 if (This->forward) 119 return NineUnknown_AddRef(This->container); 120 else 121 r = p_atomic_inc_return(&This->refs); 122 123 if (r == 1) { 124 if (This->device) 125 NineUnknown_AddRef(NineUnknown(This->device)); 126 } 127 return r; 128} 129 130ULONG NINE_WINAPI 131NineUnknown_Release( struct NineUnknown *This ) 132{ 133 if (This->forward) 134 return NineUnknown_Release(This->container); 135 136 ULONG r = p_atomic_dec_return(&This->refs); 137 138 if (r == 0) { 139 if (This->device) { 140 if (NineUnknown_Release(NineUnknown(This->device)) == 0) 141 return r; /* everything's gone */ 142 } 143 /* Containers (here with !forward) take care of item destruction */ 144 if (!This->container && This->bind == 0) { 145 This->dtor(This); 146 } 147 } 148 return r; 149} 150 151/* No need to lock the mutex protecting nine (when D3DCREATE_MULTITHREADED) 152 * for AddRef and Release, except for dtor as some of the dtors require it. */ 153ULONG NINE_WINAPI 154NineUnknown_ReleaseWithDtorLock( struct NineUnknown *This ) 155{ 156 if (This->forward) 157 return NineUnknown_ReleaseWithDtorLock(This->container); 158 159 ULONG r = p_atomic_dec_return(&This->refs); 160 161 if (r == 0) { 162 if (This->device) { 163 if (NineUnknown_ReleaseWithDtorLock(NineUnknown(This->device)) == 0) 164 return r; /* everything's gone */ 165 } 166 /* Containers (here with !forward) take care of item destruction */ 167 if (!This->container && This->bind == 0) { 168 NineLockGlobalMutex(); 169 This->dtor(This); 170 NineUnlockGlobalMutex(); 171 } 172 } 173 return r; 174} 175 176HRESULT NINE_WINAPI 177NineUnknown_GetDevice( struct NineUnknown *This, 178 IDirect3DDevice9 **ppDevice ) 179{ 180 user_assert(ppDevice, E_POINTER); 181 NineUnknown_AddRef(NineUnknown(This->device)); 182 *ppDevice = (IDirect3DDevice9 *)This->device; 183 return D3D_OK; 184} 185 186HRESULT NINE_WINAPI 187NineUnknown_SetPrivateData( struct NineUnknown *This, 188 REFGUID refguid, 189 const void *pData, 190 DWORD SizeOfData, 191 DWORD Flags ) 192{ 193 enum pipe_error err; 194 struct pheader *header; 195 const void *user_data = pData; 196 char guid_str[64]; 197 void *header_data; 198 199 DBG("This=%p GUID=%s pData=%p SizeOfData=%u Flags=%x\n", 200 This, GUID_sprintf(guid_str, refguid), pData, SizeOfData, Flags); 201 202 (void)guid_str; 203 204 if (Flags & D3DSPD_IUNKNOWN) 205 user_assert(SizeOfData == sizeof(IUnknown *), D3DERR_INVALIDCALL); 206 207 /* data consists of a header and the actual data. avoiding 2 mallocs */ 208 header = CALLOC_VARIANT_LENGTH_STRUCT(pheader, SizeOfData); 209 if (!header) { return E_OUTOFMEMORY; } 210 header->unknown = (Flags & D3DSPD_IUNKNOWN) ? TRUE : FALSE; 211 212 /* if the refguid already exists, delete it */ 213 NineUnknown_FreePrivateData(This, refguid); 214 215 /* IUnknown special case */ 216 if (header->unknown) { 217 /* here the pointer doesn't point to the data we want, so point at the 218 * pointer making what we eventually copy is the pointer itself */ 219 user_data = &pData; 220 } 221 222 header->size = SizeOfData; 223 header_data = (void *)header + sizeof(*header); 224 memcpy(header_data, user_data, header->size); 225 memcpy(&header->guid, refguid, sizeof(header->guid)); 226 227 err = util_hash_table_set(This->pdata, &header->guid, header); 228 if (err == PIPE_OK) { 229 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); } 230 return D3D_OK; 231 } 232 233 FREE(header); 234 if (err == PIPE_ERROR_OUT_OF_MEMORY) { return E_OUTOFMEMORY; } 235 236 return D3DERR_DRIVERINTERNALERROR; 237} 238 239HRESULT NINE_WINAPI 240NineUnknown_GetPrivateData( struct NineUnknown *This, 241 REFGUID refguid, 242 void *pData, 243 DWORD *pSizeOfData ) 244{ 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 header = util_hash_table_get(This->pdata, refguid); 256 if (!header) { return D3DERR_NOTFOUND; } 257 258 user_assert(pSizeOfData, E_POINTER); 259 sizeofdata = *pSizeOfData; 260 *pSizeOfData = header->size; 261 262 if (!pData) { 263 return D3D_OK; 264 } 265 if (sizeofdata < header->size) { 266 return D3DERR_MOREDATA; 267 } 268 269 header_data = (void *)header + sizeof(*header); 270 if (header->unknown) { IUnknown_AddRef(*(IUnknown **)header_data); } 271 memcpy(pData, header_data, header->size); 272 273 return D3D_OK; 274} 275 276HRESULT NINE_WINAPI 277NineUnknown_FreePrivateData( struct NineUnknown *This, 278 REFGUID refguid ) 279{ 280 struct pheader *header; 281 char guid_str[64]; 282 283 DBG("This=%p GUID=%s\n", This, GUID_sprintf(guid_str, refguid)); 284 285 (void)guid_str; 286 287 header = util_hash_table_get(This->pdata, refguid); 288 if (!header) 289 return D3DERR_NOTFOUND; 290 291 ht_guid_delete(NULL, header, NULL); 292 util_hash_table_remove(This->pdata, refguid); 293 294 return D3D_OK; 295} 296