mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
gex-gl host: framebuffer and renderbuffer names generated by the host (NVIDIA refuses binding names GenFramebuffers did not return; Hollow Knight drew black on edger)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_685ed8fb0aed4f7099b7ecc34fc3b595
This commit is contained in:
1 parent
cbbe82fff2
commit
273b46359e
3 files changed
+52
-9
No files matched your search
@@ -7,7 +7,8 @@
|
||||
*
|
||||
* Object names: the native host runs the guest's buffer, texture and other
|
||||
* names as they are (a desktop compatibility context and GLES both accept
|
||||
* names they never generated), so those tables are the identity; programs
|
||||
* names they never generated), so those tables are the identity --
|
||||
* except framebuffers and renderbuffers (gexgl_native_object); programs
|
||||
* and shaders are named by the host's GL, and host.rs tells this side about
|
||||
* each (gexgl_native_name). Program pipelines are generated by these cases
|
||||
* themselves and kept here. */
|
||||
@@ -261,6 +262,28 @@ extern "C" void gexgl_native_pixel_store(uint32_t pname, int32_t param) {
|
||||
(void)gexgl_set_pixel_store(&pack_store, &unpack_store, pname, param);
|
||||
}
|
||||
|
||||
/* A framebuffer (kind 2) or renderbuffer (kind 3) name of the guest's, as the
|
||||
* host's: op 0 looks it up, generating a host name at its first use (a
|
||||
* GenFramebuffers, or a bind of a name the guest never generated), op 1
|
||||
* forgets it and says which host name to delete. 0 is 0. */
|
||||
extern "C" uint32_t gexgl_native_object(uint32_t kind, uint32_t guest, uint32_t op) {
|
||||
if (!guest) return 0;
|
||||
auto& map = kind == 2 ? framebuffers : renderbuffers;
|
||||
auto it = map.find(guest);
|
||||
if (op == 1) {
|
||||
if (it == map.end()) return guest;
|
||||
const GLuint host = it->second;
|
||||
map.erase(it);
|
||||
return host;
|
||||
}
|
||||
if (it != map.end()) return it->second;
|
||||
GLuint host = 0;
|
||||
if (kind == 2) glGenFramebuffers(1, &host);
|
||||
else glGenRenderbuffers(1, &host);
|
||||
map[guest] = host;
|
||||
return host;
|
||||
}
|
||||
|
||||
/* A shader (kind 0) or program (kind 1) host.rs created (host != 0) or
|
||||
* deleted (host == 0). */
|
||||
extern "C" void gexgl_native_name(uint32_t kind, uint32_t guest, uint32_t host) {
|
||||
|
||||
@@ -320,6 +320,11 @@
|
||||
/* Room for what GL writes even where the table says less. */
|
||||
std::vector<GLint> values(std::max<size_t>(n, 16));
|
||||
if (n) glGetIntegerv(pname, values.data());
|
||||
/* The host's framebuffer and renderbuffer names, as the guest's. */
|
||||
if (pname == GL_DRAW_FRAMEBUFFER_BINDING || pname == GL_READ_FRAMEBUFFER_BINDING)
|
||||
values[0] = (GLint)external_name(framebuffers, (GLuint)values[0]);
|
||||
if (pname == GL_RENDERBUFFER_BINDING)
|
||||
values[0] = (GLint)external_name(renderbuffers, (GLuint)values[0]);
|
||||
put_blob(values.data(), n * sizeof(values[0]));
|
||||
break;
|
||||
}
|
||||
@@ -1504,12 +1504,26 @@ impl Gl {
|
||||
reply.u32(glGetError());
|
||||
}
|
||||
|
||||
// Framebuffers. As for buffers and textures the guest picks
|
||||
// the names, which binding accepts.
|
||||
op::GEN_FRAMEBUFFERS | op::GEN_RENDERBUFFERS => {}
|
||||
op::DELETE_FRAMEBUFFERS | op::DELETE_RENDERBUFFERS => {
|
||||
// Framebuffers and renderbuffers are named by the host: unlike
|
||||
// buffers and textures, ARB_framebuffer_object refuses a name
|
||||
// GenFramebuffers did not return (NVIDIA does, with
|
||||
// INVALID_OPERATION, and every FBO bind failed: Hollow Knight
|
||||
// drew black there; Mesa takes them). The decoder keeps the
|
||||
// map (`gexgl_native_object`), so the generated cases and the
|
||||
// binding queries see the guest's names.
|
||||
op::GEN_FRAMEBUFFERS | op::GEN_RENDERBUFFERS => {
|
||||
let kind = if opcode == op::GEN_FRAMEBUFFERS { 2 } else { 3 };
|
||||
let n = a.u32().unwrap_or(0);
|
||||
let names: Vec<GLuint> = (0..n).filter_map(|_| a.u32()).collect();
|
||||
for _ in 0..n {
|
||||
if let Some(g) = a.u32() {
|
||||
native::gexgl_native_object(kind, g, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
op::DELETE_FRAMEBUFFERS | op::DELETE_RENDERBUFFERS => {
|
||||
let kind = if opcode == op::DELETE_FRAMEBUFFERS { 2 } else { 3 };
|
||||
let n = a.u32().unwrap_or(0);
|
||||
let names: Vec<GLuint> = (0..n).filter_map(|_| a.u32()).map(|g| native::gexgl_native_object(kind, g, 1)).collect();
|
||||
if opcode == op::DELETE_FRAMEBUFFERS {
|
||||
glDeleteFramebuffers(names.len() as GLsizei, names.as_ptr());
|
||||
} else {
|
||||
@@ -1518,12 +1532,12 @@ impl Gl {
|
||||
}
|
||||
op::BIND_FRAMEBUFFER => {
|
||||
if let (Some(t), Some(n)) = (a.u32(), a.u32()) {
|
||||
glBindFramebuffer(t, n);
|
||||
glBindFramebuffer(t, native::gexgl_native_object(2, n, 0));
|
||||
}
|
||||
}
|
||||
op::BIND_RENDERBUFFER => {
|
||||
if let (Some(t), Some(n)) = (a.u32(), a.u32()) {
|
||||
glBindRenderbuffer(t, n);
|
||||
glBindRenderbuffer(t, native::gexgl_native_object(3, n, 0));
|
||||
}
|
||||
}
|
||||
op::CHECK_FRAMEBUFFER_STATUS => {
|
||||
@@ -1542,7 +1556,7 @@ impl Gl {
|
||||
let (Some(t), Some(att), Some(rt), Some(rb)) = (a.u32(), a.u32(), a.u32(), a.u32()) else {
|
||||
return true;
|
||||
};
|
||||
glFramebufferRenderbuffer(t, att, rt, rb);
|
||||
glFramebufferRenderbuffer(t, att, rt, native::gexgl_native_object(3, rb, 0));
|
||||
}
|
||||
op::RENDERBUFFER_STORAGE => {
|
||||
let (Some(t), Some(f), Some(w), Some(h)) = (a.u32(), a.u32(), a.i32(), a.i32()) else {
|
||||
@@ -1754,6 +1768,7 @@ mod native {
|
||||
pub fn gexgl_native_one(op: u32, body: *const u8, len: usize, reply: *mut *const u8, reply_len: *mut usize) -> i32;
|
||||
pub fn gexgl_native_pixel_store(pname: u32, param: i32);
|
||||
pub fn gexgl_native_name(kind: u32, guest: u32, host: u32);
|
||||
pub fn gexgl_native_object(kind: u32, guest: u32, op: u32) -> u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user