Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does Direct3D11CaptureFramePool actually require a DispatcherQueue? #59

Closed
jpark37 opened this issue May 21, 2020 · 10 comments
Closed

Does Direct3D11CaptureFramePool actually require a DispatcherQueue? #59

jpark37 opened this issue May 21, 2020 · 10 comments

Comments

@jpark37
Copy link

jpark37 commented May 21, 2020

Despite the comment saying a DispatcherQueue is required, I don't instantiate one in my application, and window capture seems to work fine. What are the consequences of not doing this?

@jpark37
Copy link
Author

jpark37 commented May 21, 2020

I ask because we're hearing infrequent reports of shutdown hangs, and I'm wondering if this is the reason.

@robmikh
Copy link
Member

robmikh commented May 21, 2020

When the frame pool is created with Direct3D11CaptureFramePool::Create, we use the DispatcherQueue to fire the frame arrived event to the thread that created the frame pool. I could have sworn calling Create without a dispatcher queue present would cause us to blow up in an internal call. Although testing it now, this behavior seems to have regressed...

Regardless, the most important part of the DispatcherQueue requirement is that you are pumping messages on that thread. As long as you're doing that, I wouldn't expect it to be the problem. Usually when I see odd behavior on tear-down, it was because I wasn't properly initializing COM on the thread. This can cause odd life-time related bugs, and may be what you're seeing but I can't be certain.

I know that you mentioned hearing infrequent reports of this, but if you get a consistent repro or a memory dump when this happens I can take a look.

@robmikh
Copy link
Member

robmikh commented May 21, 2020

Additionally, I would recommend creating a DispatcherQueue for your thread by calling CreateDispatcherQueueController. The fact that it's working without it has me a bit concerned about if the system changes in the future.

Or you can forgo the dispatcher requirement all together and use Direct3D11CaptureFramePool::CreateFreeThreaded, and then use your own mechanism to marshal the frame to a thread of your choosing.

@jpark37
Copy link
Author

jpark37 commented May 21, 2020

I can let a dispatcher queue live for the life of the thread. For simplicity, we do all WGC things on our render thread since we have that's where our D3D11 renderer operates. I suppose this would be advised?

void render_thread()
{
    // Add dispatcher queue here.
    queue = ...

    while (running) {
        tick sources, possibly create/destroy WGC captures
        render sources
        pump win32 messages until empty <- frame arrival callback, D3D11 texture copy here
    }

    // Let queue fall out of scope I guess?
}

We have a separate message pump that lives on the main thread that is implicit to Qt. Hopefully these aren't fighting each other, but my Win32/COM knowledge isn't that strong. The render thread pump was added recently just to support WGC.

I can find out if we have any memory dumps.

@robmikh
Copy link
Member

robmikh commented May 21, 2020

That sounds reasonable to me, and having the two different pumps on two different threads should be ok. The only thing I would add is that I would add a call to RoInitialize (or in your case, winrt::init_apartmnet, since you seem to be using C++/WinRT) before interacting/activating any WinRT objects on that thread.

@jpark37
Copy link
Author

jpark37 commented May 21, 2020

Thanks, you reminded me I didn't set up the winrt apartment either. I also noticed we were keeping WGC objects alive across render threads (we kill and recreate the thread in some situations), so I added a fix to destroy on thread stop and recreate on thread start. Hopefully everything is robust and stable now.

@notr1ch
Copy link

notr1ch commented May 21, 2020

I have a couple of memory dumps from the shutdown freezes we experienced, these are full heap dumps so I don't want to link them publicly. The WGC thread was stuck in a COM call to the "Capture Service" process. Debugging the service showed no stuck threads, almost like it wasn't aware of the COM call.

