This is the first part of my partial presentation support patchset. Only the tests and minor implementation changes.
Actual implementation will follow in multiple parts: minor refactoring of swapchain backbuffers, then actual implementation for dirty rectangles (with limitations), then limitations will be removed step-by-step, at the very end the scroll will be implemented.
I had to mark many new tests as "flaky" (not "todo"). They are not actually flaky, they are perfectly stable on both Windows 8 RTM and one of the latest Windows 11 Canary builds. But since they check multiple pixels and some of them match, this causes the "failures". I saw no better solution which didn't involve new APIs in `wine/test.h`. Their number will drop in the next MRs to 0.
From: Andrew Boyarshin andrew.boyarshin@gmail.com
--- dlls/dxgi/tests/Makefile.in | 2 +- dlls/dxgi/tests/dxgi.c | 753 ++++++++++++++++++++++++++++++++++++ 2 files changed, 754 insertions(+), 1 deletion(-)
diff --git a/dlls/dxgi/tests/Makefile.in b/dlls/dxgi/tests/Makefile.in index add3803ca40..4e312b27d73 100644 --- a/dlls/dxgi/tests/Makefile.in +++ b/dlls/dxgi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = dxgi.dll -IMPORTS = d3d10_1 dxgi user32 +IMPORTS = d3d10_1 dxgi gdi32 user32 EXTRADLLFLAGS = -Wl,--subsystem,console:5.2
SOURCES = \ diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 2e3131a7536..c0d7c4bca79 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -8531,6 +8531,757 @@ static void test_subresource_surface(void) ok(!refcount, "Device has %lu references left.\n", refcount); }
+struct resource_readback +{ + ID3D11Resource *resource; + D3D11_MAPPED_SUBRESOURCE map_desc; + ID3D11DeviceContext *immediate_context; + unsigned int width, height, depth, sub_resource_idx; +}; + +static void init_resource_readback(ID3D11Resource *resource, ID3D11Resource *readback_resource, + unsigned int width, unsigned int height, unsigned int depth, unsigned int sub_resource_idx, + ID3D11Device *device, struct resource_readback *rb) +{ + HRESULT hr; + + rb->resource = readback_resource; + rb->width = width; + rb->height = height; + rb->depth = depth; + rb->sub_resource_idx = sub_resource_idx; + + ID3D11Device_GetImmediateContext(device, &rb->immediate_context); + + ID3D11DeviceContext_CopyResource(rb->immediate_context, rb->resource, resource); + if (FAILED(hr = ID3D11DeviceContext_Map(rb->immediate_context, + rb->resource, sub_resource_idx, D3D11_MAP_READ, 0, &rb->map_desc))) + { + trace("Failed to map resource, hr %#lx.\n", hr); + ID3D11Resource_Release(rb->resource); + rb->resource = NULL; + ID3D11DeviceContext_Release(rb->immediate_context); + rb->immediate_context = NULL; + } +} + +static void get_texture_readback(ID3D11Texture2D *texture, unsigned int sub_resource_idx, + struct resource_readback *rb) +{ + D3D11_TEXTURE2D_DESC texture_desc; + ID3D11Resource *rb_texture; + unsigned int miplevel; + ID3D11Device *device; + HRESULT hr; + + memset(rb, 0, sizeof(*rb)); + + ID3D11Texture2D_GetDevice(texture, &device); + + ID3D11Texture2D_GetDesc(texture, &texture_desc); + texture_desc.Usage = D3D11_USAGE_STAGING; + texture_desc.BindFlags = 0; + texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + texture_desc.MiscFlags = 0; + if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &texture_desc, NULL, (ID3D11Texture2D **)&rb_texture))) + { + trace("Failed to create texture, hr %#lx.\n", hr); + ID3D11Device_Release(device); + return; + } + + miplevel = sub_resource_idx % texture_desc.MipLevels; + init_resource_readback((ID3D11Resource *)texture, rb_texture, + max(1, texture_desc.Width >> miplevel), + max(1, texture_desc.Height >> miplevel), + 1, sub_resource_idx, device, rb); + + ID3D11Device_Release(device); +} + +static void *get_readback_data(struct resource_readback *rb, + unsigned int x, unsigned int y, unsigned int z, unsigned byte_width) +{ + return (BYTE *)rb->map_desc.pData + z * rb->map_desc.DepthPitch + y * rb->map_desc.RowPitch + x * byte_width; +} + +static DWORD get_readback_u32(struct resource_readback *rb, unsigned int x, unsigned int y, unsigned int z) +{ + return *(DWORD *)get_readback_data(rb, x, y, z, sizeof(DWORD)); +} + +static DWORD get_readback_color(struct resource_readback *rb, unsigned int x, unsigned int y, unsigned int z) +{ + return get_readback_u32(rb, x, y, z); +} + +static void release_resource_readback(struct resource_readback *rb) +{ + ID3D11DeviceContext_Unmap(rb->immediate_context, rb->resource, rb->sub_resource_idx); + ID3D11Resource_Release(rb->resource); + ID3D11DeviceContext_Release(rb->immediate_context); +} + +static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) +{ + unsigned int diff = x > y ? x - y : y - x; + + return diff <= max_diff; +} + +static BOOL compare_color(DWORD c1, DWORD c2, BYTE max_diff) +{ + return compare_uint(c1 & 0xff, c2 & 0xff, max_diff) + && compare_uint((c1 >> 8) & 0xff, (c2 >> 8) & 0xff, max_diff) + && compare_uint((c1 >> 16) & 0xff, (c2 >> 16) & 0xff, max_diff) + && compare_uint((c1 >> 24) & 0xff, (c2 >> 24) & 0xff, max_diff); +} + +#define check_colors(a, b, c, d, e, f, g) check_colors_(__FILE__, __LINE__, a, b, c, d, e, f, g) +static void check_colors_(const char* file, int line, ID3D11Texture2D *texture, unsigned int x, unsigned int y, DWORD expected1, DWORD expected2, DWORD expected3, DWORD expected4) +{ + struct resource_readback rb; + DWORD actual1, actual2, actual3, actual4; + + get_texture_readback(texture, 0, &rb); + actual1 = get_readback_color(&rb, x, y, 0); + actual2 = get_readback_color(&rb, x + 1, y, 0); + actual3 = get_readback_color(&rb, x, y + 1, 0); + actual4 = get_readback_color(&rb, x + 1, y + 1, 0); + release_resource_readback(&rb); + + ok_(file, line)(compare_color(actual1, expected1, 1), "(x=%u, y=%u) expected color = 0x%08lx, actual color = 0x%08lx.\n", x, y, expected1, actual1); + ok_(file, line)(compare_color(actual2, expected2, 1), "(x=%u, y=%u) expected color = 0x%08lx, actual color = 0x%08lx.\n", x + 1, y, expected2, actual2); + ok_(file, line)(compare_color(actual3, expected3, 1), "(x=%u, y=%u) expected color = 0x%08lx, actual color = 0x%08lx.\n", x, y + 1, expected3, actual3); + ok_(file, line)(compare_color(actual4, expected4, 1), "(x=%u, y=%u) expected color = 0x%08lx, actual color = 0x%08lx.\n", x + 1, y + 1, expected4, actual4); +} + +#define check_color(a, b, c, d) check_color_(__FILE__, __LINE__, a, b, c, d) +static void check_color_(const char* file, int line, ID3D11Texture2D *texture, unsigned int x, unsigned int y, DWORD expected) +{ + check_colors_(file, line, texture, x, y, expected, expected, expected, expected); +} + +struct color_rect +{ + RECT rect; + DWORD color; +}; + +#define check_texture(a, b, c, d) check_texture_(__FILE__, __LINE__, a, b, c, d) +static void check_texture_(const char *file, int line, ID3D11Texture2D *texture, DWORD background, + UINT rect_count, struct color_rect *rects) +{ + struct resource_readback rb; + HRGN background_region; + DWORD background_region_data_size; + RGNDATA *background_region_data = NULL; + + get_texture_readback(texture, 0, &rb); + + background_region = CreateRectRgn(0, 0, rb.width, rb.height); + for (UINT i = 0; i < rect_count; ++i) + { + HRGN current_rect_region; + struct color_rect rect = rects[i]; + if (IsRectEmpty(&rect.rect)) + continue; + + current_rect_region = CreateRectRgnIndirect(&rect.rect); + CombineRgn(background_region, background_region, current_rect_region, RGN_DIFF); + DeleteObject(current_rect_region); + for (unsigned int x = rect.rect.left; x < (DWORD)rect.rect.right; ++x) + { + for (unsigned int y = rect.rect.top; y < (DWORD)rect.rect.bottom; ++y) + { + DWORD actual = get_readback_color(&rb, x, y, 0); + DWORD expected = rect.color; + if (!compare_color(actual, expected, 1)) + { + ok_(file, line)(FALSE, "(x=%u, y=%u) rectangle %u expected color = 0x%08lx, actual color = 0x%08lx.\n", i, x, y, expected, actual); + goto skip_rect; + } + } + } + skip_rect:; + } + + background_region_data_size = GetRegionData(background_region, 0, NULL); + background_region_data = (RGNDATA *)malloc(background_region_data_size); + if (!background_region_data) + { + ok_(file, line)(FALSE, "Failed to allocate %lu bytes for background region data\n", background_region_data_size); + goto cleanup; + } + + GetRegionData(background_region, background_region_data_size, background_region_data); + + for (UINT i = 0; i < background_region_data->rdh.nCount; ++i) + { + RECT rect = ((const RECT*)background_region_data->Buffer)[i]; + for (unsigned int x = rect.left; x < (DWORD)rect.right; ++x) + { + for (unsigned int y = rect.top; y < (DWORD)rect.bottom; ++y) + { + DWORD actual = get_readback_color(&rb, x, y, 0); + DWORD expected = background; + if (!compare_color(actual, expected, 1)) + { + ok_(file, line)(FALSE, "(x=%u, y=%u) background expected color = 0x%08lx, actual color = 0x%08lx.\n", x, y, expected, actual); + goto cleanup; + } + } + } + } + + ok_(file, line)(TRUE, "Texture matches\n"); + +cleanup: + free(background_region_data); + DeleteObject(background_region); + release_resource_readback(&rb); +} + +#define check_full(a, b) check_full_(__FILE__, __LINE__, a, b) +static void check_full_(const char *file, int line, ID3D11Texture2D *texture, DWORD expected) +{ + check_texture_(file, line, texture, expected, 0, NULL); +} + +static DWORD RGB_DWORD(BYTE r, BYTE g, BYTE b) +{ + return r | (WORD)g << 8 | (DWORD)b << 16 | 0x80000000u; +} + +struct test_partial_present_common_parameters +{ + DXGI_SWAP_EFFECT swap_effect; + DXGI_SCALING scaling; + UINT buffer_count; + UINT swapchain_width; + UINT swapchain_height; + UINT window_width; + UINT window_height; +}; + +static BOOL prepare_partial_present(struct test_partial_present_common_parameters *parameters, + ID3D11Device **device, + HWND *window, + IDXGISwapChain1 **swapchain, + ID3D11DeviceContext **context) +{ + static const D3D_FEATURE_LEVEL feature_level[] = + { + D3D_FEATURE_LEVEL_11_0, + }; + unsigned int feature_level_count = ARRAY_SIZE(feature_level); + DXGI_SWAP_CHAIN_DESC1 dxgi_desc; + IDXGIDevice *dxgi_device; + IDXGIAdapter *adapter; + IDXGIFactory2 *factory; + RECT rect; + HRESULT hr; + + if (!pD3D11CreateDevice) + { + win_skip("No D3D11 support, skipping partial presentation tests.\n"); + return FALSE; + } + + hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, feature_level, feature_level_count, + D3D11_SDK_VERSION, device, NULL, NULL); + if (FAILED(hr)) + hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, feature_level, feature_level_count, + D3D11_SDK_VERSION, device, NULL, NULL); + if (FAILED(hr)) + hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, 0, feature_level, feature_level_count, + D3D11_SDK_VERSION, device, NULL, NULL); + + if (FAILED(hr) || !*device) + { + skip("Failed to create D3D11 device, skipping partial presentation tests.\n"); + return FALSE; + } + + hr = ID3D11Device_QueryInterface(*device, &IID_IDXGIDevice, (void **)&dxgi_device); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDXGIDevice_GetAdapter(dxgi_device, &adapter); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IDXGIDevice_Release(dxgi_device); + hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory2, (void **)&factory); + ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Got unexpected hr %#lx.\n", hr); + IDXGIAdapter_Release(adapter); + if (!factory) + { + win_skip("No IDXGIFactory2, skipping partial presentation tests.\n"); + return FALSE; + } + + SetRect(&rect, 0, 0, parameters->window_width, parameters->window_height); + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE); + *window = CreateWindowA("static", "dxgi_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, NULL, NULL); + + dxgi_desc.Width = parameters->swapchain_width; + dxgi_desc.Height = parameters->swapchain_height; + dxgi_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + dxgi_desc.Stereo = FALSE; + dxgi_desc.SampleDesc.Count = 1; + dxgi_desc.SampleDesc.Quality = 0; + dxgi_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + dxgi_desc.BufferCount = parameters->buffer_count; + dxgi_desc.Scaling = parameters->scaling; + dxgi_desc.SwapEffect = parameters->swap_effect; + dxgi_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + dxgi_desc.Flags = 0; + + hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown *)*device, *window, &dxgi_desc, NULL, NULL, swapchain); + ok(hr == S_OK, "Failed to create swapchain, hr %#lx.\n", hr); + IDXGIFactory2_Release(factory); + + ID3D11Device_GetImmediateContext(*device, context); + return TRUE; +} + +struct test_partial_present_grid_backbuffer_info +{ + DWORD background; + struct color_rect rectangles[1]; +}; + +struct test_partial_present_grid_parameters +{ + struct test_partial_present_common_parameters c; + UINT cell_size; + UINT rows; + UINT columns; +}; + +static void test_partial_present_grid_impl(struct test_partial_present_grid_parameters *parameters) +{ + ID3D11Texture2D *backbuffer_0, *backbuffer_last; + ID3D11RenderTargetView *backbuffer_0_rtv; + ID3D11DeviceContext *context; + IDXGISwapChain1 *swapchain; + ID3D11Device *device; + ULONG refcount; + HWND window; + HRESULT hr; + DXGI_PRESENT_PARAMETERS present_params; + RECT dirty_rect; + const UINT buffer_count = parameters->c.buffer_count; + const UINT cell_size = parameters->cell_size; + const UINT rows = parameters->rows; + const UINT columns = parameters->columns; + ID3D11Texture2D **backbuffers = calloc(buffer_count, sizeof(ID3D11Texture2D*)); + const UINT backbuffer_info_size = FIELD_OFFSET(struct test_partial_present_grid_backbuffer_info, rectangles[buffer_count]); + char *backbuffer_info_memory = calloc(buffer_count, backbuffer_info_size); + struct test_partial_present_grid_backbuffer_info **backbuffer_info = calloc(buffer_count + 1, sizeof(*backbuffer_info)); + const DWORD background_dword = RGB_DWORD(1, 1, 1); + const FLOAT background_float[] = { 1.0f / 255, 1.0f / 255, 1.0f / 255, 0.5f }; + +#define PALETTE_SIZE 11 + + const BYTE PALETTE[PALETTE_SIZE][3] = { + {47, 45, 48}, + {90, 71, 67}, + {111, 108, 112}, + {43, 46, 67}, + {75, 109, 65}, + {119, 32, 46}, + {181, 186, 182}, + {207, 187, 123}, + {213, 98, 49}, + {0, 116, 168}, + {51, 190, 204} + }; + + DWORD PALETTE_RGBA_DWORD[PALETTE_SIZE]; + + FLOAT PALETTE_RGBA_FLOAT[PALETTE_SIZE][4]; + + for (UINT i = 0; i < PALETTE_SIZE; ++i) + { + BYTE red = PALETTE[i][0]; + BYTE green = PALETTE[i][1]; + BYTE blue = PALETTE[i][2]; + PALETTE_RGBA_DWORD[i] = RGB_DWORD(red, green, blue); + PALETTE_RGBA_FLOAT[i][0] = red * 1.0f / 255; + PALETTE_RGBA_FLOAT[i][1] = green * 1.0f / 255; + PALETTE_RGBA_FLOAT[i][2] = blue * 1.0f / 255; + PALETTE_RGBA_FLOAT[i][3] = 0.5f; + } + + for (size_t i = 0; i < buffer_count; ++i) + backbuffer_info[i] = (struct test_partial_present_grid_backbuffer_info*)(backbuffer_info_memory + i * backbuffer_info_size); + + if (!prepare_partial_present(¶meters->c, &device, &window, &swapchain, &context)) + return; + + for (UINT i = 0; i < buffer_count; ++i) + { + hr = IDXGISwapChain1_GetBuffer(swapchain, i, &IID_ID3D11Texture2D, (void **)&backbuffers[i]); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + } + + backbuffer_0 = backbuffers[0]; + backbuffer_last = backbuffers[buffer_count - 1]; + + hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)backbuffer_0, NULL, &backbuffer_0_rtv); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + for (UINT i = 0; i < buffer_count; ++i) + check_full(backbuffers[i], 0); + + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer_0_rtv, background_float); + + check_full(backbuffer_0, background_dword); + for (UINT i = 1; i < buffer_count; ++i) + check_full(backbuffers[i], 0); + + present_params.DirtyRectsCount = 0; + present_params.pDirtyRects = NULL; + present_params.pScrollOffset = NULL; + present_params.pScrollRect = NULL; + + IDXGISwapChain1_Present1(swapchain, 0, 0, &present_params); + + for (UINT i = 0; i < buffer_count - 1; ++i) + check_full(backbuffers[i], 0); + check_full(backbuffer_last, background_dword); + + present_params.DirtyRectsCount = 1; + present_params.pDirtyRects = &dirty_rect; + + backbuffer_info[buffer_count - 1]->background = background_dword; + + for (UINT row = 0; row < rows; ++row) + { + for (UINT col = 0; col < columns; ++col) + { + const UINT current_index = row * columns + col; + const UINT palette_index = (row * columns + col) % PALETTE_SIZE; + const FLOAT* color_float = PALETTE_RGBA_FLOAT[palette_index]; + const DWORD color_dword = PALETTE_RGBA_DWORD[palette_index]; + + for (UINT k = 0; k < buffer_count; ++k) + flaky_wine check_texture(backbuffers[k], backbuffer_info[k]->background, buffer_count, backbuffer_info[k]->rectangles); + + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer_0_rtv, color_float); + + dirty_rect.top = row * cell_size; + dirty_rect.bottom = (row + 1) * cell_size; + dirty_rect.left = col * cell_size; + dirty_rect.right = (col + 1) * cell_size; + backbuffer_info[0]->background = current_index + 1 >= buffer_count ? color_dword : background_dword; + for (UINT k = 0; k < buffer_count - 1; ++k) + backbuffer_info[0]->rectangles[k] = backbuffer_info[buffer_count - 1]->rectangles[k + 1]; + backbuffer_info[0]->rectangles[buffer_count - 1].color = color_dword; + backbuffer_info[0]->rectangles[buffer_count - 1].rect = dirty_rect; + + IDXGISwapChain1_Present1(swapchain, 0, 0, &present_params); + + backbuffer_info[buffer_count] = backbuffer_info[0]; + for (UINT k = 0; k < buffer_count; ++k) + backbuffer_info[k] = backbuffer_info[k + 1]; + + for (UINT k = 0; k < buffer_count; ++k) + flaky_wine check_texture(backbuffers[k], backbuffer_info[k]->background, buffer_count, backbuffer_info[k]->rectangles); + } + } + +#undef PALETTE_SIZE + + ID3D11RenderTargetView_Release(backbuffer_0_rtv); + for (UINT i = 0; i < buffer_count; ++i) + ID3D11Texture2D_Release(backbuffers[i]); + IDXGISwapChain1_Release(swapchain); + ID3D11DeviceContext_Release(context); + refcount = ID3D11Device_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); + DestroyWindow(window); + free(backbuffer_info); + free(backbuffer_info_memory); + free(backbuffers); +} + +static void test_partial_present_grid(void) +{ + struct test_partial_present_grid_parameters parameters = { + .c.swap_effect = DXGI_SWAP_EFFECT_SEQUENTIAL, .c.buffer_count = 5, + .c.swapchain_width = 640, .c.swapchain_height = 480, + .c.window_width = 640, .c.window_height = 480, + .cell_size = 160, .rows = 3, .columns = 4, + }; + test_partial_present_grid_impl(¶meters); + parameters.c.swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + parameters.c.window_width = 1280; + test_partial_present_grid_impl(¶meters); +} + +static void test_partial_present_scroll_impl(struct test_partial_present_common_parameters *parameters) +{ + ID3D11Texture2D *backbuffer_0, *backbuffer_1, *backbuffer_2; + ID3D11RenderTargetView *backbuffer_0_rtv; + ID3D11DeviceContext *context; + IDXGISwapChain1 *swapchain; + ID3D11Device *device; + ULONG refcount; + HWND window; + HRESULT hr; + DXGI_PRESENT_PARAMETERS present_params; + + static const float red[] = {1.0f, 0.0f, 0.0f, 0.5f}; + static const float green[] = {0.0f, 1.0f, 0.0f, 0.5f}; + static const float blue[] = {0.0f, 0.0f, 1.0f, 0.5f}; + + const DWORD RED = RGB_DWORD(0xFF, 0, 0); + const DWORD GREEN = RGB_DWORD(0, 0xFF, 0); + const DWORD BLUE = RGB_DWORD(0, 0, 0xFF); + const DWORD BLACK = 0x00000000; + + RECT dirty_rects_green[] = { + {.left = 50, .top = 50, .right = 100, .bottom = 100}, + {.left = 200, .top = 80, .right = 400, .bottom = 300} + }; + + RECT dirty_rects_blue[] = { + {.left = 50, .top = 40, .right = 100, .bottom = 80}, + {.left = 10, .top = 400, .right = 600, .bottom = 420} + }; + + RECT scroll_rect = {.left = 10, .top = 10, .right = 600, .bottom = 400}; + POINT scroll_offset = {.x = 0, .y = -20}; + + if (!prepare_partial_present(parameters, &device, &window, &swapchain, &context)) + return; + + hr = IDXGISwapChain1_GetBuffer(swapchain, 0, &IID_ID3D11Texture2D, (void **)&backbuffer_0); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain1_GetBuffer(swapchain, 1, &IID_ID3D11Texture2D, (void **)&backbuffer_1); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain1_GetBuffer(swapchain, 2, &IID_ID3D11Texture2D, (void **)&backbuffer_2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)backbuffer_0, NULL, &backbuffer_0_rtv); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + check_full(backbuffer_0, BLACK); + check_full(backbuffer_1, BLACK); + check_full(backbuffer_2, BLACK); + + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer_0_rtv, red); + + check_full(backbuffer_0, RED); + check_full(backbuffer_1, BLACK); + check_full(backbuffer_2, BLACK); + + present_params.DirtyRectsCount = 0; + present_params.pDirtyRects = NULL; + present_params.pScrollOffset = NULL; + present_params.pScrollRect = NULL; + + hr = IDXGISwapChain1_Present1(swapchain, 0, 0, &present_params); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + check_full(backbuffer_0, BLACK); + check_full(backbuffer_1, BLACK); + check_full(backbuffer_2, RED); + + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer_0_rtv, green); + + check_full(backbuffer_0, GREEN); + check_full(backbuffer_1, BLACK); + check_full(backbuffer_2, RED); + + present_params.DirtyRectsCount = ARRAYSIZE(dirty_rects_green); + present_params.pDirtyRects = dirty_rects_green; + present_params.pScrollOffset = NULL; + present_params.pScrollRect = NULL; + + hr = IDXGISwapChain1_Present1(swapchain, 0, 0, &present_params); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + check_full(backbuffer_0, BLACK); + check_full(backbuffer_1, RED); + + todo_wine check_color(backbuffer_2, 5, 5, RED); + todo_wine check_color(backbuffer_2, 20, 20, RED); + todo_wine check_color(backbuffer_2, 49, 29, RED); + todo_wine check_color(backbuffer_2, 49, 39, RED); + flaky_wine check_colors(backbuffer_2, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + todo_wine check_color(backbuffer_2, 99, 29, RED); + todo_wine check_color(backbuffer_2, 99, 39, RED); + flaky_wine check_colors(backbuffer_2, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 199, 59, RED); + todo_wine check_color(backbuffer_2, 399, 59, RED); + flaky_wine check_colors(backbuffer_2, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 9, 399, RED); + todo_wine check_color(backbuffer_2, 9, 419, RED); + todo_wine check_color(backbuffer_2, 599, 399, RED); + todo_wine check_color(backbuffer_2, 599, 419, RED); + + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer_0_rtv, blue); + + check_full(backbuffer_0, BLUE); + check_full(backbuffer_1, RED); + + todo_wine check_color(backbuffer_2, 5, 5, RED); + todo_wine check_color(backbuffer_2, 20, 20, RED); + todo_wine check_color(backbuffer_2, 49, 29, RED); + todo_wine check_color(backbuffer_2, 49, 39, RED); + flaky_wine check_colors(backbuffer_2, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + todo_wine check_color(backbuffer_2, 99, 29, RED); + todo_wine check_color(backbuffer_2, 99, 39, RED); + flaky_wine check_colors(backbuffer_2, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 199, 59, RED); + todo_wine check_color(backbuffer_2, 399, 59, RED); + flaky_wine check_colors(backbuffer_2, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 9, 399, RED); + todo_wine check_color(backbuffer_2, 9, 419, RED); + todo_wine check_color(backbuffer_2, 599, 399, RED); + todo_wine check_color(backbuffer_2, 599, 419, RED); + + present_params.DirtyRectsCount = ARRAYSIZE(dirty_rects_blue); + present_params.pDirtyRects = dirty_rects_blue; + present_params.pScrollRect = &scroll_rect; + present_params.pScrollOffset = &scroll_offset; + + hr = IDXGISwapChain1_Present1(swapchain, 0, 0, &present_params); + + if (is_flip_model(parameters->swap_effect)) + { + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + check_full(backbuffer_0, RED); + + todo_wine check_color(backbuffer_1, 5, 5, RED); + todo_wine check_color(backbuffer_1, 20, 20, RED); + todo_wine check_color(backbuffer_1, 49, 29, RED); + todo_wine check_color(backbuffer_1, 49, 39, RED); + flaky_wine check_colors(backbuffer_1, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_1, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_1, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + todo_wine check_color(backbuffer_1, 99, 29, RED); + todo_wine check_color(backbuffer_1, 99, 39, RED); + flaky_wine check_colors(backbuffer_1, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_1, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_1, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_1, 199, 59, RED); + todo_wine check_color(backbuffer_1, 399, 59, RED); + flaky_wine check_colors(backbuffer_1, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_1, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_1, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_1, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_1, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + flaky_wine check_colors(backbuffer_1, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_1, 9, 399, RED); + todo_wine check_color(backbuffer_1, 9, 419, RED); + todo_wine check_color(backbuffer_1, 599, 399, RED); + todo_wine check_color(backbuffer_1, 599, 419, RED); + + todo_wine check_color(backbuffer_2, 5, 5, RED); + todo_wine check_color(backbuffer_2, 20, 20, RED); + flaky_wine check_colors(backbuffer_2, 49, 29, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 49, 39, RED, GREEN, RED, BLUE); /* left side, green-blue edge */ + flaky_wine check_colors(backbuffer_2, 49, 49, RED, BLUE, RED, BLUE); /* left side */ + flaky_wine check_colors(backbuffer_2, 49, 79, RED, BLUE, RED, RED); /* bottom-left corner */ + todo_wine check_color(backbuffer_2, 49, 99, RED); + flaky_wine check_colors(backbuffer_2, 99, 29, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 99, 39, GREEN, RED, BLUE, RED); /* right side, green-blue edge */ + flaky_wine check_colors(backbuffer_2, 99, 49, BLUE, RED, BLUE, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 99, 79, BLUE, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 99, 99, RED); + flaky_wine check_colors(backbuffer_2, 199, 59, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 59, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 199, 79, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 399, 79, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, RED); /* bottom-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 279, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 199, 299, RED); + todo_wine check_color(backbuffer_2, 399, 299, RED); + flaky_wine check_colors(backbuffer_2, 9, 399, RED, RED, RED, BLUE); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 9, 419, RED, BLUE, RED, RED); /* bottom-left corner */ + flaky_wine check_colors(backbuffer_2, 599, 399, RED, RED, BLUE, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 599, 419, BLUE, RED, RED, RED); /* bottom-right corner */ + } + else + { + todo_wine ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr); + + /* No changes. */ + + todo_wine check_full(backbuffer_0, BLUE); + todo_wine check_full(backbuffer_1, RED); + + todo_wine check_color(backbuffer_2, 5, 5, RED); + todo_wine check_color(backbuffer_2, 20, 20, RED); + todo_wine check_color(backbuffer_2, 49, 29, RED); + todo_wine check_color(backbuffer_2, 49, 39, RED); + flaky_wine check_colors(backbuffer_2, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + todo_wine check_color(backbuffer_2, 99, 29, RED); + todo_wine check_color(backbuffer_2, 99, 39, RED); + flaky_wine check_colors(backbuffer_2, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 199, 59, RED); + todo_wine check_color(backbuffer_2, 399, 59, RED); + flaky_wine check_colors(backbuffer_2, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + flaky_wine check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + flaky_wine check_colors(backbuffer_2, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + flaky_wine check_colors(backbuffer_2, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + flaky_wine check_colors(backbuffer_2, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + todo_wine check_color(backbuffer_2, 9, 399, RED); + todo_wine check_color(backbuffer_2, 9, 419, RED); + todo_wine check_color(backbuffer_2, 599, 399, RED); + todo_wine check_color(backbuffer_2, 599, 419, RED); + } + + ID3D11RenderTargetView_Release(backbuffer_0_rtv); + ID3D11Texture2D_Release(backbuffer_0); + ID3D11Texture2D_Release(backbuffer_1); + ID3D11Texture2D_Release(backbuffer_2); + IDXGISwapChain1_Release(swapchain); + ID3D11DeviceContext_Release(context); + refcount = ID3D11Device_Release(device); + ok(!refcount, "Device has %lu references left.\n", refcount); + DestroyWindow(window); +} + +static void test_partial_present_scroll(void) +{ + struct test_partial_present_common_parameters parameters = { + .swap_effect = DXGI_SWAP_EFFECT_SEQUENTIAL, .buffer_count = 3, + .swapchain_width = 640, .swapchain_height = 480, + .window_width = 640, .window_height = 480, + }; + test_partial_present_scroll_impl(¶meters); + parameters.swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + parameters.window_width = 1280; + test_partial_present_scroll_impl(¶meters); +} + START_TEST(dxgi) { HMODULE dxgi_module, d3d11_module, d3d12_module, gdi32_module; @@ -8590,6 +9341,8 @@ START_TEST(dxgi) queue_test(test_object_wrapping); queue_test(test_factory_check_feature_support); queue_test(test_video_memory_budget_notification); + queue_test(test_partial_present_grid); + queue_test(test_partial_present_scroll);
run_queued_tests();
From: Andrew Boyarshin andrew.boyarshin@gmail.com
--- dlls/dxgi/swapchain.c | 50 ++++++++++++++++++++++++++++++++++++------ dlls/dxgi/tests/dxgi.c | 6 ++--- 2 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 5c49c7f2482..fd790cc7fe2 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -60,6 +60,18 @@ static BOOL dxgi_validate_flip_swap_effect_format(DXGI_FORMAT format) } }
+static BOOL dxgi_has_scroll_present_parameters(const DXGI_PRESENT_PARAMETERS *present_parameters) +{ + if (present_parameters->pScrollOffset && present_parameters->pScrollRect) + { + POINT scroll_offset = *present_parameters->pScrollOffset; + + return (scroll_offset.x || scroll_offset.y) && !IsRectEmpty(present_parameters->pScrollRect); + } + + return FALSE; +} + BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc) { unsigned int min_buffer_count; @@ -302,7 +314,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain4 *ifac /* IDXGISwapChain1 methods */
static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, - unsigned int sync_interval, unsigned int flags) + unsigned int sync_interval, unsigned int flags, const DXGI_PRESENT_PARAMETERS *dxgi_parameters) { HRESULT hr;
@@ -323,6 +335,27 @@ static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, return S_OK; }
+ if (dxgi_parameters && dxgi_parameters->DirtyRectsCount && dxgi_parameters->pDirtyRects) + { + FIXME("Ignored %u present dirty rectangles at %p ([0] = %s).\n", dxgi_parameters->DirtyRectsCount, dxgi_parameters->pDirtyRects, wine_dbgstr_rect(&dxgi_parameters->pDirtyRects[0])); + } + + if (dxgi_parameters && dxgi_has_scroll_present_parameters(dxgi_parameters)) + { + struct wined3d_swapchain_desc swapchain_desc; + enum wined3d_swap_effect swap_effect; + wined3d_swapchain_get_desc(swapchain->wined3d_swapchain, &swapchain_desc); + swap_effect = swapchain_desc.swap_effect; + if (swap_effect != WINED3D_SWAP_EFFECT_FLIP_SEQUENTIAL && swap_effect != WINED3D_SWAP_EFFECT_FLIP_DISCARD) + { + WARN("Partial presentation scroll (%s + %s) is only allowed on Flip-model swapchains.\n", + wine_dbgstr_rect(dxgi_parameters->pScrollRect), wine_dbgstr_point(dxgi_parameters->pScrollOffset)); + return DXGI_ERROR_INVALID_CALL; + } + + FIXME("Ignored present %s scroll of %s rectangle.\n", wine_dbgstr_point(dxgi_parameters->pScrollOffset), wine_dbgstr_rect(dxgi_parameters->pScrollRect)); + } + if (SUCCEEDED(hr = wined3d_swapchain_present(swapchain->wined3d_swapchain, NULL, NULL, NULL, sync_interval, 0))) InterlockedIncrement(&swapchain->present_count); return hr; @@ -334,7 +367,7 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_Present(IDXGI
TRACE("iface %p, sync_interval %u, flags %#x.\n", iface, sync_interval, flags);
- return d3d11_swapchain_present(swapchain, sync_interval, flags); + return d3d11_swapchain_present(swapchain, sync_interval, flags, NULL); }
static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetBuffer(IDXGISwapChain4 *iface, @@ -708,10 +741,7 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain4 *iface TRACE("iface %p, sync_interval %u, flags %#x, present_parameters %p.\n", iface, sync_interval, flags, present_parameters);
- if (present_parameters) - FIXME("Ignored present parameters %p.\n", present_parameters); - - return d3d11_swapchain_present(swapchain, sync_interval, flags); + return d3d11_swapchain_present(swapchain, sync_interval, flags, present_parameters); }
static BOOL STDMETHODCALLTYPE d3d11_swapchain_IsTemporaryMonoSupported(IDXGISwapChain4 *iface) @@ -2783,7 +2813,13 @@ static HRESULT STDMETHODCALLTYPE d3d12_swapchain_Present1(IDXGISwapChain4 *iface iface, sync_interval, flags, present_parameters);
if (present_parameters) - FIXME("Ignored present parameters %p.\n", present_parameters); + { + if (present_parameters->DirtyRectsCount && present_parameters->pDirtyRects) + FIXME("Ignored %u present dirty rectangles at %p ([0] = %s).\n", present_parameters->DirtyRectsCount, present_parameters->pDirtyRects, wine_dbgstr_rect(&present_parameters->pDirtyRects[0])); + + if (dxgi_has_scroll_present_parameters(present_parameters)) + FIXME("Ignored present %s scroll of %s rectangle.\n", wine_dbgstr_point(present_parameters->pScrollOffset), wine_dbgstr_rect(present_parameters->pScrollRect)); + }
return d3d12_swapchain_present(swapchain, sync_interval, flags); } diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index c0d7c4bca79..812695c2f99 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -9225,12 +9225,12 @@ static void test_partial_present_scroll_impl(struct test_partial_present_common_ } else { - todo_wine ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr); + ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
/* No changes. */
- todo_wine check_full(backbuffer_0, BLUE); - todo_wine check_full(backbuffer_1, RED); + check_full(backbuffer_0, BLUE); + check_full(backbuffer_1, RED);
todo_wine check_color(backbuffer_2, 5, 5, RED); todo_wine check_color(backbuffer_2, 20, 20, RED);
Thanks for the patch!
I don't want to request huge changes, and there's nothing prohibitively wrong with these tests, but I can see a things that would be improvements, if you're up for it:
(1) Move them to d3d11. Yeah, it's a dxgi feature, but it uses d3d11-specific code anyway, including a lot of stuff around device creation and readback that you've had to just duplicate here. I think it'd probably make more sense to just put the tests there.
(2) There are typically two ways that we write tests for nonuniform colors, and I think either one would probably be a bit more readable than this: either use d3d11-style functions that take a whole rect, or open-code the loop like in d3d11's test_texture_compressed_3d(). The whole "test a 2x2 region all at once" I see where it's coming from, but I don't think it's the most readable.
Some more minor comments:
(1) Why are you changing the window size between swap-effect tests?
(2) Those other parameters could probably just go into the tests themselves since they're not being changed either.
(3) What does test_partial_present_grid() demonstrate that test_partial_present_scroll() doesn't? I don't want to say that we don't want redundancy, but the former is distinctly less easy to read and I'm not sure if there's a reason it's helping other than redundancy.
1. I thought that since Present is part of DXGI, it should be tested there. If someone comes in later (with the motivation to implement it for D3D12), it can be a simple additional test parameter `is_d3d12`, without change to the checking logic, only to the test setup code and RTV clearing. But I agree that these tests are currently D3D11-specific and I don't have a particular preference, so I will move them. 2. Okay, I will see what I can do. But that means hardcoding the expected outputs for the `scroll` test, since the rectangles are not uniform there? Like H264 frames.
Minor comments:
1. I considered adding tests for what is actually displayed on the screen (not just the backbuffers), but these tests would be actually flaky even on Windows: apparently there is some optimization in DXGI/DWM that causes the background _not_ to flash different colors in `grid` test on large enough grids: they only update the actually dirty rectangles, while the backbuffer correctly updates the entire thing. The window size is necessary for testing the dirty rectangles transformation from swapchain space to window client size space. Currently it can only be done manually if you insert delays or set large enough grid size. 2. I liked being able to reconfigure the parameters fast (I even used `atoi(getenv(...))`), but I see the point that for the actual upstreaming it makes sense to hardcode what doesn't change. 3. Initial motivation was to test the expected behavior with different backbuffer count and the number of grid cells: what happens when number of grid cells is equal to or exceeds the backbuffer count? It also allowed for algorithmic rectangle comparison, not hardcoding the pixel positions like what `scroll` has to do. I honestly thought that the hardcoded pixel comparisons are harder to read, because it gets harder to understand why e.g. at `(50, 30)` there is an top-left corner of the green rectangle after scroll.
- I thought that since Present is part of DXGI, it should be tested there. If someone comes in later with the motivation to implement it for D3D12, it can be a simple additional test parameter `is_d3d12`, without change to the checking logic, only to the test setup/teardown code and RTV clearing. But I agree that these tests are currently D3D11-specific and I don't have a particular preference, so I will move them.
Yeah. Normally I'd say they should be in dxgi, and I'm still not even sure about this one. But sometimes expediency is better.
- Okay, I will see what I can do. But that means hardcoding the expected outputs for the `scroll` test, since the rectangles are not uniform there? Like H264 frames.
We're already hardcoding expected outputs, aren't we?
- I considered adding tests for what is actually displayed on the screen (not just the backbuffers), but these tests would be actually flaky even on Windows: apparently there is some optimization in DXGI/DWM that causes the background _not_ to flash different colors in `grid` test on large enough grids: they only update the actually dirty rectangles, while the backbuffer correctly updates the entire thing. This happens only when DXGI Debug Layer is disabled and no input is being directed to the window (moving the mouse cursor can break the optimization). The window size is necessary for testing the dirty rectangles transformation from swapchain space to window client size space. Currently it can only be done manually if you insert delays or set large enough grid size.
Yeah, I don't think that's going to be reliably testable. At best we could add visual tests with WINETEST_INTERACTIVE.
- I liked being able to reconfigure the parameters fast (I even used `atoi(getenv(...))`), but I see the point that for the actual upstreaming it makes sense to hardcode what doesn't change.
Sure. I don't dislike it, but I also don't see any reason we're going to need to mess with any of those parameters later.
- Initial motivation was to test the expected behavior with different backbuffer count and the number of grid cells: what happens when number of grid cells is equal to or exceeds the backbuffer count? It also allowed for algorithmic rectangle comparison, not hardcoding the pixel positions like what `scroll` has to do. I honestly thought that the hardcoded pixel comparisons are harder to read, because it gets harder to understand why e.g. at `(50, 30)` there is an top-left corner of the green rectangle after scroll.
Perhaps it's experience. It's common in d3d to write tests like
// clear rtv // draw quad from (-0.5, -0.5 to 0.5, 0.5) // test that some/all pixels in the quad we just drew are the color we expect
I'm also a bit low on sleep, though. On a second look the grid test doesn't seem too hard to read.