Fixes periodic crashes in PowerPoint 365
-- v10: d2d1: Acquire lock before attempt to draw to device context
From: Brendan McGrath bmcgrath@codeweavers.com
Tests that Direct2D can not access D2D exclusive resources whilst the Direct 2D lock is held --- dlls/d2d1/tests/d2d1.c | 50 +++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-)
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 58f905ca1dc..1e4d77e1c84 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -1163,13 +1163,13 @@ static IDXGISwapChain *create_d3d10_swapchain(ID3D10Device1 *device, HWND window }
static ID2D1RenderTarget *create_render_target_desc(IDXGISurface *surface, - const D2D1_RENDER_TARGET_PROPERTIES *desc, BOOL d3d11) + const D2D1_RENDER_TARGET_PROPERTIES *desc, BOOL d3d11, D2D1_FACTORY_TYPE factory_type) { ID2D1RenderTarget *render_target; ID2D1Factory *factory; HRESULT hr;
- hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, NULL, (void **)&factory); + hr = D2D1CreateFactory(factory_type, &IID_ID2D1Factory, NULL, (void **)&factory); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = ID2D1Factory_CreateDxgiSurfaceRenderTarget(factory, surface, desc, &render_target); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); @@ -1178,7 +1178,7 @@ static ID2D1RenderTarget *create_render_target_desc(IDXGISurface *surface, return render_target; }
-static ID2D1RenderTarget *create_render_target(IDXGISurface *surface, BOOL d3d11) +static ID2D1RenderTarget *create_render_target(IDXGISurface *surface, BOOL d3d11, D2D1_FACTORY_TYPE factory_type) { D2D1_RENDER_TARGET_PROPERTIES desc;
@@ -1190,7 +1190,7 @@ static ID2D1RenderTarget *create_render_target(IDXGISurface *surface, BOOL d3d11 desc.usage = D2D1_RENDER_TARGET_USAGE_NONE; desc.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
- return create_render_target_desc(surface, &desc, d3d11); + return create_render_target_desc(surface, &desc, d3d11, factory_type); }
#define release_test_context(ctx) release_test_context_(__LINE__, ctx) @@ -1216,8 +1216,9 @@ static void release_test_context_(unsigned int line, struct d2d1_test_context *c ok_(__FILE__, line)(!ref, "Device has %lu references left.\n", ref); }
-#define init_test_context(ctx, d3d11) init_test_context_(__LINE__, ctx, d3d11) -static BOOL init_test_context_(unsigned int line, struct d2d1_test_context *ctx, BOOL d3d11) +#define init_test_context(ctx, d3d11) init_test_context_(__LINE__, ctx, d3d11, D2D1_FACTORY_TYPE_SINGLE_THREADED) +#define init_test_multithreaded_context(ctx, d3d11) init_test_context_(__LINE__, ctx, d3d11, D2D1_FACTORY_TYPE_MULTI_THREADED) +static BOOL init_test_context_(unsigned int line, struct d2d1_test_context *ctx, BOOL d3d11, D2D1_FACTORY_TYPE factory_type) { HRESULT hr;
@@ -1237,7 +1238,7 @@ static BOOL init_test_context_(unsigned int line, struct d2d1_test_context *ctx, hr = IDXGISwapChain_GetBuffer(ctx->swapchain, 0, &IID_IDXGISurface, (void **)&ctx->surface); ok_(__FILE__, line)(hr == S_OK, "Failed to get buffer, hr %#lx.\n", hr);
- ctx->rt = create_render_target(ctx->surface, d3d11); + ctx->rt = create_render_target(ctx->surface, d3d11, factory_type); if (!ctx->rt && d3d11) { todo_wine win_skip_(__FILE__, line)("Skipping d3d11 tests.\n"); @@ -4887,7 +4888,7 @@ static void test_alpha_mode(BOOL d3d11) rt_desc.dpiY = 0.0f; rt_desc.usage = D2D1_RENDER_TARGET_USAGE_NONE; rt_desc.minLevel = D2D1_FEATURE_LEVEL_DEFAULT; - rt = create_render_target_desc(ctx.surface, &rt_desc, d3d11); + rt = create_render_target_desc(ctx.surface, &rt_desc, d3d11, D2D1_FACTORY_TYPE_SINGLE_THREADED); ok(!!rt, "Failed to create render target.\n");
ID2D1RenderTarget_SetAntialiasMode(rt, D2D1_ANTIALIAS_MODE_ALIASED); @@ -10694,8 +10695,23 @@ static DWORD WINAPI mt_factory_test_thread_func(void *param) return 0; }
+static DWORD WINAPI mt_factory_test_thread_draw_func(void *param) { + ID2D1RenderTarget *rt = param; + D2D1_COLOR_F color; + HRESULT hr; + + ID2D1RenderTarget_BeginDraw(rt); + set_color(&color, 1.0f, 1.0f, 0.0f, 1.0f); + ID2D1RenderTarget_Clear(rt, &color); + hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + return 0; +} + static void test_mt_factory(BOOL d3d11) { + struct d2d1_test_context ctx; ID2D1Multithread *multithread; ID2D1Factory *factory; HANDLE thread; @@ -10750,6 +10766,24 @@ static void test_mt_factory(BOOL d3d11) ID2D1Multithread_Release(multithread);
ID2D1Factory_Release(factory); + + if (!init_test_multithreaded_context(&ctx, d3d11)) + return; + + hr = ID2D1Factory_QueryInterface(ctx.factory, &IID_ID2D1Multithread, (void **)&multithread); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1Multithread_Enter(multithread); + thread = CreateThread(NULL, 0, mt_factory_test_thread_draw_func, ctx.rt, 0, NULL); + ok(!!thread, "Failed to create a thread.\n"); + ret = WaitForSingleObject(thread, 1000); + todo_wine ok(ret == WAIT_TIMEOUT, "Expected timeout.\n"); + ID2D1Multithread_Leave(multithread); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + ID2D1Multithread_Release(multithread); + release_test_context(&ctx); }
#define check_system_properties(effect, is_builtin) check_system_properties_(__LINE__, effect, is_builtin)
From: Brendan McGrath bmcgrath@codeweavers.com
Tests that Direct2D can access D2D exclusive resources whilst the Direct 2D lock is held if the factory type is single threaded --- dlls/d2d1/tests/d2d1.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 1e4d77e1c84..3a8d97162fa 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -10784,6 +10784,24 @@ static void test_mt_factory(BOOL d3d11)
ID2D1Multithread_Release(multithread); release_test_context(&ctx); + + if (!init_test_context(&ctx, d3d11)) + return; + + hr = ID2D1Factory_QueryInterface(ctx.factory, &IID_ID2D1Multithread, (void **)&multithread); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ID2D1Multithread_Enter(multithread); + thread = CreateThread(NULL, 0, mt_factory_test_thread_draw_func, ctx.rt, 0, NULL); + ok(!!thread, "Failed to create a thread.\n"); + ret = WaitForSingleObject(thread, 1000); + ok(ret == WAIT_OBJECT_0, "Didn't expect timeout.\n"); + ID2D1Multithread_Leave(multithread); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + ID2D1Multithread_Release(multithread); + release_test_context(&ctx); }
#define check_system_properties(effect, is_builtin) check_system_properties_(__LINE__, effect, is_builtin)
From: Brendan McGrath bmcgrath@codeweavers.com
Ensures the Direct 2D lock is held before attempting to access Direct 2D exclusive resources.
This fixes periodic crashes in PowerPoint 365 --- dlls/d2d1/d2d1_private.h | 25 ++++++++++++++++++++++++- dlls/d2d1/device.c | 12 ++++++++++++ dlls/d2d1/factory.c | 23 +---------------------- dlls/d2d1/tests/d2d1.c | 2 +- 4 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 1dd5cdb0a39..36bedf8331c 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -171,6 +171,7 @@ struct d2d_device_context const struct d2d_device_context_ops *ops;
ID2D1Factory *factory; + CRITICAL_SECTION *cs; struct d2d_device *device; ID3D11Device1 *d3d_device; ID3DDeviceContextState *d3d_state; @@ -678,7 +679,29 @@ struct d2d_effect_registration struct d2d_effect_properties properties; };
-struct d2d_factory; +struct d2d_factory +{ + ID2D1Factory3 ID2D1Factory3_iface; + ID2D1Multithread ID2D1Multithread_iface; + LONG refcount; + + ID3D10Device1 *device; + + float dpi_x; + float dpi_y; + + struct list effects; + INIT_ONCE init_builtins; + + CRITICAL_SECTION cs; + D2D1_FACTORY_TYPE factory_type; +}; + +static inline struct d2d_factory *unsafe_impl_from_ID2D1Factory(ID2D1Factory *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_factory, ID2D1Factory3_iface); +} + void d2d_effects_init_builtins(struct d2d_factory *factory); struct d2d_effect_registration * d2d_factory_get_registered_effect(ID2D1Factory *factory, const GUID *effect_id); diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index e5548878ee9..153cb2880d8 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -135,6 +135,9 @@ static void d2d_device_context_draw(struct d2d_device_context *render_target, en vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f;
+ if (render_target->cs) + EnterCriticalSection(render_target->cs); + ID3D11Device1_GetImmediateContext1(device, &context); ID3D11DeviceContext1_SwapDeviceContextState(context, render_target->d3d_state, &prev_state);
@@ -188,6 +191,9 @@ static void d2d_device_context_draw(struct d2d_device_context *render_target, en ID3D11DeviceContext1_SwapDeviceContextState(context, prev_state, NULL); ID3D11DeviceContext1_Release(context); ID3DDeviceContextState_Release(prev_state); + + if (render_target->cs) + LeaveCriticalSection(render_target->cs); }
static void d2d_device_context_set_error(struct d2d_device_context *context, HRESULT code) @@ -3196,6 +3202,7 @@ static HRESULT d2d_device_context_init(struct d2d_device_context *render_target, IDWriteFactory *dwrite_factory; D3D11_RASTERIZER_DESC rs_desc; D3D11_BUFFER_DESC buffer_desc; + struct d2d_factory *factory; unsigned int i; HRESULT hr;
@@ -4256,6 +4263,11 @@ static HRESULT d2d_device_context_init(struct d2d_device_context *render_target, render_target->device = device; ID2D1Device1_AddRef(&render_target->device->ID2D1Device1_iface);
+ factory = unsafe_impl_from_ID2D1Factory(render_target->factory); + if (factory->factory_type == D2D1_FACTORY_TYPE_MULTI_THREADED) { + render_target->cs = &factory->cs; + } + render_target->outer_unknown = outer_unknown ? outer_unknown : &render_target->IUnknown_iface; render_target->ops = ops;
diff --git a/dlls/d2d1/factory.c b/dlls/d2d1/factory.c index 3b2b1f5e4ec..d349da376fc 100644 --- a/dlls/d2d1/factory.c +++ b/dlls/d2d1/factory.c @@ -36,33 +36,11 @@ static void d2d_effect_registration_cleanup(struct d2d_effect_registration *reg) free(reg); }
-struct d2d_factory -{ - ID2D1Factory3 ID2D1Factory3_iface; - ID2D1Multithread ID2D1Multithread_iface; - LONG refcount; - - ID3D10Device1 *device; - - float dpi_x; - float dpi_y; - - struct list effects; - INIT_ONCE init_builtins; - - CRITICAL_SECTION cs; -}; - static inline struct d2d_factory *impl_from_ID2D1Factory3(ID2D1Factory3 *iface) { return CONTAINING_RECORD(iface, struct d2d_factory, ID2D1Factory3_iface); }
-static inline struct d2d_factory *unsafe_impl_from_ID2D1Factory(ID2D1Factory *iface) -{ - return CONTAINING_RECORD(iface, struct d2d_factory, ID2D1Factory3_iface); -} - static inline struct d2d_factory *impl_from_ID2D1Multithread(ID2D1Multithread *iface) { return CONTAINING_RECORD(iface, struct d2d_factory, ID2D1Multithread_iface); @@ -1215,6 +1193,7 @@ static void d2d_factory_init(struct d2d_factory *factory, D2D1_FACTORY_TYPE fact factory->ID2D1Factory3_iface.lpVtbl = &d2d_factory_vtbl; factory->ID2D1Multithread_iface.lpVtbl = factory_type == D2D1_FACTORY_TYPE_SINGLE_THREADED ? &d2d_factory_multithread_noop_vtbl : &d2d_factory_multithread_vtbl; + factory->factory_type = factory_type; factory->refcount = 1; d2d_factory_reload_sysmetrics(factory); list_init(&factory->effects); diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 3a8d97162fa..0a4d81fe857 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -10777,7 +10777,7 @@ static void test_mt_factory(BOOL d3d11) thread = CreateThread(NULL, 0, mt_factory_test_thread_draw_func, ctx.rt, 0, NULL); ok(!!thread, "Failed to create a thread.\n"); ret = WaitForSingleObject(thread, 1000); - todo_wine ok(ret == WAIT_TIMEOUT, "Expected timeout.\n"); + ok(ret == WAIT_TIMEOUT, "Expected timeout.\n"); ID2D1Multithread_Leave(multithread); WaitForSingleObject(thread, INFINITE); CloseHandle(thread);
On Mon Nov 27 18:43:00 2023 +0000, Nikolay Sivov wrote:
Let's just inline them here. enter_cs/leave_cs won't be used anywhere else, right?
I was thinking we might find somewhere else, but we can always add them when/if we need them. For now, I've removed them.
On Mon Nov 27 21:09:40 2023 +0000, Brendan McGrath wrote:
changed this line in [version 10 of the diff](/wine/wine/-/merge_requests/4452/diffs?diff_id=86186&start_sha=68f2964ae4070f831c9b63d46076fb009a8bc342#7bd8161ba70cc38a0182d56400f361d22514b82f_4275_4267)
OK, I removed the function
On Mon Nov 27 18:43:02 2023 +0000, Nikolay Sivov wrote:
The 'else' part is unnecessary.
You're right - good catch
On Mon Nov 27 21:09:43 2023 +0000, Brendan McGrath wrote:
changed this line in [version 10 of the diff](/wine/wine/-/merge_requests/4452/diffs?diff_id=86186&start_sha=68f2964ae4070f831c9b63d46076fb009a8bc342#78507d505947ac14f51de00766df6ad4893c1c07_700_700)
Oops - fixed. Thanks.
On Mon Nov 27 21:09:45 2023 +0000, Brendan McGrath wrote:
changed this line in [version 10 of the diff](/wine/wine/-/merge_requests/4452/diffs?diff_id=86186&start_sha=68f2964ae4070f831c9b63d46076fb009a8bc342#78507d505947ac14f51de00766df6ad4893c1c07_174_174)
Again oops. Thanks.
Nikolay Sivov (@nsivov) commented about dlls/d2d1/device.c:
render_target->device = device; ID2D1Device1_AddRef(&render_target->device->ID2D1Device1_iface);
- factory = unsafe_impl_from_ID2D1Factory(render_target->factory);
- if (factory->factory_type == D2D1_FACTORY_TYPE_MULTI_THREADED) {
render_target->cs = &factory->cs;
- }
One last formatting change please - existing code uses opening braces on new lines. There is one of those in tests, but here you can simply remove braces.
Otherwise looks good.