comctl32: Add implementation of LVS_EX_ONECLICKACTIVATE.
[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     ok(d3d8_ptr != NULL, "Failed to create IDirect3D8 object\n");
48     if (!d3d8_ptr) return NULL;
49
50     IDirect3D8_GetAdapterDisplayMode(d3d8_ptr, D3DADAPTER_DEFAULT, &d3ddm );
51     ZeroMemory(&present_parameters, sizeof(present_parameters));
52     present_parameters.Windowed = TRUE;
53     present_parameters.hDeviceWindow = create_window();
54     present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
55     present_parameters.BackBufferFormat = d3ddm.Format;
56
57     hr = IDirect3D8_CreateDevice(d3d8_ptr, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
58             NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &device_ptr);
59
60     if(FAILED(hr))
61     {
62         skip("could not create device, IDirect3D8_CreateDevice returned %#x\n", hr);
63         return NULL;
64     }
65
66     return device_ptr;
67 }
68
69 /* Test the behaviour of the IDirect3DDevice8::CreateImageSurface method.
70
71 Expected behaviour (and also documented in the original DX8 docs) is that the
72 call returns a surface with the SYSTEMMEM pool type. Games like Max Payne 1
73 and 2 which use Direct3D8 calls depend on this behaviour.
74
75 A short remark in the DX9 docs however states that the pool of the
76 returned surface object is of type SCRATCH. This is misinformation and results
77 in screenshots not appearing in the savegame loading menu of both games
78 mentioned above (engine tries to display a texture from the scratch pool).
79
80 This test verifies that the behaviour described in the original D3D8 docs is
81 the correct one. For more information about this issue, see the MSDN:
82
83 D3D9 docs: "Converting to Direct3D 9"
84 D3D9 reference: "IDirect3DDevice9::CreateOffscreenPlainSurface"
85 D3D8 reference: "IDirect3DDevice8::CreateImageSurface"
86 */
87
88 static void test_image_surface_pool(IDirect3DDevice8 *device) {
89     IDirect3DSurface8 *surface = 0;
90     D3DSURFACE_DESC surf_desc;
91     HRESULT hr;
92
93     hr = IDirect3DDevice8_CreateImageSurface(device, 128, 128, D3DFMT_A8R8G8B8, &surface);
94     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
95
96     hr = IDirect3DSurface8_GetDesc(surface, &surf_desc);
97     ok(SUCCEEDED(hr), "GetDesc failed (0x%08x)\n", hr);
98
99     ok((surf_desc.Pool == D3DPOOL_SYSTEMMEM),
100         "CreateImageSurface returns surface with unexpected pool type %u (should be SYSTEMMEM = 2)\n", surf_desc.Pool);
101
102     IDirect3DSurface8_Release(surface);
103 }
104
105 static void test_surface_get_container(IDirect3DDevice8 *device_ptr)
106 {
107     IDirect3DTexture8 *texture_ptr = 0;
108     IDirect3DSurface8 *surface_ptr = 0;
109     void *container_ptr;
110     HRESULT hr;
111
112     hr = IDirect3DDevice8_CreateTexture(device_ptr, 128, 128, 1, 0,
113             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture_ptr);
114     ok(SUCCEEDED(hr) && texture_ptr != NULL, "CreateTexture returned: hr %#x, texture_ptr %p. "
115         "Expected hr %#x, texture_ptr != %p\n", hr, texture_ptr, D3D_OK, NULL);
116     if (!texture_ptr || FAILED(hr)) goto cleanup;
117
118     hr = IDirect3DTexture8_GetSurfaceLevel(texture_ptr, 0, &surface_ptr);
119     ok(SUCCEEDED(hr) && surface_ptr != NULL, "GetSurfaceLevel returned: hr %#x, surface_ptr %p. "
120         "Expected hr %#x, surface_ptr != %p\n", hr, surface_ptr, D3D_OK, NULL);
121     if (!surface_ptr || FAILED(hr)) goto cleanup;
122
123     /* These should work... */
124     container_ptr = (void *)0x1337c0d3;
125     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IUnknown, &container_ptr);
126     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
127         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
128     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
129
130     container_ptr = (void *)0x1337c0d3;
131     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DResource8, &container_ptr);
132     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
133         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
134     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
135
136     container_ptr = (void *)0x1337c0d3;
137     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DBaseTexture8, &container_ptr);
138     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
139         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
140     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
141
142     container_ptr = (void *)0x1337c0d3;
143     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DTexture8, &container_ptr);
144     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
145         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
146     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
147
148     /* ...and this one shouldn't. This should return E_NOINTERFACE and set container_ptr to NULL */
149     container_ptr = (void *)0x1337c0d3;
150     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DSurface8, &container_ptr);
151     ok(hr == E_NOINTERFACE && container_ptr == NULL, "GetContainer returned: hr %#x, container_ptr %p. "
152         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, E_NOINTERFACE, NULL);
153     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
154
155 cleanup:
156     if (texture_ptr) IDirect3DTexture8_Release(texture_ptr);
157     if (surface_ptr) IDirect3DSurface8_Release(surface_ptr);
158 }
159
160 static void test_lockrect_invalid(IDirect3DDevice8 *device)
161 {
162     IDirect3DSurface8 *surface = 0;
163     D3DLOCKED_RECT locked_rect;
164     unsigned int i;
165     BYTE *base;
166     HRESULT hr;
167
168     const RECT valid[] = {
169         {60, 60, 68, 68},
170         {120, 60, 128, 68},
171         {60, 120, 68, 128},
172     };
173
174     const RECT invalid[] = {
175         {60, 60, 60, 68},       /* 0 height */
176         {60, 60, 68, 60},       /* 0 width */
177         {68, 60, 60, 68},       /* left > right */
178         {60, 68, 68, 60},       /* top > bottom */
179         {-8, 60,  0, 68},       /* left < surface */
180         {60, -8, 68,  0},       /* top < surface */
181         {-16, 60, -8, 68},      /* right < surface */
182         {60, -16, 68, -8},      /* bottom < surface */
183         {60, 60, 136, 68},      /* right > surface */
184         {60, 60, 68, 136},      /* bottom > surface */
185         {136, 60, 144, 68},     /* left > surface */
186         {60, 136, 68, 144},     /* top > surface */
187     };
188
189     hr = IDirect3DDevice8_CreateImageSurface(device, 128, 128, D3DFMT_A8R8G8B8, &surface);
190     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
191
192     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
193     ok(SUCCEEDED(hr), "LockRect failed (0x%08x)\n", hr);
194
195     base = locked_rect.pBits;
196
197     hr = IDirect3DSurface8_UnlockRect(surface);
198     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
199
200     for (i = 0; i < (sizeof(valid) / sizeof(*valid)); ++i)
201     {
202         unsigned int offset, expected_offset;
203         const RECT *rect = &valid[i];
204
205         locked_rect.pBits = (BYTE *)0xdeadbeef;
206         locked_rect.Pitch = 0xdeadbeef;
207
208         hr = IDirect3DSurface8_LockRect(surface, &locked_rect, rect, 0);
209         ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]\n",
210                 hr, rect->left, rect->top, rect->right, rect->bottom);
211
212         offset = (BYTE *)locked_rect.pBits - base;
213         expected_offset = rect->top * locked_rect.Pitch + rect->left * 4;
214         ok(offset == expected_offset, "Got offset %u, expected offset %u for rect [%d, %d]->[%d, %d]\n",
215                 offset, expected_offset, rect->left, rect->top, rect->right, rect->bottom);
216
217         hr = IDirect3DSurface8_UnlockRect(surface);
218         ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
219     }
220
221     for (i = 0; i < (sizeof(invalid) / sizeof(*invalid)); ++i)
222     {
223         const RECT *rect = &invalid[i];
224
225         hr = IDirect3DSurface8_LockRect(surface, &locked_rect, rect, 0);
226         ok(hr == D3DERR_INVALIDCALL, "LockRect returned 0x%08x for rect [%d, %d]->[%d, %d]"
227                 ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, rect->left, rect->top,
228                 rect->right, rect->bottom, D3DERR_INVALIDCALL);
229     }
230
231     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
232     ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect NULL\n", hr);
233     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
234     ok(hr == D3DERR_INVALIDCALL, "Double LockRect returned 0x%08x for rect NULL\n", hr);
235     hr = IDirect3DSurface8_UnlockRect(surface);
236     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
237
238     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[0], 0);
239     ok(hr == D3D_OK, "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
240             ", expected D3D_OK (0x%08x)\n", hr, valid[0].left, valid[0].top,
241             valid[0].right, valid[0].bottom, D3D_OK);
242     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[0], 0);
243     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
244             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, valid[0].left, valid[0].top,
245             valid[0].right, valid[0].bottom,D3DERR_INVALIDCALL);
246     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[1], 0);
247     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
248             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, valid[1].left, valid[1].top,
249             valid[1].right, valid[1].bottom, D3DERR_INVALIDCALL);
250     hr = IDirect3DSurface8_UnlockRect(surface);
251     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
252
253     IDirect3DSurface8_Release(surface);
254 }
255
256 static unsigned long getref(IUnknown *iface)
257 {
258     IUnknown_AddRef(iface);
259     return IUnknown_Release(iface);
260 }
261
262 static void test_private_data(IDirect3DDevice8 *device)
263 {
264     HRESULT hr;
265     IDirect3DSurface8 *surface;
266     ULONG ref, ref2;
267     IUnknown *ptr;
268     DWORD size = sizeof(IUnknown *);
269
270     hr = IDirect3DDevice8_CreateImageSurface(device, 4, 4, D3DFMT_A8R8G8B8, &surface);
271     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
272     if(!surface)
273     {
274         return;
275     }
276
277     /* This fails */
278     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, 0, D3DSPD_IUNKNOWN);
279     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
280     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, 5, D3DSPD_IUNKNOWN);
281     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
282     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, sizeof(IUnknown *) * 2, D3DSPD_IUNKNOWN);
283     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
284
285     ref = getref((IUnknown *) device);
286     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
287     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
288     ref2 = getref((IUnknown *) device);
289     ok(ref2 == ref + 1, "Object reference is %d, expected %d\n", ref2, ref + 1);
290     hr = IDirect3DSurface8_FreePrivateData(surface, &IID_IDirect3DSurface8);
291     ok(hr == D3D_OK, "IDirect3DSurface8_FreePrivateData returned %08x\n", hr);
292     ref2 = getref((IUnknown *) device);
293     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
294
295     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
296     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
297     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, surface, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
298     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
299     ref2 = getref((IUnknown *) device);
300     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
301
302     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
303     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
304     hr = IDirect3DSurface8_GetPrivateData(surface, &IID_IDirect3DSurface8, &ptr, &size);
305     ok(hr == D3D_OK, "IDirect3DSurface8_GetPrivateData failed with %08x\n", hr);
306     ref2 = getref((IUnknown *) device);
307     /* Object is NOT being addrefed */
308     ok(ptr == (IUnknown *) device, "Returned interface pointer is %p, expected %p\n", ptr, device);
309     ok(ref2 == ref + 2, "Object reference is %d, expected %d. ptr at %p, orig at %p\n", ref2, ref + 2, ptr, device);
310     IUnknown_Release(ptr);
311
312     IDirect3DSurface8_Release(surface);
313
314     /* Destroying the surface frees the held reference */
315     ref2 = getref((IUnknown *) device);
316     /* -1 because the surface was released and held a reference before */
317     ok(ref2 == (ref - 1), "Object reference is %d, expected %d\n", ref2, ref - 1);
318 }
319
320 START_TEST(surface)
321 {
322     HMODULE d3d8_handle;
323     IDirect3DDevice8 *device_ptr;
324
325     d3d8_handle = LoadLibraryA("d3d8.dll");
326     if (!d3d8_handle)
327     {
328         skip("Could not load d3d8.dll\n");
329         return;
330     }
331
332     device_ptr = init_d3d8(d3d8_handle);
333     if (!device_ptr) return;
334
335     test_image_surface_pool(device_ptr);
336     test_surface_get_container(device_ptr);
337     test_lockrect_invalid(device_ptr);
338     test_private_data(device_ptr);
339 }