#pragma once #include #include #include #include namespace plottest { using plot::u32; /// A surfaceless EGL context plus an off-screen RGBA framebuffer, so renderer /// output can be asserted pixel by pixel without a window or a display server. /// /// Test-only: `plot_core` neither links nor includes EGL. class HeadlessContext { public: explicit HeadlessContext(u32 w = 256, u32 h = 256) noexcept; ~HeadlessContext(); HeadlessContext(const HeadlessContext&) = delete; HeadlessContext& operator=(const HeadlessContext&) = delete; /// Makes an OpenGL 3.3 core context current on this thread, resolves the GL /// entry points against it (`plot::gl::load(eglGetProcAddress)`) and /// allocates the colour attachment. Idempotent. On failure `st` (when /// non-null) carries the reason, every resource already created is released, /// and the error is also printed to stderr so a failing CTest run is /// diagnosable. bool create(plot::Status* st = nullptr) noexcept; /// Releases the framebuffer, the GL context and the EGL display. Safe to call /// more than once, and to call after a failed create(). void destroy() noexcept; /// Binds the off-screen target and reads one RGBA8 pixel, in GL's /// origin-at-the-bottom-left convention. False when it cannot be read. bool read_pixel(u32 x, u32 y, std::uint8_t rgba[4]) const noexcept; u32 width = 0; u32 height = 0; /// The headless path create() selected ("egl surfaceless" or "egl pbuffer"); /// empty before a successful create() and after destroy(). Setting /// PLOT_HEADLESS_FORCE_PBUFFER in the environment skips the surfaceless /// attempt, so the pbuffer fallback can be exercised on a driver that does /// support surfaceless contexts. const char* egl_path = ""; private: plot::gl::Texture color_; plot::gl::Framebuffer framebuffer_; void* display_ = nullptr; // EGLDisplay void* context_ = nullptr; // EGLContext void* surface_ = nullptr; // EGLSurface, null on the surfaceless path }; } // namespace plottest