Release 1.4.1.
[wine] / dlls / d3d9 / 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 <d3d9.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 = "d3d9_test_wc";
28     RegisterClass(&wc);
29
30     return CreateWindow("d3d9_test_wc", "d3d9_test",
31             0, 0, 0, 0, 0, 0, 0, 0, 0);
32 }
33
34 static IDirect3DDevice9 *init_d3d9(HMODULE d3d9_handle)
35 {
36     IDirect3D9 * (__stdcall * d3d9_create)(UINT SDKVersion) = 0;
37     IDirect3D9 *d3d9_ptr = 0;
38     IDirect3DDevice9 *device_ptr = 0;
39     D3DPRESENT_PARAMETERS present_parameters;
40     HRESULT hr;
41
42     d3d9_create = (void *)GetProcAddress(d3d9_handle, "Direct3DCreate9");
43     ok(d3d9_create != NULL, "Failed to get address of Direct3DCreate9\n");
44     if (!d3d9_create) return NULL;
45
46     d3d9_ptr = d3d9_create(D3D_SDK_VERSION);
47     if (!d3d9_ptr)
48     {
49         skip("could not create D3D9\n");
50         return NULL;
51     }
52
53     ZeroMemory(&present_parameters, sizeof(present_parameters));
54     present_parameters.Windowed = TRUE;
55     present_parameters.hDeviceWindow = create_window();
56     present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
57     present_parameters.EnableAutoDepthStencil = TRUE;
58     present_parameters.AutoDepthStencilFormat = D3DFMT_D24S8;
59
60     hr = IDirect3D9_CreateDevice(d3d9_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, IDirect3D9_CreateDevice returned %#x\n", hr);
66         return NULL;
67     }
68
69     return device_ptr;
70 }
71
72 static void test_surface_get_container(IDirect3DDevice9 *device_ptr)
73 {
74     IDirect3DTexture9 *texture_ptr = 0;
75     IDirect3DSurface9 *surface_ptr = 0;
76     void *container_ptr;
77     HRESULT hr;
78
79     hr = IDirect3DDevice9_CreateTexture(device_ptr, 128, 128, 1, 0,
80             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture_ptr, 0);
81     ok(SUCCEEDED(hr) && texture_ptr != NULL, "CreateTexture returned: hr %#x, texture_ptr %p. "
82         "Expected hr %#x, texture_ptr != %p\n", hr, texture_ptr, D3D_OK, NULL);
83     if (!texture_ptr || FAILED(hr)) goto cleanup;
84
85     hr = IDirect3DTexture9_GetSurfaceLevel(texture_ptr, 0, &surface_ptr);
86     ok(SUCCEEDED(hr) && surface_ptr != NULL, "GetSurfaceLevel returned: hr %#x, surface_ptr %p. "
87         "Expected hr %#x, surface_ptr != %p\n", hr, surface_ptr, D3D_OK, NULL);
88     if (!surface_ptr || FAILED(hr)) goto cleanup;
89
90     /* These should work... */
91     container_ptr = (void *)0x1337c0d3;
92     hr = IDirect3DSurface9_GetContainer(surface_ptr, &IID_IUnknown, &container_ptr);
93     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
94         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
95     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
96
97     container_ptr = (void *)0x1337c0d3;
98     hr = IDirect3DSurface9_GetContainer(surface_ptr, &IID_IDirect3DResource9, &container_ptr);
99     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
100         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
101     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
102
103     container_ptr = (void *)0x1337c0d3;
104     hr = IDirect3DSurface9_GetContainer(surface_ptr, &IID_IDirect3DBaseTexture9, &container_ptr);
105     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
106         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
107     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
108
109     container_ptr = (void *)0x1337c0d3;
110     hr = IDirect3DSurface9_GetContainer(surface_ptr, &IID_IDirect3DTexture9, &container_ptr);
111     ok(SUCCEEDED(hr) && container_ptr == texture_ptr, "GetContainer returned: hr %#x, container_ptr %p. "
112         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, S_OK, texture_ptr);
113     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
114
115     /* ...and this one shouldn't. This should return E_NOINTERFACE and set container_ptr to NULL */
116     container_ptr = (void *)0x1337c0d3;
117     hr = IDirect3DSurface9_GetContainer(surface_ptr, &IID_IDirect3DSurface9, &container_ptr);
118     ok(hr == E_NOINTERFACE && container_ptr == NULL, "GetContainer returned: hr %#x, container_ptr %p. "
119         "Expected hr %#x, container_ptr %p\n", hr, container_ptr, E_NOINTERFACE, NULL);
120     if (container_ptr && container_ptr != (void *)0x1337c0d3) IUnknown_Release((IUnknown *)container_ptr);
121
122 cleanup:
123     if (texture_ptr) IDirect3DTexture9_Release(texture_ptr);
124     if (surface_ptr) IDirect3DSurface9_Release(surface_ptr);
125 }
126
127 static void test_surface_alignment(IDirect3DDevice9 *device_ptr)
128 {
129     IDirect3DSurface9 *surface_ptr = 0;
130     HRESULT hr;
131     int i;
132
133     /* Test a sysmem surface as those aren't affected by the hardware's np2 restrictions */
134     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device_ptr, 5, 5, D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &surface_ptr, 0);
135     ok(hr == D3D_OK, "IDirect3DDevice9_CreateOffscreenPlainSurface returned %08x\n", hr);
136
137     if(surface_ptr)
138     {
139         D3DLOCKED_RECT lockedRect;
140         hr = IDirect3DSurface9_LockRect(surface_ptr, &lockedRect, NULL, 0);
141         ok(hr == D3D_OK, "IDirect3DSurface9_LockRect returned %08x\n", hr);
142         ok(!(lockedRect.Pitch & 3), "Surface pitch %d is not 32-bit aligned\n", lockedRect.Pitch);
143         /* Some applications also depend on the exact pitch, rather than just
144          * the alignment.
145          */
146         ok(lockedRect.Pitch == 12, "Got pitch %d, expected 12\n", lockedRect.Pitch);
147         hr = IDirect3DSurface9_UnlockRect(surface_ptr);
148         ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect returned %#x.\n", hr);
149         IDirect3DSurface9_Release(surface_ptr);
150     }
151
152     for (i = 0; i < 5; i++)
153     {
154         IDirect3DTexture9 *pTexture;
155         int j, pitch;
156
157         hr = IDirect3DDevice9_CreateTexture(device_ptr, 64, 64, 0, 0, MAKEFOURCC('D', 'X', 'T', '1'+i),
158                                             D3DPOOL_MANAGED, &pTexture, NULL);
159         ok(SUCCEEDED(hr) || hr == D3DERR_INVALIDCALL, "IDirect3DDevice9_CreateTexture: %08x\n", hr);
160         if (FAILED(hr)) {
161             skip("DXT%d surfaces are not supported\n", i + 1);
162             continue;
163         }
164
165         for (j = IDirect3DBaseTexture9_GetLevelCount(pTexture) - 1; j >= 0; j--)
166         {
167             D3DLOCKED_RECT rc;
168             D3DSURFACE_DESC descr;
169
170             IDirect3DTexture9_GetLevelDesc(pTexture, j, &descr);
171             hr = IDirect3DTexture9_LockRect(pTexture, j, &rc, NULL, 0);
172             ok(SUCCEEDED(hr), "IDirect3DTexture9_LockRect: %08x\n", hr);
173             hr = IDirect3DTexture9_UnlockRect(pTexture, j);
174             ok(SUCCEEDED(hr), "IDirect3DTexture9_UnLockRect: %08x\n", hr);
175             /* Windows XP returns D3D_OK when calling UnlockRect on an unlocked surface,
176              * windows 7 returns an error.
177              */
178
179             pitch = ((descr.Width + 3) >> 2) << 3;
180             if (i > 0) pitch <<= 1;
181             ok(rc.Pitch == pitch, "Wrong pitch for DXT%d lvl[%d (%dx%d)]: expected %d got %d\n",
182                i + 1, j, descr.Width, descr.Height, pitch, rc.Pitch);
183         }
184         IUnknown_Release( pTexture );
185     }
186 }
187
188 /* Since the DXT formats are based on 4x4 blocks, locking works slightly
189  * different than with regular formats. This patch verifies we return the
190  * correct memory offsets */
191 static void test_lockrect_offset(IDirect3DDevice9 *device)
192 {
193     IDirect3DSurface9 *surface = 0;
194     IDirect3D9 *d3d;
195     const RECT rect = {60, 60, 68, 68};
196     D3DLOCKED_RECT locked_rect;
197     int expected_pitch;
198     unsigned int expected_offset;
199     unsigned int offset;
200     unsigned int i;
201     BYTE *base;
202     HRESULT hr;
203
204     const struct {
205         D3DFORMAT fmt;
206         const char *name;
207         unsigned int block_width;
208         unsigned int block_height;
209         unsigned int block_size;
210     } dxt_formats[] = {
211         {D3DFMT_DXT1,                 "D3DFMT_DXT1", 4, 4, 8},
212         {D3DFMT_DXT2,                 "D3DFMT_DXT2", 4, 4, 16},
213         {D3DFMT_DXT3,                 "D3DFMT_DXT3", 4, 4, 16},
214         {D3DFMT_DXT4,                 "D3DFMT_DXT4", 4, 4, 16},
215         {D3DFMT_DXT5,                 "D3DFMT_DXT5", 4, 4, 16},
216         {MAKEFOURCC('A','T','I','2'), "ATI2N",       1, 1,  1},
217     };
218     hr = IDirect3DDevice9_GetDirect3D(device, &d3d);
219     ok(SUCCEEDED(hr), "IDirect3DDevice9_GetDirect3D failed (%08x)\n", hr);
220
221     for (i = 0; i < (sizeof(dxt_formats) / sizeof(*dxt_formats)); ++i) {
222         hr = IDirect3D9_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, 0, D3DRTYPE_TEXTURE, dxt_formats[i].fmt);
223         if(FAILED(hr))
224         {
225             skip("Format %s not supported, skipping lockrect offset test\n", dxt_formats[i].name);
226             continue;
227         }
228
229         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 128, 128, dxt_formats[i].fmt, D3DPOOL_SCRATCH, &surface, 0);
230         ok(SUCCEEDED(hr), "CreateOffscreenPlainSurface failed (%08x)\n", hr);
231
232         hr = IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
233         ok(SUCCEEDED(hr), "LockRect failed (%08x)\n", hr);
234
235         base = locked_rect.pBits;
236         expected_pitch = (128 + dxt_formats[i].block_height - 1) / dxt_formats[i].block_width
237                          * dxt_formats[i].block_size;
238         ok(locked_rect.Pitch == expected_pitch, "Got pitch %d, expected pitch %d for format %s\n", locked_rect.Pitch, expected_pitch, dxt_formats[i].name);
239
240         hr = IDirect3DSurface9_UnlockRect(surface);
241         ok(SUCCEEDED(hr), "UnlockRect failed (%08x)\n", hr);
242
243         hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &rect, 0);
244         ok(SUCCEEDED(hr), "LockRect failed (%08x)\n", hr);
245
246         offset = (BYTE *)locked_rect.pBits - base;
247         expected_offset = (rect.top / dxt_formats[i].block_height) * expected_pitch
248                         + (rect.left / dxt_formats[i].block_width) * dxt_formats[i].block_size;
249         ok(offset == expected_offset, "Got offset %u, expected offset %u for format %s\n", offset, expected_offset, dxt_formats[i].name);
250
251         hr = IDirect3DSurface9_UnlockRect(surface);
252         ok(SUCCEEDED(hr), "UnlockRect failed (%08x)\n", hr);
253
254         IDirect3DSurface9_Release(surface);
255     }
256     IDirect3D9_Release(d3d);
257 }
258
259 struct rect_test
260 {
261     RECT rect;
262     HRESULT win7_result;
263 };
264
265 static void test_lockrect_invalid(IDirect3DDevice9 *device)
266 {
267     IDirect3DSurface9 *surface = 0;
268     D3DLOCKED_RECT locked_rect;
269     unsigned int i;
270     BYTE *base;
271     HRESULT hr;
272
273     const RECT test_rect_2 = { 0, 0, 8, 8 };
274     const struct rect_test test_data[] = {
275         {{60, 60, 68, 68},      D3D_OK},                /* Valid */
276         {{60, 60, 60, 68},      D3DERR_INVALIDCALL},    /* 0 height */
277         {{60, 60, 68, 60},      D3DERR_INVALIDCALL},    /* 0 width */
278         {{68, 60, 60, 68},      D3DERR_INVALIDCALL},    /* left > right */
279         {{60, 68, 68, 60},      D3DERR_INVALIDCALL},    /* top > bottom */
280         {{-8, 60,  0, 68},      D3DERR_INVALIDCALL},    /* left < surface */
281         {{60, -8, 68,  0},      D3DERR_INVALIDCALL},    /* top < surface */
282         {{-16, 60, -8, 68},     D3DERR_INVALIDCALL},    /* right < surface */
283         {{60, -16, 68, -8},     D3DERR_INVALIDCALL},    /* bottom < surface */
284         {{60, 60, 136, 68},     D3DERR_INVALIDCALL},    /* right > surface */
285         {{60, 60, 68, 136},     D3DERR_INVALIDCALL},    /* bottom > surface */
286         {{136, 60, 144, 68},    D3DERR_INVALIDCALL},    /* left > surface */
287         {{60, 136, 68, 144},    D3DERR_INVALIDCALL},    /* top > surface */
288     };
289
290     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 128, 128, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, 0);
291     ok(SUCCEEDED(hr), "CreateOffscreenPlainSurface failed (0x%08x)\n", hr);
292
293     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
294     ok(SUCCEEDED(hr), "LockRect failed (0x%08x)\n", hr);
295
296     base = locked_rect.pBits;
297
298     hr = IDirect3DSurface9_UnlockRect(surface);
299     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
300
301     for (i = 0; i < (sizeof(test_data) / sizeof(*test_data)); ++i)
302     {
303         unsigned int offset, expected_offset;
304         const RECT *rect = &test_data[i].rect;
305
306         locked_rect.pBits = (BYTE *)0xdeadbeef;
307         locked_rect.Pitch = 0xdeadbeef;
308
309         hr = IDirect3DSurface9_LockRect(surface, &locked_rect, rect, 0);
310         /* Windows XP accepts invalid locking rectangles, windows 7 rejects them.
311          * Some games(C&C3) depend on the XP behavior, mark the Win7 one broken */
312         ok(SUCCEEDED(hr) || broken(hr == test_data[i].win7_result),
313                 "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]\n",
314                 hr, rect->left, rect->top, rect->right, rect->bottom);
315         if(FAILED(hr)) continue;
316
317         offset = (BYTE *)locked_rect.pBits - base;
318         expected_offset = rect->top * locked_rect.Pitch + rect->left * 4;
319         ok(offset == expected_offset, "Got offset %u, expected offset %u for rect [%d, %d]->[%d, %d]\n",
320                 offset, expected_offset, rect->left, rect->top, rect->right, rect->bottom);
321
322         hr = IDirect3DSurface9_UnlockRect(surface);
323         ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
324     }
325
326     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
327     ok(SUCCEEDED(hr), "LockRect failed (0x%08x) for rect NULL\n", hr);
328     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
329     ok(hr == D3DERR_INVALIDCALL, "Double LockRect for rect NULL returned 0x%08x\n", hr);
330     hr = IDirect3DSurface9_UnlockRect(surface);
331     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
332
333     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &test_data[0].rect, 0);
334     ok(hr == D3D_OK, "LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
335             ", expected D3D_OK (0x%08x)\n", hr, test_data[0].rect.left, test_data[0].rect.top,
336             test_data[0].rect.right, test_data[0].rect.bottom, D3D_OK);
337     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &test_data[0].rect, 0);
338     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
339             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, test_data[0].rect.left, test_data[0].rect.top,
340             test_data[0].rect.right, test_data[0].rect.bottom, D3DERR_INVALIDCALL);
341     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &test_rect_2, 0);
342     ok(hr == D3DERR_INVALIDCALL, "Double LockRect failed (0x%08x) for rect [%d, %d]->[%d, %d]"
343             ", expected D3DERR_INVALIDCALL (0x%08x)\n", hr, test_rect_2.left, test_rect_2.top,
344             test_rect_2.right, test_rect_2.bottom, D3DERR_INVALIDCALL);
345     hr = IDirect3DSurface9_UnlockRect(surface);
346     ok(SUCCEEDED(hr), "UnlockRect failed (0x%08x)\n", hr);
347
348     IDirect3DSurface9_Release(surface);
349 }
350
351 static ULONG getref(IUnknown *iface)
352 {
353     IUnknown_AddRef(iface);
354     return IUnknown_Release(iface);
355 }
356
357 static void test_private_data(IDirect3DDevice9 *device)
358 {
359     HRESULT hr;
360     IDirect3DSurface9 *surface;
361     ULONG ref, ref2;
362     IUnknown *ptr;
363     DWORD size = sizeof(IUnknown *);
364
365     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 4, 4, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, 0);
366     ok(SUCCEEDED(hr), "CreateImageSurface failed (0x%08x)\n", hr);
367     if(!surface)
368     {
369         return;
370     }
371
372     /* This fails */
373     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9 /* Abuse this tag */, device, 0, D3DSPD_IUNKNOWN);
374     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
375     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9 /* Abuse this tag */, device, 5, D3DSPD_IUNKNOWN);
376     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
377     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9 /* Abuse this tag */, device, sizeof(IUnknown *) * 2, D3DSPD_IUNKNOWN);
378     ok(hr == D3DERR_INVALIDCALL, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
379
380     ref = getref((IUnknown *) device);
381     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9 /* Abuse this tag */, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
382     ok(hr == D3D_OK, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
383     ref2 = getref((IUnknown *) device);
384     ok(ref2 == ref + 1, "Object reference is %d, expected %d\n", ref2, ref + 1);
385     hr = IDirect3DSurface9_FreePrivateData(surface, &IID_IDirect3DSurface9);
386     ok(hr == D3D_OK, "IDirect3DSurface9_FreePrivateData returned %08x\n", hr);
387     ref2 = getref((IUnknown *) device);
388     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
389
390     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
391     ok(hr == D3D_OK, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
392     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9, surface, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
393     ok(hr == D3D_OK, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
394     ref2 = getref((IUnknown *) device);
395     ok(ref2 == ref, "Object reference is %d, expected %d\n", ref2, ref);
396
397     hr = IDirect3DSurface9_SetPrivateData(surface, &IID_IDirect3DSurface9, device, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
398     ok(hr == D3D_OK, "IDirect3DSurface9_SetPrivateData failed with %08x\n", hr);
399     hr = IDirect3DSurface9_GetPrivateData(surface, &IID_IDirect3DSurface9, &ptr, &size);
400     ok(hr == D3D_OK, "IDirect3DSurface9_GetPrivateData failed with %08x\n", hr);
401     ref2 = getref((IUnknown *) device);
402     /* Object is addrefed */
403     ok(ptr == (IUnknown *) device, "Returned interface pointer is %p, expected %p\n", ptr, device);
404     ok(ref2 == ref + 2, "Object reference is %d, expected %d. ptr at %p, orig at %p\n", ref2, ref + 2, ptr, device);
405     IUnknown_Release(ptr);
406
407     IDirect3DSurface9_Release(surface);
408
409     /* Destroying the surface frees the held reference */
410     ref2 = getref((IUnknown *) device);
411     /* -1 because the surface was released and held a reference before */
412     ok(ref2 == (ref - 1), "Object reference is %d, expected %d\n", ref2, (ref - 1));
413 }
414
415 static void test_getdc(IDirect3DDevice9 *device)
416 {
417     IDirect3DSurface9 *surface;
418     IDirect3DTexture9 *texture;
419     HRESULT hr;
420     unsigned int i;
421     HDC dc;
422
423     struct
424     {
425         const char *name;
426         D3DFORMAT fmt;
427         BOOL getdc_supported;
428     } testdata[] = {
429         { "D3DFMT_A8R8G8B8",    D3DFMT_A8R8G8B8,    TRUE    },
430         { "D3DFMT_X8R8G8B8",    D3DFMT_X8R8G8B8,    TRUE    },
431         { "D3DFMT_R5G6B5",      D3DFMT_R5G6B5,      TRUE    },
432         { "D3DFMT_X1R5G5B5",    D3DFMT_X1R5G5B5,    TRUE    },
433         { "D3DFMT_A1R5G5B5",    D3DFMT_A1R5G5B5,    TRUE    },
434         { "D3DFMT_R8G8B8",      D3DFMT_R8G8B8,      TRUE    },
435         { "D3DFMT_A2R10G10B10", D3DFMT_A2R10G10B10, FALSE   }, /* Untested, card on windows didn't support it */
436         { "D3DFMT_V8U8",        D3DFMT_V8U8,        FALSE   },
437         { "D3DFMT_Q8W8V8U8",    D3DFMT_Q8W8V8U8,    FALSE   },
438         { "D3DFMT_A8B8G8R8",    D3DFMT_A8B8G8R8,    FALSE   },
439         { "D3DFMT_X8B8G8R8",    D3DFMT_A8B8G8R8,    FALSE   },
440         { "D3DFMT_R3G3B2",      D3DFMT_R3G3B2,      FALSE   },
441         { "D3DFMT_P8",          D3DFMT_P8,          FALSE   },
442         { "D3DFMT_L8",          D3DFMT_L8,          FALSE   },
443         { "D3DFMT_A8L8",        D3DFMT_A8L8,        FALSE   },
444         { "D3DFMT_DXT1",        D3DFMT_DXT1,        FALSE   },
445         { "D3DFMT_DXT2",        D3DFMT_DXT2,        FALSE   },
446         { "D3DFMT_DXT3",        D3DFMT_DXT3,        FALSE   },
447         { "D3DFMT_DXT4",        D3DFMT_DXT4,        FALSE   },
448         { "D3DFMT_DXT5",        D3DFMT_DXT5,        FALSE   },
449     };
450
451     for(i = 0; i < (sizeof(testdata) / sizeof(testdata[0])); i++)
452     {
453         texture = NULL;
454         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 64, 64, testdata[i].fmt, D3DPOOL_SYSTEMMEM, &surface, NULL);
455         if(FAILED(hr))
456         {
457             hr = IDirect3DDevice9_CreateTexture(device, 64, 64, 1, 0, testdata[i].fmt, D3DPOOL_MANAGED, &texture, NULL);
458             if(FAILED(hr))
459             {
460                 skip("IDirect3DDevice9_CreateOffscreenPlainSurface failed, hr = 0x%08x, fmt %s\n", hr, testdata[i].name);
461                 continue;
462             }
463             IDirect3DTexture9_GetSurfaceLevel(texture, 0, &surface);
464         }
465
466         dc = (void *) 0x1234;
467         hr = IDirect3DSurface9_GetDC(surface, &dc);
468
469         if(testdata[i].getdc_supported)
470         {
471             ok(SUCCEEDED(hr), "GetDC on format %s failed(hr=0x%08x), but was expected to work\n",
472                testdata[i].name, hr);
473         }
474         else
475         {
476             ok(FAILED(hr), "GetDC on format %s worked(hr=0x%08x), but was expected to fail\n",
477                testdata[i].name, hr);
478         }
479
480         if(SUCCEEDED(hr))
481         {
482             hr = IDirect3DSurface9_ReleaseDC(surface, dc);
483             ok(hr == D3D_OK, "IDirect3DSurface9_ReleaseDC failed, hr = 0x%08x\n", hr);
484         }
485         else
486         {
487             ok(dc == (void *) 0x1234, "After failed getDC dc is %p\n", dc);
488         }
489
490         IDirect3DSurface9_Release(surface);
491         if(texture) IDirect3DTexture9_Release(texture);
492     }
493 }
494
495 static void test_surface_dimensions(IDirect3DDevice9 *device)
496 {
497     IDirect3DSurface9 *surface;
498     HRESULT hr;
499
500     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 0, 1, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, NULL);
501     ok(hr == D3DERR_INVALIDCALL, "CreateOffscreenPlainSurface returned %#x, expected D3DERR_INVALIDCALL.\n", hr);
502     if (SUCCEEDED(hr)) IDirect3DSurface9_Release(surface);
503
504     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, NULL);
505     ok(hr == D3DERR_INVALIDCALL, "CreateOffscreenPlainSurface returned %#x, expected D3DERR_INVALIDCALL.\n", hr);
506     if (SUCCEEDED(hr)) IDirect3DSurface9_Release(surface);
507 }
508
509 static void test_surface_format_null(IDirect3DDevice9 *device)
510 {
511     static const D3DFORMAT D3DFMT_NULL = MAKEFOURCC('N','U','L','L');
512     IDirect3DTexture9 *texture;
513     IDirect3DSurface9 *surface;
514     IDirect3DSurface9 *rt, *ds;
515     D3DLOCKED_RECT locked_rect;
516     D3DSURFACE_DESC desc;
517     IDirect3D9 *d3d;
518     HRESULT hr;
519
520     IDirect3DDevice9_GetDirect3D(device, &d3d);
521
522     hr = IDirect3D9_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8,
523             D3DUSAGE_RENDERTARGET,  D3DRTYPE_SURFACE, D3DFMT_NULL);
524     if (hr != D3D_OK)
525     {
526         skip("No D3DFMT_NULL support, skipping test.\n");
527         IDirect3D9_Release(d3d);
528         return;
529     }
530
531     hr = IDirect3D9_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8,
532             D3DUSAGE_RENDERTARGET,  D3DRTYPE_TEXTURE, D3DFMT_NULL);
533     ok(hr == D3D_OK, "D3DFMT_NULL should be supported for render target textures, hr %#x.\n", hr);
534
535     hr = IDirect3D9_CheckDepthStencilMatch(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8,
536             D3DFMT_NULL, D3DFMT_D24S8);
537     ok(SUCCEEDED(hr), "Depth stencil match failed for D3DFMT_NULL, hr %#x.\n", hr);
538
539     IDirect3D9_Release(d3d);
540
541     hr = IDirect3DDevice9_CreateRenderTarget(device, 128, 128, D3DFMT_NULL, 0, 0, TRUE, &surface, NULL);
542     ok(SUCCEEDED(hr), "Failed to create render target, hr %#x.\n", hr);
543
544     hr = IDirect3DDevice9_GetRenderTarget(device, 0, &rt);
545     ok(SUCCEEDED(hr), "Failed to get original render target, hr %#x.\n", hr);
546
547     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &ds);
548     ok(SUCCEEDED(hr), "Failed to get original depth/stencil, hr %#x.\n", hr);
549
550     hr = IDirect3DDevice9_SetRenderTarget(device, 0, NULL);
551     ok(FAILED(hr), "Succeeded in setting render target 0 to NULL, should fail.\n");
552
553     hr = IDirect3DDevice9_SetRenderTarget(device, 0, surface);
554     ok(SUCCEEDED(hr), "Failed to set render target, hr %#x.\n", hr);
555
556     hr = IDirect3DDevice9_SetDepthStencilSurface(device, NULL);
557     ok(SUCCEEDED(hr), "Failed to set depth/stencil, hr %#x.\n", hr);
558
559     hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0x00000000, 0.0f, 0);
560     ok(SUCCEEDED(hr), "Clear failed, hr %#x.\n", hr);
561
562     hr = IDirect3DDevice9_SetRenderTarget(device, 0, rt);
563     ok(SUCCEEDED(hr), "Failed to set render target, hr %#x.\n", hr);
564
565     hr = IDirect3DDevice9_SetDepthStencilSurface(device, ds);
566     ok(SUCCEEDED(hr), "Failed to set depth/stencil, hr %#x.\n", hr);
567
568     IDirect3DSurface9_Release(rt);
569     IDirect3DSurface9_Release(ds);
570
571     hr = IDirect3DSurface9_GetDesc(surface, &desc);
572     ok(SUCCEEDED(hr), "Failed to get surface desc, hr %#x.\n", hr);
573     ok(desc.Width == 128, "Expected width 128, got %u.\n", desc.Width);
574     ok(desc.Height == 128, "Expected height 128, got %u.\n", desc.Height);
575
576     hr = IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
577     ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
578     ok(locked_rect.Pitch, "Expected non-zero pitch, got %u.\n", locked_rect.Pitch);
579     ok(!!locked_rect.pBits, "Expected non-NULL pBits, got %p.\n", locked_rect.pBits);
580
581     hr = IDirect3DSurface9_UnlockRect(surface);
582     ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
583
584     IDirect3DSurface9_Release(surface);
585
586     hr = IDirect3DDevice9_CreateTexture(device, 128, 128, 0, D3DUSAGE_RENDERTARGET,
587             D3DFMT_NULL, D3DPOOL_DEFAULT, &texture, NULL);
588     ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
589     IDirect3DTexture9_Release(texture);
590 }
591
592 static void test_surface_double_unlock(IDirect3DDevice9 *device)
593 {
594     static const struct
595     {
596         D3DPOOL pool;
597         const char *name;
598     }
599     pools[] =
600     {
601         { D3DPOOL_DEFAULT,      "D3DPOOL_DEFAULT"   },
602         { D3DPOOL_SCRATCH,      "D3DPOOL_SCRATCH"   },
603         { D3DPOOL_SYSTEMMEM,    "D3DPOOL_SYSTEMMEM" },
604         /* There are no standalone MANAGED pool surfaces */
605     };
606     IDirect3DSurface9 *surface;
607     unsigned int i;
608     HRESULT hr;
609     D3DLOCKED_RECT lr;
610
611     for (i = 0; i < (sizeof(pools) / sizeof(*pools)); i++)
612     {
613         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 64, 64, D3DFMT_X8R8G8B8,
614                 pools[i].pool, &surface, NULL);
615         ok(SUCCEEDED(hr), "IDirect3DDevice9_CreateOffscreenPlainSurface failed, hr = 0x%08x, pool %s\n",
616                 hr, pools[i].name);
617         hr = IDirect3DSurface9_UnlockRect(surface);
618         ok(hr == D3DERR_INVALIDCALL, "Unlock without lock returned 0x%08x, expected 0x%08x, pool %s\n",
619                 hr, D3DERR_INVALIDCALL, pools[i].name);
620
621         hr = IDirect3DSurface9_LockRect(surface, &lr, NULL, 0);
622         ok(SUCCEEDED(hr), "IDirect3DSurface9_LockRect failed, hr = 0x%08x, pool %s\n",
623                 hr, pools[i].name);
624         hr = IDirect3DSurface9_UnlockRect(surface);
625         ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect failed, hr = 0x%08x, pool %s\n",
626                 hr, pools[i].name);
627
628         hr = IDirect3DSurface9_UnlockRect(surface);
629         ok(hr == D3DERR_INVALIDCALL, "Double unlock returned 0x%08x, expected 0x%08x, pool %s\n",
630                 hr, D3DERR_INVALIDCALL, pools[i].name);
631
632         IDirect3DSurface9_Release(surface);
633     }
634 }
635
636 static void test_surface_lockrect_blocks(IDirect3DDevice9 *device)
637 {
638     IDirect3DTexture9 *texture;
639     IDirect3DSurface9 *surface;
640     IDirect3D9 *d3d;
641     D3DLOCKED_RECT locked_rect;
642     unsigned int i, j;
643     HRESULT hr;
644     RECT rect;
645     BOOL surface_only;
646
647     const struct
648     {
649         D3DFORMAT fmt;
650         const char *name;
651         unsigned int block_width;
652         unsigned int block_height;
653         BOOL broken;
654     }
655     formats[] =
656     {
657         {D3DFMT_DXT1,                 "D3DFMT_DXT1", 4, 4, FALSE},
658         {D3DFMT_DXT2,                 "D3DFMT_DXT2", 4, 4, FALSE},
659         {D3DFMT_DXT3,                 "D3DFMT_DXT3", 4, 4, FALSE},
660         {D3DFMT_DXT4,                 "D3DFMT_DXT4", 4, 4, FALSE},
661         {D3DFMT_DXT5,                 "D3DFMT_DXT5", 4, 4, FALSE},
662         /* ATI2N has 2x2 blocks on all AMD cards and Geforce 7 cards,
663          * which doesn't match the format spec. On newer Nvidia cards
664          * it has the correct 4x4 block size */
665         {MAKEFOURCC('A','T','I','2'), "ATI2N",       4, 4, TRUE},
666         {D3DFMT_YUY2,                 "D3DFMT_YUY2", 2, 1, FALSE},
667         {D3DFMT_UYVY,                 "D3DFMT_UYVY", 2, 1, FALSE},
668     };
669     static const struct
670     {
671         D3DPOOL pool;
672         const char *name;
673         /* Don't check the return value, Nvidia returns D3DERR_INVALIDCALL for some formats
674          * and E_INVALIDARG/DDERR_INVALIDPARAMS for others. */
675         BOOL success;
676     }
677     pools[] =
678     {
679         {D3DPOOL_DEFAULT,       "D3DPOOL_DEFAULT",  FALSE},
680         {D3DPOOL_SCRATCH,       "D3DPOOL_SCRATCH",  TRUE},
681         {D3DPOOL_SYSTEMMEM,     "D3DPOOL_SYSTEMMEM",TRUE},
682         {D3DPOOL_MANAGED,       "D3DPOOL_MANAGED",  TRUE},
683     };
684
685     hr = IDirect3DDevice9_GetDirect3D(device, &d3d);
686     ok(SUCCEEDED(hr), "IDirect3DDevice9_GetDirect3D failed (%08x)\n", hr);
687
688     for (i = 0; i < (sizeof(formats) / sizeof(*formats)); ++i) {
689         surface_only = FALSE;
690         hr = IDirect3D9_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_DYNAMIC,
691                 D3DRTYPE_TEXTURE, formats[i].fmt);
692         if (FAILED(hr))
693         {
694             hr = IDirect3D9_CheckDeviceFormat(d3d, 0, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, 0, D3DRTYPE_SURFACE, formats[i].fmt);
695             if (FAILED(hr))
696             {
697                 skip("Format %s not supported, skipping lockrect offset test\n", formats[i].name);
698                 continue;
699             }
700             surface_only = TRUE;
701         }
702
703         for (j = 0; j < (sizeof(pools) / sizeof(*pools)); j++)
704         {
705             switch (pools[j].pool)
706             {
707                 case D3DPOOL_SYSTEMMEM:
708                 case D3DPOOL_MANAGED:
709                     if (surface_only) continue;
710                     /* Fall through */
711                 case D3DPOOL_DEFAULT:
712                     if (surface_only)
713                     {
714                         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 128, 128, formats[i].fmt,
715                                 pools[j].pool, &surface, 0);
716                         ok(SUCCEEDED(hr), "IDirect3DDevice9_CreateOffscreenPlainSurface failed (%08x)\n", hr);
717                     }
718                     else
719                     {
720                         hr = IDirect3DDevice9_CreateTexture(device, 128, 128, 1,
721                                 pools[j].pool == D3DPOOL_DEFAULT ? D3DUSAGE_DYNAMIC : 0,
722                                 formats[i].fmt, pools[j].pool, &texture, NULL);
723                         ok(SUCCEEDED(hr), "IDirect3DDevice9_CreateTexture failed (%08x)\n", hr);
724                         hr = IDirect3DTexture9_GetSurfaceLevel(texture, 0, &surface);
725                         ok(SUCCEEDED(hr), "IDirect3DTexture9_GetSurfaceLevel failed (%08x)\n", hr);
726                         IDirect3DTexture9_Release(texture);
727                     }
728                     if (FAILED(hr)) continue;
729                     break;
730
731                 case D3DPOOL_SCRATCH:
732                     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 128, 128, formats[i].fmt,
733                             pools[j].pool, &surface, 0);
734                     ok(SUCCEEDED(hr), "CreateOffscreenPlainSurface failed (%08x)\n", hr);
735                     if (FAILED(hr)) continue;
736                     break;
737
738                 default:
739                     break;
740             }
741
742             if (formats[i].block_width > 1)
743             {
744                 SetRect(&rect, formats[i].block_width >> 1, 0, formats[i].block_width, formats[i].block_height);
745                 hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &rect, 0);
746                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
747                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
748                         SUCCEEDED(hr) ? "succeeded" : "failed",
749                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
750                 if (SUCCEEDED(hr))
751                 {
752                     hr = IDirect3DSurface9_UnlockRect(surface);
753                     ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect failed (%08x)\n", hr);
754                 }
755
756                 SetRect(&rect, 0, 0, formats[i].block_width >> 1, formats[i].block_height);
757                 hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &rect, 0);
758                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
759                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
760                         SUCCEEDED(hr) ? "succeeded" : "failed",
761                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
762                 if (SUCCEEDED(hr))
763                 {
764                     hr = IDirect3DSurface9_UnlockRect(surface);
765                     ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect failed (%08x)\n", hr);
766                 }
767             }
768
769             if (formats[i].block_height > 1)
770             {
771                 SetRect(&rect, 0, formats[i].block_height >> 1, formats[i].block_width, formats[i].block_height);
772                 hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &rect, 0);
773                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
774                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
775                         SUCCEEDED(hr) ? "succeeded" : "failed",
776                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
777                 if (SUCCEEDED(hr))
778                 {
779                     hr = IDirect3DSurface9_UnlockRect(surface);
780                     ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect failed (%08x)\n", hr);
781                 }
782
783                 SetRect(&rect, 0, 0, formats[i].block_width, formats[i].block_height >> 1);
784                 hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &rect, 0);
785                 ok(!SUCCEEDED(hr) == !pools[j].success || broken(formats[i].broken),
786                         "Partial block lock %s, expected %s, format %s, pool %s.\n",
787                         SUCCEEDED(hr) ? "succeeded" : "failed",
788                         pools[j].success ? "success" : "failure", formats[i].name, pools[j].name);
789                 if (SUCCEEDED(hr))
790                 {
791                     hr = IDirect3DSurface9_UnlockRect(surface);
792                     ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect failed (%08x)\n", hr);
793                 }
794             }
795
796             SetRect(&rect, 0, 0, formats[i].block_width, formats[i].block_height);
797             hr = IDirect3DSurface9_LockRect(surface, &locked_rect, &rect, 0);
798             ok(SUCCEEDED(hr), "Full block lock returned %08x, expected %08x, format %s, pool %s\n",
799                     hr, D3D_OK, formats[i].name, pools[j].name);
800             if (SUCCEEDED(hr))
801             {
802                 hr = IDirect3DSurface9_UnlockRect(surface);
803                 ok(SUCCEEDED(hr), "IDirect3DSurface9_UnlockRect failed (%08x)\n", hr);
804             }
805
806             IDirect3DSurface9_Release(surface);
807         }
808     }
809     IDirect3D9_Release(d3d);
810 }
811
812 START_TEST(surface)
813 {
814     HMODULE d3d9_handle;
815     IDirect3DDevice9 *device_ptr;
816     ULONG refcount;
817
818     d3d9_handle = LoadLibraryA("d3d9.dll");
819     if (!d3d9_handle)
820     {
821         skip("Could not load d3d9.dll\n");
822         return;
823     }
824
825     device_ptr = init_d3d9(d3d9_handle);
826     if (!device_ptr) return;
827
828     test_surface_get_container(device_ptr);
829     test_surface_alignment(device_ptr);
830     test_lockrect_offset(device_ptr);
831     test_lockrect_invalid(device_ptr);
832     test_private_data(device_ptr);
833     test_getdc(device_ptr);
834     test_surface_dimensions(device_ptr);
835     test_surface_format_null(device_ptr);
836     test_surface_double_unlock(device_ptr);
837     test_surface_lockrect_blocks(device_ptr);
838
839     refcount = IDirect3DDevice9_Release(device_ptr);
840     ok(!refcount, "Device has %u references left\n", refcount);
841 }