Run clang-format (the repo's .clang-format: LLVM base, 120 cols, tabs, Allman functions) over every source file so the tree is formatter-clean. Whitespace only -- no behavior change; full x64 + x86 suites pass. Also set SortIncludes: false in .clang-format. Windows include order is load-bearing (windows.h must precede tlhelp32.h / mmreg.h / xinput.h / dinput.h; winsock2.h must precede windows.h), and the default alphabetical sort reorders tlhelp32.h ahead of windows.h -- a build break. Leaving order alone keeps the manual, correct grouping.
113 lines
3.8 KiB
C++
113 lines
3.8 KiB
C++
// DX10 backend for the mock game. DX10 has no rect-clear (ClearView), so it composes the
|
|
// animated pattern (background + moving bar + top-left frame-counter block) into a CPU buffer
|
|
// each frame, uploads it into a scratch texture, and CopyResource's that into the swap-chain
|
|
// back buffer -- no shaders. It presents through a real D3D10 DXGI swap chain, so the capture
|
|
// hook sees a genuine D3D10 present.
|
|
#include "render_backend.hpp"
|
|
|
|
#include <vector>
|
|
|
|
#include <d3d10.h>
|
|
#include <dxgi.h>
|
|
#include <wrl/client.h>
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
namespace coop::mock {
|
|
namespace {
|
|
std::uint32_t pack(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a = 255)
|
|
{
|
|
// R8G8B8A8_UNORM byte order: R in the low byte.
|
|
return static_cast<std::uint32_t>(r) | (static_cast<std::uint32_t>(g) << 8) | (static_cast<std::uint32_t>(b) << 16)
|
|
| (static_cast<std::uint32_t>(a) << 24);
|
|
}
|
|
|
|
class Dx10Backend : public RenderBackend {
|
|
public:
|
|
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
|
|
{
|
|
width_ = width;
|
|
height_ = height;
|
|
px_.resize(static_cast<std::size_t>(width) * height);
|
|
|
|
DXGI_SWAP_CHAIN_DESC desc = {};
|
|
desc.BufferDesc.Width = width;
|
|
desc.BufferDesc.Height = height;
|
|
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // UNORM (not sRGB) so the frame code is exact
|
|
desc.BufferDesc.RefreshRate.Numerator = 60;
|
|
desc.BufferDesc.RefreshRate.Denominator = 1;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
desc.BufferCount = 2;
|
|
desc.OutputWindow = hwnd;
|
|
desc.Windowed = TRUE;
|
|
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // blt model: GetBuffer(0) is the back buffer
|
|
|
|
if (FAILED(D3D10CreateDeviceAndSwapChain(nullptr, D3D10_DRIVER_TYPE_HARDWARE, nullptr, 0, D3D10_SDK_VERSION,
|
|
&desc, swap_.GetAddressOf(), device_.GetAddressOf()))) {
|
|
return false;
|
|
}
|
|
|
|
// Scratch texture matching the back buffer; we upload the CPU frame into it then
|
|
// CopyResource it into the back buffer (DX10 can't fill a sub-rect of an RTV directly).
|
|
D3D10_TEXTURE2D_DESC td = {};
|
|
td.Width = width;
|
|
td.Height = height;
|
|
td.MipLevels = 1;
|
|
td.ArraySize = 1;
|
|
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
td.SampleDesc.Count = 1;
|
|
td.Usage = D3D10_USAGE_DEFAULT;
|
|
td.BindFlags = D3D10_BIND_SHADER_RESOURCE;
|
|
return SUCCEEDED(device_->CreateTexture2D(&td, nullptr, scratch_.GetAddressOf()));
|
|
}
|
|
|
|
void render_and_present(std::uint32_t frame) override
|
|
{
|
|
const FramePattern p = frame_pattern(frame, width_);
|
|
const std::uint32_t bg = pack(p.bg_r, p.bg_g, p.bg_b);
|
|
const std::uint32_t whitepx = pack(255, 255, 255);
|
|
const std::uint32_t code = pack(p.code_r, p.code_g, p.code_b);
|
|
|
|
const std::uint32_t bx = p.bar_x; // moving vertical bar
|
|
for (std::uint32_t y = 0; y < height_; ++y) {
|
|
std::uint32_t* row = px_.data() + static_cast<std::size_t>(y) * width_;
|
|
for (std::uint32_t x = 0; x < width_; ++x) {
|
|
std::uint32_t c = bg;
|
|
if (x >= bx && x < bx + kBarWidth) {
|
|
c = whitepx;
|
|
}
|
|
if (x < kFrameBlock && y < kFrameBlock) {
|
|
c = code; // top-left frame-counter block
|
|
}
|
|
row[x] = c;
|
|
}
|
|
}
|
|
|
|
device_->UpdateSubresource(scratch_.Get(), 0, nullptr, px_.data(), static_cast<UINT>(width_ * 4), 0);
|
|
ComPtr<ID3D10Texture2D> back;
|
|
if (SUCCEEDED(swap_->GetBuffer(0, IID_PPV_ARGS(back.GetAddressOf())))) {
|
|
device_->CopyResource(back.Get(), scratch_.Get());
|
|
}
|
|
swap_->Present(0, 0); // uncapped (BLT model): the mock does nothing -> it must be fast
|
|
}
|
|
|
|
[[nodiscard]] const char* name() const override { return "dx10"; }
|
|
|
|
private:
|
|
std::uint32_t width_ = 0;
|
|
std::uint32_t height_ = 0;
|
|
std::vector<std::uint32_t> px_;
|
|
ComPtr<ID3D10Device> device_;
|
|
ComPtr<IDXGISwapChain> swap_;
|
|
ComPtr<ID3D10Texture2D> scratch_;
|
|
};
|
|
} // namespace
|
|
|
|
std::unique_ptr<RenderBackend> create_dx10_backend()
|
|
{
|
|
return std::make_unique<Dx10Backend>();
|
|
}
|
|
|
|
} // namespace coop::mock
|