Validate the Vulkan backend against a real game (Sphere Spectacle)
Adds coop_vk_validate, a harness that drives the Vulkan capture path end-to-end against a shipping title (default Sphere Spectacle, a pure-Vulkan game) and asserts frames reach the shared texture and advance, the captured resolution/colors are sane, saves a BMP screenshot for visual confirmation, and reports the present rate. Findings: - Layer method WORKS: coop_vk_layer mirrors the game correctly at 1920x1080 -- right colors/brightness, no BGRA/RGBA swizzle, no sRGB darkening (screenshot confirmed). The layer captures every present (no drops). - Suspended-inject "Auto-attach" is NOT applicable to a Steam title that must launch through Steam: its exe renders nothing when launched directly, so there's no Vulkan present to catch. The layer is the method for Steam Vulkan games (the early-inject mechanism itself is covered by mock_game_test's suspended-launch path). - The layer does video, but the game needs coop_hook.dll co-injected for focus-spoof or an unfocused, event-driven game throttles itself to a few fps (looks like a capture slowdown but isn't). A present-rate number alone can't prove "no FPS impact" -- it's the game's own cadence; capture stays off the critical path (every present copied, read-back on its own queue + present-semaphore re-chain). Also adds SharedTextureSource::read_frame (bulk RGBA readback) for the screenshot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,43 @@ bool SharedTextureSource::reopen(unsigned long pid, const VideoShareView& share)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedTextureSource::read_frame(std::vector<std::uint8_t>& out, std::uint32_t& w, std::uint32_t& h)
|
||||
{
|
||||
if (private_ == nullptr || ctx_ == nullptr || device_ == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
private_->GetDesc(&desc);
|
||||
D3D11_TEXTURE2D_DESC staging_desc = desc;
|
||||
staging_desc.Usage = D3D11_USAGE_STAGING;
|
||||
staging_desc.BindFlags = 0;
|
||||
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
staging_desc.MiscFlags = 0;
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> staging;
|
||||
if (FAILED(device_->CreateTexture2D(&staging_desc, nullptr, &staging)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ctx_->CopyResource(staging.Get(), private_.Get());
|
||||
D3D11_MAPPED_SUBRESOURCE map{};
|
||||
if (FAILED(ctx_->Map(staging.Get(), 0, D3D11_MAP_READ, 0, &map)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
w = desc.Width;
|
||||
h = desc.Height;
|
||||
out.resize(static_cast<std::size_t>(w) * h * 4);
|
||||
for (std::uint32_t y = 0; y < h; ++y)
|
||||
{
|
||||
memcpy(out.data() + static_cast<std::size_t>(y) * w * 4,
|
||||
static_cast<const std::uint8_t*>(map.pData) + static_cast<std::size_t>(y) * map.RowPitch,
|
||||
static_cast<std::size_t>(w) * 4);
|
||||
}
|
||||
ctx_->Unmap(staging.Get(), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedTextureSource::read_pixel(std::uint32_t x, std::uint32_t y, std::uint8_t out[4])
|
||||
{
|
||||
if (private_ == nullptr || ctx_ == nullptr || device_ == nullptr)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <d3d11_1.h>
|
||||
#include <wrl/client.h>
|
||||
@@ -31,6 +32,11 @@ public:
|
||||
// Drop the opened resources (target changed / mirror turned off).
|
||||
void reset();
|
||||
|
||||
// Read the whole last-copied frame as tightly-packed RGBA8 into `out` (sized w*h*4). For
|
||||
// screenshots / external verification. One staging copy + map; not a hot path. False if no
|
||||
// frame yet or the readback failed.
|
||||
bool read_frame(std::vector<std::uint8_t>& out, std::uint32_t& w, std::uint32_t& h);
|
||||
|
||||
// Read the RGBA8 pixel at (x,y) of the last copied frame into out[4]. For verification
|
||||
// (e.g. decoding the mock game's frame-number block in a capture test). Copies the
|
||||
// private texture to a CPU-readable staging texture and maps it -- not a hot path.
|
||||
|
||||
Reference in New Issue
Block a user