Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions iris-hostgl/src/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1779,6 +1779,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(87)?;
let g: extern "C" fn(u32, i32, u32, i32, i32, i32, i32) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as u32, a3, a4, a5, a6);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand All @@ -1794,6 +1795,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(88)?;
let g: extern "C" fn(u32, i32, u32, i32, i32, i32, i32) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as u32, a3, a4, a5, a6);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand All @@ -1810,6 +1812,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(89)?;
let g: extern "C" fn(u32, i32, u32, i32, i32, i32, i32, i32) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as u32, a3, a4, a5, a6, a7);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand All @@ -1826,6 +1829,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(90)?;
let g: extern "C" fn(u32, i32, u32, i32, i32, i32, i32, i32) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as u32, a3, a4, a5, a6, a7);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand Down Expand Up @@ -3164,6 +3168,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(209)?;
let g: extern "C" fn(u32, i32, u32, *mut f32) = unsafe { std::mem::transmute(f) };
g(a0, a1, a2, o3.as_mut_ptr());
x.texture_internal_answer_f(a0, a1, a2, &mut o3);
x.put::<f32>(a3, &o3[..n3])?;
x.image_done();
Some(0)
Expand All @@ -3179,6 +3184,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(210)?;
let g: extern "C" fn(u32, i32, u32, *mut i32) = unsafe { std::mem::transmute(f) };
g(a0, a1, a2, o3.as_mut_ptr());
x.texture_internal_answer(a0, a1, a2, &mut o3);
x.put::<i32>(a3, &o3[..n3])?;
x.image_done();
Some(0)
Expand Down Expand Up @@ -5336,6 +5342,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(424)?;
let g: extern "C" fn(u32, i32, i32, i32, i32, u32, u32, *const c_void) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as i32, a3, a4, x.gl_format(a5), x.gl_type(a5, a6), p7 as *const c_void);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand All @@ -5355,6 +5362,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let g: extern "C" fn(u32, i32, i32, i32, i32, i32, u32, u32, *const c_void) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as i32, a3, x.gl_height(a4 as i32) as i32, a5, x.gl_format(a6), x.gl_type(a6, a7), p8 as *const c_void);
}
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand All @@ -5373,6 +5381,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(426)?;
let g: extern "C" fn(u32, i32, u32, i32, i32, i32, i32, u32, u32, *const c_void) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as u32, a3, x.gl_height(a4 as i32) as i32, a5, a6, x.gl_format(a7), x.gl_type(a7, a8), p9 as *const c_void);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand All @@ -5391,6 +5400,7 @@ pub fn run(x: &mut Exec, op: usize, c: &[u8]) -> Option<u64> {
let f = x.func(427)?;
let g: extern "C" fn(u32, i32, u32, i32, i32, i32, i32, u32, u32, *const c_void) = unsafe { std::mem::transmute(f) };
g(a0, a1, x.gl_internal(a2 as i32) as u32, a3, x.gl_height(a4 as i32) as i32, a5, a6, x.gl_format(a7), x.gl_type(a7, a8), p9 as *const c_void);
x.note_internal(a0 as u32, a1 as i32, a2 as i32);
x.image_done();
Some(0)
}
Expand Down
53 changes: 53 additions & 0 deletions iris-hostgl/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ pub struct ClientSide {
/// whenever the context's drawables are bound (draw::bind_with).
pub draw_buffer: Option<u32>,
pub read_buffer: Option<u32>,
/// The internal format each texture image was given, as the program
/// named it, by (texture, target, level); a proxy target's under texture
/// 0. See `texture_internal_answer`.
pub asked_internal: HashMap<(u32, u32, i32), i32>,
}

#[derive(Default, Clone, Copy)]
Expand Down Expand Up @@ -343,6 +347,22 @@ fn relight_after_clip_plane() {
}
}

/// The texture bound to `target` on the host, 0 for a proxy target, or None
/// for a target not kept (see Exec::note_internal).
fn texture_of(target: u32) -> Option<u32> {
let binding = match target {
0x0DE0 => 0x8068, // GL_TEXTURE_1D: GL_TEXTURE_BINDING_1D
0x0DE1 => 0x8069, // GL_TEXTURE_2D
0x806F => 0x806A, // GL_TEXTURE_3D
0x8063 | 0x8064 | 0x8070 => return Some(0), // the proxies
_ => return None,
};
let mut name = 0i32;
// SAFETY: a query of one integer.
unsafe { glGetIntegerv(binding, &mut name) };
Some(name as u32)
}

impl<'a> Exec<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
Expand Down Expand Up @@ -762,6 +782,39 @@ impl<'a> Exec<'a> {
self.client.emul.select_format(tex, internal).unwrap_or(internal)
}

/// A texture image was given `internal` by the program (glTexImage*,
/// glCopyTexImage*): kept for glGetTexLevelParameter's answer.
pub fn note_internal(&mut self, target: u32, level: i32, internal: i32) {
let Some(tex) = texture_of(target) else { return };
self.client.asked_internal.insert((tex, target, level), internal);
}

