Use command_buffer.wait_until_scheduled
in metal renderer (#7283)
This commit goes back to using `wait_until_scheduled` as opposed to `wait_until_completed`. What this means, however, is that another draw could take place before the previous one finished. When that happens we don't want to reuse the same instance buffer because the GPU is actively reading from it, so we use a pool instead. Release Notes: - Fixed a bug that caused inconsistent frame rate when scrolling on certain hardware. --------- Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: Antonio <antonio@zed.dev>
This commit is contained in:
parent
cf4f3ed79a
commit
3d76ed96f5
1 changed files with 134 additions and 85 deletions
|
@ -3,6 +3,7 @@ use crate::{
|
|||
Hsla, MetalAtlas, MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch,
|
||||
Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
base::{NO, YES},
|
||||
foundation::NSUInteger,
|
||||
|
@ -15,7 +16,13 @@ use media::core_video::CVMetalTextureCache;
|
|||
use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
|
||||
use objc::{self, msg_send, sel, sel_impl};
|
||||
use smallvec::SmallVec;
|
||||
use std::{ffi::c_void, mem, ptr, sync::Arc};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
ffi::c_void,
|
||||
mem, ptr,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "runtime_shaders"))]
|
||||
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
|
||||
|
@ -25,6 +32,7 @@ const SHADERS_SOURCE_FILE: &'static str =
|
|||
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...)
|
||||
|
||||
pub(crate) struct MetalRenderer {
|
||||
device: metal::Device,
|
||||
layer: metal::MetalLayer,
|
||||
command_queue: CommandQueue,
|
||||
paths_rasterization_pipeline_state: metal::RenderPipelineState,
|
||||
|
@ -36,7 +44,7 @@ pub(crate) struct MetalRenderer {
|
|||
polychrome_sprites_pipeline_state: metal::RenderPipelineState,
|
||||
surfaces_pipeline_state: metal::RenderPipelineState,
|
||||
unit_vertices: metal::Buffer,
|
||||
instances: metal::Buffer,
|
||||
instance_buffers: Rc<RefCell<Vec<metal::Buffer>>>,
|
||||
sprite_atlas: Arc<MetalAtlas>,
|
||||
core_video_texture_cache: CVMetalTextureCache,
|
||||
}
|
||||
|
@ -93,10 +101,6 @@ impl MetalRenderer {
|
|||
mem::size_of_val(&unit_vertices) as u64,
|
||||
MTLResourceOptions::StorageModeManaged,
|
||||
);
|
||||
let instances = device.new_buffer(
|
||||
INSTANCE_BUFFER_SIZE as u64,
|
||||
MTLResourceOptions::StorageModeManaged,
|
||||
);
|
||||
|
||||
let paths_rasterization_pipeline_state = build_path_rasterization_pipeline_state(
|
||||
&device,
|
||||
|
@ -165,8 +169,11 @@ impl MetalRenderer {
|
|||
|
||||
let command_queue = device.new_command_queue();
|
||||
let sprite_atlas = Arc::new(MetalAtlas::new(device.clone()));
|
||||
let core_video_texture_cache =
|
||||
unsafe { CVMetalTextureCache::new(device.as_ptr()).unwrap() };
|
||||
|
||||
Self {
|
||||
device,
|
||||
layer,
|
||||
command_queue,
|
||||
paths_rasterization_pipeline_state,
|
||||
|
@ -178,9 +185,9 @@ impl MetalRenderer {
|
|||
polychrome_sprites_pipeline_state,
|
||||
surfaces_pipeline_state,
|
||||
unit_vertices,
|
||||
instances,
|
||||
instance_buffers: Rc::default(),
|
||||
sprite_atlas,
|
||||
core_video_texture_cache: unsafe { CVMetalTextureCache::new(device.as_ptr()).unwrap() },
|
||||
core_video_texture_cache,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,13 +215,22 @@ impl MetalRenderer {
|
|||
);
|
||||
return;
|
||||
};
|
||||
let mut instance_buffer = self.instance_buffers.borrow_mut().pop().unwrap_or_else(|| {
|
||||
self.device.new_buffer(
|
||||
INSTANCE_BUFFER_SIZE as u64,
|
||||
MTLResourceOptions::StorageModeManaged,
|
||||
)
|
||||
});
|
||||
let command_queue = self.command_queue.clone();
|
||||
let command_buffer = command_queue.new_command_buffer();
|
||||
let mut instance_offset = 0;
|
||||
|
||||
let Some(path_tiles) =
|
||||
self.rasterize_paths(scene.paths(), &mut instance_offset, command_buffer)
|
||||
else {
|
||||
let Some(path_tiles) = self.rasterize_paths(
|
||||
scene.paths(),
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
command_buffer,
|
||||
) else {
|
||||
panic!("failed to rasterize {} paths", scene.paths().len());
|
||||
};
|
||||
|
||||
|
@ -243,22 +259,29 @@ impl MetalRenderer {
|
|||
let ok = match batch {
|
||||
PrimitiveBatch::Shadows(shadows) => self.draw_shadows(
|
||||
shadows,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
),
|
||||
PrimitiveBatch::Quads(quads) => self.draw_quads(
|
||||
quads,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
),
|
||||
PrimitiveBatch::Quads(quads) => {
|
||||
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder)
|
||||
}
|
||||
PrimitiveBatch::Paths(paths) => self.draw_paths(
|
||||
paths,
|
||||
&path_tiles,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
),
|
||||
PrimitiveBatch::Underlines(underlines) => self.draw_underlines(
|
||||
underlines,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
|
@ -269,6 +292,7 @@ impl MetalRenderer {
|
|||
} => self.draw_monochrome_sprites(
|
||||
texture_id,
|
||||
sprites,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
|
@ -279,12 +303,14 @@ impl MetalRenderer {
|
|||
} => self.draw_polychrome_sprites(
|
||||
texture_id,
|
||||
sprites,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
),
|
||||
PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(
|
||||
surfaces,
|
||||
&mut instance_buffer,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
|
@ -306,22 +332,32 @@ impl MetalRenderer {
|
|||
|
||||
command_encoder.end_encoding();
|
||||
|
||||
self.instances.did_modify_range(NSRange {
|
||||
instance_buffer.did_modify_range(NSRange {
|
||||
location: 0,
|
||||
length: instance_offset as NSUInteger,
|
||||
});
|
||||
|
||||
let instance_buffers = self.instance_buffers.clone();
|
||||
let instance_buffer = Cell::new(Some(instance_buffer));
|
||||
let block = ConcreteBlock::new(move |_| {
|
||||
if let Some(instance_buffer) = instance_buffer.take() {
|
||||
instance_buffers.borrow_mut().push(instance_buffer);
|
||||
}
|
||||
});
|
||||
let block = block.copy();
|
||||
command_buffer.add_completed_handler(&block);
|
||||
command_buffer.commit();
|
||||
self.sprite_atlas.clear_textures(AtlasTextureKind::Path);
|
||||
|
||||
command_buffer.wait_until_completed();
|
||||
command_buffer.wait_until_scheduled();
|
||||
drawable.present();
|
||||
}
|
||||
|
||||
fn rasterize_paths(
|
||||
&mut self,
|
||||
paths: &[Path<ScaledPixels>],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
command_buffer: &metal::CommandBufferRef,
|
||||
) -> Option<HashMap<PathId, AtlasTile>> {
|
||||
let mut tiles = HashMap::default();
|
||||
|
@ -347,9 +383,9 @@ impl MetalRenderer {
|
|||
}
|
||||
|
||||
for (texture_id, vertices) in vertices_by_texture_id {
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
let vertices_bytes_len = mem::size_of_val(vertices.as_slice());
|
||||
let next_offset = *offset + vertices_bytes_len;
|
||||
let next_offset = *instance_offset + vertices_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return None;
|
||||
}
|
||||
|
@ -369,8 +405,8 @@ impl MetalRenderer {
|
|||
command_encoder.set_render_pipeline_state(&self.paths_rasterization_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
PathRasterizationInputIndex::Vertices as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
let texture_size = Size {
|
||||
width: DevicePixels::from(texture.width()),
|
||||
|
@ -382,7 +418,8 @@ impl MetalRenderer {
|
|||
&texture_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
let buffer_contents =
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
vertices.as_ptr() as *const u8,
|
||||
|
@ -397,7 +434,7 @@ impl MetalRenderer {
|
|||
vertices.len() as u64,
|
||||
);
|
||||
command_encoder.end_encoding();
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
}
|
||||
|
||||
Some(tiles)
|
||||
|
@ -406,14 +443,15 @@ impl MetalRenderer {
|
|||
fn draw_shadows(
|
||||
&mut self,
|
||||
shadows: &[Shadow],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
if shadows.is_empty() {
|
||||
return true;
|
||||
}
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.shadows_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
|
@ -423,13 +461,13 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
ShadowInputIndex::Shadows as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
ShadowInputIndex::Shadows as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_bytes(
|
||||
|
@ -439,9 +477,10 @@ impl MetalRenderer {
|
|||
);
|
||||
|
||||
let shadow_bytes_len = mem::size_of_val(shadows);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
let buffer_contents =
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
|
||||
let next_offset = *offset + shadow_bytes_len;
|
||||
let next_offset = *instance_offset + shadow_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
@ -460,21 +499,22 @@ impl MetalRenderer {
|
|||
6,
|
||||
shadows.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
true
|
||||
}
|
||||
|
||||
fn draw_quads(
|
||||
&mut self,
|
||||
quads: &[Quad],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
if quads.is_empty() {
|
||||
return true;
|
||||
}
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
|
@ -484,13 +524,13 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
QuadInputIndex::Quads as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
QuadInputIndex::Quads as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_bytes(
|
||||
|
@ -500,9 +540,10 @@ impl MetalRenderer {
|
|||
);
|
||||
|
||||
let quad_bytes_len = mem::size_of_val(quads);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
let buffer_contents =
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
|
||||
let next_offset = *offset + quad_bytes_len;
|
||||
let next_offset = *instance_offset + quad_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
@ -517,7 +558,7 @@ impl MetalRenderer {
|
|||
6,
|
||||
quads.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -525,7 +566,8 @@ impl MetalRenderer {
|
|||
&mut self,
|
||||
paths: &[Path<ScaledPixels>],
|
||||
tiles_by_path_id: &HashMap<PathId, AtlasTile>,
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
|
@ -573,7 +615,7 @@ impl MetalRenderer {
|
|||
if sprites.is_empty() {
|
||||
break;
|
||||
} else {
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
let texture_id = prev_texture_id.take().unwrap();
|
||||
let texture: metal::Texture = self.sprite_atlas.metal_texture(texture_id);
|
||||
let texture_size = size(
|
||||
|
@ -583,8 +625,8 @@ impl MetalRenderer {
|
|||
|
||||
command_encoder.set_vertex_buffer(
|
||||
SpriteInputIndex::Sprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_vertex_bytes(
|
||||
SpriteInputIndex::AtlasTextureSize as u64,
|
||||
|
@ -593,20 +635,20 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
SpriteInputIndex::Sprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder
|
||||
.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
|
||||
|
||||
let sprite_bytes_len = mem::size_of_val(sprites.as_slice());
|
||||
let next_offset = *offset + sprite_bytes_len;
|
||||
let next_offset = *instance_offset + sprite_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
||||
let buffer_contents =
|
||||
unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
|
@ -622,7 +664,7 @@ impl MetalRenderer {
|
|||
6,
|
||||
sprites.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
sprites.clear();
|
||||
}
|
||||
}
|
||||
|
@ -632,14 +674,15 @@ impl MetalRenderer {
|
|||
fn draw_underlines(
|
||||
&mut self,
|
||||
underlines: &[Underline],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
if underlines.is_empty() {
|
||||
return true;
|
||||
}
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.underlines_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
|
@ -649,13 +692,13 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
UnderlineInputIndex::Underlines as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
UnderlineInputIndex::Underlines as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_bytes(
|
||||
|
@ -665,9 +708,10 @@ impl MetalRenderer {
|
|||
);
|
||||
|
||||
let underline_bytes_len = mem::size_of_val(underlines);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
let buffer_contents =
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
|
||||
let next_offset = *offset + underline_bytes_len;
|
||||
let next_offset = *instance_offset + underline_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
@ -686,7 +730,7 @@ impl MetalRenderer {
|
|||
6,
|
||||
underlines.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -694,14 +738,15 @@ impl MetalRenderer {
|
|||
&mut self,
|
||||
texture_id: AtlasTextureId,
|
||||
sprites: &[MonochromeSprite],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
if sprites.is_empty() {
|
||||
return true;
|
||||
}
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
|
||||
let texture = self.sprite_atlas.metal_texture(texture_id);
|
||||
let texture_size = size(
|
||||
|
@ -716,8 +761,8 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
SpriteInputIndex::Sprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_vertex_bytes(
|
||||
SpriteInputIndex::ViewportSize as u64,
|
||||
|
@ -731,15 +776,16 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
SpriteInputIndex::Sprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
|
||||
|
||||
let sprite_bytes_len = mem::size_of_val(sprites);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
let buffer_contents =
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
|
||||
let next_offset = *offset + sprite_bytes_len;
|
||||
let next_offset = *instance_offset + sprite_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
@ -758,7 +804,7 @@ impl MetalRenderer {
|
|||
6,
|
||||
sprites.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -766,14 +812,15 @@ impl MetalRenderer {
|
|||
&mut self,
|
||||
texture_id: AtlasTextureId,
|
||||
sprites: &[PolychromeSprite],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
if sprites.is_empty() {
|
||||
return true;
|
||||
}
|
||||
align_offset(offset);
|
||||
align_offset(instance_offset);
|
||||
|
||||
let texture = self.sprite_atlas.metal_texture(texture_id);
|
||||
let texture_size = size(
|
||||
|
@ -788,8 +835,8 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
SpriteInputIndex::Sprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_vertex_bytes(
|
||||
SpriteInputIndex::ViewportSize as u64,
|
||||
|
@ -803,15 +850,16 @@ impl MetalRenderer {
|
|||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
SpriteInputIndex::Sprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
|
||||
|
||||
let sprite_bytes_len = mem::size_of_val(sprites);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
let buffer_contents =
|
||||
unsafe { (instance_buffer.contents() as *mut u8).add(*instance_offset) };
|
||||
|
||||
let next_offset = *offset + sprite_bytes_len;
|
||||
let next_offset = *instance_offset + sprite_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
@ -830,14 +878,15 @@ impl MetalRenderer {
|
|||
6,
|
||||
sprites.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
true
|
||||
}
|
||||
|
||||
fn draw_surfaces(
|
||||
&mut self,
|
||||
surfaces: &[Surface],
|
||||
offset: &mut usize,
|
||||
instance_buffer: &mut metal::Buffer,
|
||||
instance_offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) -> bool {
|
||||
|
@ -889,16 +938,16 @@ impl MetalRenderer {
|
|||
.unwrap()
|
||||
};
|
||||
|
||||
align_offset(offset);
|
||||
let next_offset = *offset + mem::size_of::<Surface>();
|
||||
align_offset(instance_offset);
|
||||
let next_offset = *instance_offset + mem::size_of::<Surface>();
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
||||
command_encoder.set_vertex_buffer(
|
||||
SurfaceInputIndex::Surfaces as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
Some(instance_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_vertex_bytes(
|
||||
SurfaceInputIndex::TextureSize as u64,
|
||||
|
@ -915,8 +964,8 @@ impl MetalRenderer {
|
|||
);
|
||||
|
||||
unsafe {
|
||||
let buffer_contents =
|
||||
(self.instances.contents() as *mut u8).add(*offset) as *mut SurfaceBounds;
|
||||
let buffer_contents = (instance_buffer.contents() as *mut u8).add(*instance_offset)
|
||||
as *mut SurfaceBounds;
|
||||
ptr::write(
|
||||
buffer_contents,
|
||||
SurfaceBounds {
|
||||
|
@ -927,7 +976,7 @@ impl MetalRenderer {
|
|||
}
|
||||
|
||||
command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
|
||||
*offset = next_offset;
|
||||
*instance_offset = next_offset;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue