Apply clang-format across the whole tree

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.
This commit is contained in:
2026-07-12 11:52:53 +02:00
parent c684a15fb9
commit 30eccf749d
155 changed files with 3333 additions and 6171 deletions

View File

@@ -21,8 +21,7 @@
#include "render_backend.hpp"
#include "tone_source.hpp"
namespace
{
namespace {
std::atomic<bool> g_running{true};
// Diagnostic: on an access violation, log the faulting address and the caller (return address on the
@@ -30,17 +29,15 @@ std::atomic<bool> g_running{true};
// crash so the test still detects it.
LONG WINAPI crash_logger(EXCEPTION_POINTERS* ep)
{
if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
{
if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
return EXCEPTION_CONTINUE_SEARCH;
}
auto mod = [](void* p, char* out, size_t n) -> unsigned long long {
HMODULE m = nullptr;
if (p != nullptr &&
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCSTR>(p), &m) &&
m != nullptr)
{
if (p != nullptr
&& GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCSTR>(p), &m)
&& m != nullptr) {
char path[MAX_PATH] = {};
GetModuleFileNameA(m, path, MAX_PATH);
const char* base = std::strrchr(path, '\\');
@@ -62,8 +59,7 @@ LONG WINAPI crash_logger(EXCEPTION_POINTERS* ep)
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (msg == WM_DESTROY)
{
if (msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
@@ -73,23 +69,18 @@ LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
// Spawns the audio render loop on its own thread (WASAPI wants its own COM apartment).
void audio_thread(coop::tone::ToneFormat want)
{
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
return;
}
coop::tone::ToneSource tone;
if (tone.open(want, 440.0))
{
if (tone.open(want, 440.0)) {
std::printf("MOCK_GAME audio: %u Hz %u ch %u-bit %s\n", tone.format().rate, tone.format().channels,
tone.format().bits, tone.format().is_float ? "float" : "pcm");
std::fflush(stdout);
while (g_running.load(std::memory_order_relaxed))
{
while (g_running.load(std::memory_order_relaxed)) {
tone.render_step(200);
}
}
else
{
} else {
std::printf("MOCK_GAME audio: failed to open requested format\n");
}
tone.close();
@@ -103,11 +94,11 @@ void audio_thread(coop::tone::ToneFormat want)
void poll_input()
{
XINPUT_STATE xs{};
(void)XInputGetState(0, &xs); // XInput hook (XInputGetState/Ex)
(void)GetAsyncKeyState(VK_SPACE); // MKB hook (GetAsyncKeyState)
(void)XInputGetState(0, &xs); // XInput hook (XInputGetState/Ex)
(void)GetAsyncKeyState(VK_SPACE); // MKB hook (GetAsyncKeyState)
BYTE kb[256] = {};
(void)GetKeyboardState(kb); // MKB hook (GetKeyboardState)
(void)GetForegroundWindow(); // focus hook (GetForegroundWindow)
(void)GetKeyboardState(kb); // MKB hook (GetKeyboardState)
(void)GetForegroundWindow(); // focus hook (GetForegroundWindow)
}
} // namespace
@@ -118,8 +109,7 @@ int main(int argc, char** argv)
const double seconds = argc > 2 ? std::strtod(argv[2], nullptr) : 0.0;
const bool want_audio = argc > 3;
coop::tone::ToneFormat audio_fmt;
if (want_audio)
{
if (want_audio) {
audio_fmt.rate = static_cast<unsigned>(std::strtoul(argv[3], nullptr, 10));
audio_fmt.channels = argc > 4 ? static_cast<unsigned>(std::strtoul(argv[4], nullptr, 10)) : 2;
audio_fmt.bits = argc > 5 ? static_cast<unsigned>(std::strtoul(argv[5], nullptr, 10)) : 32;
@@ -139,11 +129,9 @@ int main(int argc, char** argv)
RECT r = {0, 0, static_cast<LONG>(kW), static_cast<LONG>(kH)};
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowExW(0, wc.lpszClassName, L"CoopMockGame", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, nullptr,
nullptr, inst, nullptr);
if (hwnd == nullptr)
{
HWND hwnd = CreateWindowExW(0, wc.lpszClassName, L"CoopMockGame", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, nullptr, nullptr, inst, nullptr);
if (hwnd == nullptr) {
std::printf("MOCK_GAME error: CreateWindow failed\n");
return 1;
}
@@ -151,26 +139,23 @@ int main(int argc, char** argv)
// Test hook (early-load): Vulkan caches its present pointer at init, so the capture hook must
// be in place before vkCreateInstance. Under COOP_MOCK_VK_EARLY the mock loads vulkan-1.dll
// now and waits, giving an already-injected hook time to hook vkGetInstanceProcAddr first.
if (backend_name == "vk" && GetEnvironmentVariableW(L"COOP_MOCK_VK_EARLY", nullptr, 0) != 0)
{
if (backend_name == "vk" && GetEnvironmentVariableW(L"COOP_MOCK_VK_EARLY", nullptr, 0) != 0) {
LoadLibraryW(L"vulkan-1.dll");
Sleep(1500);
}
auto backend = coop::mock::RenderBackend::create(backend_name);
if (!backend || !backend->init(hwnd, kW, kH))
{
if (!backend || !backend->init(hwnd, kW, kH)) {
std::printf("MOCK_GAME error: backend '%s' unavailable\n", backend_name.c_str());
return 2;
}
std::printf("MOCK_GAME pid=%lu backend=%s w=%u h=%u audio=%s\n", GetCurrentProcessId(), backend->name(),
kW, kH, want_audio ? "yes" : "no");
std::printf("MOCK_GAME pid=%lu backend=%s w=%u h=%u audio=%s\n", GetCurrentProcessId(), backend->name(), kW, kH,
want_audio ? "yes" : "no");
std::fflush(stdout);
std::thread audio;
if (want_audio)
{
if (want_audio) {
audio = std::thread(audio_thread, audio_fmt);
}
@@ -178,20 +163,16 @@ int main(int argc, char** argv)
std::uint32_t frame = 0;
ULONGLONG fps_window_start = start; // window-title fps: frames in the last ~second
std::uint32_t fps_window_frames = 0;
for (;;)
{
for (;;) {
MSG msg;
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
g_running.store(false, std::memory_order_relaxed);
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (!g_running.load(std::memory_order_relaxed))
{
if (!g_running.load(std::memory_order_relaxed)) {
break;
}
poll_input(); // drive the input/focus/MKB detours each frame, like a real game
@@ -200,8 +181,7 @@ int main(int argc, char** argv)
// Show the backend + a once-per-second-smoothed fps in the title bar.
++fps_window_frames;
const ULONGLONG now = GetTickCount64();
if (now - fps_window_start >= 1000)
{
if (now - fps_window_start >= 1000) {
const double fps = fps_window_frames * 1000.0 / static_cast<double>(now - fps_window_start);
wchar_t title[128];
swprintf(title, 128, L"CoopMockGame [%hs] - %.0f fps", backend->name(), fps);
@@ -209,15 +189,13 @@ int main(int argc, char** argv)
fps_window_start = now;
fps_window_frames = 0;
}
if (seconds > 0.0 && (GetTickCount64() - start) >= static_cast<ULONGLONG>(seconds * 1000.0))
{
if (seconds > 0.0 && (GetTickCount64() - start) >= static_cast<ULONGLONG>(seconds * 1000.0)) {
break;
}
}
g_running.store(false, std::memory_order_relaxed);
if (audio.joinable())
{
if (audio.joinable()) {
audio.join();
}
std::printf("MOCK_GAME done: %u frames\n", frame);

View File

@@ -1,36 +1,28 @@
#include "render_backend.hpp"
namespace coop::mock
{
namespace coop::mock {
std::unique_ptr<RenderBackend> RenderBackend::create(const std::string& name)
{
if (name == "dx11")
{
if (name == "dx11") {
return create_dx11_backend();
}
if (name == "dx12")
{
if (name == "dx12") {
return create_dx12_backend();
}
if (name == "dx10")
{
if (name == "dx10") {
return create_dx10_backend();
}
if (name == "dx9ex")
{
if (name == "dx9ex") {
return create_dx9_backend(/*ex=*/true);
}
if (name == "dx9")
{
if (name == "dx9") {
return create_dx9_backend(/*ex=*/false);
}
if (name == "gl" || name == "opengl")
{
if (name == "gl" || name == "opengl") {
return create_gl_backend();
}
if (name == "vk" || name == "vulkan")
{
if (name == "vk" || name == "vulkan") {
return create_vk_backend();
}
return nullptr;

View File

@@ -15,8 +15,7 @@
#include <windows.h>
namespace coop::mock
{
namespace coop::mock {
// Encode a frame counter into an RGB triple (and back). R/G/B are the low 24 bits, so it
// is unambiguous for ~16M frames. The swap chain is UNORM (not sRGB), so the bytes survive
@@ -30,8 +29,7 @@ inline void frame_to_rgb(std::uint32_t frame, std::uint8_t& r, std::uint8_t& g,
inline std::uint32_t rgb_to_frame(std::uint8_t r, std::uint8_t g, std::uint8_t b)
{
return static_cast<std::uint32_t>(r) | (static_cast<std::uint32_t>(g) << 8) |
(static_cast<std::uint32_t>(b) << 16);
return static_cast<std::uint32_t>(r) | (static_cast<std::uint32_t>(g) << 8) | (static_cast<std::uint32_t>(b) << 16);
}
// Size (px) of the top-left frame-counter block the test samples.
@@ -44,8 +42,7 @@ inline constexpr std::uint32_t kBarWidth = 24;
// IDENTICAL image (the capture test compares backends). Each backend consumes these values with its
// own API's clear/fill calls: an animated full-screen background, a kBarWidth-wide full-height white
// bar at bar_x, and the top-left kFrameBlock-square frame-counter block whose colour encodes `frame`.
struct FramePattern
{
struct FramePattern {
std::uint8_t bg_r, bg_g, bg_b; // background; each channel sweeps at a different rate -> motion
std::uint32_t bar_x; // left edge of the moving bar
std::uint8_t code_r, code_g, code_b; // frame-counter block colour (frame_to_rgb)
@@ -63,9 +60,8 @@ inline FramePattern frame_pattern(std::uint32_t frame, std::uint32_t width)
return p;
}
class RenderBackend
{
public:
class RenderBackend {
public:
virtual ~RenderBackend() = default;
// Bring up the device + swap chain on `hwnd` at the given client size. False on failure.

View File

@@ -10,21 +10,16 @@
using Microsoft::WRL::ComPtr;
namespace coop::mock
{
namespace
{
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)
{
}
class Dx9Backend : public RenderBackend {
public:
explicit Dx9Backend(bool ex) : ex_(ex) {}
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
{
@@ -41,32 +36,24 @@ public:
pp.Windowed = TRUE;
pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // uncapped: the mock must be fast
if (ex_)
{
if (ex_) {
ComPtr<IDirect3D9Ex> d3d;
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, d3d.GetAddressOf())))
{
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())))
{
D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr, dev.GetAddressOf()))) {
return false;
}
dev_ = dev; // IDirect3DDevice9Ex derives from IDirect3DDevice9
}
else
{
} else {
ComPtr<IDirect3D9> d3d(Direct3DCreate9(D3D_SDK_VERSION));
if (!d3d)
{
if (!d3d) {
return false;
}
if (FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, dev_.GetAddressOf())))
{
if (FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING,
&pp, dev_.GetAddressOf()))) {
return false;
}
}
@@ -79,8 +66,7 @@ public:
dev_->Clear(0, nullptr, D3DCLEAR_TARGET, opaque(p.bg_r, p.bg_g, p.bg_b), 1.0f, 0);
ComPtr<IDirect3DSurface9> back;
if (SUCCEEDED(dev_->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, back.GetAddressOf())))
{
if (SUCCEEDED(dev_->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, back.GetAddressOf()))) {
const LONG bx = static_cast<LONG>(p.bar_x);
RECT bar = {bx, 0, bx + static_cast<LONG>(kBarWidth), static_cast<LONG>(height_)}; // moving vertical bar
dev_->ColorFill(back.Get(), &bar, opaque(255, 255, 255));
@@ -92,12 +78,9 @@ public:
dev_->Present(nullptr, nullptr, nullptr, nullptr);
}
[[nodiscard]] const char* name() const override
{
return ex_ ? "dx9ex" : "dx9";
}
[[nodiscard]] const char* name() const override { return ex_ ? "dx9ex" : "dx9"; }
private:
private:
bool ex_;
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;

View File

@@ -13,20 +13,17 @@
using Microsoft::WRL::ComPtr;
namespace coop::mock
{
namespace
{
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);
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:
class Dx10Backend : public RenderBackend {
public:
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
{
width_ = width;
@@ -46,10 +43,8 @@ public:
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())))
{
if (FAILED(D3D10CreateDeviceAndSwapChain(nullptr, D3D10_DRIVER_TYPE_HARDWARE, nullptr, 0, D3D10_SDK_VERSION,
&desc, swap_.GetAddressOf(), device_.GetAddressOf()))) {
return false;
}
@@ -75,18 +70,14 @@ public:
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)
{
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)
{
for (std::uint32_t x = 0; x < width_; ++x) {
std::uint32_t c = bg;
if (x >= bx && x < bx + kBarWidth)
{
if (x >= bx && x < bx + kBarWidth) {
c = whitepx;
}
if (x < kFrameBlock && y < kFrameBlock)
{
if (x < kFrameBlock && y < kFrameBlock) {
c = code; // top-left frame-counter block
}
row[x] = c;
@@ -95,19 +86,15 @@ public:
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()))))
{
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";
}
[[nodiscard]] const char* name() const override { return "dx10"; }
private:
private:
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;
std::vector<std::uint32_t> px_;

View File

@@ -9,13 +9,10 @@
using Microsoft::WRL::ComPtr;
namespace coop::mock
{
namespace
{
class Dx11Backend : public RenderBackend
{
public:
namespace coop::mock {
namespace {
class Dx11Backend : public RenderBackend {
public:
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
{
width_ = width;
@@ -24,9 +21,8 @@ public:
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())))
{
static_cast<UINT>(std::size(levels)), D3D11_SDK_VERSION, device_.GetAddressOf(),
nullptr, ctx0.GetAddressOf()))) {
return false;
}
if (FAILED(ctx0.As(&ctx_))) // ClearView needs ID3D11DeviceContext1
@@ -37,9 +33,8 @@ public:
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()))))
{
if (FAILED(device_.As(&dxgi_device)) || FAILED(dxgi_device->GetAdapter(adapter.GetAddressOf()))
|| FAILED(adapter->GetParent(IID_PPV_ARGS(factory.GetAddressOf())))) {
return false;
}
@@ -47,8 +42,7 @@ public:
// 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)))
{
if (SUCCEEDED(factory.As(&factory5))) {
factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &tearing, sizeof(tearing));
}
tearing_ = tearing != 0;
@@ -62,18 +56,16 @@ public:
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())))
{
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())))
{
if (FAILED(swap_->GetBuffer(0, IID_PPV_ARGS(back.GetAddressOf())))
|| FAILED(device_->CreateRenderTargetView(back.Get(), nullptr, rtv_.GetAddressOf()))) {
return false;
}
return true;
@@ -98,12 +90,9 @@ public:
swap_->Present(0, tearing_ ? DXGI_PRESENT_ALLOW_TEARING : 0u); // uncapped: the mock must be fast
}
[[nodiscard]] const char* name() const override
{
return "dx11";
}
[[nodiscard]] const char* name() const override { return "dx11"; }
private:
private:
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;
bool tearing_ = false;

View File

@@ -9,43 +9,36 @@
using Microsoft::WRL::ComPtr;
namespace coop::mock
{
namespace
{
namespace coop::mock {
namespace {
constexpr UINT kBackBuffers = 3; // DX12 rotates these explicitly -- the case the capture must get right
class Dx12Backend : public RenderBackend
{
public:
class Dx12Backend : public RenderBackend {
public:
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
{
width_ = width;
height_ = height;
if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(device_.GetAddressOf()))))
{
if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(device_.GetAddressOf())))) {
return false;
}
D3D12_COMMAND_QUEUE_DESC qd = {};
qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
if (FAILED(device_->CreateCommandQueue(&qd, IID_PPV_ARGS(queue_.GetAddressOf()))))
{
if (FAILED(device_->CreateCommandQueue(&qd, IID_PPV_ARGS(queue_.GetAddressOf())))) {
return false;
}
ComPtr<IDXGIFactory4> factory;
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf()))))
{
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())))) {
return false;
}
// Uncapped (the mock is a perf fixture): a flip-model swapchain needs ALLOW_TEARING to run
// free of vsync.
ComPtr<IDXGIFactory5> factory5;
BOOL tearing = FALSE;
if (SUCCEEDED(factory.As(&factory5)))
{
if (SUCCEEDED(factory.As(&factory5))) {
factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &tearing, sizeof(tearing));
}
tearing_ = tearing != 0;
@@ -60,10 +53,8 @@ public:
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.Flags = tearing_ ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u;
ComPtr<IDXGISwapChain1> sc1;
if (FAILED(factory->CreateSwapChainForHwnd(queue_.Get(), hwnd, &desc, nullptr, nullptr,
sc1.GetAddressOf())) ||
FAILED(sc1.As(&swap_)))
{
if (FAILED(factory->CreateSwapChainForHwnd(queue_.Get(), hwnd, &desc, nullptr, nullptr, sc1.GetAddressOf()))
|| FAILED(sc1.As(&swap_))) {
return false;
}
factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
@@ -71,36 +62,30 @@ public:
D3D12_DESCRIPTOR_HEAP_DESC hd = {};
hd.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
hd.NumDescriptors = kBackBuffers;
if (FAILED(device_->CreateDescriptorHeap(&hd, IID_PPV_ARGS(rtv_heap_.GetAddressOf()))))
{
if (FAILED(device_->CreateDescriptorHeap(&hd, IID_PPV_ARGS(rtv_heap_.GetAddressOf())))) {
return false;
}
rtv_stride_ = device_->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE h = rtv_heap_->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < kBackBuffers; ++i)
{
if (FAILED(swap_->GetBuffer(i, IID_PPV_ARGS(targets_[i].GetAddressOf()))))
{
for (UINT i = 0; i < kBackBuffers; ++i) {
if (FAILED(swap_->GetBuffer(i, IID_PPV_ARGS(targets_[i].GetAddressOf())))) {
return false;
}
device_->CreateRenderTargetView(targets_[i].Get(), nullptr, h);
rtv_handles_[i] = h;
h.ptr += rtv_stride_;
if (FAILED(device_->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(allocs_[i].GetAddressOf()))))
{
IID_PPV_ARGS(allocs_[i].GetAddressOf())))) {
return false;
}
}
if (FAILED(device_->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocs_[0].Get(), nullptr,
IID_PPV_ARGS(list_.GetAddressOf()))))
{
IID_PPV_ARGS(list_.GetAddressOf())))) {
return false;
}
list_->Close();
if (FAILED(device_->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence_.GetAddressOf()))))
{
if (FAILED(device_->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence_.GetAddressOf())))) {
return false;
}
fence_event_ = CreateEventW(nullptr, FALSE, FALSE, nullptr);
@@ -138,24 +123,19 @@ public:
wait_for_gpu(); // simple per-frame sync (mock game: correctness over throughput)
}
[[nodiscard]] const char* name() const override
{
return "dx12";
}
[[nodiscard]] const char* name() const override { return "dx12"; }
~Dx12Backend() override
{
if (fence_ != nullptr)
{
if (fence_ != nullptr) {
wait_for_gpu();
}
if (fence_event_ != nullptr)
{
if (fence_event_ != nullptr) {
CloseHandle(fence_event_);
}
}
private:
private:
void transition(ID3D12Resource* res, D3D12_RESOURCE_STATES from, D3D12_RESOURCE_STATES to)
{
D3D12_RESOURCE_BARRIER b = {};
@@ -171,8 +151,7 @@ private:
{
const UINT64 v = ++fence_value_;
queue_->Signal(fence_.Get(), v);
if (fence_->GetCompletedValue() < v)
{
if (fence_->GetCompletedValue() < v) {
fence_->SetEventOnCompletion(v, fence_event_);
WaitForSingleObject(fence_event_, INFINITE);
}

View File

@@ -12,21 +12,17 @@
#include <GL/gl.h>
namespace coop::mock
{
namespace
{
class GlBackend : public RenderBackend
{
public:
namespace coop::mock {
namespace {
class GlBackend : public RenderBackend {
public:
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
{
width_ = width;
height_ = height;
hwnd_ = hwnd;
hdc_ = GetDC(hwnd);
if (hdc_ == nullptr)
{
if (hdc_ == nullptr) {
return false;
}
PIXELFORMATDESCRIPTOR pfd = {};
@@ -38,20 +34,17 @@ public:
pfd.cAlphaBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
const int pf = ChoosePixelFormat(hdc_, &pfd);
if (pf == 0 || !SetPixelFormat(hdc_, pf, &pfd))
{
if (pf == 0 || !SetPixelFormat(hdc_, pf, &pfd)) {
return false;
}
hglrc_ = wglCreateContext(hdc_); // legacy context is enough for GL 1.1 clears
if (hglrc_ == nullptr || !wglMakeCurrent(hdc_, hglrc_))
{
if (hglrc_ == nullptr || !wglMakeCurrent(hdc_, hglrc_)) {
return false;
}
// Uncapped: the mock is a perf fixture and must run as fast as it can (disable vsync), so a
// capture-induced slowdown is visible. Runtime extension lookup -- no loader/submodule needed.
using PFN_wglSwapIntervalEXT = BOOL(WINAPI*)(int);
if (auto swap_interval = reinterpret_cast<PFN_wglSwapIntervalEXT>(wglGetProcAddress("wglSwapIntervalEXT")))
{
if (auto swap_interval = reinterpret_cast<PFN_wglSwapIntervalEXT>(wglGetProcAddress("wglSwapIntervalEXT"))) {
swap_interval(0);
}
return true;
@@ -87,25 +80,20 @@ public:
SwapBuffers(hdc_); // the capture hook intercepts this
}
[[nodiscard]] const char* name() const override
{
return "gl";
}
[[nodiscard]] const char* name() const override { return "gl"; }
~GlBackend() override
{
wglMakeCurrent(nullptr, nullptr);
if (hglrc_ != nullptr)
{
if (hglrc_ != nullptr) {
wglDeleteContext(hglrc_);
}
if (hdc_ != nullptr && hwnd_ != nullptr)
{
if (hdc_ != nullptr && hwnd_ != nullptr) {
ReleaseDC(hwnd_, hdc_);
}
}
private:
private:
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;
HWND hwnd_ = nullptr;

View File

@@ -10,19 +10,15 @@
#include <volk.h>
namespace coop::mock
{
namespace
{
class VkBackend : public RenderBackend
{
public:
namespace coop::mock {
namespace {
class VkBackend : public RenderBackend {
public:
bool init(HWND hwnd, std::uint32_t width, std::uint32_t height) override
{
width_ = width;
height_ = height;
if (volkInitialize() != VK_SUCCESS)
{
if (volkInitialize() != VK_SUCCESS) {
return false; // no Vulkan loader on this machine
}
@@ -34,8 +30,7 @@ public:
ici.pApplicationInfo = &app;
ici.enabledExtensionCount = 2;
ici.ppEnabledExtensionNames = inst_ext;
if (vkCreateInstance(&ici, nullptr, &instance_) != VK_SUCCESS)
{
if (vkCreateInstance(&ici, nullptr, &instance_) != VK_SUCCESS) {
return false;
}
volkLoadInstance(instance_);
@@ -43,13 +38,11 @@ public:
VkWin32SurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR};
sci.hinstance = GetModuleHandleW(nullptr);
sci.hwnd = hwnd;
if (vkCreateWin32SurfaceKHR(instance_, &sci, nullptr, &surface_) != VK_SUCCESS)
{
if (vkCreateWin32SurfaceKHR(instance_, &sci, nullptr, &surface_) != VK_SUCCESS) {
return false;
}
if (!pick_device() || !create_device() || !create_swapchain() || !create_commands())
{
if (!pick_device() || !create_device() || !create_swapchain() || !create_commands()) {
return false;
}
return true;
@@ -57,20 +50,17 @@ public:
void render_and_present(std::uint32_t frame) override
{
if (device_ == VK_NULL_HANDLE || swapchain_ == VK_NULL_HANDLE)
{
if (device_ == VK_NULL_HANDLE || swapchain_ == VK_NULL_HANDLE) {
return;
}
vkWaitForFences(device_, 1, &in_flight_, VK_TRUE, UINT64_MAX);
std::uint32_t idx = 0;
VkResult acq = vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX, acquire_sem_, VK_NULL_HANDLE, &idx);
if (acq == VK_ERROR_OUT_OF_DATE_KHR || acq == VK_SUBOPTIMAL_KHR)
{
if (acq == VK_ERROR_OUT_OF_DATE_KHR || acq == VK_SUBOPTIMAL_KHR) {
return; // skip this frame (the mock window isn't resized in practice)
}
if (acq != VK_SUCCESS)
{
if (acq != VK_SUCCESS) {
return;
}
vkResetFences(device_, 1, &in_flight_);
@@ -118,15 +108,11 @@ public:
vkQueuePresentKHR(queue_, &pi);
}
[[nodiscard]] const char* name() const override
{
return "vk";
}
[[nodiscard]] const char* name() const override { return "vk"; }
~VkBackend() override
{
if (device_ != VK_NULL_HANDLE)
{
if (device_ != VK_NULL_HANDLE) {
vkDeviceWaitIdle(device_);
if (in_flight_ != VK_NULL_HANDLE)
vkDestroyFence(device_, in_flight_, nullptr);
@@ -146,25 +132,22 @@ public:
vkDestroyInstance(instance_, nullptr);
}
private:
private:
bool pick_device()
{
std::uint32_t n = 0;
vkEnumeratePhysicalDevices(instance_, &n, nullptr);
std::vector<VkPhysicalDevice> devs(n);
vkEnumeratePhysicalDevices(instance_, &n, devs.data());
for (VkPhysicalDevice pd : devs)
{
for (VkPhysicalDevice pd : devs) {
std::uint32_t qn = 0;
vkGetPhysicalDeviceQueueFamilyProperties(pd, &qn, nullptr);
std::vector<VkQueueFamilyProperties> qf(qn);
vkGetPhysicalDeviceQueueFamilyProperties(pd, &qn, qf.data());
for (std::uint32_t i = 0; i < qn; ++i)
{
for (std::uint32_t i = 0; i < qn; ++i) {
VkBool32 present = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(pd, i, surface_, &present);
if ((qf[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present)
{
if ((qf[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present) {
phys_ = pd;
qfam_ = i;
return true;
@@ -187,8 +170,7 @@ private:
dci.pQueueCreateInfos = &qci;
dci.enabledExtensionCount = 1;
dci.ppEnabledExtensionNames = dev_ext;
if (vkCreateDevice(phys_, &dci, nullptr, &device_) != VK_SUCCESS)
{
if (vkCreateDevice(phys_, &dci, nullptr, &device_) != VK_SUCCESS) {
return false;
}
volkLoadDevice(device_);
@@ -205,13 +187,11 @@ private:
vkGetPhysicalDeviceSurfaceFormatsKHR(phys_, surface_, &fn, nullptr);
std::vector<VkSurfaceFormatKHR> formats(fn);
vkGetPhysicalDeviceSurfaceFormatsKHR(phys_, surface_, &fn, formats.data());
VkSurfaceFormatKHR chosen = formats.empty() ? VkSurfaceFormatKHR{VK_FORMAT_B8G8R8A8_UNORM,
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}
: formats[0];
for (const VkSurfaceFormatKHR& f : formats)
{
if (f.format == VK_FORMAT_B8G8R8A8_UNORM || f.format == VK_FORMAT_R8G8B8A8_UNORM)
{
VkSurfaceFormatKHR chosen =
formats.empty() ? VkSurfaceFormatKHR{VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}
: formats[0];
for (const VkSurfaceFormatKHR& f : formats) {
if (f.format == VK_FORMAT_B8G8R8A8_UNORM || f.format == VK_FORMAT_R8G8B8A8_UNORM) {
chosen = f;
break;
}
@@ -219,8 +199,7 @@ private:
format_ = chosen.format;
std::uint32_t want = caps.minImageCount + 1;
if (caps.maxImageCount > 0 && want > caps.maxImageCount)
{
if (caps.maxImageCount > 0 && want > caps.maxImageCount) {
want = caps.maxImageCount;
}
VkSwapchainCreateInfoKHR sc{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
@@ -228,9 +207,7 @@ private:
sc.minImageCount = want;
sc.imageFormat = chosen.format;
sc.imageColorSpace = chosen.colorSpace;
sc.imageExtent = caps.currentExtent.width != 0xFFFFFFFFu
? caps.currentExtent
: VkExtent2D{width_, height_};
sc.imageExtent = caps.currentExtent.width != 0xFFFFFFFFu ? caps.currentExtent : VkExtent2D{width_, height_};
sc.imageArrayLayers = 1;
// TRANSFER_DST so we can clear it; TRANSFER_SRC so the capture hook can copy it out.
sc.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
@@ -249,12 +226,11 @@ private:
return true;
return false;
};
sc.presentMode = has_mode(VK_PRESENT_MODE_IMMEDIATE_KHR) ? VK_PRESENT_MODE_IMMEDIATE_KHR
sc.presentMode = has_mode(VK_PRESENT_MODE_IMMEDIATE_KHR) ? VK_PRESENT_MODE_IMMEDIATE_KHR
: has_mode(VK_PRESENT_MODE_MAILBOX_KHR) ? VK_PRESENT_MODE_MAILBOX_KHR
: VK_PRESENT_MODE_FIFO_KHR;
: VK_PRESENT_MODE_FIFO_KHR;
sc.clipped = VK_TRUE;
if (vkCreateSwapchainKHR(device_, &sc, nullptr, &swapchain_) != VK_SUCCESS)
{
if (vkCreateSwapchainKHR(device_, &sc, nullptr, &swapchain_) != VK_SUCCESS) {
return false;
}
std::uint32_t in = 0;
@@ -269,29 +245,26 @@ private:
VkCommandPoolCreateInfo pci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
pci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
pci.queueFamilyIndex = qfam_;
if (vkCreateCommandPool(device_, &pci, nullptr, &pool_) != VK_SUCCESS)
{
if (vkCreateCommandPool(device_, &pci, nullptr, &pool_) != VK_SUCCESS) {
return false;
}
VkCommandBufferAllocateInfo ai{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
ai.commandPool = pool_;
ai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
ai.commandBufferCount = 1;
if (vkAllocateCommandBuffers(device_, &ai, &cmd_) != VK_SUCCESS)
{
if (vkAllocateCommandBuffers(device_, &ai, &cmd_) != VK_SUCCESS) {
return false;
}
VkSemaphoreCreateInfo si{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
VkFenceCreateInfo fi{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
fi.flags = VK_FENCE_CREATE_SIGNALED_BIT;
return vkCreateSemaphore(device_, &si, nullptr, &acquire_sem_) == VK_SUCCESS &&
vkCreateSemaphore(device_, &si, nullptr, &submit_sem_) == VK_SUCCESS &&
vkCreateFence(device_, &fi, nullptr, &in_flight_) == VK_SUCCESS;
return vkCreateSemaphore(device_, &si, nullptr, &acquire_sem_) == VK_SUCCESS
&& vkCreateSemaphore(device_, &si, nullptr, &submit_sem_) == VK_SUCCESS
&& vkCreateFence(device_, &fi, nullptr, &in_flight_) == VK_SUCCESS;
}
static void barrier(VkCommandBuffer cb, VkImage img, VkImageLayout from, VkImageLayout to,
VkAccessFlags src_access, VkAccessFlags dst_access, VkPipelineStageFlags src_stage,
VkPipelineStageFlags dst_stage)
static void barrier(VkCommandBuffer cb, VkImage img, VkImageLayout from, VkImageLayout to, VkAccessFlags src_access,
VkAccessFlags dst_access, VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage)
{
VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
b.srcAccessMask = src_access;