[PATCH 0/4] MR9961: d2d1: Partially implement d2d_device_context_Flush().
React Native applications rely on d2d_device_context_Flush() returning S_OK. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9961
From: Zhiyi Zhang <zzhang@codeweavers.com> --- dlls/d2d1/tests/d2d1.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 6ae1f65a24b..86832aae5da 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -17793,6 +17793,37 @@ static void test_glyph_run_world_bounds(BOOL d3d11) release_test_context(&ctx); } +static void test_begin_end_draw(BOOL d3d11) +{ + struct d2d1_test_context ctx; + ID2D1DeviceContext *context; + HRESULT hr; + + if (!init_test_context(&ctx, d3d11)) + return; + + context = ctx.context; + + /* EndDraw() without BeginDraw() */ + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + todo_wine + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + + /* BeginDraw() twice */ + ID2D1DeviceContext_BeginDraw(context); + ID2D1DeviceContext_BeginDraw(context); + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + todo_wine + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + + /* Normal */ + ID2D1DeviceContext_BeginDraw(context); + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + release_test_context(&ctx); +} + START_TEST(d2d1) { HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll"); @@ -17916,6 +17947,7 @@ START_TEST(d2d1) queue_d3d10_test(test_path_geometry_stream); queue_d3d10_test(test_transformed_geometry); queue_d3d10_test(test_glyph_run_world_bounds); + queue_test(test_begin_end_draw); run_queued_tests(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9961
From: Zhiyi Zhang <zzhang@codeweavers.com> --- dlls/d2d1/d2d1_private.h | 1 + dlls/d2d1/device.c | 24 +++++++++++++++++++----- dlls/d2d1/tests/d2d1.c | 2 -- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 51bf830fc0a..674d32fd581 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -211,6 +211,7 @@ struct d2d_device_context [D2D_SAMPLER_EXTEND_MODE_COUNT] [D2D_SAMPLER_EXTEND_MODE_COUNT]; + BOOL is_drawing; struct d2d_error_state error; D2D1_DRAWING_STATE_DESCRIPTION1 drawing_state; IDWriteRenderingParams *text_rendering_params; diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index ac1220bc9b3..3a23833df9a 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -2055,6 +2055,13 @@ static void STDMETHODCALLTYPE d2d_device_context_BeginDraw(ID2D1DeviceContext6 * TRACE("iface %p.\n", iface); + if (context->is_drawing) + { + d2d_device_context_set_error(context, D2DERR_WRONG_STATE); + return; + } + context->is_drawing = TRUE; + if (context->target.type == D2D_TARGET_COMMAND_LIST) d2d_command_list_begin_draw(context->target.command_list, context); @@ -2069,17 +2076,24 @@ static HRESULT STDMETHODCALLTYPE d2d_device_context_EndDraw(ID2D1DeviceContext6 TRACE("iface %p, tag1 %p, tag2 %p.\n", iface, tag1, tag2); + if (tag1) + *tag1 = context->error.tag1; + if (tag2) + *tag2 = context->error.tag2; + + if (!context->is_drawing) + { + d2d_device_context_set_error(context, D2DERR_WRONG_STATE); + return context->error.code; + } + context->is_drawing = FALSE; + if (context->target.type == D2D_TARGET_COMMAND_LIST) { FIXME("Unimplemented for command list target.\n"); return E_NOTIMPL; } - if (tag1) - *tag1 = context->error.tag1; - if (tag2) - *tag2 = context->error.tag2; - if (context->ops && context->ops->device_context_present) { if (FAILED(hr = context->ops->device_context_present(context->outer_unknown))) diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 86832aae5da..644e291bb03 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -17806,14 +17806,12 @@ static void test_begin_end_draw(BOOL d3d11) /* EndDraw() without BeginDraw() */ hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); - todo_wine ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); /* BeginDraw() twice */ ID2D1DeviceContext_BeginDraw(context); ID2D1DeviceContext_BeginDraw(context); hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); - todo_wine ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); /* Normal */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9961
From: Zhiyi Zhang <zzhang@codeweavers.com> --- dlls/d2d1/tests/d2d1.c | 126 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 644e291bb03..6225a3a6ebf 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -17822,6 +17822,131 @@ static void test_begin_end_draw(BOOL d3d11) release_test_context(&ctx); } +static void test_device_context_flush(BOOL d3d11) +{ + static const D2D1_COLOR_F white = {1.0f, 1.0f, 1.0f, 1.0f}; + D2D1_BITMAP_PROPERTIES1 bitmap_desc1; + ID2D1CommandList *command_list; + struct d2d1_test_context ctx; + ID2D1DeviceContext *context; + ID2D1Bitmap1 *bitmap1; + D2D1_TAG tag1, tag2; + D2D1_SIZE_U size; + HRESULT hr; + + if (!init_test_context(&ctx, d3d11)) + return; + + if (!ctx.factory1) + { + win_skip("Command lists are not supported.\n"); + release_test_context(&ctx); + return; + } + + context = ctx.context; + + /* Not inside BeginDraw()/EndDraw() */ + tag1 = 0xdeadbeef; + tag2 = 0xdeadbeef; + hr = ID2D1DeviceContext_Flush(context, &tag1, &tag2); + todo_wine + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + todo_wine + ok(tag1 == 0, "Got unexpected tag1 %#I64x.\n", tag1); + todo_wine + ok(tag2 == 0, "Got unexpected tag2 %#I64x.\n", tag2); + + /* Flush() doesn't clear the error code when not inside BeginDraw()/EndDraw() */ + tag1 = 0xdeadbeef; + tag2 = 0xdeadbeef; + hr = ID2D1DeviceContext_Flush(context, &tag1, &tag2); + todo_wine + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + todo_wine + ok(tag1 == 0, "Got unexpected tag1 %#I64x.\n", tag1); + todo_wine + ok(tag2 == 0, "Got unexpected tag2 %#I64x.\n", tag2); + + /* Inside BeginDraw()/EndDraw() */ + ID2D1DeviceContext_BeginDraw(context); + + tag1 = 0xdeadbeef; + tag2 = 0xdeadbeef; + hr = ID2D1DeviceContext_Flush(context, &tag1, &tag2); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + todo_wine + ok(tag1 == 0, "Got unexpected tag1 %#I64x.\n", tag1); + todo_wine + ok(tag2 == 0, "Got unexpected tag2 %#I64x.\n", tag2); + + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + /* Command list target */ + ID2D1DeviceContext_BeginDraw(context); + hr = ID2D1DeviceContext_CreateCommandList(context, &command_list); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1DeviceContext_SetTarget(context, (ID2D1Image *)command_list); + ID2D1DeviceContext_Clear(context, &white); + + hr = ID2D1DeviceContext_Flush(context, NULL, NULL); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1CommandList_Close(command_list); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1CommandList_Release(command_list); + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + /* Bitmap target */ + ID2D1DeviceContext_BeginDraw(context); + set_size_u(&size, 4, 4); + memset(&bitmap_desc1, 0, sizeof(bitmap_desc1)); + bitmap_desc1.dpiX = 96.0f; + bitmap_desc1.dpiY = 96.0f; + bitmap_desc1.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; + bitmap_desc1.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE; + bitmap_desc1.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; + hr = ID2D1DeviceContext_CreateBitmap(context, size, NULL, 0, &bitmap_desc1, &bitmap1); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ID2D1DeviceContext_SetTarget(context, (ID2D1Image *)bitmap1); + ID2D1DeviceContext_Clear(context, &white); + + hr = ID2D1DeviceContext_Flush(context, NULL, NULL); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1Bitmap1_Release(bitmap1); + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + /* No target */ + ID2D1DeviceContext_BeginDraw(context); + ID2D1DeviceContext_SetTarget(context, NULL); + ID2D1DeviceContext_Clear(context, &white); + + hr = ID2D1DeviceContext_Flush(context, NULL, NULL); + todo_wine + ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); + + /* The last Flush() call clears the error code */ + hr = ID2D1DeviceContext_Flush(context, NULL, NULL); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); + todo_wine + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + release_test_context(&ctx); +} + START_TEST(d2d1) { HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll"); @@ -17946,6 +18071,7 @@ START_TEST(d2d1) queue_d3d10_test(test_transformed_geometry); queue_d3d10_test(test_glyph_run_world_bounds); queue_test(test_begin_end_draw); + queue_test(test_device_context_flush); run_queued_tests(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9961
From: Zhiyi Zhang <zzhang@codeweavers.com> React Native applications rely on d2d_device_context_Flush() returning S_OK. --- dlls/d2d1/device.c | 18 ++++++++++++++++-- dlls/d2d1/tests/d2d1.c | 15 --------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index 3a23833df9a..84d2c991eda 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -1859,13 +1859,27 @@ static void STDMETHODCALLTYPE d2d_device_context_PopLayer(ID2D1DeviceContext6 *i static HRESULT STDMETHODCALLTYPE d2d_device_context_Flush(ID2D1DeviceContext6 *iface, D2D1_TAG *tag1, D2D1_TAG *tag2) { struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + HRESULT hr; FIXME("iface %p, tag1 %p, tag2 %p stub!\n", iface, tag1, tag2); + if (tag1) + *tag1 = context->error.tag1; + if (tag2) + *tag2 = context->error.tag2; + + if (!context->is_drawing) + return D2DERR_WRONG_STATE; + if (context->ops && context->ops->device_context_present) - context->ops->device_context_present(context->outer_unknown); + { + if (FAILED(hr = context->ops->device_context_present(context->outer_unknown))) + context->error.code = hr; + } - return E_NOTIMPL; + hr = context->error.code; + d2d_device_context_set_error(context, S_OK); + return hr; } static void STDMETHODCALLTYPE d2d_device_context_SaveDrawingState(ID2D1DeviceContext6 *iface, diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 6225a3a6ebf..217775641ea 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -17850,22 +17850,16 @@ static void test_device_context_flush(BOOL d3d11) tag1 = 0xdeadbeef; tag2 = 0xdeadbeef; hr = ID2D1DeviceContext_Flush(context, &tag1, &tag2); - todo_wine ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(tag1 == 0, "Got unexpected tag1 %#I64x.\n", tag1); - todo_wine ok(tag2 == 0, "Got unexpected tag2 %#I64x.\n", tag2); /* Flush() doesn't clear the error code when not inside BeginDraw()/EndDraw() */ tag1 = 0xdeadbeef; tag2 = 0xdeadbeef; hr = ID2D1DeviceContext_Flush(context, &tag1, &tag2); - todo_wine ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(tag1 == 0, "Got unexpected tag1 %#I64x.\n", tag1); - todo_wine ok(tag2 == 0, "Got unexpected tag2 %#I64x.\n", tag2); /* Inside BeginDraw()/EndDraw() */ @@ -17874,11 +17868,8 @@ static void test_device_context_flush(BOOL d3d11) tag1 = 0xdeadbeef; tag2 = 0xdeadbeef; hr = ID2D1DeviceContext_Flush(context, &tag1, &tag2); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(tag1 == 0, "Got unexpected tag1 %#I64x.\n", tag1); - todo_wine ok(tag2 == 0, "Got unexpected tag2 %#I64x.\n", tag2); hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); @@ -17892,7 +17883,6 @@ static void test_device_context_flush(BOOL d3d11) ID2D1DeviceContext_Clear(context, &white); hr = ID2D1DeviceContext_Flush(context, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = ID2D1CommandList_Close(command_list); @@ -17918,12 +17908,10 @@ static void test_device_context_flush(BOOL d3d11) ID2D1DeviceContext_Clear(context, &white); hr = ID2D1DeviceContext_Flush(context, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); ID2D1Bitmap1_Release(bitmap1); hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); /* No target */ @@ -17932,16 +17920,13 @@ static void test_device_context_flush(BOOL d3d11) ID2D1DeviceContext_Clear(context, &white); hr = ID2D1DeviceContext_Flush(context, NULL, NULL); - todo_wine ok(hr == D2DERR_WRONG_STATE, "Got unexpected hr %#lx.\n", hr); /* The last Flush() call clears the error code */ hr = ID2D1DeviceContext_Flush(context, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = ID2D1DeviceContext_EndDraw(context, NULL, NULL); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); release_test_context(&ctx); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9961
Could we simply return S_OK? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9961#note_128332
On Thu Jan 29 02:06:29 2026 +0000, Nikolay Sivov wrote:
Could we simply return S_OK? We could. This is a bit more complete than simply returning S_OK, no? I was trying to mimic the behavior shown by the tests.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9961#note_128341
QwertyChouskie (@QwertyChouskie) commented about dlls/d2d1/device.c:
static HRESULT STDMETHODCALLTYPE d2d_device_context_Flush(ID2D1DeviceContext6 *iface, D2D1_TAG *tag1, D2D1_TAG *tag2) { struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + HRESULT hr;
FIXME("iface %p, tag1 %p, tag2 %p stub!\n", iface, tag1, tag2); Should this be changed to `semi-stub`?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9961#note_128470
participants (4)
-
Nikolay Sivov (@nsivov) -
QwertyChouskie (@QwertyChouskie) -
Zhiyi Zhang -
Zhiyi Zhang (@zhiyi)