This is an adjustment of 7324.
d3d9:visual already has a test that's relatively close to what a simplified version of the tests in 7324 looked like, so I made the few changes to expand that test to match and then ported it to ddraw.
Implementation-wise, this removes the SD/HD difference (which only exists on NVidia and is not necessary to improve the mentioned application). It also removes the clamping of YUV values, which as the tests show is not correct.
-- v2: wined3d: Interpret Y'CbCr values as being from the reduced range. ddraw/tests: Port yuv_layout_test() from d3d9. d3d9/tests: Expand the YUV blit tests a bit.
From: Elizabeth Figura zfigura@codeweavers.com
Test more values, make the tests tighter, and simplify the "control" rect a bit.
Inspired by a patch by Brendan McGrath. --- dlls/d3d9/tests/visual.c | 74 ++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 30 deletions(-)
diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c index 0c511f495b3..7206e0247f7 100644 --- a/dlls/d3d9/tests/visual.c +++ b/dlls/d3d9/tests/visual.c @@ -13746,7 +13746,7 @@ done:
static void yuv_layout_test(void) { - unsigned int color, ref_color, fmt, i, x, y; + unsigned int color, fmt, i, x, y; HRESULT hr; IDirect3DSurface9 *surface, *target; D3DFORMAT format; @@ -13763,19 +13763,27 @@ static void yuv_layout_test(void)
static const struct { - DWORD color1, color2; - DWORD rgb1, rgb2; + uint8_t y, u, v; + uint32_t rgb_full, rgb_reduced; } - test_data[] = + tests[] = { - { 0x000000, 0xffffff, 0x00008800, 0x00ff7dff }, - { 0xff0000, 0x00ffff, 0x004aff14, 0x00b800ee }, - { 0x00ff00, 0xff00ff, 0x000024ee, 0x00ffe114 }, - { 0x0000ff, 0xffff00, 0x00b80000, 0x004affff }, - { 0xffff00, 0x0000ff, 0x004affff, 0x00b80000 }, - { 0xff00ff, 0x00ff00, 0x00ffe114, 0x000024ee }, - { 0x00ffff, 0xff0000, 0x00b800ee, 0x004aff14 }, - { 0xffffff, 0x000000, 0x00ff7dff, 0x00008800 }, + {0x10, 0x80, 0x80, 0x000000, 0x101010}, + {0xeb, 0x80, 0x80, 0xffffff, 0xebebeb}, + {0x51, 0x5a, 0xf0, 0xff0000, 0xee0e0e}, + {0x91, 0x36, 0x22, 0x00ff01, 0x0dee0e}, + {0x29, 0xf0, 0x6e, 0x0000ff, 0x100fef}, + {0x7e, 0x80, 0x80, 0x808080, 0x7e7e7e}, + {0x00, 0x80, 0x80, 0x000000, 0x000000}, + {0xff, 0x80, 0x80, 0xffffff, 0xffffff}, + {0x00, 0x00, 0x00, 0x008800, 0x008800}, + {0xff, 0x00, 0x00, 0x4aff14, 0x4cff1c}, + {0x00, 0xff, 0x00, 0x0024ee, 0x0030e1}, + {0x00, 0x00, 0xff, 0xb80000, 0xb20000}, + {0xff, 0xff, 0x00, 0x4affff, 0x4cffff}, + {0xff, 0x00, 0xff, 0xffe114, 0xffd01c}, + {0x00, 0xff, 0xff, 0xb800ee, 0xb200e1}, + {0xff, 0xff, 0xff, 0xff7dff, 0xff78ff}, };
static const struct @@ -13836,11 +13844,15 @@ static void yuv_layout_test(void) continue; }
+ winetest_push_context("format %s", formats[fmt].str); + hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, format, D3DPOOL_DEFAULT, &surface, NULL); ok(hr == D3D_OK, "Got hr %#lx.\n", hr);
- for (i = 0; i < ARRAY_SIZE(test_data); i++) + for (i = 0; i < ARRAY_SIZE(tests); i++) { + winetest_push_context("value (%#x,%#x,%#x)", tests[i].y, tests[i].u, tests[i].v); + hr = IDirect3DSurface9_LockRect(surface, &lr, NULL, 0); ok(hr == D3D_OK, "Got hr %#lx.\n", hr); buf = lr.pBits; @@ -13850,15 +13862,14 @@ static void yuv_layout_test(void) v_buf = chroma_buf; u_buf = chroma_buf + height / 2 * lr.Pitch/2; } - /* Draw the top left quarter of the screen with color1, the rest with color2 */ for (y = 0; y < height; y++) { for (x = 0; x < width; x += 2) { - DWORD color = (x < width / 2 && y < height / 2) ? test_data[i].color1 : test_data[i].color2; - BYTE Y = (color >> 16) & 0xff; - BYTE U = (color >> 8) & 0xff; - BYTE V = (color >> 0) & 0xff; + uint8_t Y = tests[i].y, U = tests[i].u, V = tests[i].v; + if (x < width / 2 && y < height / 2) + Y = U = V = 0x40; + if (format == D3DFMT_UYVY) { buf[y * lr.Pitch + 2 * x + 0] = U; @@ -13897,36 +13908,39 @@ static void yuv_layout_test(void) hr = IDirect3DDevice9_StretchRect(device, surface, NULL, target, NULL, D3DTEXF_POINT); ok(hr == D3D_OK, "Got hr %#lx.\n", hr);
- /* Some Windows drivers (mostly Nvidia, but also some VM drivers) insist on doing linear filtering - * although we asked for point filtering. To prevent running into precision problems, read at points - * with some margin within each quadrant. - * - * Unfortunately different implementations(Windows-Nvidia and Mac-AMD tested) interpret some colors - * vastly differently, so we need a max diff of 18. */ for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) { UINT xcoord = (1 + 2 * x) * 640 / 8; UINT ycoord = (1 + 2 * y) * 480 / 8; - ref_color = (y < 2 && x < 2) ? test_data[i].rgb1 : test_data[i].rgb2; + color = getPixelColor(device, xcoord, ycoord); - ok(color_match(color, ref_color, 18), - "Format %s: Got color %#x for pixel (%d/%d)/(%d/%d), pixel %d %d, expected %#x.\n", - fmt_string, color, x, 4, y, 4, xcoord, ycoord, ref_color); + if (x < 2 && y < 2) + ok(color_match(color, 0x008400, 1), + "Got color %#x at (%u, %u).\n", color, xcoord, ycoord); + else + ok(color_match(color, tests[i].rgb_full, 1) + || color_match(color, tests[i].rgb_reduced, 1), + "Got color %#x at (%u, %u), expected %#x.\n", color, xcoord, ycoord, tests[i].rgb_full); } } hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL); ok(hr == S_OK, "Got hr %#lx.\n", hr); + + winetest_pop_context(); }
- for (i = 0; i < ARRAY_SIZE(test_data); i++) + for (i = 0; i < ARRAY_SIZE(tests); i++) { - hr = IDirect3DDevice9_ColorFill(device, surface, NULL, test_data[i].color1); + hr = IDirect3DDevice9_ColorFill(device, surface, NULL, + ((uint32_t)tests[i].y << 16) | ((uint32_t)tests[i].u << 8) | tests[i].v); ok(hr == S_OK, "Got hr %#lx.\n", hr); }
IDirect3DSurface9_Release(surface); + + winetest_pop_context(); }
IDirect3DSurface9_Release(target);
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ddraw/tests/ddraw1.c | 185 ++++++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw2.c | 185 ++++++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw4.c | 185 ++++++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw7.c | 185 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 740 insertions(+)
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 205370a8577..e62d9283aaa 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -20,6 +20,7 @@
#define COBJMACROS
+#include <stdint.h> #include "wine/test.h" #include <limits.h> #include <math.h> @@ -15935,6 +15936,189 @@ static void test_sysmem_x_channel(void) DestroyWindow(window); }
+static void test_yuv_blit(void) +{ + DDSURFACEDESC surface_desc = {sizeof(surface_desc)}; + IDirectDrawSurface *rgb_surface, *yuv_surface; + const unsigned int width = 32, height = 32; + uint8_t *buf, *chroma_buf, *u_buf, *v_buf; + DDBLTFX fx = {.dwSize = sizeof(fx)}; + unsigned int color, refcount; + IDirectDraw *ddraw; + HWND window; + HRESULT hr; + + static const struct + { + uint8_t y, u, v; + uint32_t rgb_full, rgb_reduced; + } + tests[] = + { + {0x10, 0x80, 0x80, 0x000000, 0x101010}, + {0xeb, 0x80, 0x80, 0xffffff, 0xebebeb}, + {0x51, 0x5a, 0xf0, 0xff0000, 0xee0e0e}, + {0x91, 0x36, 0x22, 0x00ff01, 0x0dee0e}, + {0x29, 0xf0, 0x6e, 0x0000ff, 0x100fef}, + {0x7e, 0x80, 0x80, 0x808080, 0x7e7e7e}, + {0x00, 0x80, 0x80, 0x000000, 0x000000}, + {0xff, 0x80, 0x80, 0xffffff, 0xffffff}, + {0x00, 0x00, 0x00, 0x008800, 0x008800}, + {0xff, 0x00, 0x00, 0x4aff14, 0x4cff1c}, + {0x00, 0xff, 0x00, 0x0024ee, 0x0030e1}, + {0x00, 0x00, 0xff, 0xb80000, 0xb20000}, + {0xff, 0xff, 0x00, 0x4affff, 0x4cffff}, + {0xff, 0x00, 0xff, 0xffe114, 0xffd01c}, + {0x00, 0xff, 0xff, 0xb800ee, 0xb200e1}, + {0xff, 0xff, 0xff, 0xff7dff, 0xff78ff}, + }; + + static const struct + { + DWORD fourcc; + const char *str; + } + formats[] = + { + {MAKEFOURCC('U','Y','V','Y'), "UYVY"}, + {MAKEFOURCC('Y','U','Y','2'), "YUY2"}, + {MAKEFOURCC('Y','V','1','2'), "YV12"}, + {MAKEFOURCC('N','V','1','2'), "NV12"}, + }; + + window = create_window(); + ddraw = create_ddraw(); + hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + surface_desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 640; + surface_desc.dwHeight = 480; + init_format_b8g8r8x8(&surface_desc.ddpfPixelFormat); + + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &rgb_surface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + for (unsigned int fmt = 0; fmt < ARRAY_SIZE(formats); fmt++) + { + DWORD format = formats[fmt].fourcc; + + winetest_push_context("format %s", formats[fmt].str); + + surface_desc.dwWidth = width; + surface_desc.dwHeight = height; + surface_desc.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + surface_desc.ddpfPixelFormat.dwFourCC = format; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &yuv_surface, NULL); + if (hr != S_OK) + { + skip("Failed to create surface, hr %#lx.\n", hr); + winetest_pop_context(); + continue; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("value (%#x,%#x,%#x)", tests[i].y, tests[i].u, tests[i].v); + + hr = IDirectDrawSurface_Lock(yuv_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + buf = surface_desc.lpSurface; + chroma_buf = buf + surface_desc.lPitch * height; + if (format == MAKEFOURCC('Y','V','1','2')) + { + v_buf = chroma_buf; + u_buf = chroma_buf + height / 2 * surface_desc.lPitch / 2; + } + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x += 2) + { + uint8_t Y = tests[i].y, U = tests[i].u, V = tests[i].v; + if (x < width / 2 && y < height / 2) + Y = U = V = 0x40; + + if (format == MAKEFOURCC('U','Y','V','Y')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = U; + buf[y * surface_desc.lPitch + 2 * x + 1] = Y; + buf[y * surface_desc.lPitch + 2 * x + 2] = V; + buf[y * surface_desc.lPitch + 2 * x + 3] = Y; + } + else if (format == MAKEFOURCC('Y','U','Y','2')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = Y; + buf[y * surface_desc.lPitch + 2 * x + 1] = U; + buf[y * surface_desc.lPitch + 2 * x + 2] = Y; + buf[y * surface_desc.lPitch + 2 * x + 3] = V; + } + else if (format == MAKEFOURCC('Y','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + u_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = U; + v_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = V; + } + else if (format == MAKEFOURCC('N','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 0] = U; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 1] = V; + } + } + } + hr = IDirectDrawSurface_Unlock(yuv_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface_Blt(rgb_surface, NULL, yuv_surface, NULL, DDBLT_WAIT, NULL); + if (hr != D3D_OK) + { + winetest_pop_context(); + skip("Failed to blit, hr %#lx.\n", hr); + break; + } + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface_Lock(rgb_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + for (unsigned int y = 0; y < 4; y++) + { + for (unsigned int x = 0; x < 4; x++) + { + unsigned int xcoord = (1 + 2 * x) * 640 / 8; + unsigned int ycoord = (1 + 2 * y) * 480 / 8; + + color = ((uint32_t *)((uint8_t *)surface_desc.lpSurface + ycoord * surface_desc.lPitch))[xcoord]; + color &= 0xffffff; + + if (x < 2 && y < 2) + ok(compare_color(color, 0x008400, 1), + "Got color %#x at (%u, %u).\n", color, xcoord, ycoord); + else + ok(compare_color(color, tests[i].rgb_full, 1) + || compare_color(color, tests[i].rgb_reduced, 1), + "Got color %#x at (%u, %u), expected %#x.\n", color, xcoord, ycoord, tests[i].rgb_full); + } + } + hr = IDirectDrawSurface_Unlock(rgb_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + winetest_pop_context(); + } + + IDirectDrawSurface_Release(yuv_surface); + winetest_pop_context(); + } + + IDirectDrawSurface_Release(rgb_surface); + refcount = IDirectDraw_Release(ddraw); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw1) { DDDEVICEIDENTIFIER identifier; @@ -16057,4 +16241,5 @@ START_TEST(ddraw1) test_pinned_sysmem(); test_multiple_devices(); test_sysmem_x_channel(); + test_yuv_blit(); } diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index 91565a04dcd..0f11b27c130 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -21,6 +21,7 @@ #include <math.h>
#define COBJMACROS +#include <stdint.h> #include "wine/test.h" #include <limits.h> #include <math.h> @@ -16967,6 +16968,189 @@ static void test_sysmem_x_channel(void) DestroyWindow(window); }
+static void test_yuv_blit(void) +{ + DDSURFACEDESC surface_desc = {sizeof(surface_desc)}; + IDirectDrawSurface *rgb_surface, *yuv_surface; + const unsigned int width = 32, height = 32; + uint8_t *buf, *chroma_buf, *u_buf, *v_buf; + DDBLTFX fx = {.dwSize = sizeof(fx)}; + unsigned int color, refcount; + IDirectDraw2 *ddraw; + HWND window; + HRESULT hr; + + static const struct + { + uint8_t y, u, v; + uint32_t rgb_full, rgb_reduced; + } + tests[] = + { + {0x10, 0x80, 0x80, 0x000000, 0x101010}, + {0xeb, 0x80, 0x80, 0xffffff, 0xebebeb}, + {0x51, 0x5a, 0xf0, 0xff0000, 0xee0e0e}, + {0x91, 0x36, 0x22, 0x00ff01, 0x0dee0e}, + {0x29, 0xf0, 0x6e, 0x0000ff, 0x100fef}, + {0x7e, 0x80, 0x80, 0x808080, 0x7e7e7e}, + {0x00, 0x80, 0x80, 0x000000, 0x000000}, + {0xff, 0x80, 0x80, 0xffffff, 0xffffff}, + {0x00, 0x00, 0x00, 0x008800, 0x008800}, + {0xff, 0x00, 0x00, 0x4aff14, 0x4cff1c}, + {0x00, 0xff, 0x00, 0x0024ee, 0x0030e1}, + {0x00, 0x00, 0xff, 0xb80000, 0xb20000}, + {0xff, 0xff, 0x00, 0x4affff, 0x4cffff}, + {0xff, 0x00, 0xff, 0xffe114, 0xffd01c}, + {0x00, 0xff, 0xff, 0xb800ee, 0xb200e1}, + {0xff, 0xff, 0xff, 0xff7dff, 0xff78ff}, + }; + + static const struct + { + DWORD fourcc; + const char *str; + } + formats[] = + { + {MAKEFOURCC('U','Y','V','Y'), "UYVY"}, + {MAKEFOURCC('Y','U','Y','2'), "YUY2"}, + {MAKEFOURCC('Y','V','1','2'), "YV12"}, + {MAKEFOURCC('N','V','1','2'), "NV12"}, + }; + + window = create_window(); + ddraw = create_ddraw(); + hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + surface_desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 640; + surface_desc.dwHeight = 480; + init_format_b8g8r8x8(&surface_desc.ddpfPixelFormat); + + hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &rgb_surface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + for (unsigned int fmt = 0; fmt < ARRAY_SIZE(formats); fmt++) + { + DWORD format = formats[fmt].fourcc; + + winetest_push_context("format %s", formats[fmt].str); + + surface_desc.dwWidth = width; + surface_desc.dwHeight = height; + surface_desc.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + surface_desc.ddpfPixelFormat.dwFourCC = format; + hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &yuv_surface, NULL); + if (hr != S_OK) + { + skip("Failed to create surface, hr %#lx.\n", hr); + winetest_pop_context(); + continue; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("value (%#x,%#x,%#x)", tests[i].y, tests[i].u, tests[i].v); + + hr = IDirectDrawSurface_Lock(yuv_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + buf = surface_desc.lpSurface; + chroma_buf = buf + surface_desc.lPitch * height; + if (format == MAKEFOURCC('Y','V','1','2')) + { + v_buf = chroma_buf; + u_buf = chroma_buf + height / 2 * surface_desc.lPitch / 2; + } + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x += 2) + { + uint8_t Y = tests[i].y, U = tests[i].u, V = tests[i].v; + if (x < width / 2 && y < height / 2) + Y = U = V = 0x40; + + if (format == MAKEFOURCC('U','Y','V','Y')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = U; + buf[y * surface_desc.lPitch + 2 * x + 1] = Y; + buf[y * surface_desc.lPitch + 2 * x + 2] = V; + buf[y * surface_desc.lPitch + 2 * x + 3] = Y; + } + else if (format == MAKEFOURCC('Y','U','Y','2')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = Y; + buf[y * surface_desc.lPitch + 2 * x + 1] = U; + buf[y * surface_desc.lPitch + 2 * x + 2] = Y; + buf[y * surface_desc.lPitch + 2 * x + 3] = V; + } + else if (format == MAKEFOURCC('Y','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + u_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = U; + v_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = V; + } + else if (format == MAKEFOURCC('N','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 0] = U; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 1] = V; + } + } + } + hr = IDirectDrawSurface_Unlock(yuv_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface_Blt(rgb_surface, NULL, yuv_surface, NULL, DDBLT_WAIT, NULL); + if (hr != D3D_OK) + { + winetest_pop_context(); + skip("Failed to blit, hr %#lx.\n", hr); + break; + } + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface_Lock(rgb_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + for (unsigned int y = 0; y < 4; y++) + { + for (unsigned int x = 0; x < 4; x++) + { + unsigned int xcoord = (1 + 2 * x) * 640 / 8; + unsigned int ycoord = (1 + 2 * y) * 480 / 8; + + color = ((uint32_t *)((uint8_t *)surface_desc.lpSurface + ycoord * surface_desc.lPitch))[xcoord]; + color &= 0xffffff; + + if (x < 2 && y < 2) + ok(compare_color(color, 0x008400, 1), + "Got color %#x at (%u, %u).\n", color, xcoord, ycoord); + else + ok(compare_color(color, tests[i].rgb_full, 1) + || compare_color(color, tests[i].rgb_reduced, 1), + "Got color %#x at (%u, %u), expected %#x.\n", color, xcoord, ycoord, tests[i].rgb_full); + } + } + hr = IDirectDrawSurface_Unlock(rgb_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + winetest_pop_context(); + } + + IDirectDrawSurface_Release(yuv_surface); + winetest_pop_context(); + } + + IDirectDrawSurface_Release(rgb_surface); + refcount = IDirectDraw2_Release(ddraw); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw2) { DDDEVICEIDENTIFIER identifier; @@ -17095,4 +17279,5 @@ START_TEST(ddraw2) test_multiple_devices(); test_d3d_state_reset(); test_sysmem_x_channel(); + test_yuv_blit(); } diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index d55c9b599c8..4571a5426bb 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -20,6 +20,7 @@
#define COBJMACROS
+#include <stdint.h> #include "wine/test.h" #include <limits.h> #include <math.h> @@ -20040,6 +20041,189 @@ static void test_sysmem_x_channel(void) DestroyWindow(window); }
+static void test_yuv_blit(void) +{ + DDSURFACEDESC2 surface_desc = {sizeof(surface_desc)}; + IDirectDrawSurface4 *rgb_surface, *yuv_surface; + const unsigned int width = 32, height = 32; + uint8_t *buf, *chroma_buf, *u_buf, *v_buf; + DDBLTFX fx = {.dwSize = sizeof(fx)}; + unsigned int color, refcount; + IDirectDraw4 *ddraw; + HWND window; + HRESULT hr; + + static const struct + { + uint8_t y, u, v; + uint32_t rgb_full, rgb_reduced; + } + tests[] = + { + {0x10, 0x80, 0x80, 0x000000, 0x101010}, + {0xeb, 0x80, 0x80, 0xffffff, 0xebebeb}, + {0x51, 0x5a, 0xf0, 0xff0000, 0xee0e0e}, + {0x91, 0x36, 0x22, 0x00ff01, 0x0dee0e}, + {0x29, 0xf0, 0x6e, 0x0000ff, 0x100fef}, + {0x7e, 0x80, 0x80, 0x808080, 0x7e7e7e}, + {0x00, 0x80, 0x80, 0x000000, 0x000000}, + {0xff, 0x80, 0x80, 0xffffff, 0xffffff}, + {0x00, 0x00, 0x00, 0x008800, 0x008800}, + {0xff, 0x00, 0x00, 0x4aff14, 0x4cff1c}, + {0x00, 0xff, 0x00, 0x0024ee, 0x0030e1}, + {0x00, 0x00, 0xff, 0xb80000, 0xb20000}, + {0xff, 0xff, 0x00, 0x4affff, 0x4cffff}, + {0xff, 0x00, 0xff, 0xffe114, 0xffd01c}, + {0x00, 0xff, 0xff, 0xb800ee, 0xb200e1}, + {0xff, 0xff, 0xff, 0xff7dff, 0xff78ff}, + }; + + static const struct + { + DWORD fourcc; + const char *str; + } + formats[] = + { + {MAKEFOURCC('U','Y','V','Y'), "UYVY"}, + {MAKEFOURCC('Y','U','Y','2'), "YUY2"}, + {MAKEFOURCC('Y','V','1','2'), "YV12"}, + {MAKEFOURCC('N','V','1','2'), "NV12"}, + }; + + window = create_window(); + ddraw = create_ddraw(); + hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + surface_desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 640; + surface_desc.dwHeight = 480; + init_format_b8g8r8x8(&surface_desc.ddpfPixelFormat); + + hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &rgb_surface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + for (unsigned int fmt = 0; fmt < ARRAY_SIZE(formats); fmt++) + { + DWORD format = formats[fmt].fourcc; + + winetest_push_context("format %s", formats[fmt].str); + + surface_desc.dwWidth = width; + surface_desc.dwHeight = height; + surface_desc.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + surface_desc.ddpfPixelFormat.dwFourCC = format; + hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &yuv_surface, NULL); + if (hr != S_OK) + { + skip("Failed to create surface, hr %#lx.\n", hr); + winetest_pop_context(); + continue; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("value (%#x,%#x,%#x)", tests[i].y, tests[i].u, tests[i].v); + + hr = IDirectDrawSurface4_Lock(yuv_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + buf = surface_desc.lpSurface; + chroma_buf = buf + surface_desc.lPitch * height; + if (format == MAKEFOURCC('Y','V','1','2')) + { + v_buf = chroma_buf; + u_buf = chroma_buf + height / 2 * surface_desc.lPitch / 2; + } + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x += 2) + { + uint8_t Y = tests[i].y, U = tests[i].u, V = tests[i].v; + if (x < width / 2 && y < height / 2) + Y = U = V = 0x40; + + if (format == MAKEFOURCC('U','Y','V','Y')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = U; + buf[y * surface_desc.lPitch + 2 * x + 1] = Y; + buf[y * surface_desc.lPitch + 2 * x + 2] = V; + buf[y * surface_desc.lPitch + 2 * x + 3] = Y; + } + else if (format == MAKEFOURCC('Y','U','Y','2')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = Y; + buf[y * surface_desc.lPitch + 2 * x + 1] = U; + buf[y * surface_desc.lPitch + 2 * x + 2] = Y; + buf[y * surface_desc.lPitch + 2 * x + 3] = V; + } + else if (format == MAKEFOURCC('Y','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + u_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = U; + v_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = V; + } + else if (format == MAKEFOURCC('N','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 0] = U; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 1] = V; + } + } + } + hr = IDirectDrawSurface4_Unlock(yuv_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface4_Blt(rgb_surface, NULL, yuv_surface, NULL, DDBLT_WAIT, NULL); + if (hr != D3D_OK) + { + winetest_pop_context(); + skip("Failed to blit, hr %#lx.\n", hr); + break; + } + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface4_Lock(rgb_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + for (unsigned int y = 0; y < 4; y++) + { + for (unsigned int x = 0; x < 4; x++) + { + unsigned int xcoord = (1 + 2 * x) * 640 / 8; + unsigned int ycoord = (1 + 2 * y) * 480 / 8; + + color = ((uint32_t *)((uint8_t *)surface_desc.lpSurface + ycoord * surface_desc.lPitch))[xcoord]; + color &= 0xffffff; + + if (x < 2 && y < 2) + ok(compare_color(color, 0x008400, 1), + "Got color %#x at (%u, %u).\n", color, xcoord, ycoord); + else + ok(compare_color(color, tests[i].rgb_full, 1) + || compare_color(color, tests[i].rgb_reduced, 1), + "Got color %#x at (%u, %u), expected %#x.\n", color, xcoord, ycoord, tests[i].rgb_full); + } + } + hr = IDirectDrawSurface4_Unlock(rgb_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + winetest_pop_context(); + } + + IDirectDrawSurface4_Release(yuv_surface); + winetest_pop_context(); + } + + IDirectDrawSurface4_Release(rgb_surface); + refcount = IDirectDraw4_Release(ddraw); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw4) { DDDEVICEIDENTIFIER identifier; @@ -20185,4 +20369,5 @@ START_TEST(ddraw4) test_vb_desc(); test_d3d_state_reset(); test_sysmem_x_channel(); + test_yuv_blit(); } diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index e7d66278ad7..d4980250a8c 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -21,6 +21,7 @@ #define COBJMACROS
#include <stdbool.h> +#include <stdint.h> #include "wine/test.h" #include <limits.h> #include <math.h> @@ -20475,6 +20476,189 @@ static void test_sysmem_x_channel(void) DestroyWindow(window); }
+static void test_yuv_blit(void) +{ + DDSURFACEDESC2 surface_desc = {sizeof(surface_desc)}; + IDirectDrawSurface7 *rgb_surface, *yuv_surface; + const unsigned int width = 32, height = 32; + uint8_t *buf, *chroma_buf, *u_buf, *v_buf; + DDBLTFX fx = {.dwSize = sizeof(fx)}; + unsigned int color, refcount; + IDirectDraw7 *ddraw; + HWND window; + HRESULT hr; + + static const struct + { + uint8_t y, u, v; + uint32_t rgb_full, rgb_reduced; + } + tests[] = + { + {0x10, 0x80, 0x80, 0x000000, 0x101010}, + {0xeb, 0x80, 0x80, 0xffffff, 0xebebeb}, + {0x51, 0x5a, 0xf0, 0xff0000, 0xee0e0e}, + {0x91, 0x36, 0x22, 0x00ff01, 0x0dee0e}, + {0x29, 0xf0, 0x6e, 0x0000ff, 0x100fef}, + {0x7e, 0x80, 0x80, 0x808080, 0x7e7e7e}, + {0x00, 0x80, 0x80, 0x000000, 0x000000}, + {0xff, 0x80, 0x80, 0xffffff, 0xffffff}, + {0x00, 0x00, 0x00, 0x008800, 0x008800}, + {0xff, 0x00, 0x00, 0x4aff14, 0x4cff1c}, + {0x00, 0xff, 0x00, 0x0024ee, 0x0030e1}, + {0x00, 0x00, 0xff, 0xb80000, 0xb20000}, + {0xff, 0xff, 0x00, 0x4affff, 0x4cffff}, + {0xff, 0x00, 0xff, 0xffe114, 0xffd01c}, + {0x00, 0xff, 0xff, 0xb800ee, 0xb200e1}, + {0xff, 0xff, 0xff, 0xff7dff, 0xff78ff}, + }; + + static const struct + { + DWORD fourcc; + const char *str; + } + formats[] = + { + {MAKEFOURCC('U','Y','V','Y'), "UYVY"}, + {MAKEFOURCC('Y','U','Y','2'), "YUY2"}, + {MAKEFOURCC('Y','V','1','2'), "YV12"}, + {MAKEFOURCC('N','V','1','2'), "NV12"}, + }; + + window = create_window(); + ddraw = create_ddraw(); + hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + surface_desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 640; + surface_desc.dwHeight = 480; + init_format_b8g8r8x8(&surface_desc.ddpfPixelFormat); + + hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &rgb_surface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + for (unsigned int fmt = 0; fmt < ARRAY_SIZE(formats); fmt++) + { + DWORD format = formats[fmt].fourcc; + + winetest_push_context("format %s", formats[fmt].str); + + surface_desc.dwWidth = width; + surface_desc.dwHeight = height; + surface_desc.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + surface_desc.ddpfPixelFormat.dwFourCC = format; + hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &yuv_surface, NULL); + if (hr != S_OK) + { + skip("Failed to create surface, hr %#lx.\n", hr); + winetest_pop_context(); + continue; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("value (%#x,%#x,%#x)", tests[i].y, tests[i].u, tests[i].v); + + hr = IDirectDrawSurface7_Lock(yuv_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + buf = surface_desc.lpSurface; + chroma_buf = buf + surface_desc.lPitch * height; + if (format == MAKEFOURCC('Y','V','1','2')) + { + v_buf = chroma_buf; + u_buf = chroma_buf + height / 2 * surface_desc.lPitch / 2; + } + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x += 2) + { + uint8_t Y = tests[i].y, U = tests[i].u, V = tests[i].v; + if (x < width / 2 && y < height / 2) + Y = U = V = 0x40; + + if (format == MAKEFOURCC('U','Y','V','Y')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = U; + buf[y * surface_desc.lPitch + 2 * x + 1] = Y; + buf[y * surface_desc.lPitch + 2 * x + 2] = V; + buf[y * surface_desc.lPitch + 2 * x + 3] = Y; + } + else if (format == MAKEFOURCC('Y','U','Y','2')) + { + buf[y * surface_desc.lPitch + 2 * x + 0] = Y; + buf[y * surface_desc.lPitch + 2 * x + 1] = U; + buf[y * surface_desc.lPitch + 2 * x + 2] = Y; + buf[y * surface_desc.lPitch + 2 * x + 3] = V; + } + else if (format == MAKEFOURCC('Y','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + u_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = U; + v_buf[(y / 2) * (surface_desc.lPitch / 2) + (x / 2)] = V; + } + else if (format == MAKEFOURCC('N','V','1','2')) + { + buf[y * surface_desc.lPitch + x + 0] = Y; + buf[y * surface_desc.lPitch + x + 1] = Y; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 0] = U; + chroma_buf[(y / 2) * surface_desc.lPitch + 2 * (x / 2) + 1] = V; + } + } + } + hr = IDirectDrawSurface7_Unlock(yuv_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface7_Blt(rgb_surface, NULL, yuv_surface, NULL, DDBLT_WAIT, NULL); + if (hr != D3D_OK) + { + winetest_pop_context(); + skip("Failed to blit, hr %#lx.\n", hr); + break; + } + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + hr = IDirectDrawSurface7_Lock(rgb_surface, NULL, &surface_desc, DDLOCK_WAIT, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + for (unsigned int y = 0; y < 4; y++) + { + for (unsigned int x = 0; x < 4; x++) + { + unsigned int xcoord = (1 + 2 * x) * 640 / 8; + unsigned int ycoord = (1 + 2 * y) * 480 / 8; + + color = ((uint32_t *)((uint8_t *)surface_desc.lpSurface + ycoord * surface_desc.lPitch))[xcoord]; + color &= 0xffffff; + + if (x < 2 && y < 2) + ok(compare_color(color, 0x008400, 1), + "Got color %#x at (%u, %u).\n", color, xcoord, ycoord); + else + ok(compare_color(color, tests[i].rgb_full, 1) + || compare_color(color, tests[i].rgb_reduced, 1), + "Got color %#x at (%u, %u), expected %#x.\n", color, xcoord, ycoord, tests[i].rgb_full); + } + } + hr = IDirectDrawSurface7_Unlock(rgb_surface, NULL); + ok(hr == D3D_OK, "Got hr %#lx.\n", hr); + + winetest_pop_context(); + } + + IDirectDrawSurface7_Release(yuv_surface); + winetest_pop_context(); + } + + IDirectDrawSurface7_Release(rgb_surface); + refcount = IDirectDraw7_Release(ddraw); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw7) { DDDEVICEIDENTIFIER2 identifier; @@ -20654,4 +20838,5 @@ START_TEST(ddraw7) test_vb_desc(); test_d3d_state_reset(); test_sysmem_x_channel(); + test_yuv_blit(); }
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/wined3d/glsl_shader.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c index 000bf0d3995..b2e89704933 100644 --- a/dlls/wined3d/glsl_shader.c +++ b/dlls/wined3d/glsl_shader.c @@ -12682,7 +12682,14 @@ static void glsl_blitter_generate_yuv_shader(struct wined3d_string_buffer *buffe { enum complex_fixup complex_fixup = get_complex_fixup(args->fixup);
- shader_addline(buffer, "const vec4 yuv_coef = vec4(1.403, -0.344, -0.714, 1.770);\n"); + /* Drivers disagree on whether the Y'CbCr components should be interpreted + * as being in the [0,255] range (NVidia, but this is configurable) or the + * reduced [16,235]/[16,240] range (AMD, WARP). + * The game Locoland plays back video inside a black frame, the background + * of which only matches the frame if the reduced range is used, so we side + * with the reduced range here. */ + + shader_addline(buffer, "const vec4 yuv_coef = vec4(1.596, -0.392, -0.813, 2.017);\n"); shader_addline(buffer, "float luminance;\n"); shader_addline(buffer, "vec2 texcoord;\n"); shader_addline(buffer, "vec2 chroma;\n"); @@ -12724,6 +12731,7 @@ static void glsl_blitter_generate_yuv_shader(struct wined3d_string_buffer *buffe * http://www.fourcc.org/fccyvrgb.php. Note that the chroma * ranges from -0.5 to 0.5. */ shader_addline(buffer, "\n chroma.xy -= 0.5;\n"); + shader_addline(buffer, " luminance = (luminance - 0.063) * 1.164;\n");
shader_addline(buffer, " %s.x = luminance + chroma.x * yuv_coef.x;\n", output); shader_addline(buffer, " %s.y = luminance + chroma.y * yuv_coef.y + chroma.x * yuv_coef.z;\n", output);