1
0
Fork 0
forked from wry/wry

gfx: add GfxStagingBuffer

This commit is contained in:
Julian Orth 2024-10-05 16:49:22 +02:00
parent 1462933ef4
commit 3619a51fbd
11 changed files with 215 additions and 43 deletions

View file

@ -4,7 +4,8 @@ use {
gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
AsyncShmGfxTextureUploadCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage,
GfxTexture, PendingShmUpload, ReleaseSync, ShmGfxTexture, ShmMemory, SyncFile,
GfxStagingBuffer, GfxTexture, PendingShmUpload, ReleaseSync, ShmGfxTexture, ShmMemory,
SyncFile,
},
gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits,
@ -575,8 +576,16 @@ impl ShmGfxTexture for VulkanImage {
}
impl AsyncShmGfxTexture for VulkanImage {
fn staging_size(&self) -> usize {
let VulkanImageMemory::Internal(shm) = &self.ty else {
unreachable!();
};
shm.size as _
}
fn async_upload(
self: Rc<Self>,
staging: &Rc<dyn GfxStagingBuffer>,
callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: Rc<dyn ShmMemory>,
damage: Region,
@ -584,7 +593,8 @@ impl AsyncShmGfxTexture for VulkanImage {
let VulkanImageMemory::Internal(shm) = &self.ty else {
unreachable!();
};
let pending = shm.async_upload(&self, &mem, damage, callback)?;
let staging = staging.clone().into_vk(&self.renderer.device.device);
let pending = shm.async_upload(&self, staging, &mem, damage, callback)?;
Ok(pending)
}

View file

@ -17,7 +17,7 @@ use {
fence::VulkanFence,
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
renderer::{image_barrier, VulkanRenderer},
staging::VulkanStagingBuffer,
staging::{VulkanStagingBuffer, VulkanStagingShell},
VulkanError,
},
rect::{Rect, Region},
@ -55,7 +55,7 @@ pub struct VulkanShmImageAsyncData {
pub(super) busy: Cell<bool>,
pub(super) io_job: Cell<Option<Box<IoUploadJob>>>,
pub(super) copy_job: Cell<Option<Box<CopyUploadJob>>>,
pub(super) staging: CloneCell<Option<Rc<VulkanStagingBuffer>>>,
pub(super) staging: CloneCell<Option<Rc<VulkanStagingShell>>>,
pub(super) callback: Cell<Option<Rc<dyn AsyncShmGfxTextureCallback>>>,
pub(super) callback_id: Cell<u64>,
pub(super) regions: RefCell<Vec<BufferImageCopy2<'static>>>,
@ -320,6 +320,7 @@ async fn await_upload(
impl VulkanShmImageAsyncData {
fn complete(&self, result: Result<(), VulkanError>) {
self.busy.set(false);
self.staging.take().unwrap().busy.set(false);
if let Some(cb) = self.callback.take() {
cb.completed(result.map_err(|e| e.into()));
}
@ -330,12 +331,13 @@ impl VulkanShmImage {
pub fn async_upload(
&self,
img: &Rc<VulkanImage>,
staging: Rc<VulkanStagingShell>,
client_mem: &Rc<dyn ShmMemory>,
damage: Region,
callback: Rc<dyn AsyncShmGfxTextureCallback>,
) -> Result<Option<PendingShmUpload>, VulkanError> {
let data = self.async_data.as_ref().unwrap();
let res = self.try_async_upload(img, data, client_mem, damage);
let res = self.try_async_upload(img, staging, data, client_mem, damage);
match res {
Ok(()) => {
let id = img.renderer.allocate_point();
@ -350,6 +352,7 @@ impl VulkanShmImage {
fn try_async_upload(
&self,
img: &Rc<VulkanImage>,
staging: Rc<VulkanStagingShell>,
data: &VulkanShmImageAsyncData,
client_mem: &Rc<dyn ShmMemory>,
mut damage: Region,
@ -357,11 +360,19 @@ impl VulkanShmImage {
if data.busy.get() {
return Err(VulkanError::AsyncCopyBusy);
}
if staging.busy.get() {
return Err(VulkanError::StagingBufferBusy);
}
if !staging.upload {
return Err(VulkanError::StagingBufferNoUpload);
}
if self.size > client_mem.len() as u64 {
return Err(VulkanError::InvalidBufferSize);
}
data.busy.set(true);
data.data_copied.set(false);
staging.busy.set(true);
data.staging.set(Some(staging.clone()));
if img.contents_are_undefined.get() {
damage = Region::new2(Rect::new_sized(0, 0, img.width as _, img.height as _).unwrap());
}
@ -416,27 +427,22 @@ impl VulkanShmImage {
self.async_release_from_gfx_queue(img, data)?;
if let Some(staging) = data.staging.get() {
if let Some(staging) = staging.staging.get() {
return self.async_upload_initiate_copy(img, data, &staging, copies, client_mem);
}
let img2 = img.clone();
let client_mem = client_mem.clone();
img.renderer.device.create_shm_staging(
&img.renderer,
&data.cpu,
self.size,
true,
false,
move |res| {
img.renderer
.device
.fill_staging_shell(&img.renderer, &data.cpu, staging, move |res| {
let VulkanImageMemory::Internal(shm) = &img2.ty else {
unreachable!();
};
if let Err(e) = shm.async_upload_after_allocation(&img2, &client_mem, res) {
shm.async_data.as_ref().unwrap().complete(Err(e));
}
},
)
})
}
fn async_release_from_gfx_queue(
@ -532,11 +538,10 @@ impl VulkanShmImage {
&self,
img: &Rc<VulkanImage>,
client_mem: &Rc<dyn ShmMemory>,
res: Result<VulkanStagingBuffer, VulkanError>,
res: Result<Rc<VulkanStagingBuffer>, VulkanError>,
) -> Result<(), VulkanError> {
let staging = Rc::new(res?);
let staging = res?;
let data = self.async_data.as_ref().unwrap();
data.staging.set(Some(staging.clone()));
let copies = &*data.regions.borrow();
self.async_upload_initiate_copy(img, data, &staging, copies, client_mem)
}
@ -637,7 +642,7 @@ impl VulkanShmImage {
}
img.renderer.check_defunct()?;
let regions = &*data.regions.borrow();
let staging = data.staging.get().unwrap();
let staging = data.staging.get().unwrap().staging.get().unwrap();
staging.upload(|_, _| ())?;
let Some((cmd, fence, sync_file, point)) =
self.submit_buffer_to_image_copy(img, &staging, regions, true)?

View file

@ -1,17 +1,24 @@
use {
crate::{
cpu_worker::CpuWorker,
gfx_api::GfxStagingBuffer,
gfx_apis::vulkan::{
allocator::{VulkanAllocation, VulkanAllocator},
device::VulkanDevice,
renderer::VulkanRenderer,
VulkanError,
},
utils::on_drop::{OnDrop, OnDrop2},
utils::{
clonecell::CloneCell,
on_drop::{OnDrop, OnDrop2},
},
},
ash::{
vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange},
Device,
},
ash::vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange},
gpu_alloc::UsageFlags,
std::rc::Rc,
std::{any::Any, cell::Cell, rc::Rc},
};
pub struct VulkanStagingBuffer {
@ -22,6 +29,22 @@ pub struct VulkanStagingBuffer {
}
impl VulkanDevice {
pub(super) fn create_staging_shell(
self: &Rc<Self>,
size: u64,
upload: bool,
download: bool,
) -> Rc<VulkanStagingShell> {
Rc::new(VulkanStagingShell {
device: self.clone(),
staging: Default::default(),
size,
download,
upload,
busy: Cell::new(false),
})
}
pub(super) fn create_staging_buffer(
self: &Rc<Self>,
allocator: &Rc<VulkanAllocator>,
@ -51,17 +74,15 @@ impl VulkanDevice {
})
}
pub(super) fn create_shm_staging(
pub(super) fn fill_staging_shell(
self: &Rc<Self>,
renderer: &Rc<VulkanRenderer>,
cpu: &Rc<CpuWorker>,
size: u64,
upload: bool,
download: bool,
cb: impl FnOnce(Result<VulkanStagingBuffer, VulkanError>) + 'static,
shell: Rc<VulkanStagingShell>,
cb: impl FnOnce(Result<Rc<VulkanStagingBuffer>, VulkanError>) + 'static,
) -> Result<(), VulkanError> {
let (vk_usage, usage) = get_usage(upload, download, false);
let buffer = self.create_buffer(size, vk_usage)?;
let (vk_usage, usage) = get_usage(shell.upload, shell.download, false);
let buffer = self.create_buffer(shell.size, vk_usage)?;
let memory_requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) };
let slf = self.clone();
let destroy_buffer =
@ -77,12 +98,14 @@ impl VulkanDevice {
res.map_err(VulkanError::BindBufferMemory)?;
}
destroy_buffer.forget();
Ok(VulkanStagingBuffer {
let buffer = Rc::new(VulkanStagingBuffer {
device: slf.clone(),
allocation,
buffer,
size,
})
size: shell.size,
});
shell.staging.set(Some(buffer.clone()));
Ok(buffer)
};
renderer.shm_allocator.async_alloc(
renderer,
@ -159,3 +182,43 @@ impl Drop for VulkanStagingBuffer {
}
}
}
pub(super) struct VulkanStagingShell {
pub(super) device: Rc<VulkanDevice>,
pub(super) staging: CloneCell<Option<Rc<VulkanStagingBuffer>>>,
pub(super) size: u64,
pub(super) download: bool,
pub(super) upload: bool,
pub(super) busy: Cell<bool>,
}
impl GfxStagingBuffer for VulkanStagingShell {
fn size(&self) -> usize {
self.size as _
}
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
impl VulkanStagingShell {
fn assert_device(&self, device: &Device) {
assert_eq!(
self.device.device.handle(),
device.handle(),
"Mixed vulkan device use"
);
}
}
impl dyn GfxStagingBuffer {
pub(super) fn into_vk(self: Rc<Self>, device: &Device) -> Rc<VulkanStagingShell> {
let shell: Rc<VulkanStagingShell> = self
.into_any()
.downcast()
.expect("Non-vulkan staging buffer passed into vulkan");
shell.assert_device(device);
shell
}
}