gex-gl: the native host makes a compatibility context when a guest asks for core

Since 6230d6f4 (10-06) the native host honours a core-profile request,
and a core profile refuses to bind a buffer, texture or vertex array name
glGen* did not return -- but the guest library names its own objects, as
the wire protocol lets it. Every glBindBuffer of a core client failed with
INVALID_OPERATION: tests/gl-map-buffer.sh read zeros (0 of 3 on gooner,
with any gexc and any guest library from 10-04 on), and native Muck, a GL
4.x core client, drew nothing. E3 (bcc93e30) is not involved (it is the
NT opengl32 layer; this is the Linux guest path).

The host now asks for a compatibility context of the requested version,
falling back to core only where there is none, and the guest library
answers GL_CONTEXT_PROFILE_MASK with the profile the client asked for.
tests/gl-map-buffer.sh 3/3, tests/gl-state-query-cache.sh 10/10,
tests/games/glx-context.sh 0 failures (3.2 core reports mask 1).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_d2453757b8a84fdbb4ee7c9e093141df
This commit is contained in:
velzieandClaude Opus 5.5 committed 2026-10-08 10:32:07 -04:00
1 parent b755fb7adb
commit c39706baac
2 files changed
+35 -13

No files matched your search

+25 -12
View File
@@ -779,18 +779,31 @@ impl Gl {
eglBindAPI(EGL_OPENGL_ES_API);
return Err("no desktop OpenGL EGL config".into());
}
// Core only when asked for, from 3.2 (where profiles begin).
let core = !compat && (major > 3 || (major == 3 && minor >= 2));
let mut ctx_attrs = vec![
EGL_CONTEXT_OPENGL_PROFILE_MASK,
if core { EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT } else { EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT },
];
if major > 3 || (major == 3 && minor > 0) {
ctx_attrs.extend_from_slice(&[EGL_CONTEXT_MAJOR_VERSION, major as EGLint]);
ctx_attrs.extend_from_slice(&[EGL_CONTEXT_MINOR_VERSION, minor as EGLint]);
// A compatibility context even when the guest asks for core, and
// core only where the host has no compatibility context of that
// version. The guest names its own objects (the wire protocol lets
// it), and a core profile refuses to bind a buffer, texture or
// vertex array name glGen* did not return: every glBindBuffer of
// Muck's (a GL 4.x core client) failed with INVALID_OPERATION,
// and nothing drew. A compatibility profile is a superset of core
// for what a core client calls; the guest library answers
// GL_CONTEXT_PROFILE_MASK with what the client asked for.
let attrs_for = |core: bool| {
let mut a = vec![
EGL_CONTEXT_OPENGL_PROFILE_MASK,
if core { EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT } else { EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT },
];
if major > 3 || (major == 3 && minor > 0) {
a.extend_from_slice(&[EGL_CONTEXT_MAJOR_VERSION, major as EGLint]);
a.extend_from_slice(&[EGL_CONTEXT_MINOR_VERSION, minor as EGLint]);
}
a.push(EGL_NONE);
a
};
let mut context = eglCreateContext(self.display, config, std::ptr::null_mut(), attrs_for(false).as_ptr());
if context.is_null() && (major > 3 || (major == 3 && minor >= 2)) {
context = eglCreateContext(self.display, config, std::ptr::null_mut(), attrs_for(true).as_ptr());
}
ctx_attrs.push(EGL_NONE);
let context = eglCreateContext(self.display, config, std::ptr::null_mut(), ctx_attrs.as_ptr());
if context.is_null() {
eglBindAPI(EGL_OPENGL_ES_API);
return Err(format!("no desktop OpenGL context: {:#x}", eglGetError()));
@@ -815,7 +828,7 @@ impl Gl {
self.unpack_store = PixelStore::default();
self.current_on = Some(std::thread::current().id());
self.compat = Compat::load();
self.desktop = Some((major, minor, !core));
self.desktop = Some((major, minor, compat || !(major > 3 || (major == 3 && minor >= 2))));
self.vendor = gl_string(0x1F00);
self.renderer = gl_string(0x1F01);
self.version = gl_string(0x1F02);
+10 -1
View File
@@ -2117,6 +2117,8 @@ static void host_gl_version(int *major, int *minor) {
}
static size_t gexgl_state_query_components(unsigned pname);
/* Defined with the EGL context state below: what the client asked for. */
static int egl_context_major, egl_context_minor, egl_context_profile;
void glGetIntegerv(unsigned pname, int *out) {
if (pname == 0x821Bu || pname == 0x821Cu) { /* GL_MAJOR/MINOR_VERSION */
@@ -2139,12 +2141,19 @@ void glGetIntegerv(unsigned pname, int *out) {
when it is one (GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, 2), and a
desktop context of 3.2 or later otherwise is core (1). ES has no
profiles. This answered core for every desktop context. */
/* What the client asked for, when it asked for core: the native
host gives it a compatibility context regardless (crates/gex-gl
become_desktop: a core one refuses the guest's own object names),
and its version string would say so. */
int mask = 0;
if (egl_api == 0x30A2u) {
const char *v = (const char *)glGetString(0x1F02u);
int major = 0, minor = 0;
host_gl_version(&major, &minor);
if (v && strstr(v, "Compatibility")) mask = 2;
const int asked_core = egl_context_profile == 0 &&
(egl_context_major > 3 || (egl_context_major == 3 && egl_context_minor >= 2));
if (asked_core) mask = 1;
else if (v && strstr(v, "Compatibility")) mask = 2;
else if (major > 3 || (major == 3 && minor >= 2)) mask = 1;
}
if (out) out[0] = mask;