- Make it present some frames - Make it check that the frames are from the new device, instead of checking TerminateDevice count; fixes https://bugs.winehq.org/show_bug.cgi?id=55649 - Make it check for and avoid some absurd crash on Windows
I tried everything I could think of, but Windows absolutely refuses to present a frame after changing the D3D device, instead choosing to return various absurd errors (segfaults, deadlocks, out of memory, etc). I suspect the VMR9 is haunted.
From: Alfred Agrell floating@muncher.se
- Make it present some frames - Make it check that the frames are from the new device - Make it check for and tolerate some absurd crash on Windows --- dlls/quartz/tests/vmr9.c | 71 +++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-)
diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c index 447273a91df..ae7cba63972 100644 --- a/dlls/quartz/tests/vmr9.c +++ b/dlls/quartz/tests/vmr9.c @@ -2934,6 +2934,7 @@ struct presenter
D3DFORMAT format; DWORD accept_flags; + IDirect3DDevice9 *device; IDirect3DSurface9 *surfaces[5]; IVMRSurfaceAllocatorNotify9 *notify; unsigned int got_PresentImage, got_TerminateDevice; @@ -2979,9 +2980,13 @@ static HRESULT WINAPI presenter_StopPresenting(IVMRImagePresenter9 *iface, DWORD static HRESULT WINAPI presenter_PresentImage(IVMRImagePresenter9 *iface, DWORD_PTR cookie, VMR9PresentationInfo *info) { struct presenter *presenter = impl_from_IVMRImagePresenter9(iface); + IDirect3DDevice9 *device; static const RECT rect;
if (winetest_debug > 1) trace("PresentImage()\n"); + IDirect3DSurface9_GetDevice(info->lpSurf, &device); + ok(device == presenter->device, "got %p, expected %p\n", device, presenter->device); + IDirect3DDevice9_Release(device); ok(cookie == 0xabacab, "Got cookie %#Ix.\n", cookie); todo_wine ok(info->dwFlags == VMR9Sample_TimeValid, "Got flags %#lx.\n", info->dwFlags); ok(!info->rtStart, "Got start time %s.\n", wine_dbgstr_longlong(info->rtStart)); @@ -3224,6 +3229,7 @@ static void test_renderless_formats(void) IBaseFilter_QueryInterface(filter, &IID_IVMRSurfaceAllocatorNotify9, (void **)¬ify);
hr = IVMRSurfaceAllocatorNotify9_SetD3DDevice(notify, device, MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY)); + presenter.device = device; if (hr == E_NOINTERFACE) { win_skip("Direct3D does not support video rendering.\n"); @@ -4307,12 +4313,11 @@ static void test_changed3ddevice(void) { VIDEOINFOHEADER vih = { + .rcSource = {4, 6, 16, 12}, + .rcTarget = {40, 60, 160, 120}, .bmiHeader.biSize = sizeof(BITMAPINFOHEADER), - .bmiHeader.biBitCount = 32, .bmiHeader.biWidth = 32, .bmiHeader.biHeight = 16, - .bmiHeader.biPlanes = 1, - .bmiHeader.biCompression = BI_RGB, }; AM_MEDIA_TYPE req_mt = { @@ -4329,12 +4334,17 @@ static void test_changed3ddevice(void) .refcount = 1, .accept_flags = VMR9AllocFlag_TextureSurface, }; + ALLOCATOR_PROPERTIES req_props = {5, 32 * 16 * 4, 1, 0}, ret_props; IBaseFilter *filter = create_vmr9(VMR9Mode_Renderless); IFilterGraph2 *graph = create_graph(); IVMRSurfaceAllocatorNotify9 *notify; IDirect3DDevice9 *device, *device2; RECT rect = {0, 0, 640, 480}; struct testfilter source; + IMemAllocator *allocator; + IMediaControl *control; + IMemInputPin *input; + OAFilterState state; IPin *pin = NULL; HWND window; HRESULT hr; @@ -4360,6 +4370,7 @@ static void test_changed3ddevice(void) ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IVMRSurfaceAllocatorNotify9_SetD3DDevice(notify, device, MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY)); + presenter.device = device; if (hr == E_NOINTERFACE) { win_skip("Direct3D does not support video rendering.\n"); @@ -4374,20 +4385,64 @@ static void test_changed3ddevice(void) hr = IFilterGraph2_ConnectDirect(graph, &source.source.pin.IPin_iface, pin, &req_mt); ok(hr == S_OK, "Got hr %#lx.\n", hr);
+ IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&input); + + hr = IMemInputPin_GetAllocator(input, &allocator); + todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + if (hr == VFW_E_NO_ALLOCATOR) + { + CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, + &IID_IMemAllocator, (void **)&allocator); + + hr = IMemInputPin_NotifyAllocator(input, allocator, TRUE); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + } + hr = IMemAllocator_SetProperties(allocator, &req_props, &ret_props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_Commit(allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + device2 = create_device(window); ok(device2 != NULL, "Couldn't create device\n");
- ok(presenter.got_TerminateDevice == 0, "got %d\n", presenter.got_TerminateDevice); - hr = IVMRSurfaceAllocatorNotify9_ChangeD3DDevice(notify, device2, MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY)); ok(hr == S_OK, "Got hr %#lx.\n", hr); - ok(presenter.got_TerminateDevice == 1, "got %d\n", presenter.got_TerminateDevice); + presenter.device = device2; + + /* Changing the device seems to be (at least partially) asynchronous. */ + Sleep(200); + hr = IMemAllocator_Decommit(allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_Commit(allocator); + if (hr == E_OUTOFMEMORY) + { + win_skip("ChangeD3DDevice seems to corrupt the allocator somehow\n"); + } + else + { + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); + hr = IMediaControl_Run(control); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + ok(presenter.got_PresentImage == 0, "Got %u calls to PresentImage().\n", presenter.got_PresentImage);
+ join_thread(send_frame(input)); + hr = IMediaControl_GetState(control, 1000, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(presenter.got_PresentImage == 1, "Got %u calls to PresentImage().\n", presenter.got_PresentImage); + IMediaControl_Release(control); + } + + IMemAllocator_Release(allocator); + IMemInputPin_Release(input); + IPin_Release(pin); IDirect3DDevice9_Release(device2);
out: - if (pin) - IPin_Release(pin); ref = IFilterGraph2_Release(graph); ok(!ref, "Got outstanding refcount %ld.\n", ref); IVMRSurfaceAllocatorNotify9_Release(notify);
I tried everything I could think of, but Windows absolutely refuses to present a frame after changing the D3D device, instead choosing to return various absurd errors (segfaults, deadlocks, out of memory, etc). I suspect the VMR9 is haunted.
This was for Kirikiri, right? How is that application using ChangeD3DDevice()?
Like this: https://github.com/krkrz/krkr2/blob/master/kirikiri2/trunk/kirikiri2/src/cor...
I tried looking for differences, but that thing is huge and hard to follow, I couldn't find anything obviously relevant. The deadlocks, segfaults and timing dependencies make no sense either, Windows is usually pretty good at returning error codes instead.
...maybe I should run microkiri in Wine (!3938 edition), trace all calls into krmovie, make something headless that repeats them, submit that to testbot, and hope it doesn't return the same crazy segfaults. If it works, I can then trace all calls into VMR9 and reproduce that, then simplify it until I figure out what nonsense is going on here. That sure is some heavy work for testing a single function, but...
Like this: https://github.com/krkrz/krkr2/blob/master/kirikiri2/trunk/kirikiri2/src/cor...
Well, that doesn't tell us very much, not without reading through a lot of C++. Perhaps counterintuitively, it may be easier to get an idea of what's happening from a +quartz log.
Yes, they're both useful. Runtime logs tell what the program does, source code tells what it thinks it's doing and what the expected answer is.
Testing with a custom exe that loads krmovie.dll reveals that Kirikiri can run ChangeD3DDevice properly on the testbot, so clearly this test needs to be fixed somehow. Is there a button to mark a MR as draft while I figure out exactly how, other than closing it and opening a new one?
Is there a button to mark a MR as draft while I figure out exactly how, other than closing it and opening a new one?
There is, though I have no idea where. You could also close this merge request and then reopen it later.
Found it, it's in the Edit button beside the title.