Headless pictures draw on the GPU, with the viewport's pipelines (CPU rasterizer as last tier) (#1555)

Thanks @jyviko.
This commit is contained in:
Irakli Kourtis authored and GitHub committed 2026-10-15 22:19:26 -04:00
1 parent 48322f6464
commit 1a0b33fcf3
54 files changed
+3525 -1998

No files matched your search

+24 -14
View File
@@ -1,7 +1,9 @@
//! GPU evaluation of display tessellation plans: the surface point and normal at every
//! vertex's parameters, in a compute shader (`tess.wgsl`), the GPU twin of
//! [`TessPlan::evaluate`]. Shared edge nodes keep their exact positions, so faces still meet
//! exactly; evaluation runs in f32.
//! SolidCraft's GPU work: the viewport's render pipelines ([`view`]), pictures drawn with them
//! offscreen ([`capture`]), and display tessellation plans evaluated in a compute shader.
//!
//! Plan evaluation: the surface point and normal at every vertex's parameters, in a compute
//! shader (`tess.wgsl`), the GPU twin of [`TessPlan::evaluate`]. Shared edge nodes keep their
//! exact positions, so faces still meet exactly; evaluation runs in f32.
//!
//! Use [`TessGpu::new`] with the application's device, or [`TessGpu::headless`] (native only)
//! to open one without a window, as the CLI and tests do. A viewport draws a plan straight from
@@ -17,6 +19,12 @@ use solidcraft_geom::Vec3;
use solidcraft_geom::tess::{EvalCurve, EvalSurface, TessPlan};
use wgpu::util::DeviceExt;
pub mod capture;
mod pbr;
#[cfg(not(target_arch = "wasm32"))]
pub mod select;
pub mod view;
/// The tessellation compute shader (WGSL).
pub const SHADER: &str = include_str!("tess.wgsl");
@@ -30,17 +38,15 @@ fn popped(f: impl std::future::Future<Output = Option<wgpu::Error>>) -> Option<w
}
}
/// An adapter and device without a window (native only). The environment may pick the backend
/// and the D3D12 shader compiler (`WGPU_BACKEND`, `WGPU_DX12_COMPILER`).
/// An adapter and device without a window (native only): the adapter [`select::pick`] chooses
/// under the `SOLIDCRAFT_GPU` policy. The environment may narrow the backends and pick the D3D12
/// shader compiler (`WGPU_BACKEND`, `WGPU_DX12_COMPILER`).
#[cfg(not(target_arch = "wasm32"))]
fn open_headless() -> Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
let adapter = pollster::block_on(
instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, ..Default::default() }),
)
.map_err(|_| GpuError::NoAdapter)?;
let policy = select::Policy::from_env().map_err(GpuError::Policy)?;
let (adapter, _) = select::pick(policy).ok_or(GpuError::NoAdapter(policy.name()))?;
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some("solidcraft compute"),
label: Some("solidcraft headless"),
required_limits: wgpu::Limits::downlevel_defaults().using_resolution(adapter.limits()),
..Default::default()
}))
@@ -54,12 +60,16 @@ const WORKGROUP: u32 = 64;
#[derive(Debug, thiserror::Error)]
pub enum GpuError {
#[error("no GPU adapter is available")]
NoAdapter,
#[error("no GPU adapter qualifies under the `{0}` policy (SOLIDCRAFT_GPU); `solidcraft-cli gpu` lists the adapters this machine offers")]
NoAdapter(&'static str),
#[error("{0}")]
Policy(String),
#[error("the GPU device could not be opened: {0}")]
Device(String),
#[error("the GPU evaluation failed: {0}")]
Failed(String),
#[error("the GPU render failed: {0}")]
Render(String),
#[error("the GPU tessellation pipeline could not be created: {0}")]
Pipeline(String),
#[error("not supported yet: {0}")]