Home | History | Annotate | Download | only in nine
      1 /*
      2  * Copyright 2011 Joakim Sindholt <opensource (at) 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 
     33 HRESULT
     34 NineUnknown_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 
     57 void
     58 NineUnknown_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 
     71 HRESULT NINE_WINAPI
     72 NineUnknown_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 
    102 ULONG NINE_WINAPI
    103 NineUnknown_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 
    118 ULONG NINE_WINAPI
    119 NineUnknown_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. */
    141 ULONG NINE_WINAPI
    142 NineUnknown_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 
    164 HRESULT NINE_WINAPI
    165 NineUnknown_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 
    174 HRESULT NINE_WINAPI
    175 NineUnknown_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 
    227 HRESULT NINE_WINAPI
    228 NineUnknown_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 
    264 HRESULT NINE_WINAPI
    265 NineUnknown_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