// 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 #include 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_IMMEDIATE; // uncapped: the mock must be fast if (ex_) { ComPtr d3d; if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, d3d.GetAddressOf()))) { return false; } ComPtr 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 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 FramePattern p = frame_pattern(frame, width_); dev_->Clear(0, nullptr, D3DCLEAR_TARGET, opaque(p.bg_r, p.bg_g, p.bg_b), 1.0f, 0); ComPtr back; if (SUCCEEDED(dev_->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, back.GetAddressOf()))) { const LONG bx = static_cast(p.bar_x); RECT bar = {bx, 0, bx + static_cast(kBarWidth), static_cast(height_)}; // moving vertical bar dev_->ColorFill(back.Get(), &bar, opaque(255, 255, 255)); RECT block = {0, 0, static_cast(kFrameBlock), static_cast(kFrameBlock)}; dev_->ColorFill(back.Get(), &block, opaque(p.code_r, p.code_g, p.code_b)); // top-left frame 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 dev_; }; } // namespace std::unique_ptr create_dx9_backend(bool ex) { return std::make_unique(ex); } } // namespace coop::mock