From 7dc2d41b77f4ecda8f87afaf65abd81b39fd7529 Mon Sep 17 00:00:00 2001 From: Chase Payne Date: Sat, 17 Aug 2024 01:36:44 -0500 Subject: [PATCH] implement cgutmans ideas --- src/platform/windows/display_base.cpp | 18 +++++++--- tools/ddprobe.cpp | 49 ++++++++++++++++++--------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 3d178daf..18f5cb8e 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -6,6 +6,7 @@ #include #include +#include #include // We have to include boost/process.hpp before display.h due to WinSock.h, @@ -363,8 +364,10 @@ namespace platf::dxgi { // we spawn a helper tool to probe for us before we set our own GPU preference. bool probe_for_gpu_preference(const std::string &display_name) { - // If we've already been through here, there's nothing to do this time. static bool set_gpu_preference = false; + static bool verify_frame_capture = true; + + // If we've already been through here, there's nothing to do this time. if (set_gpu_preference) { return true; } @@ -378,17 +381,22 @@ namespace platf::dxgi { for (int i = 1; i < 5; i++) { // Run the probe tool. It returns the status of DuplicateOutput(). // - // Arg format: [GPU preference] [Display name] + // Arg format: [GPU preference] [Display name] [--verify--frame-capture] HRESULT result; + std::vector args = { std::to_string(i), display_name }; try { - result = bp::system(cmd, std::to_string(i), display_name, bp::std_out > bp::null, bp::std_err > bp::null); + if (verify_frame_capture) { + args.push_back("--verify-frame-capture"); + } + result = bp::system(cmd, bp::args(args), bp::std_out > bp::null, bp::std_err > bp::null); } catch (bp::process_error &e) { BOOST_LOG(error) << "Failed to start ddprobe.exe: "sv << e.what(); return false; } - BOOST_LOG(info) << "ddprobe.exe ["sv << i << "] ["sv << display_name << "] returned: 0x"sv << util::hex(result).to_string_view(); + BOOST_LOG(info) << "ddprobe.exe " << boost::algorithm::join(args, " ") << "returned 0x" + << util::hex(result).to_string_view(); // E_ACCESSDENIED can happen at the login screen. If we get this error, // we know capture would have been supported, because DXGI_ERROR_UNSUPPORTED @@ -410,6 +418,8 @@ namespace platf::dxgi { } // If none of the manual options worked, leave the GPU preference alone + // And set the verify frame capture option to false, just in case there is a chance for a false negative. + verify_frame_capture = false; return false; } diff --git a/tools/ddprobe.cpp b/tools/ddprobe.cpp index 24ffb3d1..79c1c994 100644 --- a/tools/ddprobe.cpp +++ b/tools/ddprobe.cpp @@ -121,7 +121,7 @@ is_valid_frame(const D3D11_MAPPED_SUBRESOURCE &mappedResource, const D3D11_TEXTU * * @param dup A reference to the DXGI output duplication object (`dxgi::dup_t&`) used to acquire frames. * @param device A ComPtr to the ID3D11Device interface representing the device associated with the Direct3D context. - * @return HRESULT Returns `S_OK` if a non-empty frame is captured successfully, `S_FALSE` if all frames are empty, or an error code if any failure occurs during the process. + * @return HRESULT Returns `S_OK` if a non-empty frame is captured successfully, `E_FAIL` if all frames are empty, or an error code if any failure occurs during the process. */ HRESULT test_frame_capture(dxgi::dup_t &dup, ComPtr device) { @@ -188,11 +188,11 @@ test_frame_capture(dxgi::dup_t &dup, ComPtr device) { } // All frames were empty, indicating potential capture issues. - return S_FALSE; + return E_FAIL; } HRESULT -test_dxgi_duplication(dxgi::adapter_t &adapter, dxgi::output_t &output) { +test_dxgi_duplication(dxgi::adapter_t &adapter, dxgi::output_t &output, bool verify_frame_capture) { D3D_FEATURE_LEVEL featureLevels[] { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, @@ -239,13 +239,16 @@ test_dxgi_duplication(dxgi::adapter_t &adapter, dxgi::output_t &output) { return result; } - // If duplication is successful, test frame capture - HRESULT captureResult = test_frame_capture(dup, device_ptr.Get()); - if (FAILED(captureResult)) { - std::cout << "Frame capture test failed [0x"sv << util::hex(captureResult).to_string_view() << "]" << std::endl; - return captureResult; + // To prevent false negatives, we'll make it optional to test for frame capture. + if (verify_frame_capture) { + HRESULT captureResult = test_frame_capture(dup, device_ptr.Get()); + if (FAILED(captureResult)) { + std::cout << "Frame capture test failed [0x"sv << util::hex(captureResult).to_string_view() << "]" << std::endl; + return captureResult; + } } + return S_OK; } @@ -253,20 +256,34 @@ int main(int argc, char *argv[]) { HRESULT status; - // Display name may be omitted - if (argc != 2 && argc != 3) { - std::cout << "ddprobe.exe [GPU preference value] [display name]"sv << std::endl; + // Usage message + if (argc < 2 || argc > 4) { + std::cout << "Usage: ddprobe.exe [GPU preference value] [display name] [--verify-frame-capture]"sv << std::endl; return -1; } std::wstring display_name; - if (argc == 3) { - std::wstring_convert, wchar_t> converter; - display_name = converter.from_bytes(argv[2]); + bool verify_frame_capture = false; + + // Parse GPU preference value (required) + int gpu_preference = atoi(argv[1]); + + // Parse optional arguments + for (int i = 2; i < argc; ++i) { + std::string arg = argv[i]; + + if (arg == "--verify-frame-capture") { + verify_frame_capture = true; + } + else { + // Assume any other argument is the display name + std::wstring_convert, wchar_t> converter; + display_name = converter.from_bytes(arg); + } } // We must set the GPU preference before making any DXGI/D3D calls - status = set_gpu_preference(atoi(argv[1])); + status = set_gpu_preference(gpu_preference); if (status != ERROR_SUCCESS) { return status; } @@ -310,7 +327,7 @@ main(int argc, char *argv[]) { } // We found the matching output. Test it and return the result. - return test_dxgi_duplication(adapter, output); + return test_dxgi_duplication(adapter, output, verify_frame_capture); } }