This avoids opening multiple Vulkan instances when multiple D3D kernel mode adapters are open. Some tests that leak reference to D3D adapters crash because of out of memory errors before this patch.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53201 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53231 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53235 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com
From: Zhiyi Zhang zzhang@codeweavers.com
This avoids opening multiple Vulkan instances when multiple D3D kernel mode adapters are open. Some tests that leak reference to D3D adapters crash because of out of memory errors before this patch.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53201 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53231 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53235 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/winex11.drv/x11drv_main.c | 62 ++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 26 deletions(-)
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index dc987ae70a2..4e908f2918b 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -107,7 +107,6 @@ static pthread_mutex_t error_mutex = PTHREAD_MUTEX_INITIALIZER; struct x11_d3dkmt_adapter { D3DKMT_HANDLE handle; /* Kernel mode graphics adapter handle */ - VkInstance vk_instance; /* Vulkan instance */ VkPhysicalDevice vk_device; /* Vulkan physical device */ struct list entry; /* List entry */ }; @@ -120,6 +119,7 @@ struct d3dkmt_vidpn_source struct list entry; /* List entry */ };
+static VkInstance d3dkmt_vk_instance; /* Vulkan instance for D3DKMT functions */ static struct list x11_d3dkmt_adapters = LIST_INIT( x11_d3dkmt_adapters ); static struct list d3dkmt_vidpn_sources = LIST_INIT( d3dkmt_vidpn_sources ); /* VidPN source information list */
@@ -862,12 +862,17 @@ NTSTATUS CDECL X11DRV_D3DKMTCloseAdapter( const D3DKMT_CLOSEADAPTER *desc ) { if (adapter->handle == desc->hAdapter) { - vulkan_funcs->p_vkDestroyInstance(adapter->vk_instance, NULL); list_remove(&adapter->entry); free(adapter); break; } } + + if (list_empty(&x11_d3dkmt_adapters)) + { + vulkan_funcs->p_vkDestroyInstance(d3dkmt_vk_instance, NULL); + d3dkmt_vk_instance = NULL; + } pthread_mutex_unlock(&d3dkmt_mutex); return STATUS_SUCCESS; } @@ -1134,7 +1139,6 @@ NTSTATUS CDECL X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *des struct x11_d3dkmt_adapter *adapter; VkInstanceCreateInfo create_info; VkPhysicalDeviceIDProperties id; - VkInstance vk_instance = NULL; VkResult vr; GUID uuid;
@@ -1152,30 +1156,35 @@ NTSTATUS CDECL X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *des return STATUS_UNSUCCESSFUL; }
- memset(&create_info, 0, sizeof(create_info)); - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.enabledExtensionCount = ARRAY_SIZE(extensions); - create_info.ppEnabledExtensionNames = extensions; + pthread_mutex_lock(&d3dkmt_mutex);
- vr = vulkan_funcs->p_vkCreateInstance(&create_info, NULL, &vk_instance); - if (vr != VK_SUCCESS) + if (!d3dkmt_vk_instance) { - WARN("Failed to create a Vulkan instance, vr %d.\n", vr); - goto done; + memset(&create_info, 0, sizeof(create_info)); + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.enabledExtensionCount = ARRAY_SIZE(extensions); + create_info.ppEnabledExtensionNames = extensions; + + vr = vulkan_funcs->p_vkCreateInstance(&create_info, NULL, &d3dkmt_vk_instance); + if (vr != VK_SUCCESS) + { + WARN("Failed to create a Vulkan instance, vr %d.\n", vr); + goto done; + } }
-#define LOAD_VK_FUNC(f) \ - if (!(p##f = (void *)vulkan_funcs->p_vkGetInstanceProcAddr(vk_instance, #f))) \ - { \ - WARN("Failed to load " #f ".\n"); \ - goto done; \ +#define LOAD_VK_FUNC(f) \ + if (!(p##f = (void *)vulkan_funcs->p_vkGetInstanceProcAddr(d3dkmt_vk_instance, #f))) \ + { \ + WARN("Failed to load " #f ".\n"); \ + goto done; \ }
LOAD_VK_FUNC(vkEnumeratePhysicalDevices) LOAD_VK_FUNC(vkGetPhysicalDeviceProperties2KHR) #undef LOAD_VK_FUNC
- vr = pvkEnumeratePhysicalDevices(vk_instance, &device_count, NULL); + vr = pvkEnumeratePhysicalDevices(d3dkmt_vk_instance, &device_count, NULL); if (vr != VK_SUCCESS || !device_count) { WARN("No Vulkan device found, vr %d, device_count %d.\n", vr, device_count); @@ -1185,7 +1194,7 @@ NTSTATUS CDECL X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *des if (!(vk_physical_devices = calloc(device_count, sizeof(*vk_physical_devices)))) goto done;
- vr = pvkEnumeratePhysicalDevices(vk_instance, &device_count, vk_physical_devices); + vr = pvkEnumeratePhysicalDevices(d3dkmt_vk_instance, &device_count, vk_physical_devices); if (vr != VK_SUCCESS) { WARN("vkEnumeratePhysicalDevices failed, vr %d.\n", vr); @@ -1210,19 +1219,20 @@ NTSTATUS CDECL X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *des }
adapter->handle = desc->hAdapter; - adapter->vk_instance = vk_instance; adapter->vk_device = vk_physical_devices[device_idx]; - pthread_mutex_lock(&d3dkmt_mutex); list_add_head(&x11_d3dkmt_adapters, &adapter->entry); - pthread_mutex_unlock(&d3dkmt_mutex); - free(vk_physical_devices); - return STATUS_SUCCESS; + status = STATUS_SUCCESS; + break; }
done: + if (d3dkmt_vk_instance && list_empty(&x11_d3dkmt_adapters)) + { + vulkan_funcs->p_vkDestroyInstance(d3dkmt_vk_instance, NULL); + d3dkmt_vk_instance = NULL; + } + pthread_mutex_unlock(&d3dkmt_mutex); free(vk_physical_devices); - if (vk_instance) - vulkan_funcs->p_vkDestroyInstance(vk_instance, NULL); return status; }
@@ -1253,7 +1263,7 @@ NTSTATUS CDECL X11DRV_D3DKMTQueryVideoMemoryInfo( D3DKMT_QUERYVIDEOMEMORYINFO *d if (adapter->handle != desc->hAdapter) continue;
- if (!(pvkGetPhysicalDeviceMemoryProperties2KHR = (void *)vulkan_funcs->p_vkGetInstanceProcAddr(adapter->vk_instance, "vkGetPhysicalDeviceMemoryProperties2KHR"))) + if (!(pvkGetPhysicalDeviceMemoryProperties2KHR = (void *)vulkan_funcs->p_vkGetInstanceProcAddr(d3dkmt_vk_instance, "vkGetPhysicalDeviceMemoryProperties2KHR"))) { WARN("Failed to load vkGetPhysicalDeviceMemoryProperties2KHR.\n"); status = STATUS_UNSUCCESSFUL;
From: Zhiyi Zhang zzhang@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53201 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/evr/sample.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/evr/sample.c b/dlls/evr/sample.c index f03292704b5..6a1bbf564f5 100644 --- a/dlls/evr/sample.c +++ b/dlls/evr/sample.c @@ -1747,7 +1747,10 @@ HRESULT WINAPI MFCreateVideoSampleFromSurface(IUnknown *surface, IMFSample **sam }
if (buffer) + { IMFSample_AddBuffer(object->sample, buffer); + IMFMediaBuffer_Release(buffer); + }
video_sample_create_tracking_thread();
From: Zhiyi Zhang zzhang@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53201 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/evr/tests/evr.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-)
diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index fa9ad2a0b35..37bb55739d3 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -1061,10 +1061,11 @@ static void test_surface_sample(void) ok(flags == 0x123, "Unexpected flags %#lx.\n", flags);
IMFSample_Release(sample); - -done: if (backbuffer) IDirect3DSurface9_Release(backbuffer); + ok(!IDirect3DDevice9_Release(device), "Unexpected refcount.\n"); + +done: DestroyWindow(window); }
@@ -1455,6 +1456,7 @@ static void test_default_presenter(void)
hr = MFGetService((IUnknown *)presenter, &MR_VIDEO_ACCELERATION_SERVICE, &IID_IDirect3DDeviceManager9, (void **)&dm); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IDirect3DDeviceManager9_Release(dm);
hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFVideoDisplayControl, (void **)&display_control); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1484,6 +1486,7 @@ static void test_default_presenter(void) hr = IMFVideoDisplayControl_GetVideoWindow(display_control, &hwnd2); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hwnd2 == hwnd, "Unexpected window %p.\n", hwnd2); + IMFVideoDisplayControl_Release(display_control);
/* Rate support. */ hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFRateSupport, (void **)&rate_support); @@ -1511,7 +1514,7 @@ static void test_default_presenter(void)
IMFRateSupport_Release(rate_support);
- IMFVideoPresenter_Release(presenter); + ok(!IMFVideoPresenter_Release(presenter), "Unexpected refcount.\n");
DestroyWindow(hwnd); } @@ -1697,7 +1700,10 @@ static void test_MFCreateVideoSampleAllocator(void) if (!(device = create_device(window))) { skip("Failed to create a D3D device, skipping tests.\n"); - goto done; + IMFMediaType_Release(video_type); + IMFMediaType_Release(media_type); + DestroyWindow(window); + return; }
hr = DXVA2CreateDirect3DDeviceManager9(&token, &manager); @@ -1735,14 +1741,13 @@ static void test_MFCreateVideoSampleAllocator(void) hr = IMFMediaBuffer_Unlock(buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ IMFMediaBuffer_Release(buffer); IMFSample_Release(sample); - IMFVideoSampleAllocator_Release(allocator); - + IMFMediaType_Release(video_type); IMFMediaType_Release(media_type); IDirect3DDeviceManager9_Release(manager); IDirect3DDevice9_Release(device); -done: DestroyWindow(window); }
@@ -2067,6 +2072,7 @@ static void test_presenter_native_video_size(void)
hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFTopologyServiceLookupClient, (void **)&lookup_client); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyServiceLookupClient_Release(lookup_client);
hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFVideoDisplayControl, (void **)&display_control); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2141,6 +2147,7 @@ static void test_presenter_native_video_size(void) ok((ratio.cx == 4 && ratio.cy == 3) || broken(!memcmp(&ratio, &size, sizeof(ratio))) /* < Win10 */, "Unexpected ratio %lu x %lu.\n", ratio.cx, ratio.cy);
+ IMFTopologyServiceLookupClient_Release(lookup_client); IMFMediaType_Release(video_type); IMFVideoDisplayControl_Release(display_control); IMFVideoPresenter_Release(presenter); @@ -2259,7 +2266,8 @@ static void test_presenter_video_window(void) hr = IDirect3DDeviceManager9_CloseDeviceHandle(dm, hdevice); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- IMFVideoDisplayControl_Release(display_control); + IDirect3DDeviceManager9_Release(dm); + ok(!IMFVideoDisplayControl_Release(display_control), "Unexpected refcount.\n");
DestroyWindow(window); } @@ -2420,6 +2428,7 @@ static void test_presenter_media_type(void)
hr = IMFTopologyServiceLookupClient_InitServicePointers(lookup_client, &host.IMFTopologyServiceLookup_iface); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyServiceLookupClient_Release(lookup_client);
hr = IMFVideoDisplayControl_SetVideoWindow(display_control, window); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2431,6 +2440,7 @@ static void test_presenter_media_type(void)
hr = IMFTransform_SetInputType(mixer, 0, input_type, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(input_type);
hr = IMFVideoPresenter_ProcessMessage(presenter, MFVP_MESSAGE_INVALIDATEMEDIATYPE, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2463,6 +2473,8 @@ static void test_presenter_media_type(void) IMFVideoDisplayControl_Release(display_control); IMFVideoPresenter_Release(presenter); IMFTransform_Release(mixer); + IDirect3DDeviceManager9_Release(manager); + IDirect3DDevice9_Release(device);
done: DestroyWindow(window); @@ -2949,6 +2961,7 @@ static void test_mixer_samples(void) ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr);
IMFDesiredSample_Clear(desired); + IMFDesiredSample_Release(desired);
hr = IMFTransform_ProcessInput(mixer, 0, NULL, 0); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); @@ -3045,8 +3058,8 @@ static void test_mixer_samples(void) IMFVideoProcessor_Release(processor); IMFTransform_Release(mixer);
- IDirect3DDevice9_Release(device); IDirect3DDeviceManager9_Release(manager); + ok(!IDirect3DDevice9_Release(device), "Unexpected refcount.\n");
done: DestroyWindow(window); @@ -3120,9 +3133,11 @@ static void test_mixer_render(void)
hr = IMFTransform_QueryInterface(mixer, &IID_IMFVideoProcessor, (void **)&processor); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFVideoProcessor_Release(processor);
hr = IMFTransform_QueryInterface(mixer, &IID_IMFVideoMixerControl, (void **)&mixer_control); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFVideoMixerControl_Release(mixer_control);
/* Configure device and media types. */ hr = DXVA2CreateDirect3DDeviceManager9(&token, &manager); @@ -3197,8 +3212,8 @@ static void test_mixer_render(void) IDirect3DSurface9_Release(surface); IMFTransform_Release(mixer);
- IDirect3DDevice9_Release(device); IDirect3DDeviceManager9_Release(manager); + ok(!IDirect3DDevice9_Release(device), "Unexpected refcount.\n");
done: DestroyWindow(window);
From: Zhiyi Zhang zzhang@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53231 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/d2d1/tests/d2d1.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 6ba4d8cec59..960d4480747 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -1168,7 +1168,8 @@ static void release_test_context_(unsigned int line, struct d2d1_test_context *c IDXGISurface_Release(ctx->surface); IDXGISwapChain_Release(ctx->swapchain); DestroyWindow(ctx->window); - IDXGIDevice_Release(ctx->device); + ref = IDXGIDevice_Release(ctx->device); + ok_(__FILE__, line)(!ref, "Device has %lu references left.\n", ref); }
#define init_test_context(ctx, d3d11) init_test_context_(__LINE__, ctx, d3d11) @@ -5182,16 +5183,17 @@ static void test_shared_bitmap(BOOL d3d11) todo_wine_if(i == 2 || i == 3 || i == 5 || i == 6) ok(hr == bitmap_format_tests[i].hr, "%u: Got unexpected hr %#lx.\n", i, hr);
- if (SUCCEEDED(bitmap_format_tests[i].hr)) + if (SUCCEEDED(hr) && hr == bitmap_format_tests[i].hr) { pixel_format = ID2D1Bitmap_GetPixelFormat(bitmap2); ok(pixel_format.format == bitmap_format_tests[i].result.format, "%u: unexpected pixel format %#x.\n", i, pixel_format.format); ok(pixel_format.alphaMode == bitmap_format_tests[i].result.alphaMode, "%u: unexpected alpha mode %d.\n", i, pixel_format.alphaMode); + }
+ if (SUCCEEDED(hr)) ID2D1Bitmap_Release(bitmap2); - } } }
@@ -5723,6 +5725,8 @@ static void test_draw_text_layout(BOOL d3d11) IDWriteRenderingParams_Release(rendering_params); }
+ ID2D1SolidColorBrush_Release(brush2); + ID2D1SolidColorBrush_Release(brush); IDWriteTextFormat_Release(text_format); IDWriteTextLayout_Release(text_layout); IDWriteFactory_Release(dwrite_factory); @@ -8899,7 +8903,7 @@ static void test_bitmap_surface(BOOL d3d11) todo_wine_if(bitmap_format_tests[i].hr == WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT) ok(hr == bitmap_format_tests[i].hr, "%u: Got unexpected hr %#lx.\n", i, hr);
- if (SUCCEEDED(bitmap_format_tests[i].hr)) + if (SUCCEEDED(hr) && hr == bitmap_format_tests[i].hr) { pixel_format = ID2D1Bitmap1_GetPixelFormat(bitmap);
@@ -8907,9 +8911,10 @@ static void test_bitmap_surface(BOOL d3d11) i, pixel_format.format); ok(pixel_format.alphaMode == bitmap_format_tests[i].result.alphaMode, "%u: unexpected alpha mode %d.\n", i, pixel_format.alphaMode); + }
+ if (SUCCEEDED(hr)) ID2D1Bitmap1_Release(bitmap); - } }
/* A8 surface */ @@ -8943,6 +8948,7 @@ static void test_bitmap_surface(BOOL d3d11) ID2D1DeviceContext_SetTarget(device_context, (ID2D1Image *)bitmap); ID2D1DeviceContext_GetTarget(device_context, &target); ok(target == (ID2D1Image *)bitmap, "Unexpected target.\n"); + ID2D1Image_Release(target);
check_rt_bitmap_surface((ID2D1RenderTarget *)device_context, TRUE, D2D1_BITMAP_OPTIONS_NONE);
@@ -9036,6 +9042,7 @@ static void test_bitmap_surface(BOOL d3d11)
hr = ID2D1Factory1_CreateWicBitmapRenderTarget(factory, wic_bitmap, &rt_desc, &rt); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IWICBitmap_Release(wic_bitmap);
check_rt_bitmap_surface(rt, FALSE, D2D1_BITMAP_OPTIONS_NONE); ID2D1RenderTarget_Release(rt); @@ -9423,6 +9430,7 @@ static void test_command_list(BOOL d3d11) { ID2D1DeviceContext_Release(device_context); ID2D1Factory1_Release(factory); + release_test_context(&ctx); return; }
From: Zhiyi Zhang zzhang@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53235 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 48f7a444add..deed2408a41 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6639,11 +6639,12 @@ static void test_MFCreateDXSurfaceBuffer(void) IMF2DBuffer2_Release(_2dbuffer2);
IMFMediaBuffer_Release(buffer); + IDirect3DDevice9_Release(device);
done: if (backbuffer) IDirect3DSurface9_Release(backbuffer); - IDirect3D9_Release(d3d); + ok(!IDirect3D9_Release(d3d), "Unexpected refcount.\n"); DestroyWindow(window); }
@@ -7618,6 +7619,8 @@ static void test_sample_allocator_d3d9(void)
IMFVideoSampleAllocator_Release(allocator); IMFMediaType_Release(video_type); + IDirect3DDeviceManager9_Release(d3d9_manager); + IDirect3DDevice9_Release(d3d9_device);
done: IDirect3D9_Release(d3d9); @@ -7725,6 +7728,7 @@ static void test_sample_allocator_d3d11(void) hr = IMFMediaBuffer_Unlock(buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ IMFMediaBuffer_Release(buffer); IMFSample_Release(sample);
IMFVideoSampleAllocator_Release(allocator); @@ -7867,7 +7871,7 @@ static void test_sample_allocator_d3d11(void)
static void test_sample_allocator_d3d12(void) { - IMFVideoSampleAllocator *allocator; + IMFVideoSampleAllocator *allocator = NULL; D3D12_HEAP_PROPERTIES heap_props; IMFDXGIDeviceManager *manager; D3D12_HEAP_FLAGS heap_flags; @@ -7955,11 +7959,12 @@ static void test_sample_allocator_d3d12(void)
ID3D12Resource_Release(resource); IMFDXGIBuffer_Release(dxgi_buffer); + IMFMediaBuffer_Release(buffer); IMFSample_Release(sample);
- IMFVideoSampleAllocator_Release(allocator); - done: + if (allocator) + IMFVideoSampleAllocator_Release(allocator); IMFDXGIDeviceManager_Release(manager); ID3D12Device_Release(device); }
Could you split winex11 change to a separate MR?
On Thu Jun 30 08:02:49 2022 +0000, Nikolay Sivov wrote:
Could you split winex11 change to a separate MR?
I can. But I do that the d2d1/tests will not succeed even after those resource leaks are fixed. Probably due to some tests holding multiple d3d devices at one time. Do you still want me to split it?
On Thu Jun 30 08:04:26 2022 +0000, Zhiyi Zhang wrote:
I can. But if I do that the d2d1/tests will not succeed even after those resource leaks are fixed. Probably due to some tests holding multiple d3d devices at one time. Do you still want me to split it?
There is no way for me to mark just some commits as approved, and I don't know what's going on with winex11 to review it. That's the only reason I asked.