winmm: Rearrange device mapping when a new default device is chosen.
[wine] / dlls / ddraw / tests / overlay.c
1 /*
2  * Unit tests for DirectDraw overlay functions
3  *
4  * Copyright (C) 2008,2011 Stefan Dösinger for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #define COBJMACROS
21
22 #include "wine/test.h"
23 #include "ddraw.h"
24 #include "unknwn.h"
25
26 static HRESULT (WINAPI *pDirectDrawCreateEx)(LPGUID,LPVOID*,REFIID,LPUNKNOWN);
27
28 static IDirectDraw7 *ddraw = NULL;
29 static IDirectDrawSurface7 *primary = NULL;
30
31 static IDirectDrawSurface7 *create_overlay(DWORD width, DWORD height, DWORD format) {
32     DDSURFACEDESC2 ddsd;
33     HRESULT hr;
34     IDirectDrawSurface7 *ret;
35
36     memset(&ddsd, 0, sizeof(ddsd));
37     ddsd.dwSize = sizeof(ddsd);
38     ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
39     ddsd.dwWidth = width;
40     ddsd.dwHeight = height;
41     ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY;
42     U4(ddsd).ddpfPixelFormat.dwSize = sizeof(U4(ddsd).ddpfPixelFormat);
43     U4(ddsd).ddpfPixelFormat.dwFlags = DDPF_FOURCC;
44     U4(ddsd).ddpfPixelFormat.dwFourCC = format;
45     hr = IDirectDraw7_CreateSurface(ddraw, &ddsd, &ret, NULL);
46     if(FAILED(hr)) return NULL;
47     else return ret;
48 }
49
50 static BOOL CreateDirectDraw(void)
51 {
52     HRESULT hr;
53     DDSURFACEDESC2 ddsd;
54     IDirectDrawSurface7 *overlay = NULL;
55     HMODULE hmod = GetModuleHandleA("ddraw.dll");
56
57     pDirectDrawCreateEx = (void*)GetProcAddress(hmod, "DirectDrawCreateEx");
58     if (!pDirectDrawCreateEx) {
59         win_skip("DirectDrawCreateEx is not available\n");
60         return FALSE;
61     }
62
63     hr = pDirectDrawCreateEx(NULL, (void**)&ddraw, &IID_IDirectDraw7, NULL);
64     ok(hr == DD_OK || hr == DDERR_NODIRECTDRAWSUPPORT, "DirectDrawCreateEx returned: %x\n", hr);
65     if (!ddraw) {
66         trace("DirectDrawCreateEx() failed with an error %x\n", hr);
67         return FALSE;
68     }
69
70     hr = IDirectDraw_SetCooperativeLevel(ddraw, NULL, DDSCL_NORMAL);
71     ok(hr == DD_OK, "SetCooperativeLevel returned: %x\n", hr );
72
73     memset(&ddsd, 0, sizeof(ddsd));
74     ddsd.dwSize = sizeof(ddsd);
75     ddsd.dwFlags = DDSD_CAPS;
76     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
77     hr = IDirectDraw7_CreateSurface(ddraw, &ddsd, &primary, NULL);
78     if (FAILED(hr)) {
79         IDirectDraw7_Release(ddraw);
80         trace("IDirectDraw7_CreateSurface() failed with an error %x\n", hr);
81         return FALSE;
82     }
83
84     overlay = create_overlay(64, 64, MAKEFOURCC('U','Y','V','Y'));
85     if (!overlay) {
86         IDirectDrawSurface7_Release(primary);
87         IDirectDraw7_Release(ddraw);
88         skip("Failed to create an overlay - assuming not supported\n");
89         return FALSE;
90     }
91     IDirectDraw7_Release(overlay);
92
93     return TRUE;
94 }
95
96 static void rectangle_settings(void) {
97     IDirectDrawSurface7 *overlay = create_overlay(64, 64, MAKEFOURCC('U','Y','V','Y'));
98     HRESULT hr, hr2;
99     RECT rect = {0, 0, 64, 64};
100     LONG posx, posy;
101
102     /* The dx sdk sort of implies that rect must be set when DDOVER_SHOW is used. This is not true
103      * in Windows Vista and earlier, but changed in Win7 */
104     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, primary, &rect, DDOVER_SHOW, NULL);
105     ok(hr == DD_OK, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
106     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, primary, NULL, DDOVER_HIDE, NULL);
107     ok(hr == DD_OK, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
108     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, primary, NULL, DDOVER_SHOW, NULL);
109     ok(hr == DD_OK || hr == DDERR_INVALIDPARAMS, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
110
111     /* Show that the overlay position is the (top, left) coordinate of the dest rectangle */
112     rect.top += 16;
113     rect.left += 32;
114     rect.bottom += 16;
115     rect.right += 32;
116     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, primary, &rect, DDOVER_SHOW, NULL);
117     ok(hr == DD_OK, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
118     posx = -1; posy = -1;
119     hr = IDirectDrawSurface7_GetOverlayPosition(overlay, &posx, &posy);
120     ok(hr == DD_OK, "IDirectDrawSurface7_GetOverlayPosition failed with hr=0x%08x\n", hr);
121     ok(posx == rect.left && posy == rect.top, "Overlay position is (%d, %d), expected (%d, %d)\n",
122        posx, posy, rect.left, rect.top);
123
124     /* Passing a NULL dest rect sets the position to 0/0 . Visually it can be seen that the overlay overlays the whole primary(==screen)*/
125     hr2 = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, primary, NULL, 0, NULL);
126     ok(hr2 == DD_OK || hr2 == DDERR_INVALIDPARAMS
127             || hr2 == DDERR_OUTOFCAPS, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr2);
128     hr = IDirectDrawSurface7_GetOverlayPosition(overlay, &posx, &posy);
129     ok(hr == DD_OK, "IDirectDrawSurface7_GetOverlayPosition failed with hr=0x%08x\n", hr);
130     if (SUCCEEDED(hr2))
131     {
132         ok(posx == 0 && posy == 0, "Overlay position is (%d, %d), expected (%d, %d)\n",
133                 posx, posy, 0, 0);
134     }
135     else
136     {
137         /* Otherwise the position remains untouched */
138         ok(posx == 32 && posy == 16, "Overlay position is (%d, %d), expected (%d, %d)\n",
139                 posx, posy, 32, 16);
140     }
141     /* The position cannot be retrieved when the overlay is not shown */
142     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, primary, &rect, DDOVER_HIDE, NULL);
143     ok(hr == DD_OK, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
144     posx = -1; posy = -1;
145     hr = IDirectDrawSurface7_GetOverlayPosition(overlay, &posx, &posy);
146     ok(hr == DDERR_OVERLAYNOTVISIBLE, "IDirectDrawSurface7_GetOverlayPosition failed with hr=0x%08x\n", hr);
147     ok(posx == 0 && posy == 0, "Overlay position is (%d, %d), expected (%d, %d)\n",
148        posx, posy, 0, 0);
149
150     IDirectDrawSurface7_Release(overlay);
151 }
152
153 static void offscreen_test(void) {
154     IDirectDrawSurface7 *overlay = create_overlay(64, 64, MAKEFOURCC('U','Y','V','Y')), *offscreen = NULL;
155     HRESULT hr;
156     DDSURFACEDESC2 ddsd;
157
158     /* Try to overlay a NULL surface */
159     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, NULL, NULL, DDOVER_SHOW, NULL);
160     ok(hr == DDERR_INVALIDPARAMS, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
161     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, NULL, NULL, DDOVER_HIDE, NULL);
162     ok(hr == DDERR_INVALIDPARAMS, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
163
164     /* Try to overlay an offscreen surface */
165     memset(&ddsd, 0, sizeof(ddsd));
166     ddsd.dwSize = sizeof(ddsd);
167     ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
168     ddsd.dwWidth = 64;
169     ddsd.dwHeight = 64;
170     U4(ddsd).ddpfPixelFormat.dwSize = sizeof(U4(ddsd).ddpfPixelFormat);
171     U4(ddsd).ddpfPixelFormat.dwFlags = DDPF_RGB;
172     U4(ddsd).ddpfPixelFormat.dwFourCC = 0;
173     U1(U4(ddsd).ddpfPixelFormat).dwRGBBitCount = 16;
174     U2(U4(ddsd).ddpfPixelFormat).dwRBitMask = 0xF800;
175     U3(U4(ddsd).ddpfPixelFormat).dwGBitMask = 0x07e0;
176     U4(U4(ddsd).ddpfPixelFormat).dwBBitMask = 0x001F;
177     ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
178     hr = IDirectDraw7_CreateSurface(ddraw, &ddsd, &offscreen, NULL);
179     ok(hr == DD_OK, "IDirectDraw7_CreateSurface failed with hr=0x%08x\n", hr);
180
181     hr = IDirectDrawSurface7_UpdateOverlay(overlay, NULL, offscreen, NULL, DDOVER_SHOW, NULL);
182     ok(hr == DD_OK || broken(hr == E_NOTIMPL),
183        "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
184
185     /* Try to overlay the primary with a non-overlay surface */
186     hr = IDirectDrawSurface7_UpdateOverlay(offscreen, NULL, primary, NULL, DDOVER_SHOW, NULL);
187     ok(hr == DDERR_NOTAOVERLAYSURFACE, "IDirectDrawSurface7_UpdateOverlay failed with hr=0x%08x\n", hr);
188
189     IDirectDrawSurface7_Release(offscreen);
190     IDirectDrawSurface7_Release(overlay);
191 }
192
193 static void yv12_test(void)
194 {
195     HRESULT hr;
196     DDSURFACEDESC2 desc;
197     IDirectDrawSurface7 *surface, *dst;
198     char *base;
199     RECT rect = {13, 17, 14, 18};
200     unsigned int offset, y;
201
202     surface = create_overlay(256, 256, MAKEFOURCC('Y','V','1','2'));
203     if(!surface) {
204         skip("YV12 surfaces not available\n");
205         return;
206     }
207
208     memset(&desc, 0, sizeof(desc));
209     desc.dwSize = sizeof(desc);
210     hr = IDirectDrawSurface7_Lock(surface, NULL, &desc, 0, NULL);
211     ok(hr == DD_OK, "IDirectDrawSurface7_Lock returned 0x%08x, expected DD_OK\n", hr);
212
213     ok(desc.dwFlags == (DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH),
214        "Unexpected desc.dwFlags 0x%08x\n", desc.dwFlags);
215     ok(desc.ddsCaps.dwCaps == (DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM) ||
216        desc.ddsCaps.dwCaps == (DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_HWCODEC),
217        "Unexpected desc.ddsCaps.dwCaps 0x%08x\n", desc.ddsCaps.dwCaps);
218     ok(desc.dwWidth == 256 && desc.dwHeight == 256, "Expected size 256x256, got %ux%u\n",
219        desc.dwWidth, desc.dwHeight);
220     /* The overlay pitch seems to have 256 byte alignment */
221     ok((U1(desc).lPitch & 0xff) == 0, "Expected 256 byte aligned pitch, got %u\n", U1(desc).lPitch);
222
223     /* Fill the surface with some data for the blit test */
224     base = desc.lpSurface;
225     /* Luminance */
226     for (y = 0; y < desc.dwHeight; y++)
227     {
228         memset(base + U1(desc).lPitch * y, 0x10, desc.dwWidth);
229     }
230     /* V */
231     for (; y < desc.dwHeight + desc.dwHeight / 4; y++)
232     {
233         memset(base + U1(desc).lPitch * y, 0x20, desc.dwWidth);
234     }
235     /* U */
236     for (; y < desc.dwHeight + desc.dwHeight / 2; y++)
237     {
238         memset(base + U1(desc).lPitch * y, 0x30, desc.dwWidth);
239     }
240
241     hr = IDirectDrawSurface7_Unlock(surface, NULL);
242     ok(hr == DD_OK, "IDirectDrawSurface7_Unlock returned 0x%08x, expected DD_OK\n", hr);
243
244     /* YV12 uses 2x2 blocks with 6 bytes per block(4*Y, 1*U, 1*V). Unlike other block-based formats like DXT
245      * the entire Y channel is stored in one big chunk of memory, followed by the chroma channels. So
246      * partial locks do not really make sense. Show that they are allowed nevertheless and the offset points
247      * into the luminance data */
248     hr = IDirectDrawSurface7_Lock(surface, &rect, &desc, 0, NULL);
249     ok(hr == DD_OK, "Partial lock of a YV12 surface returned 0x%08x, expected DD_OK\n", hr);
250     offset = ((const char *) desc.lpSurface - base);
251     ok(offset == rect.top * U1(desc).lPitch + rect.left, "Expected %u byte offset from partial lock, got %u\n",
252             rect.top * U1(desc).lPitch + rect.left, offset);
253     hr = IDirectDrawSurface7_Unlock(surface, NULL);
254     ok(hr == DD_OK, "IDirectDrawSurface7_Unlock returned 0x%08x, expected DD_OK\n", hr);
255
256     dst = create_overlay(256, 256, MAKEFOURCC('Y','V','1','2'));
257     if (!dst)
258     {
259         /* Windows XP with a Radeon X1600 GPU refuses to create a second overlay surface,
260          * DDERR_NOOVERLAYHW, making the blit tests moot */
261         skip("Could not create a second YV12 surface, skipping blit test\n");
262         goto cleanup;
263     }
264
265     hr = IDirectDrawSurface7_Blt(dst, NULL, surface, NULL, 0, NULL);
266     /* VMware rejects YV12 blits. This behavior has not been seen on real hardware yet, so mark it broken */
267     ok(hr == DD_OK || broken(hr == E_NOTIMPL),
268             "IDirectDrawSurface7_Blt returned 0x%08x, expected DD_OK\n", hr);
269
270     if (SUCCEEDED(hr))
271     {
272         memset(&desc, 0, sizeof(desc));
273         desc.dwSize = sizeof(desc);
274         hr = IDirectDrawSurface7_Lock(dst, NULL, &desc, 0, NULL);
275         ok(hr == DD_OK, "IDirectDrawSurface7_Lock returned 0x%08x, expected DD_OK\n", hr);
276
277         base = desc.lpSurface;
278         ok(base[0] == 0x10, "Y data is 0x%02x, expected 0x10\n", base[0]);
279         base += desc.dwHeight * U1(desc).lPitch;
280         todo_wine ok(base[0] == 0x20, "V data is 0x%02x, expected 0x20\n", base[0]);
281         base += desc.dwHeight / 4 * U1(desc).lPitch;
282         todo_wine ok(base[0] == 0x30, "U data is 0x%02x, expected 0x30\n", base[0]);
283
284         hr = IDirectDrawSurface7_Unlock(dst, NULL);
285         ok(hr == DD_OK, "IDirectDrawSurface7_Unlock returned 0x%08x, expected DD_OK\n", hr);
286     }
287
288     IDirectDrawSurface7_Release(dst);
289 cleanup:
290     IDirectDrawSurface7_Release(surface);
291 }
292
293 START_TEST(overlay)
294 {
295     if(CreateDirectDraw() == FALSE) {
296         skip("Failed to initialize ddraw\n");
297         return;
298     }
299
300     rectangle_settings();
301     offscreen_test();
302     yv12_test();
303
304     if(primary) IDirectDrawSurface7_Release(primary);
305     if(ddraw) IDirectDraw7_Release(ddraw);
306 }