M2(DX9): dual-mode DX9 mock-game render backend
Add render_dx09.cpp (selectable as `dx9ex` / `dx9`): renders the animated pattern with Clear + ColorFill (D3D9's built-in rect fill -- no shaders) and presents through a real D3D9 / D3D9Ex device. Two modes so the two D3D9 capture paths each have a matching game. Smoke-verified: both present frames and exit cleanly. The capture hook + frame-decode test land next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,12 +5,13 @@ add_executable(coop_mock_game
|
|||||||
render_backend.cpp
|
render_backend.cpp
|
||||||
render_dx11.cpp
|
render_dx11.cpp
|
||||||
render_dx12.cpp
|
render_dx12.cpp
|
||||||
render_dx10.cpp)
|
render_dx10.cpp
|
||||||
|
render_dx09.cpp)
|
||||||
|
|
||||||
# Reuses the shared ToneSource (also used by coop_tone + the audio hook self-test).
|
# Reuses the shared ToneSource (also used by coop_tone + the audio hook self-test).
|
||||||
target_include_directories(coop_mock_game PRIVATE ${CMAKE_SOURCE_DIR}/tools/audio_tone)
|
target_include_directories(coop_mock_game PRIVATE ${CMAKE_SOURCE_DIR}/tools/audio_tone)
|
||||||
|
|
||||||
target_link_libraries(coop_mock_game PRIVATE d3d11 d3d12 d3d10 dxgi ole32)
|
target_link_libraries(coop_mock_game PRIVATE d3d11 d3d12 d3d10 d3d9 dxgi ole32)
|
||||||
set_target_properties(coop_mock_game PROPERTIES OUTPUT_NAME "coop_mock_game")
|
set_target_properties(coop_mock_game PROPERTIES OUTPUT_NAME "coop_mock_game")
|
||||||
|
|
||||||
# Test fixture -> stage next to the tests (alongside coop_tone), not in the deployable root.
|
# Test fixture -> stage next to the tests (alongside coop_tone), not in the deployable root.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// CoopMockGame -- a tiny test "game" used to exercise the capture + audio + hook paths.
|
// CoopMockGame -- a tiny test "game" used to exercise the capture + audio + hook paths.
|
||||||
//
|
//
|
||||||
// coop_mock_game.exe [dx10|dx11|dx12] [seconds] [rate] [channels] [bits] [pcm|float]
|
// coop_mock_game.exe [dx9|dx9ex|dx10|dx11|dx12] [seconds] [rate] [channels] [bits] [pcm|float]
|
||||||
//
|
//
|
||||||
// It opens a normal visible window and renders an animated, frame-numbered pattern (see
|
// It opens a normal visible window and renders an animated, frame-numbered pattern (see
|
||||||
// render_backend.hpp): a moving bar + per-frame background colour make motion obvious, and
|
// render_backend.hpp): a moving bar + per-frame background colour make motion obvious, and
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ std::unique_ptr<RenderBackend> RenderBackend::create(const std::string& name)
|
|||||||
{
|
{
|
||||||
return create_dx10_backend();
|
return create_dx10_backend();
|
||||||
}
|
}
|
||||||
|
if (name == "dx9ex")
|
||||||
|
{
|
||||||
|
return create_dx9_backend(/*ex=*/true);
|
||||||
|
}
|
||||||
|
if (name == "dx9")
|
||||||
|
{
|
||||||
|
return create_dx9_backend(/*ex=*/false);
|
||||||
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,5 +61,6 @@ public:
|
|||||||
std::unique_ptr<RenderBackend> create_dx11_backend();
|
std::unique_ptr<RenderBackend> create_dx11_backend();
|
||||||
std::unique_ptr<RenderBackend> create_dx12_backend();
|
std::unique_ptr<RenderBackend> create_dx12_backend();
|
||||||
std::unique_ptr<RenderBackend> create_dx10_backend();
|
std::unique_ptr<RenderBackend> create_dx10_backend();
|
||||||
|
std::unique_ptr<RenderBackend> create_dx9_backend(bool ex); // ex: D3D9Ex vs plain D3D9
|
||||||
|
|
||||||
} // namespace coop::mock
|
} // namespace coop::mock
|
||||||
|
|||||||
118
tools/mock_game/render_dx09.cpp
Normal file
118
tools/mock_game/render_dx09.cpp
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// DX9 backend for the mock game. Renders the animated pattern with Clear + ColorFill (D3D9's
|
||||||
|
// built-in rect fill -- no shaders) and presents through a real D3D9 device, so the D3D9
|
||||||
|
// capture hook sees a genuine present. Two selectable modes: "dx9ex" (Direct3DCreate9Ex) and
|
||||||
|
// plain "dx9" (Direct3DCreate9), so the two D3D9 capture paths each have a matching game. The
|
||||||
|
// back buffer is the standard A8R8G8B8 (BGRA byte order); the capture side swizzles to RGBA.
|
||||||
|
#include "render_backend.hpp"
|
||||||
|
|
||||||
|
#include <d3d9.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
namespace coop::mock
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
D3DCOLOR opaque(std::uint8_t r, std::uint8_t g, std::uint8_t b)
|
||||||
|
{
|
||||||
|
return D3DCOLOR_ARGB(255, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dx9Backend : public RenderBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Dx9Backend(bool ex) : ex_(ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
|
||||||
|
{
|
||||||
|
width_ = width;
|
||||||
|
height_ = height;
|
||||||
|
|
||||||
|
D3DPRESENT_PARAMETERS pp = {};
|
||||||
|
pp.BackBufferWidth = width;
|
||||||
|
pp.BackBufferHeight = height;
|
||||||
|
pp.BackBufferFormat = D3DFMT_X8R8G8B8;
|
||||||
|
pp.BackBufferCount = 1;
|
||||||
|
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||||
|
pp.hDeviceWindow = hwnd;
|
||||||
|
pp.Windowed = TRUE;
|
||||||
|
pp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // vsync -> a game-like cadence
|
||||||
|
|
||||||
|
if (ex_)
|
||||||
|
{
|
||||||
|
ComPtr<IDirect3D9Ex> d3d;
|
||||||
|
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, d3d.GetAddressOf())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ComPtr<IDirect3DDevice9Ex> dev;
|
||||||
|
if (FAILED(d3d->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
|
||||||
|
D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr,
|
||||||
|
dev.GetAddressOf())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dev_ = dev; // IDirect3DDevice9Ex derives from IDirect3DDevice9
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ComPtr<IDirect3D9> d3d(Direct3DCreate9(D3D_SDK_VERSION));
|
||||||
|
if (!d3d)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
|
||||||
|
D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, dev_.GetAddressOf())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_and_present(std::uint32_t frame) override
|
||||||
|
{
|
||||||
|
const D3DCOLOR bg = opaque(static_cast<std::uint8_t>((frame * 2) % 256),
|
||||||
|
static_cast<std::uint8_t>((frame * 3) % 256),
|
||||||
|
static_cast<std::uint8_t>((frame * 5) % 256));
|
||||||
|
dev_->Clear(0, nullptr, D3DCLEAR_TARGET, bg, 1.0f, 0);
|
||||||
|
|
||||||
|
ComPtr<IDirect3DSurface9> back;
|
||||||
|
if (SUCCEEDED(dev_->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, back.GetAddressOf())))
|
||||||
|
{
|
||||||
|
const LONG span = width_ > 24 ? static_cast<LONG>(width_ - 24) : 1;
|
||||||
|
const LONG bx = static_cast<LONG>((frame * 4) % static_cast<std::uint32_t>(span));
|
||||||
|
RECT bar = {bx, 0, bx + 24, static_cast<LONG>(height_)}; // moving vertical bar
|
||||||
|
dev_->ColorFill(back.Get(), &bar, opaque(255, 255, 255));
|
||||||
|
|
||||||
|
std::uint8_t r = 0, g = 0, b = 0;
|
||||||
|
frame_to_rgb(frame, r, g, b);
|
||||||
|
RECT block = {0, 0, static_cast<LONG>(kFrameBlock), static_cast<LONG>(kFrameBlock)};
|
||||||
|
dev_->ColorFill(back.Get(), &block, opaque(r, g, b)); // top-left frame-counter block
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_->Present(nullptr, nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const char* name() const override
|
||||||
|
{
|
||||||
|
return ex_ ? "dx9ex" : "dx9";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ex_;
|
||||||
|
std::uint32_t width_ = 0;
|
||||||
|
std::uint32_t height_ = 0;
|
||||||
|
ComPtr<IDirect3DDevice9> dev_;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<RenderBackend> create_dx9_backend(bool ex)
|
||||||
|
{
|
||||||
|
return std::make_unique<Dx9Backend>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace coop::mock
|
||||||
Reference in New Issue
Block a user