Initial workspace: GPU context, compute pass, adaptive Slint shell
Establishes the v0.1 foundations on both platforms: - dr-types: SourceRef (never a filesystem path — Android SAF has none), Format, Availability, Validator with ETag quote normalisation - dr-gpu: wgpu device, compute pass writing a storage texture, resize - dr-ui: Slint shell with FR-UI-1 adaptive layout, computed in Rust to avoid a binding loop - docker/android: pinned toolchain, verified producing API 28 ARM binaries Measured the cost of the temporary CPU readback path (dr-gpu bench): compute is 0.06-0.28ms across sizes while readback is 0.63-7.43ms, so readback is 90-96% of frame time and scales with area. Recorded in ARCH §6.1 — this is why spike S1 is the priority. Mitigations pending S1: reuse the staging buffer, apply at most one resize per frame, and cap render resolution at 2048 on the long edge. 10 tests passing; core crates cross-compile for aarch64-linux-android.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import { Theme } from "theme.slint";
|
||||
|
||||
// Status strip — surfaces the GPU backend and adapter, which matters during
|
||||
// v0.1 because assumption A1 is exactly "does this compositing path work on
|
||||
// this hardware". Seeing the adapter at a glance makes vendor differences
|
||||
// obvious during the S1/S2 spikes.
|
||||
component StatusBar inherits Rectangle {
|
||||
in property <string> adapter;
|
||||
in property <string> backend;
|
||||
in property <string> layout-class;
|
||||
in property <int> fps;
|
||||
|
||||
height: 28px;
|
||||
background: Theme.surface;
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: Theme.gap;
|
||||
padding-right: Theme.gap;
|
||||
spacing: Theme.gap;
|
||||
alignment: start;
|
||||
|
||||
Text {
|
||||
text: root.backend;
|
||||
color: Theme.accent;
|
||||
font-size: Theme.text-sm;
|
||||
font-weight: 700;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.adapter;
|
||||
color: Theme.ink-dim;
|
||||
font-size: Theme.text-sm;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
Rectangle { horizontal-stretch: 1; }
|
||||
|
||||
Text {
|
||||
text: root.layout-class;
|
||||
color: Theme.ink-faint;
|
||||
font-size: Theme.text-sm;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.fps + " fps";
|
||||
color: root.fps >= 55 ? Theme.ink-dim : Theme.accent;
|
||||
font-size: Theme.text-sm;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
y: parent.height - 1px;
|
||||
height: 1px;
|
||||
background: Theme.rule;
|
||||
}
|
||||
}
|
||||
|
||||
// A placeholder panel standing in for the adjustment controls that FR-DEV-3a
|
||||
// will generate from operation descriptors.
|
||||
component SidePanel inherits Rectangle {
|
||||
background: Theme.surface;
|
||||
|
||||
VerticalLayout {
|
||||
padding: Theme.gap;
|
||||
spacing: Theme.gap;
|
||||
alignment: start;
|
||||
|
||||
Text {
|
||||
text: "DEVELOP";
|
||||
color: Theme.accent;
|
||||
font-size: Theme.text-sm;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.2px;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Controls are generated from operation\ndescriptors in v0.2 (FR-DEV-3a).";
|
||||
color: Theme.ink-faint;
|
||||
font-size: Theme.text-sm;
|
||||
wrap: word-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 1px;
|
||||
background: Theme.rule;
|
||||
}
|
||||
}
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
title: "DarkRoom";
|
||||
background: Theme.ground;
|
||||
preferred-width: 1100px;
|
||||
preferred-height: 720px;
|
||||
min-width: 360px;
|
||||
min-height: 320px;
|
||||
|
||||
// Set from Rust each frame: the compute output, delivered as a texture.
|
||||
in property <image> canvas;
|
||||
in property <string> adapter: "detecting…";
|
||||
in property <string> backend: "—";
|
||||
in property <int> fps: 0;
|
||||
|
||||
// FR-UI-1: layout class follows window width, not device type. A narrow
|
||||
// desktop window gets the compact layout, exactly as a tablet would.
|
||||
//
|
||||
// Set from Rust rather than derived from `root.width` here: a property
|
||||
// read inside the layout and also feeding it creates a binding loop,
|
||||
// which Slint warns about and which can panic at runtime.
|
||||
in property <bool> expanded: true;
|
||||
in property <string> layout-class: "expanded";
|
||||
|
||||
callback canvas-resized(int, int);
|
||||
callback window-resized(length);
|
||||
|
||||
// One-way: report width outward, never read layout back into it.
|
||||
changed width => { root.window-resized(self.width); }
|
||||
|
||||
VerticalLayout {
|
||||
StatusBar {
|
||||
adapter: root.adapter;
|
||||
backend: root.backend;
|
||||
layout-class: root.layout-class;
|
||||
fps: root.fps;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
// The canvas: compute output composited directly. No CPU
|
||||
// round-trip anywhere in this path (ARCH §6.1).
|
||||
canvas-area := Rectangle {
|
||||
horizontal-stretch: 1;
|
||||
background: Theme.ground;
|
||||
clip: true;
|
||||
|
||||
Image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
source: root.canvas;
|
||||
image-fit: contain;
|
||||
}
|
||||
|
||||
// Report size changes so the render target can be resized to
|
||||
// match. Width and height are tracked separately because
|
||||
// Slint has no single "geometry changed" hook.
|
||||
property <int> px-w: Math.round(self.width / 1px);
|
||||
property <int> px-h: Math.round(self.height / 1px);
|
||||
changed px-w => { root.canvas-resized(self.px-w, self.px-h); }
|
||||
changed px-h => { root.canvas-resized(self.px-w, self.px-h); }
|
||||
}
|
||||
|
||||
// Always instantiated, width collapsed to zero in compact mode.
|
||||
// A conditional `if` here creates a binding loop — the layout
|
||||
// depends on `expanded`, which derives from the window width,
|
||||
// which the layout then influences. Slint flags it, and it can
|
||||
// panic at runtime.
|
||||
SidePanel {
|
||||
width: root.expanded ? 260px : 0px;
|
||||
visible: root.expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user