diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index e4d43b33..ecc0bc2d 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -185,6 +185,8 @@ public: vs_t scene_vs; gpu_cursor_t cursor; + + texture2d_t last_frame_copy; }; } // namespace platf::dxgi diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index bc10b5a5..ff884589 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -292,22 +292,10 @@ int display_base_t::init(int framerate, const std::string &display_name) { //FIXME: Duplicate output on RX580 in combination with DOOM (2016) --> BSOD { - dxgi::output1_t output1 {}; - dxgi::output5_t output5 {}; - // IDXGIOutput5 is optional, but can provide improved performance and wide color support + dxgi::output5_t output5 {}; status = output->QueryInterface(IID_IDXGIOutput5, (void **)&output5); - if(FAILED(status)) { - BOOST_LOG(warning) << "Failed to query IDXGIOutput5 from the output"sv; - } - - status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; - return -1; - } - - if(output5) { + if(SUCCEEDED(status)) { // Ask the display implementation which formats it supports auto supported_formats = get_supported_sdr_capture_formats(); if(supported_formats.empty()) { @@ -324,12 +312,24 @@ int display_base_t::init(int framerate, const std::string &display_name) { std::this_thread::sleep_for(200ms); } + // We don't retry with DuplicateOutput() because we can hit this codepath when we're racing + // with mode changes and we don't want to accidentally fall back to suboptimal capture if + // we get unlucky and succeed below. if(FAILED(status)) { BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; } } + else { + BOOST_LOG(warning) << "IDXGIOutput5 is not supported by your OS. Capture performance may be reduced."sv; + + dxgi::output1_t output1 {}; + status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; + return -1; + } - if(!output5 || FAILED(status)) { for(int x = 0; x < 2; ++x) { status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup); if(SUCCEEDED(status)) { diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index ae84bfb0..7c9c8674 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -628,7 +628,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec cursor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible); } - if(capture_format != DXGI_FORMAT_UNKNOWN || frame_update_flag) { + if(frame_update_flag) { texture2d_t src {}; // Get the texture object from this frame @@ -641,12 +641,6 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec D3D11_TEXTURE2D_DESC desc; src->GetDesc(&desc); - // If we don't know the capture format yet, grab it from this texture - if(capture_format == DXGI_FORMAT_UNKNOWN) { - capture_format = desc.Format; - BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']'; - } - // 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 || desc.Height != height) { @@ -654,6 +648,29 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::reinit; } + // If we don't know the capture format yet, grab it from this texture + if(capture_format == DXGI_FORMAT_UNKNOWN) { + capture_format = desc.Format; + BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']'; + + D3D11_TEXTURE2D_DESC t {}; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = capture_format; + t.BindFlags = 0; + + // Create a texture to store the most recent copy of the desktop + auto status = device->CreateTexture2D(&t, nullptr, &last_frame_copy); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + } + // 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) { @@ -666,10 +683,11 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::error; } - // Copy the texture into this image + // Copy the texture into this image and the staging texture device_ctx->CopyResource(img->texture.get(), src.get()); + device_ctx->CopyResource(last_frame_copy.get(), src.get()); } - else { + else if(capture_format == DXGI_FORMAT_UNKNOWN) { // We don't know the final capture format yet, so we will encode a dummy image BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv; @@ -677,6 +695,18 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::error; } } + else { + // We must know the capture format in this path or we would have hit the above unknown format case + if(complete_img(img, false)) { + return capture_e::error; + } + + // We have a previously captured frame to reuse. We can't just grab the src texture from + // the call to AcquireNextFrame() because that won't be valid. It seems to return a texture + // in the unmodified desktop format (rather than the formats we passed to DuplicateOutput1()) + // if called in that case. + device_ctx->CopyResource(img->texture.get(), last_frame_copy.get()); + } if(cursor.visible && cursor_visible) { D3D11_VIEWPORT view {