fix(gl): gate fallible constructors on a complete loader table

- loader: add available(), true only after load() has resolved every
  required entry point (false at the start of load()). Program::from_source
  and the other fallible constructors return ErrorCode::Unsupported while it
  is false, so a failed load() with a partially populated table can no longer
  fault on an unresolved entry point.
- caps: GL_CONTEXT_PROFILE_MASK only exists from 3.2, so query it only on
  3.2+ contexts. A 3.0/3.1 query latches GL_INVALID_ENUM in the context, which
  the next unrelated glGetError() (e.g. read_pixel) then reports as failure.
- tests: gate the dlsym/libGL.so.1 fallback assertions on libGL being
  resolvable, so the GL suite also passes on a GLES-only host.
This commit is contained in:
Martino Ferrari committed 2026-09-27 21:15:57 +02:00
1 parent 258caa38a7
commit 6866457155
4 files changed
+59 -12

No files matched your search

+24
View File
@@ -1,5 +1,7 @@
#include <plot/gl/handle.hpp>
#include <plot/gl/loader.hpp>
#include <cstdint>
#include <cstdio>
@@ -78,6 +80,9 @@ GLuint alloc_texture(GLenum internal_format, GLenum format, u32 width, u32 heigh
// ------------------------------------------------------------------- Buffer
bool Buffer::create(Status* st) noexcept {
if (!available()) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
if (glGenBuffers == nullptr) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
@@ -109,6 +114,9 @@ void Buffer::destroy(GLuint id) noexcept { glDeleteBuffers(1, &id); }
// -------------------------------------------------------------- VertexArray
bool VertexArray::create(Status* st) noexcept {
if (!available()) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
if (glGenVertexArrays == nullptr) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
@@ -139,6 +147,10 @@ void VertexArray::destroy(GLuint id) noexcept { glDeleteVertexArrays(1, &id); }
Program Program::from_source(const char* vertex_src, const char* fragment_src,
Status* st) noexcept {
Program program;
if (!available()) {
fail(st, ErrorCode::Unsupported, kNoLoader);
return program;
}
if (glCreateShader == nullptr || glCreateProgram == nullptr) {
fail(st, ErrorCode::Unsupported, kNoLoader);
return program;
@@ -219,6 +231,9 @@ void Program::destroy(GLuint id) noexcept { glDeleteProgram(id); }
// ------------------------------------------------------------------ Texture
bool Texture::create_rgba(u32 width, u32 height, Status* st) noexcept {
if (!available()) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
if (glGenTextures == nullptr) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
@@ -235,6 +250,9 @@ bool Texture::create_rgba(u32 width, u32 height, Status* st) noexcept {
}
bool Texture::create_r8(u32 width, u32 height, Status* st) noexcept {
if (!available()) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
if (glGenTextures == nullptr) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
@@ -280,6 +298,9 @@ void Texture::destroy(GLuint id) noexcept { glDeleteTextures(1, &id); }
// -------------------------------------------------------------- Framebuffer
bool Framebuffer::create(Status* st) noexcept {
if (!available()) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
if (glGenFramebuffers == nullptr) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
@@ -310,6 +331,9 @@ void Framebuffer::destroy(GLuint id) noexcept { glDeleteFramebuffers(1, &id); }
// -------------------------------------------------------------------- Fence
bool Fence::create(Status* st) noexcept {
if (!available()) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}
if (glFenceSync == nullptr) {
return fail(st, ErrorCode::Unsupported, kNoLoader);
}