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:
2026-08-09 07:42:05 +02:00
commit 82a5e21ec6
25 changed files with 10707 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
# DarkRoom — reproducible Android build environment
#
# Pins the entire toolchain: JDK, Android SDK, NDK, Rust, and the four Android
# targets. Both CI and local builds use this image, so "works on my machine"
# and "works in CI" are the same machine.
#
# Build: podman build -t darkroom-android:latest docker/android
# Use: ./docker/android/build.sh cargo ndk -t arm64-v8a build --release
FROM docker.io/library/debian:bookworm-slim
# ---------------------------------------------------------------------------
# Versions — pinned deliberately. Bumping any of these is a reviewable change,
# not something that drifts underneath the build.
# ---------------------------------------------------------------------------
ARG JDK_VERSION=17
# Compile SDK / target API.
ARG ANDROID_API=36
# Minimum supported API — the level native code links against (NFR-COMPAT-1).
# 28 (Android 9) matches the floor where Vulkan support is dependable.
# cargo-ndk otherwise defaults to 21, which is far below what this app needs.
ARG MIN_API=28
ARG BUILD_TOOLS=36.0.0
ARG NDK_VERSION=27.2.12479018
ARG CMDLINE_TOOLS=13114758
# Must satisfy cargo-ndk's MSRV (4.1.x needs >= 1.86) as well as our own crates.
ARG RUST_VERSION=1.92.0
# JDK 17, not the host's 25: the Android Gradle Plugin supports 17 as its
# stable target, and newer JDKs regularly break Gradle in ways that cost more
# time than they save.
ENV DEBIAN_FRONTEND=noninteractive \
ANDROID_HOME=/opt/android-sdk \
ANDROID_SDK_ROOT=/opt/android-sdk \
JAVA_HOME=/usr/lib/jvm/java-${JDK_VERSION}-openjdk-amd64 \
CARGO_HOME=/opt/cargo \
RUSTUP_HOME=/opt/rustup \
PATH=/opt/cargo/bin:/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/platform-tools:$PATH
# ---------------------------------------------------------------------------
# System packages
# ---------------------------------------------------------------------------
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl unzip git \
openjdk-${JDK_VERSION}-jdk-headless \
# Slint / winit build-time needs
pkg-config libfontconfig1-dev \
# native deps that may need building for host-side tooling
build-essential cmake python3 \
&& rm -rf /var/lib/apt/lists/*
# ---------------------------------------------------------------------------
# Android SDK + NDK
# ---------------------------------------------------------------------------
RUN mkdir -p ${ANDROID_HOME}/cmdline-tools \
&& curl -fsSL -o /tmp/tools.zip \
"https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS}_latest.zip" \
&& unzip -q /tmp/tools.zip -d ${ANDROID_HOME}/cmdline-tools \
&& mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \
&& rm /tmp/tools.zip
RUN yes | sdkmanager --licenses > /dev/null 2>&1 || true \
&& sdkmanager --install \
"platform-tools" \
"platforms;android-${ANDROID_API}" \
"build-tools;${BUILD_TOOLS}" \
"ndk;${NDK_VERSION}" \
> /dev/null
ENV ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${NDK_VERSION} \
ANDROID_NDK_ROOT=${ANDROID_HOME}/ndk/${NDK_VERSION}
# ---------------------------------------------------------------------------
# Rust + Android targets
#
# All four ABIs. arm64-v8a covers essentially every current device; the others
# exist so an ABI-specific build break is caught here rather than at release.
# ---------------------------------------------------------------------------
RUN curl -fsSL https://sh.rustup.rs | sh -s -- \
-y --no-modify-path --profile minimal --default-toolchain ${RUST_VERSION} \
&& rustup target add \
aarch64-linux-android \
armv7-linux-androideabi \
x86_64-linux-android \
i686-linux-android \
&& rustup component add rustfmt clippy \
&& cargo install cargo-ndk --locked \
&& chmod -R a+rwX ${CARGO_HOME} ${RUSTUP_HOME}
# ---------------------------------------------------------------------------
# Linker configuration
#
# cargo-ndk normally handles this, but setting it explicitly means plain
# `cargo build --target …` works too, which matters for tooling that shells
# out to cargo directly (rust-analyzer, cargo-metadata).
# ---------------------------------------------------------------------------
ENV NDK_BIN=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin
# Linkers target MIN_API, not ANDROID_API — the binary must run on the oldest
# supported device, while the SDK compiles against the newest.
ENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${NDK_BIN}/aarch64-linux-android${MIN_API}-clang \
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=${NDK_BIN}/armv7a-linux-androideabi${MIN_API}-clang \
CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=${NDK_BIN}/x86_64-linux-android${MIN_API}-clang \
CARGO_TARGET_I686_LINUX_ANDROID_LINKER=${NDK_BIN}/i686-linux-android${MIN_API}-clang
# cargo-ndk reads this; without it it defaults to API 21.
ENV CARGO_NDK_PLATFORM=${MIN_API} \
ANDROID_PLATFORM=${MIN_API}
# Shared cargo registry cache — bind-mount over this to persist across runs.
VOLUME ["/opt/cargo/registry"]
WORKDIR /work
# Rootless podman maps the host user into the container, so the image must not
# assume a fixed uid. Keep world-writable toolchain dirs and let the caller
# pass --user.
RUN chmod -R a+rwX ${ANDROID_HOME}
CMD ["/bin/bash"]