use super::super::shared::{BeltChunk as BeltChunkTrait, StagingBeltCore};
use ::metal as mtl;
use anyhow::Result;
pub(super) use super::super::shared::DEFAULT_STAGING_CHUNK_SIZE;
struct MetalBeltChunk {
buffer: mtl::Buffer,
capacity: u64,
offset: u64,
mapped: *mut u8,
}
unsafe impl Send for MetalBeltChunk {}
unsafe impl Sync for MetalBeltChunk {}
impl BeltChunkTrait for MetalBeltChunk {
fn capacity(&self) -> u64 {
self.capacity
}
fn offset(&self) -> u64 {
self.offset
}
fn offset_mut(&mut self) -> &mut u64 {
&mut self.offset
}
fn mapped_ptr(&self) -> *mut u8 {
self.mapped
}
}
fn allocate_chunk(device: &mtl::DeviceRef, size: u64) -> Result<MetalBeltChunk> {
let buffer = device.new_buffer(size, mtl::MTLResourceOptions::StorageModeShared);
let mapped = buffer.contents() as *mut u8;
anyhow::ensure!(
!mapped.is_null(),
"StagingBelt: new_buffer returned null contents (size={})",
size
);
Ok(MetalBeltChunk {
buffer,
capacity: size,
offset: 0,
mapped,
})
}
pub(super) struct StagingBelt {
core: StagingBeltCore<MetalBeltChunk>,
}
impl StagingBelt {
pub fn new(chunk_size: u64) -> Self {
Self {
core: StagingBeltCore::new(chunk_size),
}
}
pub fn reclaim(&mut self, completed: u64) {
let mut i = 0;
while i < self.core.in_flight.len() {
if self.core.in_flight[i].0 <= completed {
let (_, mut chunks) = self.core.in_flight.remove(i);
for ch in &mut chunks {
ch.reset();
}
self.core.free.extend(chunks);
} else {
i += 1;
}
}
}
pub fn write(&mut self, device: &mtl::DeviceRef, data: &[u8]) -> Result<(mtl::Buffer, u64)> {
let (idx, start) = self.core.write(data, |size| allocate_chunk(device, size))?;
Ok((self.core.active[idx].buffer.clone(), start))
}
pub fn finish(&mut self, token: u64) {
self.core.finish(token);
}
#[allow(dead_code)]
pub fn trim(&mut self) {
self.core.trim_free(|_ch| {});
}
pub fn destroy_all(&mut self) {
self.core.destroy_all(|_ch| {});
}
}
pub(super) struct TextureStagingEntry {
pub buffer: mtl::Buffer,
pub capacity: u64,
mapped: *mut u8,
}
unsafe impl Send for TextureStagingEntry {}
unsafe impl Sync for TextureStagingEntry {}
impl TextureStagingEntry {
pub fn mapped_ptr(&self) -> *mut u8 {
self.mapped
}
}
fn allocate_texture_staging_entry(device: &mtl::DeviceRef, size: u64) -> Result<TextureStagingEntry> {
let buffer = device.new_buffer(size, mtl::MTLResourceOptions::StorageModeShared);
let mapped = buffer.contents() as *mut u8;
anyhow::ensure!(
!mapped.is_null(),
"TextureStagingPool: new_buffer returned null contents (size={})",
size
);
Ok(TextureStagingEntry {
buffer,
capacity: size,
mapped,
})
}
pub(super) struct TextureStagingPool {
free: Vec<TextureStagingEntry>,
in_flight: Vec<(u64, Vec<TextureStagingEntry>)>,
}
impl TextureStagingPool {
pub fn new() -> Self {
Self {
free: Vec::new(),
in_flight: Vec::new(),
}
}
pub fn acquire(&mut self, device: &mtl::DeviceRef, size: u64) -> Result<TextureStagingEntry> {
let _tz = crate::tracy_zone!("mtl.texture_staging.acquire");
if let Some(pos) = self.free.iter().rposition(|e| e.capacity >= size) {
return Ok(self.free.swap_remove(pos));
}
allocate_texture_staging_entry(device, size)
}
pub fn release(&mut self, timeline_value: u64, entries: Vec<TextureStagingEntry>) {
if !entries.is_empty() {
let _tz = crate::tracy_zone!("mtl.texture_staging.release");
self.in_flight.push((timeline_value, entries));
}
}
pub fn reclaim(&mut self, completed: u64) {
let _tz = crate::tracy_zone!("mtl.texture_staging.reclaim");
let mut i = 0;
while i < self.in_flight.len() {
if self.in_flight[i].0 <= completed {
let (_, entries) = self.in_flight.swap_remove(i);
self.free.extend(entries);
} else {
i += 1;
}
}
}
pub fn destroy_all(&mut self) {
self.free.clear();
for (_, entries) in self.in_flight.drain(..) {
drop(entries);
}
}
}