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