Based on !9515 (this MR is a draft until the first part is accepted).
This is most of the logic of partial presentation. D3D11 only.
Remaining parts:
1. Support for multiple rectangles. Requires changes to command stream and swapchain present op (GL, VK, GDI). I have it implemented, but I'm not confident in my implementation, so it must be a separate review. 2. Scroll. Simple grep.app search shows very little use. Neither Firefox nor Chromium use that. Windows Terminal is the most prominent. So this is low priority (though I have it implemented, it's not very difficult). 3. D3D12. I know of no apps that do partial presentation with D3D12. Most of my experience is with D2D & DComp, I barely have experience with D3D11, D3D12 is even more complicated, so I do not plan on implementing it. 4. Other scaling modes (see TODO). I don't think Wine implements them for D3D11. 5. Performance improvement? Right now dirty backbuffer regions are always tracked for correctness. I bet Windows does it better in the most common cases when there is no partial presentation.
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);
From: Andrew Boyarshin andrew.boyarshin@gmail.com
--- dlls/wined3d/context_gl.c | 2 +- dlls/wined3d/cs.c | 6 +- dlls/wined3d/device.c | 8 +- dlls/wined3d/surface.c | 3290 ++++++++++----------- dlls/wined3d/swapchain.c | 5059 ++++++++++++++++---------------- dlls/wined3d/texture.c | 2 +- dlls/wined3d/wined3d_private.h | 8 +- 7 files changed, 4203 insertions(+), 4172 deletions(-)
diff --git a/dlls/wined3d/context_gl.c b/dlls/wined3d/context_gl.c index ac1b9370fc1..6d16bc69b4e 100644 --- a/dlls/wined3d/context_gl.c +++ b/dlls/wined3d/context_gl.c @@ -4340,7 +4340,7 @@ struct wined3d_context *wined3d_context_gl_acquire(const struct wined3d_device * struct wined3d_swapchain *swapchain = device->swapchains[0];
if (swapchain->back_buffers) - texture = swapchain->back_buffers[0]; + texture = swapchain->back_buffers[0].texture; else texture = swapchain->front_buffer; sub_resource_idx = 0; diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c index 21ed1264971..de969ba82fd 100644 --- a/dlls/wined3d/cs.c +++ b/dlls/wined3d/cs.c @@ -671,7 +671,7 @@ static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
swapchain = op->swapchain; desc = &swapchain->state.desc; - back_buffer = swapchain->back_buffers[0]; + back_buffer = swapchain->back_buffers[0].texture; wined3d_swapchain_set_window(swapchain, op->dst_window_override);
if ((logo_texture = swapchain->device->logo_texture)) @@ -711,7 +711,7 @@ static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data) swapchain->swapchain_ops->swapchain_present(swapchain, &op->src_rect, &op->dst_rect, op->swap_interval, op->flags);
/* Discard buffers if the swap effect allows it. */ - back_buffer = swapchain->back_buffers[desc->backbuffer_count - 1]; + back_buffer = swapchain->back_buffers[desc->backbuffer_count - 1].texture; if (desc->swap_effect == WINED3D_SWAP_EFFECT_DISCARD || desc->swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD) wined3d_texture_validate_location(back_buffer, 0, WINED3D_LOCATION_DISCARDED);
@@ -777,7 +777,7 @@ void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *sw wined3d_resource_reference(&swapchain->front_buffer->resource); for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) { - wined3d_resource_reference(&swapchain->back_buffers[i]->resource); + wined3d_resource_reference(&swapchain->back_buffers[i].texture->resource); }
wined3d_device_context_submit(&cs->c, WINED3D_CS_QUEUE_DEFAULT); diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 443bf10ab76..eb26ffe8d4e 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -1487,7 +1487,7 @@ void wined3d_device_gl_create_primary_opengl_context_cs(void *object)
device = &device_gl->d; swapchain = device->swapchains[0]; - target = swapchain->back_buffers ? swapchain->back_buffers[0] : swapchain->front_buffer; + target = swapchain->back_buffers ? swapchain->back_buffers[0].texture : swapchain->front_buffer; if (!(context = context_acquire(device, target, 0))) { WARN("Failed to acquire context.\n"); @@ -1571,7 +1571,7 @@ HRESULT wined3d_device_set_implicit_swapchain(struct wined3d_device *device, str swapchain_desc = &swapchain->state.desc; if (swapchain_desc->backbuffer_count && swapchain_desc->backbuffer_bind_flags & WINED3D_BIND_RENDER_TARGET) { - struct wined3d_resource *back_buffer = &swapchain->back_buffers[0]->resource; + struct wined3d_resource *back_buffer = &swapchain->back_buffers[0].texture->resource; struct wined3d_view_desc view_desc;
view_desc.format_id = back_buffer->format->id; @@ -5154,7 +5154,7 @@ HRESULT CDECL wined3d_device_reset(struct wined3d_device *device, update_swapchain_flags(swapchain->front_buffer); for (i = 0; i < current_desc->backbuffer_count; ++i) { - update_swapchain_flags(swapchain->back_buffers[i]); + update_swapchain_flags(swapchain->back_buffers[i].texture); } }
@@ -5212,7 +5212,7 @@ HRESULT CDECL wined3d_device_reset(struct wined3d_device *device, } if (current_desc->backbuffer_count && current_desc->backbuffer_bind_flags & WINED3D_BIND_RENDER_TARGET) { - struct wined3d_resource *back_buffer = &swapchain->back_buffers[0]->resource; + struct wined3d_resource *back_buffer = &swapchain->back_buffers[0].texture->resource;
view_desc.format_id = back_buffer->format->id; view_desc.flags = 0; diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c index 067b3afc30e..3389f5fb866 100644 --- a/dlls/wined3d/surface.c +++ b/dlls/wined3d/surface.c @@ -1,1645 +1,1645 @@ -/* - * Copyright 1997-2000 Marcus Meissner - * Copyright 1998-2000 Lionel Ulmer - * Copyright 2000-2001 TransGaming Technologies Inc. - * Copyright 2002-2005 Jason Edmeades - * Copyright 2002-2003 Raphael Junqueira - * Copyright 2004 Christian Costa - * Copyright 2005 Oliver Stieber - * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers - * Copyright 2007-2008 Henri Verbeet - * Copyright 2006-2008 Roderick Colenbrander - * Copyright 2009-2011 Henri Verbeet for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wined3d_private.h" -#include "wined3d_gl.h" - -WINE_DEFAULT_DEBUG_CHANNEL(d3d); -WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); - -/* Works correctly only for <= 4 bpp formats. */ -static void get_color_masks(const struct wined3d_format *format, uint32_t *masks) -{ - masks[0] = wined3d_mask_from_size(format->red_size) << format->red_offset; - masks[1] = wined3d_mask_from_size(format->green_size) << format->green_offset; - masks[2] = wined3d_mask_from_size(format->blue_size) << format->blue_offset; -} - -static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst, - unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) -{ - unsigned short *dst_s; - const float *src_f; - unsigned int x, y; - - TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); - - for (y = 0; y < h; ++y) - { - src_f = (const float *)(src + y * pitch_in); - dst_s = (unsigned short *) (dst + y * pitch_out); - for (x = 0; x < w; ++x) - { - dst_s[x] = float_32_to_16(src_f + x); - } - } -} - -static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst, - unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) -{ - static const unsigned char convert_5to8[] = - { - 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a, - 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b, - 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd, - 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff, - }; - static const unsigned char convert_6to8[] = - { - 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, - 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d, - 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d, - 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d, - 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e, - 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe, - 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf, - 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff, - }; - unsigned int x, y; - - TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); - - for (y = 0; y < h; ++y) - { - const WORD *src_line = (const WORD *)(src + y * pitch_in); - DWORD *dst_line = (DWORD *)(dst + y * pitch_out); - for (x = 0; x < w; ++x) - { - WORD pixel = src_line[x]; - dst_line[x] = 0xff000000u - | convert_5to8[(pixel & 0xf800u) >> 11] << 16 - | convert_6to8[(pixel & 0x07e0u) >> 5] << 8 - | convert_5to8[(pixel & 0x001fu)]; - } - } -} - -/* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since - * in both cases we're just setting the X / Alpha channel to 0xff. */ -static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst, - unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) -{ - unsigned int x, y; - - TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); - - for (y = 0; y < h; ++y) - { - const DWORD *src_line = (const DWORD *)(src + y * pitch_in); - DWORD *dst_line = (DWORD *)(dst + y * pitch_out); - - for (x = 0; x < w; ++x) - { - dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff); - } - } -} - -static inline BYTE cliptobyte(int x) -{ - return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x)); -} - -static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst, - unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) -{ - int c2, d, e, r2 = 0, g2 = 0, b2 = 0; - unsigned int x, y; - - TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); - - for (y = 0; y < h; ++y) - { - const BYTE *src_line = src + y * pitch_in; - DWORD *dst_line = (DWORD *)(dst + y * pitch_out); - for (x = 0; x < w; ++x) - { - /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV: - * C = Y - 16; D = U - 128; E = V - 128; - * R = cliptobyte((298 * C + 409 * E + 128) >> 8); - * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8); - * B = cliptobyte((298 * C + 516 * D + 128) >> 8); - * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V . - * U and V are shared between the pixels. */ - if (!(x & 1)) /* For every even pixel, read new U and V. */ - { - d = (int) src_line[1] - 128; - e = (int) src_line[3] - 128; - r2 = 409 * e + 128; - g2 = - 100 * d - 208 * e + 128; - b2 = 516 * d + 128; - } - c2 = 298 * ((int) src_line[0] - 16); - dst_line[x] = 0xff000000 - | cliptobyte((c2 + r2) >> 8) << 16 /* red */ - | cliptobyte((c2 + g2) >> 8) << 8 /* green */ - | cliptobyte((c2 + b2) >> 8); /* blue */ - /* Scale RGB values to 0..255 range, - * then clip them if still not in range (may be negative), - * then shift them within DWORD if necessary. */ - src_line += 2; - } - } -} - -static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst, - unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) -{ - unsigned int x, y; - int c2, d, e, r2 = 0, g2 = 0, b2 = 0; - - TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); - - for (y = 0; y < h; ++y) - { - const BYTE *src_line = src + y * pitch_in; - WORD *dst_line = (WORD *)(dst + y * pitch_out); - for (x = 0; x < w; ++x) - { - /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV: - * C = Y - 16; D = U - 128; E = V - 128; - * R = cliptobyte((298 * C + 409 * E + 128) >> 8); - * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8); - * B = cliptobyte((298 * C + 516 * D + 128) >> 8); - * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V . - * U and V are shared between the pixels. */ - if (!(x & 1)) /* For every even pixel, read new U and V. */ - { - d = (int) src_line[1] - 128; - e = (int) src_line[3] - 128; - r2 = 409 * e + 128; - g2 = - 100 * d - 208 * e + 128; - b2 = 516 * d + 128; - } - c2 = 298 * ((int) src_line[0] - 16); - dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */ - | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */ - | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */ - /* Scale RGB values to 0..255 range, - * then clip them if still not in range (may be negative), - * then shift them within DWORD if necessary. */ - src_line += 2; - } - } -} - -struct d3dfmt_converter_desc -{ - enum wined3d_format_id from, to; - void (*convert)(const BYTE *src, BYTE *dst, - unsigned int pitch_in, unsigned int pitch_out, - unsigned int w, unsigned int h); -}; - -static const struct d3dfmt_converter_desc converters[] = -{ - {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float}, - {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8}, - {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8}, - {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8}, - {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8}, - {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5}, -}; - -static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from, - enum wined3d_format_id to) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(converters); ++i) - { - if (converters[i].from == from && converters[i].to == to) - return &converters[i]; - } - - return NULL; -} - -static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture, - unsigned int sub_resource_idx, const struct wined3d_format *dst_format) -{ - unsigned int texture_level = sub_resource_idx % src_texture->level_count; - const struct wined3d_format *src_format = src_texture->resource.format; - struct wined3d_device *device = src_texture->resource.device; - const struct d3dfmt_converter_desc *conv = NULL; - unsigned int src_row_pitch, src_slice_pitch; - struct wined3d_texture *dst_texture; - struct wined3d_bo_address src_data; - struct wined3d_resource_desc desc; - struct wined3d_context *context; - DWORD map_binding; - - if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D) - || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count - || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count - || ((src_format->attrs & WINED3D_FORMAT_ATTR_COMPRESSED) - && !src_format->decompress))) - { - FIXME("Cannot find a conversion function from format %s to %s.\n", - debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); - return NULL; - } - - /* FIXME: Multisampled conversion? */ - desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; - desc.format = dst_format->id; - desc.multisample_type = WINED3D_MULTISAMPLE_NONE; - desc.multisample_quality = 0; - desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_CS; - desc.bind_flags = 0; - desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; - desc.width = wined3d_texture_get_level_width(src_texture, texture_level); - desc.height = wined3d_texture_get_level_height(src_texture, texture_level); - desc.depth = 1; - desc.size = 0; - if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD, - NULL, NULL, &wined3d_null_parent_ops, &dst_texture))) - { - ERR("Failed to create a destination texture for conversion.\n"); - return NULL; - } - - context = context_acquire(device, NULL, 0); - - map_binding = src_texture->resource.map_binding; - if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding)) - ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding)); - wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch); - wined3d_texture_get_bo_address(src_texture, sub_resource_idx, &src_data, map_binding); - - if (conv) - { - unsigned int dst_row_pitch, dst_slice_pitch; - struct wined3d_bo_address dst_data; - struct wined3d_range range; - const BYTE *src; - BYTE *dst; - - map_binding = dst_texture->resource.map_binding; - if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding)) - ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); - wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch); - wined3d_texture_get_bo_address(dst_texture, 0, &dst_data, map_binding); - - src = wined3d_context_map_bo_address(context, &src_data, - src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ); - dst = wined3d_context_map_bo_address(context, &dst_data, - dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE); - - conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height); - - range.offset = 0; - range.size = dst_texture->sub_resources[0].size; - wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding); - wined3d_context_unmap_bo_address(context, &dst_data, 1, &range); - wined3d_context_unmap_bo_address(context, &src_data, 0, NULL); - } - else - { - struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1}; - - TRACE("Using upload conversion.\n"); - - wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); - dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data), - src_format, &src_box, src_row_pitch, src_slice_pitch, - dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0); - - wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB); - wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB); - } - - context_release(context); - - return dst_texture; -} - -void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx, - struct wined3d_context *context, DWORD src_location, DWORD dst_location) -{ - struct wined3d_resource *resource = &texture->resource; - struct wined3d_device *device = resource->device; - const struct wined3d_format_gl *format_gl; - struct wined3d_texture *restore_texture; - const struct wined3d_gl_info *gl_info; - struct wined3d_context_gl *context_gl; - unsigned int row_pitch, slice_pitch; - unsigned int width, height, level; - struct wined3d_bo_address data; - bool restore_context = false; - unsigned int restore_idx; - BYTE *row, *top, *bottom; - BOOL src_is_upside_down; - BYTE *mem = NULL; - uint8_t *offset; - unsigned int i; - - TRACE("texture %p, sub_resource_idx %u, context %p, src_location %s, dst_location %s.\n", - texture, sub_resource_idx, context, wined3d_debug_location(src_location), wined3d_debug_location(dst_location)); - - /* dst_location was already prepared by the caller. */ - wined3d_texture_get_bo_address(texture, sub_resource_idx, &data, dst_location); - offset = data.addr; - - restore_texture = context->current_rt.texture; - restore_idx = context->current_rt.sub_resource_idx; - if (!wined3d_resource_is_offscreen(resource) && (restore_texture != texture || restore_idx != sub_resource_idx)) - { - context = context_acquire(device, texture, sub_resource_idx); - restore_context = true; - } - context_gl = wined3d_context_gl(context); - gl_info = context_gl->gl_info; - - if (resource->format->depth_size || resource->format->stencil_size) - wined3d_context_gl_apply_fbo_state_explicit(context_gl, GL_READ_FRAMEBUFFER, - NULL, 0, resource, sub_resource_idx, src_location); - else - wined3d_context_gl_apply_fbo_state_explicit(context_gl, GL_READ_FRAMEBUFFER, - resource, sub_resource_idx, NULL, 0, src_location); - - /* Select the correct read buffer, and give some debug output. There is no - * need to keep track of the current read buffer or reset it, every part - * of the code that reads pixels sets the read buffer as desired. */ - if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource)) - { - /* Mapping the primary render target which is not on a swapchain. - * Read from the back buffer. */ - TRACE("Mapping offscreen render target.\n"); - gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl)); - src_is_upside_down = TRUE; - } - else - { - /* Onscreen surfaces are always part of a swapchain */ - GLenum buffer = wined3d_texture_get_gl_buffer(texture); - TRACE("Mapping %#x buffer.\n", buffer); - gl_info->gl_ops.gl.p_glReadBuffer(buffer); - src_is_upside_down = FALSE; - } - checkGLcall("glReadBuffer"); - wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER); - - if (data.buffer_object) - { - GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, wined3d_bo_gl(data.buffer_object)->id)); - checkGLcall("glBindBuffer"); - offset += data.buffer_object->buffer_offset; - } - else - { - GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); - checkGLcall("glBindBuffer"); - } - - level = sub_resource_idx % texture->level_count; - wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch); - format_gl = wined3d_format_gl(resource->format); - - /* Setup pixel store pack state -- to glReadPixels into the correct place */ - gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count); - checkGLcall("glPixelStorei"); - - width = wined3d_texture_get_level_width(texture, level); - height = wined3d_texture_get_level_height(texture, level); - gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height, - format_gl->format, format_gl->type, offset); - checkGLcall("glReadPixels"); - - /* Reset previous pixel store pack state */ - gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0); - checkGLcall("glPixelStorei"); - - if (!src_is_upside_down) - { - /* glReadPixels returns the image upside down, and there is no way to - * prevent this. Flip the lines in software. */ - - if (!(row = malloc(row_pitch))) - goto error; - - if (data.buffer_object) - { - mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE)); - checkGLcall("glMapBuffer"); - } - mem += (uintptr_t)offset; - - top = mem; - bottom = mem + row_pitch * (height - 1); - for (i = 0; i < height / 2; i++) - { - memcpy(row, top, row_pitch); - memcpy(top, bottom, row_pitch); - memcpy(bottom, row, row_pitch); - top += row_pitch; - bottom -= row_pitch; - } - free(row); - - if (data.buffer_object) - GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER)); - } - -error: - if (data.buffer_object) - { - GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); - wined3d_context_gl_reference_bo(context_gl, wined3d_bo_gl(data.buffer_object)); - checkGLcall("glBindBuffer"); - } - - if (restore_context) - context_restore(context, restore_texture, restore_idx); -} - -/* Context activation is done by the caller. */ -static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) -{ - struct wined3d_blitter *next; - - if ((next = blitter->next)) - next->ops->blitter_destroy(next, context); - - free(blitter); -} - -static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data, - UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h, - const struct wined3d_format *format, uint32_t flags, const struct wined3d_blt_fx *fx) -{ - UINT row_block_count; - const BYTE *src_row; - BYTE *dst_row; - UINT x, y; - - src_row = src_data; - dst_row = dst_data; - - row_block_count = (update_w + format->block_width - 1) / format->block_width; - - if (!flags) - { - for (y = 0; y < update_h; y += format->block_height) - { - memcpy(dst_row, src_row, row_block_count * format->block_byte_count); - src_row += src_pitch; - dst_row += dst_pitch; - } - - return WINED3D_OK; - } - - if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN) - { - src_row += (((update_h / format->block_height) - 1) * src_pitch); - - switch (format->id) - { - case WINED3DFMT_DXT1: - for (y = 0; y < update_h; y += format->block_height) - { - struct block - { - WORD color[2]; - BYTE control_row[4]; - }; - - const struct block *s = (const struct block *)src_row; - struct block *d = (struct block *)dst_row; - - for (x = 0; x < row_block_count; ++x) - { - d[x].color[0] = s[x].color[0]; - d[x].color[1] = s[x].color[1]; - d[x].control_row[0] = s[x].control_row[3]; - d[x].control_row[1] = s[x].control_row[2]; - d[x].control_row[2] = s[x].control_row[1]; - d[x].control_row[3] = s[x].control_row[0]; - } - src_row -= src_pitch; - dst_row += dst_pitch; - } - return WINED3D_OK; - - case WINED3DFMT_DXT2: - case WINED3DFMT_DXT3: - for (y = 0; y < update_h; y += format->block_height) - { - struct block - { - WORD alpha_row[4]; - WORD color[2]; - BYTE control_row[4]; - }; - - const struct block *s = (const struct block *)src_row; - struct block *d = (struct block *)dst_row; - - for (x = 0; x < row_block_count; ++x) - { - d[x].alpha_row[0] = s[x].alpha_row[3]; - d[x].alpha_row[1] = s[x].alpha_row[2]; - d[x].alpha_row[2] = s[x].alpha_row[1]; - d[x].alpha_row[3] = s[x].alpha_row[0]; - d[x].color[0] = s[x].color[0]; - d[x].color[1] = s[x].color[1]; - d[x].control_row[0] = s[x].control_row[3]; - d[x].control_row[1] = s[x].control_row[2]; - d[x].control_row[2] = s[x].control_row[1]; - d[x].control_row[3] = s[x].control_row[0]; - } - src_row -= src_pitch; - dst_row += dst_pitch; - } - return WINED3D_OK; - - default: - FIXME("Compressed flip not implemented for format %s.\n", - debug_d3dformat(format->id)); - return E_NOTIMPL; - } - } - - FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n", - debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0); - - return E_NOTIMPL; -} - -static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, - const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, - const struct wined3d_box *src_box, uint32_t flags, const struct wined3d_blt_fx *fx, - enum wined3d_texture_filter_type filter) -{ - unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count; - struct wined3d_device *device = dst_texture->resource.device; - const struct wined3d_format *src_format, *dst_format; - struct wined3d_texture *converted_texture = NULL; - struct wined3d_bo_address src_data, dst_data; - unsigned int src_fmt_attrs, dst_fmt_attrs; - struct wined3d_map_desc dst_map, src_map; - unsigned int x, sx, xinc, y, sy, yinc; - struct wined3d_context *context; - struct wined3d_range dst_range; - unsigned int texture_level; - BYTE *tmp_buffer = NULL; - HRESULT hr = WINED3D_OK; - BOOL same_sub_resource; - BOOL upload = FALSE; - DWORD map_binding; - const BYTE *sbase; - const BYTE *sbuf; - BYTE *dbuf; - - TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, " - "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n", - dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, - src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter)); - - context = context_acquire(device, NULL, 0); - - src_format = src_texture->resource.format; - dst_format = dst_texture->resource.format; - - if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id) - src_format = dst_format; - if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id) - dst_format = src_format; - - src_height = src_box->bottom - src_box->top; - src_width = src_box->right - src_box->left; - dst_height = dst_box->bottom - dst_box->top; - dst_width = dst_box->right - dst_box->left; - - dst_range.offset = 0; - dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size; - if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx) - { - same_sub_resource = TRUE; - - map_binding = dst_texture->resource.map_binding; - texture_level = dst_sub_resource_idx % dst_texture->level_count; - if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding)) - ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); - wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding); - wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch); - wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding); - dst_map.data = wined3d_context_map_bo_address(context, &dst_data, - dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE); - - src_map = dst_map; - } - else - { - same_sub_resource = FALSE; - upload = dst_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS - && (dst_width != src_width || dst_height != src_height); - - if (upload) - { - dst_format = src_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS - ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format; - } - - if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id) - { - if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format))) - { - FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id), - debug_d3dformat(dst_format->id)); - context_release(context); - return WINED3DERR_NOTAVAILABLE; - } - src_texture = converted_texture; - src_sub_resource_idx = 0; - src_format = src_texture->resource.format; - } - - map_binding = src_texture->resource.map_binding; - texture_level = src_sub_resource_idx % src_texture->level_count; - if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding)) - ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding)); - wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch); - wined3d_texture_get_bo_address(src_texture, src_sub_resource_idx, &src_data, map_binding); - src_map.data = wined3d_context_map_bo_address(context, &src_data, - src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ); - - if (upload) - { - wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom, - &dst_map.row_pitch, &dst_map.slice_pitch); - dst_map.data = malloc(dst_map.slice_pitch); - } - else - { - map_binding = dst_texture->resource.map_binding; - texture_level = dst_sub_resource_idx % dst_texture->level_count; - if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding)) - ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); - - wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding); - wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch); - wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding); - dst_map.data = wined3d_context_map_bo_address(context, &dst_data, - dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE); - } - } - src_fmt_attrs = src_format->attrs; - dst_fmt_attrs = dst_format->attrs; - flags &= ~WINED3D_BLT_RAW; - - bpp = dst_format->byte_count; - row_byte_count = dst_width * bpp; - - sbase = (BYTE *)src_map.data - + ((src_box->top / src_format->block_height) * src_map.row_pitch) - + ((src_box->left / src_format->block_width) * src_format->block_byte_count); - dbuf = (BYTE *)dst_map.data - + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch) - + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count); - - if (src_fmt_attrs & dst_fmt_attrs & WINED3D_FORMAT_ATTR_BLOCKS) - { - TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); - - if (same_sub_resource) - { - FIXME("Only plain blits supported on compressed surfaces.\n"); - hr = E_NOTIMPL; - goto release; - } - - hr = surface_cpu_blt_compressed(sbase, dbuf, - src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height, - src_format, flags, fx); - goto release; - } - - if ((src_fmt_attrs | dst_fmt_attrs) & WINED3D_FORMAT_ATTR_HEIGHT_SCALE) - { - FIXME("Unsupported blit between height-scaled formats (src %s, dst %s).\n", - debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); - hr = E_NOTIMPL; - goto release; - } - - if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT - && (src_width != dst_width || src_height != dst_height)) - { - /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */ - FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter)); - } - - xinc = (src_width << 16) / dst_width; - yinc = (src_height << 16) / dst_height; - - if (!flags) - { - /* No effects, we can cheat here. */ - if (dst_width == src_width) - { - if (dst_height == src_height) - { - /* No stretching in either direction. This needs to be as fast - * as possible. */ - sbuf = sbase; - - /* Check for overlapping surfaces. */ - if (!same_sub_resource || dst_box->top < src_box->top - || dst_box->right <= src_box->left || src_box->right <= dst_box->left) - { - /* No overlap, or dst above src, so copy from top downwards. */ - for (y = 0; y < dst_height; ++y) - { - memcpy(dbuf, sbuf, row_byte_count); - sbuf += src_map.row_pitch; - dbuf += dst_map.row_pitch; - } - } - else if (dst_box->top > src_box->top) - { - /* Copy from bottom upwards. */ - sbuf += src_map.row_pitch * dst_height; - dbuf += dst_map.row_pitch * dst_height; - for (y = 0; y < dst_height; ++y) - { - sbuf -= src_map.row_pitch; - dbuf -= dst_map.row_pitch; - memcpy(dbuf, sbuf, row_byte_count); - } - } - else - { - /* Src and dst overlapping on the same line, use memmove. */ - for (y = 0; y < dst_height; ++y) - { - memmove(dbuf, sbuf, row_byte_count); - sbuf += src_map.row_pitch; - dbuf += dst_map.row_pitch; - } - } - } - else - { - /* Stretching in y direction only. */ - - if (same_sub_resource) - { - /* Use a temporary buffer if blitting a surface to itself. */ - if ((tmp_buffer = malloc(src_height * src_map.row_pitch))) - { - memcpy(tmp_buffer, sbase, src_height * src_map.row_pitch); - sbase = tmp_buffer; - } - } - - for (y = sy = 0; y < dst_height; ++y, sy += yinc) - { - sbuf = sbase + (sy >> 16) * src_map.row_pitch; - memcpy(dbuf, sbuf, row_byte_count); - dbuf += dst_map.row_pitch; - } - - if (same_sub_resource) - free(tmp_buffer); - } - } - else - { - /* Stretching in X direction. */ - unsigned int last_sy = ~0u; - - if (same_sub_resource) - { - /* Use a temporary buffer if blitting a surface to itself. */ - if ((tmp_buffer = malloc(src_height * src_map.row_pitch))) - { - memcpy(tmp_buffer, sbase, src_height * src_map.row_pitch); - sbase = tmp_buffer; - } - } - - for (y = sy = 0; y < dst_height; ++y, sy += yinc) - { - sbuf = sbase + (sy >> 16) * src_map.row_pitch; - - if ((sy >> 16) == (last_sy >> 16)) - { - /* This source row is the same as last source row - - * Copy the already stretched row. */ - memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count); - } - else - { -#define STRETCH_ROW(type) \ -do { \ - const type *s = (const type *)sbuf; \ - type *d = (type *)dbuf; \ - for (x = sx = 0; x < dst_width; ++x, sx += xinc) \ - d[x] = s[sx >> 16]; \ -} while(0) - - switch(bpp) - { - case 1: - STRETCH_ROW(BYTE); - break; - case 2: - STRETCH_ROW(WORD); - break; - case 4: - STRETCH_ROW(DWORD); - break; - case 3: - { - const BYTE *s; - BYTE *d = dbuf; - for (x = sx = 0; x < dst_width; x++, sx+= xinc) - { - DWORD pixel; - - s = sbuf + 3 * (sx >> 16); - pixel = s[0] | (s[1] << 8) | (s[2] << 16); - d[0] = (pixel ) & 0xff; - d[1] = (pixel >> 8) & 0xff; - d[2] = (pixel >> 16) & 0xff; - d += 3; - } - break; - } - default: - FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8); - hr = WINED3DERR_NOTAVAILABLE; - goto error; - } -#undef STRETCH_ROW - } - dbuf += dst_map.row_pitch; - last_sy = sy; - } - - if (same_sub_resource) - free(tmp_buffer); - } - } - else - { - LONG dstyinc = dst_map.row_pitch, dstxinc = bpp; - DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff; - DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff; - if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY - | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE)) - { - /* The color keying flags are checked for correctness in ddraw. */ - if (flags & WINED3D_BLT_SRC_CKEY) - { - keylow = src_texture->async.src_blt_color_key.color_space_low_value; - keyhigh = src_texture->async.src_blt_color_key.color_space_high_value; - } - else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE) - { - keylow = fx->src_color_key.color_space_low_value; - keyhigh = fx->src_color_key.color_space_high_value; - } - - if (flags & WINED3D_BLT_DST_CKEY) - { - /* Destination color keys are taken from the source surface! */ - destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value; - destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value; - } - else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE) - { - destkeylow = fx->dst_color_key.color_space_low_value; - destkeyhigh = fx->dst_color_key.color_space_high_value; - } - - if (bpp == 1) - { - keymask = 0xff; - } - else - { - uint32_t masks[3]; - get_color_masks(src_format, masks); - keymask = masks[0] | masks[1] | masks[2]; - } - flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY - | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE); - } - - if (flags & WINED3D_BLT_FX) - { - BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp; - LONG tmpxy; - dTopLeft = dbuf; - dTopRight = dbuf + ((dst_width - 1) * bpp); - dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch); - dBottomRight = dBottomLeft + ((dst_width - 1) * bpp); - - if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY) - { - /* I don't think we need to do anything about this flag. */ - WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n"); - } - if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT) - { - tmp = dTopRight; - dTopRight = dTopLeft; - dTopLeft = tmp; - tmp = dBottomRight; - dBottomRight = dBottomLeft; - dBottomLeft = tmp; - dstxinc = dstxinc * -1; - } - if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN) - { - tmp = dTopLeft; - dTopLeft = dBottomLeft; - dBottomLeft = tmp; - tmp = dTopRight; - dTopRight = dBottomRight; - dBottomRight = tmp; - dstyinc = dstyinc * -1; - } - if (fx->fx & WINEDDBLTFX_NOTEARING) - { - /* I don't think we need to do anything about this flag. */ - WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n"); - } - if (fx->fx & WINEDDBLTFX_ROTATE180) - { - tmp = dBottomRight; - dBottomRight = dTopLeft; - dTopLeft = tmp; - tmp = dBottomLeft; - dBottomLeft = dTopRight; - dTopRight = tmp; - dstxinc = dstxinc * -1; - dstyinc = dstyinc * -1; - } - if (fx->fx & WINEDDBLTFX_ROTATE270) - { - tmp = dTopLeft; - dTopLeft = dBottomLeft; - dBottomLeft = dBottomRight; - dBottomRight = dTopRight; - dTopRight = tmp; - tmpxy = dstxinc; - dstxinc = dstyinc; - dstyinc = tmpxy; - dstxinc = dstxinc * -1; - } - if (fx->fx & WINEDDBLTFX_ROTATE90) - { - tmp = dTopLeft; - dTopLeft = dTopRight; - dTopRight = dBottomRight; - dBottomRight = dBottomLeft; - dBottomLeft = tmp; - tmpxy = dstxinc; - dstxinc = dstyinc; - dstyinc = tmpxy; - dstyinc = dstyinc * -1; - } - if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST) - { - /* I don't think we need to do anything about this flag. */ - WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n"); - } - dbuf = dTopLeft; - flags &= ~(WINED3D_BLT_FX); - } - -#define COPY_COLORKEY_FX(type) \ -do { \ - const type *s; \ - type *d = (type *)dbuf, *dx, tmp; \ - for (y = sy = 0; y < dst_height; ++y, sy += yinc) \ - { \ - s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \ - dx = d; \ - for (x = sx = 0; x < dst_width; ++x, sx += xinc) \ - { \ - tmp = s[sx >> 16]; \ - if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \ - && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \ - { \ - dx[0] = tmp; \ - } \ - dx = (type *)(((BYTE *)dx) + dstxinc); \ - } \ - d = (type *)(((BYTE *)d) + dstyinc); \ - } \ -} while(0) - - switch (bpp) - { - case 1: - COPY_COLORKEY_FX(BYTE); - break; - case 2: - COPY_COLORKEY_FX(WORD); - break; - case 4: - COPY_COLORKEY_FX(DWORD); - break; - case 3: - { - const BYTE *s; - BYTE *d = dbuf, *dx; - for (y = sy = 0; y < dst_height; ++y, sy += yinc) - { - sbuf = sbase + (sy >> 16) * src_map.row_pitch; - dx = d; - for (x = sx = 0; x < dst_width; ++x, sx+= xinc) - { - DWORD pixel, dpixel = 0; - s = sbuf + 3 * (sx>>16); - pixel = s[0] | (s[1] << 8) | (s[2] << 16); - dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16); - if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh) - && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh)) - { - dx[0] = (pixel ) & 0xff; - dx[1] = (pixel >> 8) & 0xff; - dx[2] = (pixel >> 16) & 0xff; - } - dx += dstxinc; - } - d += dstyinc; - } - break; - } - default: - FIXME("%s color-keyed blit not implemented for bpp %u.\n", - (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8); - hr = WINED3DERR_NOTAVAILABLE; - goto error; -#undef COPY_COLORKEY_FX - } - } - -error: - if (flags) - FIXME(" Unsupported flags %#x.\n", flags); - -release: - if (upload && hr == WINED3D_OK) - { - struct wined3d_bo_address data; - - data.buffer_object = 0; - data.addr = dst_map.data; - - texture_level = dst_sub_resource_idx % dst_texture->level_count; - - wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB); - dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format, - dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level, - WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0); - - wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB); - wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB); - } - - if (upload) - { - free(dst_map.data); - } - else - { - wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range); - } - - if (!same_sub_resource) - wined3d_context_unmap_bo_address(context, &src_data, 0, NULL); - if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture) - { - SetRect(&dst_texture->swapchain->front_buffer_update, - dst_box->left, dst_box->top, dst_box->right, dst_box->bottom); - dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain); - } - if (converted_texture) - wined3d_texture_decref(converted_texture); - context_release(context); - - return hr; -} - -void cpu_blitter_clear_texture(struct wined3d_texture *texture, unsigned int sub_resource_idx, - const struct wined3d_box *box, const struct wined3d_color *colour) -{ - struct wined3d_device *device = texture->resource.device; - struct wined3d_context *context; - struct wined3d_bo_address data; - struct wined3d_box level_box; - struct wined3d_map_desc map; - struct wined3d_range range; - bool full_subresource; - unsigned int level; - DWORD map_binding; - - TRACE("texture %p, sub_resource_idx %u, box %s, colour %s.\n", - texture, sub_resource_idx, debug_box(box), debug_color(colour)); - - if (texture->resource.format_attrs & WINED3D_FORMAT_ATTR_BLOCKS) - { - FIXME("Not implemented for format %s.\n", debug_d3dformat(texture->resource.format->id)); - return; - } - - context = context_acquire(device, NULL, 0); - - level = sub_resource_idx % texture->level_count; - wined3d_texture_get_level_box(texture, level, &level_box); - full_subresource = !memcmp(box, &level_box, sizeof(*box)); - - map_binding = texture->resource.map_binding; - if (!wined3d_texture_load_location(texture, sub_resource_idx, context, map_binding)) - ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding)); - wined3d_texture_invalidate_location(texture, sub_resource_idx, ~map_binding); - wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch); - wined3d_texture_get_bo_address(texture, sub_resource_idx, &data, map_binding); - map.data = wined3d_context_map_bo_address(context, &data, - texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_WRITE); - range.offset = 0; - range.size = texture->sub_resources[sub_resource_idx].size; - - wined3d_resource_memory_colour_fill(&texture->resource, &map, colour, box, full_subresource); - - wined3d_context_unmap_bo_address(context, &data, 1, &range); - context_release(context); -} - -static void cpu_blitter_clear_rtv(struct wined3d_rendertarget_view *view, - const struct wined3d_box *box, const struct wined3d_color *colour) -{ - if (view->format->id != view->resource->format->id) - FIXME("View format %s doesn't match resource format %s.\n", - debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id)); - - if (view->resource->type == WINED3D_RTYPE_BUFFER) - { - FIXME("Not implemented for buffers.\n"); - return; - } - - cpu_blitter_clear_texture(texture_from_resource(view->resource), view->sub_resource_idx, box, colour); -} - -static bool wined3d_box_intersect(struct wined3d_box *ret, const struct wined3d_box *b1, - const struct wined3d_box *b2) -{ - wined3d_box_set(ret, max(b1->left, b2->left), max(b1->top, b2->top), - min(b1->right, b2->right), min(b1->bottom, b2->bottom), - max(b1->front, b2->front), min(b1->back, b2->back)); - return ret->right > ret->left && ret->bottom > ret->top && ret->back > ret->front; -} - -static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, - unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, - const RECT *draw_rect, uint32_t flags, const struct wined3d_color *colour, float depth, unsigned int stencil) -{ - struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f}; - struct wined3d_box box, box_clip, box_view; - struct wined3d_rendertarget_view *view; - unsigned int i, j; - - if (!rect_count) - { - rect_count = 1; - clear_rects = draw_rect; - } - - for (i = 0; i < rect_count; ++i) - { - box.left = max(clear_rects[i].left, draw_rect->left); - box.top = max(clear_rects[i].top, draw_rect->top); - box.right = min(clear_rects[i].right, draw_rect->right); - box.bottom = min(clear_rects[i].bottom, draw_rect->bottom); - box.front = 0; - box.back = ~0u; - - if (box.left >= box.right || box.top >= box.bottom) - continue; - - if (flags & WINED3DCLEAR_TARGET) - { - for (j = 0; j < rt_count; ++j) - { - if ((view = fb->render_targets[j])) - { - wined3d_rendertarget_view_get_box(view, &box_view); - if (wined3d_box_intersect(&box_clip, &box_view, &box)) - cpu_blitter_clear_rtv(view, &box_clip, colour); - } - } - } - - if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)) - { - if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER)) - || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL))) - FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id)); - - wined3d_rendertarget_view_get_box(view, &box_view); - if (wined3d_box_intersect(&box_clip, &box_view, &box)) - cpu_blitter_clear_rtv(view, &box_clip, &c); - } - } -} - -static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, - struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, - DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, - unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, - const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter, - const struct wined3d_format *resolve_format) -{ - struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1}; - struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1}; - struct wined3d_blt_fx fx; - DWORD flags = 0; - - memset(&fx, 0, sizeof(fx)); - switch (op) - { - case WINED3D_BLIT_OP_COLOR_BLIT: - case WINED3D_BLIT_OP_DEPTH_BLIT: - break; - case WINED3D_BLIT_OP_RAW_BLIT: - flags |= WINED3D_BLT_RAW; - break; - case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: - flags |= WINED3D_BLT_ALPHA_TEST; - break; - case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: - flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX; - fx.src_color_key = *color_key; - break; - default: - FIXME("Unhandled op %#x.\n", op); - break; - } - - if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box, - src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter))) - ERR("Failed to blit.\n"); - wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location); - - return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations - & dst_texture->resource.map_binding); -} - -static const struct wined3d_blitter_ops cpu_blitter_ops = -{ - cpu_blitter_destroy, - cpu_blitter_clear, - cpu_blitter_blit, -}; - -struct wined3d_blitter *wined3d_cpu_blitter_create(void) -{ - struct wined3d_blitter *blitter; - - if (!(blitter = malloc(sizeof(*blitter)))) - return NULL; - - TRACE("Created blitter %p.\n", blitter); - - blitter->ops = &cpu_blitter_ops; - blitter->next = NULL; - - return blitter; -} - -static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op) -{ - switch (blit_op) - { - case WINED3D_BLIT_OP_COLOR_BLIT: - case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: - case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: - return true; - - default: - return false; - } -} - -static bool sub_resource_is_on_cpu(const struct wined3d_texture *texture, unsigned int sub_resource_idx) -{ - DWORD locations = texture->sub_resources[sub_resource_idx].locations; - - if (locations & (WINED3D_LOCATION_BUFFER | WINED3D_LOCATION_SYSMEM)) - return true; - - if (!(texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) && (locations & WINED3D_LOCATION_CLEARED)) - return true; - - return false; -} - -HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, - const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, - const struct wined3d_box *src_box, uint32_t flags, const struct wined3d_blt_fx *fx, - enum wined3d_texture_filter_type filter) -{ - struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource; - struct wined3d_device *device = dst_texture->resource.device; - struct wined3d_swapchain *src_swapchain, *dst_swapchain; - BOOL scale, convert, resolve, resolve_typeless = FALSE; - const struct wined3d_format *resolve_format = NULL; - const struct wined3d_color_key *colour_key = NULL; - DWORD src_location, dst_location, valid_locations; - struct wined3d_context *context; - enum wined3d_blit_op blit_op; - RECT src_rect, dst_rect; - bool src_ds, dst_ds; - - static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY - | WINED3D_BLT_SRC_CKEY_OVERRIDE - | WINED3D_BLT_ALPHA_TEST - | WINED3D_BLT_RAW; - - TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, " - "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n", - dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx, - debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter)); - TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage)); - - if (fx) - { - TRACE("fx %#x.\n", fx->fx); - TRACE("dst_color_key {0x%08x, 0x%08x}.\n", - fx->dst_color_key.color_space_low_value, - fx->dst_color_key.color_space_high_value); - TRACE("src_color_key {0x%08x, 0x%08x}.\n", - fx->src_color_key.color_space_low_value, - fx->src_color_key.color_space_high_value); - TRACE("resolve_format_id %s.\n", debug_d3dformat(fx->resolve_format_id)); - - if (fx->resolve_format_id != WINED3DFMT_UNKNOWN) - resolve_format = wined3d_get_format(device->adapter, fx->resolve_format_id, 0); - } - - dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx]; - src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx]; - - if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED) - { - WARN("Source sub-resource is discarded, nothing to do.\n"); - return WINED3D_OK; - } - - SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom); - SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom); - - if (!fx || !(fx->fx)) - flags &= ~WINED3D_BLT_FX; - - /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */ - if (flags & WINED3D_BLT_DO_NOT_WAIT) - { - static unsigned int once; - - if (!once++) - FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n"); - } - - flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT); - - if (flags & ~simple_blit) - { - WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags); - goto cpu; - } - - src_swapchain = src_texture->swapchain; - dst_swapchain = dst_texture->swapchain; - - if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain - && src_texture == src_swapchain->front_buffer) - { - /* TODO: We could support cross-swapchain blits by first downloading - * the source to a texture. */ - FIXME("Cross-swapchain blit not supported.\n"); - return WINED3DERR_INVALIDCALL; - } - - scale = src_box->right - src_box->left != dst_box->right - dst_box->left - || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top; - convert = src_texture->resource.format->id != dst_texture->resource.format->id; - resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type; - if (resolve) - { - resolve_typeless = (wined3d_format_is_typeless(src_texture->resource.format) - || wined3d_format_is_typeless(dst_texture->resource.format)) - && (src_texture->resource.format->typeless_id == dst_texture->resource.format->typeless_id); - if (resolve_typeless && !resolve_format) - WARN("Resolve format for typeless resolve not specified.\n"); - } - - dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size; - src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size; - - if (src_ds || dst_ds) - { - TRACE("Depth/stencil blit.\n"); - - if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) - dst_location = dst_texture->resource.draw_binding; - else - dst_location = dst_texture->resource.map_binding; - - if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve)) - blit_op = WINED3D_BLIT_OP_RAW_BLIT; - else - blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; - - context = context_acquire(device, dst_texture, dst_sub_resource_idx); - valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context, - src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect, - dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter, resolve_format); - context_release(context); - - wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations); - wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations); - - return WINED3D_OK; - } - - TRACE("Colour blit.\n"); - - /* In principle this would apply to depth blits as well, but we don't - * implement those in the CPU blitter at the moment. */ - if ((dst_sub_resource->locations & dst_texture->resource.map_binding) - && (src_sub_resource->locations & src_texture->resource.map_binding)) - { - if (scale) - TRACE("Not doing sysmem blit because of scaling.\n"); - else if (convert) - TRACE("Not doing sysmem blit because of format conversion.\n"); - else - goto cpu; - } - - blit_op = WINED3D_BLIT_OP_COLOR_BLIT; - if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE) - { - colour_key = &fx->src_color_key; - blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY; - } - else if (flags & WINED3D_BLT_SRC_CKEY) - { - colour_key = &src_texture->async.src_blt_color_key; - blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY; - } - else if (flags & WINED3D_BLT_ALPHA_TEST) - { - blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST; - } - else if ((src_sub_resource->locations & WINED3D_LOCATION_CLEARED) - && wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect)) - { - TRACE("Source is cleared.\n"); - wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_CLEARED); - wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_CLEARED); - dst_sub_resource->clear_value.colour = src_sub_resource->clear_value.colour; - return WINED3D_OK; - } - else if (sub_resource_is_on_cpu(src_texture, src_sub_resource_idx) - && !sub_resource_is_on_cpu(dst_texture, dst_sub_resource_idx) - && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) - { - /* Upload */ - if (scale) - TRACE("Not doing upload because of scaling.\n"); - else if (convert) - TRACE("Not doing upload because of format conversion.\n"); - else if (dst_texture->resource.format->conv_byte_count) - TRACE("Not doing upload because the destination format needs conversion.\n"); - else - { - wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left, - dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box); - if (!wined3d_resource_is_offscreen(&dst_texture->resource)) - { - context = context_acquire(device, dst_texture, dst_sub_resource_idx); - wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, - context, dst_texture->resource.draw_binding); - context_release(context); - } - return WINED3D_OK; - } - } - else if (!(src_sub_resource->locations & (WINED3D_LOCATION_BUFFER | WINED3D_LOCATION_SYSMEM)) - && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) - { - /* Download */ - if (scale) - TRACE("Not doing download because of scaling.\n"); - else if (convert) - TRACE("Not doing download because of format conversion.\n"); - else if (src_texture->resource.format->conv_byte_count) - TRACE("Not doing download because the source format needs conversion.\n"); - else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE)) - TRACE("Not doing download because texture is not downloadable.\n"); - else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect)) - TRACE("Not doing download because of partial download (src).\n"); - else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect)) - TRACE("Not doing download because of partial download (dst).\n"); - else - { - wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture, - src_sub_resource_idx); - return WINED3D_OK; - } - } - else if (dst_swapchain && dst_swapchain->back_buffers - && dst_texture == dst_swapchain->front_buffer - && src_texture == dst_swapchain->back_buffers[0]) - { - /* Use present for back -> front blits. The idea behind this is that - * present is potentially faster than a blit, in particular when FBO - * blits aren't available. Some ddraw applications like Half-Life and - * Prince of Persia 3D use Blt() from the backbuffer to the - * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications - * can't blit directly to the frontbuffer. */ - enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect; - - TRACE("Using present for backbuffer -> frontbuffer blit.\n"); - - /* Set the swap effect to COPY, we don't want the backbuffer to become - * undefined. */ - dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY; - wined3d_swapchain_present(dst_swapchain, NULL, NULL, - dst_swapchain->win_handle, dst_swapchain->swap_interval, 0); - dst_swapchain->state.desc.swap_effect = swap_effect; - - return WINED3D_OK; - } - - if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve)) - blit_op = WINED3D_BLIT_OP_RAW_BLIT; - - context = context_acquire(device, dst_texture, dst_sub_resource_idx); - - if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE && !resolve_typeless - && ((scale && !context->d3d_info->scaled_resolve) - || convert || !wined3d_is_colour_blit(blit_op))) - src_location = WINED3D_LOCATION_RB_RESOLVED; - else - src_location = src_texture->resource.draw_binding; - - if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) - dst_location = dst_texture->resource.map_binding; - else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE - && (scale || convert || !wined3d_is_colour_blit(blit_op))) - dst_location = WINED3D_LOCATION_RB_RESOLVED; - else - dst_location = dst_texture->resource.draw_binding; - - valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context, - src_texture, src_sub_resource_idx, src_location, &src_rect, - dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter, resolve_format); - - context_release(context); - - wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations); - wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations); - - return WINED3D_OK; - -cpu: - return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box, - src_texture, src_sub_resource_idx, src_box, flags, fx, filter); -} +/* + * Copyright 1997-2000 Marcus Meissner + * Copyright 1998-2000 Lionel Ulmer + * Copyright 2000-2001 TransGaming Technologies Inc. + * Copyright 2002-2005 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Christian Costa + * Copyright 2005 Oliver Stieber + * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers + * Copyright 2007-2008 Henri Verbeet + * Copyright 2006-2008 Roderick Colenbrander + * Copyright 2009-2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wined3d_private.h" +#include "wined3d_gl.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +/* Works correctly only for <= 4 bpp formats. */ +static void get_color_masks(const struct wined3d_format *format, uint32_t *masks) +{ + masks[0] = wined3d_mask_from_size(format->red_size) << format->red_offset; + masks[1] = wined3d_mask_from_size(format->green_size) << format->green_offset; + masks[2] = wined3d_mask_from_size(format->blue_size) << format->blue_offset; +} + +static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst, + unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) +{ + unsigned short *dst_s; + const float *src_f; + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + src_f = (const float *)(src + y * pitch_in); + dst_s = (unsigned short *) (dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + dst_s[x] = float_32_to_16(src_f + x); + } + } +} + +static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst, + unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) +{ + static const unsigned char convert_5to8[] = + { + 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a, + 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b, + 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd, + 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff, + }; + static const unsigned char convert_6to8[] = + { + 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, + 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d, + 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d, + 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d, + 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e, + 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe, + 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf, + 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff, + }; + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const WORD *src_line = (const WORD *)(src + y * pitch_in); + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + WORD pixel = src_line[x]; + dst_line[x] = 0xff000000u + | convert_5to8[(pixel & 0xf800u) >> 11] << 16 + | convert_6to8[(pixel & 0x07e0u) >> 5] << 8 + | convert_5to8[(pixel & 0x001fu)]; + } + } +} + +/* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since + * in both cases we're just setting the X / Alpha channel to 0xff. */ +static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst, + unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) +{ + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const DWORD *src_line = (const DWORD *)(src + y * pitch_in); + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + + for (x = 0; x < w; ++x) + { + dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff); + } + } +} + +static inline BYTE cliptobyte(int x) +{ + return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x)); +} + +static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst, + unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) +{ + int c2, d, e, r2 = 0, g2 = 0, b2 = 0; + unsigned int x, y; + + TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const BYTE *src_line = src + y * pitch_in; + DWORD *dst_line = (DWORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV: + * C = Y - 16; D = U - 128; E = V - 128; + * R = cliptobyte((298 * C + 409 * E + 128) >> 8); + * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8); + * B = cliptobyte((298 * C + 516 * D + 128) >> 8); + * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V . + * U and V are shared between the pixels. */ + if (!(x & 1)) /* For every even pixel, read new U and V. */ + { + d = (int) src_line[1] - 128; + e = (int) src_line[3] - 128; + r2 = 409 * e + 128; + g2 = - 100 * d - 208 * e + 128; + b2 = 516 * d + 128; + } + c2 = 298 * ((int) src_line[0] - 16); + dst_line[x] = 0xff000000 + | cliptobyte((c2 + r2) >> 8) << 16 /* red */ + | cliptobyte((c2 + g2) >> 8) << 8 /* green */ + | cliptobyte((c2 + b2) >> 8); /* blue */ + /* Scale RGB values to 0..255 range, + * then clip them if still not in range (may be negative), + * then shift them within DWORD if necessary. */ + src_line += 2; + } + } +} + +static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst, + unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h) +{ + unsigned int x, y; + int c2, d, e, r2 = 0, g2 = 0, b2 = 0; + + TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out); + + for (y = 0; y < h; ++y) + { + const BYTE *src_line = src + y * pitch_in; + WORD *dst_line = (WORD *)(dst + y * pitch_out); + for (x = 0; x < w; ++x) + { + /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV: + * C = Y - 16; D = U - 128; E = V - 128; + * R = cliptobyte((298 * C + 409 * E + 128) >> 8); + * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8); + * B = cliptobyte((298 * C + 516 * D + 128) >> 8); + * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V . + * U and V are shared between the pixels. */ + if (!(x & 1)) /* For every even pixel, read new U and V. */ + { + d = (int) src_line[1] - 128; + e = (int) src_line[3] - 128; + r2 = 409 * e + 128; + g2 = - 100 * d - 208 * e + 128; + b2 = 516 * d + 128; + } + c2 = 298 * ((int) src_line[0] - 16); + dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */ + | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */ + | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */ + /* Scale RGB values to 0..255 range, + * then clip them if still not in range (may be negative), + * then shift them within DWORD if necessary. */ + src_line += 2; + } + } +} + +struct d3dfmt_converter_desc +{ + enum wined3d_format_id from, to; + void (*convert)(const BYTE *src, BYTE *dst, + unsigned int pitch_in, unsigned int pitch_out, + unsigned int w, unsigned int h); +}; + +static const struct d3dfmt_converter_desc converters[] = +{ + {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float}, + {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8}, + {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8}, + {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8}, + {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8}, + {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5}, +}; + +static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from, + enum wined3d_format_id to) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(converters); ++i) + { + if (converters[i].from == from && converters[i].to == to) + return &converters[i]; + } + + return NULL; +} + +static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture, + unsigned int sub_resource_idx, const struct wined3d_format *dst_format) +{ + unsigned int texture_level = sub_resource_idx % src_texture->level_count; + const struct wined3d_format *src_format = src_texture->resource.format; + struct wined3d_device *device = src_texture->resource.device; + const struct d3dfmt_converter_desc *conv = NULL; + unsigned int src_row_pitch, src_slice_pitch; + struct wined3d_texture *dst_texture; + struct wined3d_bo_address src_data; + struct wined3d_resource_desc desc; + struct wined3d_context *context; + DWORD map_binding; + + if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D) + || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count + || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count + || ((src_format->attrs & WINED3D_FORMAT_ATTR_COMPRESSED) + && !src_format->decompress))) + { + FIXME("Cannot find a conversion function from format %s to %s.\n", + debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); + return NULL; + } + + /* FIXME: Multisampled conversion? */ + desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + desc.format = dst_format->id; + desc.multisample_type = WINED3D_MULTISAMPLE_NONE; + desc.multisample_quality = 0; + desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_CS; + desc.bind_flags = 0; + desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; + desc.width = wined3d_texture_get_level_width(src_texture, texture_level); + desc.height = wined3d_texture_get_level_height(src_texture, texture_level); + desc.depth = 1; + desc.size = 0; + if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD, + NULL, NULL, &wined3d_null_parent_ops, &dst_texture))) + { + ERR("Failed to create a destination texture for conversion.\n"); + return NULL; + } + + context = context_acquire(device, NULL, 0); + + map_binding = src_texture->resource.map_binding; + if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding)) + ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch); + wined3d_texture_get_bo_address(src_texture, sub_resource_idx, &src_data, map_binding); + + if (conv) + { + unsigned int dst_row_pitch, dst_slice_pitch; + struct wined3d_bo_address dst_data; + struct wined3d_range range; + const BYTE *src; + BYTE *dst; + + map_binding = dst_texture->resource.map_binding; + if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch); + wined3d_texture_get_bo_address(dst_texture, 0, &dst_data, map_binding); + + src = wined3d_context_map_bo_address(context, &src_data, + src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ); + dst = wined3d_context_map_bo_address(context, &dst_data, + dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE); + + conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height); + + range.offset = 0; + range.size = dst_texture->sub_resources[0].size; + wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding); + wined3d_context_unmap_bo_address(context, &dst_data, 1, &range); + wined3d_context_unmap_bo_address(context, &src_data, 0, NULL); + } + else + { + struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1}; + + TRACE("Using upload conversion.\n"); + + wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB); + dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data), + src_format, &src_box, src_row_pitch, src_slice_pitch, + dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0); + + wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB); + } + + context_release(context); + + return dst_texture; +} + +void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx, + struct wined3d_context *context, DWORD src_location, DWORD dst_location) +{ + struct wined3d_resource *resource = &texture->resource; + struct wined3d_device *device = resource->device; + const struct wined3d_format_gl *format_gl; + struct wined3d_texture *restore_texture; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + unsigned int row_pitch, slice_pitch; + unsigned int width, height, level; + struct wined3d_bo_address data; + bool restore_context = false; + unsigned int restore_idx; + BYTE *row, *top, *bottom; + BOOL src_is_upside_down; + BYTE *mem = NULL; + uint8_t *offset; + unsigned int i; + + TRACE("texture %p, sub_resource_idx %u, context %p, src_location %s, dst_location %s.\n", + texture, sub_resource_idx, context, wined3d_debug_location(src_location), wined3d_debug_location(dst_location)); + + /* dst_location was already prepared by the caller. */ + wined3d_texture_get_bo_address(texture, sub_resource_idx, &data, dst_location); + offset = data.addr; + + restore_texture = context->current_rt.texture; + restore_idx = context->current_rt.sub_resource_idx; + if (!wined3d_resource_is_offscreen(resource) && (restore_texture != texture || restore_idx != sub_resource_idx)) + { + context = context_acquire(device, texture, sub_resource_idx); + restore_context = true; + } + context_gl = wined3d_context_gl(context); + gl_info = context_gl->gl_info; + + if (resource->format->depth_size || resource->format->stencil_size) + wined3d_context_gl_apply_fbo_state_explicit(context_gl, GL_READ_FRAMEBUFFER, + NULL, 0, resource, sub_resource_idx, src_location); + else + wined3d_context_gl_apply_fbo_state_explicit(context_gl, GL_READ_FRAMEBUFFER, + resource, sub_resource_idx, NULL, 0, src_location); + + /* Select the correct read buffer, and give some debug output. There is no + * need to keep track of the current read buffer or reset it, every part + * of the code that reads pixels sets the read buffer as desired. */ + if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource)) + { + /* Mapping the primary render target which is not on a swapchain. + * Read from the back buffer. */ + TRACE("Mapping offscreen render target.\n"); + gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl)); + src_is_upside_down = TRUE; + } + else + { + /* Onscreen surfaces are always part of a swapchain */ + GLenum buffer = wined3d_texture_get_gl_buffer(texture); + TRACE("Mapping %#x buffer.\n", buffer); + gl_info->gl_ops.gl.p_glReadBuffer(buffer); + src_is_upside_down = FALSE; + } + checkGLcall("glReadBuffer"); + wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER); + + if (data.buffer_object) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, wined3d_bo_gl(data.buffer_object)->id)); + checkGLcall("glBindBuffer"); + offset += data.buffer_object->buffer_offset; + } + else + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + checkGLcall("glBindBuffer"); + } + + level = sub_resource_idx % texture->level_count; + wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch); + format_gl = wined3d_format_gl(resource->format); + + /* Setup pixel store pack state -- to glReadPixels into the correct place */ + gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count); + checkGLcall("glPixelStorei"); + + width = wined3d_texture_get_level_width(texture, level); + height = wined3d_texture_get_level_height(texture, level); + gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height, + format_gl->format, format_gl->type, offset); + checkGLcall("glReadPixels"); + + /* Reset previous pixel store pack state */ + gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0); + checkGLcall("glPixelStorei"); + + if (!src_is_upside_down) + { + /* glReadPixels returns the image upside down, and there is no way to + * prevent this. Flip the lines in software. */ + + if (!(row = malloc(row_pitch))) + goto error; + + if (data.buffer_object) + { + mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE)); + checkGLcall("glMapBuffer"); + } + mem += (uintptr_t)offset; + + top = mem; + bottom = mem + row_pitch * (height - 1); + for (i = 0; i < height / 2; i++) + { + memcpy(row, top, row_pitch); + memcpy(top, bottom, row_pitch); + memcpy(bottom, row, row_pitch); + top += row_pitch; + bottom -= row_pitch; + } + free(row); + + if (data.buffer_object) + GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER)); + } + +error: + if (data.buffer_object) + { + GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + wined3d_context_gl_reference_bo(context_gl, wined3d_bo_gl(data.buffer_object)); + checkGLcall("glBindBuffer"); + } + + if (restore_context) + context_restore(context, restore_texture, restore_idx); +} + +/* Context activation is done by the caller. */ +static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context) +{ + struct wined3d_blitter *next; + + if ((next = blitter->next)) + next->ops->blitter_destroy(next, context); + + free(blitter); +} + +static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data, + UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h, + const struct wined3d_format *format, uint32_t flags, const struct wined3d_blt_fx *fx) +{ + UINT row_block_count; + const BYTE *src_row; + BYTE *dst_row; + UINT x, y; + + src_row = src_data; + dst_row = dst_data; + + row_block_count = (update_w + format->block_width - 1) / format->block_width; + + if (!flags) + { + for (y = 0; y < update_h; y += format->block_height) + { + memcpy(dst_row, src_row, row_block_count * format->block_byte_count); + src_row += src_pitch; + dst_row += dst_pitch; + } + + return WINED3D_OK; + } + + if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN) + { + src_row += (((update_h / format->block_height) - 1) * src_pitch); + + switch (format->id) + { + case WINED3DFMT_DXT1: + for (y = 0; y < update_h; y += format->block_height) + { + struct block + { + WORD color[2]; + BYTE control_row[4]; + }; + + const struct block *s = (const struct block *)src_row; + struct block *d = (struct block *)dst_row; + + for (x = 0; x < row_block_count; ++x) + { + d[x].color[0] = s[x].color[0]; + d[x].color[1] = s[x].color[1]; + d[x].control_row[0] = s[x].control_row[3]; + d[x].control_row[1] = s[x].control_row[2]; + d[x].control_row[2] = s[x].control_row[1]; + d[x].control_row[3] = s[x].control_row[0]; + } + src_row -= src_pitch; + dst_row += dst_pitch; + } + return WINED3D_OK; + + case WINED3DFMT_DXT2: + case WINED3DFMT_DXT3: + for (y = 0; y < update_h; y += format->block_height) + { + struct block + { + WORD alpha_row[4]; + WORD color[2]; + BYTE control_row[4]; + }; + + const struct block *s = (const struct block *)src_row; + struct block *d = (struct block *)dst_row; + + for (x = 0; x < row_block_count; ++x) + { + d[x].alpha_row[0] = s[x].alpha_row[3]; + d[x].alpha_row[1] = s[x].alpha_row[2]; + d[x].alpha_row[2] = s[x].alpha_row[1]; + d[x].alpha_row[3] = s[x].alpha_row[0]; + d[x].color[0] = s[x].color[0]; + d[x].color[1] = s[x].color[1]; + d[x].control_row[0] = s[x].control_row[3]; + d[x].control_row[1] = s[x].control_row[2]; + d[x].control_row[2] = s[x].control_row[1]; + d[x].control_row[3] = s[x].control_row[0]; + } + src_row -= src_pitch; + dst_row += dst_pitch; + } + return WINED3D_OK; + + default: + FIXME("Compressed flip not implemented for format %s.\n", + debug_d3dformat(format->id)); + return E_NOTIMPL; + } + } + + FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n", + debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0); + + return E_NOTIMPL; +} + +static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + const struct wined3d_box *src_box, uint32_t flags, const struct wined3d_blt_fx *fx, + enum wined3d_texture_filter_type filter) +{ + unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count; + struct wined3d_device *device = dst_texture->resource.device; + const struct wined3d_format *src_format, *dst_format; + struct wined3d_texture *converted_texture = NULL; + struct wined3d_bo_address src_data, dst_data; + unsigned int src_fmt_attrs, dst_fmt_attrs; + struct wined3d_map_desc dst_map, src_map; + unsigned int x, sx, xinc, y, sy, yinc; + struct wined3d_context *context; + struct wined3d_range dst_range; + unsigned int texture_level; + BYTE *tmp_buffer = NULL; + HRESULT hr = WINED3D_OK; + BOOL same_sub_resource; + BOOL upload = FALSE; + DWORD map_binding; + const BYTE *sbase; + const BYTE *sbuf; + BYTE *dbuf; + + TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, " + "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n", + dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, + src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter)); + + context = context_acquire(device, NULL, 0); + + src_format = src_texture->resource.format; + dst_format = dst_texture->resource.format; + + if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id) + src_format = dst_format; + if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id) + dst_format = src_format; + + src_height = src_box->bottom - src_box->top; + src_width = src_box->right - src_box->left; + dst_height = dst_box->bottom - dst_box->top; + dst_width = dst_box->right - dst_box->left; + + dst_range.offset = 0; + dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size; + if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx) + { + same_sub_resource = TRUE; + + map_binding = dst_texture->resource.map_binding; + texture_level = dst_sub_resource_idx % dst_texture->level_count; + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding); + wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch); + wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding); + dst_map.data = wined3d_context_map_bo_address(context, &dst_data, + dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE); + + src_map = dst_map; + } + else + { + same_sub_resource = FALSE; + upload = dst_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS + && (dst_width != src_width || dst_height != src_height); + + if (upload) + { + dst_format = src_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS + ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format; + } + + if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id) + { + if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format))) + { + FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id), + debug_d3dformat(dst_format->id)); + context_release(context); + return WINED3DERR_NOTAVAILABLE; + } + src_texture = converted_texture; + src_sub_resource_idx = 0; + src_format = src_texture->resource.format; + } + + map_binding = src_texture->resource.map_binding; + texture_level = src_sub_resource_idx % src_texture->level_count; + if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding)) + ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch); + wined3d_texture_get_bo_address(src_texture, src_sub_resource_idx, &src_data, map_binding); + src_map.data = wined3d_context_map_bo_address(context, &src_data, + src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ); + + if (upload) + { + wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom, + &dst_map.row_pitch, &dst_map.slice_pitch); + dst_map.data = malloc(dst_map.slice_pitch); + } + else + { + map_binding = dst_texture->resource.map_binding; + texture_level = dst_sub_resource_idx % dst_texture->level_count; + if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding)) + ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding)); + + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding); + wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch); + wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding); + dst_map.data = wined3d_context_map_bo_address(context, &dst_data, + dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE); + } + } + src_fmt_attrs = src_format->attrs; + dst_fmt_attrs = dst_format->attrs; + flags &= ~WINED3D_BLT_RAW; + + bpp = dst_format->byte_count; + row_byte_count = dst_width * bpp; + + sbase = (BYTE *)src_map.data + + ((src_box->top / src_format->block_height) * src_map.row_pitch) + + ((src_box->left / src_format->block_width) * src_format->block_byte_count); + dbuf = (BYTE *)dst_map.data + + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch) + + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count); + + if (src_fmt_attrs & dst_fmt_attrs & WINED3D_FORMAT_ATTR_BLOCKS) + { + TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); + + if (same_sub_resource) + { + FIXME("Only plain blits supported on compressed surfaces.\n"); + hr = E_NOTIMPL; + goto release; + } + + hr = surface_cpu_blt_compressed(sbase, dbuf, + src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height, + src_format, flags, fx); + goto release; + } + + if ((src_fmt_attrs | dst_fmt_attrs) & WINED3D_FORMAT_ATTR_HEIGHT_SCALE) + { + FIXME("Unsupported blit between height-scaled formats (src %s, dst %s).\n", + debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id)); + hr = E_NOTIMPL; + goto release; + } + + if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT + && (src_width != dst_width || src_height != dst_height)) + { + /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */ + FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter)); + } + + xinc = (src_width << 16) / dst_width; + yinc = (src_height << 16) / dst_height; + + if (!flags) + { + /* No effects, we can cheat here. */ + if (dst_width == src_width) + { + if (dst_height == src_height) + { + /* No stretching in either direction. This needs to be as fast + * as possible. */ + sbuf = sbase; + + /* Check for overlapping surfaces. */ + if (!same_sub_resource || dst_box->top < src_box->top + || dst_box->right <= src_box->left || src_box->right <= dst_box->left) + { + /* No overlap, or dst above src, so copy from top downwards. */ + for (y = 0; y < dst_height; ++y) + { + memcpy(dbuf, sbuf, row_byte_count); + sbuf += src_map.row_pitch; + dbuf += dst_map.row_pitch; + } + } + else if (dst_box->top > src_box->top) + { + /* Copy from bottom upwards. */ + sbuf += src_map.row_pitch * dst_height; + dbuf += dst_map.row_pitch * dst_height; + for (y = 0; y < dst_height; ++y) + { + sbuf -= src_map.row_pitch; + dbuf -= dst_map.row_pitch; + memcpy(dbuf, sbuf, row_byte_count); + } + } + else + { + /* Src and dst overlapping on the same line, use memmove. */ + for (y = 0; y < dst_height; ++y) + { + memmove(dbuf, sbuf, row_byte_count); + sbuf += src_map.row_pitch; + dbuf += dst_map.row_pitch; + } + } + } + else + { + /* Stretching in y direction only. */ + + if (same_sub_resource) + { + /* Use a temporary buffer if blitting a surface to itself. */ + if ((tmp_buffer = malloc(src_height * src_map.row_pitch))) + { + memcpy(tmp_buffer, sbase, src_height * src_map.row_pitch); + sbase = tmp_buffer; + } + } + + for (y = sy = 0; y < dst_height; ++y, sy += yinc) + { + sbuf = sbase + (sy >> 16) * src_map.row_pitch; + memcpy(dbuf, sbuf, row_byte_count); + dbuf += dst_map.row_pitch; + } + + if (same_sub_resource) + free(tmp_buffer); + } + } + else + { + /* Stretching in X direction. */ + unsigned int last_sy = ~0u; + + if (same_sub_resource) + { + /* Use a temporary buffer if blitting a surface to itself. */ + if ((tmp_buffer = malloc(src_height * src_map.row_pitch))) + { + memcpy(tmp_buffer, sbase, src_height * src_map.row_pitch); + sbase = tmp_buffer; + } + } + + for (y = sy = 0; y < dst_height; ++y, sy += yinc) + { + sbuf = sbase + (sy >> 16) * src_map.row_pitch; + + if ((sy >> 16) == (last_sy >> 16)) + { + /* This source row is the same as last source row - + * Copy the already stretched row. */ + memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count); + } + else + { +#define STRETCH_ROW(type) \ +do { \ + const type *s = (const type *)sbuf; \ + type *d = (type *)dbuf; \ + for (x = sx = 0; x < dst_width; ++x, sx += xinc) \ + d[x] = s[sx >> 16]; \ +} while(0) + + switch(bpp) + { + case 1: + STRETCH_ROW(BYTE); + break; + case 2: + STRETCH_ROW(WORD); + break; + case 4: + STRETCH_ROW(DWORD); + break; + case 3: + { + const BYTE *s; + BYTE *d = dbuf; + for (x = sx = 0; x < dst_width; x++, sx+= xinc) + { + DWORD pixel; + + s = sbuf + 3 * (sx >> 16); + pixel = s[0] | (s[1] << 8) | (s[2] << 16); + d[0] = (pixel ) & 0xff; + d[1] = (pixel >> 8) & 0xff; + d[2] = (pixel >> 16) & 0xff; + d += 3; + } + break; + } + default: + FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8); + hr = WINED3DERR_NOTAVAILABLE; + goto error; + } +#undef STRETCH_ROW + } + dbuf += dst_map.row_pitch; + last_sy = sy; + } + + if (same_sub_resource) + free(tmp_buffer); + } + } + else + { + LONG dstyinc = dst_map.row_pitch, dstxinc = bpp; + DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff; + DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff; + if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY + | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE)) + { + /* The color keying flags are checked for correctness in ddraw. */ + if (flags & WINED3D_BLT_SRC_CKEY) + { + keylow = src_texture->async.src_blt_color_key.color_space_low_value; + keyhigh = src_texture->async.src_blt_color_key.color_space_high_value; + } + else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE) + { + keylow = fx->src_color_key.color_space_low_value; + keyhigh = fx->src_color_key.color_space_high_value; + } + + if (flags & WINED3D_BLT_DST_CKEY) + { + /* Destination color keys are taken from the source surface! */ + destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value; + destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value; + } + else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE) + { + destkeylow = fx->dst_color_key.color_space_low_value; + destkeyhigh = fx->dst_color_key.color_space_high_value; + } + + if (bpp == 1) + { + keymask = 0xff; + } + else + { + uint32_t masks[3]; + get_color_masks(src_format, masks); + keymask = masks[0] | masks[1] | masks[2]; + } + flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY + | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE); + } + + if (flags & WINED3D_BLT_FX) + { + BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp; + LONG tmpxy; + dTopLeft = dbuf; + dTopRight = dbuf + ((dst_width - 1) * bpp); + dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch); + dBottomRight = dBottomLeft + ((dst_width - 1) * bpp); + + if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY) + { + /* I don't think we need to do anything about this flag. */ + WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n"); + } + if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT) + { + tmp = dTopRight; + dTopRight = dTopLeft; + dTopLeft = tmp; + tmp = dBottomRight; + dBottomRight = dBottomLeft; + dBottomLeft = tmp; + dstxinc = dstxinc * -1; + } + if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN) + { + tmp = dTopLeft; + dTopLeft = dBottomLeft; + dBottomLeft = tmp; + tmp = dTopRight; + dTopRight = dBottomRight; + dBottomRight = tmp; + dstyinc = dstyinc * -1; + } + if (fx->fx & WINEDDBLTFX_NOTEARING) + { + /* I don't think we need to do anything about this flag. */ + WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n"); + } + if (fx->fx & WINEDDBLTFX_ROTATE180) + { + tmp = dBottomRight; + dBottomRight = dTopLeft; + dTopLeft = tmp; + tmp = dBottomLeft; + dBottomLeft = dTopRight; + dTopRight = tmp; + dstxinc = dstxinc * -1; + dstyinc = dstyinc * -1; + } + if (fx->fx & WINEDDBLTFX_ROTATE270) + { + tmp = dTopLeft; + dTopLeft = dBottomLeft; + dBottomLeft = dBottomRight; + dBottomRight = dTopRight; + dTopRight = tmp; + tmpxy = dstxinc; + dstxinc = dstyinc; + dstyinc = tmpxy; + dstxinc = dstxinc * -1; + } + if (fx->fx & WINEDDBLTFX_ROTATE90) + { + tmp = dTopLeft; + dTopLeft = dTopRight; + dTopRight = dBottomRight; + dBottomRight = dBottomLeft; + dBottomLeft = tmp; + tmpxy = dstxinc; + dstxinc = dstyinc; + dstyinc = tmpxy; + dstyinc = dstyinc * -1; + } + if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST) + { + /* I don't think we need to do anything about this flag. */ + WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n"); + } + dbuf = dTopLeft; + flags &= ~(WINED3D_BLT_FX); + } + +#define COPY_COLORKEY_FX(type) \ +do { \ + const type *s; \ + type *d = (type *)dbuf, *dx, tmp; \ + for (y = sy = 0; y < dst_height; ++y, sy += yinc) \ + { \ + s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \ + dx = d; \ + for (x = sx = 0; x < dst_width; ++x, sx += xinc) \ + { \ + tmp = s[sx >> 16]; \ + if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \ + && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \ + { \ + dx[0] = tmp; \ + } \ + dx = (type *)(((BYTE *)dx) + dstxinc); \ + } \ + d = (type *)(((BYTE *)d) + dstyinc); \ + } \ +} while(0) + + switch (bpp) + { + case 1: + COPY_COLORKEY_FX(BYTE); + break; + case 2: + COPY_COLORKEY_FX(WORD); + break; + case 4: + COPY_COLORKEY_FX(DWORD); + break; + case 3: + { + const BYTE *s; + BYTE *d = dbuf, *dx; + for (y = sy = 0; y < dst_height; ++y, sy += yinc) + { + sbuf = sbase + (sy >> 16) * src_map.row_pitch; + dx = d; + for (x = sx = 0; x < dst_width; ++x, sx+= xinc) + { + DWORD pixel, dpixel = 0; + s = sbuf + 3 * (sx>>16); + pixel = s[0] | (s[1] << 8) | (s[2] << 16); + dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16); + if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh) + && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh)) + { + dx[0] = (pixel ) & 0xff; + dx[1] = (pixel >> 8) & 0xff; + dx[2] = (pixel >> 16) & 0xff; + } + dx += dstxinc; + } + d += dstyinc; + } + break; + } + default: + FIXME("%s color-keyed blit not implemented for bpp %u.\n", + (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8); + hr = WINED3DERR_NOTAVAILABLE; + goto error; +#undef COPY_COLORKEY_FX + } + } + +error: + if (flags) + FIXME(" Unsupported flags %#x.\n", flags); + +release: + if (upload && hr == WINED3D_OK) + { + struct wined3d_bo_address data; + + data.buffer_object = 0; + data.addr = dst_map.data; + + texture_level = dst_sub_resource_idx % dst_texture->level_count; + + wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB); + dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format, + dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level, + WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0); + + wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB); + } + + if (upload) + { + free(dst_map.data); + } + else + { + wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range); + } + + if (!same_sub_resource) + wined3d_context_unmap_bo_address(context, &src_data, 0, NULL); + if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture) + { + SetRect(&dst_texture->swapchain->front_buffer_update, + dst_box->left, dst_box->top, dst_box->right, dst_box->bottom); + dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain); + } + if (converted_texture) + wined3d_texture_decref(converted_texture); + context_release(context); + + return hr; +} + +void cpu_blitter_clear_texture(struct wined3d_texture *texture, unsigned int sub_resource_idx, + const struct wined3d_box *box, const struct wined3d_color *colour) +{ + struct wined3d_device *device = texture->resource.device; + struct wined3d_context *context; + struct wined3d_bo_address data; + struct wined3d_box level_box; + struct wined3d_map_desc map; + struct wined3d_range range; + bool full_subresource; + unsigned int level; + DWORD map_binding; + + TRACE("texture %p, sub_resource_idx %u, box %s, colour %s.\n", + texture, sub_resource_idx, debug_box(box), debug_color(colour)); + + if (texture->resource.format_attrs & WINED3D_FORMAT_ATTR_BLOCKS) + { + FIXME("Not implemented for format %s.\n", debug_d3dformat(texture->resource.format->id)); + return; + } + + context = context_acquire(device, NULL, 0); + + level = sub_resource_idx % texture->level_count; + wined3d_texture_get_level_box(texture, level, &level_box); + full_subresource = !memcmp(box, &level_box, sizeof(*box)); + + map_binding = texture->resource.map_binding; + if (!wined3d_texture_load_location(texture, sub_resource_idx, context, map_binding)) + ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding)); + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~map_binding); + wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch); + wined3d_texture_get_bo_address(texture, sub_resource_idx, &data, map_binding); + map.data = wined3d_context_map_bo_address(context, &data, + texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_WRITE); + range.offset = 0; + range.size = texture->sub_resources[sub_resource_idx].size; + + wined3d_resource_memory_colour_fill(&texture->resource, &map, colour, box, full_subresource); + + wined3d_context_unmap_bo_address(context, &data, 1, &range); + context_release(context); +} + +static void cpu_blitter_clear_rtv(struct wined3d_rendertarget_view *view, + const struct wined3d_box *box, const struct wined3d_color *colour) +{ + if (view->format->id != view->resource->format->id) + FIXME("View format %s doesn't match resource format %s.\n", + debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id)); + + if (view->resource->type == WINED3D_RTYPE_BUFFER) + { + FIXME("Not implemented for buffers.\n"); + return; + } + + cpu_blitter_clear_texture(texture_from_resource(view->resource), view->sub_resource_idx, box, colour); +} + +static bool wined3d_box_intersect(struct wined3d_box *ret, const struct wined3d_box *b1, + const struct wined3d_box *b2) +{ + wined3d_box_set(ret, max(b1->left, b2->left), max(b1->top, b2->top), + min(b1->right, b2->right), min(b1->bottom, b2->bottom), + max(b1->front, b2->front), min(b1->back, b2->back)); + return ret->right > ret->left && ret->bottom > ret->top && ret->back > ret->front; +} + +static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device, + unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects, + const RECT *draw_rect, uint32_t flags, const struct wined3d_color *colour, float depth, unsigned int stencil) +{ + struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f}; + struct wined3d_box box, box_clip, box_view; + struct wined3d_rendertarget_view *view; + unsigned int i, j; + + if (!rect_count) + { + rect_count = 1; + clear_rects = draw_rect; + } + + for (i = 0; i < rect_count; ++i) + { + box.left = max(clear_rects[i].left, draw_rect->left); + box.top = max(clear_rects[i].top, draw_rect->top); + box.right = min(clear_rects[i].right, draw_rect->right); + box.bottom = min(clear_rects[i].bottom, draw_rect->bottom); + box.front = 0; + box.back = ~0u; + + if (box.left >= box.right || box.top >= box.bottom) + continue; + + if (flags & WINED3DCLEAR_TARGET) + { + for (j = 0; j < rt_count; ++j) + { + if ((view = fb->render_targets[j])) + { + wined3d_rendertarget_view_get_box(view, &box_view); + if (wined3d_box_intersect(&box_clip, &box_view, &box)) + cpu_blitter_clear_rtv(view, &box_clip, colour); + } + } + } + + if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)) + { + if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER)) + || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL))) + FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id)); + + wined3d_rendertarget_view_get_box(view, &box_view); + if (wined3d_box_intersect(&box_clip, &box_view, &box)) + cpu_blitter_clear_rtv(view, &box_clip, &c); + } + } +} + +static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op, + struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture, + unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect, + const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter, + const struct wined3d_format *resolve_format) +{ + struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1}; + struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1}; + struct wined3d_blt_fx fx; + DWORD flags = 0; + + memset(&fx, 0, sizeof(fx)); + switch (op) + { + case WINED3D_BLIT_OP_COLOR_BLIT: + case WINED3D_BLIT_OP_DEPTH_BLIT: + break; + case WINED3D_BLIT_OP_RAW_BLIT: + flags |= WINED3D_BLT_RAW; + break; + case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: + flags |= WINED3D_BLT_ALPHA_TEST; + break; + case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: + flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX; + fx.src_color_key = *color_key; + break; + default: + FIXME("Unhandled op %#x.\n", op); + break; + } + + if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box, + src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter))) + ERR("Failed to blit.\n"); + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location); + + return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations + & dst_texture->resource.map_binding); +} + +static const struct wined3d_blitter_ops cpu_blitter_ops = +{ + cpu_blitter_destroy, + cpu_blitter_clear, + cpu_blitter_blit, +}; + +struct wined3d_blitter *wined3d_cpu_blitter_create(void) +{ + struct wined3d_blitter *blitter; + + if (!(blitter = malloc(sizeof(*blitter)))) + return NULL; + + TRACE("Created blitter %p.\n", blitter); + + blitter->ops = &cpu_blitter_ops; + blitter->next = NULL; + + return blitter; +} + +static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op) +{ + switch (blit_op) + { + case WINED3D_BLIT_OP_COLOR_BLIT: + case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST: + case WINED3D_BLIT_OP_COLOR_BLIT_CKEY: + return true; + + default: + return false; + } +} + +static bool sub_resource_is_on_cpu(const struct wined3d_texture *texture, unsigned int sub_resource_idx) +{ + DWORD locations = texture->sub_resources[sub_resource_idx].locations; + + if (locations & (WINED3D_LOCATION_BUFFER | WINED3D_LOCATION_SYSMEM)) + return true; + + if (!(texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) && (locations & WINED3D_LOCATION_CLEARED)) + return true; + + return false; +} + +HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, + const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, + const struct wined3d_box *src_box, uint32_t flags, const struct wined3d_blt_fx *fx, + enum wined3d_texture_filter_type filter) +{ + struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource; + struct wined3d_device *device = dst_texture->resource.device; + struct wined3d_swapchain *src_swapchain, *dst_swapchain; + BOOL scale, convert, resolve, resolve_typeless = FALSE; + const struct wined3d_format *resolve_format = NULL; + const struct wined3d_color_key *colour_key = NULL; + DWORD src_location, dst_location, valid_locations; + struct wined3d_context *context; + enum wined3d_blit_op blit_op; + RECT src_rect, dst_rect; + bool src_ds, dst_ds; + + static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY + | WINED3D_BLT_SRC_CKEY_OVERRIDE + | WINED3D_BLT_ALPHA_TEST + | WINED3D_BLT_RAW; + + TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, " + "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n", + dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx, + debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter)); + TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage)); + + if (fx) + { + TRACE("fx %#x.\n", fx->fx); + TRACE("dst_color_key {0x%08x, 0x%08x}.\n", + fx->dst_color_key.color_space_low_value, + fx->dst_color_key.color_space_high_value); + TRACE("src_color_key {0x%08x, 0x%08x}.\n", + fx->src_color_key.color_space_low_value, + fx->src_color_key.color_space_high_value); + TRACE("resolve_format_id %s.\n", debug_d3dformat(fx->resolve_format_id)); + + if (fx->resolve_format_id != WINED3DFMT_UNKNOWN) + resolve_format = wined3d_get_format(device->adapter, fx->resolve_format_id, 0); + } + + dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx]; + src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx]; + + if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED) + { + WARN("Source sub-resource is discarded, nothing to do.\n"); + return WINED3D_OK; + } + + SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom); + SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom); + + if (!fx || !(fx->fx)) + flags &= ~WINED3D_BLT_FX; + + /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */ + if (flags & WINED3D_BLT_DO_NOT_WAIT) + { + static unsigned int once; + + if (!once++) + FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n"); + } + + flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT); + + if (flags & ~simple_blit) + { + WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags); + goto cpu; + } + + src_swapchain = src_texture->swapchain; + dst_swapchain = dst_texture->swapchain; + + if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain + && src_texture == src_swapchain->front_buffer) + { + /* TODO: We could support cross-swapchain blits by first downloading + * the source to a texture. */ + FIXME("Cross-swapchain blit not supported.\n"); + return WINED3DERR_INVALIDCALL; + } + + scale = src_box->right - src_box->left != dst_box->right - dst_box->left + || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top; + convert = src_texture->resource.format->id != dst_texture->resource.format->id; + resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type; + if (resolve) + { + resolve_typeless = (wined3d_format_is_typeless(src_texture->resource.format) + || wined3d_format_is_typeless(dst_texture->resource.format)) + && (src_texture->resource.format->typeless_id == dst_texture->resource.format->typeless_id); + if (resolve_typeless && !resolve_format) + WARN("Resolve format for typeless resolve not specified.\n"); + } + + dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size; + src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size; + + if (src_ds || dst_ds) + { + TRACE("Depth/stencil blit.\n"); + + if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) + dst_location = dst_texture->resource.draw_binding; + else + dst_location = dst_texture->resource.map_binding; + + if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve)) + blit_op = WINED3D_BLIT_OP_RAW_BLIT; + else + blit_op = WINED3D_BLIT_OP_DEPTH_BLIT; + + context = context_acquire(device, dst_texture, dst_sub_resource_idx); + valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context, + src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect, + dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter, resolve_format); + context_release(context); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations); + + return WINED3D_OK; + } + + TRACE("Colour blit.\n"); + + /* In principle this would apply to depth blits as well, but we don't + * implement those in the CPU blitter at the moment. */ + if ((dst_sub_resource->locations & dst_texture->resource.map_binding) + && (src_sub_resource->locations & src_texture->resource.map_binding)) + { + if (scale) + TRACE("Not doing sysmem blit because of scaling.\n"); + else if (convert) + TRACE("Not doing sysmem blit because of format conversion.\n"); + else + goto cpu; + } + + blit_op = WINED3D_BLIT_OP_COLOR_BLIT; + if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE) + { + colour_key = &fx->src_color_key; + blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY; + } + else if (flags & WINED3D_BLT_SRC_CKEY) + { + colour_key = &src_texture->async.src_blt_color_key; + blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY; + } + else if (flags & WINED3D_BLT_ALPHA_TEST) + { + blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST; + } + else if ((src_sub_resource->locations & WINED3D_LOCATION_CLEARED) + && wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect)) + { + TRACE("Source is cleared.\n"); + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_CLEARED); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_CLEARED); + dst_sub_resource->clear_value.colour = src_sub_resource->clear_value.colour; + return WINED3D_OK; + } + else if (sub_resource_is_on_cpu(src_texture, src_sub_resource_idx) + && !sub_resource_is_on_cpu(dst_texture, dst_sub_resource_idx) + && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + /* Upload */ + if (scale) + TRACE("Not doing upload because of scaling.\n"); + else if (convert) + TRACE("Not doing upload because of format conversion.\n"); + else if (dst_texture->resource.format->conv_byte_count) + TRACE("Not doing upload because the destination format needs conversion.\n"); + else + { + wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left, + dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box); + if (!wined3d_resource_is_offscreen(&dst_texture->resource)) + { + context = context_acquire(device, dst_texture, dst_sub_resource_idx); + wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, + context, dst_texture->resource.draw_binding); + context_release(context); + } + return WINED3D_OK; + } + } + else if (!(src_sub_resource->locations & (WINED3D_LOCATION_BUFFER | WINED3D_LOCATION_SYSMEM)) + && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + { + /* Download */ + if (scale) + TRACE("Not doing download because of scaling.\n"); + else if (convert) + TRACE("Not doing download because of format conversion.\n"); + else if (src_texture->resource.format->conv_byte_count) + TRACE("Not doing download because the source format needs conversion.\n"); + else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE)) + TRACE("Not doing download because texture is not downloadable.\n"); + else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect)) + TRACE("Not doing download because of partial download (src).\n"); + else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect)) + TRACE("Not doing download because of partial download (dst).\n"); + else + { + wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture, + src_sub_resource_idx); + return WINED3D_OK; + } + } + else if (dst_swapchain && dst_swapchain->back_buffers + && dst_texture == dst_swapchain->front_buffer + && src_texture == dst_swapchain->back_buffers[0].texture) + { + /* Use present for back -> front blits. The idea behind this is that + * present is potentially faster than a blit, in particular when FBO + * blits aren't available. Some ddraw applications like Half-Life and + * Prince of Persia 3D use Blt() from the backbuffer to the + * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications + * can't blit directly to the frontbuffer. */ + enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect; + + TRACE("Using present for backbuffer -> frontbuffer blit.\n"); + + /* Set the swap effect to COPY, we don't want the backbuffer to become + * undefined. */ + dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY; + wined3d_swapchain_present(dst_swapchain, NULL, NULL, + dst_swapchain->win_handle, dst_swapchain->swap_interval, 0); + dst_swapchain->state.desc.swap_effect = swap_effect; + + return WINED3D_OK; + } + + if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve)) + blit_op = WINED3D_BLIT_OP_RAW_BLIT; + + context = context_acquire(device, dst_texture, dst_sub_resource_idx); + + if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE && !resolve_typeless + && ((scale && !context->d3d_info->scaled_resolve) + || convert || !wined3d_is_colour_blit(blit_op))) + src_location = WINED3D_LOCATION_RB_RESOLVED; + else + src_location = src_texture->resource.draw_binding; + + if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)) + dst_location = dst_texture->resource.map_binding; + else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE + && (scale || convert || !wined3d_is_colour_blit(blit_op))) + dst_location = WINED3D_LOCATION_RB_RESOLVED; + else + dst_location = dst_texture->resource.draw_binding; + + valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context, + src_texture, src_sub_resource_idx, src_location, &src_rect, + dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter, resolve_format); + + context_release(context); + + wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations); + wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations); + + return WINED3D_OK; + +cpu: + return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box, + src_texture, src_sub_resource_idx, src_box, flags, fx, filter); +} diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index a1000ec2393..a59936fd468 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -1,2517 +1,2542 @@ -/* - * Copyright 2002-2003 Jason Edmeades - * Copyright 2002-2003 Raphael Junqueira - * Copyright 2005 Oliver Stieber - * Copyright 2007-2008 Stefan Dösinger for CodeWeavers - * Copyright 2011 Henri Verbeet for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wined3d_private.h" -#include "wined3d_gl.h" -#include "wined3d_vk.h" - -WINE_DEFAULT_DEBUG_CHANNEL(d3d); -WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); - -void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) -{ - HRESULT hr; - UINT i; - - TRACE("Destroying swapchain %p.\n", swapchain); - - wined3d_swapchain_state_cleanup(&swapchain->state); - wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma); - - /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0] - * is the last buffer to be destroyed, FindContext() depends on that. */ - if (swapchain->front_buffer) - { - wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); - if (wined3d_texture_decref(swapchain->front_buffer)) - WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer); - swapchain->front_buffer = NULL; - } - - if (swapchain->back_buffers) - { - i = swapchain->state.desc.backbuffer_count; - - while (i--) - { - wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); - if (wined3d_texture_decref(swapchain->back_buffers[i])) - WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]); - } - free(swapchain->back_buffers); - swapchain->back_buffers = NULL; - } - - /* Restore the screen resolution if we rendered in fullscreen. - * This will restore the screen resolution to what it was before creating - * the swapchain. In case of d3d8 and d3d9 this will be the original - * desktop resolution. In case of d3d7 this will be a NOP because ddraw - * sets the resolution before starting up Direct3D, thus orig_width and - * orig_height will be equal to the modes in the presentation params. */ - if (!swapchain->state.desc.windowed) - { - if (swapchain->state.desc.auto_restore_display_mode) - { - if (FAILED(hr = wined3d_restore_display_modes(swapchain->device->wined3d))) - ERR("Failed to restore display mode, hr %#lx.\n", hr); - - if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) - { - wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, - swapchain->state.device_window, &swapchain->state.original_window_rect); - wined3d_device_release_focus_window(swapchain->device); - } - } - else - { - wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, swapchain->state.device_window, NULL); - } - } -} - -void wined3d_swapchain_gl_cleanup(struct wined3d_swapchain_gl *swapchain_gl) -{ - wined3d_swapchain_cleanup(&swapchain_gl->s); -} - -static void wined3d_swapchain_vk_destroy_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk) -{ - struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); - const struct wined3d_vk_info *vk_info; - unsigned int i; - VkResult vr; - - TRACE("swapchain_vk %p.\n", swapchain_vk); - - vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; - - if ((vr = VK_CALL(vkQueueWaitIdle(device_vk->graphics_queue.vk_queue))) < 0) - ERR("Failed to wait on queue, vr %s.\n", wined3d_debug_vkresult(vr)); - free(swapchain_vk->vk_images); - for (i = 0; i < swapchain_vk->image_count; ++i) - { - VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL)); - VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL)); - } - free(swapchain_vk->vk_semaphores); - VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, NULL)); - VK_CALL(vkDestroySurfaceKHR(vk_info->instance, swapchain_vk->vk_surface, NULL)); -} - -static void wined3d_swapchain_vk_destroy_object(void *object) -{ - wined3d_swapchain_vk_destroy_vulkan_swapchain(object); -} - -void wined3d_swapchain_vk_cleanup(struct wined3d_swapchain_vk *swapchain_vk) -{ - struct wined3d_cs *cs = swapchain_vk->s.device->cs; - - wined3d_cs_destroy_object(cs, wined3d_swapchain_vk_destroy_object, swapchain_vk); - wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); - - wined3d_swapchain_cleanup(&swapchain_vk->s); -} - -ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain) -{ - unsigned int refcount = InterlockedIncrement(&swapchain->ref); - - TRACE("%p increasing refcount to %u.\n", swapchain, refcount); - - return refcount; -} - -ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain) -{ - unsigned int refcount = InterlockedDecrement(&swapchain->ref); - - TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); - - if (!refcount) - { - struct wined3d_device *device; - - wined3d_mutex_lock(); - - device = swapchain->device; - if (device->swapchain_count && device->swapchains[0] == swapchain) - wined3d_device_uninit_3d(device); - wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); - - swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent); - swapchain->device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain); - - wined3d_mutex_unlock(); - } - - return refcount; -} - -void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain) -{ - TRACE("swapchain %p.\n", swapchain); - - return swapchain->parent; -} - -void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window) -{ - if (!window) - window = swapchain->state.device_window; - if (window == swapchain->win_handle) - return; - - TRACE("Setting swapchain %p window from %p to %p.\n", - swapchain, swapchain->win_handle, window); - - wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); - - swapchain->win_handle = window; -} - -HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, - const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, - unsigned int swap_interval, uint32_t flags) -{ - RECT s, d; - - TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, swap_interval %u, flags %#x.\n", - swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect), - dst_window_override, swap_interval, flags); - - if (flags) - FIXME("Ignoring flags %#x.\n", flags); - - wined3d_mutex_lock(); - - if (!swapchain->back_buffers) - { - WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL.\n"); - wined3d_mutex_unlock(); - return WINED3DERR_INVALIDCALL; - } - - if (!src_rect) - { - SetRect(&s, 0, 0, swapchain->state.desc.backbuffer_width, - swapchain->state.desc.backbuffer_height); - src_rect = &s; - } - - if (!dst_rect) - { - GetClientRect(swapchain->win_handle, &d); - dst_rect = &d; - } - - wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect, - dst_rect, dst_window_override, swap_interval, flags); - - wined3d_mutex_unlock(); - - return WINED3D_OK; -} - -HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain, - struct wined3d_texture *dst_texture, unsigned int sub_resource_idx) -{ - RECT src_rect, dst_rect; - - TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx); - - SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height); - dst_rect = src_rect; - - if (swapchain->state.desc.windowed) - { - MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2); - FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n", - wine_dbgstr_rect(&dst_rect)); - } - - return wined3d_device_context_blt(&swapchain->device->cs->c, dst_texture, sub_resource_idx, &dst_rect, - swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT); -} - -struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain, - UINT back_buffer_idx) -{ - TRACE("swapchain %p, back_buffer_idx %u.\n", - swapchain, back_buffer_idx); - - /* Return invalid if there is no backbuffer array, otherwise it will - * crash when ddraw is used (there swapchain->back_buffers is always - * NULL). We need this because this function is called from - * stateblock_init_default_state() to get the default scissorrect - * dimensions. */ - if (!swapchain->back_buffers || back_buffer_idx >= swapchain->state.desc.backbuffer_count) - { - WARN("Invalid back buffer index.\n"); - /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it - * here in wined3d to avoid problems in other libs. */ - return NULL; - } - - TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx]); - - return swapchain->back_buffers[back_buffer_idx]; -} - -struct wined3d_texture * CDECL wined3d_swapchain_get_front_buffer(const struct wined3d_swapchain *swapchain) -{ - TRACE("swapchain %p.\n", swapchain); - - return swapchain->front_buffer; -} - -struct wined3d_output * wined3d_swapchain_get_output(const struct wined3d_swapchain *swapchain) -{ - TRACE("swapchain %p.\n", swapchain); - - return swapchain->state.desc.output; -} - -HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain, - struct wined3d_raster_status *raster_status) -{ - struct wined3d_output *output; - - TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status); - - output = wined3d_swapchain_get_output(swapchain); - if (!output) - { - ERR("Failed to get output from swapchain %p.\n", swapchain); - return E_FAIL; - } - - return wined3d_output_get_raster_status(output, raster_status); -} - -struct wined3d_swapchain_state * CDECL wined3d_swapchain_get_state(struct wined3d_swapchain *swapchain) -{ - return &swapchain->state; -} - -HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain, - struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation) -{ - struct wined3d_output *output; - HRESULT hr; - - TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation); - - if (!(output = wined3d_swapchain_get_output(swapchain))) - { - ERR("Failed to get output from swapchain %p.\n", swapchain); - return E_FAIL; - } - - hr = wined3d_output_get_display_mode(output, mode, rotation); - - TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n", - mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id)); - - return hr; -} - -struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain) -{ - TRACE("swapchain %p.\n", swapchain); - - return swapchain->device; -} - -void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain, - struct wined3d_swapchain_desc *desc) -{ - TRACE("swapchain %p, desc %p.\n", swapchain, desc); - - *desc = swapchain->state.desc; -} - -HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain, - uint32_t flags, const struct wined3d_gamma_ramp *ramp) -{ - struct wined3d_output *output; - - TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp); - - if (flags) - FIXME("Ignoring flags %#x.\n", flags); - - if (!(output = wined3d_swapchain_get_output(swapchain))) - { - ERR("Failed to get output from swapchain %p.\n", swapchain); - return E_FAIL; - } - - return wined3d_output_set_gamma_ramp(output, ramp); -} - -void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette) -{ - TRACE("swapchain %p, palette %p.\n", swapchain, palette); - - wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); - - swapchain->palette = palette; -} - -HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain, - struct wined3d_gamma_ramp *ramp) -{ - struct wined3d_output *output; - - TRACE("swapchain %p, ramp %p.\n", swapchain, ramp); - - if (!(output = wined3d_swapchain_get_output(swapchain))) - { - ERR("Failed to get output from swapchain %p.\n", swapchain); - return E_FAIL; - } - - return wined3d_output_get_gamma_ramp(output, ramp); -} - -/* The is a fallback for cases where we e.g. can't create a GL context or - * Vulkan swapchain for the swapchain window. */ -static void swapchain_blit_gdi(struct wined3d_swapchain *swapchain, - struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect) -{ - struct wined3d_texture *back_buffer = swapchain->back_buffers[0]; - D3DKMT_DESTROYDCFROMMEMORY destroy_desc; - D3DKMT_CREATEDCFROMMEMORY create_desc; - const struct wined3d_format *format; - unsigned int row_pitch, slice_pitch; - HDC src_dc, dst_dc; - NTSTATUS status; - HBITMAP bitmap; - - static unsigned int once; - - TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n", - swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect)); - - if (!once++) - FIXME("Using GDI present.\n"); - - format = back_buffer->resource.format; - if (!format->ddi_format) - { - WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id)); - return; - } - - wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_SYSMEM); - wined3d_texture_get_pitch(back_buffer, 0, &row_pitch, &slice_pitch); - - create_desc.pMemory = back_buffer->resource.heap_memory; - create_desc.Format = format->ddi_format; - create_desc.Width = wined3d_texture_get_level_width(back_buffer, 0); - create_desc.Height = wined3d_texture_get_level_height(back_buffer, 0); - create_desc.Pitch = row_pitch; - create_desc.hDeviceDc = CreateCompatibleDC(NULL); - create_desc.pColorTable = NULL; - - status = D3DKMTCreateDCFromMemory(&create_desc); - DeleteDC(create_desc.hDeviceDc); - if (status) - { - WARN("Failed to create DC, status %#lx.\n", status); - return; - } - - src_dc = create_desc.hDc; - bitmap = create_desc.hBitmap; - - TRACE("Created source DC %p, bitmap %p for backbuffer %p.\n", src_dc, bitmap, back_buffer); - - if (!(dst_dc = GetDCEx(swapchain->win_handle, 0, DCX_USESTYLE | DCX_CACHE))) - ERR("Failed to get destination DC.\n"); - - if (!StretchBlt(dst_dc, dst_rect->left, dst_rect->top, dst_rect->right - dst_rect->left, - dst_rect->bottom - dst_rect->top, src_dc, src_rect->left, src_rect->top, - src_rect->right - src_rect->left, src_rect->bottom - src_rect->top, SRCCOPY)) - ERR("Failed to blit.\n"); - - ReleaseDC(swapchain->win_handle, dst_dc); - destroy_desc.hDc = src_dc; - destroy_desc.hBitmap = bitmap; - if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc))) - ERR("Failed to destroy src dc, status %#lx.\n", status); -} - -/* A GL context is provided by the caller */ -static void swapchain_blit(const struct wined3d_swapchain *swapchain, - struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect) -{ - struct wined3d_texture *texture = swapchain->back_buffers[0]; - struct wined3d_device *device = swapchain->device; - enum wined3d_texture_filter_type filter; - DWORD location; - - TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n", - swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect)); - - if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left - && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top) - || is_complex_fixup(texture->resource.format->color_fixup)) - filter = WINED3D_TEXF_NONE; - else - filter = WINED3D_TEXF_LINEAR; - - location = WINED3D_LOCATION_TEXTURE_RGB; - if (texture->resource.multisample_type) - location = WINED3D_LOCATION_RB_RESOLVED; - - wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE); - device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, texture, 0, - location, src_rect, texture, 0, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter, NULL); - wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE); -} - -static void swapchain_gl_set_swap_interval(struct wined3d_swapchain *swapchain, - struct wined3d_context_gl *context_gl, unsigned int swap_interval) -{ - const struct wined3d_gl_info *gl_info = context_gl->gl_info; - - swap_interval = swap_interval <= 4 ? swap_interval : 1; - if (swapchain->swap_interval == swap_interval) - return; - - swapchain->swap_interval = swap_interval; - - if (!gl_info->supported[WGL_EXT_SWAP_CONTROL]) - return; - - if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval))) - { - ERR("Failed to set swap interval %u for context %p, last error %#lx.\n", - swap_interval, context_gl, GetLastError()); - } -} - -/* Context activation is done by the caller. */ -static void wined3d_swapchain_gl_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context) -{ - struct wined3d_texture_sub_resource *sub_resource; - struct wined3d_texture_gl *texture, *texture_prev; - struct gl_texture tex0; - GLuint rb0; - DWORD locations0; - unsigned int i; - static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE; - - if (swapchain->state.desc.backbuffer_count < 2) - return; - - texture_prev = wined3d_texture_gl(swapchain->back_buffers[0]); - - /* Back buffer 0 is already in the draw binding. */ - tex0 = texture_prev->texture_rgb; - rb0 = texture_prev->rb_multisample; - locations0 = texture_prev->t.sub_resources[0].locations; - - for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i) - { - texture = wined3d_texture_gl(swapchain->back_buffers[i]); - sub_resource = &texture->t.sub_resources[0]; - - if (!(sub_resource->locations & supported_locations)) - wined3d_texture_load_location(&texture->t, 0, context, texture->t.resource.draw_binding); - - texture_prev->texture_rgb = texture->texture_rgb; - texture_prev->rb_multisample = texture->rb_multisample; - - wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations); - wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations)); - - texture_prev = texture; - } - - texture_prev->texture_rgb = tex0; - texture_prev->rb_multisample = rb0; - - wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations); - wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations)); - - device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER); -} - -static bool swapchain_present_is_partial_copy(struct wined3d_swapchain *swapchain, const RECT *dst_rect) -{ - enum wined3d_swap_effect swap_effect = swapchain->state.desc.swap_effect; - RECT client_rect; - unsigned int t; - - if (swap_effect != WINED3D_SWAP_EFFECT_COPY && swap_effect != WINED3D_SWAP_EFFECT_COPY_VSYNC) - return false; - - GetClientRect(swapchain->win_handle, &client_rect); - - t = client_rect.right - client_rect.left; - if ((dst_rect->left && dst_rect->right) || abs(dst_rect->right - dst_rect->left) != t) - return true; - t = client_rect.bottom - client_rect.top; - if ((dst_rect->top && dst_rect->bottom) || abs(dst_rect->bottom - dst_rect->top) != t) - return true; - - return false; -} - -static void swapchain_gl_present(struct wined3d_swapchain *swapchain, - const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) -{ - struct wined3d_texture *back_buffer = swapchain->back_buffers[0]; - const struct wined3d_pixel_format *pixel_format; - const struct wined3d_gl_info *gl_info; - struct wined3d_context_gl *context_gl; - struct wined3d_context *context; - - context = context_acquire(swapchain->device, swapchain->front_buffer, 0); - context_gl = wined3d_context_gl(context); - if (!context_gl->valid) - { - context_release(context); - WARN("Invalid context, skipping present.\n"); - return; - } - - TRACE("Presenting DC %p.\n", context_gl->dc); - - pixel_format = &wined3d_adapter_gl(swapchain->device->adapter)->pixel_formats[context_gl->pixel_format]; - if (context_gl->dc == wined3d_device_gl(swapchain->device)->backup_dc - || (pixel_format->swap_method != WGL_SWAP_COPY_ARB - && swapchain_present_is_partial_copy(swapchain, dst_rect))) - { - swapchain_blit_gdi(swapchain, context, src_rect, dst_rect); - } - else - { - gl_info = context_gl->gl_info; - - swapchain_gl_set_swap_interval(swapchain, context_gl, swap_interval); - - wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding); - - swapchain_blit(swapchain, context, src_rect, dst_rect); - - if (swapchain->device->context_count > 1) - { - WARN_(d3d_perf)("Multiple contexts, calling glFinish() to enforce ordering.\n"); - gl_info->gl_ops.gl.p_glFinish(); - } - - /* call wglSwapBuffers through the gl table to avoid confusing the Steam overlay */ - gl_info->gl_ops.wgl.p_wglSwapBuffers(context_gl->dc); - } - - if (context->d3d_info->fences) - wined3d_context_gl_submit_command_fence(context_gl); - - wined3d_swapchain_gl_rotate(swapchain, context); - - TRACE("SwapBuffers called, Starting new frame\n"); - - wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); - wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); - - context_release(context); -} - -static void swapchain_frontbuffer_updated(struct wined3d_swapchain *swapchain) -{ - struct wined3d_texture *front_buffer = swapchain->front_buffer; - struct wined3d_context *context; - - context = context_acquire(swapchain->device, front_buffer, 0); - wined3d_texture_load_location(front_buffer, 0, context, front_buffer->resource.draw_binding); - context_release(context); - SetRectEmpty(&swapchain->front_buffer_update); -} - -static const struct wined3d_swapchain_ops swapchain_gl_ops = -{ - swapchain_gl_present, - swapchain_frontbuffer_updated, -}; - -static bool wined3d_swapchain_vk_present_mode_supported(struct wined3d_swapchain_vk *swapchain_vk, - VkPresentModeKHR vk_present_mode) -{ - struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); - const struct wined3d_vk_info *vk_info; - struct wined3d_adapter_vk *adapter_vk; - VkPhysicalDevice vk_physical_device; - VkPresentModeKHR *vk_modes; - bool supported = false; - uint32_t count, i; - VkResult vr; - - adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); - vk_physical_device = adapter_vk->physical_device; - vk_info = &adapter_vk->vk_info; - - if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device, - swapchain_vk->vk_surface, &count, NULL))) < 0) - { - ERR("Failed to get supported present mode count, vr %s.\n", wined3d_debug_vkresult(vr)); - return false; - } - - if (!(vk_modes = calloc(count, sizeof(*vk_modes)))) - return false; - - if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device, - swapchain_vk->vk_surface, &count, vk_modes))) < 0) - { - ERR("Failed to get supported present modes, vr %s.\n", wined3d_debug_vkresult(vr)); - goto done; - } - - for (i = 0; i < count; ++i) - { - if (vk_modes[i] == vk_present_mode) - { - supported = true; - goto done; - } - } - -done: - free(vk_modes); - return supported; -} - -static VkFormat get_swapchain_fallback_format(VkFormat vk_format) -{ - switch (vk_format) - { - case VK_FORMAT_R8G8B8A8_SRGB: - return VK_FORMAT_B8G8R8A8_SRGB; - case VK_FORMAT_R8G8B8A8_UNORM: - case VK_FORMAT_A2B10G10R10_UNORM_PACK32: - case VK_FORMAT_R16G16B16A16_SFLOAT: - return VK_FORMAT_B8G8R8A8_UNORM; - default: - WARN("Unhandled format %#x.\n", vk_format); - return VK_FORMAT_UNDEFINED; - } -} - -static VkFormat wined3d_swapchain_vk_select_vk_format(struct wined3d_swapchain_vk *swapchain_vk, - VkSurfaceKHR vk_surface) -{ - struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); - const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; - const struct wined3d_vk_info *vk_info; - struct wined3d_adapter_vk *adapter_vk; - const struct wined3d_format *format; - VkPhysicalDevice vk_physical_device; - VkSurfaceFormatKHR *vk_formats; - uint32_t format_count, i; - VkFormat vk_format; - VkResult vr; - - adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); - vk_physical_device = adapter_vk->physical_device; - vk_info = &adapter_vk->vk_info; - - if ((format = wined3d_get_format(&adapter_vk->a, desc->backbuffer_format, WINED3D_BIND_RENDER_TARGET))) - vk_format = wined3d_format_vk(format)->vk_format; - else - vk_format = VK_FORMAT_B8G8R8A8_UNORM; - - vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, vk_surface, &format_count, NULL)); - if (vr < 0 || !format_count) - { - WARN("Failed to get supported surface format count, vr %s.\n", wined3d_debug_vkresult(vr)); - return VK_FORMAT_UNDEFINED; - } - - if (!(vk_formats = calloc(format_count, sizeof(*vk_formats)))) - return VK_FORMAT_UNDEFINED; - - if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, - vk_surface, &format_count, vk_formats))) < 0) - { - WARN("Failed to get supported surface formats, vr %s.\n", wined3d_debug_vkresult(vr)); - free(vk_formats); - return VK_FORMAT_UNDEFINED; - } - - for (i = 0; i < format_count; ++i) - { - if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - break; - } - if (i == format_count) - { - /* Try to create a swapchain with format conversion. */ - vk_format = get_swapchain_fallback_format(vk_format); - WARN("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format)); - for (i = 0; i < format_count; ++i) - { - if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - break; - } - } - free(vk_formats); - if (i == format_count) - { - FIXME("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format)); - return VK_FORMAT_UNDEFINED; - } - - TRACE("Using Vulkan swapchain format %#x.\n", vk_format); - - return vk_format; -} - -static bool wined3d_swapchain_vk_create_vulkan_swapchain_images(struct wined3d_swapchain_vk *swapchain_vk, - VkSwapchainKHR vk_swapchain) -{ - struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); - const struct wined3d_vk_info *vk_info; - VkSemaphoreCreateInfo semaphore_info; - uint32_t image_count, i; - VkResult vr; - - vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; - - if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, vk_swapchain, &image_count, NULL))) < 0) - { - ERR("Failed to get image count, vr %s\n", wined3d_debug_vkresult(vr)); - return false; - } - - if (!(swapchain_vk->vk_images = calloc(image_count, sizeof(*swapchain_vk->vk_images)))) - { - ERR("Failed to allocate images array.\n"); - return false; - } - - if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, - vk_swapchain, &image_count, swapchain_vk->vk_images))) < 0) - { - ERR("Failed to get swapchain images, vr %s.\n", wined3d_debug_vkresult(vr)); - free(swapchain_vk->vk_images); - return false; - } - - if (!(swapchain_vk->vk_semaphores = calloc(image_count, sizeof(*swapchain_vk->vk_semaphores)))) - { - ERR("Failed to allocate semaphores array.\n"); - free(swapchain_vk->vk_images); - return false; - } - - semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphore_info.pNext = NULL; - semaphore_info.flags = 0; - for (i = 0; i < image_count; ++i) - { - if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, - &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].available))) < 0) - { - ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); - goto fail; - } - - if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, - &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].presentable))) < 0) - { - ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); - goto fail; - } - } - swapchain_vk->image_count = image_count; - - return true; - -fail: - for (i = 0; i < image_count; ++i) - { - if (swapchain_vk->vk_semaphores[i].available) - VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL)); - if (swapchain_vk->vk_semaphores[i].presentable) - VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL)); - } - free(swapchain_vk->vk_semaphores); - free(swapchain_vk->vk_images); - return false; -} - -static HRESULT wined3d_swapchain_vk_create_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk) -{ - struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); - const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; - VkSwapchainCreateInfoKHR vk_swapchain_desc; - VkWin32SurfaceCreateInfoKHR surface_desc; - unsigned int width, height, image_count; - const struct wined3d_vk_info *vk_info; - VkSurfaceCapabilitiesKHR surface_caps; - struct wined3d_adapter_vk *adapter_vk; - VkPresentModeKHR vk_present_mode; - VkSwapchainKHR vk_swapchain; - VkImageUsageFlags usage; - VkSurfaceKHR vk_surface; - VkBool32 supported; - VkFormat vk_format; - RECT client_rect; - VkResult vr; - - adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); - vk_info = &adapter_vk->vk_info; - - surface_desc.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surface_desc.pNext = NULL; - surface_desc.flags = 0; - surface_desc.hinstance = (HINSTANCE)GetWindowLongPtrW(swapchain_vk->s.win_handle, GWLP_HINSTANCE); - surface_desc.hwnd = swapchain_vk->s.win_handle; - if ((vr = VK_CALL(vkCreateWin32SurfaceKHR(vk_info->instance, &surface_desc, NULL, &vk_surface))) < 0) - { - ERR("Failed to create Vulkan surface, vr %s.\n", wined3d_debug_vkresult(vr)); - return E_FAIL; - } - swapchain_vk->vk_surface = vk_surface; - - if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceSupportKHR(adapter_vk->physical_device, - device_vk->graphics_queue.vk_queue_family_index, vk_surface, &supported))) < 0 || !supported) - { - ERR("Queue family does not support presentation on this surface, vr %s.\n", wined3d_debug_vkresult(vr)); - goto fail; - } - - if ((vk_format = wined3d_swapchain_vk_select_vk_format(swapchain_vk, vk_surface)) == VK_FORMAT_UNDEFINED) - { - ERR("Failed to select swapchain format.\n"); - goto fail; - } - - if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(adapter_vk->physical_device, - swapchain_vk->vk_surface, &surface_caps))) < 0) - { - ERR("Failed to get surface capabilities, vr %s.\n", wined3d_debug_vkresult(vr)); - goto fail; - } - - image_count = desc->backbuffer_count; - if (image_count < surface_caps.minImageCount) - image_count = surface_caps.minImageCount; - else if (surface_caps.maxImageCount && image_count > surface_caps.maxImageCount) - image_count = surface_caps.maxImageCount; - - if (image_count != desc->backbuffer_count) - WARN("Image count %u is not supported (%u-%u).\n", desc->backbuffer_count, - surface_caps.minImageCount, surface_caps.maxImageCount); - - GetClientRect(swapchain_vk->s.win_handle, &client_rect); - - width = client_rect.right - client_rect.left; - if (width < surface_caps.minImageExtent.width) - width = surface_caps.minImageExtent.width; - else if (width > surface_caps.maxImageExtent.width) - width = surface_caps.maxImageExtent.width; - - height = client_rect.bottom - client_rect.top; - if (height < surface_caps.minImageExtent.height) - height = surface_caps.minImageExtent.height; - else if (height > surface_caps.maxImageExtent.height) - height = surface_caps.maxImageExtent.height; - - if (width != client_rect.right - client_rect.left || height != client_rect.bottom - client_rect.top) - WARN("Swapchain dimensions %lux%lu are not supported (%u-%u x %u-%u).\n", - client_rect.right - client_rect.left, client_rect.bottom - client_rect.top, - surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width, - surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height); - - usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT; - if (!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) || !(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) - WARN("Transfer not supported for swapchain images.\n"); - - if (!(surface_caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) - { - FIXME("Unsupported alpha mode, %#x.\n", surface_caps.supportedCompositeAlpha); - goto fail; - } - - vk_present_mode = VK_PRESENT_MODE_FIFO_KHR; - if (!swapchain_vk->s.swap_interval) - { - if (wined3d_swapchain_vk_present_mode_supported(swapchain_vk, VK_PRESENT_MODE_IMMEDIATE_KHR)) - vk_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; - else - FIXME("Unsupported swap interval %u.\n", swapchain_vk->s.swap_interval); - } - - vk_swapchain_desc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - vk_swapchain_desc.pNext = NULL; - vk_swapchain_desc.flags = 0; - vk_swapchain_desc.surface = vk_surface; - vk_swapchain_desc.minImageCount = image_count; - vk_swapchain_desc.imageFormat = vk_format; - vk_swapchain_desc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; - vk_swapchain_desc.imageExtent.width = width; - vk_swapchain_desc.imageExtent.height = height; - vk_swapchain_desc.imageArrayLayers = 1; - vk_swapchain_desc.imageUsage = usage; - vk_swapchain_desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - vk_swapchain_desc.queueFamilyIndexCount = 0; - vk_swapchain_desc.pQueueFamilyIndices = NULL; - vk_swapchain_desc.preTransform = surface_caps.currentTransform; - vk_swapchain_desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - vk_swapchain_desc.presentMode = vk_present_mode; - vk_swapchain_desc.clipped = VK_TRUE; - vk_swapchain_desc.oldSwapchain = VK_NULL_HANDLE; - if ((vr = VK_CALL(vkCreateSwapchainKHR(device_vk->vk_device, &vk_swapchain_desc, NULL, &vk_swapchain))) < 0) - { - ERR("Failed to create Vulkan swapchain, vr %s.\n", wined3d_debug_vkresult(vr)); - goto fail; - } - swapchain_vk->vk_swapchain = vk_swapchain; - - if (!wined3d_swapchain_vk_create_vulkan_swapchain_images(swapchain_vk, vk_swapchain)) - { - VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, vk_swapchain, NULL)); - goto fail; - } - - swapchain_vk->width = width; - swapchain_vk->height = height; - - return WINED3D_OK; - -fail: - VK_CALL(vkDestroySurfaceKHR(vk_info->instance, vk_surface, NULL)); - swapchain_vk->vk_surface = 0; - return E_FAIL; -} - -static HRESULT wined3d_swapchain_vk_recreate(struct wined3d_swapchain_vk *swapchain_vk) -{ - TRACE("swapchain_vk %p.\n", swapchain_vk); - - wined3d_swapchain_vk_destroy_vulkan_swapchain(swapchain_vk); - - return wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk); -} - -static void wined3d_swapchain_vk_set_swap_interval(struct wined3d_swapchain_vk *swapchain_vk, - unsigned int swap_interval) -{ - if (swap_interval > 1) - { - if (swap_interval <= 4) - FIXME("Unsupported swap interval %u.\n", swap_interval); - swap_interval = 1; - } - - if (swapchain_vk->s.swap_interval == swap_interval) - return; - - swapchain_vk->s.swap_interval = swap_interval; - wined3d_swapchain_vk_recreate(swapchain_vk); -} - -static VkResult wined3d_swapchain_vk_blit(struct wined3d_swapchain_vk *swapchain_vk, - struct wined3d_context_vk *context_vk, const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval) -{ - struct wined3d_texture_vk *back_buffer_vk = wined3d_texture_vk(swapchain_vk->s.back_buffers[0]); - struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); - const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; - const struct wined3d_vk_info *vk_info = context_vk->vk_info; - VkCommandBuffer vk_command_buffer; - VkImageSubresourceRange vk_range; - VkPresentInfoKHR present_desc; - unsigned int present_idx; - VkImageLayout vk_layout; - uint32_t image_idx; - RECT dst_rect_tmp; - VkImageBlit blit; - VkFilter filter; - VkResult vr; - - static const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - - TRACE("swapchain_vk %p, context_vk %p, src_rect %s, dst_rect %s, swap_interval %u.\n", - swapchain_vk, context_vk, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect), swap_interval); - - wined3d_swapchain_vk_set_swap_interval(swapchain_vk, swap_interval); - - present_idx = swapchain_vk->current++ % swapchain_vk->image_count; - wined3d_context_vk_wait_command_buffer(context_vk, swapchain_vk->vk_semaphores[present_idx].command_buffer_id); - if ((vr = VK_CALL(vkAcquireNextImageKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, UINT64_MAX, - swapchain_vk->vk_semaphores[present_idx].available, VK_NULL_HANDLE, &image_idx))) < 0) - { - WARN("Failed to acquire image, vr %s.\n", wined3d_debug_vkresult(vr)); - return vr; - } - - if (dst_rect->right > swapchain_vk->width || dst_rect->bottom > swapchain_vk->height) - { - dst_rect_tmp = *dst_rect; - if (dst_rect->right > swapchain_vk->width) - dst_rect_tmp.right = swapchain_vk->width; - if (dst_rect->bottom > swapchain_vk->height) - dst_rect_tmp.bottom = swapchain_vk->height; - dst_rect = &dst_rect_tmp; - } - filter = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left - || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top - ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; - vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk); - - wined3d_context_vk_end_current_render_pass(context_vk); - - vk_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vk_range.baseMipLevel = 0; - vk_range.levelCount = 1; - vk_range.baseArrayLayer = 0; - vk_range.layerCount = 1; - - wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags), - VK_ACCESS_TRANSFER_READ_BIT, - back_buffer_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - back_buffer_vk->image.vk_image, &vk_range); - - wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - swapchain_vk->vk_images[image_idx], &vk_range); - - blit.srcSubresource.aspectMask = vk_range.aspectMask; - blit.srcSubresource.mipLevel = vk_range.baseMipLevel; - blit.srcSubresource.baseArrayLayer = vk_range.baseArrayLayer; - blit.srcSubresource.layerCount = vk_range.layerCount; - blit.srcOffsets[0].x = src_rect->left; - blit.srcOffsets[0].y = src_rect->top; - blit.srcOffsets[0].z = 0; - blit.srcOffsets[1].x = src_rect->right; - blit.srcOffsets[1].y = src_rect->bottom; - blit.srcOffsets[1].z = 1; - blit.dstSubresource = blit.srcSubresource; - blit.dstOffsets[0].x = dst_rect->left; - blit.dstOffsets[0].y = dst_rect->top; - blit.dstOffsets[0].z = 0; - blit.dstOffsets[1].x = dst_rect->right; - blit.dstOffsets[1].y = dst_rect->bottom; - blit.dstOffsets[1].z = 1; - VK_CALL(vkCmdBlitImage(vk_command_buffer, - back_buffer_vk->image.vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - swapchain_vk->vk_images[image_idx], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &blit, filter)); - - wined3d_context_vk_reference_texture(context_vk, back_buffer_vk); - wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, 0, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - swapchain_vk->vk_images[image_idx], &vk_range); - - if (desc->swap_effect == WINED3D_SWAP_EFFECT_DISCARD || desc->swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD) - vk_layout = VK_IMAGE_LAYOUT_UNDEFINED; - else - vk_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags), - vk_layout, back_buffer_vk->layout, - back_buffer_vk->image.vk_image, &vk_range); - back_buffer_vk->bind_mask = 0; - - swapchain_vk->vk_semaphores[present_idx].command_buffer_id = context_vk->current_command_buffer.id; - wined3d_context_vk_submit_command_buffer(context_vk, - 1, &swapchain_vk->vk_semaphores[present_idx].available, &wait_stage, - 1, &swapchain_vk->vk_semaphores[present_idx].presentable); - - present_desc.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present_desc.pNext = NULL; - present_desc.waitSemaphoreCount = 1; - present_desc.pWaitSemaphores = &swapchain_vk->vk_semaphores[present_idx].presentable; - present_desc.swapchainCount = 1; - present_desc.pSwapchains = &swapchain_vk->vk_swapchain; - present_desc.pImageIndices = &image_idx; - present_desc.pResults = NULL; - if ((vr = VK_CALL(vkQueuePresentKHR(device_vk->graphics_queue.vk_queue, &present_desc)))) - WARN("Present returned vr %s.\n", wined3d_debug_vkresult(vr)); - return vr; -} - -static void wined3d_swapchain_vk_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context_vk *context_vk) -{ - struct wined3d_texture_sub_resource *sub_resource; - struct wined3d_texture_vk *texture, *texture_prev; - struct wined3d_image_vk image0; - VkDescriptorImageInfo vk_info0; - VkImageLayout vk_layout0; - uint32_t bind_mask0; - DWORD locations0; - unsigned int i; - - static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE; - - if (swapchain->state.desc.backbuffer_count < 2) - return; - - texture_prev = wined3d_texture_vk(swapchain->back_buffers[0]); - - /* Back buffer 0 is already in the draw binding. */ - image0 = texture_prev->image; - vk_layout0 = texture_prev->layout; - bind_mask0 = texture_prev->bind_mask; - vk_info0 = texture_prev->default_image_info; - locations0 = texture_prev->t.sub_resources[0].locations; - - for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i) - { - texture = wined3d_texture_vk(swapchain->back_buffers[i]); - sub_resource = &texture->t.sub_resources[0]; - - if (!(sub_resource->locations & supported_locations)) - wined3d_texture_load_location(&texture->t, 0, &context_vk->c, texture->t.resource.draw_binding); - - texture_prev->image = texture->image; - texture_prev->layout = texture->layout; - texture_prev->bind_mask = texture->bind_mask; - texture_prev->default_image_info = texture->default_image_info; - - wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations); - wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations)); - - texture_prev = texture; - } - - texture_prev->image = image0; - texture_prev->layout = vk_layout0; - texture_prev->bind_mask = bind_mask0; - texture_prev->default_image_info = vk_info0; - - wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations); - wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations)); - - device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER); -} - -static void swapchain_vk_present(struct wined3d_swapchain *swapchain, const RECT *src_rect, - const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) -{ - struct wined3d_swapchain_vk *swapchain_vk = wined3d_swapchain_vk(swapchain); - struct wined3d_texture *back_buffer = swapchain->back_buffers[0]; - struct wined3d_context_vk *context_vk; - VkResult vr; - HRESULT hr; - - context_vk = wined3d_context_vk(context_acquire(swapchain->device, back_buffer, 0)); - - if (!swapchain_vk->vk_swapchain || swapchain_present_is_partial_copy(swapchain, dst_rect)) - { - swapchain_blit_gdi(swapchain, &context_vk->c, src_rect, dst_rect); - } - else - { - wined3d_texture_load_location(back_buffer, 0, &context_vk->c, back_buffer->resource.draw_binding); - - if ((vr = wined3d_swapchain_vk_blit(swapchain_vk, context_vk, src_rect, dst_rect, swap_interval))) - { - if (vr == VK_ERROR_OUT_OF_DATE_KHR || vr == VK_SUBOPTIMAL_KHR) - { - if (FAILED(hr = wined3d_swapchain_vk_recreate(swapchain_vk))) - ERR("Failed to recreate swapchain, hr %#lx.\n", hr); - else if (vr == VK_ERROR_OUT_OF_DATE_KHR && (vr = wined3d_swapchain_vk_blit( - swapchain_vk, context_vk, src_rect, dst_rect, swap_interval))) - ERR("Failed to blit image, vr %s.\n", wined3d_debug_vkresult(vr)); - } - else - { - ERR("Failed to blit image, vr %s.\n", wined3d_debug_vkresult(vr)); - } - } - } - - wined3d_swapchain_vk_rotate(swapchain, context_vk); - - wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); - wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); - - TRACE("Starting new frame.\n"); - - context_release(&context_vk->c); -} - -static const struct wined3d_swapchain_ops swapchain_vk_ops = -{ - swapchain_vk_present, - swapchain_frontbuffer_updated, -}; - -static void swapchain_gdi_frontbuffer_updated(struct wined3d_swapchain *swapchain) -{ - struct wined3d_dc_info *front; - POINT offset = {0, 0}; - HDC src_dc, dst_dc; - RECT draw_rect; - HWND window; - - TRACE("swapchain %p.\n", swapchain); - - front = &swapchain->front_buffer->dc_info[0]; - if (swapchain->palette) - wined3d_palette_apply_to_dc(swapchain->palette, front->dc); - - if (swapchain->front_buffer->resource.map_count) - ERR("Trying to blit a mapped surface.\n"); - - TRACE("Copying surface %p to screen.\n", front); - - src_dc = front->dc; - window = swapchain->win_handle; - dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE); - - /* Front buffer coordinates are screen coordinates. Map them to the - * destination window if not fullscreened. */ - if (swapchain->state.desc.windowed) - ClientToScreen(window, &offset); - - TRACE("offset %s.\n", wine_dbgstr_point(&offset)); - - SetRect(&draw_rect, 0, 0, swapchain->front_buffer->resource.width, - swapchain->front_buffer->resource.height); - IntersectRect(&draw_rect, &draw_rect, &swapchain->front_buffer_update); - - BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y, - draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top, - src_dc, draw_rect.left, draw_rect.top, SRCCOPY); - ReleaseDC(window, dst_dc); - - SetRectEmpty(&swapchain->front_buffer_update); -} - -static void swapchain_gdi_present(struct wined3d_swapchain *swapchain, - const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) -{ - struct wined3d_dc_info *front, *back; - HBITMAP bitmap; - void *heap_pointer; - void *heap_memory; - HDC dc; - - front = &swapchain->front_buffer->dc_info[0]; - back = &swapchain->back_buffers[0]->dc_info[0]; - - /* Flip the surface data. */ - dc = front->dc; - bitmap = front->bitmap; - heap_pointer = swapchain->front_buffer->resource.heap_pointer; - heap_memory = swapchain->front_buffer->resource.heap_memory; - - front->dc = back->dc; - front->bitmap = back->bitmap; - swapchain->front_buffer->resource.heap_pointer = swapchain->back_buffers[0]->resource.heap_pointer; - swapchain->front_buffer->resource.heap_memory = swapchain->back_buffers[0]->resource.heap_memory; - - back->dc = dc; - back->bitmap = bitmap; - swapchain->back_buffers[0]->resource.heap_pointer = heap_pointer; - swapchain->back_buffers[0]->resource.heap_memory = heap_memory; - - SetRect(&swapchain->front_buffer_update, 0, 0, - swapchain->front_buffer->resource.width, - swapchain->front_buffer->resource.height); - swapchain_gdi_frontbuffer_updated(swapchain); -} - -static const struct wined3d_swapchain_ops swapchain_no3d_ops = -{ - swapchain_gdi_present, - swapchain_gdi_frontbuffer_updated, -}; - -static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_swapchain *swapchain, - enum wined3d_format_id format_id, enum wined3d_multisample_type *type, unsigned int *quality) -{ - const struct wined3d_adapter *adapter; - const struct wined3d_format *format; - enum wined3d_multisample_type t; - - if (wined3d_settings.sample_count == ~0u) - return; - - adapter = swapchain->device->adapter; - if (!(format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET))) - return; - - if ((t = min(wined3d_settings.sample_count, adapter->d3d_info.limits.sample_count))) - while (!(format->multisample_types & 1u << (t - 1))) - ++t; - TRACE("Using sample count %u.\n", t); - *type = t; - *quality = 0; -} - -void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, const struct wined3d_device *device) -{ - /* Subtract 1 for the implicit OpenGL latency. */ - swapchain->max_frame_latency = device->max_frame_latency >= 2 ? device->max_frame_latency - 1 : 1; -} - -static enum wined3d_format_id adapter_format_from_backbuffer_format(const struct wined3d_adapter *adapter, - enum wined3d_format_id format_id) -{ - const struct wined3d_format *backbuffer_format; - - backbuffer_format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET); - return pixelformat_for_depth(backbuffer_format->byte_count * CHAR_BIT); -} - -static HRESULT wined3d_swapchain_state_init(struct wined3d_swapchain_state *state, - const struct wined3d_swapchain_desc *desc, HWND window, struct wined3d *wined3d, - struct wined3d_swapchain_state_parent *parent) -{ - HRESULT hr; - - state->desc = *desc; - - if (FAILED(hr = wined3d_output_get_display_mode(desc->output, &state->original_mode, NULL))) - { - ERR("Failed to get current display mode, hr %#lx.\n", hr); - return hr; - } - - if (state->desc.windowed) - { - RECT client_rect; - - GetClientRect(window, &client_rect); - TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect)); - - if (!state->desc.backbuffer_width) - { - state->desc.backbuffer_width = client_rect.right ? client_rect.right : 8; - TRACE("Updating width to %u.\n", state->desc.backbuffer_width); - } - if (!state->desc.backbuffer_height) - { - state->desc.backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; - TRACE("Updating height to %u.\n", state->desc.backbuffer_height); - } - - if (state->desc.backbuffer_format == WINED3DFMT_UNKNOWN) - { - state->desc.backbuffer_format = state->original_mode.format_id; - TRACE("Updating format to %s.\n", debug_d3dformat(state->original_mode.format_id)); - } - } - else - { - if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) - { - state->d3d_mode.width = desc->backbuffer_width; - state->d3d_mode.height = desc->backbuffer_height; - state->d3d_mode.format_id = adapter_format_from_backbuffer_format(desc->output->adapter, - desc->backbuffer_format); - state->d3d_mode.refresh_rate = desc->refresh_rate; - state->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; - } - else - { - state->d3d_mode = state->original_mode; - } - } - - GetWindowRect(window, &state->original_window_rect); - state->wined3d = wined3d; - state->device_window = window; - state->desc.device_window = window; - state->parent = parent; - - if (desc->flags & WINED3D_SWAPCHAIN_REGISTER_STATE) - wined3d_swapchain_state_register(state); - - return hr; -} - -static HRESULT swapchain_create_texture(struct wined3d_swapchain *swapchain, - bool front, bool depth, struct wined3d_texture **texture) -{ - const struct wined3d_swapchain_desc *swapchain_desc = &swapchain->state.desc; - struct wined3d_device *device = swapchain->device; - struct wined3d_resource_desc texture_desc; - uint32_t texture_flags = 0; - HRESULT hr; - - texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; - texture_desc.format = depth ? swapchain_desc->auto_depth_stencil_format : swapchain_desc->backbuffer_format; - texture_desc.multisample_type = swapchain_desc->multisample_type; - texture_desc.multisample_quality = swapchain_desc->multisample_quality; - texture_desc.usage = 0; - if (!depth && (device->wined3d->flags & WINED3D_NO3D)) - texture_desc.usage |= WINED3DUSAGE_OWNDC; - if (device->wined3d->flags & WINED3D_NO3D) - texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU; - else - texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU; - if (!depth && (swapchain_desc->flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER)) - texture_desc.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; - texture_desc.width = swapchain_desc->backbuffer_width; - texture_desc.height = swapchain_desc->backbuffer_height; - texture_desc.depth = 1; - texture_desc.size = 0; - - if (front) - texture_desc.bind_flags = 0; - else if (depth) - texture_desc.bind_flags = WINED3D_BIND_DEPTH_STENCIL; - else - texture_desc.bind_flags = swapchain_desc->backbuffer_bind_flags; - - if (swapchain_desc->flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE) - texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC; - - if (FAILED(hr = wined3d_texture_create(device, &texture_desc, 1, 1, - texture_flags, NULL, NULL, &wined3d_null_parent_ops, texture))) - { - WARN("Failed to create texture, hr %#lx.\n", hr); - return hr; - } - - if (!depth) - wined3d_texture_set_swapchain(*texture, swapchain); - - return S_OK; -} - -static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device, - const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, - void *parent, const struct wined3d_parent_ops *parent_ops, - const struct wined3d_swapchain_ops *swapchain_ops) -{ - struct wined3d_output_desc output_desc; - BOOL displaymode_set = FALSE; - HRESULT hr = E_FAIL; - unsigned int i; - HWND window; - - wined3d_mutex_lock(); - - if (desc->backbuffer_count > 1) - { - FIXME("The application requested more than one back buffer, this is not properly supported.\n" - "Please configure the application to use double buffering (1 back buffer) if possible.\n"); - } - - if (desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD - && desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL - && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY) - FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect); - - window = desc->device_window ? desc->device_window : device->create_parms.focus_window; - TRACE("Using target window %p.\n", window); - - if (FAILED(hr = wined3d_swapchain_state_init(&swapchain->state, desc, window, device->wined3d, state_parent))) - { - ERR("Failed to initialise swapchain state, hr %#lx.\n", hr); - wined3d_mutex_unlock(); - return hr; - } - - swapchain->swapchain_ops = swapchain_ops; - swapchain->device = device; - swapchain->parent = parent; - swapchain->parent_ops = parent_ops; - swapchain->ref = 1; - swapchain->win_handle = window; - swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT; - swapchain_set_max_frame_latency(swapchain, device); - - if (!swapchain->state.desc.windowed) - { - if (FAILED(hr = wined3d_output_get_desc(desc->output, &output_desc))) - { - ERR("Failed to get output description, hr %#lx.\n", hr); - goto err; - } - - wined3d_swapchain_state_setup_fullscreen(&swapchain->state, window, - output_desc.desktop_rect.left, output_desc.desktop_rect.top, desc->backbuffer_width, - desc->backbuffer_height); - } - wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->state.desc.backbuffer_format, - &swapchain->state.desc.multisample_type, &swapchain->state.desc.multisample_quality); - - TRACE("Creating front buffer.\n"); - - if (FAILED(hr = swapchain_create_texture(swapchain, true, false, &swapchain->front_buffer))) - { - WARN("Failed to create front buffer, hr %#lx.\n", hr); - goto err; - } - - if (!(device->wined3d->flags & WINED3D_NO3D)) - { - wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); - wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); - } - - /* MSDN says we're only allowed a single fullscreen swapchain per device, - * so we should really check to see if there is a fullscreen swapchain - * already. Does a single head count as full screen? */ - if (!desc->windowed && desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) - { - /* Change the display settings */ - if (FAILED(hr = wined3d_output_set_display_mode(desc->output, - &swapchain->state.d3d_mode))) - { - WARN("Failed to set display mode, hr %#lx.\n", hr); - goto err; - } - displaymode_set = TRUE; - } - - if (swapchain->state.desc.backbuffer_count > 0) - { - if (!(swapchain->back_buffers = calloc(swapchain->state.desc.backbuffer_count, - sizeof(*swapchain->back_buffers)))) - { - ERR("Failed to allocate backbuffer array memory.\n"); - hr = E_OUTOFMEMORY; - goto err; - } - - for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) - { - TRACE("Creating back buffer %u.\n", i); - if (FAILED(hr = swapchain_create_texture(swapchain, false, false, &swapchain->back_buffers[i]))) - { - WARN("Failed to create back buffer %u, hr %#lx.\n", i, hr); - swapchain->state.desc.backbuffer_count = i; - goto err; - } - } - } - - /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */ - if (desc->enable_auto_depth_stencil) - { - TRACE("Creating depth/stencil buffer.\n"); - if (!device->auto_depth_stencil_view) - { - struct wined3d_view_desc desc; - struct wined3d_texture *ds; - - if (FAILED(hr = swapchain_create_texture(swapchain, false, true, &ds))) - { - WARN("Failed to create the auto depth/stencil surface, hr %#lx.\n", hr); - goto err; - } - - desc.format_id = ds->resource.format->id; - desc.flags = 0; - desc.u.texture.level_idx = 0; - desc.u.texture.level_count = 1; - desc.u.texture.layer_idx = 0; - desc.u.texture.layer_count = 1; - hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops, - &device->auto_depth_stencil_view); - wined3d_texture_decref(ds); - if (FAILED(hr)) - { - ERR("Failed to create rendertarget view, hr %#lx.\n", hr); - goto err; - } - } - } - - wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma); - - wined3d_mutex_unlock(); - - return WINED3D_OK; - -err: - if (displaymode_set) - { - if (FAILED(wined3d_restore_display_modes(device->wined3d))) - ERR("Failed to restore display mode.\n"); - } - - if (swapchain->back_buffers) - { - for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) - { - if (swapchain->back_buffers[i]) - { - wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); - wined3d_texture_decref(swapchain->back_buffers[i]); - } - } - free(swapchain->back_buffers); - } - - if (swapchain->front_buffer) - { - wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); - wined3d_texture_decref(swapchain->front_buffer); - } - - wined3d_swapchain_state_cleanup(&swapchain->state); - wined3d_mutex_unlock(); - - return hr; -} - -HRESULT wined3d_swapchain_no3d_init(struct wined3d_swapchain *swapchain_no3d, struct wined3d_device *device, - const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, - void *parent, const struct wined3d_parent_ops *parent_ops) -{ - TRACE("swapchain_no3d %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", - swapchain_no3d, device, desc, state_parent, parent, parent_ops); - - return wined3d_swapchain_init(swapchain_no3d, device, desc, state_parent, parent, parent_ops, - &swapchain_no3d_ops); -} - -HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, struct wined3d_device *device, - const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, - void *parent, const struct wined3d_parent_ops *parent_ops) -{ - TRACE("swapchain_gl %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", - swapchain_gl, device, desc, state_parent, parent, parent_ops); - - return wined3d_swapchain_init(&swapchain_gl->s, device, desc, state_parent, parent, - parent_ops, &swapchain_gl_ops); -} - -HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, struct wined3d_device *device, - const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, - void *parent, const struct wined3d_parent_ops *parent_ops) -{ - HRESULT hr; - - TRACE("swapchain_vk %p, device %p, desc %p, parent %p, parent_ops %p.\n", - swapchain_vk, device, desc, parent, parent_ops); - - if (FAILED(hr = wined3d_swapchain_init(&swapchain_vk->s, device, desc, state_parent, parent, - parent_ops, &swapchain_vk_ops))) - return hr; - - if (swapchain_vk->s.win_handle == GetDesktopWindow()) - { - WARN("Creating a desktop window swapchain.\n"); - return hr; - } - - if (FAILED(hr = wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk))) - { - WARN("Failed to create a Vulkan swapchain, hr %#lx.\n", hr); - return hr; - } - - return WINED3D_OK; -} - -HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, - const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, - void *parent, const struct wined3d_parent_ops *parent_ops, - struct wined3d_swapchain **swapchain) -{ - struct wined3d_swapchain *object; - HRESULT hr; - - if (FAILED(hr = device->adapter->adapter_ops->adapter_create_swapchain(device, - desc, state_parent, parent, parent_ops, &object))) - return hr; - - if (desc->flags & WINED3D_SWAPCHAIN_IMPLICIT) - { - wined3d_mutex_lock(); - if (FAILED(hr = wined3d_device_set_implicit_swapchain(device, object))) - { - wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); - device->adapter->adapter_ops->adapter_destroy_swapchain(object); - wined3d_mutex_unlock(); - return hr; - } - wined3d_mutex_unlock(); - } - - *swapchain = object; - - return hr; -} - -static struct wined3d_context_gl *wined3d_swapchain_gl_create_context(struct wined3d_swapchain_gl *swapchain_gl) -{ - struct wined3d_device *device = swapchain_gl->s.device; - struct wined3d_context_gl *context_gl; - - TRACE("Creating a new context for swapchain %p, thread %lu.\n", swapchain_gl, GetCurrentThreadId()); - - wined3d_from_cs(device->cs); - - if (!(context_gl = calloc(1, sizeof(*context_gl)))) - { - ERR("Failed to allocate context memory.\n"); - return NULL; - } - - if (FAILED(wined3d_context_gl_init(context_gl, swapchain_gl))) - { - WARN("Failed to initialise context.\n"); - free(context_gl); - return NULL; - } - - if (!device_context_add(device, &context_gl->c)) - { - ERR("Failed to add the newly created context to the context list.\n"); - wined3d_context_gl_destroy(context_gl); - return NULL; - } - - TRACE("Created context %p.\n", context_gl); - - context_release(&context_gl->c); - - return context_gl; -} - -struct wined3d_context_gl *wined3d_swapchain_gl_get_context(struct wined3d_swapchain_gl *swapchain_gl) -{ - struct wined3d_device *device = swapchain_gl->s.device; - DWORD tid = GetCurrentThreadId(); - unsigned int i; - - for (i = 0; i < device->context_count; ++i) - { - if (wined3d_context_gl(device->contexts[i])->tid == tid) - return wined3d_context_gl(device->contexts[i]); - } - - /* Create a new context for the thread. */ - return wined3d_swapchain_gl_create_context(swapchain_gl); -} - -void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain) -{ - UINT i; - - wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource); - - for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) - { - wined3d_resource_update_draw_binding(&swapchain->back_buffers[i]->resource); - } -} - -void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate) -{ - struct wined3d_device *device = swapchain->device; - HWND window = swapchain->state.device_window; - struct wined3d_output_desc output_desc; - unsigned int screensaver_active; - struct wined3d_output *output; - BOOL focus_messages, filter; - HRESULT hr; - - /* This code is not protected by the wined3d mutex, so it may run while - * wined3d_device_reset is active. Testing on Windows shows that changing - * focus during resets and resetting during focus change events causes - * the application to crash with an invalid memory access. */ - - if (!(focus_messages = device->wined3d->flags & WINED3D_FOCUS_MESSAGES)) - filter = wined3d_filter_messages(window, TRUE); - - if (activate) - { - SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &screensaver_active, 0); - if ((device->restore_screensaver = !!screensaver_active)) - SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0); - - if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES)) - { - /* The d3d versions do not agree on the exact messages here. D3d8 restores - * the window but leaves the size untouched, d3d9 sets the size on an - * invisible window, generates messages but doesn't change the window - * properties. The implementation follows d3d9. - * - * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to - * resume drawing after a focus loss. */ - output = wined3d_swapchain_get_output(swapchain); - if (!output) - { - ERR("Failed to get output from swapchain %p.\n", swapchain); - return; - } - - if (SUCCEEDED(hr = wined3d_output_get_desc(output, &output_desc))) - SetWindowPos(window, NULL, output_desc.desktop_rect.left, - output_desc.desktop_rect.top, swapchain->state.desc.backbuffer_width, - swapchain->state.desc.backbuffer_height, SWP_NOACTIVATE | SWP_NOZORDER); - else - ERR("Failed to get output description, hr %#lx.\n", hr); - } - - if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE) - { - output = wined3d_swapchain_get_output(swapchain); - if (!output) - { - ERR("Failed to get output from swapchain %p.\n", swapchain); - return; - } - - if (FAILED(hr = wined3d_output_set_display_mode(output, - &swapchain->state.d3d_mode))) - ERR("Failed to set display mode, hr %#lx.\n", hr); - } - - if (swapchain == device->swapchains[0]) - device->device_parent->ops->activate(device->device_parent, TRUE); - } - else - { - if (device->restore_screensaver) - { - SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0); - device->restore_screensaver = FALSE; - } - - if (FAILED(hr = wined3d_restore_display_modes(device->wined3d))) - ERR("Failed to restore display modes, hr %#lx.\n", hr); - - swapchain->reapply_mode = TRUE; - - /* Some DDraw apps (Deus Ex: GOTY, and presumably all UT 1 based games) destroy the device - * during window minimization. Do our housekeeping now, as the device may not exist after - * the ShowWindow call. - * - * In d3d9, the device is marked lost after the window is minimized. If we find an app - * that needs this behavior (e.g. because it calls TestCooperativeLevel in the window proc) - * we'll have to control this via a create flag. Note that the device and swapchain are not - * safe to access after the ShowWindow call. */ - if (swapchain == device->swapchains[0]) - device->device_parent->ops->activate(device->device_parent, FALSE); - - if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES) && IsWindowVisible(window)) - ShowWindow(window, SW_MINIMIZE); - } - - if (!focus_messages) - wined3d_filter_messages(window, filter); -} - -HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count, - unsigned int width, unsigned int height, enum wined3d_format_id format_id, - enum wined3d_multisample_type multisample_type, unsigned int multisample_quality) -{ - struct wined3d_swapchain_desc *desc = &swapchain->state.desc; - bool recreate = false; - - TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, " - "multisample_type %#x, multisample_quality %#x.\n", - swapchain, buffer_count, width, height, debug_d3dformat(format_id), - multisample_type, multisample_quality); - - wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality); - - if (buffer_count && buffer_count != desc->backbuffer_count) - FIXME("Cannot change the back buffer count yet.\n"); - - wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); - - if (!width || !height) - { - RECT client_rect; - - /* The application is requesting that either the swapchain width or - * height be set to the corresponding dimension in the window's - * client rect. */ - - if (!GetClientRect(swapchain->state.device_window, &client_rect)) - { - ERR("Failed to get client rect, last error %#lx.\n", GetLastError()); - return WINED3DERR_INVALIDCALL; - } - - if (!width) - width = client_rect.right; - - if (!height) - height = client_rect.bottom; - } - - if (width != desc->backbuffer_width || height != desc->backbuffer_height) - { - desc->backbuffer_width = width; - desc->backbuffer_height = height; - recreate = true; - } - - if (format_id == WINED3DFMT_UNKNOWN) - { - if (!desc->windowed) - return WINED3DERR_INVALIDCALL; - format_id = swapchain->state.original_mode.format_id; - } - - if (format_id != desc->backbuffer_format) - { - desc->backbuffer_format = format_id; - recreate = true; - } - - if (multisample_type != desc->multisample_type - || multisample_quality != desc->multisample_quality) - { - desc->multisample_type = multisample_type; - desc->multisample_quality = multisample_quality; - recreate = true; - } - - if (recreate) - { - struct wined3d_texture *new_texture; - HRESULT hr; - UINT i; - - TRACE("Recreating swapchain textures.\n"); - - if (FAILED(hr = swapchain_create_texture(swapchain, true, false, &new_texture))) - return hr; - wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); - if (wined3d_texture_decref(swapchain->front_buffer)) - ERR("Something's still holding the front buffer (%p).\n", swapchain->front_buffer); - swapchain->front_buffer = new_texture; - - if (!(swapchain->device->wined3d->flags & WINED3D_NO3D)) - { - wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); - wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); - } - - for (i = 0; i < desc->backbuffer_count; ++i) - { - if (FAILED(hr = swapchain_create_texture(swapchain, false, false, &new_texture))) - return hr; - wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); - if (wined3d_texture_decref(swapchain->back_buffers[i])) - ERR("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]); - swapchain->back_buffers[i] = new_texture; - } - } - - swapchain_update_draw_bindings(swapchain); - - return WINED3D_OK; -} - -static HRESULT wined3d_swapchain_state_set_display_mode(struct wined3d_swapchain_state *state, - struct wined3d_output *output, struct wined3d_display_mode *mode) -{ - HRESULT hr; - - if (state->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE) - { - if (FAILED(hr = wined3d_output_find_closest_matching_mode(output, mode))) - { - WARN("Failed to find closest matching mode, hr %#lx.\n", hr); - } - } - - if (output != state->desc.output) - { - if (FAILED(hr = wined3d_restore_display_modes(state->wined3d))) - { - WARN("Failed to restore display modes, hr %#lx.\n", hr); - return hr; - } - - if (FAILED(hr = wined3d_output_get_display_mode(output, &state->original_mode, NULL))) - { - WARN("Failed to get current display mode, hr %#lx.\n", hr); - return hr; - } - } - - if (FAILED(hr = wined3d_output_set_display_mode(output, mode))) - { - WARN("Failed to set display mode, hr %#lx.\n", hr); - return WINED3DERR_INVALIDCALL; - } - - return WINED3D_OK; -} - -HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_state *state, - const struct wined3d_display_mode *mode) -{ - struct wined3d_display_mode actual_mode; - struct wined3d_output_desc output_desc; - RECT original_window_rect, window_rect; - int x, y, width, height; - HWND window; - HRESULT hr; - - TRACE("state %p, mode %p.\n", state, mode); - - wined3d_mutex_lock(); - - window = state->device_window; - - if (state->desc.windowed) - { - SetRect(&window_rect, 0, 0, mode->width, mode->height); - AdjustWindowRectEx(&window_rect, - GetWindowLongW(window, GWL_STYLE), FALSE, - GetWindowLongW(window, GWL_EXSTYLE)); - GetWindowRect(window, &original_window_rect); - - x = original_window_rect.left; - y = original_window_rect.top; - width = window_rect.right - window_rect.left; - height = window_rect.bottom - window_rect.top; - } - else - { - if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) - { - ERR("Failed to get output description, hr %#lx.\n", hr); - wined3d_mutex_unlock(); - return hr; - } - width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; - height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; - - GetWindowRect(window, &window_rect); - if (width != window_rect.right - window_rect.left || height != window_rect.bottom - window_rect.top) - { - TRACE("Update saved window state.\n"); - state->original_window_rect = window_rect; - } - - if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) - { - actual_mode = *mode; - if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, state->desc.output, - &actual_mode))) - { - ERR("Failed to set display mode, hr %#lx.\n", hr); - wined3d_mutex_unlock(); - return hr; - } - if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) - { - ERR("Failed to get output description, hr %#lx.\n", hr); - wined3d_mutex_unlock(); - return hr; - } - - width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; - height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; - } - x = output_desc.desktop_rect.left; - y = output_desc.desktop_rect.top; - } - - wined3d_mutex_unlock(); - - MoveWindow(window, x, y, width, height, TRUE); - - return WINED3D_OK; -} - -static LONG fullscreen_style(LONG style) -{ - /* Make sure the window is managed, otherwise we won't get keyboard input. */ - style |= WS_POPUP | WS_SYSMENU; - style &= ~(WS_CAPTION | WS_THICKFRAME); - - return style; -} - -static LONG fullscreen_exstyle(LONG exstyle) -{ - /* Filter out window decorations. */ - exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE); - - return exstyle; -} - -struct wined3d_window_state -{ - HWND window; - HWND window_pos_after; - LONG style, exstyle; - int x, y, width, height; - uint32_t flags; - bool set_style; - bool register_topmost_timer; - bool set_topmost_timer; -}; - -#define WINED3D_WINDOW_TOPMOST_TIMER_ID 0x4242 - -static void CALLBACK topmost_timer_proc(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) -{ - if (!(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) - SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - - KillTimer(hwnd, WINED3D_WINDOW_TOPMOST_TIMER_ID); -} - -static DWORD WINAPI set_window_state_thread(void *ctx) -{ - struct wined3d_window_state *s = ctx; - bool filter; - - filter = wined3d_filter_messages(s->window, TRUE); - - if (s->set_style) - { - SetWindowLongW(s->window, GWL_STYLE, s->style); - SetWindowLongW(s->window, GWL_EXSTYLE, s->exstyle); - } - SetWindowPos(s->window, s->window_pos_after, s->x, s->y, s->width, s->height, s->flags); - - wined3d_filter_messages(s->window, filter); - - free(s); - - return 0; -} - -static void set_window_state(struct wined3d_window_state *s) -{ - static const UINT timeout = 1500; - DWORD window_tid = GetWindowThreadProcessId(s->window, NULL); - DWORD tid = GetCurrentThreadId(); - HANDLE thread; - - TRACE("Window %p belongs to thread %#lx.\n", s->window, window_tid); - /* If the window belongs to a different thread, modifying the style and/or - * position can potentially deadlock if that thread isn't processing - * messages. */ - if (window_tid == tid) - { - /* Deus Ex: Game of the Year Edition removes WS_EX_TOPMOST after changing resolutions in - * exclusive fullscreen mode. Tests show that WS_EX_TOPMOST will be restored when a ~1.5s - * timer times out */ - if (s->register_topmost_timer) - { - if (s->set_topmost_timer) - SetTimer(s->window, WINED3D_WINDOW_TOPMOST_TIMER_ID, timeout, topmost_timer_proc); - else - KillTimer(s->window, WINED3D_WINDOW_TOPMOST_TIMER_ID); - } - - set_window_state_thread(s); - } - else if ((thread = CreateThread(NULL, 0, set_window_state_thread, s, 0, NULL))) - { - SetThreadDescription(thread, L"wined3d_set_window_state"); - CloseHandle(thread); - } - else - { - ERR("Failed to create thread.\n"); - } -} - -HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state *state, - HWND window, int x, int y, int width, int height) -{ - struct wined3d_window_state *s; - - TRACE("Setting up window %p for fullscreen mode.\n", window); - - if (!IsWindow(window)) - { - WARN("%p is not a valid window.\n", window); - return WINED3DERR_NOTAVAILABLE; - } - - if (!(s = malloc(sizeof(*s)))) - return E_OUTOFMEMORY; - s->window = window; - s->window_pos_after = HWND_TOPMOST; - s->x = x; - s->y = y; - s->width = width; - s->height = height; - - if (state->style || state->exstyle) - { - ERR("Changing the window style for window %p, but another style (%08lx, %08lx) is already stored.\n", - window, state->style, state->exstyle); - } - - s->flags = SWP_FRAMECHANGED | SWP_NOACTIVATE; - if (state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES) - s->flags |= SWP_NOZORDER; - else - s->flags |= SWP_SHOWWINDOW; - - state->style = GetWindowLongW(window, GWL_STYLE); - state->exstyle = GetWindowLongW(window, GWL_EXSTYLE); - - s->style = fullscreen_style(state->style); - s->exstyle = fullscreen_exstyle(state->exstyle); - s->set_style = true; - s->register_topmost_timer = !!(state->desc.flags & WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER); - s->set_topmost_timer = true; - - TRACE("Old style was %08lx, %08lx, setting to %08lx, %08lx.\n", - state->style, state->exstyle, s->style, s->exstyle); - - set_window_state(s); - return WINED3D_OK; -} - -void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_state *state, - HWND window, const RECT *window_rect) -{ - struct wined3d_window_state *s; - LONG style, exstyle; - - if (!state->style && !state->exstyle) - return; - - if (!(s = malloc(sizeof(*s)))) - return; - - s->window = window; - s->window_pos_after = NULL; - s->flags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE; - - if ((state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_STATE) - && !(state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES)) - { - s->window_pos_after = (state->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST; - s->flags |= (state->style & WS_VISIBLE) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; - s->flags &= ~SWP_NOZORDER; - } - - style = GetWindowLongW(window, GWL_STYLE); - exstyle = GetWindowLongW(window, GWL_EXSTYLE); - - /* These flags are set by wined3d_device_setup_fullscreen_window, not the - * application, and we want to ignore them in the test below, since it's - * not the application's fault that they changed. Additionally, we want to - * preserve the current status of these flags (i.e. don't restore them) to - * more closely emulate the behavior of Direct3D, which leaves these flags - * alone when returning to windowed mode. */ - state->style ^= (state->style ^ style) & WS_VISIBLE; - state->exstyle ^= (state->exstyle ^ exstyle) & WS_EX_TOPMOST; - - TRACE("Restoring window style of window %p to %08lx, %08lx.\n", - window, state->style, state->exstyle); - - s->style = state->style; - s->exstyle = state->exstyle; - /* Only restore the style if the application didn't modify it during the - * fullscreen phase. Some applications change it before calling Reset() - * when switching between windowed and fullscreen modes (HL2), some - * depend on the original style (Eve Online). */ - s->set_style = style == fullscreen_style(state->style) && exstyle == fullscreen_exstyle(state->exstyle); - s->register_topmost_timer = !!(state->desc.flags & WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER); - s->set_topmost_timer = false; - - if (window_rect) - { - s->x = window_rect->left; - s->y = window_rect->top; - s->width = window_rect->right - window_rect->left; - s->height = window_rect->bottom - window_rect->top; - } - else - { - s->x = s->y = s->width = s->height = 0; - s->flags |= (SWP_NOMOVE | SWP_NOSIZE); - } - - set_window_state(s); - - /* Delete the old values. */ - state->style = 0; - state->exstyle = 0; -} - -HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_state *state, - const struct wined3d_swapchain_desc *swapchain_desc, - const struct wined3d_display_mode *mode) -{ - struct wined3d_display_mode actual_mode; - struct wined3d_output_desc output_desc; - BOOL windowed = state->desc.windowed; - HRESULT hr; - - TRACE("state %p, swapchain_desc %p, mode %p.\n", state, swapchain_desc, mode); - - if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) - { - if (mode) - { - actual_mode = *mode; - if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output, - &actual_mode))) - return hr; - } - else - { - if (!swapchain_desc->windowed) - { - actual_mode.width = swapchain_desc->backbuffer_width; - actual_mode.height = swapchain_desc->backbuffer_height; - actual_mode.refresh_rate = swapchain_desc->refresh_rate; - actual_mode.format_id = adapter_format_from_backbuffer_format(swapchain_desc->output->adapter, - swapchain_desc->backbuffer_format); - actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; - if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output, - &actual_mode))) - return hr; - } - else - { - if (FAILED(hr = wined3d_restore_display_modes(state->wined3d))) - { - WARN("Failed to restore display modes for all outputs, hr %#lx.\n", hr); - return hr; - } - } - } - } - else - { - if (mode) - WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n"); - - if (FAILED(hr = wined3d_output_get_display_mode(swapchain_desc->output, &actual_mode, - NULL))) - { - ERR("Failed to get display mode, hr %#lx.\n", hr); - return WINED3DERR_INVALIDCALL; - } - } - - if (!swapchain_desc->windowed) - { - unsigned int width = actual_mode.width; - unsigned int height = actual_mode.height; - - if (FAILED(hr = wined3d_output_get_desc(swapchain_desc->output, &output_desc))) - { - ERR("Failed to get output description, hr %#lx.\n", hr); - return hr; - } - - if (state->desc.windowed) - { - /* Switch from windowed to fullscreen */ - if (FAILED(hr = wined3d_swapchain_state_setup_fullscreen(state, state->device_window, - output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, height))) - return hr; - } - else - { - HWND window = state->device_window; - BOOL filter; - - /* Fullscreen -> fullscreen mode change */ - filter = wined3d_filter_messages(window, TRUE); - MoveWindow(window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, - height, TRUE); - ShowWindow(window, SW_SHOW); - wined3d_filter_messages(window, filter); - } - state->d3d_mode = actual_mode; - } - else if (!state->desc.windowed) - { - /* Fullscreen -> windowed switch */ - RECT *window_rect = NULL; - if (state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) - window_rect = &state->original_window_rect; - wined3d_swapchain_state_restore_from_fullscreen(state, state->device_window, window_rect); - } - - state->desc.output = swapchain_desc->output; - state->desc.windowed = swapchain_desc->windowed; - - if (windowed != state->desc.windowed) - state->parent->ops->windowed_state_changed(state->parent, state->desc.windowed); - - return WINED3D_OK; -} - -BOOL CDECL wined3d_swapchain_state_is_windowed(const struct wined3d_swapchain_state *state) -{ - TRACE("state %p.\n", state); - - return state->desc.windowed; -} - -void CDECL wined3d_swapchain_state_get_size(const struct wined3d_swapchain_state *state, - unsigned int *width, unsigned int *height) -{ - TRACE("state %p.\n", state); - - *width = state->desc.backbuffer_width; - *height = state->desc.backbuffer_height; -} - -void CDECL wined3d_swapchain_state_destroy(struct wined3d_swapchain_state *state) -{ - wined3d_swapchain_state_cleanup(state); - free(state); -} - -HRESULT CDECL wined3d_swapchain_state_create(const struct wined3d_swapchain_desc *desc, - HWND window, struct wined3d *wined3d, struct wined3d_swapchain_state_parent *state_parent, - struct wined3d_swapchain_state **state) -{ - struct wined3d_swapchain_state *s; - HRESULT hr; - - TRACE("desc %p, window %p, wined3d %p, state %p.\n", desc, window, wined3d, state); - - if (!(s = calloc(1, sizeof(*s)))) - return E_OUTOFMEMORY; - - if (FAILED(hr = wined3d_swapchain_state_init(s, desc, window, wined3d, state_parent))) - { - free(s); - return hr; - } - - *state = s; - - return hr; -} +/* + * Copyright 2002-2003 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2005 Oliver Stieber + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2011 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wined3d_private.h" +#include "wined3d_gl.h" +#include "wined3d_vk.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); + +void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) +{ + HRESULT hr; + UINT i; + + TRACE("Destroying swapchain %p.\n", swapchain); + + wined3d_swapchain_state_cleanup(&swapchain->state); + wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma); + + /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0] + * is the last buffer to be destroyed, FindContext() depends on that. */ + if (swapchain->front_buffer) + { + wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); + if (wined3d_texture_decref(swapchain->front_buffer)) + WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer); + swapchain->front_buffer = NULL; + } + + if (swapchain->back_buffers) + { + i = swapchain->state.desc.backbuffer_count; + + while (i--) + { + struct wined3d_backbuffer *backbuffer = &swapchain->back_buffers[i]; + if (backbuffer->dirty_region) DeleteObject(backbuffer->dirty_region); + wined3d_texture_set_swapchain(backbuffer->texture, NULL); + if (wined3d_texture_decref(backbuffer->texture)) + WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i].texture); + } + free(swapchain->back_buffers); + swapchain->back_buffers = NULL; + } + + /* Restore the screen resolution if we rendered in fullscreen. + * This will restore the screen resolution to what it was before creating + * the swapchain. In case of d3d8 and d3d9 this will be the original + * desktop resolution. In case of d3d7 this will be a NOP because ddraw + * sets the resolution before starting up Direct3D, thus orig_width and + * orig_height will be equal to the modes in the presentation params. */ + if (!swapchain->state.desc.windowed) + { + if (swapchain->state.desc.auto_restore_display_mode) + { + if (FAILED(hr = wined3d_restore_display_modes(swapchain->device->wined3d))) + ERR("Failed to restore display mode, hr %#lx.\n", hr); + + if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) + { + wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, + swapchain->state.device_window, &swapchain->state.original_window_rect); + wined3d_device_release_focus_window(swapchain->device); + } + } + else + { + wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, swapchain->state.device_window, NULL); + } + } +} + +void wined3d_swapchain_gl_cleanup(struct wined3d_swapchain_gl *swapchain_gl) +{ + wined3d_swapchain_cleanup(&swapchain_gl->s); +} + +static void wined3d_swapchain_vk_destroy_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_vk_info *vk_info; + unsigned int i; + VkResult vr; + + TRACE("swapchain_vk %p.\n", swapchain_vk); + + vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; + + if ((vr = VK_CALL(vkQueueWaitIdle(device_vk->graphics_queue.vk_queue))) < 0) + ERR("Failed to wait on queue, vr %s.\n", wined3d_debug_vkresult(vr)); + free(swapchain_vk->vk_images); + for (i = 0; i < swapchain_vk->image_count; ++i) + { + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL)); + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL)); + } + free(swapchain_vk->vk_semaphores); + VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, NULL)); + VK_CALL(vkDestroySurfaceKHR(vk_info->instance, swapchain_vk->vk_surface, NULL)); +} + +static void wined3d_swapchain_vk_destroy_object(void *object) +{ + wined3d_swapchain_vk_destroy_vulkan_swapchain(object); +} + +void wined3d_swapchain_vk_cleanup(struct wined3d_swapchain_vk *swapchain_vk) +{ + struct wined3d_cs *cs = swapchain_vk->s.device->cs; + + wined3d_cs_destroy_object(cs, wined3d_swapchain_vk_destroy_object, swapchain_vk); + wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT); + + wined3d_swapchain_cleanup(&swapchain_vk->s); +} + +ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain) +{ + unsigned int refcount = InterlockedIncrement(&swapchain->ref); + + TRACE("%p increasing refcount to %u.\n", swapchain, refcount); + + return refcount; +} + +ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain) +{ + unsigned int refcount = InterlockedDecrement(&swapchain->ref); + + TRACE("%p decreasing refcount to %u.\n", swapchain, refcount); + + if (!refcount) + { + struct wined3d_device *device; + + wined3d_mutex_lock(); + + device = swapchain->device; + if (device->swapchain_count && device->swapchains[0] == swapchain) + wined3d_device_uninit_3d(device); + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + + swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent); + swapchain->device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain); + + wined3d_mutex_unlock(); + } + + return refcount; +} + +void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->parent; +} + +void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window) +{ + if (!window) + window = swapchain->state.device_window; + if (window == swapchain->win_handle) + return; + + TRACE("Setting swapchain %p window from %p to %p.\n", + swapchain, swapchain->win_handle, window); + + wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + swapchain->win_handle = window; +} + +HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, + unsigned int swap_interval, uint32_t flags) +{ + RECT s, d; + + TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, swap_interval %u, flags %#x.\n", + swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect), + dst_window_override, swap_interval, flags); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + wined3d_mutex_lock(); + + if (!swapchain->back_buffers) + { + WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL.\n"); + wined3d_mutex_unlock(); + return WINED3DERR_INVALIDCALL; + } + + if (!src_rect) + { + SetRect(&s, 0, 0, swapchain->state.desc.backbuffer_width, + swapchain->state.desc.backbuffer_height); + src_rect = &s; + } + + if (!dst_rect) + { + GetClientRect(swapchain->win_handle, &d); + dst_rect = &d; + } + + wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect, + dst_rect, dst_window_override, swap_interval, flags); + + wined3d_mutex_unlock(); + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain, + struct wined3d_texture *dst_texture, unsigned int sub_resource_idx) +{ + RECT src_rect, dst_rect; + + TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx); + + SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height); + dst_rect = src_rect; + + if (swapchain->state.desc.windowed) + { + MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2); + FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n", + wine_dbgstr_rect(&dst_rect)); + } + + return wined3d_device_context_blt(&swapchain->device->cs->c, dst_texture, sub_resource_idx, &dst_rect, + swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT); +} + +struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain, + UINT back_buffer_idx) +{ + TRACE("swapchain %p, back_buffer_idx %u.\n", + swapchain, back_buffer_idx); + + /* Return invalid if there is no backbuffer array, otherwise it will + * crash when ddraw is used (there swapchain->back_buffers is always + * NULL). We need this because this function is called from + * stateblock_init_default_state() to get the default scissorrect + * dimensions. */ + if (!swapchain->back_buffers || back_buffer_idx >= swapchain->state.desc.backbuffer_count) + { + WARN("Invalid back buffer index.\n"); + /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it + * here in wined3d to avoid problems in other libs. */ + return NULL; + } + + TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx].texture); + + return swapchain->back_buffers[back_buffer_idx].texture; +} + +struct wined3d_texture * CDECL wined3d_swapchain_get_front_buffer(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->front_buffer; +} + +struct wined3d_output * wined3d_swapchain_get_output(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->state.desc.output; +} + +HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain, + struct wined3d_raster_status *raster_status) +{ + struct wined3d_output *output; + + TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status); + + output = wined3d_swapchain_get_output(swapchain); + if (!output) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return E_FAIL; + } + + return wined3d_output_get_raster_status(output, raster_status); +} + +struct wined3d_swapchain_state * CDECL wined3d_swapchain_get_state(struct wined3d_swapchain *swapchain) +{ + return &swapchain->state; +} + +HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain, + struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation) +{ + struct wined3d_output *output; + HRESULT hr; + + TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation); + + if (!(output = wined3d_swapchain_get_output(swapchain))) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return E_FAIL; + } + + hr = wined3d_output_get_display_mode(output, mode, rotation); + + TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n", + mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id)); + + return hr; +} + +struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain) +{ + TRACE("swapchain %p.\n", swapchain); + + return swapchain->device; +} + +void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain, + struct wined3d_swapchain_desc *desc) +{ + TRACE("swapchain %p, desc %p.\n", swapchain, desc); + + *desc = swapchain->state.desc; +} + +HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain, + uint32_t flags, const struct wined3d_gamma_ramp *ramp) +{ + struct wined3d_output *output; + + TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + if (!(output = wined3d_swapchain_get_output(swapchain))) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return E_FAIL; + } + + return wined3d_output_set_gamma_ramp(output, ramp); +} + +void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette) +{ + TRACE("swapchain %p, palette %p.\n", swapchain, palette); + + wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + swapchain->palette = palette; +} + +HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain, + struct wined3d_gamma_ramp *ramp) +{ + struct wined3d_output *output; + + TRACE("swapchain %p, ramp %p.\n", swapchain, ramp); + + if (!(output = wined3d_swapchain_get_output(swapchain))) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return E_FAIL; + } + + return wined3d_output_get_gamma_ramp(output, ramp); +} + +/* The is a fallback for cases where we e.g. can't create a GL context or + * Vulkan swapchain for the swapchain window. */ +static void swapchain_blit_gdi(struct wined3d_swapchain *swapchain, + struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect) +{ + struct wined3d_texture *back_buffer = swapchain->back_buffers[0].texture; + D3DKMT_DESTROYDCFROMMEMORY destroy_desc; + D3DKMT_CREATEDCFROMMEMORY create_desc; + const struct wined3d_format *format; + unsigned int row_pitch, slice_pitch; + HDC src_dc, dst_dc; + NTSTATUS status; + HBITMAP bitmap; + + static unsigned int once; + + TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n", + swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect)); + + if (!once++) + FIXME("Using GDI present.\n"); + + format = back_buffer->resource.format; + if (!format->ddi_format) + { + WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id)); + return; + } + + wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_SYSMEM); + wined3d_texture_get_pitch(back_buffer, 0, &row_pitch, &slice_pitch); + + create_desc.pMemory = back_buffer->resource.heap_memory; + create_desc.Format = format->ddi_format; + create_desc.Width = wined3d_texture_get_level_width(back_buffer, 0); + create_desc.Height = wined3d_texture_get_level_height(back_buffer, 0); + create_desc.Pitch = row_pitch; + create_desc.hDeviceDc = CreateCompatibleDC(NULL); + create_desc.pColorTable = NULL; + + status = D3DKMTCreateDCFromMemory(&create_desc); + DeleteDC(create_desc.hDeviceDc); + if (status) + { + WARN("Failed to create DC, status %#lx.\n", status); + return; + } + + src_dc = create_desc.hDc; + bitmap = create_desc.hBitmap; + + TRACE("Created source DC %p, bitmap %p for backbuffer %p.\n", src_dc, bitmap, back_buffer); + + if (!(dst_dc = GetDCEx(swapchain->win_handle, 0, DCX_USESTYLE | DCX_CACHE))) + ERR("Failed to get destination DC.\n"); + + if (!StretchBlt(dst_dc, dst_rect->left, dst_rect->top, dst_rect->right - dst_rect->left, + dst_rect->bottom - dst_rect->top, src_dc, src_rect->left, src_rect->top, + src_rect->right - src_rect->left, src_rect->bottom - src_rect->top, SRCCOPY)) + ERR("Failed to blit.\n"); + + ReleaseDC(swapchain->win_handle, dst_dc); + destroy_desc.hDc = src_dc; + destroy_desc.hBitmap = bitmap; + if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc))) + ERR("Failed to destroy src dc, status %#lx.\n", status); +} + +/* A GL context is provided by the caller */ +static void swapchain_blit(const struct wined3d_swapchain *swapchain, + struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect) +{ + struct wined3d_texture *texture = swapchain->back_buffers[0].texture; + struct wined3d_device *device = swapchain->device; + enum wined3d_texture_filter_type filter; + DWORD location; + + TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n", + swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect)); + + if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left + && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top) + || is_complex_fixup(texture->resource.format->color_fixup)) + filter = WINED3D_TEXF_NONE; + else + filter = WINED3D_TEXF_LINEAR; + + location = WINED3D_LOCATION_TEXTURE_RGB; + if (texture->resource.multisample_type) + location = WINED3D_LOCATION_RB_RESOLVED; + + wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE); + device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, texture, 0, + location, src_rect, texture, 0, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter, NULL); + wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE); +} + +static void swapchain_gl_set_swap_interval(struct wined3d_swapchain *swapchain, + struct wined3d_context_gl *context_gl, unsigned int swap_interval) +{ + const struct wined3d_gl_info *gl_info = context_gl->gl_info; + + swap_interval = swap_interval <= 4 ? swap_interval : 1; + if (swapchain->swap_interval == swap_interval) + return; + + swapchain->swap_interval = swap_interval; + + if (!gl_info->supported[WGL_EXT_SWAP_CONTROL]) + return; + + if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval))) + { + ERR("Failed to set swap interval %u for context %p, last error %#lx.\n", + swap_interval, context_gl, GetLastError()); + } +} + +/* Context activation is done by the caller. */ +static void wined3d_swapchain_gl_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context) +{ + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_backbuffer *backbuffer, *backbuffer_prev; + struct wined3d_texture_gl *texture, *texture_prev; + struct gl_texture tex0; + HRGN dirty_region0; + GLuint rb0; + DWORD locations0; + unsigned int i; + static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE; + + if (swapchain->state.desc.backbuffer_count < 2) + return; + + backbuffer_prev = &swapchain->back_buffers[0]; + texture_prev = wined3d_texture_gl(backbuffer_prev->texture); + + /* Back buffer 0 is already in the draw binding. */ + dirty_region0 = backbuffer_prev->dirty_region; + tex0 = texture_prev->texture_rgb; + rb0 = texture_prev->rb_multisample; + locations0 = texture_prev->t.sub_resources[0].locations; + + for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i) + { + backbuffer = &swapchain->back_buffers[i]; + texture = wined3d_texture_gl(backbuffer->texture); + sub_resource = &texture->t.sub_resources[0]; + + if (!(sub_resource->locations & supported_locations)) + wined3d_texture_load_location(&texture->t, 0, context, texture->t.resource.draw_binding); + + backbuffer_prev->dirty_region = backbuffer->dirty_region; + texture_prev->texture_rgb = texture->texture_rgb; + texture_prev->rb_multisample = texture->rb_multisample; + + wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations)); + + backbuffer_prev = backbuffer; + texture_prev = texture; + } + + backbuffer_prev->dirty_region = dirty_region0; + texture_prev->texture_rgb = tex0; + texture_prev->rb_multisample = rb0; + + wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations)); + + device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER); +} + +static bool swapchain_present_is_partial_copy(struct wined3d_swapchain *swapchain, const RECT *dst_rect) +{ + enum wined3d_swap_effect swap_effect = swapchain->state.desc.swap_effect; + RECT client_rect; + unsigned int t; + + if (swap_effect != WINED3D_SWAP_EFFECT_COPY && swap_effect != WINED3D_SWAP_EFFECT_COPY_VSYNC) + return false; + + GetClientRect(swapchain->win_handle, &client_rect); + + t = client_rect.right - client_rect.left; + if ((dst_rect->left && dst_rect->right) || abs(dst_rect->right - dst_rect->left) != t) + return true; + t = client_rect.bottom - client_rect.top; + if ((dst_rect->top && dst_rect->bottom) || abs(dst_rect->bottom - dst_rect->top) != t) + return true; + + return false; +} + +static void swapchain_gl_present(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) +{ + struct wined3d_texture *back_buffer = swapchain->back_buffers[0].texture; + const struct wined3d_pixel_format *pixel_format; + const struct wined3d_gl_info *gl_info; + struct wined3d_context_gl *context_gl; + struct wined3d_context *context; + + context = context_acquire(swapchain->device, swapchain->front_buffer, 0); + context_gl = wined3d_context_gl(context); + if (!context_gl->valid) + { + context_release(context); + WARN("Invalid context, skipping present.\n"); + return; + } + + TRACE("Presenting DC %p.\n", context_gl->dc); + + pixel_format = &wined3d_adapter_gl(swapchain->device->adapter)->pixel_formats[context_gl->pixel_format]; + if (context_gl->dc == wined3d_device_gl(swapchain->device)->backup_dc + || (pixel_format->swap_method != WGL_SWAP_COPY_ARB + && swapchain_present_is_partial_copy(swapchain, dst_rect))) + { + swapchain_blit_gdi(swapchain, context, src_rect, dst_rect); + } + else + { + gl_info = context_gl->gl_info; + + swapchain_gl_set_swap_interval(swapchain, context_gl, swap_interval); + + wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding); + + swapchain_blit(swapchain, context, src_rect, dst_rect); + + if (swapchain->device->context_count > 1) + { + WARN_(d3d_perf)("Multiple contexts, calling glFinish() to enforce ordering.\n"); + gl_info->gl_ops.gl.p_glFinish(); + } + + /* call wglSwapBuffers through the gl table to avoid confusing the Steam overlay */ + gl_info->gl_ops.wgl.p_wglSwapBuffers(context_gl->dc); + } + + if (context->d3d_info->fences) + wined3d_context_gl_submit_command_fence(context_gl); + + wined3d_swapchain_gl_rotate(swapchain, context); + + TRACE("SwapBuffers called, Starting new frame\n"); + + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + + context_release(context); +} + +static void swapchain_frontbuffer_updated(struct wined3d_swapchain *swapchain) +{ + struct wined3d_texture *front_buffer = swapchain->front_buffer; + struct wined3d_context *context; + + context = context_acquire(swapchain->device, front_buffer, 0); + wined3d_texture_load_location(front_buffer, 0, context, front_buffer->resource.draw_binding); + context_release(context); + SetRectEmpty(&swapchain->front_buffer_update); +} + +static const struct wined3d_swapchain_ops swapchain_gl_ops = +{ + swapchain_gl_present, + swapchain_frontbuffer_updated, +}; + +static bool wined3d_swapchain_vk_present_mode_supported(struct wined3d_swapchain_vk *swapchain_vk, + VkPresentModeKHR vk_present_mode) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_vk_info *vk_info; + struct wined3d_adapter_vk *adapter_vk; + VkPhysicalDevice vk_physical_device; + VkPresentModeKHR *vk_modes; + bool supported = false; + uint32_t count, i; + VkResult vr; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + vk_physical_device = adapter_vk->physical_device; + vk_info = &adapter_vk->vk_info; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device, + swapchain_vk->vk_surface, &count, NULL))) < 0) + { + ERR("Failed to get supported present mode count, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + if (!(vk_modes = calloc(count, sizeof(*vk_modes)))) + return false; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device, + swapchain_vk->vk_surface, &count, vk_modes))) < 0) + { + ERR("Failed to get supported present modes, vr %s.\n", wined3d_debug_vkresult(vr)); + goto done; + } + + for (i = 0; i < count; ++i) + { + if (vk_modes[i] == vk_present_mode) + { + supported = true; + goto done; + } + } + +done: + free(vk_modes); + return supported; +} + +static VkFormat get_swapchain_fallback_format(VkFormat vk_format) +{ + switch (vk_format) + { + case VK_FORMAT_R8G8B8A8_SRGB: + return VK_FORMAT_B8G8R8A8_SRGB; + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + case VK_FORMAT_R16G16B16A16_SFLOAT: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + WARN("Unhandled format %#x.\n", vk_format); + return VK_FORMAT_UNDEFINED; + } +} + +static VkFormat wined3d_swapchain_vk_select_vk_format(struct wined3d_swapchain_vk *swapchain_vk, + VkSurfaceKHR vk_surface) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; + const struct wined3d_vk_info *vk_info; + struct wined3d_adapter_vk *adapter_vk; + const struct wined3d_format *format; + VkPhysicalDevice vk_physical_device; + VkSurfaceFormatKHR *vk_formats; + uint32_t format_count, i; + VkFormat vk_format; + VkResult vr; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + vk_physical_device = adapter_vk->physical_device; + vk_info = &adapter_vk->vk_info; + + if ((format = wined3d_get_format(&adapter_vk->a, desc->backbuffer_format, WINED3D_BIND_RENDER_TARGET))) + vk_format = wined3d_format_vk(format)->vk_format; + else + vk_format = VK_FORMAT_B8G8R8A8_UNORM; + + vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, vk_surface, &format_count, NULL)); + if (vr < 0 || !format_count) + { + WARN("Failed to get supported surface format count, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_FORMAT_UNDEFINED; + } + + if (!(vk_formats = calloc(format_count, sizeof(*vk_formats)))) + return VK_FORMAT_UNDEFINED; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, + vk_surface, &format_count, vk_formats))) < 0) + { + WARN("Failed to get supported surface formats, vr %s.\n", wined3d_debug_vkresult(vr)); + free(vk_formats); + return VK_FORMAT_UNDEFINED; + } + + for (i = 0; i < format_count; ++i) + { + if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + break; + } + if (i == format_count) + { + /* Try to create a swapchain with format conversion. */ + vk_format = get_swapchain_fallback_format(vk_format); + WARN("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format)); + for (i = 0; i < format_count; ++i) + { + if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + break; + } + } + free(vk_formats); + if (i == format_count) + { + FIXME("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format)); + return VK_FORMAT_UNDEFINED; + } + + TRACE("Using Vulkan swapchain format %#x.\n", vk_format); + + return vk_format; +} + +static bool wined3d_swapchain_vk_create_vulkan_swapchain_images(struct wined3d_swapchain_vk *swapchain_vk, + VkSwapchainKHR vk_swapchain) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_vk_info *vk_info; + VkSemaphoreCreateInfo semaphore_info; + uint32_t image_count, i; + VkResult vr; + + vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info; + + if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, vk_swapchain, &image_count, NULL))) < 0) + { + ERR("Failed to get image count, vr %s\n", wined3d_debug_vkresult(vr)); + return false; + } + + if (!(swapchain_vk->vk_images = calloc(image_count, sizeof(*swapchain_vk->vk_images)))) + { + ERR("Failed to allocate images array.\n"); + return false; + } + + if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, + vk_swapchain, &image_count, swapchain_vk->vk_images))) < 0) + { + ERR("Failed to get swapchain images, vr %s.\n", wined3d_debug_vkresult(vr)); + free(swapchain_vk->vk_images); + return false; + } + + if (!(swapchain_vk->vk_semaphores = calloc(image_count, sizeof(*swapchain_vk->vk_semaphores)))) + { + ERR("Failed to allocate semaphores array.\n"); + free(swapchain_vk->vk_images); + return false; + } + + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_info.pNext = NULL; + semaphore_info.flags = 0; + for (i = 0; i < image_count; ++i) + { + if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, + &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].available))) < 0) + { + ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, + &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].presentable))) < 0) + { + ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + } + swapchain_vk->image_count = image_count; + + return true; + +fail: + for (i = 0; i < image_count; ++i) + { + if (swapchain_vk->vk_semaphores[i].available) + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL)); + if (swapchain_vk->vk_semaphores[i].presentable) + VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL)); + } + free(swapchain_vk->vk_semaphores); + free(swapchain_vk->vk_images); + return false; +} + +static HRESULT wined3d_swapchain_vk_create_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; + VkSwapchainCreateInfoKHR vk_swapchain_desc; + VkWin32SurfaceCreateInfoKHR surface_desc; + unsigned int width, height, image_count; + const struct wined3d_vk_info *vk_info; + VkSurfaceCapabilitiesKHR surface_caps; + struct wined3d_adapter_vk *adapter_vk; + VkPresentModeKHR vk_present_mode; + VkSwapchainKHR vk_swapchain; + VkImageUsageFlags usage; + VkSurfaceKHR vk_surface; + VkBool32 supported; + VkFormat vk_format; + RECT client_rect; + VkResult vr; + + adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); + vk_info = &adapter_vk->vk_info; + + surface_desc.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surface_desc.pNext = NULL; + surface_desc.flags = 0; + surface_desc.hinstance = (HINSTANCE)GetWindowLongPtrW(swapchain_vk->s.win_handle, GWLP_HINSTANCE); + surface_desc.hwnd = swapchain_vk->s.win_handle; + if ((vr = VK_CALL(vkCreateWin32SurfaceKHR(vk_info->instance, &surface_desc, NULL, &vk_surface))) < 0) + { + ERR("Failed to create Vulkan surface, vr %s.\n", wined3d_debug_vkresult(vr)); + return E_FAIL; + } + swapchain_vk->vk_surface = vk_surface; + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceSupportKHR(adapter_vk->physical_device, + device_vk->graphics_queue.vk_queue_family_index, vk_surface, &supported))) < 0 || !supported) + { + ERR("Queue family does not support presentation on this surface, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + if ((vk_format = wined3d_swapchain_vk_select_vk_format(swapchain_vk, vk_surface)) == VK_FORMAT_UNDEFINED) + { + ERR("Failed to select swapchain format.\n"); + goto fail; + } + + if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(adapter_vk->physical_device, + swapchain_vk->vk_surface, &surface_caps))) < 0) + { + ERR("Failed to get surface capabilities, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + + image_count = desc->backbuffer_count; + if (image_count < surface_caps.minImageCount) + image_count = surface_caps.minImageCount; + else if (surface_caps.maxImageCount && image_count > surface_caps.maxImageCount) + image_count = surface_caps.maxImageCount; + + if (image_count != desc->backbuffer_count) + WARN("Image count %u is not supported (%u-%u).\n", desc->backbuffer_count, + surface_caps.minImageCount, surface_caps.maxImageCount); + + GetClientRect(swapchain_vk->s.win_handle, &client_rect); + + width = client_rect.right - client_rect.left; + if (width < surface_caps.minImageExtent.width) + width = surface_caps.minImageExtent.width; + else if (width > surface_caps.maxImageExtent.width) + width = surface_caps.maxImageExtent.width; + + height = client_rect.bottom - client_rect.top; + if (height < surface_caps.minImageExtent.height) + height = surface_caps.minImageExtent.height; + else if (height > surface_caps.maxImageExtent.height) + height = surface_caps.maxImageExtent.height; + + if (width != client_rect.right - client_rect.left || height != client_rect.bottom - client_rect.top) + WARN("Swapchain dimensions %lux%lu are not supported (%u-%u x %u-%u).\n", + client_rect.right - client_rect.left, client_rect.bottom - client_rect.top, + surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width, + surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height); + + usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT; + if (!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) || !(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) + WARN("Transfer not supported for swapchain images.\n"); + + if (!(surface_caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) + { + FIXME("Unsupported alpha mode, %#x.\n", surface_caps.supportedCompositeAlpha); + goto fail; + } + + vk_present_mode = VK_PRESENT_MODE_FIFO_KHR; + if (!swapchain_vk->s.swap_interval) + { + if (wined3d_swapchain_vk_present_mode_supported(swapchain_vk, VK_PRESENT_MODE_IMMEDIATE_KHR)) + vk_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; + else + FIXME("Unsupported swap interval %u.\n", swapchain_vk->s.swap_interval); + } + + vk_swapchain_desc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + vk_swapchain_desc.pNext = NULL; + vk_swapchain_desc.flags = 0; + vk_swapchain_desc.surface = vk_surface; + vk_swapchain_desc.minImageCount = image_count; + vk_swapchain_desc.imageFormat = vk_format; + vk_swapchain_desc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + vk_swapchain_desc.imageExtent.width = width; + vk_swapchain_desc.imageExtent.height = height; + vk_swapchain_desc.imageArrayLayers = 1; + vk_swapchain_desc.imageUsage = usage; + vk_swapchain_desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + vk_swapchain_desc.queueFamilyIndexCount = 0; + vk_swapchain_desc.pQueueFamilyIndices = NULL; + vk_swapchain_desc.preTransform = surface_caps.currentTransform; + vk_swapchain_desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + vk_swapchain_desc.presentMode = vk_present_mode; + vk_swapchain_desc.clipped = VK_TRUE; + vk_swapchain_desc.oldSwapchain = VK_NULL_HANDLE; + if ((vr = VK_CALL(vkCreateSwapchainKHR(device_vk->vk_device, &vk_swapchain_desc, NULL, &vk_swapchain))) < 0) + { + ERR("Failed to create Vulkan swapchain, vr %s.\n", wined3d_debug_vkresult(vr)); + goto fail; + } + swapchain_vk->vk_swapchain = vk_swapchain; + + if (!wined3d_swapchain_vk_create_vulkan_swapchain_images(swapchain_vk, vk_swapchain)) + { + VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, vk_swapchain, NULL)); + goto fail; + } + + swapchain_vk->width = width; + swapchain_vk->height = height; + + return WINED3D_OK; + +fail: + VK_CALL(vkDestroySurfaceKHR(vk_info->instance, vk_surface, NULL)); + swapchain_vk->vk_surface = 0; + return E_FAIL; +} + +static HRESULT wined3d_swapchain_vk_recreate(struct wined3d_swapchain_vk *swapchain_vk) +{ + TRACE("swapchain_vk %p.\n", swapchain_vk); + + wined3d_swapchain_vk_destroy_vulkan_swapchain(swapchain_vk); + + return wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk); +} + +static void wined3d_swapchain_vk_set_swap_interval(struct wined3d_swapchain_vk *swapchain_vk, + unsigned int swap_interval) +{ + if (swap_interval > 1) + { + if (swap_interval <= 4) + FIXME("Unsupported swap interval %u.\n", swap_interval); + swap_interval = 1; + } + + if (swapchain_vk->s.swap_interval == swap_interval) + return; + + swapchain_vk->s.swap_interval = swap_interval; + wined3d_swapchain_vk_recreate(swapchain_vk); +} + +static VkResult wined3d_swapchain_vk_blit(struct wined3d_swapchain_vk *swapchain_vk, + struct wined3d_context_vk *context_vk, const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval) +{ + struct wined3d_texture_vk *back_buffer_vk = wined3d_texture_vk(swapchain_vk->s.back_buffers[0].texture); + struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device); + const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBuffer vk_command_buffer; + VkImageSubresourceRange vk_range; + VkPresentInfoKHR present_desc; + unsigned int present_idx; + VkImageLayout vk_layout; + uint32_t image_idx; + RECT dst_rect_tmp; + VkImageBlit blit; + VkFilter filter; + VkResult vr; + + static const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + TRACE("swapchain_vk %p, context_vk %p, src_rect %s, dst_rect %s, swap_interval %u.\n", + swapchain_vk, context_vk, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect), swap_interval); + + wined3d_swapchain_vk_set_swap_interval(swapchain_vk, swap_interval); + + present_idx = swapchain_vk->current++ % swapchain_vk->image_count; + wined3d_context_vk_wait_command_buffer(context_vk, swapchain_vk->vk_semaphores[present_idx].command_buffer_id); + if ((vr = VK_CALL(vkAcquireNextImageKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, UINT64_MAX, + swapchain_vk->vk_semaphores[present_idx].available, VK_NULL_HANDLE, &image_idx))) < 0) + { + WARN("Failed to acquire image, vr %s.\n", wined3d_debug_vkresult(vr)); + return vr; + } + + if (dst_rect->right > swapchain_vk->width || dst_rect->bottom > swapchain_vk->height) + { + dst_rect_tmp = *dst_rect; + if (dst_rect->right > swapchain_vk->width) + dst_rect_tmp.right = swapchain_vk->width; + if (dst_rect->bottom > swapchain_vk->height) + dst_rect_tmp.bottom = swapchain_vk->height; + dst_rect = &dst_rect_tmp; + } + filter = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left + || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top + ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; + vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk); + + wined3d_context_vk_end_current_render_pass(context_vk); + + vk_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vk_range.baseMipLevel = 0; + vk_range.levelCount = 1; + vk_range.baseArrayLayer = 0; + vk_range.layerCount = 1; + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_READ_BIT, + back_buffer_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + back_buffer_vk->image.vk_image, &vk_range); + + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + swapchain_vk->vk_images[image_idx], &vk_range); + + blit.srcSubresource.aspectMask = vk_range.aspectMask; + blit.srcSubresource.mipLevel = vk_range.baseMipLevel; + blit.srcSubresource.baseArrayLayer = vk_range.baseArrayLayer; + blit.srcSubresource.layerCount = vk_range.layerCount; + blit.srcOffsets[0].x = src_rect->left; + blit.srcOffsets[0].y = src_rect->top; + blit.srcOffsets[0].z = 0; + blit.srcOffsets[1].x = src_rect->right; + blit.srcOffsets[1].y = src_rect->bottom; + blit.srcOffsets[1].z = 1; + blit.dstSubresource = blit.srcSubresource; + blit.dstOffsets[0].x = dst_rect->left; + blit.dstOffsets[0].y = dst_rect->top; + blit.dstOffsets[0].z = 0; + blit.dstOffsets[1].x = dst_rect->right; + blit.dstOffsets[1].y = dst_rect->bottom; + blit.dstOffsets[1].z = 1; + VK_CALL(vkCmdBlitImage(vk_command_buffer, + back_buffer_vk->image.vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + swapchain_vk->vk_images[image_idx], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, filter)); + + wined3d_context_vk_reference_texture(context_vk, back_buffer_vk); + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + swapchain_vk->vk_images[image_idx], &vk_range); + + if (desc->swap_effect == WINED3D_SWAP_EFFECT_DISCARD || desc->swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD) + vk_layout = VK_IMAGE_LAYOUT_UNDEFINED; + else + vk_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + wined3d_context_vk_image_barrier(context_vk, vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags), + vk_layout, back_buffer_vk->layout, + back_buffer_vk->image.vk_image, &vk_range); + back_buffer_vk->bind_mask = 0; + + swapchain_vk->vk_semaphores[present_idx].command_buffer_id = context_vk->current_command_buffer.id; + wined3d_context_vk_submit_command_buffer(context_vk, + 1, &swapchain_vk->vk_semaphores[present_idx].available, &wait_stage, + 1, &swapchain_vk->vk_semaphores[present_idx].presentable); + + present_desc.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_desc.pNext = NULL; + present_desc.waitSemaphoreCount = 1; + present_desc.pWaitSemaphores = &swapchain_vk->vk_semaphores[present_idx].presentable; + present_desc.swapchainCount = 1; + present_desc.pSwapchains = &swapchain_vk->vk_swapchain; + present_desc.pImageIndices = &image_idx; + present_desc.pResults = NULL; + if ((vr = VK_CALL(vkQueuePresentKHR(device_vk->graphics_queue.vk_queue, &present_desc)))) + WARN("Present returned vr %s.\n", wined3d_debug_vkresult(vr)); + return vr; +} + +static void wined3d_swapchain_vk_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context_vk *context_vk) +{ + struct wined3d_texture_sub_resource *sub_resource; + struct wined3d_backbuffer *backbuffer, *backbuffer_prev; + struct wined3d_texture_vk *texture, *texture_prev; + struct wined3d_image_vk image0; + VkDescriptorImageInfo vk_info0; + VkImageLayout vk_layout0; + uint32_t bind_mask0; + HRGN dirty_region0; + DWORD locations0; + unsigned int i; + + static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE; + + if (swapchain->state.desc.backbuffer_count < 2) + return; + + backbuffer_prev = &swapchain->back_buffers[0]; + texture_prev = wined3d_texture_vk(backbuffer_prev->texture); + + /* Back buffer 0 is already in the draw binding. */ + dirty_region0 = backbuffer_prev->dirty_region; + image0 = texture_prev->image; + vk_layout0 = texture_prev->layout; + bind_mask0 = texture_prev->bind_mask; + vk_info0 = texture_prev->default_image_info; + locations0 = texture_prev->t.sub_resources[0].locations; + + for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i) + { + backbuffer = &swapchain->back_buffers[i]; + texture = wined3d_texture_vk(backbuffer->texture); + sub_resource = &texture->t.sub_resources[0]; + + if (!(sub_resource->locations & supported_locations)) + wined3d_texture_load_location(&texture->t, 0, &context_vk->c, texture->t.resource.draw_binding); + + backbuffer_prev->dirty_region = backbuffer->dirty_region; + texture_prev->image = texture->image; + texture_prev->layout = texture->layout; + texture_prev->bind_mask = texture->bind_mask; + texture_prev->default_image_info = texture->default_image_info; + + wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations)); + + backbuffer_prev = backbuffer; + texture_prev = texture; + } + + backbuffer_prev->dirty_region = dirty_region0; + texture_prev->image = image0; + texture_prev->layout = vk_layout0; + texture_prev->bind_mask = bind_mask0; + texture_prev->default_image_info = vk_info0; + + wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations); + wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations)); + + device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER); +} + +static void swapchain_vk_present(struct wined3d_swapchain *swapchain, const RECT *src_rect, + const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) +{ + struct wined3d_swapchain_vk *swapchain_vk = wined3d_swapchain_vk(swapchain); + struct wined3d_texture *back_buffer = swapchain->back_buffers[0].texture; + struct wined3d_context_vk *context_vk; + VkResult vr; + HRESULT hr; + + context_vk = wined3d_context_vk(context_acquire(swapchain->device, back_buffer, 0)); + + if (!swapchain_vk->vk_swapchain || swapchain_present_is_partial_copy(swapchain, dst_rect)) + { + swapchain_blit_gdi(swapchain, &context_vk->c, src_rect, dst_rect); + } + else + { + wined3d_texture_load_location(back_buffer, 0, &context_vk->c, back_buffer->resource.draw_binding); + + if ((vr = wined3d_swapchain_vk_blit(swapchain_vk, context_vk, src_rect, dst_rect, swap_interval))) + { + if (vr == VK_ERROR_OUT_OF_DATE_KHR || vr == VK_SUBOPTIMAL_KHR) + { + if (FAILED(hr = wined3d_swapchain_vk_recreate(swapchain_vk))) + ERR("Failed to recreate swapchain, hr %#lx.\n", hr); + else if (vr == VK_ERROR_OUT_OF_DATE_KHR && (vr = wined3d_swapchain_vk_blit( + swapchain_vk, context_vk, src_rect, dst_rect, swap_interval))) + ERR("Failed to blit image, vr %s.\n", wined3d_debug_vkresult(vr)); + } + else + { + ERR("Failed to blit image, vr %s.\n", wined3d_debug_vkresult(vr)); + } + } + } + + wined3d_swapchain_vk_rotate(swapchain, context_vk); + + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + + TRACE("Starting new frame.\n"); + + context_release(&context_vk->c); +} + +static const struct wined3d_swapchain_ops swapchain_vk_ops = +{ + swapchain_vk_present, + swapchain_frontbuffer_updated, +}; + +static void swapchain_gdi_frontbuffer_updated(struct wined3d_swapchain *swapchain) +{ + struct wined3d_dc_info *front; + POINT offset = {0, 0}; + HDC src_dc, dst_dc; + RECT draw_rect; + HWND window; + + TRACE("swapchain %p.\n", swapchain); + + front = &swapchain->front_buffer->dc_info[0]; + if (swapchain->palette) + wined3d_palette_apply_to_dc(swapchain->palette, front->dc); + + if (swapchain->front_buffer->resource.map_count) + ERR("Trying to blit a mapped surface.\n"); + + TRACE("Copying surface %p to screen.\n", front); + + src_dc = front->dc; + window = swapchain->win_handle; + dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE); + + /* Front buffer coordinates are screen coordinates. Map them to the + * destination window if not fullscreened. */ + if (swapchain->state.desc.windowed) + ClientToScreen(window, &offset); + + TRACE("offset %s.\n", wine_dbgstr_point(&offset)); + + SetRect(&draw_rect, 0, 0, swapchain->front_buffer->resource.width, + swapchain->front_buffer->resource.height); + IntersectRect(&draw_rect, &draw_rect, &swapchain->front_buffer_update); + + BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y, + draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top, + src_dc, draw_rect.left, draw_rect.top, SRCCOPY); + ReleaseDC(window, dst_dc); + + SetRectEmpty(&swapchain->front_buffer_update); +} + +static void swapchain_gdi_present(struct wined3d_swapchain *swapchain, + const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, uint32_t flags) +{ + struct wined3d_dc_info *front, *back; + HBITMAP bitmap; + void *heap_pointer; + void *heap_memory; + HDC dc; + + front = &swapchain->front_buffer->dc_info[0]; + back = &swapchain->back_buffers[0].texture->dc_info[0]; + + /* Flip the surface data. */ + dc = front->dc; + bitmap = front->bitmap; + heap_pointer = swapchain->front_buffer->resource.heap_pointer; + heap_memory = swapchain->front_buffer->resource.heap_memory; + + front->dc = back->dc; + front->bitmap = back->bitmap; + swapchain->front_buffer->resource.heap_pointer = swapchain->back_buffers[0].texture->resource.heap_pointer; + swapchain->front_buffer->resource.heap_memory = swapchain->back_buffers[0].texture->resource.heap_memory; + + back->dc = dc; + back->bitmap = bitmap; + swapchain->back_buffers[0].texture->resource.heap_pointer = heap_pointer; + swapchain->back_buffers[0].texture->resource.heap_memory = heap_memory; + + SetRect(&swapchain->front_buffer_update, 0, 0, + swapchain->front_buffer->resource.width, + swapchain->front_buffer->resource.height); + swapchain_gdi_frontbuffer_updated(swapchain); +} + +static const struct wined3d_swapchain_ops swapchain_no3d_ops = +{ + swapchain_gdi_present, + swapchain_gdi_frontbuffer_updated, +}; + +static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_swapchain *swapchain, + enum wined3d_format_id format_id, enum wined3d_multisample_type *type, unsigned int *quality) +{ + const struct wined3d_adapter *adapter; + const struct wined3d_format *format; + enum wined3d_multisample_type t; + + if (wined3d_settings.sample_count == ~0u) + return; + + adapter = swapchain->device->adapter; + if (!(format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET))) + return; + + if ((t = min(wined3d_settings.sample_count, adapter->d3d_info.limits.sample_count))) + while (!(format->multisample_types & 1u << (t - 1))) + ++t; + TRACE("Using sample count %u.\n", t); + *type = t; + *quality = 0; +} + +void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, const struct wined3d_device *device) +{ + /* Subtract 1 for the implicit OpenGL latency. */ + swapchain->max_frame_latency = device->max_frame_latency >= 2 ? device->max_frame_latency - 1 : 1; +} + +static enum wined3d_format_id adapter_format_from_backbuffer_format(const struct wined3d_adapter *adapter, + enum wined3d_format_id format_id) +{ + const struct wined3d_format *backbuffer_format; + + backbuffer_format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET); + return pixelformat_for_depth(backbuffer_format->byte_count * CHAR_BIT); +} + +static HRESULT wined3d_swapchain_state_init(struct wined3d_swapchain_state *state, + const struct wined3d_swapchain_desc *desc, HWND window, struct wined3d *wined3d, + struct wined3d_swapchain_state_parent *parent) +{ + HRESULT hr; + + state->desc = *desc; + + if (FAILED(hr = wined3d_output_get_display_mode(desc->output, &state->original_mode, NULL))) + { + ERR("Failed to get current display mode, hr %#lx.\n", hr); + return hr; + } + + if (state->desc.windowed) + { + RECT client_rect; + + GetClientRect(window, &client_rect); + TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect)); + + if (!state->desc.backbuffer_width) + { + state->desc.backbuffer_width = client_rect.right ? client_rect.right : 8; + TRACE("Updating width to %u.\n", state->desc.backbuffer_width); + } + if (!state->desc.backbuffer_height) + { + state->desc.backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; + TRACE("Updating height to %u.\n", state->desc.backbuffer_height); + } + + if (state->desc.backbuffer_format == WINED3DFMT_UNKNOWN) + { + state->desc.backbuffer_format = state->original_mode.format_id; + TRACE("Updating format to %s.\n", debug_d3dformat(state->original_mode.format_id)); + } + } + else + { + if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + state->d3d_mode.width = desc->backbuffer_width; + state->d3d_mode.height = desc->backbuffer_height; + state->d3d_mode.format_id = adapter_format_from_backbuffer_format(desc->output->adapter, + desc->backbuffer_format); + state->d3d_mode.refresh_rate = desc->refresh_rate; + state->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; + } + else + { + state->d3d_mode = state->original_mode; + } + } + + GetWindowRect(window, &state->original_window_rect); + state->wined3d = wined3d; + state->device_window = window; + state->desc.device_window = window; + state->parent = parent; + + if (desc->flags & WINED3D_SWAPCHAIN_REGISTER_STATE) + wined3d_swapchain_state_register(state); + + return hr; +} + +static HRESULT swapchain_create_texture(struct wined3d_swapchain *swapchain, + bool front, bool depth, struct wined3d_texture **texture) +{ + const struct wined3d_swapchain_desc *swapchain_desc = &swapchain->state.desc; + struct wined3d_device *device = swapchain->device; + struct wined3d_resource_desc texture_desc; + uint32_t texture_flags = 0; + HRESULT hr; + + texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D; + texture_desc.format = depth ? swapchain_desc->auto_depth_stencil_format : swapchain_desc->backbuffer_format; + texture_desc.multisample_type = swapchain_desc->multisample_type; + texture_desc.multisample_quality = swapchain_desc->multisample_quality; + texture_desc.usage = 0; + if (!depth && (device->wined3d->flags & WINED3D_NO3D)) + texture_desc.usage |= WINED3DUSAGE_OWNDC; + if (device->wined3d->flags & WINED3D_NO3D) + texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU; + else + texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU; + if (!depth && (swapchain_desc->flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER)) + texture_desc.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W; + texture_desc.width = swapchain_desc->backbuffer_width; + texture_desc.height = swapchain_desc->backbuffer_height; + texture_desc.depth = 1; + texture_desc.size = 0; + + if (front) + texture_desc.bind_flags = 0; + else if (depth) + texture_desc.bind_flags = WINED3D_BIND_DEPTH_STENCIL; + else + texture_desc.bind_flags = swapchain_desc->backbuffer_bind_flags; + + if (swapchain_desc->flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE) + texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC; + + if (FAILED(hr = wined3d_texture_create(device, &texture_desc, 1, 1, + texture_flags, NULL, NULL, &wined3d_null_parent_ops, texture))) + { + WARN("Failed to create texture, hr %#lx.\n", hr); + return hr; + } + + if (!depth) + wined3d_texture_set_swapchain(*texture, swapchain); + + return S_OK; +} + +static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, + const struct wined3d_swapchain_ops *swapchain_ops) +{ + struct wined3d_output_desc output_desc; + BOOL displaymode_set = FALSE; + HRESULT hr = E_FAIL; + unsigned int i; + HWND window; + + wined3d_mutex_lock(); + + if (desc->backbuffer_count > 1) + { + FIXME("The application requested more than one back buffer, this is not properly supported.\n" + "Please configure the application to use double buffering (1 back buffer) if possible.\n"); + } + + if (desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD + && desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL + && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY) + FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect); + + window = desc->device_window ? desc->device_window : device->create_parms.focus_window; + TRACE("Using target window %p.\n", window); + + if (FAILED(hr = wined3d_swapchain_state_init(&swapchain->state, desc, window, device->wined3d, state_parent))) + { + ERR("Failed to initialise swapchain state, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + + swapchain->swapchain_ops = swapchain_ops; + swapchain->device = device; + swapchain->parent = parent; + swapchain->parent_ops = parent_ops; + swapchain->ref = 1; + swapchain->win_handle = window; + swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT; + swapchain_set_max_frame_latency(swapchain, device); + + if (!swapchain->state.desc.windowed) + { + if (FAILED(hr = wined3d_output_get_desc(desc->output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + goto err; + } + + wined3d_swapchain_state_setup_fullscreen(&swapchain->state, window, + output_desc.desktop_rect.left, output_desc.desktop_rect.top, desc->backbuffer_width, + desc->backbuffer_height); + } + wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->state.desc.backbuffer_format, + &swapchain->state.desc.multisample_type, &swapchain->state.desc.multisample_quality); + + TRACE("Creating front buffer.\n"); + + if (FAILED(hr = swapchain_create_texture(swapchain, true, false, &swapchain->front_buffer))) + { + WARN("Failed to create front buffer, hr %#lx.\n", hr); + goto err; + } + + if (!(device->wined3d->flags & WINED3D_NO3D)) + { + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + } + + /* MSDN says we're only allowed a single fullscreen swapchain per device, + * so we should really check to see if there is a fullscreen swapchain + * already. Does a single head count as full screen? */ + if (!desc->windowed && desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + /* Change the display settings */ + if (FAILED(hr = wined3d_output_set_display_mode(desc->output, + &swapchain->state.d3d_mode))) + { + WARN("Failed to set display mode, hr %#lx.\n", hr); + goto err; + } + displaymode_set = TRUE; + } + + if (swapchain->state.desc.backbuffer_count > 0) + { + if (!(swapchain->back_buffers = calloc(swapchain->state.desc.backbuffer_count, + sizeof(*swapchain->back_buffers)))) + { + ERR("Failed to allocate backbuffer array memory.\n"); + hr = E_OUTOFMEMORY; + goto err; + } + + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + TRACE("Creating back buffer %u.\n", i); + if (FAILED(hr = swapchain_create_texture(swapchain, false, false, &swapchain->back_buffers[i].texture))) + { + WARN("Failed to create back buffer %u, hr %#lx.\n", i, hr); + swapchain->state.desc.backbuffer_count = i; + goto err; + } + } + } + + /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */ + if (desc->enable_auto_depth_stencil) + { + TRACE("Creating depth/stencil buffer.\n"); + if (!device->auto_depth_stencil_view) + { + struct wined3d_view_desc desc; + struct wined3d_texture *ds; + + if (FAILED(hr = swapchain_create_texture(swapchain, false, true, &ds))) + { + WARN("Failed to create the auto depth/stencil surface, hr %#lx.\n", hr); + goto err; + } + + desc.format_id = ds->resource.format->id; + desc.flags = 0; + desc.u.texture.level_idx = 0; + desc.u.texture.level_count = 1; + desc.u.texture.layer_idx = 0; + desc.u.texture.layer_count = 1; + hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops, + &device->auto_depth_stencil_view); + wined3d_texture_decref(ds); + if (FAILED(hr)) + { + ERR("Failed to create rendertarget view, hr %#lx.\n", hr); + goto err; + } + } + } + + wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma); + + wined3d_mutex_unlock(); + + return WINED3D_OK; + +err: + if (displaymode_set) + { + if (FAILED(wined3d_restore_display_modes(device->wined3d))) + ERR("Failed to restore display mode.\n"); + } + + if (swapchain->back_buffers) + { + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + if (swapchain->back_buffers[i].texture) + { + wined3d_texture_set_swapchain(swapchain->back_buffers[i].texture, NULL); + wined3d_texture_decref(swapchain->back_buffers[i].texture); + } + // No Present possible, no need to check and free dirty HRGN + } + free(swapchain->back_buffers); + } + + if (swapchain->front_buffer) + { + wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); + wined3d_texture_decref(swapchain->front_buffer); + } + + wined3d_swapchain_state_cleanup(&swapchain->state); + wined3d_mutex_unlock(); + + return hr; +} + +HRESULT wined3d_swapchain_no3d_init(struct wined3d_swapchain *swapchain_no3d, struct wined3d_device *device, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("swapchain_no3d %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", + swapchain_no3d, device, desc, state_parent, parent, parent_ops); + + return wined3d_swapchain_init(swapchain_no3d, device, desc, state_parent, parent, parent_ops, + &swapchain_no3d_ops); +} + +HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, struct wined3d_device *device, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + TRACE("swapchain_gl %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", + swapchain_gl, device, desc, state_parent, parent, parent_ops); + + return wined3d_swapchain_init(&swapchain_gl->s, device, desc, state_parent, parent, + parent_ops, &swapchain_gl_ops); +} + +HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, struct wined3d_device *device, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops) +{ + HRESULT hr; + + TRACE("swapchain_vk %p, device %p, desc %p, parent %p, parent_ops %p.\n", + swapchain_vk, device, desc, parent, parent_ops); + + if (FAILED(hr = wined3d_swapchain_init(&swapchain_vk->s, device, desc, state_parent, parent, + parent_ops, &swapchain_vk_ops))) + return hr; + + if (swapchain_vk->s.win_handle == GetDesktopWindow()) + { + WARN("Creating a desktop window swapchain.\n"); + return hr; + } + + if (FAILED(hr = wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk))) + { + WARN("Failed to create a Vulkan swapchain, hr %#lx.\n", hr); + return hr; + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_swapchain **swapchain) +{ + struct wined3d_swapchain *object; + HRESULT hr; + + if (FAILED(hr = device->adapter->adapter_ops->adapter_create_swapchain(device, + desc, state_parent, parent, parent_ops, &object))) + return hr; + + if (desc->flags & WINED3D_SWAPCHAIN_IMPLICIT) + { + wined3d_mutex_lock(); + if (FAILED(hr = wined3d_device_set_implicit_swapchain(device, object))) + { + wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT); + device->adapter->adapter_ops->adapter_destroy_swapchain(object); + wined3d_mutex_unlock(); + return hr; + } + wined3d_mutex_unlock(); + } + + *swapchain = object; + + return hr; +} + +static struct wined3d_context_gl *wined3d_swapchain_gl_create_context(struct wined3d_swapchain_gl *swapchain_gl) +{ + struct wined3d_device *device = swapchain_gl->s.device; + struct wined3d_context_gl *context_gl; + + TRACE("Creating a new context for swapchain %p, thread %lu.\n", swapchain_gl, GetCurrentThreadId()); + + wined3d_from_cs(device->cs); + + if (!(context_gl = calloc(1, sizeof(*context_gl)))) + { + ERR("Failed to allocate context memory.\n"); + return NULL; + } + + if (FAILED(wined3d_context_gl_init(context_gl, swapchain_gl))) + { + WARN("Failed to initialise context.\n"); + free(context_gl); + return NULL; + } + + if (!device_context_add(device, &context_gl->c)) + { + ERR("Failed to add the newly created context to the context list.\n"); + wined3d_context_gl_destroy(context_gl); + return NULL; + } + + TRACE("Created context %p.\n", context_gl); + + context_release(&context_gl->c); + + return context_gl; +} + +struct wined3d_context_gl *wined3d_swapchain_gl_get_context(struct wined3d_swapchain_gl *swapchain_gl) +{ + struct wined3d_device *device = swapchain_gl->s.device; + DWORD tid = GetCurrentThreadId(); + unsigned int i; + + for (i = 0; i < device->context_count; ++i) + { + if (wined3d_context_gl(device->contexts[i])->tid == tid) + return wined3d_context_gl(device->contexts[i]); + } + + /* Create a new context for the thread. */ + return wined3d_swapchain_gl_create_context(swapchain_gl); +} + +void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain) +{ + UINT i; + + wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource); + + for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i) + { + wined3d_resource_update_draw_binding(&swapchain->back_buffers[i].texture->resource); + } +} + +void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate) +{ + struct wined3d_device *device = swapchain->device; + HWND window = swapchain->state.device_window; + struct wined3d_output_desc output_desc; + unsigned int screensaver_active; + struct wined3d_output *output; + BOOL focus_messages, filter; + HRESULT hr; + + /* This code is not protected by the wined3d mutex, so it may run while + * wined3d_device_reset is active. Testing on Windows shows that changing + * focus during resets and resetting during focus change events causes + * the application to crash with an invalid memory access. */ + + if (!(focus_messages = device->wined3d->flags & WINED3D_FOCUS_MESSAGES)) + filter = wined3d_filter_messages(window, TRUE); + + if (activate) + { + SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &screensaver_active, 0); + if ((device->restore_screensaver = !!screensaver_active)) + SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0); + + if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES)) + { + /* The d3d versions do not agree on the exact messages here. D3d8 restores + * the window but leaves the size untouched, d3d9 sets the size on an + * invisible window, generates messages but doesn't change the window + * properties. The implementation follows d3d9. + * + * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to + * resume drawing after a focus loss. */ + output = wined3d_swapchain_get_output(swapchain); + if (!output) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return; + } + + if (SUCCEEDED(hr = wined3d_output_get_desc(output, &output_desc))) + SetWindowPos(window, NULL, output_desc.desktop_rect.left, + output_desc.desktop_rect.top, swapchain->state.desc.backbuffer_width, + swapchain->state.desc.backbuffer_height, SWP_NOACTIVATE | SWP_NOZORDER); + else + ERR("Failed to get output description, hr %#lx.\n", hr); + } + + if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE) + { + output = wined3d_swapchain_get_output(swapchain); + if (!output) + { + ERR("Failed to get output from swapchain %p.\n", swapchain); + return; + } + + if (FAILED(hr = wined3d_output_set_display_mode(output, + &swapchain->state.d3d_mode))) + ERR("Failed to set display mode, hr %#lx.\n", hr); + } + + if (swapchain == device->swapchains[0]) + device->device_parent->ops->activate(device->device_parent, TRUE); + } + else + { + if (device->restore_screensaver) + { + SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0); + device->restore_screensaver = FALSE; + } + + if (FAILED(hr = wined3d_restore_display_modes(device->wined3d))) + ERR("Failed to restore display modes, hr %#lx.\n", hr); + + swapchain->reapply_mode = TRUE; + + /* Some DDraw apps (Deus Ex: GOTY, and presumably all UT 1 based games) destroy the device + * during window minimization. Do our housekeeping now, as the device may not exist after + * the ShowWindow call. + * + * In d3d9, the device is marked lost after the window is minimized. If we find an app + * that needs this behavior (e.g. because it calls TestCooperativeLevel in the window proc) + * we'll have to control this via a create flag. Note that the device and swapchain are not + * safe to access after the ShowWindow call. */ + if (swapchain == device->swapchains[0]) + device->device_parent->ops->activate(device->device_parent, FALSE); + + if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES) && IsWindowVisible(window)) + ShowWindow(window, SW_MINIMIZE); + } + + if (!focus_messages) + wined3d_filter_messages(window, filter); +} + +HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count, + unsigned int width, unsigned int height, enum wined3d_format_id format_id, + enum wined3d_multisample_type multisample_type, unsigned int multisample_quality) +{ + struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + bool recreate = false; + + TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, " + "multisample_type %#x, multisample_quality %#x.\n", + swapchain, buffer_count, width, height, debug_d3dformat(format_id), + multisample_type, multisample_quality); + + wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality); + + if (buffer_count && buffer_count != desc->backbuffer_count) + FIXME("Cannot change the back buffer count yet.\n"); + + wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT); + + if (!width || !height) + { + RECT client_rect; + + /* The application is requesting that either the swapchain width or + * height be set to the corresponding dimension in the window's + * client rect. */ + + if (!GetClientRect(swapchain->state.device_window, &client_rect)) + { + ERR("Failed to get client rect, last error %#lx.\n", GetLastError()); + return WINED3DERR_INVALIDCALL; + } + + if (!width) + width = client_rect.right; + + if (!height) + height = client_rect.bottom; + } + + if (width != desc->backbuffer_width || height != desc->backbuffer_height) + { + desc->backbuffer_width = width; + desc->backbuffer_height = height; + recreate = true; + } + + if (format_id == WINED3DFMT_UNKNOWN) + { + if (!desc->windowed) + return WINED3DERR_INVALIDCALL; + format_id = swapchain->state.original_mode.format_id; + } + + if (format_id != desc->backbuffer_format) + { + desc->backbuffer_format = format_id; + recreate = true; + } + + if (multisample_type != desc->multisample_type + || multisample_quality != desc->multisample_quality) + { + desc->multisample_type = multisample_type; + desc->multisample_quality = multisample_quality; + recreate = true; + } + + if (recreate) + { + struct wined3d_texture *new_texture; + HRESULT hr; + UINT i; + + TRACE("Recreating swapchain textures.\n"); + + if (FAILED(hr = swapchain_create_texture(swapchain, true, false, &new_texture))) + return hr; + wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); + if (wined3d_texture_decref(swapchain->front_buffer)) + ERR("Something's still holding the front buffer (%p).\n", swapchain->front_buffer); + swapchain->front_buffer = new_texture; + + if (!(swapchain->device->wined3d->flags & WINED3D_NO3D)) + { + wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE); + wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE); + } + + for (i = 0; i < desc->backbuffer_count; ++i) + { + struct wined3d_backbuffer *backbuffer = &swapchain->back_buffers[i]; + if (FAILED(hr = swapchain_create_texture(swapchain, false, false, &new_texture))) + return hr; + wined3d_texture_set_swapchain(backbuffer->texture, NULL); + if (wined3d_texture_decref(backbuffer->texture)) + ERR("Something's still holding back buffer %u (%p).\n", i, backbuffer->texture); + backbuffer->texture = new_texture; + if (backbuffer->dirty_region) + { + DeleteObject(backbuffer->dirty_region); + backbuffer->dirty_region = NULL; + } + } + } + + swapchain_update_draw_bindings(swapchain); + + return WINED3D_OK; +} + +static HRESULT wined3d_swapchain_state_set_display_mode(struct wined3d_swapchain_state *state, + struct wined3d_output *output, struct wined3d_display_mode *mode) +{ + HRESULT hr; + + if (state->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE) + { + if (FAILED(hr = wined3d_output_find_closest_matching_mode(output, mode))) + { + WARN("Failed to find closest matching mode, hr %#lx.\n", hr); + } + } + + if (output != state->desc.output) + { + if (FAILED(hr = wined3d_restore_display_modes(state->wined3d))) + { + WARN("Failed to restore display modes, hr %#lx.\n", hr); + return hr; + } + + if (FAILED(hr = wined3d_output_get_display_mode(output, &state->original_mode, NULL))) + { + WARN("Failed to get current display mode, hr %#lx.\n", hr); + return hr; + } + } + + if (FAILED(hr = wined3d_output_set_display_mode(output, mode))) + { + WARN("Failed to set display mode, hr %#lx.\n", hr); + return WINED3DERR_INVALIDCALL; + } + + return WINED3D_OK; +} + +HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_state *state, + const struct wined3d_display_mode *mode) +{ + struct wined3d_display_mode actual_mode; + struct wined3d_output_desc output_desc; + RECT original_window_rect, window_rect; + int x, y, width, height; + HWND window; + HRESULT hr; + + TRACE("state %p, mode %p.\n", state, mode); + + wined3d_mutex_lock(); + + window = state->device_window; + + if (state->desc.windowed) + { + SetRect(&window_rect, 0, 0, mode->width, mode->height); + AdjustWindowRectEx(&window_rect, + GetWindowLongW(window, GWL_STYLE), FALSE, + GetWindowLongW(window, GWL_EXSTYLE)); + GetWindowRect(window, &original_window_rect); + + x = original_window_rect.left; + y = original_window_rect.top; + width = window_rect.right - window_rect.left; + height = window_rect.bottom - window_rect.top; + } + else + { + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; + + GetWindowRect(window, &window_rect); + if (width != window_rect.right - window_rect.left || height != window_rect.bottom - window_rect.top) + { + TRACE("Update saved window state.\n"); + state->original_window_rect = window_rect; + } + + if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + actual_mode = *mode; + if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, state->desc.output, + &actual_mode))) + { + ERR("Failed to set display mode, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; + } + x = output_desc.desktop_rect.left; + y = output_desc.desktop_rect.top; + } + + wined3d_mutex_unlock(); + + MoveWindow(window, x, y, width, height, TRUE); + + return WINED3D_OK; +} + +static LONG fullscreen_style(LONG style) +{ + /* Make sure the window is managed, otherwise we won't get keyboard input. */ + style |= WS_POPUP | WS_SYSMENU; + style &= ~(WS_CAPTION | WS_THICKFRAME); + + return style; +} + +static LONG fullscreen_exstyle(LONG exstyle) +{ + /* Filter out window decorations. */ + exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE); + + return exstyle; +} + +struct wined3d_window_state +{ + HWND window; + HWND window_pos_after; + LONG style, exstyle; + int x, y, width, height; + uint32_t flags; + bool set_style; + bool register_topmost_timer; + bool set_topmost_timer; +}; + +#define WINED3D_WINDOW_TOPMOST_TIMER_ID 0x4242 + +static void CALLBACK topmost_timer_proc(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) +{ + if (!(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + KillTimer(hwnd, WINED3D_WINDOW_TOPMOST_TIMER_ID); +} + +static DWORD WINAPI set_window_state_thread(void *ctx) +{ + struct wined3d_window_state *s = ctx; + bool filter; + + filter = wined3d_filter_messages(s->window, TRUE); + + if (s->set_style) + { + SetWindowLongW(s->window, GWL_STYLE, s->style); + SetWindowLongW(s->window, GWL_EXSTYLE, s->exstyle); + } + SetWindowPos(s->window, s->window_pos_after, s->x, s->y, s->width, s->height, s->flags); + + wined3d_filter_messages(s->window, filter); + + free(s); + + return 0; +} + +static void set_window_state(struct wined3d_window_state *s) +{ + static const UINT timeout = 1500; + DWORD window_tid = GetWindowThreadProcessId(s->window, NULL); + DWORD tid = GetCurrentThreadId(); + HANDLE thread; + + TRACE("Window %p belongs to thread %#lx.\n", s->window, window_tid); + /* If the window belongs to a different thread, modifying the style and/or + * position can potentially deadlock if that thread isn't processing + * messages. */ + if (window_tid == tid) + { + /* Deus Ex: Game of the Year Edition removes WS_EX_TOPMOST after changing resolutions in + * exclusive fullscreen mode. Tests show that WS_EX_TOPMOST will be restored when a ~1.5s + * timer times out */ + if (s->register_topmost_timer) + { + if (s->set_topmost_timer) + SetTimer(s->window, WINED3D_WINDOW_TOPMOST_TIMER_ID, timeout, topmost_timer_proc); + else + KillTimer(s->window, WINED3D_WINDOW_TOPMOST_TIMER_ID); + } + + set_window_state_thread(s); + } + else if ((thread = CreateThread(NULL, 0, set_window_state_thread, s, 0, NULL))) + { + SetThreadDescription(thread, L"wined3d_set_window_state"); + CloseHandle(thread); + } + else + { + ERR("Failed to create thread.\n"); + } +} + +HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state *state, + HWND window, int x, int y, int width, int height) +{ + struct wined3d_window_state *s; + + TRACE("Setting up window %p for fullscreen mode.\n", window); + + if (!IsWindow(window)) + { + WARN("%p is not a valid window.\n", window); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(s = malloc(sizeof(*s)))) + return E_OUTOFMEMORY; + s->window = window; + s->window_pos_after = HWND_TOPMOST; + s->x = x; + s->y = y; + s->width = width; + s->height = height; + + if (state->style || state->exstyle) + { + ERR("Changing the window style for window %p, but another style (%08lx, %08lx) is already stored.\n", + window, state->style, state->exstyle); + } + + s->flags = SWP_FRAMECHANGED | SWP_NOACTIVATE; + if (state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES) + s->flags |= SWP_NOZORDER; + else + s->flags |= SWP_SHOWWINDOW; + + state->style = GetWindowLongW(window, GWL_STYLE); + state->exstyle = GetWindowLongW(window, GWL_EXSTYLE); + + s->style = fullscreen_style(state->style); + s->exstyle = fullscreen_exstyle(state->exstyle); + s->set_style = true; + s->register_topmost_timer = !!(state->desc.flags & WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER); + s->set_topmost_timer = true; + + TRACE("Old style was %08lx, %08lx, setting to %08lx, %08lx.\n", + state->style, state->exstyle, s->style, s->exstyle); + + set_window_state(s); + return WINED3D_OK; +} + +void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_state *state, + HWND window, const RECT *window_rect) +{ + struct wined3d_window_state *s; + LONG style, exstyle; + + if (!state->style && !state->exstyle) + return; + + if (!(s = malloc(sizeof(*s)))) + return; + + s->window = window; + s->window_pos_after = NULL; + s->flags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE; + + if ((state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_STATE) + && !(state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES)) + { + s->window_pos_after = (state->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST; + s->flags |= (state->style & WS_VISIBLE) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + s->flags &= ~SWP_NOZORDER; + } + + style = GetWindowLongW(window, GWL_STYLE); + exstyle = GetWindowLongW(window, GWL_EXSTYLE); + + /* These flags are set by wined3d_device_setup_fullscreen_window, not the + * application, and we want to ignore them in the test below, since it's + * not the application's fault that they changed. Additionally, we want to + * preserve the current status of these flags (i.e. don't restore them) to + * more closely emulate the behavior of Direct3D, which leaves these flags + * alone when returning to windowed mode. */ + state->style ^= (state->style ^ style) & WS_VISIBLE; + state->exstyle ^= (state->exstyle ^ exstyle) & WS_EX_TOPMOST; + + TRACE("Restoring window style of window %p to %08lx, %08lx.\n", + window, state->style, state->exstyle); + + s->style = state->style; + s->exstyle = state->exstyle; + /* Only restore the style if the application didn't modify it during the + * fullscreen phase. Some applications change it before calling Reset() + * when switching between windowed and fullscreen modes (HL2), some + * depend on the original style (Eve Online). */ + s->set_style = style == fullscreen_style(state->style) && exstyle == fullscreen_exstyle(state->exstyle); + s->register_topmost_timer = !!(state->desc.flags & WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER); + s->set_topmost_timer = false; + + if (window_rect) + { + s->x = window_rect->left; + s->y = window_rect->top; + s->width = window_rect->right - window_rect->left; + s->height = window_rect->bottom - window_rect->top; + } + else + { + s->x = s->y = s->width = s->height = 0; + s->flags |= (SWP_NOMOVE | SWP_NOSIZE); + } + + set_window_state(s); + + /* Delete the old values. */ + state->style = 0; + state->exstyle = 0; +} + +HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_state *state, + const struct wined3d_swapchain_desc *swapchain_desc, + const struct wined3d_display_mode *mode) +{ + struct wined3d_display_mode actual_mode; + struct wined3d_output_desc output_desc; + BOOL windowed = state->desc.windowed; + HRESULT hr; + + TRACE("state %p, swapchain_desc %p, mode %p.\n", state, swapchain_desc, mode); + + if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) + { + if (mode) + { + actual_mode = *mode; + if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output, + &actual_mode))) + return hr; + } + else + { + if (!swapchain_desc->windowed) + { + actual_mode.width = swapchain_desc->backbuffer_width; + actual_mode.height = swapchain_desc->backbuffer_height; + actual_mode.refresh_rate = swapchain_desc->refresh_rate; + actual_mode.format_id = adapter_format_from_backbuffer_format(swapchain_desc->output->adapter, + swapchain_desc->backbuffer_format); + actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; + if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output, + &actual_mode))) + return hr; + } + else + { + if (FAILED(hr = wined3d_restore_display_modes(state->wined3d))) + { + WARN("Failed to restore display modes for all outputs, hr %#lx.\n", hr); + return hr; + } + } + } + } + else + { + if (mode) + WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n"); + + if (FAILED(hr = wined3d_output_get_display_mode(swapchain_desc->output, &actual_mode, + NULL))) + { + ERR("Failed to get display mode, hr %#lx.\n", hr); + return WINED3DERR_INVALIDCALL; + } + } + + if (!swapchain_desc->windowed) + { + unsigned int width = actual_mode.width; + unsigned int height = actual_mode.height; + + if (FAILED(hr = wined3d_output_get_desc(swapchain_desc->output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + return hr; + } + + if (state->desc.windowed) + { + /* Switch from windowed to fullscreen */ + if (FAILED(hr = wined3d_swapchain_state_setup_fullscreen(state, state->device_window, + output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, height))) + return hr; + } + else + { + HWND window = state->device_window; + BOOL filter; + + /* Fullscreen -> fullscreen mode change */ + filter = wined3d_filter_messages(window, TRUE); + MoveWindow(window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, + height, TRUE); + ShowWindow(window, SW_SHOW); + wined3d_filter_messages(window, filter); + } + state->d3d_mode = actual_mode; + } + else if (!state->desc.windowed) + { + /* Fullscreen -> windowed switch */ + RECT *window_rect = NULL; + if (state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) + window_rect = &state->original_window_rect; + wined3d_swapchain_state_restore_from_fullscreen(state, state->device_window, window_rect); + } + + state->desc.output = swapchain_desc->output; + state->desc.windowed = swapchain_desc->windowed; + + if (windowed != state->desc.windowed) + state->parent->ops->windowed_state_changed(state->parent, state->desc.windowed); + + return WINED3D_OK; +} + +BOOL CDECL wined3d_swapchain_state_is_windowed(const struct wined3d_swapchain_state *state) +{ + TRACE("state %p.\n", state); + + return state->desc.windowed; +} + +void CDECL wined3d_swapchain_state_get_size(const struct wined3d_swapchain_state *state, + unsigned int *width, unsigned int *height) +{ + TRACE("state %p.\n", state); + + *width = state->desc.backbuffer_width; + *height = state->desc.backbuffer_height; +} + +void CDECL wined3d_swapchain_state_destroy(struct wined3d_swapchain_state *state) +{ + wined3d_swapchain_state_cleanup(state); + free(state); +} + +HRESULT CDECL wined3d_swapchain_state_create(const struct wined3d_swapchain_desc *desc, + HWND window, struct wined3d *wined3d, struct wined3d_swapchain_state_parent *state_parent, + struct wined3d_swapchain_state **state) +{ + struct wined3d_swapchain_state *s; + HRESULT hr; + + TRACE("desc %p, window %p, wined3d %p, state %p.\n", desc, window, wined3d, state); + + if (!(s = calloc(1, sizeof(*s)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wined3d_swapchain_state_init(s, desc, window, wined3d, state_parent))) + { + free(s); + return hr; + } + + *state = s; + + return hr; +} diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c index deb4304c282..715e883020a 100644 --- a/dlls/wined3d/texture.c +++ b/dlls/wined3d/texture.c @@ -128,7 +128,7 @@ GLenum wined3d_texture_get_gl_buffer(const struct wined3d_texture *texture) return GL_FRONT; }
- if (texture == swapchain->back_buffers[0]) + if (texture == swapchain->back_buffers[0].texture) { TRACE("Returning GL_BACK.\n"); return GL_BACK; diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 7b2ef0d4b7f..7bcd37651de 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -4080,6 +4080,12 @@ struct wined3d_swapchain_ops void (*swapchain_frontbuffer_updated)(struct wined3d_swapchain *swapchain); };
+struct wined3d_backbuffer +{ + struct wined3d_texture *texture; + HRGN dirty_region; +}; + struct wined3d_swapchain { LONG ref; @@ -4088,7 +4094,7 @@ struct wined3d_swapchain const struct wined3d_swapchain_ops *swapchain_ops; struct wined3d_device *device;
- struct wined3d_texture **back_buffers; + struct wined3d_backbuffer *back_buffers; struct wined3d_texture *front_buffer; struct wined3d_gamma_ramp orig_gamma; bool reapply_mode;
From: Andrew Boyarshin andrew.boyarshin@gmail.com
--- dlls/d3d8/swapchain.c | 2 +- dlls/d3d9/device.c | 4 +- dlls/d3d9/swapchain.c | 2 +- dlls/ddraw/surface.c | 2 +- dlls/dxgi/swapchain.c | 15 +- dlls/dxgi/tests/dxgi.c | 210 ++++++++--------- dlls/wined3d/surface.c | 2 +- dlls/wined3d/swapchain.c | 476 +++++++++++++++++++++++++++++++++++++- dlls/wined3d/wined3d.spec | 3 +- include/wine/wined3d.h | 12 +- 10 files changed, 612 insertions(+), 116 deletions(-)
diff --git a/dlls/d3d8/swapchain.c b/dlls/d3d8/swapchain.c index 433da09d04b..8f34ea4ccdb 100644 --- a/dlls/d3d8/swapchain.c +++ b/dlls/d3d8/swapchain.c @@ -97,7 +97,7 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH d3d8_swapchain_Present(IDirect3DSwapChai if (dirty_region) FIXME("Ignoring dirty_region %p.\n", dirty_region);
- return wined3d_swapchain_present(swapchain->wined3d_swapchain, + return wined3d_swapchain_present_legacy(swapchain->wined3d_swapchain, src_rect, dst_rect, dst_window_override, swapchain->swap_interval, 0); }
diff --git a/dlls/d3d9/device.c b/dlls/d3d9/device.c index 5f59d10079d..5235a6c663e 100644 --- a/dlls/d3d9/device.c +++ b/dlls/d3d9/device.c @@ -1343,7 +1343,7 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH d3d9_device_Present(IDirect3DDevice9Ex * for (i = 0; i < device->implicit_swapchain_count; ++i) { swapchain = wined3d_swapchain_get_parent(device->implicit_swapchains[i]); - if (FAILED(hr = wined3d_swapchain_present(swapchain->wined3d_swapchain, + if (FAILED(hr = wined3d_swapchain_present_legacy(swapchain->wined3d_swapchain, src_rect, dst_rect, dst_window_override, swapchain->swap_interval, 0))) { wined3d_mutex_unlock(); @@ -4266,7 +4266,7 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH d3d9_device_PresentEx(IDirect3DDevice9Ex for (i = 0; i < device->implicit_swapchain_count; ++i) { swapchain = wined3d_swapchain_get_parent(device->implicit_swapchains[i]); - if (FAILED(hr = wined3d_swapchain_present(swapchain->wined3d_swapchain, + if (FAILED(hr = wined3d_swapchain_present_legacy(swapchain->wined3d_swapchain, src_rect, dst_rect, dst_window_override, swapchain->swap_interval, flags))) { wined3d_mutex_unlock(); diff --git a/dlls/d3d9/swapchain.c b/dlls/d3d9/swapchain.c index e87b8627ba3..97bd1c244ca 100644 --- a/dlls/d3d9/swapchain.c +++ b/dlls/d3d9/swapchain.c @@ -150,7 +150,7 @@ static HRESULT WINAPI DECLSPEC_HOTPATCH d3d9_swapchain_Present(IDirect3DSwapChai if (dirty_region) FIXME("Ignoring dirty_region %p.\n", dirty_region);
- return wined3d_swapchain_present(swapchain->wined3d_swapchain, + return wined3d_swapchain_present_legacy(swapchain->wined3d_swapchain, src_rect, dst_rect, dst_window_override, swapchain->swap_interval, flags); }
diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c index 545c76738cd..8d68c579e3a 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -139,7 +139,7 @@ HRESULT ddraw_surface_update_frontbuffer(struct ddraw_surface *surface, ddraw_surface_get_any_texture(surface, DDRAW_SURFACE_READ), surface->sub_resource_idx, rect, 0, NULL, WINED3D_TEXF_POINT)) && swap_interval) { - hr = wined3d_swapchain_present(ddraw->wined3d_swapchain, rect, rect, NULL, swap_interval, 0); + hr = wined3d_swapchain_present_legacy(ddraw->wined3d_swapchain, rect, rect, NULL, swap_interval, 0); ddraw->flags |= DDRAW_SWAPPED; } return hr; diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index fd790cc7fe2..347567d3268 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -317,6 +317,7 @@ static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, unsigned int sync_interval, unsigned int flags, const DXGI_PRESENT_PARAMETERS *dxgi_parameters) { HRESULT hr; + struct wined3d_present_parameters wined3d_parameters;
if (sync_interval > 4) { @@ -335,9 +336,19 @@ static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, return S_OK; }
+ wined3d_parameters.swapchain = swapchain->wined3d_swapchain; + wined3d_parameters.swap_interval = sync_interval; + wined3d_parameters.flags = 0; + 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])); + wined3d_parameters.dirty_rectangle_count = dxgi_parameters->DirtyRectsCount; + wined3d_parameters.dirty_rectangles = dxgi_parameters->pDirtyRects; + } + else + { + wined3d_parameters.dirty_rectangle_count = 0; + wined3d_parameters.dirty_rectangles = NULL; }
if (dxgi_parameters && dxgi_has_scroll_present_parameters(dxgi_parameters)) @@ -356,7 +367,7 @@ static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, 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))) + if (SUCCEEDED(hr = wined3d_swapchain_present(&wined3d_parameters))) InterlockedIncrement(&swapchain->present_count); return hr; } diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 812695c2f99..ed968aa46a6 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -8965,7 +8965,7 @@ static void test_partial_present_grid_impl(struct test_partial_present_grid_para 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); + check_texture(backbuffers[k], backbuffer_info[k]->background, buffer_count, backbuffer_info[k]->rectangles);
ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer_0_rtv, color_float);
@@ -8986,7 +8986,7 @@ static void test_partial_present_grid_impl(struct test_partial_present_grid_para 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); + check_texture(backbuffers[k], backbuffer_info[k]->background, buffer_count, backbuffer_info[k]->rectangles); } }
@@ -9105,60 +9105,60 @@ static void test_partial_present_scroll_impl(struct test_partial_present_common_ 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); + check_color(backbuffer_2, 5, 5, RED); + check_color(backbuffer_2, 20, 20, RED); + check_color(backbuffer_2, 49, 29, RED); + check_color(backbuffer_2, 49, 39, RED); + check_colors(backbuffer_2, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_2, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_2, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + check_color(backbuffer_2, 99, 29, RED); + check_color(backbuffer_2, 99, 39, RED); + check_colors(backbuffer_2, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_2, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_2, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_2, 199, 59, RED); + check_color(backbuffer_2, 399, 59, RED); + check_colors(backbuffer_2, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_2, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_2, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_2, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + check_colors(backbuffer_2, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_2, 9, 399, RED); + check_color(backbuffer_2, 9, 419, RED); + check_color(backbuffer_2, 599, 399, RED); + 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); + check_color(backbuffer_2, 5, 5, RED); + check_color(backbuffer_2, 20, 20, RED); + check_color(backbuffer_2, 49, 29, RED); + check_color(backbuffer_2, 49, 39, RED); + check_colors(backbuffer_2, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_2, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_2, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + check_color(backbuffer_2, 99, 29, RED); + check_color(backbuffer_2, 99, 39, RED); + check_colors(backbuffer_2, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_2, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_2, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_2, 199, 59, RED); + check_color(backbuffer_2, 399, 59, RED); + check_colors(backbuffer_2, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_2, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_2, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_2, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + check_colors(backbuffer_2, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_2, 9, 399, RED); + check_color(backbuffer_2, 9, 419, RED); + check_color(backbuffer_2, 599, 399, RED); + check_color(backbuffer_2, 599, 419, RED);
present_params.DirtyRectsCount = ARRAYSIZE(dirty_rects_blue); present_params.pDirtyRects = dirty_rects_blue; @@ -9173,51 +9173,51 @@ static void test_partial_present_scroll_impl(struct test_partial_present_common_
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); + check_color(backbuffer_1, 5, 5, RED); + check_color(backbuffer_1, 20, 20, RED); + check_color(backbuffer_1, 49, 29, RED); + check_color(backbuffer_1, 49, 39, RED); + check_colors(backbuffer_1, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_1, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_1, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + check_color(backbuffer_1, 99, 29, RED); + check_color(backbuffer_1, 99, 39, RED); + check_colors(backbuffer_1, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_1, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_1, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_1, 199, 59, RED); + check_color(backbuffer_1, 399, 59, RED); + check_colors(backbuffer_1, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_1, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_1, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_1, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_1, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + check_colors(backbuffer_1, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_1, 9, 399, RED); + check_color(backbuffer_1, 9, 419, RED); + check_color(backbuffer_1, 599, 399, RED); + check_color(backbuffer_1, 599, 419, RED); + + check_color(backbuffer_2, 5, 5, RED); + 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_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_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_color(backbuffer_2, 199, 299, RED); + flaky_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 */ @@ -9232,30 +9232,30 @@ static void test_partial_present_scroll_impl(struct test_partial_present_common_ 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); + check_color(backbuffer_2, 5, 5, RED); + check_color(backbuffer_2, 20, 20, RED); + check_color(backbuffer_2, 49, 29, RED); + check_color(backbuffer_2, 49, 39, RED); + check_colors(backbuffer_2, 49, 49, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_2, 49, 79, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_2, 49, 99, RED, GREEN, RED, RED); /* bottom-left corner */ + check_color(backbuffer_2, 99, 29, RED); + check_color(backbuffer_2, 99, 39, RED); + check_colors(backbuffer_2, 99, 49, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_2, 99, 79, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_2, 99, 99, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_2, 199, 59, RED); + check_color(backbuffer_2, 399, 59, RED); + check_colors(backbuffer_2, 199, 79, RED, RED, RED, GREEN); /* top-left corner */ + check_colors(backbuffer_2, 399, 79, RED, RED, GREEN, RED); /* top-right corner */ + check_colors(backbuffer_2, 199, 279, RED, GREEN, RED, GREEN); /* left side */ + check_colors(backbuffer_2, 399, 279, GREEN, RED, GREEN, RED); /* right side */ + check_colors(backbuffer_2, 199, 299, RED, GREEN, RED, RED); /* bottom-left corner */ + check_colors(backbuffer_2, 399, 299, GREEN, RED, RED, RED); /* bottom-right corner */ + check_color(backbuffer_2, 9, 399, RED); + check_color(backbuffer_2, 9, 419, RED); + check_color(backbuffer_2, 599, 399, RED); + check_color(backbuffer_2, 599, 419, RED); }
ID3D11RenderTargetView_Release(backbuffer_0_rtv); diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c index 3389f5fb866..a37377ba935 100644 --- a/dlls/wined3d/surface.c +++ b/dlls/wined3d/surface.c @@ -1601,7 +1601,7 @@ HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_ /* Set the swap effect to COPY, we don't want the backbuffer to become * undefined. */ dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY; - wined3d_swapchain_present(dst_swapchain, NULL, NULL, + wined3d_swapchain_present_legacy(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, dst_swapchain->swap_interval, 0); dst_swapchain->state.desc.swap_effect = swap_effect;
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index a59936fd468..874f002a87d 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -27,6 +27,205 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d); WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
+static const char *wine_dbgstr_rgn( HRGN hrgn ) +{ + DWORD i, size; + RGNDATA *data = NULL; + RECT *rect; + char buffer[200]; + int buffer_position = 0; + + if (!hrgn) return "(null)"; + + if (!(size = GetRegionData(hrgn, 0, NULL))) return "(ERROR)"; + if (!(data = malloc(size))) return "(NOMEM)"; + GetRegionData(hrgn, size, data); + + if (buffer_position < ARRAYSIZE(buffer)) + buffer_position += snprintf(buffer + buffer_position, ARRAYSIZE(buffer) - buffer_position, "{"); + + for (i = 0, rect = (RECT *) data->Buffer; i < data->rdh.nCount; ++i, ++rect) + { + if (i != 0 && buffer_position < ARRAYSIZE(buffer)) + buffer_position += snprintf(buffer + buffer_position, ARRAYSIZE(buffer) - buffer_position, ", "); + + if (buffer_position < ARRAYSIZE(buffer)) + buffer_position += snprintf(buffer + buffer_position, ARRAYSIZE(buffer) - buffer_position, "%s", wine_dbgstr_rect(rect)); + } + + free(data); + + if (buffer_position < ARRAYSIZE(buffer)) + buffer_position += snprintf(buffer + buffer_position, ARRAYSIZE(buffer) - buffer_position, "}"); + + buffer[ARRAYSIZE(buffer) - 1] = 0; + return __wine_dbg_strdup(buffer); +} + +static void region_set(HRGN *region, const RECT *rect) +{ + HRGN hrgn = *region; + + TRACE("region %p %s, rect %s.\n", region, wine_dbgstr_rgn(hrgn), wine_dbgstr_rect(rect)); + + if (hrgn) + { + if (!SetRectRgn(hrgn, rect->left, rect->top, rect->right, rect->bottom)) + ERR("Failed to update region %p with %s.\n", hrgn, wine_dbgstr_rect(rect)); + } + else + { + *region = hrgn = CreateRectRgnIndirect(rect); + if (!hrgn) + ERR("Failed to create region from %s.\n", wine_dbgstr_rect(rect)); + } +} + +static INT region_combine_region(HRGN *region, HRGN other, INT mode) +{ + HRGN hrgn = *region; + INT result; + + TRACE("region %p %p %s, other %p %s, mode %d.\n", + region, hrgn, wine_dbgstr_rgn(hrgn), other, wine_dbgstr_rgn(other), mode); + + if (hrgn && other) + { + /* CombineRgn only works well with non-NULL destination and sources */ + result = CombineRgn(hrgn, hrgn, other, mode); + } + else if (hrgn) + { + /* lhs != NULL, rhs == NULL */ + switch (mode) + { + case RGN_AND: + if (!SetRectRgn(hrgn, 0, 0, 0, 0)) + { + ERR("Failed to clear region %p.\n", hrgn); + return ERROR; + } + return NULLREGION; + case RGN_OR: + case RGN_XOR: + case RGN_DIFF: + case RGN_COPY: + result = CombineRgn(hrgn, hrgn, NULL, RGN_COPY); + break; + default: + ERR("Invalid CombineRgn mode %d.\n", mode); + return ERROR; + } + } + else if (other) + { + /* lhs == NULL, rhs != NULL */ + switch (mode) + { + case RGN_AND: + case RGN_DIFF: + case RGN_COPY: + return NULLREGION; + case RGN_OR: + case RGN_XOR: + *region = hrgn = CreateRectRgn(0, 0, 0, 0); + result = CombineRgn(hrgn, other, NULL, RGN_COPY); + break; + default: + ERR("Invalid CombineRgn mode %d.\n", mode); + return ERROR; + } + } + else + { + /* lhs == NULL, rhs == NULL */ + return NULLREGION; + } + if (result == ERROR) + { + ERR("CombineRgn(%p, %p, %d) failed.\n", *region, other, mode); + } + return result; +} + +static INT region_combine_rect(HRGN *region, const RECT *other, INT mode) +{ + HRGN hrgn = *region; + INT result; + + TRACE("region %p %p %s, other %s, mode %d.\n", + region, hrgn, wine_dbgstr_rgn(hrgn), wine_dbgstr_rect(other), mode); + + if (hrgn && !IsRectEmpty(other)) + { + /* lhs != NULL, rhs != NULL */ + HRGN other_region = CreateRectRgnIndirect(other); + if (!other_region) + { + ERR("Failed to create region from %s.\n", wine_dbgstr_rect(other)); + return ERROR; + } + + result = CombineRgn(hrgn, hrgn, other_region, mode); + } + else if (hrgn) + { + /* lhs != NULL, rhs == NULL */ + switch (mode) + { + case RGN_AND: + if (!SetRectRgn(hrgn, 0, 0, 0, 0)) + { + ERR("Failed to clear region %p.\n", hrgn); + return ERROR; + } + return NULLREGION; + case RGN_OR: + case RGN_XOR: + case RGN_DIFF: + case RGN_COPY: + result = CombineRgn(hrgn, hrgn, NULL, RGN_COPY); + break; + default: + ERR("Invalid CombineRgn mode %d.\n", mode); + return ERROR; + } + } + else if (!IsRectEmpty(other)) + { + /* lhs == NULL, rhs != NULL */ + switch (mode) + { + case RGN_AND: + case RGN_DIFF: + case RGN_COPY: + return NULLREGION; + case RGN_OR: + case RGN_XOR: + *region = hrgn = CreateRectRgnIndirect(other); + if (!hrgn) + { + ERR("Failed to create region from %s.\n", wine_dbgstr_rect(other)); + return ERROR; + } + return SIMPLEREGION; + default: + ERR("Invalid CombineRgn mode %d.\n", mode); + return ERROR; + } + } + else + { + /* lhs == NULL, rhs == NULL */ + return NULLREGION; + } + if (result == ERROR) + { + ERR("CombineRgn(%p, %p, %d) failed.\n", *region, other, mode); + } + return result; +} + void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) { HRESULT hr; @@ -191,7 +390,7 @@ void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWN swapchain->win_handle = window; }
-HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, +HRESULT CDECL wined3d_swapchain_present_legacy(struct wined3d_swapchain *swapchain, const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, unsigned int swap_interval, uint32_t flags) { @@ -226,6 +425,8 @@ HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, dst_rect = &d; }
+ region_set(&swapchain->back_buffers[0].dirty_region, src_rect); + wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect, dst_rect, dst_window_override, swap_interval, flags);
@@ -234,6 +435,279 @@ HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain, return WINED3D_OK; }
+static HRESULT present_region_blit(struct wined3d_swapchain *swapchain, + struct wined3d_texture *src_texture, struct wined3d_texture *dst_texture, HRGN region) +{ + DWORD region_data_size_expected, region_data_size_actual; + RGNDATA *region_data = NULL; + struct wined3d_device_context *context; + HRESULT hr; + RECT bounds; + + TRACE("swapchain %p, src_texture %p, dst_texture %p, region %p %s.\n", + swapchain, src_texture, dst_texture, region, wine_dbgstr_rgn(region)); + + region_data_size_expected = GetRegionData(region, 0, NULL); + region_data = malloc(region_data_size_expected); + if (!region_data) + { + ERR("Failed to allocate %lu bytes.\n", region_data_size_expected); + hr = E_OUTOFMEMORY; + goto state_cleanup; + } + + region_data_size_actual = GetRegionData(region, region_data_size_expected, region_data); + if (!region_data_size_actual) + { + DWORD error = GetLastError(); + ERR("Failed to acquire region data, error %#lx.\n", error); + hr = HRESULT_FROM_WIN32(error); + goto state_cleanup; + } + + hr = S_OK; + context = wined3d_device_get_immediate_context(swapchain->device); + SetRect(&bounds, 0, 0, dst_texture->resource.width, dst_texture->resource.height); + + for (DWORD i = 0; i < region_data->rdh.nCount; ++i) + { + RECT dst_rect = ((const RECT *) region_data->Buffer)[i]; + RECT clipped_dst_rect; + RECT src_rect; + + if (!IntersectRect(&clipped_dst_rect, &dst_rect, &bounds)) + { + TRACE("Destination rect %s is completely out of bounds after clipping to %s.\n", + wine_dbgstr_rect(&dst_rect), wine_dbgstr_rect(&bounds)); + continue; + } + + if (!EqualRect(&dst_rect, &clipped_dst_rect)) + { + TRACE("Destination rect %s clipped to %s is %s.\n", + wine_dbgstr_rect(&dst_rect), wine_dbgstr_rect(&bounds), wine_dbgstr_rect(&clipped_dst_rect)); + dst_rect = clipped_dst_rect; + } + + src_rect = dst_rect; + + hr = wined3d_device_context_blt(context, dst_texture, 0, &dst_rect, src_texture, 0, &src_rect, WINED3D_BLT_RAW, NULL, WINED3D_TEXF_POINT); + if (FAILED(hr)) + { + ERR("Failed to blit region #%lu: %s -> %s.\n", i, wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect)); + break; + } + } + +state_cleanup: + free(region_data); + return hr; +} + +HRESULT CDECL present_partial_compute_dirty(struct wined3d_present_parameters *parameters) +{ + struct wined3d_swapchain *swapchain = parameters->swapchain; + struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + UINT backbuffer_count = desc->backbuffer_count; + struct wined3d_backbuffer *backbuffer_0 = &swapchain->back_buffers[0]; + struct wined3d_backbuffer *backbuffer_prev = &swapchain->back_buffers[backbuffer_count - 1]; + unsigned int backbuffer_width = desc->backbuffer_width; + unsigned int backbuffer_height = desc->backbuffer_height; + HRGN blt_region = NULL; + RECT backbuffer_rect; + INT region_kind; + + SetRect(&backbuffer_rect, 0, 0, backbuffer_width, backbuffer_height); + + if (parameters->dirty_rectangle_count) + { + if (backbuffer_0->dirty_region) + { + SetRectRgn(backbuffer_0->dirty_region, 0, 0, 0, 0); + } + + for (UINT i = 0; i < parameters->dirty_rectangle_count; ++i) + { + RECT dirty_rect = parameters->dirty_rectangles[i]; + region_combine_rect(&backbuffer_0->dirty_region, &dirty_rect, RGN_OR); + } + } + else + { + region_set(&backbuffer_0->dirty_region, &backbuffer_rect); + } + + for (UINT i = 1; i < backbuffer_count; ++i) + { + struct wined3d_backbuffer *backbuffer = &swapchain->back_buffers[i]; + region_combine_region(&blt_region, backbuffer->dirty_region, RGN_OR); + } + + TRACE("dirty_other = %p %s\n", blt_region, wine_dbgstr_rgn(blt_region)); + TRACE("dirty_0 = %p %s\n", backbuffer_0->dirty_region, wine_dbgstr_rgn(backbuffer_0->dirty_region)); + + region_kind = region_combine_region(&blt_region, backbuffer_0->dirty_region, RGN_DIFF); + TRACE("blt_region = [%d] %p %s\n", region_kind, blt_region, wine_dbgstr_rgn(blt_region)); + + if (region_kind != NULLREGION) + present_region_blit(swapchain, backbuffer_prev->texture, backbuffer_0->texture, blt_region); + + if (blt_region) + DeleteObject(blt_region); + + return S_OK; +} + +void CDECL present_transform_dirty_rect(SIZE *swapchain, SIZE *client, RECT *src_rect, RECT *dst_rect) +{ + DOUBLE scale_x, scale_y; + if (!client->cx || !client->cy || !swapchain->cx || !swapchain->cy) + { + SetRectEmpty(dst_rect); + return; + } + + /* + * TODO: consider supporting scaling other that DXGI_SCALING_STRETCH: + * * DXGI_SCALING_NONE should copy src_rect to dst_rect + * * DXGI_SCALING_ASPECT_RATIO_STRETCH should apply letterboxing, a bit more complex than simple stretch + */ + scale_x = client->cx / swapchain->cx; + scale_y = client->cy / swapchain->cy; + dst_rect->left = floor(scale_x * src_rect->left); + dst_rect->top = floor(scale_y * src_rect->top); + dst_rect->right = ceil(scale_x * src_rect->right); + dst_rect->bottom = ceil(scale_y * src_rect->bottom); +} + +HRESULT CDECL present_partial_as_cs_op(struct wined3d_present_parameters *parameters, RECT *src_rect, RECT *dst_rect) +{ + struct wined3d_swapchain *swapchain = parameters->swapchain; + struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + HRGN dirty_region_0 = swapchain->back_buffers[0].dirty_region; + DWORD region_data_size_expected, region_data_size_actual; + SIZE swapchain_size, client_size; + DWORD final_dirty_rectangle_count; + RECT client_rect; + RGNDATA *region_data = NULL; + HRESULT hr; + + swapchain_size.cx = desc->backbuffer_width; + swapchain_size.cy = desc->backbuffer_height; + GetClientRect(parameters->swapchain->win_handle, &client_rect); + client_size.cx = client_rect.right - client_rect.left; + client_size.cy = client_rect.bottom - client_rect.top; + + region_data_size_expected = GetRegionData(dirty_region_0, 0, NULL); + region_data = malloc(region_data_size_expected); + if (!region_data) + { + ERR("Failed to allocate %lu bytes.\n", region_data_size_expected); + hr = E_OUTOFMEMORY; + goto state_cleanup; + } + + region_data_size_actual = GetRegionData(dirty_region_0, region_data_size_expected, region_data); + if (!region_data_size_actual) + { + DWORD error = GetLastError(); + ERR("Failed to acquire region data, error %#lx.\n", error); + hr = HRESULT_FROM_WIN32(error); + goto state_cleanup; + } + + final_dirty_rectangle_count = region_data->rdh.nCount; + if (final_dirty_rectangle_count == 1) + { + RECT dirty_rect = ((const RECT *) region_data->Buffer)[0]; + + *src_rect = dirty_rect; + present_transform_dirty_rect(&swapchain_size, &client_size, src_rect, dst_rect); + } + else if (final_dirty_rectangle_count > 1) + { + FIXME("Multiple (%lu) dirty rectangles are not properly supported.\n", final_dirty_rectangle_count); + + *src_rect = region_data->rdh.rcBound; + present_transform_dirty_rect(&swapchain_size, &client_size, src_rect, dst_rect); + } + else + { + SetRectEmpty(src_rect); + SetRectEmpty(dst_rect); + } + + hr = S_OK; + +state_cleanup: + free(region_data); + + return hr; +} + +HRESULT CDECL wined3d_swapchain_present(struct wined3d_present_parameters *parameters) +{ + struct wined3d_swapchain *swapchain = parameters->swapchain; + uint32_t flags = parameters->flags; + struct wined3d_backbuffer *back_buffers; + HRESULT hr; + RECT src_rect, dst_rect; + + TRACE("swapchain %p, swap_interval %u, flags %#x, dirty_rectangle_count %u.\n", + swapchain, parameters->swap_interval, flags, parameters->dirty_rectangle_count); + + if (flags) + FIXME("Ignoring flags %#x.\n", flags); + + wined3d_mutex_lock(); + + back_buffers = swapchain->back_buffers; + if (!back_buffers) + { + WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL.\n"); + hr = WINED3DERR_INVALIDCALL; + goto cleanup; + } + + if (parameters->dirty_rectangle_count) + { + /* Partial presentation */ + if (FAILED(hr = present_partial_compute_dirty(parameters))) + { + goto cleanup; + } + + if (FAILED(hr = present_partial_as_cs_op(parameters, &src_rect, &dst_rect))) + { + goto cleanup; + } + } + else + { + /* Full presentation */ + struct wined3d_swapchain_desc *desc = &swapchain->state.desc; + struct wined3d_backbuffer *backbuffer_0 = &back_buffers[0]; + RECT backbuffer_rect; + + SetRect(&backbuffer_rect, 0, 0, desc->backbuffer_width, desc->backbuffer_height); + + region_set(&backbuffer_0->dirty_region, &backbuffer_rect); + + src_rect = backbuffer_rect; + GetClientRect(parameters->swapchain->win_handle, &dst_rect); + } + + wined3d_cs_emit_present(swapchain->device->cs, swapchain, &src_rect, + &dst_rect, NULL, parameters->swap_interval, flags); + + hr = WINED3D_OK; + +cleanup: + wined3d_mutex_unlock(); + + return hr; +} + HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain, struct wined3d_texture *dst_texture, unsigned int sub_resource_idx) { diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec index 94e418ca885..ee2fda3fe8c 100644 --- a/dlls/wined3d/wined3d.spec +++ b/dlls/wined3d/wined3d.spec @@ -306,7 +306,8 @@ @ cdecl wined3d_swapchain_get_raster_status(ptr ptr) @ cdecl wined3d_swapchain_get_state(ptr) @ cdecl wined3d_swapchain_incref(ptr) -@ cdecl wined3d_swapchain_present(ptr ptr ptr ptr long long) +@ cdecl wined3d_swapchain_present_legacy(ptr ptr ptr ptr long long) +@ cdecl wined3d_swapchain_present(ptr) @ cdecl wined3d_swapchain_resize_buffers(ptr long long long long long long) @ cdecl wined3d_swapchain_set_gamma_ramp(ptr long ptr) @ cdecl wined3d_swapchain_set_palette(ptr ptr) diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index a3d449f14c6..be0de4c36b0 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -2318,6 +2318,15 @@ struct wined3d_streaming_buffer unsigned int bind_flags; };
+struct wined3d_present_parameters +{ + struct wined3d_swapchain *swapchain; + unsigned int swap_interval; + uint32_t flags; + uint32_t dirty_rectangle_count; + RECT *dirty_rectangles; +}; + void __stdcall wined3d_mutex_lock(void); void __stdcall wined3d_mutex_unlock(void);
@@ -2901,8 +2910,9 @@ HRESULT __cdecl wined3d_swapchain_get_raster_status(const struct wined3d_swapcha struct wined3d_raster_status *raster_status); struct wined3d_swapchain_state * __cdecl wined3d_swapchain_get_state(struct wined3d_swapchain *swapchain); ULONG __cdecl wined3d_swapchain_incref(struct wined3d_swapchain *swapchain); -HRESULT __cdecl wined3d_swapchain_present(struct wined3d_swapchain *swapchain, const RECT *src_rect, +HRESULT __cdecl wined3d_swapchain_present_legacy(struct wined3d_swapchain *swapchain, const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, unsigned int swap_interval, uint32_t flags); +HRESULT __cdecl wined3d_swapchain_present(struct wined3d_present_parameters *parameters); HRESULT __cdecl wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count, unsigned int width, unsigned int height, enum wined3d_format_id format_id, enum wined3d_multisample_type multisample_type, unsigned int multisample_quality);