Phase 1b: video mirror via Windows Graphics Capture
Mirror the injected game's window into the host's borderless window so Steam RPT streams a live copy of the game. No injection needed for video. - capture/window_capture: WGC capture of a target HWND. Wraps our D3D11 device as an IDirect3DDevice, creates a free-threaded frame pool + capture session, hides the cursor and (best-effort) the capture border. Frames arrive on a WGC thread and are handed to the render thread, which copies the newest into a shader-resource texture and draws it -- keeping all D3D11 context use on one thread. Handles window resize via frame-pool Recreate. - capture/frame_renderer: fullscreen-triangle shader that blits the captured texture letterboxed (aspect-preserved) into the window. - capture_panel: "Mirror game window" toggle + lifecycle; target HWND comes from the hook's reported game window. - main: winrt apartment init; mirrored frame drawn as background, ImGui on top. - CMake: link windowsapp + d3dcompiler. Verified: builds clean; host starts (apartment init + shader compile succeed). Visual capture quality/latency to be judged on a real game. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
75
host/src/capture/window_capture.hpp
Normal file
75
host/src/capture/window_capture.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Mirrors a target window into a D3D11 texture using Windows.Graphics.Capture
|
||||
// (no injection). Frames arrive on a WGC thread; the newest is handed to the
|
||||
// render thread, which copies and draws it -- so all D3D11 context use stays on
|
||||
// one thread.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <windows.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <winrt/Windows.Graphics.Capture.h>
|
||||
#include <winrt/Windows.Graphics.DirectX.Direct3D11.h>
|
||||
|
||||
namespace coop
|
||||
{
|
||||
|
||||
class FrameRenderer;
|
||||
|
||||
class WindowCapture
|
||||
{
|
||||
public:
|
||||
~WindowCapture();
|
||||
|
||||
// Begins capturing `target`. Returns false if WGC is unavailable or the
|
||||
// window can't be captured.
|
||||
bool start(HWND target, ID3D11Device* device);
|
||||
void stop();
|
||||
|
||||
[[nodiscard]] bool running() const
|
||||
{
|
||||
return session_ != nullptr;
|
||||
}
|
||||
[[nodiscard]] HWND target() const
|
||||
{
|
||||
return target_;
|
||||
}
|
||||
[[nodiscard]] std::uint32_t frame_width() const
|
||||
{
|
||||
return width_;
|
||||
}
|
||||
[[nodiscard]] std::uint32_t frame_height() const
|
||||
{
|
||||
return height_;
|
||||
}
|
||||
|
||||
// Render thread: consume the newest frame (if any) and draw it letterboxed
|
||||
// into a dst_w x dst_h target via `renderer`.
|
||||
void draw_latest(FrameRenderer& renderer, ID3D11DeviceContext* ctx, std::uint32_t dst_w, std::uint32_t dst_h);
|
||||
|
||||
private:
|
||||
void on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& pool,
|
||||
winrt::Windows::Foundation::IInspectable const&);
|
||||
|
||||
HWND target_ = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID3D11Device> device_;
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> latest_;
|
||||
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> latest_srv_;
|
||||
std::uint32_t width_ = 0;
|
||||
std::uint32_t height_ = 0;
|
||||
|
||||
std::mutex mutex_; // guards pending_ handoff only (no context work under it)
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame pending_{nullptr};
|
||||
|
||||
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice winrt_device_{nullptr};
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item_{nullptr};
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool_{nullptr};
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureSession session_{nullptr};
|
||||
winrt::event_token frame_token_{};
|
||||
winrt::Windows::Graphics::SizeInt32 pool_size_{0, 0};
|
||||
};
|
||||
|
||||
} // namespace coop
|
||||
Reference in New Issue
Block a user