M2: DX10 capture via D3D10 read-back; mock_game_test covers DX10

A pure-D3D10 game's backbuffer QIs to ID3D11Texture2D but that view reads back
empty (content lives on the game's D3D10 device), and a D3D11 backbuffer also
QIs to ID3D10Texture2D -- so GetBuffer can't discriminate. The reliable signal
is that a feature-level-10 device rejects CreateTexture2D with the NT-handle
keyed-mutex share flags (E_INVALIDARG). The present hook now tries the D3D11
fast path and, on that failure, switches (sticky) to reading the backbuffer
through the game's own D3D10 device into a staging texture and uploading it into
the shared texture on a hook-owned D3D11 device (Map blocks until the GPU copy
completes -> no cross-device race). The host reader is unchanged.

mock_game_test now decodes DX10 frames through the hook (monotonic/advancing)
alongside DX11/DX12; 15/15 ctest. Roadmap: DX10 milestone done and removed
(remaining renumbered); architecture + lessons updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 11:43:25 +02:00
parent a8de75b2f6
commit 68b9aabc8c
3 changed files with 254 additions and 58 deletions

View File

@@ -223,7 +223,12 @@ VideoShareView read_video_share(const SharedBlock* block)
void test_video_capture(const char* backend, ID3D11Device* device)
{
std::printf("== video capture: %s ==\n", backend);
const std::wstring args = (std::string(backend) == "dx12" ? L"dx12 " : L"dx11 ") + std::wstring(L"30");
std::wstring args;
for (const char* p = backend; *p != '\0'; ++p) // backend names are ASCII (dx10/dx11/dx12/...)
{
args.push_back(static_cast<wchar_t>(*p));
}
args += L" 30";
MockGame game = MockGame::launch(args);
if (!game.ok)
{
@@ -517,6 +522,7 @@ int main()
return 0;
}
test_video_capture("dx10", device);
test_video_capture("dx11", device);
test_video_capture("dx12", device);