ole32: Fix memory leaks in the storage test.
[wine] / dlls / d3d8 / tests / surface.c
1 /*
2  * Copyright 2006-2007 Henri Verbeet
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #define COBJMACROS
19 #include <d3d8.h>
20 #include "wine/test.h"
21
22 static HWND create_window(void)
23 {
24     WNDCLASS wc = {0};
25     wc.lpfnWndProc = DefWindowProc;
26     wc.lpszClassName = "d3d8_test_wc";
27     RegisterClass(&wc);
28
29     return CreateWindow("d3d8_test_wc", "d3d8_test",
30             0, 0, 0, 0, 0, 0, 0, 0, 0);
31 }
32
33 static IDirect3DDevice8 *init_d3d8(HMODULE d3d8_handle)
34 {
35     IDirect3D8 * (__stdcall * d3d8_create)(UINT SDKVersion) = 0;
36     IDirect3D8 *d3d8_ptr = 0;
37     IDirect3DDevice8 *device_ptr = 0;
38     D3DPRESENT_PARAMETERS present_parameters;
39     D3DDISPLAYMODE               d3ddm;
40     HRESULT hr;
41
42     d3d8_create = (void *)GetProcAddress(d3d8_handle, "Direct3DCreate8");
43     ok(d3d8_create != NULL, "Failed to get address of Direct3DCreate8\n");
44     if (!d3d8_create) return NULL;
45
46     d3d8_ptr = d3d8_create(D3D_SDK_VERSION);
47     if (!d3d8_ptr)
48     {
49         skip("could not create D3D8\n");
50         return NULL;
51     }
52
53     IDirect3D8_GetAdapterDisplayMode(d3d8_ptr, D3DADAPTER_DEFAULT, &d3ddm );
54     ZeroMemory(&present_parameters, sizeof(present_parameters));
55     present_parameters.Windowed = TRUE;
56     present_parameters.hDeviceWindow = create_window();
57     present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
58     present_parameters.BackBufferFormat = d3ddm.Format;
59
60     hr = IDirect3D8_CreateDevice(d3d8_ptr, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
61             NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &device_ptr);
62
63     if(FAILED(hr))
64     {
65         skip("could not create device, IDirect3D8_CreateDevice returned %#x\n", hr);
66         return NULL;
67     }
68
69     return device_ptr;
70 }
71
72 /* Test the behaviour of the IDirect3DDevice8::CreateImageSurface method.
73
74 Expected behaviour (and also documented in the original DX8 docs) is that the
75 call returns a surface with the SYSTEMMEM pool type. Games like Max Payne 1
76 and 2 which use Direct3D8 calls depend on this behaviour.
77
78 A short remark in the DX9 docs however states that the pool of the
79 returned surface object is of type SCRATCH. This is misinformation and results
80 in screenshots not appearing in the savegame loading menu of both games
81 mentioned above (engine tries to display a texture from the scratch pool).
82
83 This test verifies that the behaviour described in the original D3D8 docs is
84 the correct one. For more information about this issue, see the MSDN:
85
86 D3D9 docs: "Converting to Direct3D 9"
87 D3D9 reference: "IDirect3DDevice9::CreateOffscreenPlainSurface"
88 D3D8 reference: "IDirect3DDevice8::CreateImageSurface"
89 */
90
91 static void test_image_surface_pool(IDirect3DDevice8 *device) {
92     IDirect3DSurface8 *surface = 0;
93     D3DSURFACE_DESC surf_desc;
94     HRESULT hr;
95
96     hr = IDirect3DDevice8_CreateImageSurface(device, 128, 128, D3DFMT_A8R8G8B8, &surface);
97     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
98
99     hr = IDirect3DSurface8_GetDesc(surface, &surf_desc);
100     ok(SUCCEEDED(hr), "GetDesc failed (0x%08x)\n", hr);
101
102     ok((surf_desc.Pool == D3DPOOL_SYSTEMMEM),
103         "CreateImageSurface returns surface with unexpected pool type %u (should be SYSTEMMEM = 2)\n", surf_desc.Pool);
104
105     IDirect3DSurface8_Release(surface);
106 }
107
108 static void test_surface_get_container(IDirect3DDevice8 *device_ptr)
109 {
110     IDirect3DTexture8 *texture_ptr = 0;
111     IDirect3DSurface8 *surface_ptr = 0;
112     void *container_ptr;
113     HRESULT hr;
114
115     hr = IDirect3DDevice8_CreateTexture(device_ptr, 128, 128, 1, 0,
116             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture_ptr);
117     ok(SUCCEEDED(hr) && texture_ptr != NULL, "CreateTexture returned: hr %#x, texture_ptr %p. "
118         "Expected hr %#x, texture_ptr != %p\n", hr, texture_ptr, D3D_OK, NULL);
119     if (!texture_ptr || FAILED(hr)) goto cleanup;
120
121     hr = IDirect3DTexture8_GetSurfaceLevel(texture_ptr, 0, &surface_ptr);
122     ok(SUCCEEDED(hr) && surface_ptr != NULL, "GetSurfaceLevel returned: hr %#x, surface_ptr %p. "
123         "Expected hr %#x, surface_ptr != %p\n", hr, surface_ptr, D3D_OK, NULL);
124     if (!surface_ptr || FAILED(hr)) goto cleanup;
125
126     /* These should work... */
127     container_ptr = (void *)0x1337c0d3;
128     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IUnknown, &container_ptr);
129     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
130         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
131     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
132
133     container_ptr = (void *)0x1337c0d3;
134     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DResource8, &container_ptr);
135     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
136         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
137     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
138
139     container_ptr = (void *)0x1337c0d3;
140     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DBaseTexture8, &container_ptr);
141     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
142         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
143     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
144
145     container_ptr = (void *)0x1337c0d3;
146     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DTexture8, &container_ptr);
147     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
148         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
149     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
150
151     /* ...and this one shouldn't. This should return E_NOINTERFACE and set container_ptr to NULL */
152     container_ptr = (void *)0x1337c0d3;
153     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DSurface8, &container_ptr);
154     ok(hr == E_NOINTERFACE && container_ptr == NULL, "GetContainer returned: hr %#x, container_ptr %p. "
155         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, E_NOINTERFACE, NULL);
156     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
157
158 cleanup:
159     if (texture_ptr) IDirect3DTexture8_Release(texture_ptr);
160     if (surface_ptr) IDirect3DSurface8_Release(surface_ptr);
161 }
162
163 static void test_lockrect_invalid(IDirect3DDevice8 *device)
164 {
165     IDirect3DSurface8 *surface = 0;
166     D3DLOCKED_RECT locked_rect;
167     unsigned int i;
168     BYTE *base;
169     HRESULT hr;
170
171     const RECT valid[] = {
172         {60, 60, 68, 68},
173         {120, 60, 128, 68},
174         {60, 120, 68, 128},
175     };
176
177     const RECT invalid[] = {
178         {60, 60, 60, 68},       /* 0 height */
179         {60, 60, 68, 60},       /* 0 width */
180         {68, 60, 60, 68},       /* left > right */
181         {60, 68, 68, 60},       /* top > bottom */
182         {-8, 60,  0, 68},       /* left < surface */
183         {60, -8, 68,  0},       /* top < surface */
184         {-16, 60, -8, 68},      /* right < surface */
185         {60, -16, 68, -8},      /* bottom < surface */
186         {60, 60, 136, 68},      /* right > surface */
187         {60, 60, 68, 136},      /* bottom > surface */
188         {136, 60, 144, 68},     /* left > surface */
189         {60, 136, 68, 144},     /* top > surface */
190     };
191
192     hr = IDirect3DDevice8_CreateImageSurface(device, 128, 128, D3DFMT_A8R8G8B8, &surface);
193     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
194
195     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
196     ok(SUCCEEDED(hr), "LockRect failed (0x%08x)\n", hr);
197
198     base = locked_rect.pBits;
199
200     hr = IDirect3DSurface8_UnlockRect(surface);
201     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
202
203     for (i = 0; i < (sizeof(valid) / sizeof(*valid)); ++i)
204     {
205         unsigned int offset, expected_offset;
206         const RECT *rect = &valid[i];
207
208         locked_rect.pBits = (BYTE *)0xdeadbeef;
209         locked_rect.Pitch = 0xdeadbeef;
210
211         hr = IDirect3DSurface8_LockRect(surface, &locked_rect, rect, 0);
212         ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]\n",
213                 hr, rect->left, rect->top, rect->right, rect->bottom);
214
215         offset = (BYTE *)locked_rect.pBits - base;
216         expected_offset = rect->top * locked_rect.Pitch + rect->left * 4;
217         ok(offset == expected_offset, "Got offset %u, expected offset %u for rect [%d, %d]->[%d, %d]\n",
218                 offset, expected_offset, rect->left, rect->top, rect->right, rect->bottom);
219
220         hr = IDirect3DSurface8_UnlockRect(surface);
221         ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
222     }
223
224     for (i = 0; i < (sizeof(invalid) / sizeof(*invalid)); ++i)
225     {
226         const RECT *rect = &invalid[i];
227
228         hr = IDirect3DSurface8_LockRect(surface, &locked_rect, rect, 0);
229         ok(hr == D3DERR_INVALIDCALL, "LockRect returned 0x%08x for rect [%d, %d]->[%d, %d]"
230                 ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, rect->left, rect->top,
231                 rect->right, rect->bottom, D3DERR_INVALIDCALL);
232     }
233
234     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
235     ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect NULL\n", hr);
236     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
237     ok(hr == D3DERR_INVALIDCALL, "Double LockRect returned 0x%08x for rect NULL\n", hr);
238     hr = IDirect3DSurface8_UnlockRect(surface);
239     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
240
241     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[0], 0);
242     ok(hr == D3D_OK, "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
243             ", expected D3D_OK (0x%08x)\n", hr, valid[0].left, valid[0].top,
244             valid[0].right, valid[0].bottom, D3D_OK);
245     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[0], 0);
246     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
247             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, valid[0].left, valid[0].top,
248             valid[0].right, valid[0].bottom,D3DERR_INVALIDCALL);
249     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[1], 0);
250     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
251             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, valid[1].left, valid[1].top,
252             valid[1].right, valid[1].bottom, D3DERR_INVALIDCALL);
253     hr = IDirect3DSurface8_UnlockRect(surface);
254     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
255
256     IDirect3DSurface8_Release(surface);
257 }
258
259 static ULONG getref(IUnknown *iface)
260 {
261     IUnknown_AddRef(iface);
262     return IUnknown_Release(iface);
263 }
264
265 static void test_private_data(IDirect3DDevice8 *device)
266 {
267     HRESULT hr;
268     IDirect3DSurface8 *surface;
269     ULONG ref, ref2;
270     IUnknown *ptr;
271     DWORD size = sizeof(IUnknown *);
272
273     hr = IDirect3DDevice8_CreateImageSurface(device, 4, 4, D3DFMT_A8R8G8B8, &surface);
274     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
275     if(!surface)
276     {
277         return;
278     }
279
280     /* This fails */
281     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, 0, D3DSPD_IUNKNOWN);
282     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
283     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, 5, D3DSPD_IUNKNOWN);
284     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
285     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, sizeof(IUnknown *) * 2, D3DSPD_IUNKNOWN);
286     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
287
288     ref = getref((IUnknown *) device);
289     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
290     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
291     ref2 = getref((IUnknown *) device);
292     ok(ref2 == ref + 1, "Object reference is %d, expected %d\n", ref2, ref + 1);
293     hr = IDirect3DSurface8_FreePrivateData(surface, &IID_IDirect3DSurface8);
294     ok(hr == D3D_OK, "IDirect3DSurface8_FreePrivateData returned %08x\n", hr);
295     ref2 = getref((IUnknown *) device);
296     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
297
298     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
299     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
300     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, surface, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
301     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
302     ref2 = getref((IUnknown *) device);
303     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
304
305     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
306     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
307     hr = IDirect3DSurface8_GetPrivateData(surface, &IID_IDirect3DSurface8, &ptr, &size);
308     ok(hr == D3D_OK, "IDirect3DSurface8_GetPrivateData failed with %08x\n", hr);
309     ref2 = getref((IUnknown *) device);
310     /* Object is NOT being addrefed */
311     ok(ptr == (IUnknown *) device, "Returned interface pointer is %p, expected %p\n", ptr, device);
312     ok(ref2 == ref + 2, "Object reference is %d, expected %d. ptr at %p, orig at %p\n", ref2, ref + 2, ptr, device);
313     IUnknown_Release(ptr);
314
315     IDirect3DSurface8_Release(surface);
316
317     /* Destroying the surface frees the held reference */
318     ref2 = getref((IUnknown *) device);
319     /* -1 because the surface was released and held a reference before */
320     ok(ref2 == (ref - 1), "Object reference is %d, expected %d\n", ref2, ref - 1);
321 }
322
323 START_TEST(surface)
324 {
325     HMODULE d3d8_handle;
326     IDirect3DDevice8 *device_ptr;
327     ULONG refcount;
328
329     d3d8_handle = LoadLibraryA("d3d8.dll");
330     if (!d3d8_handle)
331     {
332         skip("Could not load d3d8.dll\n");
333         return;
334     }
335
336     device_ptr = init_d3d8(d3d8_handle);
337     if (!device_ptr) return;
338
339     test_image_surface_pool(device_ptr);
340     test_surface_get_container(device_ptr);
341     test_lockrect_invalid(device_ptr);
342     test_private_data(device_ptr);
343
344     refcount = IDirect3DDevice8_Release(device_ptr);
345     ok(!refcount, "Device has %u references left\n", refcount);
346 }