xref: /reactos/dll/directx/wine/d3d9/surface.c (revision 9987f029)
1 /*
2  * IDirect3DSurface9 implementation
3  *
4  * Copyright 2002-2005 Jason Edmeades
5  *                     Raphael Junqueira
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "config.h"
23 #include "d3d9_private.h"
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(d3d9);
26 
impl_from_IDirect3DSurface9(IDirect3DSurface9 * iface)27 static inline struct d3d9_surface *impl_from_IDirect3DSurface9(IDirect3DSurface9 *iface)
28 {
29     return CONTAINING_RECORD(iface, struct d3d9_surface, IDirect3DSurface9_iface);
30 }
31 
d3d9_surface_QueryInterface(IDirect3DSurface9 * iface,REFIID riid,void ** out)32 static HRESULT WINAPI d3d9_surface_QueryInterface(IDirect3DSurface9 *iface, REFIID riid, void **out)
33 {
34     TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
35 
36     if (IsEqualGUID(riid, &IID_IDirect3DSurface9)
37             || IsEqualGUID(riid, &IID_IDirect3DResource9)
38             || IsEqualGUID(riid, &IID_IUnknown))
39     {
40         IDirect3DSurface9_AddRef(iface);
41         *out = iface;
42         return S_OK;
43     }
44 
45     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
46 
47     *out = NULL;
48     return E_NOINTERFACE;
49 }
50 
d3d9_surface_AddRef(IDirect3DSurface9 * iface)51 static ULONG WINAPI d3d9_surface_AddRef(IDirect3DSurface9 *iface)
52 {
53     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
54     ULONG refcount;
55 
56     TRACE("iface %p.\n", iface);
57 
58     if (surface->texture)
59     {
60         TRACE("Forwarding to %p.\n", surface->texture);
61         return IDirect3DBaseTexture9_AddRef(&surface->texture->IDirect3DBaseTexture9_iface);
62     }
63 
64     refcount = InterlockedIncrement(&surface->resource.refcount);
65     TRACE("%p increasing refcount to %u.\n", iface, refcount);
66 
67     if (refcount == 1)
68     {
69         if (surface->parent_device)
70             IDirect3DDevice9Ex_AddRef(surface->parent_device);
71         wined3d_mutex_lock();
72         if (surface->wined3d_rtv)
73             wined3d_rendertarget_view_incref(surface->wined3d_rtv);
74         wined3d_texture_incref(surface->wined3d_texture);
75         wined3d_mutex_unlock();
76     }
77 
78     return refcount;
79 }
80 
d3d9_surface_Release(IDirect3DSurface9 * iface)81 static ULONG WINAPI d3d9_surface_Release(IDirect3DSurface9 *iface)
82 {
83     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
84     ULONG refcount;
85 
86     TRACE("iface %p.\n", iface);
87 
88     if (surface->texture)
89     {
90         TRACE("Forwarding to %p.\n", surface->texture);
91         return IDirect3DBaseTexture9_Release(&surface->texture->IDirect3DBaseTexture9_iface);
92     }
93 
94     if (!surface->resource.refcount)
95     {
96         WARN("Surface does not have any references.\n");
97         return 0;
98     }
99 
100     refcount = InterlockedDecrement(&surface->resource.refcount);
101     TRACE("%p decreasing refcount to %u.\n", iface, refcount);
102 
103     if (!refcount)
104     {
105         IDirect3DDevice9Ex *parent_device = surface->parent_device;
106 
107         wined3d_mutex_lock();
108         if (surface->wined3d_rtv)
109             wined3d_rendertarget_view_decref(surface->wined3d_rtv);
110         wined3d_texture_decref(surface->wined3d_texture);
111         wined3d_mutex_unlock();
112 
113         /* Release the device last, as it may cause the device to be destroyed. */
114         if (parent_device)
115             IDirect3DDevice9Ex_Release(parent_device);
116     }
117 
118     return refcount;
119 }
120 
d3d9_surface_GetDevice(IDirect3DSurface9 * iface,IDirect3DDevice9 ** device)121 static HRESULT WINAPI d3d9_surface_GetDevice(IDirect3DSurface9 *iface, IDirect3DDevice9 **device)
122 {
123     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
124 
125     TRACE("iface %p, device %p.\n", iface, device);
126 
127     if (surface->texture)
128         return IDirect3DBaseTexture9_GetDevice(&surface->texture->IDirect3DBaseTexture9_iface, device);
129 
130     *device = (IDirect3DDevice9 *)surface->parent_device;
131     IDirect3DDevice9_AddRef(*device);
132 
133     TRACE("Returning device %p.\n", *device);
134 
135     return D3D_OK;
136 }
137 
d3d9_surface_SetPrivateData(IDirect3DSurface9 * iface,REFGUID guid,const void * data,DWORD data_size,DWORD flags)138 static HRESULT WINAPI d3d9_surface_SetPrivateData(IDirect3DSurface9 *iface, REFGUID guid,
139         const void *data, DWORD data_size, DWORD flags)
140 {
141     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
142     TRACE("iface %p, guid %s, data %p, data_size %u, flags %#x.\n",
143             iface, debugstr_guid(guid), data, data_size, flags);
144 
145     return d3d9_resource_set_private_data(&surface->resource, guid, data, data_size, flags);
146 }
147 
d3d9_surface_GetPrivateData(IDirect3DSurface9 * iface,REFGUID guid,void * data,DWORD * data_size)148 static HRESULT WINAPI d3d9_surface_GetPrivateData(IDirect3DSurface9 *iface, REFGUID guid,
149         void *data, DWORD *data_size)
150 {
151     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
152     TRACE("iface %p, guid %s, data %p, data_size %p.\n",
153             iface, debugstr_guid(guid), data, data_size);
154 
155     return d3d9_resource_get_private_data(&surface->resource, guid, data, data_size);
156 }
157 
d3d9_surface_FreePrivateData(IDirect3DSurface9 * iface,REFGUID guid)158 static HRESULT WINAPI d3d9_surface_FreePrivateData(IDirect3DSurface9 *iface, REFGUID guid)
159 {
160     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
161     TRACE("iface %p, guid %s.\n", iface, debugstr_guid(guid));
162 
163     return d3d9_resource_free_private_data(&surface->resource, guid);
164 }
165 
d3d9_surface_SetPriority(IDirect3DSurface9 * iface,DWORD priority)166 static DWORD WINAPI d3d9_surface_SetPriority(IDirect3DSurface9 *iface, DWORD priority)
167 {
168     TRACE("iface %p, priority %u. Ignored on surfaces.\n", iface, priority);
169     return 0;
170 }
171 
d3d9_surface_GetPriority(IDirect3DSurface9 * iface)172 static DWORD WINAPI d3d9_surface_GetPriority(IDirect3DSurface9 *iface)
173 {
174     TRACE("iface %p. Ignored on surfaces.\n", iface);
175     return 0;
176 }
177 
d3d9_surface_PreLoad(IDirect3DSurface9 * iface)178 static void WINAPI d3d9_surface_PreLoad(IDirect3DSurface9 *iface)
179 {
180     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
181 
182     TRACE("iface %p.\n", iface);
183 
184     wined3d_mutex_lock();
185     wined3d_resource_preload(wined3d_texture_get_resource(surface->wined3d_texture));
186     wined3d_mutex_unlock();
187 }
188 
d3d9_surface_GetType(IDirect3DSurface9 * iface)189 static D3DRESOURCETYPE WINAPI d3d9_surface_GetType(IDirect3DSurface9 *iface)
190 {
191     TRACE("iface %p.\n", iface);
192 
193     return D3DRTYPE_SURFACE;
194 }
195 
d3d9_surface_GetContainer(IDirect3DSurface9 * iface,REFIID riid,void ** container)196 static HRESULT WINAPI d3d9_surface_GetContainer(IDirect3DSurface9 *iface, REFIID riid, void **container)
197 {
198     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
199     HRESULT hr;
200 
201     TRACE("iface %p, riid %s, container %p.\n", iface, debugstr_guid(riid), container);
202 
203     if (!surface->container)
204         return E_NOINTERFACE;
205 
206     hr = IUnknown_QueryInterface(surface->container, riid, container);
207 
208     TRACE("Returning %p.\n", *container);
209 
210     return hr;
211 }
212 
d3d9_surface_GetDesc(IDirect3DSurface9 * iface,D3DSURFACE_DESC * desc)213 static HRESULT WINAPI d3d9_surface_GetDesc(IDirect3DSurface9 *iface, D3DSURFACE_DESC *desc)
214 {
215     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
216     struct wined3d_sub_resource_desc wined3d_desc;
217 
218     TRACE("iface %p, desc %p.\n", iface, desc);
219 
220     wined3d_mutex_lock();
221     wined3d_texture_get_sub_resource_desc(surface->wined3d_texture, surface->sub_resource_idx, &wined3d_desc);
222     wined3d_mutex_unlock();
223 
224     desc->Format = d3dformat_from_wined3dformat(wined3d_desc.format);
225     desc->Type = D3DRTYPE_SURFACE;
226     desc->Usage = d3dusage_from_wined3dusage(wined3d_desc.usage);
227     desc->Pool = d3dpool_from_wined3daccess(wined3d_desc.access, wined3d_desc.usage);
228     desc->MultiSampleType = wined3d_desc.multisample_type;
229     desc->MultiSampleQuality = wined3d_desc.multisample_quality;
230     desc->Width = wined3d_desc.width;
231     desc->Height = wined3d_desc.height;
232 
233     return D3D_OK;
234 }
235 
d3d9_surface_LockRect(IDirect3DSurface9 * iface,D3DLOCKED_RECT * locked_rect,const RECT * rect,DWORD flags)236 static HRESULT WINAPI d3d9_surface_LockRect(IDirect3DSurface9 *iface,
237         D3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
238 {
239     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
240     struct wined3d_box box;
241     struct wined3d_map_desc map_desc;
242     HRESULT hr;
243 
244     TRACE("iface %p, locked_rect %p, rect %s, flags %#x.\n",
245             iface, locked_rect, wine_dbgstr_rect(rect), flags);
246 
247     if (rect)
248         wined3d_box_set(&box, rect->left, rect->top, rect->right, rect->bottom, 0, 1);
249 
250     wined3d_mutex_lock();
251     hr = wined3d_resource_map(wined3d_texture_get_resource(surface->wined3d_texture), surface->sub_resource_idx,
252             &map_desc, rect ? &box : NULL, wined3dmapflags_from_d3dmapflags(flags));
253     wined3d_mutex_unlock();
254 
255     if (SUCCEEDED(hr))
256     {
257         locked_rect->Pitch = map_desc.row_pitch;
258         locked_rect->pBits = map_desc.data;
259     }
260 
261     return hr;
262 }
263 
d3d9_surface_UnlockRect(IDirect3DSurface9 * iface)264 static HRESULT WINAPI d3d9_surface_UnlockRect(IDirect3DSurface9 *iface)
265 {
266     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
267     HRESULT hr;
268 
269     TRACE("iface %p.\n", iface);
270 
271     wined3d_mutex_lock();
272     hr = wined3d_resource_unmap(wined3d_texture_get_resource(surface->wined3d_texture), surface->sub_resource_idx);
273     if (SUCCEEDED(hr) && surface->texture)
274         d3d9_texture_flag_auto_gen_mipmap(surface->texture);
275     wined3d_mutex_unlock();
276 
277     if (hr == WINEDDERR_NOTLOCKED)
278     {
279         D3DRESOURCETYPE type;
280         if (surface->texture)
281             type = IDirect3DBaseTexture9_GetType(&surface->texture->IDirect3DBaseTexture9_iface);
282         else
283             type = D3DRTYPE_SURFACE;
284         hr = type == D3DRTYPE_TEXTURE ? D3D_OK : D3DERR_INVALIDCALL;
285     }
286     return hr;
287 }
288 
d3d9_surface_GetDC(IDirect3DSurface9 * iface,HDC * dc)289 static HRESULT WINAPI d3d9_surface_GetDC(IDirect3DSurface9 *iface, HDC *dc)
290 {
291     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
292     HRESULT hr;
293 
294     TRACE("iface %p, dc %p.\n", iface, dc);
295 
296     wined3d_mutex_lock();
297     hr = wined3d_texture_get_dc(surface->wined3d_texture, surface->sub_resource_idx, dc);
298     wined3d_mutex_unlock();
299 
300     return hr;
301 }
302 
d3d9_surface_ReleaseDC(IDirect3DSurface9 * iface,HDC dc)303 static HRESULT WINAPI d3d9_surface_ReleaseDC(IDirect3DSurface9 *iface, HDC dc)
304 {
305     struct d3d9_surface *surface = impl_from_IDirect3DSurface9(iface);
306     HRESULT hr;
307 
308     TRACE("iface %p, dc %p.\n", iface, dc);
309 
310     wined3d_mutex_lock();
311     hr = wined3d_texture_release_dc(surface->wined3d_texture, surface->sub_resource_idx, dc);
312     if (SUCCEEDED(hr) && surface->texture)
313         d3d9_texture_flag_auto_gen_mipmap(surface->texture);
314     wined3d_mutex_unlock();
315 
316     return hr;
317 }
318 
319 static const struct IDirect3DSurface9Vtbl d3d9_surface_vtbl =
320 {
321     /* IUnknown */
322     d3d9_surface_QueryInterface,
323     d3d9_surface_AddRef,
324     d3d9_surface_Release,
325     /* IDirect3DResource9 */
326     d3d9_surface_GetDevice,
327     d3d9_surface_SetPrivateData,
328     d3d9_surface_GetPrivateData,
329     d3d9_surface_FreePrivateData,
330     d3d9_surface_SetPriority,
331     d3d9_surface_GetPriority,
332     d3d9_surface_PreLoad,
333     d3d9_surface_GetType,
334     /* IDirect3DSurface9 */
335     d3d9_surface_GetContainer,
336     d3d9_surface_GetDesc,
337     d3d9_surface_LockRect,
338     d3d9_surface_UnlockRect,
339     d3d9_surface_GetDC,
340     d3d9_surface_ReleaseDC,
341 };
342 
surface_wined3d_object_destroyed(void * parent)343 static void STDMETHODCALLTYPE surface_wined3d_object_destroyed(void *parent)
344 {
345     struct d3d9_surface *surface = parent;
346     d3d9_resource_cleanup(&surface->resource);
347     heap_free(surface);
348 }
349 
350 static const struct wined3d_parent_ops d3d9_surface_wined3d_parent_ops =
351 {
352     surface_wined3d_object_destroyed,
353 };
354 
surface_init(struct d3d9_surface * surface,struct wined3d_texture * wined3d_texture,unsigned int sub_resource_idx,const struct wined3d_parent_ops ** parent_ops)355 void surface_init(struct d3d9_surface *surface, struct wined3d_texture *wined3d_texture,
356         unsigned int sub_resource_idx, const struct wined3d_parent_ops **parent_ops)
357 {
358     IDirect3DBaseTexture9 *texture;
359 
360     surface->IDirect3DSurface9_iface.lpVtbl = &d3d9_surface_vtbl;
361     d3d9_resource_init(&surface->resource);
362     surface->resource.refcount = 0;
363     list_init(&surface->rtv_entry);
364     surface->container = wined3d_texture_get_parent(wined3d_texture);
365     surface->wined3d_texture = wined3d_texture;
366     surface->sub_resource_idx = sub_resource_idx;
367 
368     if (surface->container && SUCCEEDED(IUnknown_QueryInterface(surface->container,
369             &IID_IDirect3DBaseTexture9, (void **)&texture)))
370     {
371         surface->texture = unsafe_impl_from_IDirect3DBaseTexture9(texture);
372         IDirect3DBaseTexture9_Release(texture);
373     }
374 
375     *parent_ops = &d3d9_surface_wined3d_parent_ops;
376 }
377 
view_wined3d_object_destroyed(void * parent)378 static void STDMETHODCALLTYPE view_wined3d_object_destroyed(void *parent)
379 {
380     struct d3d9_surface *surface = parent;
381 
382     /* If the surface reference count drops to zero, we release our reference
383      * to the view, but don't clear the pointer yet, in case e.g. a
384      * GetRenderTarget() call brings the surface back before the view is
385      * actually destroyed. When the view is destroyed, we need to clear the
386      * pointer, or a subsequent surface AddRef() would reference it again.
387      *
388      * This is safe because as long as the view still has a reference to the
389      * texture, the surface is also still alive, and we're called before the
390      * view releases that reference. */
391     surface->wined3d_rtv = NULL;
392     list_remove(&surface->rtv_entry);
393 }
394 
395 static const struct wined3d_parent_ops d3d9_view_wined3d_parent_ops =
396 {
397     view_wined3d_object_destroyed,
398 };
399 
d3d9_surface_get_device(const struct d3d9_surface * surface)400 struct d3d9_device *d3d9_surface_get_device(const struct d3d9_surface *surface)
401 {
402     IDirect3DDevice9Ex *device;
403     device = surface->texture ? surface->texture->parent_device : surface->parent_device;
404     return impl_from_IDirect3DDevice9Ex(device);
405 }
406 
d3d9_surface_acquire_rendertarget_view(struct d3d9_surface * surface)407 struct wined3d_rendertarget_view *d3d9_surface_acquire_rendertarget_view(struct d3d9_surface *surface)
408 {
409     HRESULT hr;
410 
411     /* The surface reference count can be equal to 0 when this function is
412      * called. In order to properly manage the render target view reference
413      * count, we temporarily increment the surface reference count. */
414     d3d9_surface_AddRef(&surface->IDirect3DSurface9_iface);
415 
416     if (surface->wined3d_rtv)
417         return surface->wined3d_rtv;
418 
419     if (FAILED(hr = wined3d_rendertarget_view_create_from_sub_resource(surface->wined3d_texture,
420             surface->sub_resource_idx, surface, &d3d9_view_wined3d_parent_ops, &surface->wined3d_rtv)))
421     {
422         ERR("Failed to create rendertarget view, hr %#x.\n", hr);
423         d3d9_surface_Release(&surface->IDirect3DSurface9_iface);
424         return NULL;
425     }
426 
427     if (surface->texture)
428         list_add_head(&surface->texture->rtv_list, &surface->rtv_entry);
429 
430     return surface->wined3d_rtv;
431 }
432 
d3d9_surface_release_rendertarget_view(struct d3d9_surface * surface,struct wined3d_rendertarget_view * rtv)433 void d3d9_surface_release_rendertarget_view(struct d3d9_surface *surface,
434         struct wined3d_rendertarget_view *rtv)
435 {
436     if (rtv)
437         d3d9_surface_Release(&surface->IDirect3DSurface9_iface);
438 }
439 
unsafe_impl_from_IDirect3DSurface9(IDirect3DSurface9 * iface)440 struct d3d9_surface *unsafe_impl_from_IDirect3DSurface9(IDirect3DSurface9 *iface)
441 {
442     if (!iface)
443         return NULL;
444     assert(iface->lpVtbl == &d3d9_surface_vtbl);
445 
446     return impl_from_IDirect3DSurface9(iface);
447 }
448