shell32: Indentation fix.
[wine] / dlls / d3d8 / tests / surface.c
1 /*
2  * Copyright 2006-2007 Henri Verbeet
3  * Copyright 2011 Stefan Dösinger for CodeWeavers
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 #define COBJMACROS
20 #include <d3d8.h>
21 #include "wine/test.h"
22
23 static HWND create_window(void)
24 {
25     WNDCLASS wc = {0};
26     wc.lpfnWndProc = DefWindowProc;
27     wc.lpszClassName = "d3d8_test_wc";
28     RegisterClass(&wc);
29
30     return CreateWindow("d3d8_test_wc", "d3d8_test",
31             0, 0, 0, 0, 0, 0, 0, 0, 0);
32 }
33
34 static IDirect3DDevice8 *init_d3d8(HMODULE d3d8_handle)
35 {
36     IDirect3D8 * (__stdcall * d3d8_create)(UINT SDKVersion) = 0;
37     IDirect3D8 *d3d8_ptr = 0;
38     IDirect3DDevice8 *device_ptr = 0;
39     D3DPRESENT_PARAMETERS present_parameters;
40     D3DDISPLAYMODE               d3ddm;
41     HRESULT hr;
42
43     d3d8_create = (void *)GetProcAddress(d3d8_handle, "Direct3DCreate8");
44     ok(d3d8_create != NULL, "Failed to get address of Direct3DCreate8\n");
45     if (!d3d8_create) return NULL;
46
47     d3d8_ptr = d3d8_create(D3D_SDK_VERSION);
48     if (!d3d8_ptr)
49     {
50         skip("could not create D3D8\n");
51         return NULL;
52     }
53
54     IDirect3D8_GetAdapterDisplayMode(d3d8_ptr, D3DADAPTER_DEFAULT, &d3ddm );
55     ZeroMemory(&present_parameters, sizeof(present_parameters));
56     present_parameters.Windowed = TRUE;
57     present_parameters.hDeviceWindow = create_window();
58     present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
59     present_parameters.BackBufferFormat = d3ddm.Format;
60     present_parameters.EnableAutoDepthStencil = TRUE;
61     present_parameters.AutoDepthStencilFormat = D3DFMT_D24S8;
62
63     hr = IDirect3D8_CreateDevice(d3d8_ptr, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
64             NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &device_ptr);
65
66     if(FAILED(hr))
67     {
68         skip("could not create device, IDirect3D8_CreateDevice returned %#x\n", hr);
69         return NULL;
70     }
71
72     return device_ptr;
73 }
74
75 /* Test the behaviour of the IDirect3DDevice8::CreateImageSurface method.
76
77 Expected behaviour (and also documented in the original DX8 docs) is that the
78 call returns a surface with the SYSTEMMEM pool type. Games like Max Payne 1
79 and 2 which use Direct3D8 calls depend on this behaviour.
80
81 A short remark in the DX9 docs however states that the pool of the
82 returned surface object is of type SCRATCH. This is misinformation and results
83 in screenshots not appearing in the savegame loading menu of both games
84 mentioned above (engine tries to display a texture from the scratch pool).
85
86 This test verifies that the behaviour described in the original D3D8 docs is
87 the correct one. For more information about this issue, see the MSDN:
88
89 D3D9 docs: "Converting to Direct3D 9"
90 D3D9 reference: "IDirect3DDevice9::CreateOffscreenPlainSurface"
91 D3D8 reference: "IDirect3DDevice8::CreateImageSurface"
92 */
93
94 static void test_image_surface_pool(IDirect3DDevice8 *device) {
95     IDirect3DSurface8 *surface = 0;
96     D3DSURFACE_DESC surf_desc;
97     HRESULT hr;
98
99     hr = IDirect3DDevice8_CreateImageSurface(device, 128, 128, D3DFMT_A8R8G8B8, &surface);
100     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
101
102     hr = IDirect3DSurface8_GetDesc(surface, &surf_desc);
103     ok(SUCCEEDED(hr), "GetDesc failed (0x%08x)\n", hr);
104
105     ok((surf_desc.Pool == D3DPOOL_SYSTEMMEM),
106         "CreateImageSurface returns surface with unexpected pool type %u (should be SYSTEMMEM = 2)\n", surf_desc.Pool);
107
108     IDirect3DSurface8_Release(surface);
109 }
110
111 static void test_surface_get_container(IDirect3DDevice8 *device_ptr)
112 {
113     IDirect3DTexture8 *texture_ptr = 0;
114     IDirect3DSurface8 *surface_ptr = 0;
115     void *container_ptr;
116     HRESULT hr;
117
118     hr = IDirect3DDevice8_CreateTexture(device_ptr, 128, 128, 1, 0,
119             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture_ptr);
120     ok(SUCCEEDED(hr) && texture_ptr != NULL, "CreateTexture returned: hr %#x, texture_ptr %p. "
121         "Expected hr %#x, texture_ptr != %p\n", hr, texture_ptr, D3D_OK, NULL);
122     if (!texture_ptr || FAILED(hr)) goto cleanup;
123
124     hr = IDirect3DTexture8_GetSurfaceLevel(texture_ptr, 0, &surface_ptr);
125     ok(SUCCEEDED(hr) && surface_ptr != NULL, "GetSurfaceLevel returned: hr %#x, surface_ptr %p. "
126         "Expected hr %#x, surface_ptr != %p\n", hr, surface_ptr, D3D_OK, NULL);
127     if (!surface_ptr || FAILED(hr)) goto cleanup;
128
129     /* These should work... */
130     container_ptr = (void *)0x1337c0d3;
131     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IUnknown, &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_IDirect3DResource8, &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_IDirect3DBaseTexture8, &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     container_ptr = (void *)0x1337c0d3;
149     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DTexture8, &container_ptr);
150     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
151         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
152     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
153
154     /* ...and this one shouldn't. This should return E_NOINTERFACE and set container_ptr to NULL */
155     container_ptr = (void *)0x1337c0d3;
156     hr = IDirect3DSurface8_GetContainer(surface_ptr, &IID_IDirect3DSurface8, &container_ptr);
157     ok(hr == E_NOINTERFACE && container_ptr == NULL, "GetContainer returned: hr %#x, container_ptr %p. "
158         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, E_NOINTERFACE, NULL);
159     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
160
161 cleanup:
162     if (texture_ptr) IDirect3DTexture8_Release(texture_ptr);
163     if (surface_ptr) IDirect3DSurface8_Release(surface_ptr);
164 }
165
166 static void test_lockrect_invalid(IDirect3DDevice8 *device)
167 {
168     IDirect3DSurface8 *surface = 0;
169     D3DLOCKED_RECT locked_rect;
170     unsigned int i;
171     BYTE *base;
172     HRESULT hr;
173
174     const RECT valid[] = {
175         {60, 60, 68, 68},
176         {120, 60, 128, 68},
177         {60, 120, 68, 128},
178     };
179
180     const RECT invalid[] = {
181         {60, 60, 60, 68},       /* 0 height */
182         {60, 60, 68, 60},       /* 0 width */
183         {68, 60, 60, 68},       /* left > right */
184         {60, 68, 68, 60},       /* top > bottom */
185         {-8, 60,  0, 68},       /* left < surface */
186         {60, -8, 68,  0},       /* top < surface */
187         {-16, 60, -8, 68},      /* right < surface */
188         {60, -16, 68, -8},      /* bottom < surface */
189         {60, 60, 136, 68},      /* right > surface */
190         {60, 60, 68, 136},      /* bottom > surface */
191         {136, 60, 144, 68},     /* left > surface */
192         {60, 136, 68, 144},     /* top > surface */
193     };
194
195     hr = IDirect3DDevice8_CreateImageSurface(device, 128, 128, D3DFMT_A8R8G8B8, &surface);
196     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
197
198     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
199     ok(SUCCEEDED(hr), "LockRect failed (0x%08x)\n", hr);
200
201     base = locked_rect.pBits;
202
203     hr = IDirect3DSurface8_UnlockRect(surface);
204     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
205
206     for (i = 0; i < (sizeof(valid) / sizeof(*valid)); ++i)
207     {
208         unsigned int offset, expected_offset;
209         const RECT *rect = &valid[i];
210
211         locked_rect.pBits = (BYTE *)0xdeadbeef;
212         locked_rect.Pitch = 0xdeadbeef;
213
214         hr = IDirect3DSurface8_LockRect(surface, &locked_rect, rect, 0);
215         ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]\n",
216                 hr, rect->left, rect->top, rect->right, rect->bottom);
217
218         offset = (BYTE *)locked_rect.pBits - base;
219         expected_offset = rect->top * locked_rect.Pitch + rect->left * 4;
220         ok(offset == expected_offset, "Got offset %u, expected offset %u for rect [%d, %d]->[%d, %d]\n",
221                 offset, expected_offset, rect->left, rect->top, rect->right, rect->bottom);
222
223         hr = IDirect3DSurface8_UnlockRect(surface);
224         ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
225     }
226
227     for (i = 0; i < (sizeof(invalid) / sizeof(*invalid)); ++i)
228     {
229         const RECT *rect = &invalid[i];
230
231         hr = IDirect3DSurface8_LockRect(surface, &locked_rect, rect, 0);
232         ok(hr == D3DERR_INVALIDCALL, "LockRect returned 0x%08x for rect [%d, %d]->[%d, %d]"
233                 ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, rect->left, rect->top,
234                 rect->right, rect->bottom, D3DERR_INVALIDCALL);
235     }
236
237     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
238     ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect NULL\n", hr);
239     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
240     ok(hr == D3DERR_INVALIDCALL, "Double LockRect returned 0x%08x for rect NULL\n", hr);
241     hr = IDirect3DSurface8_UnlockRect(surface);
242     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
243
244     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[0], 0);
245     ok(hr == D3D_OK, "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
246             ", expected D3D_OK (0x%08x)\n", hr, valid[0].left, valid[0].top,
247             valid[0].right, valid[0].bottom, D3D_OK);
248     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[0], 0);
249     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
250             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, valid[0].left, valid[0].top,
251             valid[0].right, valid[0].bottom,D3DERR_INVALIDCALL);
252     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &valid[1], 0);
253     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
254             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, valid[1].left, valid[1].top,
255             valid[1].right, valid[1].bottom, D3DERR_INVALIDCALL);
256     hr = IDirect3DSurface8_UnlockRect(surface);
257     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
258
259     IDirect3DSurface8_Release(surface);
260 }
261
262 static ULONG getref(IUnknown *iface)
263 {
264     IUnknown_AddRef(iface);
265     return IUnknown_Release(iface);
266 }
267
268 static void test_private_data(IDirect3DDevice8 *device)
269 {
270     HRESULT hr;
271     IDirect3DSurface8 *surface;
272     ULONG ref, ref2;
273     IUnknown *ptr;
274     DWORD size = sizeof(IUnknown *);
275
276     hr = IDirect3DDevice8_CreateImageSurface(device, 4, 4, D3DFMT_A8R8G8B8, &surface);
277     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
278     if(!surface)
279     {
280         return;
281     }
282
283     /* This fails */
284     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, 0, D3DSPD_IUNKNOWN);
285     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
286     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, 5, D3DSPD_IUNKNOWN);
287     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
288     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, sizeof(IUnknown *) * 2, D3DSPD_IUNKNOWN);
289     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
290
291     ref = getref((IUnknown *) device);
292     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8 /* Abuse this tag */, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
293     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
294     ref2 = getref((IUnknown *) device);
295     ok(ref2 == ref + 1, "Object reference is %d, expected %d\n", ref2, ref + 1);
296     hr = IDirect3DSurface8_FreePrivateData(surface, &IID_IDirect3DSurface8);
297     ok(hr == D3D_OK, "IDirect3DSurface8_FreePrivateData returned %08x\n", hr);
298     ref2 = getref((IUnknown *) device);
299     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
300
301     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
302     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
303     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, surface, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
304     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
305     ref2 = getref((IUnknown *) device);
306     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
307
308     hr = IDirect3DSurface8_SetPrivateData(surface, &IID_IDirect3DSurface8, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
309     ok(hr == D3D_OK, "IDirect3DSurface8_SetPrivateData failed with %08x\n", hr);
310     hr = IDirect3DSurface8_GetPrivateData(surface, &IID_IDirect3DSurface8, &ptr, &size);
311     ok(hr == D3D_OK, "IDirect3DSurface8_GetPrivateData failed with %08x\n", hr);
312     ref2 = getref((IUnknown *) device);
313     /* Object is NOT being addrefed */
314     ok(ptr == (IUnknown *) device, "Returned interface pointer is %p, expected %p\n", ptr, device);
315     ok(ref2 == ref + 2, "Object reference is %d, expected %d. ptr at %p, orig at %p\n", ref2, ref + 2, ptr, device);
316     IUnknown_Release(ptr);
317
318     IDirect3DSurface8_Release(surface);
319
320     /* Destroying the surface frees the held reference */
321     ref2 = getref((IUnknown *) device);
322     /* -1 because the surface was released and held a reference before */
323     ok(ref2 == (ref - 1), "Object reference is %d, expected %d\n", ref2, ref - 1);
324 }
325
326 static void test_surface_dimensions(IDirect3DDevice8 *device)
327 {
328     IDirect3DSurface8 *surface;
329     HRESULT hr;
330
331     hr = IDirect3DDevice8_CreateImageSurface(device, 0, 1, D3DFMT_A8R8G8B8, &surface);
332     ok(hr == D3DERR_INVALIDCALL, "CreateOffscreenPlainSurface returned %#x, expected D3DERR_INVALIDCALL.\n", hr);
333     if (SUCCEEDED(hr)) IDirect3DSurface8_Release(surface);
334
335     hr = IDirect3DDevice8_CreateImageSurface(device, 1, 0, D3DFMT_A8R8G8B8, &surface);
336     ok(hr == D3DERR_INVALIDCALL, "CreateOffscreenPlainSurface returned %#x, expected D3DERR_INVALIDCALL.\n", hr);
337     if (SUCCEEDED(hr)) IDirect3DSurface8_Release(surface);
338 }
339
340 static void test_surface_format_null(IDirect3DDevice8 *device)
341 {
342     static const D3DFORMAT D3DFMT_NULL = MAKEFOURCC('N','U','L','L');
343     IDirect3DTexture8 *texture;
344     IDirect3DSurface8 *surface;
345     IDirect3DSurface8 *rt, *ds;
346     D3DLOCKED_RECT locked_rect;
347     D3DSURFACE_DESC desc;
348     IDirect3D8 *d3d;
349     HRESULT hr;
350
351     IDirect3DDevice8_GetDirect3D(device, &d3d);
352
353     hr = IDirect3D8_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8,
354             D3DUSAGE_RENDERTARGET,  D3DRTYPE_SURFACE, D3DFMT_NULL);
355     if (hr != D3D_OK)
356     {
357         skip("No D3DFMT_NULL support, skipping test.\n");
358         IDirect3D8_Release(d3d);
359         return;
360     }
361
362     hr = IDirect3D8_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8,
363             D3DUSAGE_RENDERTARGET,  D3DRTYPE_TEXTURE, D3DFMT_NULL);
364     ok(hr == D3D_OK, "D3DFMT_NULL should be supported for render target textures, hr %#x.\n", hr);
365
366     hr = IDirect3D8_CheckDepthStencilMatch(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8,
367             D3DFMT_NULL, D3DFMT_D24S8);
368     ok(SUCCEEDED(hr), "Depth stencil match failed for D3DFMT_NULL, hr %#x.\n", hr);
369
370     IDirect3D8_Release(d3d);
371
372     hr = IDirect3DDevice8_CreateRenderTarget(device, 128, 128, D3DFMT_NULL,
373             D3DMULTISAMPLE_NONE, TRUE, &surface);
374     ok(SUCCEEDED(hr), "Failed to create render target, hr %#x.\n", hr);
375
376     hr = IDirect3DDevice8_GetRenderTarget(device, &rt);
377     ok(SUCCEEDED(hr), "Failed to get original render target, hr %#x.\n", hr);
378
379     hr = IDirect3DDevice8_GetDepthStencilSurface(device, &ds);
380     ok(SUCCEEDED(hr), "Failed to get original depth/stencil, hr %#x.\n", hr);
381
382     hr = IDirect3DDevice8_SetRenderTarget(device, surface, NULL);
383     ok(SUCCEEDED(hr), "Failed to set render target, hr %#x.\n", hr);
384
385     hr = IDirect3DDevice8_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0x00000000, 0.0f, 0);
386     ok(SUCCEEDED(hr), "Clear failed, hr %#x.\n", hr);
387
388     hr = IDirect3DDevice8_SetRenderTarget(device, rt, ds);
389     ok(SUCCEEDED(hr), "Failed to set render target, hr %#x.\n", hr);
390
391     IDirect3DSurface8_Release(rt);
392     IDirect3DSurface8_Release(ds);
393
394     hr = IDirect3DSurface8_GetDesc(surface, &desc);
395     ok(SUCCEEDED(hr), "Failed to get surface desc, hr %#x.\n", hr);
396     ok(desc.Width == 128, "Expected width 128, got %u.\n", desc.Width);
397     ok(desc.Height == 128, "Expected height 128, got %u.\n", desc.Height);
398
399     hr = IDirect3DSurface8_LockRect(surface, &locked_rect, NULL, 0);
400     ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
401     ok(locked_rect.Pitch, "Expected non-zero pitch, got %u.\n", locked_rect.Pitch);
402     ok(!!locked_rect.pBits, "Expected non-NULL pBits, got %p.\n", locked_rect.pBits);
403
404     hr = IDirect3DSurface8_UnlockRect(surface);
405     ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
406
407     IDirect3DSurface8_Release(surface);
408
409     hr = IDirect3DDevice8_CreateTexture(device, 128, 128, 0, D3DUSAGE_RENDERTARGET,
410             D3DFMT_NULL, D3DPOOL_DEFAULT, &texture);
411     ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
412     IDirect3DTexture8_Release(texture);
413 }
414
415 static void test_surface_double_unlock(IDirect3DDevice8 *device)
416 {
417     static  struct
418     {
419         D3DPOOL pool;
420         const char *name;
421     }
422     pools[] =
423     {
424         { D3DPOOL_DEFAULT,      "D3DPOOL_DEFAULT"   },
425         { D3DPOOL_SYSTEMMEM,    "D3DPOOL_SYSTEMMEM" },
426     };
427     IDirect3DSurface8 *surface;
428     unsigned int i;
429     HRESULT hr;
430     D3DLOCKED_RECT lr;
431     IDirect3D8 *d3d;
432
433     hr = IDirect3DDevice8_GetDirect3D(device, &d3d);
434     ok(SUCCEEDED(hr), "IDirect3DDevice8_GetDirect3D failed, hr = 0x%08x\n", hr);
435
436     for (i = 0; i < (sizeof(pools) / sizeof(*pools)); i++)
437     {
438         switch (pools[i].pool)
439         {
440             case D3DPOOL_DEFAULT:
441                 hr = IDirect3D8_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET,
442                         D3DRTYPE_SURFACE, D3DFMT_X8R8G8B8);
443                 if (FAILED(hr))
444                 {
445                     skip("D3DFMT_X8R8G8B8 render targets not supported, skipping double unlock DEFAULT pool test\n");
446                     continue;
447                 }
448
449                 hr = IDirect3DDevice8_CreateRenderTarget(device, 64, 64, D3DFMT_X8R8G8B8,
450                         D3DMULTISAMPLE_NONE, TRUE, &surface);
451                 ok(SUCCEEDED(hr), "IDirect3DDevice8_CreateRenderTarget failed, hr = 0x%08x, pool %s\n",
452                         hr, pools[i].name);
453                 break;
454
455             case D3DPOOL_SYSTEMMEM:
456                 hr = IDirect3DDevice8_CreateImageSurface(device, 64, 64, D3DFMT_X8R8G8B8, &surface);
457                 ok(SUCCEEDED(hr), "IDirect3DDevice8_CreateImageSurface failed, hr = 0x%08x, pool %s\n",
458                         hr, pools[i].name);
459                 break;
460
461             default:
462                 break;
463         }
464
465         hr = IDirect3DSurface8_UnlockRect(surface);
466         ok(hr == D3DERR_INVALIDCALL, "Unlock without lock returned 0x%08x, expected 0x%08x, pool %s\n",
467                 hr, D3DERR_INVALIDCALL, pools[i].name);
468
469         hr = IDirect3DSurface8_LockRect(surface, &lr, NULL, 0);
470         ok(SUCCEEDED(hr), "IDirect3DSurface8_LockRect failed, hr = 0x%08x, pool %s\n",
471                 hr, pools[i].name);
472         hr = IDirect3DSurface8_UnlockRect(surface);
473         ok(SUCCEEDED(hr), "IDirect3DSurface8_UnlockRect failed, hr = 0x%08x, pool %s\n",
474                 hr, pools[i].name);
475
476         hr = IDirect3DSurface8_UnlockRect(surface);
477         ok(hr == D3DERR_INVALIDCALL, "Double unlock returned 0x%08x, expected 0x%08x, pool %s\n",
478                 hr, D3DERR_INVALIDCALL, pools[i].name);
479
480         IDirect3DSurface8_Release(surface);
481     }
482
483     IDirect3D8_Release(d3d);
484 }
485
486 static void test_surface_lockrect_blocks(IDirect3DDevice8 *device)
487 {
488     IDirect3DTexture8 *texture;
489     IDirect3DSurface8 *surface;
490     IDirect3D8 *d3d;
491     D3DLOCKED_RECT locked_rect;
492     unsigned int i, j;
493     HRESULT hr;
494     RECT rect;
495
496     const struct
497     {
498         D3DFORMAT fmt;
499         const char *name;
500         unsigned int block_width;
501         unsigned int block_height;
502         BOOL broken;
503     }
504     formats[] =
505     {
506         {D3DFMT_DXT1,                 "D3DFMT_DXT1", 4, 4, FALSE},
507         {D3DFMT_DXT2,                 "D3DFMT_DXT2", 4, 4, FALSE},
508         {D3DFMT_DXT3,                 "D3DFMT_DXT3", 4, 4, FALSE},
509         {D3DFMT_DXT4,                 "D3DFMT_DXT4", 4, 4, FALSE},
510         {D3DFMT_DXT5,                 "D3DFMT_DXT5", 4, 4, FALSE},
511         /* ATI2N has 2x2 blocks on all AMD cards and Geforce 7 cards,
512          * which doesn't match the format spec. On newer Nvidia cards
513          * it has the correct 4x4 block size */
514         {MAKEFOURCC('A','T','I','2'), "ATI2N",       4, 4, TRUE},
515         /* YUY2 and UYVY are not supported in d3d8, there is no way
516          * to use them with this API considering their restrictions */
517     };
518     const struct
519     {
520         D3DPOOL pool;
521         const char *name;
522         /* Don't check the return value, Nvidia returns D3DERR_INVALIDCALL for some formats
523          * and E_INVALIDARG/DDERR_INVALIDPARAMS for others. */
524         BOOL success;
525     }
526     pools[] =
527     {
528         {D3DPOOL_DEFAULT,       "D3DPOOL_DEFAULT",  FALSE},
529         {D3DPOOL_SCRATCH,       "D3DPOOL_SCRATCH",  TRUE},
530         {D3DPOOL_SYSTEMMEM,     "D3DPOOL_SYSTEMMEM",TRUE},
531         {D3DPOOL_MANAGED,       "D3DPOOL_MANAGED",  TRUE},
532     };
533
534     hr = IDirect3DDevice8_GetDirect3D(device, &d3d);
535     ok(SUCCEEDED(hr), "IDirect3DDevice8_GetDirect3D failed (%08x)\n", hr);
536
537     for (i = 0; i < (sizeof(formats) / sizeof(*formats)); ++i) {
538         hr = IDirect3D8_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_DYNAMIC,
539                 D3DRTYPE_TEXTURE, formats[i].fmt);
540         if (FAILED(hr))
541         {
542             skip("Format %s not supported, skipping lockrect offset test\n", formats[i].name);
543             continue;
544         }
545
546         for (j = 0; j < (sizeof(pools) / sizeof(*pools)); j++)
547         {
548             hr = IDirect3DDevice8_CreateTexture(device, 128, 128, 1,
549                     pools[j].pool == D3DPOOL_DEFAULT ? D3DUSAGE_DYNAMIC : 0,
550                     formats[i].fmt, pools[j].pool, &texture);
551             ok(SUCCEEDED(hr), "IDirect3DDevice8_CreateTexture failed (%08x)\n", hr);
552             hr = IDirect3DTexture8_GetSurfaceLevel(texture, 0, &surface);
553             ok(SUCCEEDED(hr), "IDirect3DTexture8_GetSurfaceLevel failed (%08x)\n", hr);
554             IDirect3DTexture8_Release(texture);
555
556             if (formats[i].block_width > 1)
557             {
558                 SetRect(&rect, formats[i].block_width >> 1, 0, formats[i].block_width, formats[i].block_height);
559                 hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &rect, 0);
560                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
561                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
562                         SUCCEEDED(hr) ? "succeeded" : "failed",
563                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
564                 if (SUCCEEDED(hr))
565                 {
566                     hr = IDirect3DSurface8_UnlockRect(surface);
567                     ok(SUCCEEDED(hr), "IDirect3DSurface8_UnlockRect failed (%08x)\n", hr);
568                 }
569
570                 SetRect(&rect, 0, 0, formats[i].block_width >> 1, formats[i].block_height);
571                 hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &rect, 0);
572                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
573                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
574                         SUCCEEDED(hr) ? "succeeded" : "failed",
575                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
576                 if (SUCCEEDED(hr))
577                 {
578                     hr = IDirect3DSurface8_UnlockRect(surface);
579                     ok(SUCCEEDED(hr), "IDirect3DSurface8_UnlockRect failed (%08x)\n", hr);
580                 }
581             }
582
583             if (formats[i].block_height > 1)
584             {
585                 SetRect(&rect, 0, formats[i].block_height >> 1, formats[i].block_width, formats[i].block_height);
586                 hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &rect, 0);
587                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
588                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
589                         SUCCEEDED(hr) ? "succeeded" : "failed",
590                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
591                 if (SUCCEEDED(hr))
592                 {
593                     hr = IDirect3DSurface8_UnlockRect(surface);
594                     ok(SUCCEEDED(hr), "IDirect3DSurface8_UnlockRect failed (%08x)\n", hr);
595                 }
596
597                 SetRect(&rect, 0, 0, formats[i].block_width, formats[i].block_height >> 1);
598                 hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &rect, 0);
599                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
600                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
601                         SUCCEEDED(hr) ? "succeeded" : "failed",
602                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
603                 if (SUCCEEDED(hr))
604                 {
605                     hr = IDirect3DSurface8_UnlockRect(surface);
606                     ok(SUCCEEDED(hr), "IDirect3DSurface8_UnlockRect failed (%08x)\n", hr);
607                 }
608             }
609
610             SetRect(&rect, 0, 0, formats[i].block_width, formats[i].block_height);
611             hr = IDirect3DSurface8_LockRect(surface, &locked_rect, &rect, 0);
612             ok(hr == D3D_OK, "Full block lock returned %08x, expected %08x, format %s, pool %s\n",
613                     hr, D3D_OK, formats[i].name, pools[j].name);
614             if (SUCCEEDED(hr))
615             {
616                 hr = IDirect3DSurface8_UnlockRect(surface);
617                 ok(SUCCEEDED(hr), "IDirect3DSurface8_UnlockRect failed (%08x)\n", hr);
618             }
619
620             IDirect3DSurface8_Release(surface);
621         }
622     }
623     IDirect3D8_Release(d3d);
624 }
625
626 START_TEST(surface)
627 {
628     HMODULE d3d8_handle;
629     IDirect3DDevice8 *device_ptr;
630     ULONG refcount;
631
632     d3d8_handle = LoadLibraryA("d3d8.dll");
633     if (!d3d8_handle)
634     {
635         skip("Could not load d3d8.dll\n");
636         return;
637     }
638
639     device_ptr = init_d3d8(d3d8_handle);
640     if (!device_ptr) return;
641
642     test_image_surface_pool(device_ptr);
643     test_surface_get_container(device_ptr);
644     test_lockrect_invalid(device_ptr);
645     test_private_data(device_ptr);
646     test_surface_dimensions(device_ptr);
647     test_surface_format_null(device_ptr);
648     test_surface_double_unlock(device_ptr);
649     test_surface_lockrect_blocks(device_ptr);
650
651     refcount = IDirect3DDevice8_Release(device_ptr);
652     ok(!refcount, "Device has %u references left\n", refcount);
653 }