ddraw: AddAttachedSurface() just references whatever we pass it.
[wine] / dlls / ddraw / tests / ddraw4.c
1 /*
2  * Copyright 2011 Henri Verbeet for CodeWeavers
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
19 #include "wine/test.h"
20 #include <limits.h>
21 #include "d3d.h"
22
23 struct vec3
24 {
25     float x, y, z;
26 };
27
28 struct vec4
29 {
30     float x, y, z, w;
31 };
32
33 struct create_window_thread_param
34 {
35     HWND window;
36     HANDLE window_created;
37     HANDLE destroy_window;
38     HANDLE thread;
39 };
40
41 static BOOL compare_float(float f, float g, unsigned int ulps)
42 {
43     int x = *(int *)&f;
44     int y = *(int *)&g;
45
46     if (x < 0)
47         x = INT_MIN - x;
48     if (y < 0)
49         y = INT_MIN - y;
50
51     if (abs(x - y) > ulps)
52         return FALSE;
53
54     return TRUE;
55 }
56
57 static BOOL compare_vec4(struct vec4 *vec, float x, float y, float z, float w, unsigned int ulps)
58 {
59     return compare_float(vec->x, x, ulps)
60             && compare_float(vec->y, y, ulps)
61             && compare_float(vec->z, z, ulps)
62             && compare_float(vec->w, w, ulps);
63 }
64
65 static BOOL compare_color(D3DCOLOR c1, D3DCOLOR c2, BYTE max_diff)
66 {
67     if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
68     c1 >>= 8; c2 >>= 8;
69     if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
70     c1 >>= 8; c2 >>= 8;
71     if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
72     c1 >>= 8; c2 >>= 8;
73     if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
74     return TRUE;
75 }
76
77 static DWORD WINAPI create_window_thread_proc(void *param)
78 {
79     struct create_window_thread_param *p = param;
80     DWORD res;
81     BOOL ret;
82
83     p->window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
84             0, 0, 640, 480, 0, 0, 0, 0);
85     ret = SetEvent(p->window_created);
86     ok(ret, "SetEvent failed, last error %#x.\n", GetLastError());
87
88     for (;;)
89     {
90         MSG msg;
91
92         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
93             DispatchMessage(&msg);
94         res = WaitForSingleObject(p->destroy_window, 100);
95         if (res == WAIT_OBJECT_0)
96             break;
97         if (res != WAIT_TIMEOUT)
98         {
99             ok(0, "Wait failed (%#x), last error %#x.\n", res, GetLastError());
100             break;
101         }
102     }
103
104     DestroyWindow(p->window);
105
106     return 0;
107 }
108
109 static void create_window_thread(struct create_window_thread_param *p)
110 {
111     DWORD res, tid;
112
113     p->window_created = CreateEvent(NULL, FALSE, FALSE, NULL);
114     ok(!!p->window_created, "CreateEvent failed, last error %#x.\n", GetLastError());
115     p->destroy_window = CreateEvent(NULL, FALSE, FALSE, NULL);
116     ok(!!p->destroy_window, "CreateEvent failed, last error %#x.\n", GetLastError());
117     p->thread = CreateThread(NULL, 0, create_window_thread_proc, p, 0, &tid);
118     ok(!!p->thread, "Failed to create thread, last error %#x.\n", GetLastError());
119     res = WaitForSingleObject(p->window_created, INFINITE);
120     ok(res == WAIT_OBJECT_0, "Wait failed (%#x), last error %#x.\n", res, GetLastError());
121 }
122
123 static void destroy_window_thread(struct create_window_thread_param *p)
124 {
125     SetEvent(p->destroy_window);
126     WaitForSingleObject(p->thread, INFINITE);
127     CloseHandle(p->destroy_window);
128     CloseHandle(p->window_created);
129     CloseHandle(p->thread);
130 }
131
132 static D3DCOLOR get_surface_color(IDirectDrawSurface4 *surface, UINT x, UINT y)
133 {
134     RECT rect = {x, y, x + 1, y + 1};
135     DDSURFACEDESC2 surface_desc;
136     D3DCOLOR color;
137     HRESULT hr;
138
139     memset(&surface_desc, 0, sizeof(surface_desc));
140     surface_desc.dwSize = sizeof(surface_desc);
141
142     hr = IDirectDrawSurface4_Lock(surface, &rect, &surface_desc, DDLOCK_READONLY | DDLOCK_WAIT, NULL);
143     ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
144     if (FAILED(hr))
145         return 0xdeadbeef;
146
147     color = *((DWORD *)surface_desc.lpSurface) & 0x00ffffff;
148
149     hr = IDirectDrawSurface4_Unlock(surface, &rect);
150     ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
151
152     return color;
153 }
154
155 static HRESULT CALLBACK enum_z_fmt(DDPIXELFORMAT *format, void *ctx)
156 {
157     DDPIXELFORMAT *z_fmt = ctx;
158
159     if (U1(*format).dwZBufferBitDepth > U1(*z_fmt).dwZBufferBitDepth)
160         *z_fmt = *format;
161
162     return DDENUMRET_OK;
163 }
164
165 static IDirectDraw4 *create_ddraw(void)
166 {
167     IDirectDraw4 *ddraw4;
168     IDirectDraw *ddraw1;
169     HRESULT hr;
170
171     if (FAILED(DirectDrawCreate(NULL, &ddraw1, NULL)))
172         return NULL;
173
174     hr = IDirectDraw_QueryInterface(ddraw1, &IID_IDirectDraw4, (void **)&ddraw4);
175     IDirectDraw_Release(ddraw1);
176     if (FAILED(hr))
177         return NULL;
178
179     return ddraw4;
180 }
181
182 static IDirect3DDevice3 *create_device(HWND window, DWORD coop_level)
183 {
184     IDirectDrawSurface4 *surface, *ds;
185     IDirect3DDevice3 *device = NULL;
186     DDSURFACEDESC2 surface_desc;
187     IDirectDraw4 *ddraw4;
188     DDPIXELFORMAT z_fmt;
189     IDirect3D3 *d3d3;
190     HRESULT hr;
191
192     if (!(ddraw4 = create_ddraw()))
193         return NULL;
194
195     hr = IDirectDraw4_SetCooperativeLevel(ddraw4, window, coop_level);
196     ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
197
198     memset(&surface_desc, 0, sizeof(surface_desc));
199     surface_desc.dwSize = sizeof(surface_desc);
200     surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
201     surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
202     surface_desc.dwWidth = 640;
203     surface_desc.dwHeight = 480;
204
205     hr = IDirectDraw4_CreateSurface(ddraw4, &surface_desc, &surface, NULL);
206     ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
207
208     if (coop_level & DDSCL_NORMAL)
209     {
210         IDirectDrawClipper *clipper;
211
212         hr = IDirectDraw4_CreateClipper(ddraw4, 0, &clipper, NULL);
213         ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
214         hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
215         ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
216         hr = IDirectDrawSurface4_SetClipper(surface, clipper);
217         ok(SUCCEEDED(hr), "Failed to set surface clipper, hr %#x.\n", hr);
218         IDirectDrawClipper_Release(clipper);
219     }
220
221     hr = IDirectDraw4_QueryInterface(ddraw4, &IID_IDirect3D3, (void **)&d3d3);
222     IDirectDraw4_Release(ddraw4);
223     if (FAILED(hr))
224     {
225         IDirectDrawSurface4_Release(surface);
226         return NULL;
227     }
228
229     memset(&z_fmt, 0, sizeof(z_fmt));
230     hr = IDirect3D3_EnumZBufferFormats(d3d3, &IID_IDirect3DHALDevice, enum_z_fmt, &z_fmt);
231     if (FAILED(hr) || !z_fmt.dwSize)
232     {
233         IDirect3D3_Release(d3d3);
234         IDirectDrawSurface4_Release(surface);
235         return NULL;
236     }
237
238     memset(&surface_desc, 0, sizeof(surface_desc));
239     surface_desc.dwSize = sizeof(surface_desc);
240     surface_desc.dwFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
241     surface_desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER;
242     U4(surface_desc).ddpfPixelFormat = z_fmt;
243     surface_desc.dwWidth = 640;
244     surface_desc.dwHeight = 480;
245     hr = IDirectDraw4_CreateSurface(ddraw4, &surface_desc, &ds, NULL);
246     ok(SUCCEEDED(hr), "Failed to create depth buffer, hr %#x.\n", hr);
247     if (FAILED(hr))
248     {
249         IDirect3D3_Release(d3d3);
250         IDirectDrawSurface4_Release(surface);
251         return NULL;
252     }
253
254     hr = IDirectDrawSurface_AddAttachedSurface(surface, ds);
255     ok(SUCCEEDED(hr), "Failed to attach depth buffer, hr %#x.\n", hr);
256     IDirectDrawSurface4_Release(ds);
257     if (FAILED(hr))
258     {
259         IDirect3D3_Release(d3d3);
260         IDirectDrawSurface4_Release(surface);
261         return NULL;
262     }
263
264     hr = IDirect3D3_CreateDevice(d3d3, &IID_IDirect3DHALDevice, surface, &device, NULL);
265     IDirect3D3_Release(d3d3);
266     IDirectDrawSurface4_Release(surface);
267     if (FAILED(hr))
268         return NULL;
269
270     return device;
271 }
272
273 static void test_process_vertices(void)
274 {
275     IDirect3DVertexBuffer *src_vb, *dst_vb;
276     IDirect3DViewport3 *viewport;
277     D3DVERTEXBUFFERDESC vb_desc;
278     IDirect3DDevice3 *device;
279     struct vec3 *src_data;
280     struct vec4 *dst_data;
281     IDirect3D3 *d3d3;
282     D3DVIEWPORT2 vp2;
283     D3DVIEWPORT vp1;
284     HWND window;
285     HRESULT hr;
286
287     static D3DMATRIX identity =
288     {
289         1.0f, 0.0f, 0.0f, 0.0f,
290         0.0f, 1.0f, 0.0f, 0.0f,
291         0.0f, 0.0f, 1.0f, 0.0f,
292         0.0f, 0.0f, 0.0f, 1.0f,
293     };
294     static D3DMATRIX projection =
295     {
296         1.0f, 0.0f, 0.0f, 0.0f,
297         0.0f, 1.0f, 0.0f, 0.0f,
298         0.0f, 0.0f, 1.0f, 0.0f,
299         6.0f, 7.0f, 8.0f, 1.0f,
300     };
301
302     window = CreateWindowA("static", "d3d7_test", WS_OVERLAPPEDWINDOW,
303             0, 0, 640, 480, 0, 0, 0, 0);
304     if (!(device = create_device(window, DDSCL_NORMAL)))
305     {
306         skip("Failed to create a 3D device, skipping test.\n");
307         DestroyWindow(window);
308         return;
309     }
310
311     hr = IDirect3DDevice3_GetDirect3D(device, &d3d3);
312     ok(SUCCEEDED(hr), "Failed to get Direct3D3 interface, hr %#x.\n", hr);
313
314     memset(&vb_desc, 0, sizeof(vb_desc));
315     vb_desc.dwSize = sizeof(vb_desc);
316     vb_desc.dwFVF = D3DFVF_XYZ;
317     vb_desc.dwNumVertices = 3;
318     hr = IDirect3D3_CreateVertexBuffer(d3d3, &vb_desc, &src_vb, 0, NULL);
319     ok(SUCCEEDED(hr), "Failed to create source vertex buffer, hr %#x.\n", hr);
320
321     hr = IDirect3DVertexBuffer_Lock(src_vb, DDLOCK_WRITEONLY, (void **)&src_data, NULL);
322     ok(SUCCEEDED(hr), "Failed to lock source vertex buffer, hr %#x.\n", hr);
323     src_data[0].x = -1.0f;
324     src_data[0].y = -1.0f;
325     src_data[0].z = -1.0f;
326     src_data[1].x = 0.0f;
327     src_data[1].y = 0.0f;
328     src_data[1].z = 0.0f;
329     src_data[2].x = 1.0f;
330     src_data[2].y = 1.0f;
331     src_data[2].z = 1.0f;
332     hr = IDirect3DVertexBuffer_Unlock(src_vb);
333     ok(SUCCEEDED(hr), "Failed to unlock source vertex buffer, hr %#x.\n", hr);
334
335     memset(&vb_desc, 0, sizeof(vb_desc));
336     vb_desc.dwSize = sizeof(vb_desc);
337     vb_desc.dwFVF = D3DFVF_XYZRHW;
338     vb_desc.dwNumVertices = 3;
339     hr = IDirect3D3_CreateVertexBuffer(d3d3, &vb_desc, &dst_vb, 0, NULL);
340     ok(SUCCEEDED(hr), "Failed to create destination vertex buffer, hr %#x.\n", hr);
341
342     hr = IDirect3D3_CreateViewport(d3d3, &viewport, NULL);
343     ok(SUCCEEDED(hr), "Failed to create viewport, hr %#x.\n", hr);
344     hr = IDirect3DDevice3_AddViewport(device, viewport);
345     ok(SUCCEEDED(hr), "Failed to add viewport, hr %#x.\n", hr);
346     vp2.dwSize = sizeof(vp2);
347     vp2.dwX = 10;
348     vp2.dwY = 20;
349     vp2.dwWidth = 100;
350     vp2.dwHeight = 200;
351     vp2.dvClipX = 2.0f;
352     vp2.dvClipY = 3.0f;
353     vp2.dvClipWidth = 4.0f;
354     vp2.dvClipHeight = 5.0f;
355     vp2.dvMinZ = -2.0f;
356     vp2.dvMaxZ = 3.0f;
357     hr = IDirect3DViewport3_SetViewport2(viewport, &vp2);
358     ok(SUCCEEDED(hr), "Failed to set viewport data, hr %#x.\n", hr);
359     hr = IDirect3DDevice3_SetCurrentViewport(device, viewport);
360     ok(SUCCEEDED(hr), "Failed to set current viewport, hr %#x.\n", hr);
361
362     hr = IDirect3DDevice3_SetTransform(device, D3DTRANSFORMSTATE_WORLD, &identity);
363     ok(SUCCEEDED(hr), "Failed to set world transformation, hr %#x.\n", hr);
364     hr = IDirect3DDevice3_SetTransform(device, D3DTRANSFORMSTATE_VIEW, &identity);
365     ok(SUCCEEDED(hr), "Failed to set view transformation, hr %#x.\n", hr);
366     hr = IDirect3DDevice3_SetTransform(device, D3DTRANSFORMSTATE_PROJECTION, &identity);
367     ok(SUCCEEDED(hr), "Failed to set projection transformation, hr %#x.\n", hr);
368
369     hr = IDirect3DVertexBuffer_ProcessVertices(dst_vb, D3DVOP_TRANSFORM, 0, 3, src_vb, 0, device, 0);
370     ok(SUCCEEDED(hr), "Failed to process vertices, hr %#x.\n", hr);
371
372     hr = IDirect3DVertexBuffer_Lock(dst_vb, DDLOCK_READONLY, (void **)&dst_data, NULL);
373     ok(SUCCEEDED(hr), "Failed to lock destination vertex buffer, hr %#x.\n", hr);
374     ok(compare_vec4(&dst_data[0], -6.500e+1f, +1.800e+2f, +2.000e-1f, +1.000e+0f, 4096),
375             "Got unexpected vertex 0 {%.8e, %.8e, %.8e, %.8e}.\n",
376             dst_data[0].x, dst_data[0].y, dst_data[0].z, dst_data[0].w);
377     ok(compare_vec4(&dst_data[1], -4.000e+1f, +1.400e+2f, +4.000e-1f, +1.000e+0f, 4096),
378             "Got unexpected vertex 1 {%.8e, %.8e, %.8e, %.8e}.\n",
379             dst_data[1].x, dst_data[1].y, dst_data[1].z, dst_data[1].w);
380     ok(compare_vec4(&dst_data[2], -1.500e+1f, +1.000e+2f, +6.000e-1f, +1.000e+0f, 4096),
381             "Got unexpected vertex 2 {%.8e, %.8e, %.8e, %.8e}.\n",
382             dst_data[2].x, dst_data[2].y, dst_data[2].z, dst_data[2].w);
383     hr = IDirect3DVertexBuffer_Unlock(dst_vb);
384     ok(SUCCEEDED(hr), "Failed to unlock destination vertex buffer, hr %#x.\n", hr);
385
386     hr = IDirect3DDevice3_MultiplyTransform(device, D3DTRANSFORMSTATE_PROJECTION, &projection);
387     ok(SUCCEEDED(hr), "Failed to set projection transformation, hr %#x.\n", hr);
388
389     hr = IDirect3DVertexBuffer_ProcessVertices(dst_vb, D3DVOP_TRANSFORM, 0, 3, src_vb, 0, device, 0);
390     ok(SUCCEEDED(hr), "Failed to process vertices, hr %#x.\n", hr);
391
392     hr = IDirect3DVertexBuffer_Lock(dst_vb, DDLOCK_READONLY, (void **)&dst_data, NULL);
393     ok(SUCCEEDED(hr), "Failed to lock destination vertex buffer, hr %#x.\n", hr);
394     ok(compare_vec4(&dst_data[0], +8.500e+1f, -1.000e+2f, +1.800e+0f, +1.000e+0f, 4096),
395             "Got unexpected vertex 0 {%.8e, %.8e, %.8e, %.8e}.\n",
396             dst_data[0].x, dst_data[0].y, dst_data[0].z, dst_data[0].w);
397     ok(compare_vec4(&dst_data[1], +1.100e+2f, -1.400e+2f, +2.000e+0f, +1.000e+0f, 4096),
398             "Got unexpected vertex 1 {%.8e, %.8e, %.8e, %.8e}.\n",
399             dst_data[1].x, dst_data[1].y, dst_data[1].z, dst_data[1].w);
400     ok(compare_vec4(&dst_data[2], +1.350e+2f, -1.800e+2f, +2.200e+0f, +1.000e+0f, 4096),
401             "Got unexpected vertex 2 {%.8e, %.8e, %.8e, %.8e}.\n",
402             dst_data[2].x, dst_data[2].y, dst_data[2].z, dst_data[2].w);
403     hr = IDirect3DVertexBuffer_Unlock(dst_vb);
404     ok(SUCCEEDED(hr), "Failed to unlock destination vertex buffer, hr %#x.\n", hr);
405
406     vp2.dwSize = sizeof(vp2);
407     vp2.dwX = 30;
408     vp2.dwY = 40;
409     vp2.dwWidth = 90;
410     vp2.dwHeight = 80;
411     vp2.dvClipX = 4.0f;
412     vp2.dvClipY = 6.0f;
413     vp2.dvClipWidth = 2.0f;
414     vp2.dvClipHeight = 4.0f;
415     vp2.dvMinZ = 3.0f;
416     vp2.dvMaxZ = -2.0f;
417     hr = IDirect3DViewport3_SetViewport2(viewport, &vp2);
418     ok(SUCCEEDED(hr), "Failed to set viewport data, hr %#x.\n", hr);
419
420     hr = IDirect3DVertexBuffer_ProcessVertices(dst_vb, D3DVOP_TRANSFORM, 0, 3, src_vb, 0, device, 0);
421     ok(SUCCEEDED(hr), "Failed to process vertices, hr %#x.\n", hr);
422
423     hr = IDirect3DVertexBuffer_Lock(dst_vb, DDLOCK_READONLY, (void **)&dst_data, NULL);
424     ok(SUCCEEDED(hr), "Failed to lock destination vertex buffer, hr %#x.\n", hr);
425     ok(compare_vec4(&dst_data[0], +7.500e+1f, +4.000e+1f, -8.000e-1f, +1.000e+0f, 4096),
426             "Got unexpected vertex 0 {%.8e, %.8e, %.8e, %.8e}.\n",
427             dst_data[0].x, dst_data[0].y, dst_data[0].z, dst_data[0].w);
428     ok(compare_vec4(&dst_data[1], +1.200e+2f, +2.000e+1f, -1.000e+0f, +1.000e+0f, 4096),
429             "Got unexpected vertex 1 {%.8e, %.8e, %.8e, %.8e}.\n",
430             dst_data[1].x, dst_data[1].y, dst_data[1].z, dst_data[1].w);
431     ok(compare_vec4(&dst_data[2], +1.650e+2f, +0.000e+0f, -1.200e+0f, +1.000e+0f, 4096),
432             "Got unexpected vertex 2 {%.8e, %.8e, %.8e, %.8e}.\n",
433             dst_data[2].x, dst_data[2].y, dst_data[2].z, dst_data[2].w);
434     hr = IDirect3DVertexBuffer_Unlock(dst_vb);
435     ok(SUCCEEDED(hr), "Failed to unlock destination vertex buffer, hr %#x.\n", hr);
436
437     vp1.dwSize = sizeof(vp1);
438     vp1.dwX = 30;
439     vp1.dwY = 40;
440     vp1.dwWidth = 90;
441     vp1.dwHeight = 80;
442     vp1.dvScaleX = 7.0f;
443     vp1.dvScaleY = 2.0f;
444     vp1.dvMaxX = 6.0f;
445     vp1.dvMaxY = 10.0f;
446     vp1.dvMinZ = -2.0f;
447     vp1.dvMaxZ = 3.0f;
448     hr = IDirect3DViewport3_SetViewport(viewport, &vp1);
449     ok(SUCCEEDED(hr), "Failed to set viewport data, hr %#x.\n", hr);
450
451     hr = IDirect3DVertexBuffer_ProcessVertices(dst_vb, D3DVOP_TRANSFORM, 0, 3, src_vb, 0, device, 0);
452     ok(SUCCEEDED(hr), "Failed to process vertices, hr %#x.\n", hr);
453
454     hr = IDirect3DVertexBuffer_Lock(dst_vb, DDLOCK_READONLY, (void **)&dst_data, NULL);
455     ok(SUCCEEDED(hr), "Failed to lock destination vertex buffer, hr %#x.\n", hr);
456     ok(compare_vec4(&dst_data[0], +1.100e+2f, +6.800e+1f, +7.000e+0f, +1.000e+0f, 4096),
457             "Got unexpected vertex 0 {%.8e, %.8e, %.8e, %.8e}.\n",
458             dst_data[0].x, dst_data[0].y, dst_data[0].z, dst_data[0].w);
459     ok(compare_vec4(&dst_data[1], +1.170e+2f, +6.600e+1f, +8.000e+0f, +1.000e+0f, 4096),
460             "Got unexpected vertex 1 {%.8e, %.8e, %.8e, %.8e}.\n",
461             dst_data[1].x, dst_data[1].y, dst_data[1].z, dst_data[1].w);
462     ok(compare_vec4(&dst_data[2], +1.240e+2f, +6.400e+1f, +9.000e+0f, +1.000e+0f, 4096),
463             "Got unexpected vertex 2 {%.8e, %.8e, %.8e, %.8e}.\n",
464             dst_data[2].x, dst_data[2].y, dst_data[2].z, dst_data[2].w);
465     hr = IDirect3DVertexBuffer_Unlock(dst_vb);
466     ok(SUCCEEDED(hr), "Failed to unlock destination vertex buffer, hr %#x.\n", hr);
467
468     hr = IDirect3DDevice3_DeleteViewport(device, viewport);
469     ok(SUCCEEDED(hr), "Failed to delete viewport, hr %#x.\n", hr);
470
471     IDirect3DVertexBuffer_Release(dst_vb);
472     IDirect3DVertexBuffer_Release(src_vb);
473     IDirect3DViewport3_Release(viewport);
474     IDirect3D3_Release(d3d3);
475     IDirect3DDevice3_Release(device);
476     DestroyWindow(window);
477 }
478
479 static void test_coop_level_create_device_window(void)
480 {
481     HWND focus_window, device_window;
482     IDirectDraw4 *ddraw;
483     HRESULT hr;
484
485     focus_window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
486             0, 0, 640, 480, 0, 0, 0, 0);
487     if (!(ddraw = create_ddraw()))
488     {
489         skip("Failed to create a ddraw object, skipping test.\n");
490         DestroyWindow(focus_window);
491         return;
492     }
493
494     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
495     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
496     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
497     ok(!device_window, "Unexpected device window found.\n");
498     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_CREATEDEVICEWINDOW);
499     ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
500     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
501     ok(!device_window, "Unexpected device window found.\n");
502     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_CREATEDEVICEWINDOW | DDSCL_NORMAL);
503     ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
504     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
505     ok(!device_window, "Unexpected device window found.\n");
506     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_CREATEDEVICEWINDOW | DDSCL_NORMAL | DDSCL_FULLSCREEN);
507     ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
508     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
509     ok(!device_window, "Unexpected device window found.\n");
510     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_CREATEDEVICEWINDOW | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
511     ok(hr == DDERR_NOFOCUSWINDOW || broken(hr == DDERR_INVALIDPARAMS), "Got unexpected hr %#x.\n", hr);
512     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
513     ok(!device_window, "Unexpected device window found.\n");
514
515     /* Windows versions before 98 / NT5 don't support DDSCL_CREATEDEVICEWINDOW. */
516     if (broken(hr == DDERR_INVALIDPARAMS))
517     {
518         win_skip("DDSCL_CREATEDEVICEWINDOW not supported, skipping test.\n");
519         IDirectDraw4_Release(ddraw);
520         DestroyWindow(focus_window);
521         return;
522     }
523
524     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
525     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
526     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
527     ok(!device_window, "Unexpected device window found.\n");
528     hr = IDirectDraw4_SetCooperativeLevel(ddraw, focus_window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
529     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
530     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
531     ok(!device_window, "Unexpected device window found.\n");
532
533     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
534     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
535     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
536     ok(!device_window, "Unexpected device window found.\n");
537     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_SETFOCUSWINDOW
538             | DDSCL_CREATEDEVICEWINDOW | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
539     ok(hr == DDERR_NOHWND, "Got unexpected hr %#x.\n", hr);
540     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
541     ok(!!device_window, "Device window not found.\n");
542
543     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
544     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
545     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
546     ok(!device_window, "Unexpected device window found.\n");
547     hr = IDirectDraw4_SetCooperativeLevel(ddraw, focus_window, DDSCL_SETFOCUSWINDOW
548             | DDSCL_CREATEDEVICEWINDOW | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
549     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
550     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
551     ok(!!device_window, "Device window not found.\n");
552
553     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
554     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
555     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
556     ok(!device_window, "Unexpected device window found.\n");
557     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_CREATEDEVICEWINDOW | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
558     ok(hr == DDERR_NOFOCUSWINDOW, "Got unexpected hr %#x.\n", hr);
559     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
560     ok(!device_window, "Unexpected device window found.\n");
561     hr = IDirectDraw4_SetCooperativeLevel(ddraw, focus_window, DDSCL_SETFOCUSWINDOW);
562     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
563     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
564     ok(!device_window, "Unexpected device window found.\n");
565     hr = IDirectDraw4_SetCooperativeLevel(ddraw, NULL, DDSCL_CREATEDEVICEWINDOW | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
566     ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
567     device_window = FindWindowA("DirectDrawDeviceWnd", "DirectDrawDeviceWnd");
568     ok(!!device_window, "Device window not found.\n");
569
570     IDirectDraw4_Release(ddraw);
571     DestroyWindow(focus_window);
572 }
573
574 static void test_clipper_blt(void)
575 {
576     IDirectDrawSurface4 *src_surface, *dst_surface;
577     RECT client_rect, src_rect, *rect;
578     IDirectDrawClipper *clipper;
579     DDSURFACEDESC2 surface_desc;
580     unsigned int i, j, x, y;
581     IDirectDraw4 *ddraw;
582     RGNDATA *rgn_data;
583     D3DCOLOR color;
584     HRGN r1, r2;
585     HWND window;
586     DDBLTFX fx;
587     HRESULT hr;
588     DWORD *ptr;
589     DWORD ret;
590
591     static const DWORD src_data[] =
592     {
593         0xff0000ff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffffffff, 0xffffffff,
594         0xff0000ff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffffffff, 0xffffffff,
595         0xff0000ff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffffffff, 0xffffffff,
596     };
597     static const D3DCOLOR expected1[] =
598     {
599         0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
600         0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
601         0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
602         0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
603     };
604     static const D3DCOLOR expected2[] =
605     {
606         0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
607         0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
608         0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
609         0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
610     };
611
612     window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
613             10, 10, 640, 480, 0, 0, 0, 0);
614     ShowWindow(window, SW_SHOW);
615     if (!(ddraw = create_ddraw()))
616     {
617         skip("Failed to create a ddraw object, skipping test.\n");
618         DestroyWindow(window);
619         return;
620     }
621
622     ret = GetClientRect(window, &client_rect);
623     ok(ret, "Failed to get client rect.\n");
624     ret = MapWindowPoints(window, NULL, (POINT *)&client_rect, 2);
625     ok(ret, "Failed to map client rect.\n");
626
627     hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
628     ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
629
630     hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper, NULL);
631     ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
632     hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
633     ok(hr == DDERR_NOCLIPLIST, "Got unexpected hr %#x.\n", hr);
634     hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
635     ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
636     hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
637     ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
638     rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
639     hr = IDirectDrawClipper_GetClipList(clipper, NULL, rgn_data, &ret);
640     ok(SUCCEEDED(hr), "Failed to get clip list, hr %#x.\n", hr);
641     ok(rgn_data->rdh.dwSize == sizeof(rgn_data->rdh), "Got unexpected structure size %#x.\n", rgn_data->rdh.dwSize);
642     ok(rgn_data->rdh.iType == RDH_RECTANGLES, "Got unexpected type %#x.\n", rgn_data->rdh.iType);
643     ok(rgn_data->rdh.nCount == 1, "Got unexpected count %u.\n", rgn_data->rdh.nCount);
644     ok(rgn_data->rdh.nRgnSize == 16, "Got unexpected region size %u.\n", rgn_data->rdh.nRgnSize);
645     ok(EqualRect(&rgn_data->rdh.rcBound, &client_rect),
646             "Got unexpected bounding rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
647             rgn_data->rdh.rcBound.left, rgn_data->rdh.rcBound.top,
648             rgn_data->rdh.rcBound.right, rgn_data->rdh.rcBound.bottom,
649             client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
650     rect = (RECT *)&rgn_data->Buffer[0];
651     ok(EqualRect(rect, &client_rect),
652             "Got unexpected clip rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
653             rect->left, rect->top, rect->right, rect->bottom,
654             client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
655     HeapFree(GetProcessHeap(), 0, rgn_data);
656
657     r1 = CreateRectRgn(0, 0, 320, 240);
658     ok(!!r1, "Failed to create region.\n");
659     r2 = CreateRectRgn(320, 240, 640, 480);
660     ok(!!r2, "Failed to create region.\n");
661     CombineRgn(r1, r1, r2, RGN_OR);
662     ret = GetRegionData(r1, 0, NULL);
663     rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
664     ret = GetRegionData(r1, ret, rgn_data);
665     ok(!!ret, "Failed to get region data.\n");
666
667     DeleteObject(r2);
668     DeleteObject(r1);
669
670     hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
671     ok(hr == DDERR_CLIPPERISUSINGHWND, "Got unexpected hr %#x.\n", hr);
672     hr = IDirectDrawClipper_SetHWnd(clipper, 0, NULL);
673     ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
674     hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
675     ok(SUCCEEDED(hr), "Failed to set clip list, hr %#x.\n", hr);
676
677     HeapFree(GetProcessHeap(), 0, rgn_data);
678
679     memset(&surface_desc, 0, sizeof(surface_desc));
680     surface_desc.dwSize = sizeof(surface_desc);
681     surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
682     surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
683     surface_desc.dwWidth = 640;
684     surface_desc.dwHeight = 480;
685     U4(surface_desc).ddpfPixelFormat.dwSize = sizeof(U4(surface_desc).ddpfPixelFormat);
686     U4(surface_desc).ddpfPixelFormat.dwFlags = DDPF_RGB;
687     U1(U4(surface_desc).ddpfPixelFormat).dwRGBBitCount = 32;
688     U2(U4(surface_desc).ddpfPixelFormat).dwRBitMask = 0x00ff0000;
689     U3(U4(surface_desc).ddpfPixelFormat).dwGBitMask = 0x0000ff00;
690     U4(U4(surface_desc).ddpfPixelFormat).dwBBitMask = 0x000000ff;
691
692     hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &src_surface, NULL);
693     ok(SUCCEEDED(hr), "Failed to create source surface, hr %#x.\n", hr);
694     hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &dst_surface, NULL);
695     ok(SUCCEEDED(hr), "Failed to create destination surface, hr %#x.\n", hr);
696
697     memset(&fx, 0, sizeof(fx));
698     fx.dwSize = sizeof(fx);
699     hr = IDirectDrawSurface4_Blt(src_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
700     ok(SUCCEEDED(hr), "Failed to clear source surface, hr %#x.\n", hr);
701     hr = IDirectDrawSurface4_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
702     ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
703
704     hr = IDirectDrawSurface4_Lock(src_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL);
705     ok(SUCCEEDED(hr), "Failed to lock source surface, hr %#x.\n", hr);
706     ok(U1(surface_desc).lPitch == 2560, "Got unexpected surface pitch %u.\n", U1(surface_desc).lPitch);
707     ptr = surface_desc.lpSurface;
708     memcpy(&ptr[   0], &src_data[ 0], 6 * sizeof(DWORD));
709     memcpy(&ptr[ 640], &src_data[ 6], 6 * sizeof(DWORD));
710     memcpy(&ptr[1280], &src_data[12], 6 * sizeof(DWORD));
711     hr = IDirectDrawSurface4_Unlock(src_surface, NULL);
712     ok(SUCCEEDED(hr), "Failed to unlock source surface, hr %#x.\n", hr);
713
714     hr = IDirectDrawSurface4_SetClipper(dst_surface, clipper);
715     ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
716
717     SetRect(&src_rect, 1, 1, 5, 2);
718     hr = IDirectDrawSurface4_Blt(dst_surface, NULL, src_surface, &src_rect, DDBLT_WAIT, NULL);
719     ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
720     for (i = 0; i < 4; ++i)
721     {
722         for (j = 0; j < 4; ++j)
723         {
724             x = 80 * ((2 * j) + 1);
725             y = 60 * ((2 * i) + 1);
726             color = get_surface_color(dst_surface, x, y);
727             ok(compare_color(color, expected1[i * 4 + j], 1),
728                     "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
729         }
730     }
731
732     U5(fx).dwFillColor = 0xff0000ff;
733     hr = IDirectDrawSurface4_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
734     ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
735     for (i = 0; i < 4; ++i)
736     {
737         for (j = 0; j < 4; ++j)
738         {
739             x = 80 * ((2 * j) + 1);
740             y = 60 * ((2 * i) + 1);
741             color = get_surface_color(dst_surface, x, y);
742             ok(compare_color(color, expected2[i * 4 + j], 1),
743                     "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
744         }
745     }
746
747     hr = IDirectDrawSurface4_BltFast(dst_surface, 0, 0, src_surface, NULL, DDBLTFAST_WAIT);
748     ok(hr == DDERR_BLTFASTCANTCLIP, "Got unexpected hr %#x.\n", hr);
749
750     hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
751     ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
752     hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
753     ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
754     DestroyWindow(window);
755     hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
756     ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
757     hr = IDirectDrawClipper_SetHWnd(clipper, 0, NULL);
758     ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
759     hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
760     ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
761     hr = IDirectDrawClipper_SetClipList(clipper, NULL, 0);
762     ok(SUCCEEDED(hr), "Failed to set clip list, hr %#x.\n", hr);
763     hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
764     ok(hr == DDERR_NOCLIPLIST, "Got unexpected hr %#x.\n", hr);
765     hr = IDirectDrawSurface4_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
766     ok(hr == DDERR_NOCLIPLIST, "Got unexpected hr %#x.\n", hr);
767
768     IDirectDrawSurface4_Release(dst_surface);
769     IDirectDrawSurface4_Release(src_surface);
770     IDirectDrawClipper_Release(clipper);
771     IDirectDraw4_Release(ddraw);
772 }
773
774 static void test_coop_level_d3d_state(void)
775 {
776     D3DRECT clear_rect = {{0}, {0}, {640}, {480}};
777     IDirectDrawSurface4 *rt, *surface;
778     IDirect3DViewport3 *viewport;
779     IDirect3DDevice3 *device;
780     IDirectDraw4 *ddraw;
781     D3DVIEWPORT2 vp;
782     IDirect3D3 *d3d;
783     D3DCOLOR color;
784     DWORD value;
785     HWND window;
786     HRESULT hr;
787
788     window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
789             0, 0, 640, 480, 0, 0, 0, 0);
790     if (!(device = create_device(window, DDSCL_NORMAL)))
791     {
792         skip("Failed to create D3D device, skipping test.\n");
793         DestroyWindow(window);
794         return;
795     }
796
797     hr = IDirect3DDevice3_GetDirect3D(device, &d3d);
798     ok(SUCCEEDED(hr), "Failed to get d3d interface, hr %#x.\n", hr);
799
800     hr = IDirect3D3_CreateViewport(d3d, &viewport, NULL);
801     ok(SUCCEEDED(hr), "Failed to create viewport, hr %#x.\n", hr);
802     hr = IDirect3DDevice3_AddViewport(device, viewport);
803     ok(SUCCEEDED(hr), "Failed to add viewport, hr %#x.\n", hr);
804     memset(&vp, 0, sizeof(vp));
805     vp.dwSize = sizeof(vp);
806     vp.dwX = 0;
807     vp.dwY = 0;
808     vp.dwWidth = 640;
809     vp.dwHeight = 480;
810     vp.dvClipX = -1.0f;
811     vp.dvClipY =  1.0f;
812     vp.dvClipWidth = 2.0f;
813     vp.dvClipHeight = 2.0f;
814     vp.dvMinZ = 0.0f;
815     vp.dvMaxZ = 1.0f;
816     hr = IDirect3DViewport3_SetViewport2(viewport, &vp);
817     ok(SUCCEEDED(hr), "Failed to set viewport data, hr %#x.\n", hr);
818
819     hr = IDirect3DDevice3_GetRenderTarget(device, &rt);
820     ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
821     hr = IDirect3DDevice3_GetRenderState(device, D3DRENDERSTATE_ZENABLE, &value);
822     ok(SUCCEEDED(hr), "Failed to get render state, hr %#x.\n", hr);
823     ok(!!value, "Got unexpected z-enable state %#x.\n", value);
824     hr = IDirect3DDevice3_GetRenderState(device, D3DRENDERSTATE_ALPHABLENDENABLE, &value);
825     ok(SUCCEEDED(hr), "Failed to get render state, hr %#x.\n", hr);
826     ok(!value, "Got unexpected alpha blend enable state %#x.\n", value);
827     hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
828     ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr);
829     hr = IDirect3DViewport3_Clear2(viewport, 1, &clear_rect, D3DCLEAR_TARGET, 0xffff0000, 0.0f, 0);
830     ok(SUCCEEDED(hr), "Failed to clear viewport, hr %#x.\n", hr);
831     color = get_surface_color(rt, 320, 240);
832     ok(compare_color(color, 0x00ff0000, 1), "Got unexpected color 0x%08x.\n", color);
833
834     hr = IDirect3D3_QueryInterface(d3d, &IID_IDirectDraw4, (void **)&ddraw);
835     ok(SUCCEEDED(hr), "Failed to get ddraw interface, hr %#x.\n", hr);
836     IDirect3D3_Release(d3d);
837     hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
838     ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
839     hr = IDirectDrawSurface4_IsLost(rt);
840     ok(hr == DDERR_SURFACELOST, "Got unexpected hr %#x.\n", hr);
841     hr = IDirectDraw4_RestoreAllSurfaces(ddraw);
842     ok(SUCCEEDED(hr), "Failed to restore surfaces, hr %#x.\n", hr);
843     IDirectDraw4_Release(ddraw);
844
845     hr = IDirect3DDevice3_GetRenderTarget(device, &surface);
846     ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
847     ok(surface == rt, "Got unexpected surface %p.\n", surface);
848     hr = IDirect3DDevice3_GetRenderState(device, D3DRENDERSTATE_ZENABLE, &value);
849     ok(SUCCEEDED(hr), "Failed to get render state, hr %#x.\n", hr);
850     ok(!!value, "Got unexpected z-enable state %#x.\n", value);
851     hr = IDirect3DDevice3_GetRenderState(device, D3DRENDERSTATE_ALPHABLENDENABLE, &value);
852     ok(SUCCEEDED(hr), "Failed to get render state, hr %#x.\n", hr);
853     ok(!!value, "Got unexpected alpha blend enable state %#x.\n", value);
854     hr = IDirect3DViewport3_Clear2(viewport, 1, &clear_rect, D3DCLEAR_TARGET, 0xff00ff00, 0.0f, 0);
855     ok(SUCCEEDED(hr), "Failed to clear viewport, hr %#x.\n", hr);
856     color = get_surface_color(rt, 320, 240);
857     ok(compare_color(color, 0x0000ff00, 1), "Got unexpected color 0x%08x.\n", color);
858
859     hr = IDirect3DDevice3_DeleteViewport(device, viewport);
860     ok(SUCCEEDED(hr), "Failed to delete viewport, hr %#x.\n", hr);
861     IDirect3DViewport3_Release(viewport);
862     IDirectDrawSurface4_Release(surface);
863     IDirectDrawSurface4_Release(rt);
864     IDirect3DDevice3_Release(device);
865     DestroyWindow(window);
866 }
867
868 static void test_surface_interface_mismatch(void)
869 {
870     IDirectDraw4 *ddraw = NULL;
871     IDirect3D3 *d3d = NULL;
872     IDirectDrawSurface4 *surface = NULL, *ds;
873     IDirectDrawSurface3 *surface3 = NULL;
874     IDirect3DDevice3 *device = NULL;
875     IDirect3DViewport3 *viewport = NULL;
876     DDSURFACEDESC2 surface_desc;
877     DDPIXELFORMAT z_fmt;
878     ULONG refcount;
879     HRESULT hr;
880     D3DCOLOR color;
881     HWND window;
882     D3DVIEWPORT2 vp;
883     D3DRECT clear_rect = {{0}, {0}, {640}, {480}};
884
885     window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
886             0, 0, 640, 480, 0, 0, 0, 0);
887
888     if (!(ddraw = create_ddraw()))
889     {
890         skip("Failed to create a ddraw object, skipping test.\n");
891         goto cleanup;
892     }
893
894     hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
895     ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
896
897     memset(&surface_desc, 0, sizeof(surface_desc));
898     surface_desc.dwSize = sizeof(surface_desc);
899     surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
900     surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
901     surface_desc.dwWidth = 640;
902     surface_desc.dwHeight = 480;
903
904     hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
905     ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
906
907     hr = IDirectDrawSurface4_QueryInterface(surface, &IID_IDirectDrawSurface3, (void **)&surface3);
908     ok(SUCCEEDED(hr), "Failed to QI IDirectDrawSurface3, hr %#x.\n", hr);
909
910     hr = IDirectDraw4_QueryInterface(ddraw, &IID_IDirect3D3, (void **)&d3d);
911     if (FAILED(hr))
912     {
913         skip("Failed to get the IDirect3D7 interface, skipping test.\n");
914         goto cleanup;
915     }
916
917     memset(&z_fmt, 0, sizeof(z_fmt));
918     hr = IDirect3D3_EnumZBufferFormats(d3d, &IID_IDirect3DHALDevice, enum_z_fmt, &z_fmt);
919     if (FAILED(hr) || !z_fmt.dwSize)
920     {
921         skip("No depth buffer formats available, skipping test.\n");
922         goto cleanup;
923     }
924
925     memset(&surface_desc, 0, sizeof(surface_desc));
926     surface_desc.dwSize = sizeof(surface_desc);
927     surface_desc.dwFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
928     surface_desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER;
929     U4(surface_desc).ddpfPixelFormat = z_fmt;
930     surface_desc.dwWidth = 640;
931     surface_desc.dwHeight = 480;
932     hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &ds, NULL);
933     ok(SUCCEEDED(hr), "Failed to create depth buffer, hr %#x.\n", hr);
934     if (FAILED(hr))
935         goto cleanup;
936
937     /* Using a different surface interface version still works */
938     hr = IDirectDrawSurface3_AddAttachedSurface(surface3, (IDirectDrawSurface3 *)ds);
939     ok(SUCCEEDED(hr), "Failed to attach depth buffer, hr %#x.\n", hr);
940     refcount = IDirectDrawSurface4_Release(ds);
941     ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
942     if (FAILED(hr))
943         goto cleanup;
944
945     /* Here too */
946     hr = IDirect3D3_CreateDevice(d3d, &IID_IDirect3DHALDevice, (IDirectDrawSurface4 *)surface3, &device, NULL);
947     ok(SUCCEEDED(hr), "Failed to create d3d device.\n");
948     if (FAILED(hr))
949         goto cleanup;
950
951     hr = IDirect3D3_CreateViewport(d3d, &viewport, NULL);
952     ok(SUCCEEDED(hr), "Failed to create viewport, hr %#x.\n", hr);
953     hr = IDirect3DDevice3_AddViewport(device, viewport);
954     ok(SUCCEEDED(hr), "Failed to add viewport, hr %#x.\n", hr);
955     memset(&vp, 0, sizeof(vp));
956     vp.dwSize = sizeof(vp);
957     vp.dwX = 0;
958     vp.dwY = 0;
959     vp.dwWidth = 640;
960     vp.dwHeight = 480;
961     vp.dvClipX = -1.0f;
962     vp.dvClipY =  1.0f;
963     vp.dvClipWidth = 2.0f;
964     vp.dvClipHeight = 2.0f;
965     vp.dvMinZ = 0.0f;
966     vp.dvMaxZ = 1.0f;
967     hr = IDirect3DViewport3_SetViewport2(viewport, &vp);
968     ok(SUCCEEDED(hr), "Failed to set viewport data, hr %#x.\n", hr);
969
970     hr = IDirect3DViewport3_Clear2(viewport, 1, &clear_rect, D3DCLEAR_TARGET, 0xffff0000, 0.0f, 0);
971     ok(SUCCEEDED(hr), "Failed to clear viewport, hr %#x.\n", hr);
972     color = get_surface_color(surface, 320, 240);
973     ok(compare_color(color, 0x00ff0000, 1), "Got unexpected color 0x%08x.\n", color);
974
975 cleanup:
976     if (viewport)
977     {
978         IDirect3DDevice2_DeleteViewport(device, viewport);
979         IDirect3DViewport2_Release(viewport);
980     }
981     if (surface3) IDirectDrawSurface3_Release(surface3);
982     if (surface) IDirectDrawSurface4_Release(surface);
983     if (device) IDirect3DDevice3_Release(device);
984     if (d3d) IDirect3D3_Release(d3d);
985     if (ddraw) IDirectDraw4_Release(ddraw);
986     DestroyWindow(window);
987 }
988
989 static void test_coop_level_threaded(void)
990 {
991     struct create_window_thread_param p;
992     IDirectDraw4 *ddraw;
993     HRESULT hr;
994
995     if (!(ddraw = create_ddraw()))
996     {
997         skip("Failed to create a ddraw object, skipping test.\n");
998         return;
999     }
1000     create_window_thread(&p);
1001
1002     hr = IDirectDraw4_SetCooperativeLevel(ddraw, p.window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
1003     ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
1004
1005     IDirectDraw4_Release(ddraw);
1006     destroy_window_thread(&p);
1007 }
1008
1009 static IDirectDrawSurface4 *get_depth_stencil(IDirect3DDevice3 *device)
1010 {
1011     IDirectDrawSurface4 *rt, *ret;
1012     DDSCAPS2 caps = {DDSCAPS_ZBUFFER, 0, 0, 0};
1013     HRESULT hr;
1014
1015     hr = IDirect3DDevice3_GetRenderTarget(device, &rt);
1016     ok(SUCCEEDED(hr), "Failed to get the render target, hr %#x.\n", hr);
1017     hr = IDirectDrawSurface4_GetAttachedSurface(rt, &caps, &ret);
1018     ok(SUCCEEDED(hr) || hr == DDERR_NOTFOUND, "Failed to get the z buffer, hr %#x.\n", hr);
1019     IDirectDrawSurface4_Release(rt);
1020     return ret;
1021 }
1022
1023 static void test_depth_blit(void)
1024 {
1025     static struct
1026     {
1027         float x, y, z;
1028         DWORD color;
1029     }
1030     quad1[] =
1031     {
1032         { -1.0,  1.0, 0.50f, 0xff00ff00},
1033         {  1.0,  1.0, 0.50f, 0xff00ff00},
1034         { -1.0, -1.0, 0.50f, 0xff00ff00},
1035         {  1.0, -1.0, 0.50f, 0xff00ff00},
1036     };
1037     static const D3DCOLOR expected_colors[4][4] =
1038     {
1039         {0x00ff0000, 0x00ff0000, 0x0000ff00, 0x0000ff00},
1040         {0x00ff0000, 0x00ff0000, 0x0000ff00, 0x0000ff00},
1041         {0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00},
1042         {0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00},
1043     };
1044     DDSURFACEDESC2 ddsd_new, ddsd_existing;
1045
1046     IDirect3DDevice3 *device;
1047     IDirectDrawSurface4 *ds1, *ds2, *ds3, *rt;
1048     IDirect3DViewport3 *viewport;
1049     D3DVIEWPORT2 vp_data;
1050     RECT src_rect, dst_rect;
1051     unsigned int i, j;
1052     D3DCOLOR color;
1053     HRESULT hr;
1054     IDirect3D3 *d3d;
1055     IDirectDraw4 *ddraw;
1056     DDBLTFX fx;
1057     HWND window;
1058     D3DRECT d3drect;
1059
1060     window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
1061             0, 0, 640, 480, 0, 0, 0, 0);
1062     if (!(device = create_device(window, DDSCL_NORMAL)))
1063     {
1064         skip("Failed to create D3D device, skipping test.\n");
1065         DestroyWindow(window);
1066         return;
1067     }
1068
1069     hr = IDirect3DDevice3_GetDirect3D(device, &d3d);
1070     ok(SUCCEEDED(hr), "Failed to get Direct3D3 interface, hr %#x.\n", hr);
1071     hr = IDirect3D3_QueryInterface(d3d, &IID_IDirectDraw4, (void **)&ddraw);
1072     ok(SUCCEEDED(hr), "Failed to get DirectDraw4 interface, hr %#x.\n", hr);
1073     hr = IDirect3D3_CreateViewport(d3d, &viewport, NULL);
1074     ok(SUCCEEDED(hr), "Failed to create a viewport, hr %#x.\n", hr);
1075     IDirect3D3_Release(d3d);
1076
1077     ds1 = get_depth_stencil(device);
1078
1079     memset(&ddsd_new, 0, sizeof(ddsd_new));
1080     ddsd_new.dwSize = sizeof(ddsd_new);
1081     memset(&ddsd_existing, 0, sizeof(ddsd_existing));
1082     ddsd_existing.dwSize = sizeof(ddsd_existing);
1083     hr = IDirectDrawSurface4_GetSurfaceDesc(ds1, &ddsd_existing);
1084     ddsd_new.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
1085     ddsd_new.ddsCaps.dwCaps = DDSCAPS_ZBUFFER;
1086     ddsd_new.dwWidth = ddsd_existing.dwWidth;
1087     ddsd_new.dwHeight = ddsd_existing.dwHeight;
1088     U4(ddsd_new).ddpfPixelFormat = U4(ddsd_existing).ddpfPixelFormat;
1089     hr = IDirectDraw4_CreateSurface(ddraw, &ddsd_new, &ds2, NULL);
1090     ok(SUCCEEDED(hr), "Failed to create a surface, hr %#x.\n", hr);
1091     hr = IDirectDraw4_CreateSurface(ddraw, &ddsd_new, &ds3, NULL);
1092     ok(SUCCEEDED(hr), "Failed to create a surface, hr %#x.\n", hr);
1093     IDirectDraw4_Release(ddraw);
1094
1095     hr = IDirect3DDevice3_AddViewport(device, viewport);
1096     ok(SUCCEEDED(hr), "Failed to add viewport to device, hr %#x.\n", hr);
1097     memset(&vp_data, 0, sizeof(vp_data));
1098     vp_data.dwSize = sizeof(vp_data);
1099     vp_data.dwWidth = ddsd_existing.dwWidth;
1100     vp_data.dwHeight = ddsd_existing.dwHeight;
1101     vp_data.dvMaxZ = 1.0;
1102     vp_data.dvClipX = -1.0f;
1103     vp_data.dvClipWidth = 2.0f;
1104     vp_data.dvClipY = 1.0f;
1105     vp_data.dvClipHeight = 2.0f;
1106     hr = IDirect3DViewport3_SetViewport2(viewport, &vp_data);
1107     ok(SUCCEEDED(hr), "Failed to set viewport data, hr %#x.\n", hr);
1108     hr = IDirect3DDevice3_SetCurrentViewport(device, viewport);
1109     ok(SUCCEEDED(hr), "Failed to activate the viewport, hr %#x.\n", hr);
1110
1111     hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_TRUE);
1112     ok(SUCCEEDED(hr), "Failed to enable z testing, hr %#x.\n", hr);
1113     hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL);
1114     ok(SUCCEEDED(hr), "Failed to set the z function, hr %#x.\n", hr);
1115     hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE);
1116     ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr);
1117
1118     U1(d3drect).x1 = U2(d3drect).y1 = 0;
1119     U3(d3drect).x2 = vp_data.dwWidth; U4(d3drect).y2 = vp_data.dwHeight;
1120     hr = IDirect3DViewport3_Clear2(viewport, 1, &d3drect, D3DCLEAR_ZBUFFER, 0, 0.0f, 0);
1121     ok(SUCCEEDED(hr), "Failed to clear the z buffer, hr %#x.\n", hr);
1122
1123     /* Partial blit. */
1124     SetRect(&src_rect, 0, 0, 320, 240);
1125     SetRect(&dst_rect, 0, 0, 320, 240);
1126     hr = IDirectDrawSurface4_Blt(ds2, &dst_rect, ds1, &src_rect, DDBLT_WAIT, NULL);
1127     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1128     /* Different locations. */
1129     SetRect(&src_rect, 0, 0, 320, 240);
1130     SetRect(&dst_rect, 320, 240, 640, 480);
1131     hr = IDirectDrawSurface4_Blt(ds2, &dst_rect, ds1, &src_rect, DDBLT_WAIT, NULL);
1132     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1133     /* Streched. */
1134     SetRect(&src_rect, 0, 0, 320, 240);
1135     SetRect(&dst_rect, 0, 0, 640, 480);
1136     hr = IDirectDrawSurface4_Blt(ds2, &dst_rect, ds1, &src_rect, DDBLT_WAIT, NULL);
1137     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1138     /* Flipped. */
1139     SetRect(&src_rect, 0, 480, 640, 0);
1140     SetRect(&dst_rect, 0, 0, 640, 480);
1141     hr = IDirectDrawSurface4_Blt(ds2, &dst_rect, ds1, &src_rect, DDBLT_WAIT, NULL);
1142     ok(hr == DDERR_INVALIDRECT, "Got unexpected hr %#x.\n", hr);
1143     SetRect(&src_rect, 0, 0, 640, 480);
1144     SetRect(&dst_rect, 0, 480, 640, 0);
1145     hr = IDirectDrawSurface4_Blt(ds2, &dst_rect, ds1, &src_rect, DDBLT_WAIT, NULL);
1146     ok(hr == DDERR_INVALIDRECT, "Got unexpected hr %#x.\n", hr);
1147     /* Full, explicit. */
1148     SetRect(&src_rect, 0, 0, 640, 480);
1149     SetRect(&dst_rect, 0, 0, 640, 480);
1150     hr = IDirectDrawSurface4_Blt(ds2, &dst_rect, ds1, &src_rect, DDBLT_WAIT, NULL);
1151     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1152     /* Depth -> color blit: Succeeds on Win7 + Radeon HD 5700, fails on WinXP + Radeon X1600 */
1153
1154     /* Depth blit inside a BeginScene / EndScene pair */
1155     hr = IDirect3DDevice3_BeginScene(device);
1156     ok(SUCCEEDED(hr), "Failed to start a scene, hr %#x.\n", hr);
1157     /* From the current depth stencil */
1158     hr = IDirectDrawSurface4_Blt(ds2, NULL, ds1, NULL, DDBLT_WAIT, NULL);
1159     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1160     /* To the current depth stencil */
1161     hr = IDirectDrawSurface4_Blt(ds1, NULL, ds2, NULL, DDBLT_WAIT, NULL);
1162     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1163     /* Between unbound surfaces */
1164     hr = IDirectDrawSurface4_Blt(ds3, NULL, ds2, NULL, DDBLT_WAIT, NULL);
1165     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1166     hr = IDirect3DDevice3_EndScene(device);
1167     ok(SUCCEEDED(hr), "Failed to end a scene, hr %#x.\n", hr);
1168
1169     /* Avoid changing the depth stencil, it doesn't work properly on Windows.
1170      * Instead use DDBLT_DEPTHFILL to clear the depth stencil. Unfortunately
1171      * drivers disagree on the meaning of dwFillDepth. Only 0 seems to produce
1172      * a reliable result(z = 0.0) */
1173     memset(&fx, 0, sizeof(fx));
1174     fx.dwSize = sizeof(fx);
1175     hr = IDirectDrawSurface4_Blt(ds2, NULL, NULL, NULL, DDBLT_DEPTHFILL | DDBLT_WAIT, &fx);
1176     ok(SUCCEEDED(hr), "Failed to clear the source z buffer, hr %#x.\n", hr);
1177
1178     hr = IDirect3DViewport3_Clear2(viewport, 1, &d3drect, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, 0xffff0000, 1.0f, 0);
1179     ok(SUCCEEDED(hr), "Failed to clear the color and z buffers, hr %#x.\n", hr);
1180     SetRect(&dst_rect, 0, 0, 320, 240);
1181     hr = IDirectDrawSurface4_Blt(ds1, &dst_rect, ds2, NULL, DDBLT_WAIT, NULL);
1182     ok(SUCCEEDED(hr), "Got unexpected hr %#x.\n", hr);
1183     IDirectDrawSurface4_Release(ds3);
1184     IDirectDrawSurface4_Release(ds2);
1185     IDirectDrawSurface4_Release(ds1);
1186
1187     hr = IDirect3DDevice3_BeginScene(device);
1188     ok(SUCCEEDED(hr), "Failed to start a scene, hr %#x.\n", hr);
1189     hr = IDirect3DDevice3_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DFVF_XYZ | D3DFVF_DIFFUSE,
1190             quad1, 4, 0);
1191     ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
1192     hr = IDirect3DDevice3_EndScene(device);
1193     ok(SUCCEEDED(hr), "Failed to end a scene, hr %#x.\n", hr);
1194
1195     hr = IDirect3DDevice3_GetRenderTarget(device, &rt);
1196     ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
1197     for (i = 0; i < 4; ++i)
1198     {
1199         for (j = 0; j < 4; ++j)
1200         {
1201             unsigned int x = 80 * ((2 * j) + 1);
1202             unsigned int y = 60 * ((2 * i) + 1);
1203             color = get_surface_color(rt, x, y);
1204             ok(compare_color(color, expected_colors[i][j], 1),
1205                     "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected_colors[i][j], x, y, color);
1206         }
1207     }
1208     IDirectDrawSurface4_Release(rt);
1209
1210     hr = IDirect3DDevice3_DeleteViewport(device, viewport);
1211     ok(SUCCEEDED(hr), "Failed to delete viewport from device, hr %#x.\n", hr);
1212     IDirect3DViewport3_Release(viewport);
1213     IDirect3DDevice3_Release(device);
1214     DestroyWindow(window);
1215 }
1216
1217 START_TEST(ddraw4)
1218 {
1219     test_process_vertices();
1220     test_coop_level_create_device_window();
1221     test_clipper_blt();
1222     test_coop_level_d3d_state();
1223     test_surface_interface_mismatch();
1224     test_coop_level_threaded();
1225     test_depth_blit();
1226 }