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:
59
crates/xenia-ui/src/shaders/blit.wgsl
Normal file
59
crates/xenia-ui/src/shaders/blit.wgsl
Normal file
@@ -0,0 +1,59 @@
|
||||
// Full-screen blit of the guest frontbuffer texture, with aspect-ratio
|
||||
// letterboxing. A three-vertex fullscreen triangle covers the viewport; the
|
||||
// fragment shader samples the frontbuffer and emits sRGB colors directly,
|
||||
// letting the swapchain handle the final gamma step.
|
||||
|
||||
struct Uniforms {
|
||||
// aspect_correction.xy = scaling factors applied to the clip-space
|
||||
// position to letterbox the frontbuffer into the swapchain window.
|
||||
aspect_correction: vec2<f32>,
|
||||
// tint for when the frontbuffer is missing / unknown format. When the
|
||||
// guest has not yet swapped a frame, CPU side uploads a 1x1 texture and
|
||||
// we just want the background color.
|
||||
fallback_rgb: vec3<f32>,
|
||||
_pad: f32,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> u: Uniforms;
|
||||
@group(0) @binding(1) var frontbuffer: texture_2d<f32>;
|
||||
@group(0) @binding(2) var samp: sampler;
|
||||
|
||||
struct VsOut {
|
||||
@builtin(position) clip: vec4<f32>,
|
||||
@location(0) uv: vec2<f32>,
|
||||
@location(1) in_bounds: f32,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vs_main(@builtin(vertex_index) vid: u32) -> VsOut {
|
||||
// Fullscreen triangle: (-1,-1), (3,-1), (-1,3).
|
||||
var pos = array<vec2<f32>, 3>(
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>( 3.0, -1.0),
|
||||
vec2<f32>(-1.0, 3.0),
|
||||
);
|
||||
let p = pos[vid];
|
||||
// Scale by the aspect-correction factor so the frontbuffer stays at its
|
||||
// native aspect inside the window.
|
||||
let corrected = p * u.aspect_correction;
|
||||
var out: VsOut;
|
||||
out.clip = vec4<f32>(corrected, 0.0, 1.0);
|
||||
// UV runs [0,1] across the unscaled fullscreen triangle's extent
|
||||
// [(-1,-1), (3,-1), (-1,3)] → UV = (p+1)*0.5 mapped with Y flipped.
|
||||
let uv_raw = (p + vec2<f32>(1.0, 1.0)) * 0.5;
|
||||
out.uv = vec2<f32>(uv_raw.x, 1.0 - uv_raw.y);
|
||||
// Also pass a flag telling the FS whether the original position (before
|
||||
// aspect correction) was inside [-1,1]; outside = letterbox region.
|
||||
let in_bounds = f32(all(abs(corrected) <= vec2<f32>(1.0, 1.0)));
|
||||
out.in_bounds = in_bounds;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
|
||||
if (in.uv.x < 0.0 || in.uv.x > 1.0 || in.uv.y < 0.0 || in.uv.y > 1.0) {
|
||||
return vec4<f32>(u.fallback_rgb, 1.0);
|
||||
}
|
||||
let sample = textureSample(frontbuffer, samp, in.uv);
|
||||
return vec4<f32>(sample.rgb, 1.0);
|
||||
}
|
||||
47
crates/xenia-ui/src/shaders/hud.wgsl
Normal file
47
crates/xenia-ui/src/shaders/hud.wgsl
Normal file
@@ -0,0 +1,47 @@
|
||||
// Textured-quad pipeline for the HUD overlay. Each character is a 6-vertex
|
||||
// (two-triangle) quad with a UV into the font atlas. The CPU-side HUD code
|
||||
// pushes a vertex buffer every frame describing the text to draw.
|
||||
|
||||
struct VsIn {
|
||||
@location(0) pos_px: vec2<f32>, // pixel-space position in the window
|
||||
@location(1) uv: vec2<f32>, // atlas UV
|
||||
@location(2) rgba: vec4<f32>,
|
||||
};
|
||||
|
||||
struct HudUniforms {
|
||||
// Inverse of the surface dimensions so the VS can map pixel coordinates
|
||||
// into clip space: clip.xy = (pos_px / surface_size) * 2.0 - 1.0 with Y
|
||||
// flipped.
|
||||
inv_surface: vec2<f32>,
|
||||
_pad: vec2<f32>,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> u: HudUniforms;
|
||||
@group(0) @binding(1) var atlas: texture_2d<f32>;
|
||||
@group(0) @binding(2) var samp: sampler;
|
||||
|
||||
struct VsOut {
|
||||
@builtin(position) clip: vec4<f32>,
|
||||
@location(0) uv: vec2<f32>,
|
||||
@location(1) rgba: vec4<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VsIn) -> VsOut {
|
||||
var out: VsOut;
|
||||
let clip_xy = in.pos_px * u.inv_surface * 2.0 - vec2<f32>(1.0, 1.0);
|
||||
// Invert Y so pos_px=(0,0) is top-left.
|
||||
out.clip = vec4<f32>(clip_xy.x, -clip_xy.y, 0.0, 1.0);
|
||||
out.uv = in.uv;
|
||||
out.rgba = in.rgba;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
|
||||
let coverage = textureSample(atlas, samp, in.uv).r;
|
||||
// Atlas is R8 where 0xFF = glyph pixel. We premultiply by the input alpha
|
||||
// so the pipeline's alpha-blend pass composits correctly.
|
||||
let a = coverage * in.rgba.a;
|
||||
return vec4<f32>(in.rgba.rgb * a, a);
|
||||
}
|
||||
Reference in New Issue
Block a user