/// GL_TEXTURE_INTERNAL_FORMAT (GL_TEXTURE_COMPONENTS) is answered with
/// the internal format the program asked for, as IRIX's libGL answers it
/// (and Mesa's), not the sized one the Mac picked: GL_RGBA, not GL_RGBA8.
/// Cosmo Player checks a proxy texture's answer against what it asked
/// for, and on the Mac's took every texture size to be refused: LivingO2
/// (the O2 demos) drew each texture as one texel. A level the host has no
/// image for (a refused proxy: 0) is left as it answered.
pub fn texture_internal_answer(&self, target: u32, level: i32, pname: u32, answer: &mut [i32]) {
const GL_TEXTURE_INTERNAL_FORMAT: u32 = 0x1003;
if pname != GL_TEXTURE_INTERNAL_FORMAT || answer.first().is_none_or(|&a| a == 0) {
return;
}
let Some(tex) = texture_of(target) else { return };
if let Some(&asked) = self.client.asked_internal.get(&(tex, target, level)) {
answer[0] = asked;
}
}

pub fn texture_internal_answer_f(&self, target: u32, level: i32, pname: u32, answer: &mut [f32]) {
let mut i = [answer.first().map_or(0, |&a| a as i32)];
self.texture_internal_answer(target, level, pname, &mut i);
if let Some(a) = answer.first_mut() {
*a = i[0] as f32;
}
}

/// The pixel format to give the host for the one the guest named.
///
/// Only `GL_ABGR_EXT` differs, and `image` has already put the data in the
Expand Down
25 changes: 25 additions & 0 deletions iris-hostgl/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1709,3 +1709,28 @@ fn lit_drawing_after_a_clip_plane_goes_off_is_drawn() {
let p = g.mem.get(px, 4).to_vec();
assert!(p[1] > 200 && p[2] < 50, "the quad drawn lit after the plane went off: {p:?}");
}

/// glGetTexLevelParameteriv(GL_TEXTURE_INTERNAL_FORMAT) answers with the
/// internal format the program gave, as IRIX's libGL does, not the sized one
/// the host chose: Cosmo Player compares a proxy's answer with what it asked
/// for, and on GL_RGBA8 for GL_RGBA shrank every texture to 1x1 (LivingO2).
#[test]
fn texture_internal_format_is_answered_as_the_program_gave_it() {
let mut g = Guest::new(512);
g.context(0x0040_000d, 16, 16);
let out = g.mem.alloc(16, 0);
let px = g.mem.alloc(4 * 4 * 4, 0);
g.mem.put(px, &[0x80u8; 4 * 4 * 4]);
let mut e = Enc::default();
// A proxy, as Cosmo probes: GL_PROXY_TEXTURE_2D, GL_RGBA.
e.cmd("glTexImage2D", &[V::I(0x8064), V::I(0), V::I(0x1908), V::I(64), V::I(64), V::I(0), V::I(0x1908), V::I(0x1401), V::A(0)])
.cmd("glGetTexLevelParameteriv", &[V::I(0x8064), V::I(0), V::I(0x1003), V::A(out)])
// A real texture with GL 1.0's "3 components".
.cmd("glBindTexture", &[V::I(0x0DE1), V::I(5)])
.cmd("glTexImage2D", &[V::I(0x0DE1), V::I(0), V::I(3), V::I(4), V::I(4), V::I(0), V::I(0x1908), V::I(0x1401), V::A(px)])
.cmd("glGetTexLevelParameteriv", &[V::I(0x0DE1), V::I(0), V::I(0x1003), V::A(out + 4)]);
assert_eq!(g.batch(&e.take()), Reply::Ok(0, 0));
let v = |i: u64| u32::from_be_bytes(g.mem.get(out + i, 4).try_into().unwrap());
assert_eq!(v(0), 0x1908, "the proxy: GL_RGBA, as asked");
assert_eq!(v(4), 3, "the texture: 3, as given");
}
10 changes: 10 additions & 0 deletions iris-hostgl/tools/glshim.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,16 @@ def gen_rust(api):
lines.append(f"g({', '.join(call_args)});")
if guard:
lines.append("}")
# The internal format a texture image was given, as the
# program named it, for glGetTexLevelParameter to answer
# with (Exec::texture_internal_answer).
if internal_params and "target" in local and "level" in local:
ik = next(k for k, p in enumerate(params) if p["name"] in internal_params)
lines.append(f"x.note_internal({local['target']} as u32, {local['level']} as i32, a{ik} as i32);")
if name == "glGetTexLevelParameteriv":
lines.append("x.texture_internal_answer(a0, a1, a2, &mut o3);")
if name == "glGetTexLevelParameterfv":
lines.append("x.texture_internal_answer_f(a0, a1, a2, &mut o3);")
# The state queries go past the executor on the way back, so
# that a limit the host reports but this library cannot stand
# behind is answered with one it can (Exec::get_limit).
Expand Down
Loading