GPU: devices favour memory use, so Purge and Close give video memory back

Both devices EffectCraft creates now ask for `MemoryHints::MemoryUsage`:

- the window device (`apps/effectcraft/src/main.rs`, through a new `device_descriptor` helper);
- the headless compositor device (`crates/gpu/src/context.rs`).

The limits, labels, features and error paths are unchanged. The devices tests create stay as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
arnav-hiwarkar
2026-10-21 21:45:28 +05:30
co-authored by Claude Opus 5.5
parent 9db7f3b217
commit 2b3732153b
3 changed files with 40 additions and 9 deletions
+23 -5
View File
@@ -371,15 +371,22 @@ fn wgpu_options(backends: Option<eframe::wgpu::Backends>) -> 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() {
+7 -4
View File
@@ -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}"),
})?;
+10
View File
@@ -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);
}