use concinnity_core::render::buffer_growth::grow_capacity;
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::fullscreen::align_up;
use std::cell::RefCell;
use windows::Win32::Graphics::Direct3D12::*;
use crate::directx::allocator::{DeviceAllocator, PooledBuffer};
use crate::directx::com;
pub(in crate::directx) const UPLOAD_ALIGN: u64 = 16;
const UPLOAD_MIN_CAPACITY: u64 = 64 * 1024;
struct Slot {
buffer: Option<PooledBuffer>,
base: *mut u8,
gpu_va: u64,
capacity: u64,
cursor: u64,
}
impl Slot {
fn empty() -> Self {
Slot {
buffer: None,
base: std::ptr::null_mut(),
gpu_va: 0,
capacity: 0,
cursor: 0,
}
}
}
pub(in crate::directx) struct UploadRing {
slots: Vec<RefCell<Slot>>,
}
impl UploadRing {
pub(in crate::directx) fn new(frames: usize) -> Self {
UploadRing {
slots: (0..frames).map(|_| RefCell::new(Slot::empty())).collect(),
}
}
pub(in crate::directx) fn reserve(
&self,
alloc: &DeviceAllocator,
frame: usize,
needed: u64,
) -> RenderResult<()> {
let mut slot = self.slots[frame].borrow_mut();
slot.cursor = 0;
let Some(new_cap) = grow_capacity(slot.capacity, needed, UPLOAD_MIN_CAPACITY) else {
return Ok(());
};
let buffer = alloc.alloc_buffer(
new_cap,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
let mut base = std::ptr::null_mut::<std::ffi::c_void>();
unsafe { buffer.Map(0, None, Some(&mut base)) }
.map_err(|e| super::error::map_hresult(e.code(), "upload map"))?;
let gpu_va = com::gpu_va(&buffer);
slot.buffer = Some(buffer);
slot.base = base as *mut u8;
slot.gpu_va = gpu_va;
slot.capacity = new_cap;
Ok(())
}
pub(in crate::directx) fn push(&self, frame: usize, bytes: &[u8]) -> RenderResult<u64> {
let mut slot = self.slots[frame].borrow_mut();
let offset = align_up(slot.cursor, UPLOAD_ALIGN);
let end = offset + bytes.len() as u64;
if end > slot.capacity {
return Err(RenderError::Other(format!(
"upload ring overflow: need {end} bytes, reserved {}",
slot.capacity
)));
}
unsafe {
std::ptr::copy_nonoverlapping(
bytes.as_ptr(),
slot.base.add(offset as usize),
bytes.len(),
);
}
slot.cursor = end;
Ok(slot.gpu_va + offset)
}
}