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.
112 lines
3.9 KiB
C++
112 lines
3.9 KiB
C++
// DX11 backend for the mock game. Renders the animated pattern entirely with region
|
|
// clears (ID3D11DeviceContext1::ClearView) so it needs no shaders: an animated full-screen
|
|
// background, a moving vertical bar, and the top-left frame-counter block.
|
|
#include "render_backend.hpp"
|
|
|
|
#include <d3d11_1.h>
|
|
#include <dxgi1_6.h>
|
|
#include <wrl/client.h>
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
namespace coop::mock {
|
|
namespace {
|
|
class Dx11Backend : public RenderBackend {
|
|
public:
|
|
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
|
|
{
|
|
width_ = width;
|
|
height_ = height;
|
|
|
|
const D3D_FEATURE_LEVEL levels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0};
|
|
ComPtr<ID3D11DeviceContext> ctx0;
|
|
if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, levels,
|
|
static_cast<UINT>(std::size(levels)), D3D11_SDK_VERSION, device_.GetAddressOf(),
|
|
nullptr, ctx0.GetAddressOf()))) {
|
|
return false;
|
|
}
|
|
if (FAILED(ctx0.As(&ctx_))) // ClearView needs ID3D11DeviceContext1
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ComPtr<IDXGIDevice> dxgi_device;
|
|
ComPtr<IDXGIAdapter> adapter;
|
|
ComPtr<IDXGIFactory2> factory;
|
|
if (FAILED(device_.As(&dxgi_device)) || FAILED(dxgi_device->GetAdapter(adapter.GetAddressOf()))
|
|
|| FAILED(adapter->GetParent(IID_PPV_ARGS(factory.GetAddressOf())))) {
|
|
return false;
|
|
}
|
|
|
|
// The mock does trivial work, so it must render UNCAPPED (it's a perf fixture, not a real
|
|
// game) -- a flip-model swapchain only tears free of vsync with ALLOW_TEARING, so require it.
|
|
ComPtr<IDXGIFactory5> factory5;
|
|
BOOL tearing = FALSE;
|
|
if (SUCCEEDED(factory.As(&factory5))) {
|
|
factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &tearing, sizeof(tearing));
|
|
}
|
|
tearing_ = tearing != 0;
|
|
|
|
DXGI_SWAP_CHAIN_DESC1 desc = {};
|
|
desc.Width = width;
|
|
desc.Height = height;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // UNORM (not sRGB) so the frame code is exact
|
|
desc.SampleDesc.Count = 1;
|
|
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
desc.BufferCount = 2;
|
|
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
desc.Flags = tearing_ ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u;
|
|
if (FAILED(
|
|
factory->CreateSwapChainForHwnd(device_.Get(), hwnd, &desc, nullptr, nullptr, swap_.GetAddressOf()))) {
|
|
return false;
|
|
}
|
|
factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
|
|
|
|
// D3D11 flip-model: GetBuffer(0) stays the live back buffer, so one RTV is reused.
|
|
ComPtr<ID3D11Texture2D> back;
|
|
if (FAILED(swap_->GetBuffer(0, IID_PPV_ARGS(back.GetAddressOf())))
|
|
|| FAILED(device_->CreateRenderTargetView(back.Get(), nullptr, rtv_.GetAddressOf()))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void render_and_present(std::uint32_t frame) override
|
|
{
|
|
const FramePattern p = frame_pattern(frame, width_);
|
|
|
|
const float bg[4] = {p.bg_r / 255.0f, p.bg_g / 255.0f, p.bg_b / 255.0f, 1.0f};
|
|
ctx_->ClearRenderTargetView(rtv_.Get(), bg);
|
|
|
|
const float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
const LONG bx = static_cast<LONG>(p.bar_x);
|
|
const D3D11_RECT bar = {bx, 0, bx + static_cast<LONG>(kBarWidth), static_cast<LONG>(height_)};
|
|
ctx_->ClearView(rtv_.Get(), white, &bar, 1);
|
|
|
|
const float code[4] = {p.code_r / 255.0f, p.code_g / 255.0f, p.code_b / 255.0f, 1.0f};
|
|
const D3D11_RECT block = {0, 0, static_cast<LONG>(kFrameBlock), static_cast<LONG>(kFrameBlock)};
|
|
ctx_->ClearView(rtv_.Get(), code, &block, 1);
|
|
|
|
swap_->Present(0, tearing_ ? DXGI_PRESENT_ALLOW_TEARING : 0u); // uncapped: the mock must be fast
|
|
}
|
|
|
|
[[nodiscard]] const char* name() const override { return "dx11"; }
|
|
|
|
private:
|
|
std::uint32_t width_ = 0;
|
|
std::uint32_t height_ = 0;
|
|
bool tearing_ = false;
|
|
ComPtr<ID3D11Device> device_;
|
|
ComPtr<ID3D11DeviceContext1> ctx_;
|
|
ComPtr<IDXGISwapChain1> swap_;
|
|
ComPtr<ID3D11RenderTargetView> rtv_;
|
|
};
|
|
} // namespace
|
|
|
|
std::unique_ptr<RenderBackend> create_dx11_backend()
|
|
{
|
|
return std::make_unique<Dx11Backend>();
|
|
}
|
|
|
|
} // namespace coop::mock
|