Add xenia-ui crate; switch analysis store to DuckDB

Workspace gains a new xenia-ui member that owns the winit/wgpu
window, the Xenos display pipeline (xenos_pipeline + render +
texture_cache_host), HUD font/blit shaders, and the input-bridge
plumbing the app uses to surface guest framebuffers and overlays.

Workspace dependencies grow accordingly: rusqlite is replaced with
duckdb (analysis pipeline now writes DuckDB stores), and tracing /
metrics / pprof / winit / wgpu / gilrs / pollster / crossbeam /
bytemuck are added at workspace level so xenia-ui and xenia-app
share versions. Cargo.lock regenerated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-01 16:26:48 +02:00
parent f166d061be
commit e2b8860e10
13 changed files with 7534 additions and 42 deletions

231
crates/xenia-ui/src/font.rs Normal file
View File

@@ -0,0 +1,231 @@
//! Minimal 8×8 bitmap font for the HUD.
//!
//! Only printable ASCII (0x20..0x7E) is populated. Each glyph is 8 bytes, one
//! byte per row, MSB = leftmost pixel. This is a bespoke hand-drawn font just
//! wide enough for status-line text ("Swap: 0x1234 Pad: 0x0040 …"); no
//! international glyphs, no kerning, no anti-aliasing.
//!
//! The font is rendered by uploading the 8×(8·N) atlas as an R8 texture and
//! sampling one cell per character quad from the HUD shader.
pub const CELL_W: u32 = 8;
pub const CELL_H: u32 = 8;
pub const FIRST: u8 = 0x20;
pub const LAST: u8 = 0x7E;
pub const GLYPH_COUNT: u32 = (LAST as u32) - (FIRST as u32) + 1;
/// Build the atlas as a linear R8 buffer, `CELL_W × (CELL_H * GLYPH_COUNT)`,
/// one glyph per 8-row strip stacked top-to-bottom.
pub fn build_atlas() -> Vec<u8> {
let mut out = vec![0u8; (CELL_W * CELL_H * GLYPH_COUNT) as usize];
for ch in FIRST..=LAST {
let rows = GLYPHS[(ch - FIRST) as usize];
for (row, byte) in rows.iter().enumerate() {
for col in 0..8 {
let bit = (byte >> (7 - col)) & 1;
if bit != 0 {
let idx = ((ch - FIRST) as u32 * CELL_H + row as u32) * CELL_W + col as u32;
out[idx as usize] = 0xFF;
}
}
}
}
out
}
/// 95 glyphs (0x20..0x7E). Each entry is 8 bytes of rows.
/// Hand-crafted for the HUD lines we actually emit. Most punctuation is
/// covered; unsupported chars fall through to the space glyph.
#[rustfmt::skip]
pub const GLYPHS: [[u8; 8]; 95] = [
// 0x20 ' '
[0,0,0,0,0,0,0,0],
// '!'
[0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x00],
// '"'
[0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00],
// '#'
[0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x00],
// '$'
[0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00],
// '%'
[0x00,0xC6,0xCC,0x18,0x30,0x66,0xC6,0x00],
// '&'
[0x38,0x6C,0x38,0x76,0xDC,0xCC,0x76,0x00],
// '\''
[0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00],
// '('
[0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00],
// ')'
[0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00],
// '*'
[0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00],
// '+'
[0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00],
// ','
[0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30],
// '-'
[0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00],
// '.'
[0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00],
// '/'
[0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00],
// '0'
[0x7C,0xC6,0xCE,0xD6,0xE6,0xC6,0x7C,0x00],
// '1'
[0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00],
// '2'
[0x7C,0xC6,0x06,0x1C,0x30,0x66,0xFE,0x00],
// '3'
[0x7C,0xC6,0x06,0x3C,0x06,0xC6,0x7C,0x00],
// '4'
[0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x1E,0x00],
// '5'
[0xFE,0xC0,0xC0,0xFC,0x06,0xC6,0x7C,0x00],
// '6'
[0x38,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00],
// '7'
[0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00],
// '8'
[0x7C,0xC6,0xC6,0x7C,0xC6,0xC6,0x7C,0x00],
// '9'
[0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00],
// ':'
[0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00],
// ';'
[0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30],
// '<'
[0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00],
// '='
[0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00],
// '>'
[0x60,0x30,0x18,0x0C,0x18,0x30,0x60,0x00],
// '?'
[0x7C,0xC6,0x0C,0x18,0x18,0x00,0x18,0x00],
// '@'
[0x7C,0xC6,0xDE,0xDE,0xDE,0xC0,0x78,0x00],
// 'A'
[0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00],
// 'B'
[0xFC,0x66,0x66,0x7C,0x66,0x66,0xFC,0x00],
// 'C'
[0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00],
// 'D'
[0xF8,0x6C,0x66,0x66,0x66,0x6C,0xF8,0x00],
// 'E'
[0xFE,0x62,0x68,0x78,0x68,0x62,0xFE,0x00],
// 'F'
[0xFE,0x62,0x68,0x78,0x68,0x60,0xF0,0x00],
// 'G'
[0x3C,0x66,0xC0,0xC0,0xCE,0x66,0x3E,0x00],
// 'H'
[0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00],
// 'I'
[0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00],
// 'J'
[0x1E,0x0C,0x0C,0x0C,0xCC,0xCC,0x78,0x00],
// 'K'
[0xE6,0x66,0x6C,0x78,0x6C,0x66,0xE6,0x00],
// 'L'
[0xF0,0x60,0x60,0x60,0x62,0x66,0xFE,0x00],
// 'M'
[0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00],
// 'N'
[0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0x00],
// 'O'
[0x38,0x6C,0xC6,0xC6,0xC6,0x6C,0x38,0x00],
// 'P'
[0xFC,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00],
// 'Q'
[0x7C,0xC6,0xC6,0xC6,0xCE,0x7C,0x0E,0x00],
// 'R'
[0xFC,0x66,0x66,0x7C,0x6C,0x66,0xE6,0x00],
// 'S'
[0x7C,0xC6,0xE0,0x78,0x0E,0xC6,0x7C,0x00],
// 'T'
[0x7E,0x7E,0x5A,0x18,0x18,0x18,0x3C,0x00],
// 'U'
[0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00],
// 'V'
[0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00],
// 'W'
[0xC6,0xC6,0xC6,0xD6,0xFE,0xEE,0xC6,0x00],
// 'X'
[0xC6,0xC6,0x6C,0x38,0x6C,0xC6,0xC6,0x00],
// 'Y'
[0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00],
// 'Z'
[0xFE,0xC6,0x8C,0x18,0x32,0x66,0xFE,0x00],
// '['
[0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00],
// '\\'
[0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00],
// ']'
[0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00],
// '^'
[0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00],
// '_'
[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF],
// '`'
[0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00],
// 'a'
[0x00,0x00,0x78,0x0C,0x7C,0xCC,0x76,0x00],
// 'b'
[0xE0,0x60,0x7C,0x66,0x66,0x66,0xDC,0x00],
// 'c'
[0x00,0x00,0x7C,0xC6,0xC0,0xC6,0x7C,0x00],
// 'd'
[0x1C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00],
// 'e'
[0x00,0x00,0x7C,0xC6,0xFE,0xC0,0x7C,0x00],
// 'f'
[0x1C,0x36,0x30,0x78,0x30,0x30,0x78,0x00],
// 'g'
[0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0xF8],
// 'h'
[0xE0,0x60,0x6C,0x76,0x66,0x66,0xE6,0x00],
// 'i'
[0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00],
// 'j'
[0x06,0x00,0x0E,0x06,0x06,0x66,0x66,0x3C],
// 'k'
[0xE0,0x60,0x66,0x6C,0x78,0x6C,0xE6,0x00],
// 'l'
[0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00],
// 'm'
[0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0x00],
// 'n'
[0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x00],
// 'o'
[0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0x00],
// 'p'
[0x00,0x00,0xDC,0x66,0x66,0x7C,0x60,0xF0],
// 'q'
[0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0x1E],
// 'r'
[0x00,0x00,0xDC,0x76,0x60,0x60,0xF0,0x00],
// 's'
[0x00,0x00,0x7E,0xC0,0x7C,0x06,0xFC,0x00],
// 't'
[0x30,0x30,0x7C,0x30,0x30,0x36,0x1C,0x00],
// 'u'
[0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x76,0x00],
// 'v'
[0x00,0x00,0xC6,0xC6,0xC6,0x6C,0x38,0x00],
// 'w'
[0x00,0x00,0xC6,0xD6,0xD6,0xFE,0x6C,0x00],
// 'x'
[0x00,0x00,0xC6,0x6C,0x38,0x6C,0xC6,0x00],
// 'y'
[0x00,0x00,0xC6,0xC6,0xC6,0x7E,0x06,0xFC],
// 'z'
[0x00,0x00,0xFE,0x4C,0x18,0x32,0xFE,0x00],
// '{'
[0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00],
// '|'
[0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00],
// '}'
[0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00],
// '~'
[0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00],
];