Reduce GPU memory usage (#7319)

This pull request decreases the size of each instance buffer and shares
instance buffers across windows.

Release Notes:

- Improved GPU memory usage.

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-02-03 05:27:08 -07:00 committed by GitHub
parent d08d4174a5
commit c906fd232d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 13 deletions

View file

@ -24,7 +24,7 @@ const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shader
#[cfg(feature = "runtime_shaders")] #[cfg(feature = "runtime_shaders")]
const SHADERS_SOURCE_FILE: &'static str = const SHADERS_SOURCE_FILE: &'static str =
include_str!(concat!(env!("OUT_DIR"), "/stitched_shaders.metal")); include_str!(concat!(env!("OUT_DIR"), "/stitched_shaders.metal"));
const INSTANCE_BUFFER_SIZE: usize = 32 * 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value (maybe even we could adjust dynamically...) const INSTANCE_BUFFER_SIZE: usize = 2 * 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value (maybe even we could adjust dynamically...)
pub(crate) struct MetalRenderer { pub(crate) struct MetalRenderer {
device: metal::Device, device: metal::Device,
@ -40,13 +40,13 @@ pub(crate) struct MetalRenderer {
surfaces_pipeline_state: metal::RenderPipelineState, surfaces_pipeline_state: metal::RenderPipelineState,
unit_vertices: metal::Buffer, unit_vertices: metal::Buffer,
#[allow(clippy::arc_with_non_send_sync)] #[allow(clippy::arc_with_non_send_sync)]
instance_buffers: Arc<Mutex<Vec<metal::Buffer>>>, instance_buffer_pool: Arc<Mutex<Vec<metal::Buffer>>>,
sprite_atlas: Arc<MetalAtlas>, sprite_atlas: Arc<MetalAtlas>,
core_video_texture_cache: CVMetalTextureCache, core_video_texture_cache: CVMetalTextureCache,
} }
impl MetalRenderer { impl MetalRenderer {
pub fn new(is_opaque: bool) -> Self { pub fn new(instance_buffer_pool: Arc<Mutex<Vec<metal::Buffer>>>) -> Self {
let device: metal::Device = if let Some(device) = metal::Device::system_default() { let device: metal::Device = if let Some(device) = metal::Device::system_default() {
device device
} else { } else {
@ -58,7 +58,7 @@ impl MetalRenderer {
layer.set_device(&device); layer.set_device(&device);
layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
layer.set_presents_with_transaction(true); layer.set_presents_with_transaction(true);
layer.set_opaque(is_opaque); layer.set_opaque(true);
unsafe { unsafe {
let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO]; let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES]; let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
@ -181,7 +181,7 @@ impl MetalRenderer {
polychrome_sprites_pipeline_state, polychrome_sprites_pipeline_state,
surfaces_pipeline_state, surfaces_pipeline_state,
unit_vertices, unit_vertices,
instance_buffers: Arc::default(), instance_buffer_pool,
sprite_atlas, sprite_atlas,
core_video_texture_cache, core_video_texture_cache,
} }
@ -211,7 +211,7 @@ impl MetalRenderer {
); );
return; return;
}; };
let mut instance_buffer = self.instance_buffers.lock().pop().unwrap_or_else(|| { let mut instance_buffer = self.instance_buffer_pool.lock().pop().unwrap_or_else(|| {
self.device.new_buffer( self.device.new_buffer(
INSTANCE_BUFFER_SIZE as u64, INSTANCE_BUFFER_SIZE as u64,
MTLResourceOptions::StorageModeManaged, MTLResourceOptions::StorageModeManaged,
@ -227,7 +227,8 @@ impl MetalRenderer {
&mut instance_offset, &mut instance_offset,
command_buffer, command_buffer,
) else { ) else {
panic!("failed to rasterize {} paths", scene.paths().len()); log::error!("failed to rasterize {} paths", scene.paths().len());
return;
}; };
let render_pass_descriptor = metal::RenderPassDescriptor::new(); let render_pass_descriptor = metal::RenderPassDescriptor::new();
@ -314,7 +315,7 @@ impl MetalRenderer {
}; };
if !ok { if !ok {
panic!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces", log::error!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
scene.paths.len(), scene.paths.len(),
scene.shadows.len(), scene.shadows.len(),
scene.quads.len(), scene.quads.len(),
@ -322,7 +323,8 @@ impl MetalRenderer {
scene.monochrome_sprites.len(), scene.monochrome_sprites.len(),
scene.polychrome_sprites.len(), scene.polychrome_sprites.len(),
scene.surfaces.len(), scene.surfaces.len(),
) );
break;
} }
} }
@ -333,11 +335,11 @@ impl MetalRenderer {
length: instance_offset as NSUInteger, length: instance_offset as NSUInteger,
}); });
let instance_buffers = self.instance_buffers.clone(); let instance_buffer_pool = self.instance_buffer_pool.clone();
let instance_buffer = Cell::new(Some(instance_buffer)); let instance_buffer = Cell::new(Some(instance_buffer));
let block = ConcreteBlock::new(move |_| { let block = ConcreteBlock::new(move |_| {
if let Some(instance_buffer) = instance_buffer.take() { if let Some(instance_buffer) = instance_buffer.take() {
instance_buffers.lock().push(instance_buffer); instance_buffer_pool.lock().push(instance_buffer);
} }
}); });
let block = block.copy(); let block = block.copy();

View file

@ -146,6 +146,7 @@ pub(crate) struct MacPlatformState {
foreground_executor: ForegroundExecutor, foreground_executor: ForegroundExecutor,
text_system: Arc<MacTextSystem>, text_system: Arc<MacTextSystem>,
display_linker: MacDisplayLinker, display_linker: MacDisplayLinker,
instance_buffer_pool: Arc<Mutex<Vec<metal::Buffer>>>,
pasteboard: id, pasteboard: id,
text_hash_pasteboard_type: id, text_hash_pasteboard_type: id,
metadata_pasteboard_type: id, metadata_pasteboard_type: id,
@ -176,6 +177,7 @@ impl MacPlatform {
foreground_executor: ForegroundExecutor::new(dispatcher), foreground_executor: ForegroundExecutor::new(dispatcher),
text_system: Arc::new(MacTextSystem::new()), text_system: Arc::new(MacTextSystem::new()),
display_linker: MacDisplayLinker::new(), display_linker: MacDisplayLinker::new(),
instance_buffer_pool: Arc::default(),
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) }, pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") }, text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
metadata_pasteboard_type: unsafe { ns_string("zed-metadata") }, metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
@ -494,7 +496,13 @@ impl Platform for MacPlatform {
handle: AnyWindowHandle, handle: AnyWindowHandle,
options: WindowOptions, options: WindowOptions,
) -> Box<dyn PlatformWindow> { ) -> Box<dyn PlatformWindow> {
Box::new(MacWindow::open(handle, options, self.foreground_executor())) let instance_buffer_pool = self.0.lock().instance_buffer_pool.clone();
Box::new(MacWindow::open(
handle,
options,
self.foreground_executor(),
instance_buffer_pool,
))
} }
fn set_display_link_output_callback( fn set_display_link_output_callback(

View file

@ -459,6 +459,7 @@ impl MacWindow {
handle: AnyWindowHandle, handle: AnyWindowHandle,
options: WindowOptions, options: WindowOptions,
executor: ForegroundExecutor, executor: ForegroundExecutor,
instance_buffer_pool: Arc<Mutex<Vec<metal::Buffer>>>,
) -> Self { ) -> Self {
unsafe { unsafe {
let pool = NSAutoreleasePool::new(nil); let pool = NSAutoreleasePool::new(nil);
@ -535,7 +536,7 @@ impl MacWindow {
native_window, native_window,
native_view: NonNull::new_unchecked(native_view as *mut _), native_view: NonNull::new_unchecked(native_view as *mut _),
display_link, display_link,
renderer: MetalRenderer::new(true), renderer: MetalRenderer::new(instance_buffer_pool),
kind: options.kind, kind: options.kind,
request_frame_callback: None, request_frame_callback: None,
event_callback: None, event_callback: None,