feat(win): new capture method, Windows.Graphics.Capture (#2580)

This commit is contained in:
Tejas Rao 2024-05-27 11:16:14 -07:00 committed by GitHub
commit 287ac4c0fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 701 additions and 154 deletions

View file

@ -945,9 +945,8 @@ namespace platf::dxgi {
}
capture_e
display_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
display_ddup_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
HRESULT status;
DXGI_OUTDUPL_FRAME_INFO frame_info;
resource_t::pointer res_p {};
@ -1329,9 +1328,14 @@ namespace platf::dxgi {
return capture_e::ok;
}
capture_e
display_ddup_vram_t::release_snapshot() {
return dup.release_frame();
}
int
display_vram_t::init(const ::video::config_t &config, const std::string &display_name) {
if (display_base_t::init(config, display_name)) {
display_ddup_vram_t::init(const ::video::config_t &config, const std::string &display_name) {
if (display_base_t::init(config, display_name) || dup.init(this, config)) {
return -1;
}
@ -1410,6 +1414,80 @@ namespace platf::dxgi {
return 0;
}
/**
* Get the next frame from the Windows.Graphics.Capture API and copy it into a new snapshot texture.
* @param pull_free_image_cb call this to get a new free image from the video subsystem.
* @param img_out the captured frame is returned here
* @param timeout how long to wait for the next frame
* @param cursor_visible
*/
capture_e
display_wgc_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
texture2d_t src;
uint64_t frame_qpc;
dup.set_cursor_visible(cursor_visible);
auto capture_status = dup.next_frame(timeout, &src, frame_qpc);
if (capture_status != capture_e::ok)
return capture_status;
auto frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), frame_qpc);
D3D11_TEXTURE2D_DESC desc;
src->GetDesc(&desc);
// It's possible for our display enumeration to race with mode changes and result in
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
if (desc.Width != width_before_rotation || desc.Height != height_before_rotation) {
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
return capture_e::reinit;
}
// It's also possible for the capture format to change on the fly. If that happens,
// reinitialize capture to try format detection again and create new images.
if (capture_format != desc.Format) {
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
return capture_e::reinit;
}
std::shared_ptr<platf::img_t> img;
if (!pull_free_image_cb(img))
return capture_e::interrupted;
auto d3d_img = std::static_pointer_cast<img_d3d_t>(img);
d3d_img->blank = false; // image is always ready for capture
if (complete_img(d3d_img.get(), false) == 0) {
texture_lock_helper lock_helper(d3d_img->capture_mutex.get());
if (lock_helper.lock()) {
device_ctx->CopyResource(d3d_img->capture_texture.get(), src.get());
}
else {
BOOST_LOG(error) << "Failed to lock capture texture";
return capture_e::error;
}
}
else {
return capture_e::error;
}
img_out = img;
if (img_out) {
img_out->frame_timestamp = frame_timestamp;
}
return capture_e::ok;
}
capture_e
display_wgc_vram_t::release_snapshot() {
return dup.release_frame();
}
int
display_wgc_vram_t::init(const ::video::config_t &config, const std::string &display_name) {
if (display_base_t::init(config, display_name) || dup.init(this, config))
return -1;
return 0;
}
std::shared_ptr<platf::img_t>
display_vram_t::alloc_img() {
auto img = std::make_shared<img_d3d_t>();