mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
gex-gl native host: glDrawElements with client-side indices takes them from the command
The guest library (guest/gl) sends a draw's indices inline when no element array buffer is bound, with a flag word; the browser's decoder used them, the native host ignored the flag and passed the guest's pointer to the host's glDrawElements as an offset. Mesa read the host's own address space: gexc died of SIGSEGV in swrast_dri.so when Retrocycles (Linux) started its first game. Test: client_side_indices_come_with_the_draw (a guest pointer of 0xdead0000 as the offset, the indices inline, the triangle drawn). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_461fe3ff06374ecba693831148697ba6
This commit is contained in:
1 parent
348c2ff3f6
commit
b7556e59df
1 file changed
+71
-1
@@ -1334,7 +1334,19 @@ impl Gl {
|
||||
else {
|
||||
return true;
|
||||
};
|
||||
glDrawElements(mode, count, ty, offset as usize as *const c_void);
|
||||
// { ..., client } then, for client-side indices (no
|
||||
// element array buffer bound in the guest), the indices
|
||||
// themselves: the guest's pointer means nothing here, and
|
||||
// passing it as an offset had the host's GL read its own
|
||||
// address space (Retrocycles' arena: SIGSEGV in Mesa).
|
||||
// Older guests send no fifth word.
|
||||
match a.u32() {
|
||||
Some(1) => {
|
||||
let Some(indices) = a.blob() else { return true };
|
||||
glDrawElements(mode, count, ty, indices.as_ptr() as *const c_void);
|
||||
}
|
||||
_ => glDrawElements(mode, count, ty, offset as usize as *const c_void),
|
||||
}
|
||||
}
|
||||
|
||||
op::READ_PIXELS => {
|
||||
@@ -1647,6 +1659,64 @@ mod tests {
|
||||
v
|
||||
}
|
||||
|
||||
/// glDrawElements with client-side indices: they travel with the
|
||||
/// command (the guest's pointer, sent as the offset, must never reach
|
||||
/// the host's GL: Retrocycles crashed Mesa that way).
|
||||
#[test]
|
||||
fn client_side_indices_come_with_the_draw() {
|
||||
let mut gl = match Gl::new(64, 64) {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
eprintln!("skipping: no host GL ({e})");
|
||||
return;
|
||||
}
|
||||
};
|
||||
const GL_UNSIGNED_SHORT: u32 = 0x1403;
|
||||
let verts: [f32; 6] = [-1.0, -1.0, 0.0, -1.0, -1.0, 1.0];
|
||||
let bytes: Vec<u8> = verts.iter().flat_map(|v| v.to_le_bytes()).collect();
|
||||
let idx: Vec<u8> = [0u16, 1, 2].iter().flat_map(|v| v.to_le_bytes()).collect();
|
||||
let vs = b"attribute vec2 pos;\nvoid main(){gl_Position=vec4(pos,0.0,1.0);}\n";
|
||||
let fs = b"precision mediump float;\nvoid main(){gl_FragColor=vec4(0.0,1.0,0.0,1.0);}\n";
|
||||
let mut b = Batch::new();
|
||||
b.cmd(op::CONTEXT_CREATE, &[client_api::ES, 0, 0]);
|
||||
b.cmd(op::VIEWPORT, &[0, 0, 64, 64]);
|
||||
b.cmd(op::CLEAR_COLOR, &[0, 0, 1.0f32.to_bits(), 1.0f32.to_bits()]);
|
||||
b.cmd(op::CLEAR, &[GL_COLOR_BUFFER_BIT]);
|
||||
b.cmd(op::CREATE_SHADER, &[1, GL_VERTEX_SHADER]);
|
||||
b.raw(op::SHADER_SOURCE, &[1, vs.len() as u32], vs);
|
||||
b.cmd(op::COMPILE_SHADER, &[1]);
|
||||
b.cmd(op::CREATE_SHADER, &[2, GL_FRAGMENT_SHADER]);
|
||||
b.raw(op::SHADER_SOURCE, &[2, fs.len() as u32], fs);
|
||||
b.cmd(op::COMPILE_SHADER, &[2]);
|
||||
b.cmd(op::CREATE_PROGRAM, &[3]);
|
||||
b.cmd(op::ATTACH_SHADER, &[3, 1]);
|
||||
b.cmd(op::ATTACH_SHADER, &[3, 2]);
|
||||
b.cmd(op::LINK_PROGRAM, &[3]);
|
||||
b.cmd(op::USE_PROGRAM, &[3]);
|
||||
b.cmd(op::GEN_BUFFERS, &[1, 7]);
|
||||
b.cmd(op::BIND_BUFFER, &[GL_ARRAY_BUFFER, 7]);
|
||||
b.raw(op::BUFFER_DATA, &[GL_ARRAY_BUFFER, GL_STATIC_DRAW, bytes.len() as u32, 0, 1, bytes.len() as u32], &bytes);
|
||||
b.cmd(op::ENABLE_VERTEX_ATTRIB_ARRAY, &[0]);
|
||||
b.cmd(op::VERTEX_ATTRIB_POINTER, &[0, 2, GL_FLOAT, 0, 0, 0]);
|
||||
// A guest pointer far outside anything the host maps, as the offset.
|
||||
b.raw(op::DRAW_ELEMENTS, &[GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0xdead_0000, 1, idx.len() as u32], &idx);
|
||||
b.cmd(op::FINISH, &[]);
|
||||
b.cmd(op::READ_PIXELS, &[0, 0, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, 0, 1, 0]);
|
||||
b.cmd(op::GET_ERROR, &[]);
|
||||
let (reply, status, _) = gl.run(b.bytes());
|
||||
assert_eq!(status, error::NONE);
|
||||
let r = reply.bytes();
|
||||
let mut at = 0;
|
||||
for _ in 0..3 {
|
||||
read_blob(r, &mut at); // vendor, renderer, version
|
||||
}
|
||||
let _finish = read_u32(r, &mut at);
|
||||
let _offset = read_u32(r, &mut at);
|
||||
let pixels = read_blob(r, &mut at);
|
||||
assert_eq!(read_u32(r, &mut at), 0, "GL reported no error");
|
||||
assert_eq!(&pixels[(2 * 64 + 2) * 4..(2 * 64 + 2) * 4 + 4], &[0, 255, 0, 255], "the indexed triangle");
|
||||
}
|
||||
|
||||
/// The whole path: bring up a context, compile the application's own
|
||||
/// GLSL, upload vertices, draw, read the pixels back.
|
||||
///
|
||||
|
||||
Reference in new issue
Block a user