mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-11 04:51:32 +11:00
294 lines
8.3 KiB
Rust
294 lines
8.3 KiB
Rust
|
//! A convenience layer on top of raw hal.
|
||
|
//!
|
||
|
//! This layer takes care of some lifetime and synchronization bookkeeping.
|
||
|
//! It is likely that it will also take care of compile time and runtime
|
||
|
//! negotiation of backends (Vulkan, DX12), but right now it's Vulkan-only.
|
||
|
|
||
|
use std::any::Any;
|
||
|
use std::sync::{Arc, Mutex, Weak};
|
||
|
|
||
|
use crate::vulkan;
|
||
|
use crate::{Device, Error};
|
||
|
|
||
|
pub type MemFlags = <vulkan::VkDevice as Device>::MemFlags;
|
||
|
pub type Semaphore = <vulkan::VkDevice as Device>::Semaphore;
|
||
|
pub type Pipeline = <vulkan::VkDevice as Device>::Pipeline;
|
||
|
pub type DescriptorSet = <vulkan::VkDevice as Device>::DescriptorSet;
|
||
|
pub type QueryPool = <vulkan::VkDevice as Device>::QueryPool;
|
||
|
|
||
|
type Fence = <vulkan::VkDevice as Device>::Fence;
|
||
|
|
||
|
type VkImage = <vulkan::VkDevice as Device>::Image;
|
||
|
type VkBuffer = <vulkan::VkDevice as Device>::Buffer;
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub struct Session(Arc<SessionInner>);
|
||
|
|
||
|
struct SessionInner {
|
||
|
device: vulkan::VkDevice,
|
||
|
cmd_buf_pool: Mutex<Vec<(vulkan::CmdBuf, Fence)>>,
|
||
|
/// Command buffers that are still pending (so resources can't be freed).
|
||
|
pending: Mutex<Vec<SubmittedCmdBufInner>>,
|
||
|
}
|
||
|
|
||
|
pub struct CmdBuf {
|
||
|
cmd_buf: vulkan::CmdBuf,
|
||
|
fence: Fence,
|
||
|
resources: Vec<Box<dyn Any>>,
|
||
|
session: Weak<SessionInner>,
|
||
|
}
|
||
|
|
||
|
// Maybe "pending" is a better name?
|
||
|
pub struct SubmittedCmdBuf(Option<SubmittedCmdBufInner>, Weak<SessionInner>);
|
||
|
|
||
|
struct SubmittedCmdBufInner {
|
||
|
cmd_buf: vulkan::CmdBuf,
|
||
|
fence: Fence,
|
||
|
resources: Vec<Box<dyn Any>>,
|
||
|
}
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub struct Image(Arc<ImageInner>);
|
||
|
|
||
|
struct ImageInner {
|
||
|
image: VkImage,
|
||
|
session: Weak<SessionInner>,
|
||
|
}
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub struct Buffer(Arc<BufferInner>);
|
||
|
|
||
|
struct BufferInner {
|
||
|
buffer: VkBuffer,
|
||
|
session: Weak<SessionInner>,
|
||
|
}
|
||
|
|
||
|
impl Session {
|
||
|
pub fn new(device: vulkan::VkDevice) -> Session {
|
||
|
Session(Arc::new(SessionInner {
|
||
|
device,
|
||
|
cmd_buf_pool: Default::default(),
|
||
|
pending: Default::default(),
|
||
|
}))
|
||
|
}
|
||
|
|
||
|
pub fn cmd_buf(&self) -> Result<CmdBuf, Error> {
|
||
|
self.poll_cleanup();
|
||
|
let (cmd_buf, fence) = if let Some(cf) = self.0.cmd_buf_pool.lock().unwrap().pop() {
|
||
|
cf
|
||
|
} else {
|
||
|
let cmd_buf = self.0.device.create_cmd_buf()?;
|
||
|
let fence = unsafe { self.0.device.create_fence(false)? };
|
||
|
(cmd_buf, fence)
|
||
|
};
|
||
|
Ok(CmdBuf {
|
||
|
cmd_buf,
|
||
|
fence,
|
||
|
resources: Vec::new(),
|
||
|
session: Arc::downgrade(&self.0),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
fn poll_cleanup(&self) {
|
||
|
let mut pending = self.0.pending.lock().unwrap();
|
||
|
unsafe {
|
||
|
let mut i = 0;
|
||
|
while i < pending.len() {
|
||
|
if let Ok(true) = self.0.device.get_fence_status(pending[i].fence) {
|
||
|
let item = pending.swap_remove(i);
|
||
|
// TODO: wait is superfluous, can just reset
|
||
|
let _ = self.0.device.wait_and_reset(&[item.fence]);
|
||
|
self.0
|
||
|
.cmd_buf_pool
|
||
|
.lock()
|
||
|
.unwrap()
|
||
|
.push((item.cmd_buf, item.fence));
|
||
|
std::mem::drop(item.resources);
|
||
|
} else {
|
||
|
i += 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub unsafe fn run_cmd_buf(
|
||
|
&self,
|
||
|
cmd_buf: CmdBuf,
|
||
|
wait_semaphores: &[Semaphore],
|
||
|
signal_semaphores: &[Semaphore],
|
||
|
) -> Result<SubmittedCmdBuf, Error> {
|
||
|
self.0.device.run_cmd_buf(
|
||
|
&cmd_buf.cmd_buf,
|
||
|
wait_semaphores,
|
||
|
signal_semaphores,
|
||
|
Some(&cmd_buf.fence),
|
||
|
)?;
|
||
|
Ok(SubmittedCmdBuf(
|
||
|
Some(SubmittedCmdBufInner {
|
||
|
cmd_buf: cmd_buf.cmd_buf,
|
||
|
fence: cmd_buf.fence,
|
||
|
resources: cmd_buf.resources,
|
||
|
}),
|
||
|
cmd_buf.session,
|
||
|
))
|
||
|
}
|
||
|
|
||
|
pub fn create_buffer(&self, size: u64, mem_flags: MemFlags) -> Result<Buffer, Error> {
|
||
|
let buffer = self.0.device.create_buffer(size, mem_flags)?;
|
||
|
Ok(Buffer(Arc::new(BufferInner {
|
||
|
buffer,
|
||
|
session: Arc::downgrade(&self.0),
|
||
|
})))
|
||
|
}
|
||
|
|
||
|
pub unsafe fn create_image2d(
|
||
|
&self,
|
||
|
width: u32,
|
||
|
height: u32,
|
||
|
mem_flags: MemFlags,
|
||
|
) -> Result<Image, Error> {
|
||
|
let image = self.0.device.create_image2d(width, height, mem_flags)?;
|
||
|
Ok(Image(Arc::new(ImageInner {
|
||
|
image,
|
||
|
session: Arc::downgrade(&self.0),
|
||
|
})))
|
||
|
}
|
||
|
|
||
|
pub unsafe fn create_semaphore(&self) -> Result<Semaphore, Error> {
|
||
|
self.0.device.create_semaphore()
|
||
|
}
|
||
|
|
||
|
/// This creates a pipeline that runs over the buffer.
|
||
|
///
|
||
|
/// The descriptor set layout is just some number of storage buffers and storage images (this might change).
|
||
|
pub unsafe fn create_simple_compute_pipeline(
|
||
|
&self,
|
||
|
code: &[u8],
|
||
|
n_buffers: u32,
|
||
|
n_images: u32,
|
||
|
) -> Result<Pipeline, Error> {
|
||
|
self.0
|
||
|
.device
|
||
|
.create_simple_compute_pipeline(code, n_buffers, n_images)
|
||
|
}
|
||
|
|
||
|
/// Create a descriptor set for a simple pipeline that just references buffers and images.
|
||
|
///
|
||
|
/// Note: when we do portability, the signature will change to not reference the Vulkan types
|
||
|
/// directly.
|
||
|
pub unsafe fn create_descriptor_set(
|
||
|
&self,
|
||
|
pipeline: &Pipeline,
|
||
|
bufs: &[&vulkan::Buffer],
|
||
|
images: &[&vulkan::Image],
|
||
|
) -> Result<DescriptorSet, Error> {
|
||
|
self.0.device.create_descriptor_set(pipeline, bufs, images)
|
||
|
}
|
||
|
|
||
|
/// Create a query pool for timestamp queries.
|
||
|
pub fn create_query_pool(&self, n_queries: u32) -> Result<QueryPool, Error> {
|
||
|
self.0.device.create_query_pool(n_queries)
|
||
|
}
|
||
|
|
||
|
pub unsafe fn fetch_query_pool(&self, pool: &QueryPool) -> Result<Vec<f64>, Error> {
|
||
|
self.0.device.fetch_query_pool(pool)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl CmdBuf {
|
||
|
pub fn add_resource<T: Clone + 'static>(&mut self, resource: &T) {
|
||
|
self.resources.push(Box::new(resource.clone()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl SubmittedCmdBuf {
|
||
|
pub fn wait(mut self) -> Result<(), Error> {
|
||
|
let item = self.0.take().unwrap();
|
||
|
if let Some(session) = Weak::upgrade(&self.1) {
|
||
|
unsafe {
|
||
|
session.device.wait_and_reset(&[item.fence])?;
|
||
|
}
|
||
|
session
|
||
|
.cmd_buf_pool
|
||
|
.lock()
|
||
|
.unwrap()
|
||
|
.push((item.cmd_buf, item.fence));
|
||
|
std::mem::drop(item.resources);
|
||
|
}
|
||
|
// else session dropped error?
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Drop for SubmittedCmdBuf {
|
||
|
fn drop(&mut self) {
|
||
|
if let Some(inner) = self.0.take() {
|
||
|
if let Some(session) = Weak::upgrade(&self.1) {
|
||
|
session.pending.lock().unwrap().push(inner);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Drop for BufferInner {
|
||
|
fn drop(&mut self) {
|
||
|
if let Some(session) = Weak::upgrade(&self.session) {
|
||
|
unsafe {
|
||
|
let _ = session.device.destroy_buffer(&self.buffer);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Drop for ImageInner {
|
||
|
fn drop(&mut self) {
|
||
|
if let Some(session) = Weak::upgrade(&self.session) {
|
||
|
unsafe {
|
||
|
let _ = session.device.destroy_image(&self.image);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// For now, we deref, but for runtime backend switching we'll need to wrap
|
||
|
/// all methods.
|
||
|
impl std::ops::Deref for CmdBuf {
|
||
|
type Target = vulkan::CmdBuf;
|
||
|
fn deref(&self) -> &Self::Target {
|
||
|
&self.cmd_buf
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl std::ops::DerefMut for CmdBuf {
|
||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||
|
&mut self.cmd_buf
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Image {
|
||
|
pub fn vk_image(&self) -> &vulkan::Image {
|
||
|
&self.0.image
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Buffer {
|
||
|
pub fn vk_buffer(&self) -> &vulkan::Buffer {
|
||
|
&self.0.buffer
|
||
|
}
|
||
|
|
||
|
pub unsafe fn write<T: Sized>(&mut self, contents: &[T]) -> Result<(), Error> {
|
||
|
if let Some(session) = Weak::upgrade(&self.0.session) {
|
||
|
session.device.write_buffer(&self.0.buffer, contents)?;
|
||
|
}
|
||
|
// else session lost error?
|
||
|
Ok(())
|
||
|
}
|
||
|
pub unsafe fn read<T: Sized>(&self, result: &mut Vec<T>) -> Result<(), Error> {
|
||
|
if let Some(session) = Weak::upgrade(&self.0.session) {
|
||
|
session.device.read_buffer(&self.0.buffer, result)?;
|
||
|
}
|
||
|
// else session lost error?
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|