xenia-gpu: end-to-end Xenos pipeline (PM4, ucode, EDRAM, resolve)
First real GPU implementation. Ring/PM4 frontend (ring_view,
ring_drain, pm4) drains the command processor; gpu_system owns the
threaded backend (DrainFence RPC + parker/fence helpers from M1) and
the MMIO-mapped register block (mmio_region).
Xenos shader frontend: ucode/{alu,control_flow,fetch,mod}.rs decode
the Xbox 360 microcode, translator.rs lowers it onto the WGSL
xenos_interp interpreter shader (shaders/xenos_interp.wgsl).
shader_metrics.rs counts decode/translate work.
Render state: draw_state, primitive, render_target_cache,
texture_cache, tiled_address (Xenos's swizzled tiled-memory layout),
xenos_constants (register field constants), edram (the 10 MiB EDRAM
model with MSAA), and resolve.rs (TILE_FLUSH copy-out — clear-resolve
plus bitwise-equivalent 32 bpp + 64 bpp paths landed). handle.rs
owns the typed GPU-resource handles the kernel hands out.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
117
crates/xenia-gpu/src/ucode/fetch.rs
Normal file
117
crates/xenia-gpu/src/ucode/fetch.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
//! Xenos fetch (vertex + texture) instruction decoder.
|
||||
//!
|
||||
//! Like ALU instructions, fetches are 96 bits (3 dwords). The opcode lives
|
||||
//! in the low 5 bits of word0. We split them into `VertexFetch` and
|
||||
//! `TextureFetch` structurally because their operand layouts differ.
|
||||
//!
|
||||
//! Reference: `xenia-canary/src/xenia/gpu/ucode.h:690-877`.
|
||||
|
||||
/// Decoded fetch instruction.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FetchInstruction {
|
||||
Vertex(VertexFetch),
|
||||
Texture(TextureFetch),
|
||||
/// Unknown / minor variants we don't model yet.
|
||||
Unknown { opcode: u8, raw: [u32; 3] },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct VertexFetch {
|
||||
/// Vertex fetch constant index (0..=95).
|
||||
pub fetch_const: u8,
|
||||
/// Source register index (vertex index in r#).
|
||||
pub src_register: u8,
|
||||
/// Destination register for the fetched value.
|
||||
pub dest_register: u8,
|
||||
/// 4-bit write mask.
|
||||
pub dest_write_mask: u8,
|
||||
pub raw: [u32; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct TextureFetch {
|
||||
/// Texture fetch constant index (0..=31).
|
||||
pub fetch_const: u8,
|
||||
pub src_register: u8,
|
||||
pub dest_register: u8,
|
||||
pub dest_write_mask: u8,
|
||||
/// Dimension: 0=1D, 1=2D, 2=3D/stacked, 3=cube.
|
||||
pub dimension: u8,
|
||||
pub raw: [u32; 3],
|
||||
}
|
||||
|
||||
/// Opcodes (low 5 bits of word0). From `ucode.h`.
|
||||
pub mod op {
|
||||
pub const VERTEX_FETCH: u8 = 0x00;
|
||||
pub const TEXTURE_FETCH: u8 = 0x01;
|
||||
pub const GET_TEXTURE_BORDER_COLOR_FRAC: u8 = 0x16;
|
||||
pub const GET_TEXTURE_COMPUTED_LOD: u8 = 0x17;
|
||||
pub const GET_TEXTURE_WEIGHTS: u8 = 0x18;
|
||||
pub const GET_TEXTURE_GRADIENTS: u8 = 0x19;
|
||||
pub const SET_TEXTURE_LOD: u8 = 0x1A;
|
||||
pub const SET_TEXTURE_GRADIENTS_HORZ: u8 = 0x1B;
|
||||
pub const SET_TEXTURE_GRADIENTS_VERT: u8 = 0x1C;
|
||||
}
|
||||
|
||||
pub fn decode_fetch(words: [u32; 3]) -> FetchInstruction {
|
||||
let w0 = words[0];
|
||||
let w1 = words[1];
|
||||
let opcode = (w0 & 0x1F) as u8;
|
||||
match opcode {
|
||||
op::VERTEX_FETCH => FetchInstruction::Vertex(VertexFetch {
|
||||
fetch_const: ((w0 >> 5) & 0x1F) as u8,
|
||||
src_register: ((w0 >> 17) & 0x7F) as u8,
|
||||
dest_register: ((w0 >> 10) & 0x7F) as u8,
|
||||
dest_write_mask: ((w1 >> 23) & 0xF) as u8,
|
||||
raw: words,
|
||||
}),
|
||||
op::TEXTURE_FETCH => FetchInstruction::Texture(TextureFetch {
|
||||
fetch_const: ((w0 >> 5) & 0x1F) as u8,
|
||||
src_register: ((w0 >> 17) & 0x7F) as u8,
|
||||
dest_register: ((w0 >> 10) & 0x7F) as u8,
|
||||
dest_write_mask: ((w1 >> 23) & 0xF) as u8,
|
||||
dimension: ((w1 >> 29) & 0x3) as u8,
|
||||
raw: words,
|
||||
}),
|
||||
_ => FetchInstruction::Unknown { opcode, raw: words },
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn decode_vertex_fetch() {
|
||||
// opcode=0 (vertex), fetch_const=5, src=2, dest=7.
|
||||
let w0 = 0u32 | (5 << 5) | (7 << 10) | (2 << 17);
|
||||
let v = decode_fetch([w0, 0, 0]);
|
||||
match v {
|
||||
FetchInstruction::Vertex(vf) => {
|
||||
assert_eq!(vf.fetch_const, 5);
|
||||
assert_eq!(vf.src_register, 2);
|
||||
assert_eq!(vf.dest_register, 7);
|
||||
}
|
||||
other => panic!("expected Vertex, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_texture_fetch() {
|
||||
let w0 = 1u32 | (3 << 5) | (4 << 10) | (1 << 17);
|
||||
let t = decode_fetch([w0, (2u32 << 29), 0]);
|
||||
match t {
|
||||
FetchInstruction::Texture(tf) => {
|
||||
assert_eq!(tf.fetch_const, 3);
|
||||
assert_eq!(tf.dimension, 2);
|
||||
}
|
||||
other => panic!("expected Texture, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_opcode_is_classified() {
|
||||
let v = decode_fetch([0x16, 0, 0]); // GET_TEXTURE_BORDER_COLOR_FRAC
|
||||
assert!(matches!(v, FetchInstruction::Unknown { opcode: 0x16, .. }));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user