rpcrt4!LRPC_BASE_CCALL::DoSendReceive+0x112
rpcrt4!LRPC_CCALL::SendReceive+0x51
rpcrt4!I_RpcSendReceive+0x6f
combase!CMessageCall::CallI_RpcSendReceive+0x30 [onecore\com\combase\dcomrem\call.cxx @ 3956] 
combase!ThreadSendReceive+0xf0 [onecore\com\combase\dcomrem\channelb.cxx @ 7316] 
combase!CSyncClientCall::SwitchAptAndDispatchCall+0xa0 [onecore\com\combase\dcomrem\channelb.cxx @ 5742] 
combase!CSyncClientCall::SendReceive2+0x182 [onecore\com\combase\dcomrem\channelb.cxx @ 5377] 
combase!SyncClientCallRetryContext::SendReceiveWithRetry+0x25 [onecore\com\combase\dcomrem\callctrl.cxx @ 1617] 
combase!CSyncClientCall::SendReceiveInRetryContext+0x25 [onecore\com\combase\dcomrem\callctrl.cxx @ 567] 
combase!DefaultSendReceive+0x64 [onecore\com\combase\dcomrem\callctrl.cxx @ 525] 
combase!CSyncClientCall::SendReceive+0x128 [onecore\com\combase\dcomrem\ctxchnl.cxx @ 783] 
combase!CClientChannel::SendReceive+0x84 [onecore\com\combase\dcomrem\ctxchnl.cxx @ 653] 
combase!NdrExtpProxySendReceive+0x4e [onecore\com\combase\ndr\ndrole\proxy.cxx @ 2002] 
rpcrt4!NdrpClientCall3+0x395
combase!ObjectStublessClient+0x13b [onecore\com\combase\ndr\ndrole\amd64\stblsclt.cxx @ 369] 
combase!ObjectStubless+0x42 [onecore\com\combase\ndr\ndrole\amd64\stubless.asm @ 176] 
GraphicsCapture!winrt::Windows::Graphics::Capture::implementation::ServerCaptureSessionCore::StopCapture+0x15
GraphicsCapture!winrt::Windows::Graphics::Capture::implementation::GraphicsCaptureSession::Close+0x34
GraphicsCapture!winrt::impl::produce<winrt::Windows::Graphics::Capture::implementation::GraphicsCaptureSession,winrt::Windows::Foundation::IClosable>::Close+0x1f
libobs_winrt!winrt::impl::consume_Windows_Foundation_IClosable<winrt::Windows::Graphics::Capture::GraphicsCaptureSession>::Close+0x26 [c:\program files (x86)\windows kits\10\include\10.0.18362.0\cppwinrt\winrt\windows.foundation.h @ 17] 
libobs_winrt!winrt_capture_free+0xa3 [d:\obs2\libobs-winrt\winrt-capture.cpp @ 453] 
win_capture!wc_tick+0xb6 [d:\obs2\plugins\win-capture\window-capture.c @ 402] 
obs!obs_source_video_tick+0x1a2 [d:\obs2\libobs\obs-source.c @ 1159] 
obs!tick_sources+0x103 [d:\obs2\libobs\obs-video.c @ 71] 
obs!obs_graphics_thread+0x204 [d:\obs2\libobs\obs-video.c @ 905] 
w32_pthreads!ptw32_threadStart+0x8a [d:\obs2\deps\w32-pthreads\ptw32_threadstart.c @ 225] 
ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21```

@robmikh
Copy link
Member

robmikh commented May 21, 2020

Thanks, @notr1ch! It would be interesting to know if this still happens after initializing COM on the thread. If the proxy stub is blocked and the service is unaware of it, it might be an initialization issue.

If it persists, we can maybe talk through another channel about looking at the dump if you feel comfortable. A dump of the capture service would be interesting as well.

@robmikh
Copy link
Member

robmikh commented Jan 22, 2021

Closing this issue. If you still see this after properly initializing COM, please let us know.

@robmikh robmikh closed this as completed Jan 22, 2021
@jpark37
Copy link
Author

jpark37 commented Jan 22, 2021

We added the DispatchQueue and RoInitialize call, and we haven't seen the problem since, so we're probably good now.

I also added code to disable the yellow border recently, but I had to switch our thread from STA to MTA to get rid of the assert from blocking on the RequestAccessAsync result in-place. Do you think this is fine?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants