Merge pull request #97 from linebender/mux

Add multiplexer abstraction
This commit is contained in:
Raph Levien 2021-05-26 16:31:08 -07:00 committed by GitHub
commit acb4e5f8af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 879 additions and 129 deletions

5
Cargo.lock generated
View file

@ -851,6 +851,7 @@ dependencies = [
"ash-window", "ash-window",
"bitflags", "bitflags",
"raw-window-handle", "raw-window-handle",
"smallvec",
"winapi 0.3.9", "winapi 0.3.9",
"wio", "wio",
] ]
@ -1036,9 +1037,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.4.2" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"

View file

@ -11,7 +11,9 @@ ash = "0.31"
ash-window = "0.5" ash-window = "0.5"
raw-window-handle = "0.3" raw-window-handle = "0.3"
bitflags = "1.2.1" bitflags = "1.2.1"
smallvec = "1.6.1"
[target.'cfg(target_os="windows")'.dependencies]
winapi = { version = "0.3.9", features = [ winapi = { version = "0.3.9", features = [
'd3d12', 'd3d12sdklayers', 'd3dcommon', 'd3dcompiler', 'dxgi', 'd3d12', 'd3d12sdklayers', 'd3dcommon', 'd3dcompiler', 'dxgi',
'dxgi1_2', 'dxgi1_3', 'dxgi1_4', 'dxgidebug', 'dxgiformat', 'dxgitype', 'dxgi1_2', 'dxgi1_3', 'dxgi1_4', 'dxgidebug', 'dxgiformat', 'dxgitype',

View file

@ -1,16 +1,16 @@
use piet_gpu_hal::hub; use piet_gpu_hal::hub;
use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::mux::{Instance, ShaderCode};
use piet_gpu_hal::{BufferUsage, CmdBuf}; use piet_gpu_hal::BufferUsage;
fn main() { fn main() {
let (instance, _) = VkInstance::new(None).unwrap(); let (instance, _) = Instance::new(None).unwrap();
unsafe { unsafe {
let device = instance.device(None).unwrap(); let device = instance.device(None).unwrap();
let session = hub::Session::new(device); let session = hub::Session::new(device);
let usage = BufferUsage::MAP_READ | BufferUsage::STORAGE; let usage = BufferUsage::MAP_READ | BufferUsage::STORAGE;
let src = (0..256).map(|x| x + 1).collect::<Vec<u32>>(); let src = (0..256).map(|x| x + 1).collect::<Vec<u32>>();
let buffer = session.create_buffer_init(&src, usage).unwrap(); let buffer = session.create_buffer_init(&src, usage).unwrap();
let code = include_bytes!("./shader/collatz.spv"); let code = ShaderCode::Spv(include_bytes!("./shader/collatz.spv"));
let pipeline = session.create_simple_compute_pipeline(code, 1).unwrap(); let pipeline = session.create_simple_compute_pipeline(code, 1).unwrap();
let descriptor_set = session let descriptor_set = session
.create_simple_descriptor_set(&pipeline, &[&buffer]) .create_simple_descriptor_set(&pipeline, &[&buffer])

View file

@ -50,8 +50,8 @@ void main(SPIRV_Cross_Input stage_input)
"#; "#;
fn toy() -> Result<(), Error> { fn toy() -> Result<(), Error> {
let instance = dx12::Dx12Instance::new()?; let (instance, _surface) = dx12::Dx12Instance::new(None)?;
let device = instance.device()?; let device = instance.device(None)?;
let buf = device.create_buffer( let buf = device.create_buffer(
1024, 1024,
BufferUsage::MAP_READ BufferUsage::MAP_READ
@ -87,7 +87,7 @@ fn toy() -> Result<(), Error> {
cmd_buf.host_barrier(); cmd_buf.host_barrier();
cmd_buf.finish(); cmd_buf.finish();
device.run_cmd_bufs(&[&cmd_buf], &[], &[], Some(&fence))?; device.run_cmd_bufs(&[&cmd_buf], &[], &[], Some(&fence))?;
device.wait_and_reset(&[fence])?; device.wait_and_reset(&[&fence])?;
let mut readback: Vec<u32> = vec![0u32; 256]; let mut readback: Vec<u32> = vec![0u32; 256];
device.read_buffer(&buf, readback.as_mut_ptr() as *mut u8, 0, 1024)?; device.read_buffer(&buf, readback.as_mut_ptr() as *mut u8, 0, 1024)?;
println!("{:?}", readback); println!("{:?}", readback);

View file

@ -9,6 +9,8 @@ use winapi::shared::dxgi1_3;
use winapi::shared::minwindef::TRUE; use winapi::shared::minwindef::TRUE;
use winapi::um::d3d12; use winapi::um::d3d12;
use smallvec::SmallVec;
use crate::{BufferUsage, Error, ImageLayout}; use crate::{BufferUsage, Error, ImageLayout};
use self::wrappers::{ use self::wrappers::{
@ -19,6 +21,12 @@ pub struct Dx12Instance {
factory: Factory4, factory: Factory4,
} }
// TODO
pub struct Dx12Surface;
// TODO
pub struct Dx12Swapchain;
pub struct Dx12Device { pub struct Dx12Device {
device: Device, device: Device,
command_allocator: CommandAllocator, command_allocator: CommandAllocator,
@ -30,7 +38,7 @@ pub struct Dx12Device {
#[derive(Clone)] #[derive(Clone)]
pub struct Buffer { pub struct Buffer {
resource: Resource, resource: Resource,
size: u64, pub size: u64,
} }
#[derive(Clone)] #[derive(Clone)]
@ -95,7 +103,7 @@ impl Dx12Instance {
/// ///
/// TODO: take a raw window handle. /// TODO: take a raw window handle.
/// TODO: can probably be a trait. /// TODO: can probably be a trait.
pub fn new() -> Result<Dx12Instance, Error> { pub fn new(window_handle: Option<&dyn raw_window_handle::HasRawWindowHandle>) -> Result<(Dx12Instance, Option<Dx12Surface>), Error> {
unsafe { unsafe {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
if let Err(e) = wrappers::enable_debug_layer() { if let Err(e) = wrappers::enable_debug_layer() {
@ -110,7 +118,7 @@ impl Dx12Instance {
let factory_flags: u32 = 0; let factory_flags: u32 = 0;
let factory = Factory4::create(factory_flags)?; let factory = Factory4::create(factory_flags)?;
Ok(Dx12Instance { factory }) Ok((Dx12Instance { factory }, None))
} }
} }
@ -118,7 +126,7 @@ impl Dx12Instance {
/// ///
/// TODO: handle window. /// TODO: handle window.
/// TODO: probably can also be trait'ified. /// TODO: probably can also be trait'ified.
pub fn device(&self) -> Result<Dx12Device, Error> { pub fn device(&self, surface: Option<&Dx12Surface>) -> Result<Dx12Device, Error> {
unsafe { unsafe {
let device = Device::create_device(&self.factory)?; let device = Device::create_device(&self.factory)?;
let list_type = d3d12::D3D12_COMMAND_LIST_TYPE_DIRECT; let list_type = d3d12::D3D12_COMMAND_LIST_TYPE_DIRECT;
@ -262,16 +270,15 @@ impl crate::Device for Dx12Device {
unsafe fn run_cmd_bufs( unsafe fn run_cmd_bufs(
&self, &self,
cmd_bufs: &[&Self::CmdBuf], cmd_bufs: &[&Self::CmdBuf],
wait_semaphores: &[Self::Semaphore], wait_semaphores: &[&Self::Semaphore],
signal_semaphores: &[Self::Semaphore], signal_semaphores: &[&Self::Semaphore],
fence: Option<&Self::Fence>, fence: Option<&Self::Fence>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// TODO: handle semaphores // TODO: handle semaphores
// SmallVec?
let lists = cmd_bufs let lists = cmd_bufs
.iter() .iter()
.map(|c| c.0.as_raw_command_list()) .map(|c| c.0.as_raw_command_list())
.collect::<Vec<_>>(); .collect::<SmallVec<[_; 4]>>();
self.command_queue.execute_command_lists(&lists); self.command_queue.execute_command_lists(&lists);
if let Some(fence) = fence { if let Some(fence) = fence {
let val = fence.val.get() + 1; let val = fence.val.get() + 1;
@ -319,7 +326,7 @@ impl crate::Device for Dx12Device {
Ok(Fence { fence, event, val }) Ok(Fence { fence, event, val })
} }
unsafe fn wait_and_reset(&self, fences: &[Self::Fence]) -> Result<(), Error> { unsafe fn wait_and_reset(&self, fences: &[&Self::Fence]) -> Result<(), Error> {
for fence in fences { for fence in fences {
// TODO: probably handle errors here. // TODO: probably handle errors here.
let _status = fence.event.wait(winapi::um::winbase::INFINITE); let _status = fence.event.wait(winapi::um::winbase::INFINITE);
@ -327,7 +334,7 @@ impl crate::Device for Dx12Device {
Ok(()) Ok(())
} }
unsafe fn get_fence_status(&self, fence: Self::Fence) -> Result<bool, Error> { unsafe fn get_fence_status(&self, fence: &Self::Fence) -> Result<bool, Error> {
let fence_val = fence.fence.get_value(); let fence_val = fence.fence.get_value();
Ok(fence_val == fence.val.get()) Ok(fence_val == fence.val.get())
} }

View file

@ -1,35 +1,28 @@
//! A convenience layer on top of raw hal. //! A somewhat higher level GPU abstraction.
//! //!
//! This layer takes care of some lifetime and synchronization bookkeeping. //! This layer is on top of the lower-level layer that multiplexes different
//! It is likely that it will also take care of compile time and runtime //! back-ends. It handles details such as managing staging buffers for creating
//! negotiation of backends (Vulkan, DX12), but right now it's Vulkan-only. //! buffers with initial content, deferring dropping of resources until command
//! submission is complete, and a bit more. These conveniences might expand
//! even more in time.
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use crate::vulkan; use smallvec::SmallVec;
use crate::CmdBuf as CmdBufTrait;
use crate::DescriptorSetBuilder as DescriptorSetBuilderTrait;
use crate::PipelineBuilder as PipelineBuilderTrait;
use crate::{BufferUsage, Device, Error, GpuInfo, SamplerParams};
pub type Semaphore = <vulkan::VkDevice as Device>::Semaphore; use crate::mux;
pub type Pipeline = <vulkan::VkDevice as Device>::Pipeline;
pub type DescriptorSet = <vulkan::VkDevice as Device>::DescriptorSet;
pub type QueryPool = <vulkan::VkDevice as Device>::QueryPool;
pub type Sampler = <vulkan::VkDevice as Device>::Sampler;
type Fence = <vulkan::VkDevice as Device>::Fence; use crate::{BufferUsage, Error, GpuInfo, SamplerParams};
type VkImage = <vulkan::VkDevice as Device>::Image; pub use crate::mux::{DescriptorSet, Fence, Pipeline, QueryPool, Sampler, Semaphore, ShaderCode};
type VkBuffer = <vulkan::VkDevice as Device>::Buffer;
#[derive(Clone)] #[derive(Clone)]
pub struct Session(Arc<SessionInner>); pub struct Session(Arc<SessionInner>);
struct SessionInner { struct SessionInner {
device: vulkan::VkDevice, device: mux::Device,
cmd_buf_pool: Mutex<Vec<(vulkan::CmdBuf, Fence)>>, cmd_buf_pool: Mutex<Vec<(mux::CmdBuf, Fence)>>,
/// Command buffers that are still pending (so resources can't be freed). /// Command buffers that are still pending (so resources can't be freed).
pending: Mutex<Vec<SubmittedCmdBufInner>>, pending: Mutex<Vec<SubmittedCmdBufInner>>,
/// A command buffer that is used for copying from staging buffers. /// A command buffer that is used for copying from staging buffers.
@ -38,7 +31,7 @@ struct SessionInner {
} }
pub struct CmdBuf { pub struct CmdBuf {
cmd_buf: vulkan::CmdBuf, cmd_buf: mux::CmdBuf,
fence: Fence, fence: Fence,
resources: Vec<RetainResource>, resources: Vec<RetainResource>,
session: Weak<SessionInner>, session: Weak<SessionInner>,
@ -50,7 +43,7 @@ pub struct SubmittedCmdBuf(Option<SubmittedCmdBufInner>, Weak<SessionInner>);
struct SubmittedCmdBufInner { struct SubmittedCmdBufInner {
// It's inconsistent, cmd_buf is unpacked, staging_cmd_buf isn't. Probably // It's inconsistent, cmd_buf is unpacked, staging_cmd_buf isn't. Probably
// better to chose one or the other. // better to chose one or the other.
cmd_buf: vulkan::CmdBuf, cmd_buf: mux::CmdBuf,
fence: Fence, fence: Fence,
resources: Vec<RetainResource>, resources: Vec<RetainResource>,
staging_cmd_buf: Option<CmdBuf>, staging_cmd_buf: Option<CmdBuf>,
@ -60,7 +53,7 @@ struct SubmittedCmdBufInner {
pub struct Image(Arc<ImageInner>); pub struct Image(Arc<ImageInner>);
struct ImageInner { struct ImageInner {
image: VkImage, image: mux::Image,
session: Weak<SessionInner>, session: Weak<SessionInner>,
} }
@ -68,13 +61,13 @@ struct ImageInner {
pub struct Buffer(Arc<BufferInner>); pub struct Buffer(Arc<BufferInner>);
struct BufferInner { struct BufferInner {
buffer: VkBuffer, buffer: mux::Buffer,
session: Weak<SessionInner>, session: Weak<SessionInner>,
} }
pub struct PipelineBuilder(vulkan::PipelineBuilder); pub struct PipelineBuilder(mux::PipelineBuilder);
pub struct DescriptorSetBuilder(vulkan::DescriptorSetBuilder); pub struct DescriptorSetBuilder(mux::DescriptorSetBuilder);
/// Data types that can be stored in a GPU buffer. /// Data types that can be stored in a GPU buffer.
pub unsafe trait PlainData {} pub unsafe trait PlainData {}
@ -97,7 +90,7 @@ pub enum RetainResource {
} }
impl Session { impl Session {
pub fn new(device: vulkan::VkDevice) -> Session { pub fn new(device: mux::Device) -> Session {
let gpu_info = device.query_gpu_info(); let gpu_info = device.query_gpu_info();
Session(Arc::new(SessionInner { Session(Arc::new(SessionInner {
device, device,
@ -130,10 +123,10 @@ impl Session {
unsafe { unsafe {
let mut i = 0; let mut i = 0;
while i < pending.len() { while i < pending.len() {
if let Ok(true) = self.0.device.get_fence_status(pending[i].fence) { if let Ok(true) = self.0.device.get_fence_status(&pending[i].fence) {
let item = pending.swap_remove(i); let item = pending.swap_remove(i);
// TODO: wait is superfluous, can just reset // TODO: wait is superfluous, can just reset
let _ = self.0.device.wait_and_reset(&[item.fence]); let _ = self.0.device.wait_and_reset(&[&item.fence]);
let mut pool = self.0.cmd_buf_pool.lock().unwrap(); let mut pool = self.0.cmd_buf_pool.lock().unwrap();
pool.push((item.cmd_buf, item.fence)); pool.push((item.cmd_buf, item.fence));
std::mem::drop(item.resources); std::mem::drop(item.resources);
@ -151,8 +144,8 @@ impl Session {
pub unsafe fn run_cmd_buf( pub unsafe fn run_cmd_buf(
&self, &self,
cmd_buf: CmdBuf, cmd_buf: CmdBuf,
wait_semaphores: &[Semaphore], wait_semaphores: &[&Semaphore],
signal_semaphores: &[Semaphore], signal_semaphores: &[&Semaphore],
) -> Result<SubmittedCmdBuf, Error> { ) -> Result<SubmittedCmdBuf, Error> {
// Again, SmallVec here? // Again, SmallVec here?
let mut cmd_bufs = Vec::with_capacity(2); let mut cmd_bufs = Vec::with_capacity(2);
@ -222,7 +215,7 @@ impl Session {
let create_buf = self.create_buffer(size, create_usage)?; let create_buf = self.create_buffer(size, create_usage)?;
self.0 self.0
.device .device
.write_buffer(&create_buf.vk_buffer(), contents, 0, size)?; .write_buffer(&create_buf.mux_buffer(), contents, 0, size)?;
if use_staging_buffer { if use_staging_buffer {
let buf = self.create_buffer(size, usage | BufferUsage::COPY_DST)?; let buf = self.create_buffer(size, usage | BufferUsage::COPY_DST)?;
let mut staging_cmd_buf = self.0.staging_cmd_buf.lock().unwrap(); let mut staging_cmd_buf = self.0.staging_cmd_buf.lock().unwrap();
@ -232,8 +225,8 @@ impl Session {
*staging_cmd_buf = Some(cmd_buf); *staging_cmd_buf = Some(cmd_buf);
} }
let staging_cmd_buf = staging_cmd_buf.as_mut().unwrap(); let staging_cmd_buf = staging_cmd_buf.as_mut().unwrap();
// This will ensure the staging buffer is deallocated. It would be nice to // This will ensure the staging buffer is deallocated.
staging_cmd_buf.copy_buffer(create_buf.vk_buffer(), buf.vk_buffer()); staging_cmd_buf.copy_buffer(create_buf.mux_buffer(), buf.mux_buffer());
staging_cmd_buf.add_resource(create_buf); staging_cmd_buf.add_resource(create_buf);
Ok(buf) Ok(buf)
} else { } else {
@ -256,9 +249,9 @@ impl Session {
/// This creates a pipeline that operates on some buffers and images. /// This creates a pipeline that operates on some buffers and images.
/// ///
/// The descriptor set layout is just some number of storage buffers and storage images (this might change). /// The descriptor set layout is just some number of storage buffers and storage images (this might change).
pub unsafe fn create_simple_compute_pipeline( pub unsafe fn create_simple_compute_pipeline<'a>(
&self, &self,
code: &[u8], code: ShaderCode<'a>,
n_buffers: u32, n_buffers: u32,
) -> Result<Pipeline, Error> { ) -> Result<Pipeline, Error> {
self.pipeline_builder() self.pipeline_builder()
@ -295,7 +288,8 @@ impl Session {
} }
pub unsafe fn create_sampler(&self, params: SamplerParams) -> Result<Sampler, Error> { pub unsafe fn create_sampler(&self, params: SamplerParams) -> Result<Sampler, Error> {
self.0.device.create_sampler(params) todo!()
//self.0.device.create_sampler(params)
} }
pub fn gpu_info(&self) -> &GpuInfo { pub fn gpu_info(&self) -> &GpuInfo {
@ -322,7 +316,7 @@ impl SubmittedCmdBuf {
let item = self.0.take().unwrap(); let item = self.0.take().unwrap();
if let Some(session) = Weak::upgrade(&self.1) { if let Some(session) = Weak::upgrade(&self.1) {
unsafe { unsafe {
session.device.wait_and_reset(&[item.fence])?; session.device.wait_and_reset(&[&item.fence])?;
} }
session session
.cmd_buf_pool .cmd_buf_pool
@ -366,10 +360,9 @@ impl Drop for ImageInner {
} }
} }
/// For now, we deref, but for runtime backend switching we'll need to wrap // Probably migrate from deref here to wrapping all methods.
/// all methods.
impl std::ops::Deref for CmdBuf { impl std::ops::Deref for CmdBuf {
type Target = vulkan::CmdBuf; type Target = mux::CmdBuf;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.cmd_buf &self.cmd_buf
} }
@ -382,13 +375,13 @@ impl std::ops::DerefMut for CmdBuf {
} }
impl Image { impl Image {
pub fn vk_image(&self) -> &vulkan::Image { pub fn mux_image(&self) -> &mux::Image {
&self.0.image &self.0.image
} }
} }
impl Buffer { impl Buffer {
pub fn vk_buffer(&self) -> &vulkan::Buffer { pub fn mux_buffer(&self) -> &mux::Buffer {
&self.0.buffer &self.0.buffer
} }
@ -405,7 +398,7 @@ impl Buffer {
Ok(()) Ok(())
} }
pub unsafe fn read<T: PlainData>(&self, result: &mut Vec<T>) -> Result<(), Error> { pub unsafe fn read<T: PlainData>(&self, result: &mut Vec<T>) -> Result<(), Error> {
let size = self.vk_buffer().size; let size = self.mux_buffer().size();
let len = size as usize / std::mem::size_of::<T>(); let len = size as usize / std::mem::size_of::<T>();
if len > result.len() { if len > result.len() {
result.reserve(len - result.len()); result.reserve(len - result.len());
@ -440,10 +433,10 @@ impl PipelineBuilder {
self self
} }
pub unsafe fn create_compute_pipeline( pub unsafe fn create_compute_pipeline<'a>(
self, self,
session: &Session, session: &Session,
code: &[u8], code: ShaderCode<'a>,
) -> Result<Pipeline, Error> { ) -> Result<Pipeline, Error> {
self.0.create_compute_pipeline(&session.0.device, code) self.0.create_compute_pipeline(&session.0.device, code)
} }
@ -451,23 +444,29 @@ impl PipelineBuilder {
impl DescriptorSetBuilder { impl DescriptorSetBuilder {
pub fn add_buffers<'a>(mut self, buffers: impl IntoRefs<'a, Buffer>) -> Self { pub fn add_buffers<'a>(mut self, buffers: impl IntoRefs<'a, Buffer>) -> Self {
let vk_buffers = buffers let mux_buffers = buffers
.into_refs() .into_refs()
.map(|b| b.vk_buffer()) .map(|b| b.mux_buffer())
.collect::<Vec<_>>(); .collect::<SmallVec<[_; 8]>>();
self.0.add_buffers(&vk_buffers); self.0.add_buffers(&mux_buffers);
self self
} }
pub fn add_images<'a>(mut self, images: impl IntoRefs<'a, Image>) -> Self { pub fn add_images<'a>(mut self, images: impl IntoRefs<'a, Image>) -> Self {
let vk_images = images.into_refs().map(|i| i.vk_image()).collect::<Vec<_>>(); let mux_images = images
self.0.add_images(&vk_images); .into_refs()
.map(|i| i.mux_image())
.collect::<Vec<_>>();
self.0.add_images(&mux_images);
self self
} }
pub fn add_textures<'a>(mut self, images: impl IntoRefs<'a, Image>) -> Self { pub fn add_textures<'a>(mut self, images: impl IntoRefs<'a, Image>) -> Self {
let vk_images = images.into_refs().map(|i| i.vk_image()).collect::<Vec<_>>(); let mux_images = images
self.0.add_textures(&vk_images); .into_refs()
.map(|i| i.mux_image())
.collect::<Vec<_>>();
self.0.add_textures(&mux_images);
self self
} }

View file

@ -6,9 +6,20 @@ use bitflags::bitflags;
pub mod hub; pub mod hub;
#[cfg(target_os = "windows")] #[macro_use]
pub mod dx12; mod macros;
pub mod vulkan;
// TODO make this not pub
pub mod mux;
mux_cfg! {
#[cfg(vk)]
pub mod vulkan;
}
mux_cfg! {
#[cfg(dx12)]
pub mod dx12;
}
/// This isn't great but is expedient. /// This isn't great but is expedient.
pub type Error = Box<dyn std::error::Error>; pub type Error = Box<dyn std::error::Error>;
@ -178,8 +189,8 @@ pub trait Device: Sized {
unsafe fn run_cmd_bufs( unsafe fn run_cmd_bufs(
&self, &self,
cmd_buf: &[&Self::CmdBuf], cmd_buf: &[&Self::CmdBuf],
wait_semaphores: &[Self::Semaphore], wait_semaphores: &[&Self::Semaphore],
signal_semaphores: &[Self::Semaphore], signal_semaphores: &[&Self::Semaphore],
fence: Option<&Self::Fence>, fence: Option<&Self::Fence>,
) -> Result<(), Error>; ) -> Result<(), Error>;
@ -217,8 +228,8 @@ pub trait Device: Sized {
unsafe fn create_semaphore(&self) -> Result<Self::Semaphore, Error>; unsafe fn create_semaphore(&self) -> Result<Self::Semaphore, Error>;
unsafe fn create_fence(&self, signaled: bool) -> Result<Self::Fence, Error>; unsafe fn create_fence(&self, signaled: bool) -> Result<Self::Fence, Error>;
unsafe fn wait_and_reset(&self, fences: &[Self::Fence]) -> Result<(), Error>; unsafe fn wait_and_reset(&self, fences: &[&Self::Fence]) -> Result<(), Error>;
unsafe fn get_fence_status(&self, fence: Self::Fence) -> Result<bool, Error>; unsafe fn get_fence_status(&self, fence: &Self::Fence) -> Result<bool, Error>;
unsafe fn create_sampler(&self, params: SamplerParams) -> Result<Self::Sampler, Error>; unsafe fn create_sampler(&self, params: SamplerParams) -> Result<Self::Sampler, Error>;
} }

104
piet-gpu-hal/src/macros.rs Normal file
View file

@ -0,0 +1,104 @@
// Copyright 2021 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
//! Macros, mostly to automate backend selection tedium.
#[macro_export]
macro_rules! mux_cfg {
( #[cfg(vk)] $($tokens:tt)* ) => {
#[cfg(not(target_os="macos"))] $( $tokens )*
};
( #[cfg(dx12)] $($tokens:tt)* ) => {
#[cfg(target_os="windows")] $( $tokens )*
};
}
#[macro_export]
macro_rules! mux_enum {
( $(#[$outer:meta])* $v:vis enum $name:ident {
Vk($vk:ty),
Dx12($dx12:ty),
} ) => {
$(#[$outer])* $v enum $name {
#[cfg(not(target_os="macos"))]
Vk($vk),
#[cfg(target_os="windows")]
Dx12($dx12),
}
impl $name {
$crate::mux_cfg! {
#[cfg(vk)]
#[allow(unused)]
fn vk(&self) -> &$vk {
match self {
$name::Vk(x) => x,
_ => panic!("downcast error")
}
}
}
$crate::mux_cfg! {
#[cfg(dx12)]
#[allow(unused)]
fn dx12(&self) -> &$dx12 {
match self {
$name::Dx12(x) => x,
_ => panic!("downcast error")
}
}
}
}
};
}
macro_rules! mux_device_enum {
( $(#[$outer:meta])* $assoc_type: ident) => {
$crate::mux_enum! {
$(#[$outer])*
pub enum $assoc_type {
Vk(<$crate::vulkan::VkDevice as $crate::Device>::$assoc_type),
Dx12(<$crate::dx12::Dx12Device as $crate::Device>::$assoc_type),
}
}
}
}
#[macro_export]
macro_rules! mux_match {
( $e:expr ;
$vkname:ident::Vk($vkvar:ident) => $vkblock: block
$dx12name:ident::Dx12($dx12var:ident) => $dx12block: block
) => {
match $e {
#[cfg(not(target_os="macos"))]
$vkname::Vk($vkvar) => $vkblock
#[cfg(target_os="windows")]
$dx12name::Dx12($dx12var) => $dx12block
}
};
( $e:expr ;
$vkname:ident::Vk($vkvar:ident) => $vkblock: expr,
$dx12name:ident::Dx12($dx12var:ident) => $dx12block: expr,
) => {
$crate::mux_match! { $e;
$vkname::Vk($vkvar) => { $vkblock }
$dx12name::Dx12($dx12var) => { $dx12block }
}
};
}

601
piet-gpu-hal/src/mux.rs Normal file
View file

@ -0,0 +1,601 @@
// Copyright 2021 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
//! A multiplexer module that selects a back-end at runtime.
use smallvec::SmallVec;
mux_cfg! {
#[cfg(vk)]
use crate::vulkan;
}
mux_cfg! {
#[cfg(dx12)]
use crate::dx12;
}
use crate::CmdBuf as CmdBufTrait;
use crate::DescriptorSetBuilder as DescriptorSetBuilderTrait;
use crate::Device as DeviceTrait;
use crate::PipelineBuilder as PipelineBuilderTrait;
use crate::{BufferUsage, Error, GpuInfo, ImageLayout};
mux_enum! {
/// An instance, selected from multiple backends.
pub enum Instance {
Vk(vulkan::VkInstance),
Dx12(dx12::Dx12Instance),
}
}
mux_enum! {
/// A device, selected from multiple backends.
pub enum Device {
Vk(vulkan::VkDevice),
Dx12(dx12::Dx12Device),
}
}
mux_enum! {
/// A surface, which can apply to one of multiple backends.
pub enum Surface {
Vk(vulkan::VkSurface),
Dx12(dx12::Dx12Surface),
}
}
mux_enum! {
/// A surface, which can apply to one of multiple backends.
pub enum Swapchain {
Vk(vulkan::VkSwapchain),
Dx12(dx12::Dx12Swapchain),
}
}
mux_device_enum! { Buffer }
mux_device_enum! { Image }
mux_device_enum! { Fence }
mux_device_enum! { Semaphore }
mux_device_enum! { PipelineBuilder }
mux_device_enum! { Pipeline }
mux_device_enum! { DescriptorSetBuilder }
mux_device_enum! { DescriptorSet }
mux_device_enum! { CmdBuf }
mux_device_enum! { QueryPool }
mux_device_enum! { Sampler }
/// The code for a shader, either as source or intermediate representation.
pub enum ShaderCode<'a> {
Spv(&'a [u8]),
Hlsl(&'a str),
}
impl Instance {
pub fn new(
window_handle: Option<&dyn raw_window_handle::HasRawWindowHandle>,
) -> Result<(Instance, Option<Surface>), Error> {
mux_cfg! {
#[cfg(vk)]
{
let result = vulkan::VkInstance::new(window_handle);
if let Ok((instance, surface)) = result {
return Ok((Instance::Vk(instance), surface.map(Surface::Vk)));
}
}
}
mux_cfg! {
#[cfg(dx12)]
{
let result = dx12::Dx12Instance::new(window_handle);
if let Ok((instance, surface)) = result {
return Ok((Instance::Dx12(instance), surface.map(Surface::Dx12)));
}
}
}
// TODO plumb creation errors through.
Err("No suitable instances found".into())
}
pub unsafe fn device(&self, surface: Option<&Surface>) -> Result<Device, Error> {
mux_match! { self;
Instance::Vk(i) => i.device(surface.map(Surface::vk)).map(Device::Vk),
Instance::Dx12(i) => i.device(surface.map(Surface::dx12)).map(Device::Dx12),
}
}
pub unsafe fn swapchain(
&self,
width: usize,
height: usize,
device: &Device,
surface: &Surface,
) -> Result<Swapchain, Error> {
mux_match! { self;
Instance::Vk(i) => i
.swapchain(width, height, device.vk(), surface.vk())
.map(Swapchain::Vk),
Instance::Dx12(_i) => todo!(),
}
}
}
// This is basically re-exporting the backend device trait, and we could do that,
// but not doing so lets us diverge more easily (at the moment, the divergence is
// missing functionality).
impl Device {
pub fn query_gpu_info(&self) -> GpuInfo {
mux_match! { self;
Device::Vk(d) => d.query_gpu_info(),
Device::Dx12(d) => d.query_gpu_info(),
}
}
pub fn create_buffer(&self, size: u64, usage: BufferUsage) -> Result<Buffer, Error> {
mux_match! { self;
Device::Vk(d) => d.create_buffer(size, usage).map(Buffer::Vk),
Device::Dx12(d) => d.create_buffer(size, usage).map(Buffer::Dx12),
}
}
pub unsafe fn destroy_buffer(&self, buffer: &Buffer) -> Result<(), Error> {
mux_match! { self;
Device::Vk(d) => d.destroy_buffer(buffer.vk()),
Device::Dx12(d) => d.destroy_buffer(buffer.dx12()),
}
}
pub unsafe fn create_image2d(&self, width: u32, height: u32) -> Result<Image, Error> {
mux_match! { self;
Device::Vk(d) => d.create_image2d(width, height).map(Image::Vk),
Device::Dx12(d) => d.create_image2d(width, height).map(Image::Dx12),
}
}
pub unsafe fn destroy_image(&self, image: &Image) -> Result<(), Error> {
mux_match! { self;
Device::Vk(d) => d.destroy_image(image.vk()),
Device::Dx12(d) => d.destroy_image(image.dx12()),
}
}
pub unsafe fn create_fence(&self, signaled: bool) -> Result<Fence, Error> {
mux_match! { self;
Device::Vk(d) => d.create_fence(signaled).map(Fence::Vk),
Device::Dx12(d) => d.create_fence(signaled).map(Fence::Dx12),
}
}
pub unsafe fn wait_and_reset(&self, fences: &[&Fence]) -> Result<(), Error> {
mux_match! { self;
Device::Vk(d) => {
let fences = fences
.iter()
.copied()
.map(Fence::vk)
.collect::<SmallVec<[_; 4]>>();
d.wait_and_reset(&*fences)
}
Device::Dx12(d) => {
let fences = fences
.iter()
.copied()
.map(Fence::dx12)
.collect::<SmallVec<[_; 4]>>();
d.wait_and_reset(&*fences)
}
}
}
pub unsafe fn get_fence_status(&self, fence: &Fence) -> Result<bool, Error> {
mux_match! { self;
Device::Vk(d) => d.get_fence_status(fence.vk()),
Device::Dx12(d) => d.get_fence_status(fence.dx12()),
}
}
pub unsafe fn create_semaphore(&self) -> Result<Semaphore, Error> {
mux_match! { self;
Device::Vk(d) => d.create_semaphore().map(Semaphore::Vk),
Device::Dx12(d) => d.create_semaphore().map(Semaphore::Dx12),
}
}
pub unsafe fn pipeline_builder(&self) -> PipelineBuilder {
mux_match! { self;
Device::Vk(d) => PipelineBuilder::Vk(d.pipeline_builder()),
Device::Dx12(d) => PipelineBuilder::Dx12(d.pipeline_builder()),
}
}
pub unsafe fn descriptor_set_builder(&self) -> DescriptorSetBuilder {
mux_match! { self;
Device::Vk(d) => DescriptorSetBuilder::Vk(d.descriptor_set_builder()),
Device::Dx12(d) => DescriptorSetBuilder::Dx12(d.descriptor_set_builder()),
}
}
pub fn create_cmd_buf(&self) -> Result<CmdBuf, Error> {
mux_match! { self;
Device::Vk(d) => d.create_cmd_buf().map(CmdBuf::Vk),
Device::Dx12(d) => d.create_cmd_buf().map(CmdBuf::Dx12),
}
}
pub fn create_query_pool(&self, n_queries: u32) -> Result<QueryPool, Error> {
mux_match! { self;
Device::Vk(d) => d.create_query_pool(n_queries).map(QueryPool::Vk),
Device::Dx12(d) => d.create_query_pool(n_queries).map(QueryPool::Dx12),
}
}
pub unsafe fn fetch_query_pool(&self, pool: &QueryPool) -> Result<Vec<f64>, Error> {
mux_match! { self;
Device::Vk(d) => d.fetch_query_pool(pool.vk()),
Device::Dx12(d) => d.fetch_query_pool(pool.dx12()),
}
}
pub unsafe fn run_cmd_bufs(
&self,
cmd_bufs: &[&CmdBuf],
wait_semaphores: &[&Semaphore],
signal_semaphores: &[&Semaphore],
fence: Option<&Fence>,
) -> Result<(), Error> {
mux_match! { self;
Device::Vk(d) => d.run_cmd_bufs(
&cmd_bufs
.iter()
.map(|c| c.vk())
.collect::<SmallVec<[_; 4]>>(),
&wait_semaphores
.iter()
.copied()
.map(Semaphore::vk)
.collect::<SmallVec<[_; 4]>>(),
&signal_semaphores
.iter()
.copied()
.map(Semaphore::vk)
.collect::<SmallVec<[_; 4]>>(),
fence.map(Fence::vk),
),
Device::Dx12(d) => d.run_cmd_bufs(
&cmd_bufs
.iter()
.map(|c| c.dx12())
.collect::<SmallVec<[_; 4]>>(),
&wait_semaphores
.iter()
.copied()
.map(Semaphore::dx12)
.collect::<SmallVec<[_; 4]>>(),
&signal_semaphores
.iter()
.copied()
.map(Semaphore::dx12)
.collect::<SmallVec<[_; 4]>>(),
fence.map(Fence::dx12),
),
}
}
pub unsafe fn read_buffer(
&self,
buffer: &Buffer,
dst: *mut u8,
offset: u64,
size: u64,
) -> Result<(), Error> {
mux_match! { self;
Device::Vk(d) => d.read_buffer(buffer.vk(), dst, offset, size),
Device::Dx12(d) => d.read_buffer(buffer.dx12(), dst, offset, size),
}
}
pub unsafe fn write_buffer(
&self,
buffer: &Buffer,
contents: *const u8,
offset: u64,
size: u64,
) -> Result<(), Error> {
mux_match! { self;
Device::Vk(d) => d.write_buffer(buffer.vk(), contents, offset, size),
Device::Dx12(d) => d.write_buffer(buffer.dx12(), contents, offset, size),
}
}
}
impl PipelineBuilder {
pub fn add_buffers(&mut self, n_buffers: u32) {
mux_match! { self;
PipelineBuilder::Vk(x) => x.add_buffers(n_buffers),
PipelineBuilder::Dx12(x) => x.add_buffers(n_buffers),
}
}
pub fn add_images(&mut self, n_buffers: u32) {
mux_match! { self;
PipelineBuilder::Vk(x) => x.add_images(n_buffers),
PipelineBuilder::Dx12(x) => x.add_images(n_buffers),
}
}
pub fn add_textures(&mut self, n_buffers: u32) {
mux_match! { self;
PipelineBuilder::Vk(x) => x.add_textures(n_buffers),
PipelineBuilder::Dx12(x) => x.add_textures(n_buffers),
}
}
pub unsafe fn create_compute_pipeline<'a>(
self,
device: &Device,
code: ShaderCode<'a>,
) -> Result<Pipeline, Error> {
mux_match! { self;
PipelineBuilder::Vk(x) => {
let shader_code = match code {
ShaderCode::Spv(spv) => spv,
// Panic or return "incompatible shader" error here?
_ => panic!("Vulkan backend requires shader code in SPIR-V format"),
};
x.create_compute_pipeline(device.vk(), shader_code)
.map(Pipeline::Vk)
}
PipelineBuilder::Dx12(x) => {
let shader_code = match code {
ShaderCode::Hlsl(hlsl) => hlsl,
// Panic or return "incompatible shader" error here?
_ => panic!("DX12 backend requires shader code in HLSL format"),
};
x.create_compute_pipeline(device.dx12(), shader_code)
.map(Pipeline::Dx12)
}
}
}
}
impl DescriptorSetBuilder {
pub fn add_buffers(&mut self, buffers: &[&Buffer]) {
mux_match! { self;
DescriptorSetBuilder::Vk(x) => x.add_buffers(
&buffers
.iter()
.copied()
.map(Buffer::vk)
.collect::<SmallVec<[_; 8]>>(),
),
DescriptorSetBuilder::Dx12(x) => x.add_buffers(
&buffers
.iter()
.copied()
.map(Buffer::dx12)
.collect::<SmallVec<[_; 8]>>(),
),
}
}
pub fn add_images(&mut self, images: &[&Image]) {
mux_match! { self;
DescriptorSetBuilder::Vk(x) => x.add_images(
&images
.iter()
.copied()
.map(Image::vk)
.collect::<SmallVec<[_; 8]>>(),
),
DescriptorSetBuilder::Dx12(x) => x.add_images(
&images
.iter()
.copied()
.map(Image::dx12)
.collect::<SmallVec<[_; 8]>>(),
),
}
}
pub fn add_textures(&mut self, images: &[&Image]) {
mux_match! { self;
DescriptorSetBuilder::Vk(x) => x.add_textures(
&images
.iter()
.copied()
.map(Image::vk)
.collect::<SmallVec<[_; 8]>>(),
),
DescriptorSetBuilder::Dx12(x) => x.add_textures(
&images
.iter()
.copied()
.map(Image::dx12)
.collect::<SmallVec<[_; 8]>>(),
),
}
}
pub unsafe fn build(
self,
device: &Device,
pipeline: &Pipeline,
) -> Result<DescriptorSet, Error> {
mux_match! { self;
DescriptorSetBuilder::Vk(x) =>
x.build(device.vk(), pipeline.vk()).map(DescriptorSet::Vk),
DescriptorSetBuilder::Dx12(x) => x
.build(device.dx12(), pipeline.dx12())
.map(DescriptorSet::Dx12),
}
}
}
impl CmdBuf {
pub unsafe fn begin(&mut self) {
mux_match! { self;
CmdBuf::Vk(c) => c.begin(),
CmdBuf::Dx12(c) => c.begin(),
}
}
pub unsafe fn finish(&mut self) {
mux_match! { self;
CmdBuf::Vk(c) => c.finish(),
CmdBuf::Dx12(c) => c.finish(),
}
}
pub unsafe fn dispatch(
&mut self,
pipeline: &Pipeline,
descriptor_set: &DescriptorSet,
size: (u32, u32, u32),
) {
mux_match! { self;
CmdBuf::Vk(c) => c.dispatch(pipeline.vk(), descriptor_set.vk(), size),
CmdBuf::Dx12(c) => c.dispatch(pipeline.dx12(), descriptor_set.dx12(), size),
}
}
pub unsafe fn memory_barrier(&mut self) {
mux_match! { self;
CmdBuf::Vk(c) => c.memory_barrier(),
CmdBuf::Dx12(c) => c.memory_barrier(),
}
}
pub unsafe fn host_barrier(&mut self) {
mux_match! { self;
CmdBuf::Vk(c) => c.host_barrier(),
CmdBuf::Dx12(c) => c.host_barrier(),
}
}
pub unsafe fn image_barrier(
&mut self,
image: &Image,
src_layout: ImageLayout,
dst_layout: ImageLayout,
) {
mux_match! { self;
CmdBuf::Vk(c) => c.image_barrier(image.vk(), src_layout, dst_layout),
CmdBuf::Dx12(c) => c.image_barrier(image.dx12(), src_layout, dst_layout),
}
}
pub unsafe fn clear_buffer(&mut self, buffer: &Buffer, size: Option<u64>) {
mux_match! { self;
CmdBuf::Vk(c) => c.clear_buffer(buffer.vk(), size),
CmdBuf::Dx12(c) => c.clear_buffer(buffer.dx12(), size),
}
}
pub unsafe fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer) {
mux_match! { self;
CmdBuf::Vk(c) => c.copy_buffer(src.vk(), dst.vk()),
CmdBuf::Dx12(c) => c.copy_buffer(src.dx12(), dst.dx12()),
}
}
pub unsafe fn copy_image_to_buffer(&mut self, src: &Image, dst: &Buffer) {
mux_match! { self;
CmdBuf::Vk(c) => c.copy_image_to_buffer(src.vk(), dst.vk()),
CmdBuf::Dx12(c) => c.copy_image_to_buffer(src.dx12(), dst.dx12()),
}
}
pub unsafe fn copy_buffer_to_image(&mut self, src: &Buffer, dst: &Image) {
mux_match! { self;
CmdBuf::Vk(c) => c.copy_buffer_to_image(src.vk(), dst.vk()),
CmdBuf::Dx12(c) => c.copy_buffer_to_image(src.dx12(), dst.dx12()),
}
}
pub unsafe fn blit_image(&mut self, src: &Image, dst: &Image) {
mux_match! { self;
CmdBuf::Vk(c) => c.blit_image(src.vk(), dst.vk()),
CmdBuf::Dx12(c) => c.blit_image(src.dx12(), dst.dx12()),
}
}
pub unsafe fn reset_query_pool(&mut self, pool: &QueryPool) {
mux_match! { self;
CmdBuf::Vk(c) => c.reset_query_pool(pool.vk()),
CmdBuf::Dx12(c) => c.reset_query_pool(pool.dx12()),
}
}
pub unsafe fn write_timestamp(&mut self, pool: &QueryPool, query: u32) {
mux_match! { self;
CmdBuf::Vk(c) => c.write_timestamp(pool.vk(), query),
CmdBuf::Dx12(c) => c.write_timestamp(pool.dx12(), query),
}
}
pub unsafe fn finish_timestamps(&mut self, pool: &QueryPool) {
mux_match! { self;
CmdBuf::Vk(c) => c.finish_timestamps(pool.vk()),
CmdBuf::Dx12(c) => c.finish_timestamps(pool.dx12()),
}
}
}
impl Buffer {
pub fn size(&self) -> u64 {
mux_match! { self;
Buffer::Vk(b) => b.size,
Buffer::Dx12(b) => b.size,
}
}
}
impl Swapchain {
pub unsafe fn next(&mut self) -> Result<(usize, Semaphore), Error> {
mux_match! { self;
Swapchain::Vk(s) => {
let (idx, sem) = s.next()?;
Ok((idx, Semaphore::Vk(sem)))
}
Swapchain::Dx12(_s) => {
todo!()
}
}
}
pub unsafe fn image(&self, idx: usize) -> Image {
mux_match! { self;
Swapchain::Vk(s) => Image::Vk(s.image(idx)),
Swapchain::Dx12(_s) => todo!(),
}
}
pub unsafe fn present(
&self,
image_idx: usize,
semaphores: &[&Semaphore],
) -> Result<bool, Error> {
mux_match! { self;
Swapchain::Vk(s) => s.present(
image_idx,
&semaphores
.iter()
.copied()
.map(Semaphore::vk)
.collect::<SmallVec<[_; 4]>>(),
),
Swapchain::Dx12(_s) => todo!(),
}
}
}

View file

@ -10,6 +10,8 @@ use ash::extensions::{ext::DebugUtils, khr};
use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0, InstanceV1_1}; use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0, InstanceV1_1};
use ash::{vk, Device, Entry, Instance}; use ash::{vk, Device, Entry, Instance};
use smallvec::SmallVec;
use crate::{ use crate::{
BufferUsage, Device as DeviceTrait, Error, GpuInfo, ImageLayout, SamplerParams, SubgroupSize, BufferUsage, Device as DeviceTrait, Error, GpuInfo, ImageLayout, SamplerParams, SubgroupSize,
}; };
@ -617,16 +619,21 @@ impl crate::Device for VkDevice {
Ok(device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None)?) Ok(device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None)?)
} }
unsafe fn wait_and_reset(&self, fences: &[Self::Fence]) -> Result<(), Error> { unsafe fn wait_and_reset(&self, fences: &[&Self::Fence]) -> Result<(), Error> {
let device = &self.device.device; let device = &self.device.device;
device.wait_for_fences(fences, true, !0)?; let fences = fences
device.reset_fences(fences)?; .iter()
.copied()
.copied()
.collect::<SmallVec<[_; 4]>>();
device.wait_for_fences(&fences, true, !0)?;
device.reset_fences(&fences)?;
Ok(()) Ok(())
} }
unsafe fn get_fence_status(&self, fence: Self::Fence) -> Result<bool, Error> { unsafe fn get_fence_status(&self, fence: &Self::Fence) -> Result<bool, Error> {
let device = &self.device.device; let device = &self.device.device;
Ok(device.get_fence_status(fence)?) Ok(device.get_fence_status(*fence)?)
} }
unsafe fn pipeline_builder(&self) -> PipelineBuilder { unsafe fn pipeline_builder(&self) -> PipelineBuilder {
@ -709,8 +716,8 @@ impl crate::Device for VkDevice {
unsafe fn run_cmd_bufs( unsafe fn run_cmd_bufs(
&self, &self,
cmd_bufs: &[&CmdBuf], cmd_bufs: &[&CmdBuf],
wait_semaphores: &[Self::Semaphore], wait_semaphores: &[&Self::Semaphore],
signal_semaphores: &[Self::Semaphore], signal_semaphores: &[&Self::Semaphore],
fence: Option<&Self::Fence>, fence: Option<&Self::Fence>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let device = &self.device.device; let device = &self.device.device;
@ -722,16 +729,28 @@ impl crate::Device for VkDevice {
let wait_stages = wait_semaphores let wait_stages = wait_semaphores
.iter() .iter()
.map(|_| vk::PipelineStageFlags::ALL_COMMANDS) .map(|_| vk::PipelineStageFlags::ALL_COMMANDS)
.collect::<Vec<_>>(); .collect::<SmallVec<[_; 4]>>();
// Use SmallVec or similar here to reduce allocation? let cmd_bufs = cmd_bufs
let cmd_bufs = cmd_bufs.iter().map(|c| c.cmd_buf).collect::<Vec<_>>(); .iter()
.map(|c| c.cmd_buf)
.collect::<SmallVec<[_; 4]>>();
let wait_semaphores = wait_semaphores
.iter()
.copied()
.copied()
.collect::<SmallVec<[_; 2]>>();
let signal_semaphores = signal_semaphores
.iter()
.copied()
.copied()
.collect::<SmallVec<[_; 2]>>();
device.queue_submit( device.queue_submit(
self.queue, self.queue,
&[vk::SubmitInfo::builder() &[vk::SubmitInfo::builder()
.command_buffers(&cmd_bufs) .command_buffers(&cmd_bufs)
.wait_semaphores(wait_semaphores) .wait_semaphores(&wait_semaphores)
.wait_dst_stage_mask(&wait_stages) .wait_dst_stage_mask(&wait_stages)
.signal_semaphores(signal_semaphores) .signal_semaphores(&signal_semaphores)
.build()], .build()],
fence, fence,
)?; )?;
@ -1287,14 +1306,15 @@ impl VkSwapchain {
pub unsafe fn present( pub unsafe fn present(
&self, &self,
image_idx: usize, image_idx: usize,
semaphores: &[vk::Semaphore], semaphores: &[&vk::Semaphore],
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let semaphores = semaphores.iter().copied().copied().collect::<SmallVec<[_; 4]>>();
Ok(self.swapchain_fn.queue_present( Ok(self.swapchain_fn.queue_present(
self.present_queue, self.present_queue,
&vk::PresentInfoKHR::builder() &vk::PresentInfoKHR::builder()
.swapchains(&[self.swapchain]) .swapchains(&[self.swapchain])
.image_indices(&[image_idx as u32]) .image_indices(&[image_idx as u32])
.wait_semaphores(semaphores) .wait_semaphores(&semaphores)
.build(), .build(),
)?) )?)
} }

View file

@ -12,7 +12,7 @@ use ndk::native_window::NativeWindow;
use ndk_glue::Event; use ndk_glue::Event;
use piet_gpu_hal::hub; use piet_gpu_hal::hub;
use piet_gpu_hal::vulkan::{QueryPool, VkInstance, VkSurface, VkSwapchain}; use piet_gpu_hal::mux::{QueryPool, Instance, Surface, Swapchain};
use piet_gpu_hal::{CmdBuf, Error, ImageLayout}; use piet_gpu_hal::{CmdBuf, Error, ImageLayout};
use piet_gpu::{render_scene, PietGpuRenderContext, Renderer}; use piet_gpu::{render_scene, PietGpuRenderContext, Renderer};
@ -30,7 +30,7 @@ struct MyHandle {
struct GfxState { struct GfxState {
session: hub::Session, session: hub::Session,
renderer: Renderer, renderer: Renderer,
swapchain: VkSwapchain, swapchain: Swapchain,
current_frame: usize, current_frame: usize,
last_frame_idx: usize, last_frame_idx: usize,
submitted: Option<hub::SubmittedCmdBuf>, submitted: Option<hub::SubmittedCmdBuf>,
@ -52,7 +52,7 @@ fn my_main() -> Result<(), Error> {
let window = ndk_glue::native_window(); let window = ndk_glue::native_window();
if let Some(window) = &*window { if let Some(window) = &*window {
let handle = get_handle(window); let handle = get_handle(window);
let (instance, surface) = VkInstance::new(Some(&handle))?; let (instance, surface) = Instance::new(Some(&handle))?;
gfx_state = Some(GfxState::new(&instance, surface.as_ref())?); gfx_state = Some(GfxState::new(&instance, surface.as_ref())?);
} else { } else {
println!("native window is sadly none"); println!("native window is sadly none");
@ -90,7 +90,7 @@ unsafe impl HasRawWindowHandle for MyHandle {
} }
impl GfxState { impl GfxState {
fn new(instance: &VkInstance, surface: Option<&VkSurface>) -> Result<GfxState, Error> { fn new(instance: &Instance, surface: Option<&Surface>) -> Result<GfxState, Error> {
unsafe { unsafe {
let device = instance.device(surface)?; let device = instance.device(surface)?;
let mut swapchain = let mut swapchain =
@ -151,7 +151,7 @@ impl GfxState {
// Image -> Swapchain // Image -> Swapchain
cmd_buf.image_barrier(&swap_image, ImageLayout::Undefined, ImageLayout::BlitDst); cmd_buf.image_barrier(&swap_image, ImageLayout::Undefined, ImageLayout::BlitDst);
cmd_buf.blit_image(self.renderer.image_dev.vk_image(), &swap_image); cmd_buf.blit_image(self.renderer.image_dev.mux_image(), &swap_image);
cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present);
cmd_buf.finish(); cmd_buf.finish();
@ -159,15 +159,15 @@ impl GfxState {
self.session self.session
.run_cmd_buf( .run_cmd_buf(
cmd_buf, cmd_buf,
&[acquisition_semaphore], &[&acquisition_semaphore],
&[self.present_semaphores[frame_idx]], &[&self.present_semaphores[frame_idx]],
) )
.unwrap(), .unwrap(),
); );
self.last_frame_idx = frame_idx; self.last_frame_idx = frame_idx;
self.swapchain self.swapchain
.present(image_idx, &[self.present_semaphores[frame_idx]]) .present(image_idx, &[&self.present_semaphores[frame_idx]])
.unwrap(); .unwrap();
self.current_frame += 1; self.current_frame += 1;

View file

@ -5,8 +5,8 @@ use std::path::Path;
use clap::{App, Arg}; use clap::{App, Arg};
use piet_gpu_hal::hub; use piet_gpu_hal::hub;
use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::mux::Instance;
use piet_gpu_hal::{BufferUsage, CmdBuf, Error}; use piet_gpu_hal::{BufferUsage, Error};
use piet_gpu::{render_scene, render_svg, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; use piet_gpu::{render_scene, render_svg, PietGpuRenderContext, Renderer, HEIGHT, WIDTH};
@ -225,7 +225,7 @@ fn main() -> Result<(), Error> {
.takes_value(true), .takes_value(true),
) )
.get_matches(); .get_matches();
let (instance, _) = VkInstance::new(None)?; let (instance, _) = Instance::new(None)?;
unsafe { unsafe {
let device = instance.device(None)?; let device = instance.device(None)?;
let session = hub::Session::new(device); let session = hub::Session::new(device);
@ -258,7 +258,7 @@ fn main() -> Result<(), Error> {
cmd_buf.begin(); cmd_buf.begin();
renderer.record(&mut cmd_buf, &query_pool); renderer.record(&mut cmd_buf, &query_pool);
cmd_buf.copy_image_to_buffer(renderer.image_dev.vk_image(), image_buf.vk_buffer()); cmd_buf.copy_image_to_buffer(renderer.image_dev.mux_image(), image_buf.mux_buffer());
cmd_buf.host_barrier(); cmd_buf.host_barrier();
cmd_buf.finish(); cmd_buf.finish();
let start = std::time::Instant::now(); let start = std::time::Instant::now();

View file

@ -1,6 +1,6 @@
use piet_gpu_hal::hub; use piet_gpu_hal::hub;
use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::mux::Instance;
use piet_gpu_hal::{CmdBuf, Error, ImageLayout}; use piet_gpu_hal::{Error, ImageLayout};
use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH};
@ -22,7 +22,7 @@ fn main() -> Result<(), Error> {
.with_resizable(false) // currently not supported .with_resizable(false) // currently not supported
.build(&event_loop)?; .build(&event_loop)?;
let (instance, surface) = VkInstance::new(Some(&window))?; let (instance, surface) = Instance::new(Some(&window))?;
unsafe { unsafe {
let device = instance.device(surface.as_ref())?; let device = instance.device(surface.as_ref())?;
let mut swapchain = let mut swapchain =
@ -103,21 +103,21 @@ fn main() -> Result<(), Error> {
ImageLayout::Undefined, ImageLayout::Undefined,
ImageLayout::BlitDst, ImageLayout::BlitDst,
); );
cmd_buf.blit_image(renderer.image_dev.vk_image(), &swap_image); cmd_buf.blit_image(renderer.image_dev.mux_image(), &swap_image);
cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present);
cmd_buf.finish(); cmd_buf.finish();
submitted = Some(session submitted = Some(session
.run_cmd_buf( .run_cmd_buf(
cmd_buf, cmd_buf,
&[acquisition_semaphore], &[&acquisition_semaphore],
&[present_semaphores[frame_idx]], &[&present_semaphores[frame_idx]],
) )
.unwrap()); .unwrap());
last_frame_idx = frame_idx; last_frame_idx = frame_idx;
swapchain swapchain
.present(image_idx, &[present_semaphores[frame_idx]]) .present(image_idx, &[&present_semaphores[frame_idx]])
.unwrap(); .unwrap();
current_frame += 1; current_frame += 1;

View file

@ -13,7 +13,8 @@ use piet::{Color, ImageFormat, RenderContext};
use piet_gpu_types::encoder::Encode; use piet_gpu_types::encoder::Encode;
use piet_gpu_hal::hub; use piet_gpu_hal::hub;
use piet_gpu_hal::{BufferUsage, CmdBuf, Error, ImageLayout}; use piet_gpu_hal::hub::ShaderCode;
use piet_gpu_hal::{BufferUsage, Error, ImageLayout};
use pico_svg::PicoSvg; use pico_svg::PicoSvg;
@ -292,35 +293,35 @@ impl Renderer {
let memory_buf_dev = session.create_buffer(128 * 1024 * 1024, dev)?; let memory_buf_dev = session.create_buffer(128 * 1024 * 1024, dev)?;
memory_buf_host.write(&[alloc as u32, 0 /* Overflow flag */])?; memory_buf_host.write(&[alloc as u32, 0 /* Overflow flag */])?;
let el_code = include_bytes!("../shader/elements.spv"); let el_code = ShaderCode::Spv(include_bytes!("../shader/elements.spv"));
let el_pipeline = session.create_simple_compute_pipeline(el_code, 4)?; let el_pipeline = session.create_simple_compute_pipeline(el_code, 4)?;
let el_ds = session.create_simple_descriptor_set( let el_ds = session.create_simple_descriptor_set(
&el_pipeline, &el_pipeline,
&[&memory_buf_dev, &config_buf, &scene_buf, &state_buf], &[&memory_buf_dev, &config_buf, &scene_buf, &state_buf],
)?; )?;
let tile_alloc_code = include_bytes!("../shader/tile_alloc.spv"); let tile_alloc_code = ShaderCode::Spv(include_bytes!("../shader/tile_alloc.spv"));
let tile_pipeline = session.create_simple_compute_pipeline(tile_alloc_code, 2)?; let tile_pipeline = session.create_simple_compute_pipeline(tile_alloc_code, 2)?;
let tile_ds = session let tile_ds = session
.create_simple_descriptor_set(&tile_pipeline, &[&memory_buf_dev, &config_buf])?; .create_simple_descriptor_set(&tile_pipeline, &[&memory_buf_dev, &config_buf])?;
let path_alloc_code = include_bytes!("../shader/path_coarse.spv"); let path_alloc_code = ShaderCode::Spv(include_bytes!("../shader/path_coarse.spv"));
let path_pipeline = session.create_simple_compute_pipeline(path_alloc_code, 2)?; let path_pipeline = session.create_simple_compute_pipeline(path_alloc_code, 2)?;
let path_ds = session let path_ds = session
.create_simple_descriptor_set(&path_pipeline, &[&memory_buf_dev, &config_buf])?; .create_simple_descriptor_set(&path_pipeline, &[&memory_buf_dev, &config_buf])?;
let backdrop_alloc_code = include_bytes!("../shader/backdrop.spv"); let backdrop_alloc_code = ShaderCode::Spv(include_bytes!("../shader/backdrop.spv"));
let backdrop_pipeline = session.create_simple_compute_pipeline(backdrop_alloc_code, 2)?; let backdrop_pipeline = session.create_simple_compute_pipeline(backdrop_alloc_code, 2)?;
let backdrop_ds = session let backdrop_ds = session
.create_simple_descriptor_set(&backdrop_pipeline, &[&memory_buf_dev, &config_buf])?; .create_simple_descriptor_set(&backdrop_pipeline, &[&memory_buf_dev, &config_buf])?;
// TODO: constants // TODO: constants
let bin_code = include_bytes!("../shader/binning.spv"); let bin_code = ShaderCode::Spv(include_bytes!("../shader/binning.spv"));
let bin_pipeline = session.create_simple_compute_pipeline(bin_code, 2)?; let bin_pipeline = session.create_simple_compute_pipeline(bin_code, 2)?;
let bin_ds = let bin_ds =
session.create_simple_descriptor_set(&bin_pipeline, &[&memory_buf_dev, &config_buf])?; session.create_simple_descriptor_set(&bin_pipeline, &[&memory_buf_dev, &config_buf])?;
let coarse_code = include_bytes!("../shader/coarse.spv"); let coarse_code = ShaderCode::Spv(include_bytes!("../shader/coarse.spv"));
let coarse_pipeline = session.create_simple_compute_pipeline(coarse_code, 2)?; let coarse_pipeline = session.create_simple_compute_pipeline(coarse_code, 2)?;
let coarse_ds = session let coarse_ds = session
.create_simple_descriptor_set(&coarse_pipeline, &[&memory_buf_dev, &config_buf])?; .create_simple_descriptor_set(&coarse_pipeline, &[&memory_buf_dev, &config_buf])?;
@ -328,10 +329,10 @@ impl Renderer {
let bg_image = Self::make_test_bg_image(&session); let bg_image = Self::make_test_bg_image(&session);
let k4_code = if session.gpu_info().has_descriptor_indexing { let k4_code = if session.gpu_info().has_descriptor_indexing {
&include_bytes!("../shader/kernel4_idx.spv")[..] ShaderCode::Spv(include_bytes!("../shader/kernel4_idx.spv"))
} else { } else {
println!("doing non-indexed k4"); println!("doing non-indexed k4");
&include_bytes!("../shader/kernel4.spv")[..] ShaderCode::Spv(include_bytes!("../shader/kernel4.spv"))
}; };
// This is an arbitrary limit on the number of textures that can be referenced by // This is an arbitrary limit on the number of textures that can be referenced by
// the fine rasterizer. To set it for real, we probably want to pay attention both // the fine rasterizer. To set it for real, we probably want to pay attention both
@ -386,13 +387,13 @@ impl Renderer {
pub unsafe fn record(&self, cmd_buf: &mut hub::CmdBuf, query_pool: &hub::QueryPool) { pub unsafe fn record(&self, cmd_buf: &mut hub::CmdBuf, query_pool: &hub::QueryPool) {
cmd_buf.copy_buffer( cmd_buf.copy_buffer(
self.memory_buf_host.vk_buffer(), self.memory_buf_host.mux_buffer(),
self.memory_buf_dev.vk_buffer(), self.memory_buf_dev.mux_buffer(),
); );
cmd_buf.clear_buffer(self.state_buf.vk_buffer(), None); cmd_buf.clear_buffer(self.state_buf.mux_buffer(), None);
cmd_buf.memory_barrier(); cmd_buf.memory_barrier();
cmd_buf.image_barrier( cmd_buf.image_barrier(
self.image_dev.vk_image(), self.image_dev.mux_image(),
ImageLayout::Undefined, ImageLayout::Undefined,
ImageLayout::General, ImageLayout::General,
); );
@ -451,7 +452,7 @@ impl Renderer {
cmd_buf.write_timestamp(&query_pool, 7); cmd_buf.write_timestamp(&query_pool, 7);
cmd_buf.memory_barrier(); cmd_buf.memory_barrier();
cmd_buf.image_barrier( cmd_buf.image_barrier(
self.image_dev.vk_image(), self.image_dev.mux_image(),
ImageLayout::General, ImageLayout::General,
ImageLayout::BlitSrc, ImageLayout::BlitSrc,
); );
@ -475,12 +476,16 @@ impl Renderer {
let mut cmd_buf = session.cmd_buf()?; let mut cmd_buf = session.cmd_buf()?;
cmd_buf.begin(); cmd_buf.begin();
cmd_buf.image_barrier( cmd_buf.image_barrier(
image.vk_image(), image.mux_image(),
ImageLayout::Undefined, ImageLayout::Undefined,
ImageLayout::BlitDst, ImageLayout::BlitDst,
); );
cmd_buf.copy_buffer_to_image(buffer.vk_buffer(), image.vk_image()); cmd_buf.copy_buffer_to_image(buffer.mux_buffer(), image.mux_image());
cmd_buf.image_barrier(image.vk_image(), ImageLayout::BlitDst, ImageLayout::General); cmd_buf.image_barrier(
image.mux_image(),
ImageLayout::BlitDst,
ImageLayout::General,
);
cmd_buf.finish(); cmd_buf.finish();
// Make sure not to drop the buffer and image until the command buffer completes. // Make sure not to drop the buffer and image until the command buffer completes.
cmd_buf.add_resource(&buffer); cmd_buf.add_resource(&buffer);