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