• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 HRESULT
NineUnknown_ctor(struct NineUnknown * This,struct NineUnknownParams * pParams)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
NineUnknown_dtor(struct NineUnknown * This)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
NineUnknown_QueryInterface(struct NineUnknown * This,REFIID riid,void ** ppvObject)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
NineUnknown_AddRef(struct NineUnknown * This)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
NineUnknown_Release(struct NineUnknown * This)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
NineUnknown_ReleaseWithDtorLock(struct NineUnknown * This)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
NineUnknown_GetDevice(struct NineUnknown * This,IDirect3DDevice9 ** ppDevice)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
NineUnknown_SetPrivateData(struct NineUnknown * This,REFGUID refguid,const void * pData,DWORD SizeOfData,DWORD Flags)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
NineUnknown_GetPrivateData(struct NineUnknown * This,REFGUID refguid,void * pData,DWORD * pSizeOfData)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
NineUnknown_FreePrivateData(struct NineUnknown * This,REFGUID refguid)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