Files
CoopAllTheThings/tools/mock_game/render_gl.cpp
BlackMark 30eccf749d 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.
2026-07-12 11:52:53 +02:00

111 lines
3.5 KiB
C++

// OpenGL backend for the mock game. Renders the animated pattern with scissored clears
// (glClear + glScissor -- all GL 1.1, exported straight from opengl32, so **no loader /
// submodule** is needed) and presents with SwapBuffers, so the OpenGL capture hook
// (SwapBuffers / wglSwapBuffers + glReadPixels) sees a genuine GL present.
//
// GL's framebuffer is bottom-left origin and the capture flips it vertically to top-down, so
// the frame-counter block is drawn at the GL *top* (y = h-block) to land at the captured
// image's top-left where the test samples it.
#include "render_backend.hpp"
#include <windows.h>
#include <GL/gl.h>
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) {
return false;
}
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cAlphaBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
const int pf = ChoosePixelFormat(hdc_, &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_)) {
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"))) {
swap_interval(0);
}
return true;
}
void render_and_present(std::uint32_t frame) override
{
const GLsizei w = static_cast<GLsizei>(width_);
const GLsizei h = static_cast<GLsizei>(height_);
glViewport(0, 0, w, h);
const FramePattern p = frame_pattern(frame, width_);
// Animated background (whole framebuffer).
glDisable(GL_SCISSOR_TEST);
glClearColor(p.bg_r / 255.0f, p.bg_g / 255.0f, p.bg_b / 255.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
// Moving vertical bar (full height).
glScissor(static_cast<GLint>(p.bar_x), 0, static_cast<GLsizei>(kBarWidth), h);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Frame-counter block at the GL top-left (y = h - block) so it lands top-left after the
// capture's vertical flip.
glScissor(0, h - static_cast<GLint>(kFrameBlock), static_cast<GLsizei>(kFrameBlock),
static_cast<GLsizei>(kFrameBlock));
glClearColor(p.code_r / 255.0f, p.code_g / 255.0f, p.code_b / 255.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
SwapBuffers(hdc_); // the capture hook intercepts this
}
[[nodiscard]] const char* name() const override { return "gl"; }
~GlBackend() override
{
wglMakeCurrent(nullptr, nullptr);
if (hglrc_ != nullptr) {
wglDeleteContext(hglrc_);
}
if (hdc_ != nullptr && hwnd_ != nullptr) {
ReleaseDC(hwnd_, hdc_);
}
}
private:
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;
HWND hwnd_ = nullptr;
HDC hdc_ = nullptr;
HGLRC hglrc_ = nullptr;
};
} // namespace
std::unique_ptr<RenderBackend> create_gl_backend()
{
return std::make_unique<GlBackend>();
}
} // namespace coop::mock