wgc framepool buffer

This commit is contained in:
pigeatgarlic 2026-05-09 11:49:22 +07:00
parent d50b6ac0c2
commit 71b561b95e
3 changed files with 27 additions and 19 deletions

View File

@ -245,6 +245,10 @@ protected:
std::chrono::milliseconds timeout, bool cursor_visible) = 0;
virtual capture_e release_snapshot() = 0;
virtual int complete_img(img_t *img, bool dummy) = 0;
// DDUP holds an unfair D3D11 device lock during AcquireNextFrame, which starves
// the encoder thread on timeout. WGC doesn't have this problem.
virtual bool needs_timeout_yield() const { return true; }
};
class display_ram_t : public display_base_t {
@ -391,6 +395,7 @@ public:
std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout,
bool cursor_visible) override;
capture_e release_snapshot() override;
bool needs_timeout_yield() const override { return false; }
};
/**
@ -405,5 +410,6 @@ public:
std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout,
bool cursor_visible) override;
capture_e release_snapshot() override;
bool needs_timeout_yield() const override { return false; }
};
} // namespace platf::dxgi

View File

@ -256,24 +256,26 @@ capture_e display_base_t::capture(const push_captured_image_cb_t &push_captured_
frame_pacing_group_frames = 1;
} else if (status == platf::capture_e::timeout) {
// The D3D11 device is protected by an unfair lock that is held the entire time that
// IDXGIOutputDuplication::AcquireNextFrame() is running. This is normally harmless,
// however sometimes the encoding thread needs to interact with our ID3D11Device to
// create dummy images or initialize the shared state that is used to pass textures
// between the capture and encoding ID3D11Devices.
//
// When we're in a state where we're not actively receiving frames regularly, we will
// spend almost 100% of our time in AcquireNextFrame() holding that critical lock.
// Worse still, since it's unfair, we can monopolize it while the encoding thread
// is starved. The encoding thread may acquire it for a few moments across a few
// ID3D11Device calls before losing it again to us for another long time waiting in
// AcquireNextFrame(). The starvation caused by this lock contention causes encoder
// reinitialization to take several seconds instead of a fraction of a second.
//
// To avoid starving the encoding thread, sleep without the lock held for a little
// while each time we reach our max frame timeout. This will only happen when nothing
// is updating the display, so no visible stutter should be introduced by the sleep.
timer->sleep_for(10ms);
if (needs_timeout_yield()) {
// The D3D11 device is protected by an unfair lock that is held the entire time that
// IDXGIOutputDuplication::AcquireNextFrame() is running. This is normally harmless,
// however sometimes the encoding thread needs to interact with our ID3D11Device to
// create dummy images or initialize the shared state that is used to pass textures
// between the capture and encoding ID3D11Devices.
//
// When we're in a state where we're not actively receiving frames regularly, we will
// spend almost 100% of our time in AcquireNextFrame() holding that critical lock.
// Worse still, since it's unfair, we can monopolize it while the encoding thread
// is starved. The encoding thread may acquire it for a few moments across a few
// ID3D11Device calls before losing it again to us for another long time waiting in
// AcquireNextFrame(). The starvation caused by this lock contention causes encoder
// reinitialization to take several seconds instead of a fraction of a second.
//
// To avoid starving the encoding thread, sleep without the lock held for a little
// while each time we reach our max frame timeout. This will only happen when nothing
// is updating the display, so no visible stutter should be introduced by the sleep.
timer->sleep_for(10ms);
}
}
}

View File

@ -127,7 +127,7 @@ int wgc_capture_t::init(display_base_t *display, const ::video::config_t &config
frame_pool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded(
uwp_device,
static_cast<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>(display->capture_format),
2, item.Size());
4, item.Size());
capture_session = frame_pool.CreateCaptureSession(item);
frame_pool.FrameArrived({this, &wgc_capture_t::on_frame_arrived});
} catch (winrt::hresult_error &e) {