Support Vello CPU fallback on systems with older GPUs
This commit is contained in:
parent
7a3f522735
commit
ce7ed2586f
|
|
@ -37,15 +37,13 @@ pub struct BackendContext<'a> {
|
|||
/// Audio engine controller (optional - may not be initialized)
|
||||
pub audio_controller: Option<&'a mut daw_backend::EngineController>,
|
||||
|
||||
/// Mapping from document layer UUIDs to backend track IDs
|
||||
/// Mapping from all document layer/clip/group UUIDs to backend track IDs.
|
||||
/// Covers audio layers, MIDI layers, group layers, and vector clip metatracks.
|
||||
pub layer_to_track_map: &'a HashMap<Uuid, daw_backend::TrackId>,
|
||||
|
||||
/// Mapping from document clip instance UUIDs to backend clip instance IDs
|
||||
pub clip_instance_to_backend_map: &'a mut HashMap<Uuid, BackendClipInstanceId>,
|
||||
|
||||
/// Mapping from movie clip UUIDs to backend metatrack (group track) TrackIds
|
||||
pub clip_to_metatrack_map: &'a HashMap<Uuid, daw_backend::TrackId>,
|
||||
|
||||
// Future: pub video_controller: Option<&'a mut VideoController>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ impl Action for MoveClipInstancesAction {
|
|||
for (instance_id, _old_start, new_start) in moves {
|
||||
if let Some(instance) = vl.clip_instances.iter().find(|ci| ci.id == *instance_id) {
|
||||
// Check if this clip has a metatrack
|
||||
if let Some(&metatrack_id) = backend.clip_to_metatrack_map.get(&instance.clip_id) {
|
||||
if let Some(&metatrack_id) = backend.layer_to_track_map.get(&instance.clip_id) {
|
||||
controller.set_offset(metatrack_id, *new_start);
|
||||
controller.set_trim_start(metatrack_id, instance.trim_start);
|
||||
controller.set_trim_end(metatrack_id, instance.trim_end);
|
||||
|
|
@ -287,7 +287,7 @@ impl Action for MoveClipInstancesAction {
|
|||
if let AnyLayer::Vector(vl) = layer {
|
||||
for (instance_id, old_start, _new_start) in moves {
|
||||
if let Some(instance) = vl.clip_instances.iter().find(|ci| ci.id == *instance_id) {
|
||||
if let Some(&metatrack_id) = backend.clip_to_metatrack_map.get(&instance.clip_id) {
|
||||
if let Some(&metatrack_id) = backend.layer_to_track_map.get(&instance.clip_id) {
|
||||
controller.set_offset(metatrack_id, *old_start);
|
||||
controller.set_trim_start(metatrack_id, instance.trim_start);
|
||||
controller.set_trim_end(metatrack_id, instance.trim_end);
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ impl Action for TrimClipInstancesAction {
|
|||
if let AnyLayer::Vector(vl) = layer {
|
||||
for (instance_id, _trim_type, _old, _new) in trims {
|
||||
if let Some(instance) = vl.clip_instances.iter().find(|ci| ci.id == *instance_id) {
|
||||
if let Some(&metatrack_id) = backend.clip_to_metatrack_map.get(&instance.clip_id) {
|
||||
if let Some(&metatrack_id) = backend.layer_to_track_map.get(&instance.clip_id) {
|
||||
// Instance already has new values after execute()
|
||||
controller.set_offset(metatrack_id, instance.timeline_start);
|
||||
controller.set_trim_start(metatrack_id, instance.trim_start);
|
||||
|
|
@ -459,7 +459,7 @@ impl Action for TrimClipInstancesAction {
|
|||
if let AnyLayer::Vector(vl) = layer {
|
||||
for (instance_id, _trim_type, _old, _new) in trims {
|
||||
if let Some(instance) = vl.clip_instances.iter().find(|ci| ci.id == *instance_id) {
|
||||
if let Some(&metatrack_id) = backend.clip_to_metatrack_map.get(&instance.clip_id) {
|
||||
if let Some(&metatrack_id) = backend.layer_to_track_map.get(&instance.clip_id) {
|
||||
// Instance already has old values after rollback()
|
||||
controller.set_offset(metatrack_id, instance.timeline_start);
|
||||
controller.set_trim_start(metatrack_id, instance.trim_start);
|
||||
|
|
|
|||
|
|
@ -61,9 +61,6 @@ pub struct SerializedAudioBackend {
|
|||
#[serde(default)]
|
||||
pub layer_to_track_map: std::collections::HashMap<uuid::Uuid, u32>,
|
||||
|
||||
/// Mapping from movie clip UUIDs to backend metatrack (group track) TrackIds
|
||||
#[serde(default)]
|
||||
pub clip_to_metatrack_map: std::collections::HashMap<uuid::Uuid, u32>,
|
||||
}
|
||||
|
||||
/// Settings for saving a project
|
||||
|
|
@ -100,9 +97,6 @@ pub struct LoadedProject {
|
|||
/// Mapping from UI layer UUIDs to backend TrackIds (empty for old files)
|
||||
pub layer_to_track_map: std::collections::HashMap<uuid::Uuid, u32>,
|
||||
|
||||
/// Mapping from movie clip UUIDs to backend metatrack TrackIds (empty for old files)
|
||||
pub clip_to_metatrack_map: std::collections::HashMap<uuid::Uuid, u32>,
|
||||
|
||||
/// Loaded audio pool entries
|
||||
pub audio_pool_entries: Vec<AudioPoolEntry>,
|
||||
|
||||
|
|
@ -154,7 +148,6 @@ pub fn save_beam(
|
|||
audio_project: &mut AudioProject,
|
||||
audio_pool_entries: Vec<AudioPoolEntry>,
|
||||
layer_to_track_map: &std::collections::HashMap<uuid::Uuid, u32>,
|
||||
clip_to_metatrack_map: &std::collections::HashMap<uuid::Uuid, u32>,
|
||||
_settings: &SaveSettings,
|
||||
) -> Result<(), String> {
|
||||
let fn_start = std::time::Instant::now();
|
||||
|
|
@ -414,7 +407,6 @@ pub fn save_beam(
|
|||
project: audio_project.clone(),
|
||||
audio_pool_entries: modified_entries,
|
||||
layer_to_track_map: layer_to_track_map.clone(),
|
||||
clip_to_metatrack_map: clip_to_metatrack_map.clone(),
|
||||
},
|
||||
};
|
||||
eprintln!("📊 [SAVE_BEAM] Step 5: Build BeamProject structure took {:.2}ms", step5_start.elapsed().as_secs_f64() * 1000.0);
|
||||
|
|
@ -502,7 +494,6 @@ pub fn load_beam(path: &Path) -> Result<LoadedProject, String> {
|
|||
let mut audio_project = beam_project.audio_backend.project;
|
||||
let audio_pool_entries = beam_project.audio_backend.audio_pool_entries;
|
||||
let layer_to_track_map = beam_project.audio_backend.layer_to_track_map;
|
||||
let clip_to_metatrack_map = beam_project.audio_backend.clip_to_metatrack_map;
|
||||
eprintln!("📊 [LOAD_BEAM] Step 5: Extract document and audio state took {:.2}ms", step5_start.elapsed().as_secs_f64() * 1000.0);
|
||||
|
||||
// 6. Rebuild AudioGraphs from presets
|
||||
|
|
@ -679,7 +670,6 @@ pub fn load_beam(path: &Path) -> Result<LoadedProject, String> {
|
|||
document,
|
||||
audio_project,
|
||||
layer_to_track_map,
|
||||
clip_to_metatrack_map,
|
||||
audio_pool_entries: restored_entries,
|
||||
missing_files,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ struct Args {
|
|||
/// Use dark theme
|
||||
#[arg(long, conflicts_with = "light")]
|
||||
dark: bool,
|
||||
|
||||
/// Force Vello to use its CPU renderer instead of the GPU.
|
||||
/// Useful for testing the CPU fallback path or working around GPU driver issues.
|
||||
#[arg(long)]
|
||||
cpu_renderer: bool,
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result {
|
||||
|
|
@ -89,6 +94,11 @@ fn main() -> eframe::Result {
|
|||
// Parse command line arguments
|
||||
let args = Args::parse();
|
||||
|
||||
if args.cpu_renderer {
|
||||
panes::stage::FORCE_CPU_RENDERER.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
println!("⚠️ CPU renderer forced via --cpu-renderer");
|
||||
}
|
||||
|
||||
// Load config to get theme preference
|
||||
let config = AppConfig::load();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ use lightningbeam_core::layer::{AnyLayer, AudioLayer};
|
|||
use lightningbeam_core::renderer::RenderedLayerType;
|
||||
use super::{DragClipType, NodePath, PaneRenderer, SharedPaneState};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// When set to `true` (via `--cpu-renderer`), forces Vello to use its CPU
|
||||
/// rendering path regardless of GPU capability.
|
||||
pub static FORCE_CPU_RENDERER: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Enable HDR compositing pipeline (per-layer rendering with proper opacity)
|
||||
/// Set to true to use the new pipeline, false for legacy single-scene rendering
|
||||
|
|
@ -63,15 +68,51 @@ pub struct VelloResourcesMap {
|
|||
|
||||
impl SharedVelloResources {
|
||||
pub fn new(device: &wgpu::Device, video_manager: std::sync::Arc<std::sync::Mutex<lightningbeam_core::video::VideoManager>>, target_format: wgpu::TextureFormat) -> Result<Self, String> {
|
||||
let renderer = vello::Renderer::new(
|
||||
device,
|
||||
vello::RendererOptions {
|
||||
use_cpu: false,
|
||||
antialiasing_support: vello::AaSupport::all(),
|
||||
num_init_threads: std::num::NonZeroUsize::new(1),
|
||||
pipeline_cache: None,
|
||||
},
|
||||
).map_err(|e| format!("Failed to create Vello renderer: {}", e))?;
|
||||
let use_cpu = FORCE_CPU_RENDERER.load(Ordering::Relaxed);
|
||||
|
||||
// wgpu panics (rather than returning Err) when shader validation fails, so we
|
||||
// catch panics here and fall back to Vello's CPU renderer. This commonly
|
||||
// happens on old GPUs lacking SHADER_FLOAT16_IN_FLOAT32 (required by Vello's
|
||||
// flatten shader via unpack2x16float). The CPU path uses pre-compiled Rust
|
||||
// implementations of the same compute shaders, so no GPU shader compilation
|
||||
// occurs and the capability check is bypassed entirely.
|
||||
let gpu_result = if use_cpu {
|
||||
// Skip GPU attempt entirely when forced via --cpu-renderer.
|
||||
Err(Box::new("cpu-renderer flag set") as Box<dyn std::any::Any + Send>)
|
||||
} else {
|
||||
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
vello::Renderer::new(
|
||||
device,
|
||||
vello::RendererOptions {
|
||||
use_cpu: false,
|
||||
antialiasing_support: vello::AaSupport::all(),
|
||||
num_init_threads: std::num::NonZeroUsize::new(1),
|
||||
pipeline_cache: None,
|
||||
},
|
||||
)
|
||||
}))
|
||||
};
|
||||
let renderer = match gpu_result {
|
||||
Ok(Ok(r)) => r,
|
||||
Ok(Err(e)) => return Err(format!("Failed to create Vello renderer: {e}")),
|
||||
Err(_) => {
|
||||
if !use_cpu {
|
||||
eprintln!(
|
||||
"WARNING: GPU Vello renderer failed to initialise (missing shader \
|
||||
capability). Falling back to CPU renderer — performance may be reduced."
|
||||
);
|
||||
}
|
||||
vello::Renderer::new(
|
||||
device,
|
||||
vello::RendererOptions {
|
||||
use_cpu: true,
|
||||
antialiasing_support: vello::AaSupport::all(),
|
||||
num_init_threads: std::num::NonZeroUsize::new(1),
|
||||
pipeline_cache: None,
|
||||
},
|
||||
).map_err(|e| format!("CPU fallback renderer also failed: {e}"))?
|
||||
}
|
||||
};
|
||||
|
||||
// Create blit shader for rendering texture to screen
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
|
|
@ -480,7 +521,8 @@ impl egui_wgpu::CallbackTrait for VelloCallback {
|
|||
// Initialize shared resources if not yet created (only happens once for first Stage pane)
|
||||
if map.shared.is_none() {
|
||||
map.shared = Some(Arc::new(
|
||||
SharedVelloResources::new(device, self.ctx.video_manager.clone(), self.ctx.target_format).expect("Failed to initialize shared Vello resources")
|
||||
SharedVelloResources::new(device, self.ctx.video_manager.clone(), self.ctx.target_format)
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue