diff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs index a1fb6e4a..2bc3265d 100644 --- a/apps/effectcraft/src/main.rs +++ b/apps/effectcraft/src/main.rs @@ -371,15 +371,22 @@ fn wgpu_options(backends: Option) -> eframe::WgpuConfigu // Iris Pro: the device is lost and the window never opens. // `WGPU_VALIDATION_INDIRECT_CALL=1` turns the check back on. setup.instance_descriptor.flags = setup.instance_descriptor.flags.difference(eframe::wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL).with_env(); - setup.device_descriptor = std::sync::Arc::new(|adapter| eframe::wgpu::DeviceDescriptor { - label: Some("EffectCraft device"), - required_limits: device_limits(adapter.limits()), - ..Default::default() - }); + setup.device_descriptor = std::sync::Arc::new(|adapter| device_descriptor(adapter.limits())); } config } +/// The window device's descriptor: the adapter's limits ([`device_limits`]) and `MemoryUsage`, +/// so memory the compositor frees on the shared device goes back to the driver. +fn device_descriptor(adapter: eframe::wgpu::Limits) -> eframe::wgpu::DeviceDescriptor<'static> { + eframe::wgpu::DeviceDescriptor { + label: Some("EffectCraft device"), + required_limits: device_limits(adapter), + memory_hints: eframe::wgpu::MemoryHints::MemoryUsage, + ..Default::default() + } +} + /// The limits to request from an adapter that offers `adapter`: all of them, with textures /// capped at 16384 px like the headless renderer's device. fn device_limits(adapter: eframe::wgpu::Limits) -> eframe::wgpu::Limits { @@ -539,6 +546,17 @@ mod tests { assert_eq!(super::device_limits(big).max_texture_dimension_2d, 16384); } + /// The window device asks for `MemoryUsage`: the adapter's limits, unchanged. + #[test] + fn the_window_device_favours_memory_usage() { + use eframe::wgpu::Limits; + let big = Limits { max_texture_dimension_2d: 32768, ..Limits::default() }; + let d = super::device_descriptor(big); + assert!(matches!(d.memory_hints, eframe::wgpu::MemoryHints::MemoryUsage)); + assert_eq!(d.required_limits.max_texture_dimension_2d, 16384); + assert_eq!(d.label, Some("EffectCraft device")); + } + /// A positional .ecprojx is a project file, not an unsupported media import (#528). #[test] fn launch_classifies_xml_and_json_projects_as_projects() { diff --git a/crates/gpu/src/context.rs b/crates/gpu/src/context.rs index 7229aa5a..2fbf86bc 100644 --- a/crates/gpu/src/context.rs +++ b/crates/gpu/src/context.rs @@ -462,6 +462,12 @@ pub(crate) fn buffer_init(device: &wgpu::Device, queue: &wgpu::Queue, label: Opt buffer } +/// The descriptor for a device the compositor owns: `MemoryUsage` so freed textures give their +/// allocator blocks back instead of a few long-lived objects pinning large ones. +pub(crate) fn device_descriptor(required_limits: wgpu::Limits) -> wgpu::DeviceDescriptor<'static> { + wgpu::DeviceDescriptor { label: Some("effectcraft gpu"), required_limits, memory_hints: wgpu::MemoryHints::MemoryUsage, ..Default::default() } +} + impl GpuContext { /// [`buffer_init`] on this context's device. pub(crate) fn buffer_init(&self, label: Option<&str>, contents: &[u8], usage: wgpu::BufferUsages) -> wgpu::Buffer { @@ -870,10 +876,7 @@ impl GpuContext { max_buffer_size: limits.max_buffer_size, ..wgpu::Limits::default() }; - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor { label: Some("effectcraft gpu"), required_limits, ..Default::default() }) - .await - .map_err(|e| match by_name { + let (device, queue) = adapter.request_device(&device_descriptor(required_limits)).await.map_err(|e| match by_name { true => format!("no device on {} (picked by WGPU_ADAPTER_NAME): {e}", adapter.get_info().name), false => format!("no device: {e}"), })?; diff --git a/crates/gpu/src/tests.rs b/crates/gpu/src/tests.rs index 24f8c961..b466e539 100644 --- a/crates/gpu/src/tests.rs +++ b/crates/gpu/src/tests.rs @@ -1071,3 +1071,13 @@ fn upload_cache_drops_textures_of_freed_buffers() { let kept = g.ctx.upload_entries(); assert!(kept <= before + 1, "the upload cache kept {kept} textures of freed buffers (had {before})"); } + +/// The device the compositor owns asks for `MemoryUsage` so freed textures give +/// their allocator blocks back, while its label and limits stay as they were. +#[test] +fn owned_devices_favour_memory_usage() { + let d = crate::context::device_descriptor(wgpu::Limits::default()); + assert!(matches!(d.memory_hints, wgpu::MemoryHints::MemoryUsage)); + assert_eq!(d.label, Some("effectcraft gpu")); + assert_eq!(d.required_limits.max_texture_dimension_2d, wgpu::Limits::default().max_texture_dimension_2d); +}