The animated pattern math (background sweep, moving 24px bar, frame- counter block) was copy-pasted across all five clear-based backends (dx9/dx10/dx11/dx12/gl). The backends MUST render an identical image -- the capture test decodes the same frame-counter block from each -- so a drift between copies would be a silent test hazard. Factor it into one frame_pattern() helper (plus kBarWidth) in render_backend.hpp; each backend now consumes the computed bg/bar_x/code values with its own API's clear/fill. mock_game_test still decodes every backend correctly.
123 lines
3.5 KiB
C++
123 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
|