diff --git a/Cargo.lock b/Cargo.lock index a87a519..a04cbf4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -849,6 +849,7 @@ version = "0.1.0" dependencies = [ "ash", "ash-window", + "bitflags", "raw-window-handle", "winapi 0.3.9", "wio", diff --git a/piet-gpu-hal/Cargo.toml b/piet-gpu-hal/Cargo.toml index e7c4766..abbe98a 100644 --- a/piet-gpu-hal/Cargo.toml +++ b/piet-gpu-hal/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" ash = "0.31" ash-window = "0.5" raw-window-handle = "0.3" +bitflags = "1.2.1" winapi = { version = "0.3.9", features = [ 'd3d12', 'd3d12sdklayers', 'd3dcommon', 'd3dcompiler', 'dxgi', diff --git a/piet-gpu-hal/examples/collatz.rs b/piet-gpu-hal/examples/collatz.rs index 84a6fed..ddf6769 100644 --- a/piet-gpu-hal/examples/collatz.rs +++ b/piet-gpu-hal/examples/collatz.rs @@ -1,16 +1,16 @@ use piet_gpu_hal::hub; use piet_gpu_hal::vulkan::VkInstance; -use piet_gpu_hal::{CmdBuf, MemFlags}; +use piet_gpu_hal::{BufferUsage, CmdBuf}; fn main() { let (instance, _) = VkInstance::new(None).unwrap(); unsafe { let device = instance.device(None).unwrap(); let session = hub::Session::new(device); - let mem_flags = MemFlags::host_coherent(); + let usage = BufferUsage::MAP_READ | BufferUsage::MAP_WRITE | BufferUsage::STORAGE; let src = (0..256).map(|x| x + 1).collect::>(); let mut buffer = session - .create_buffer(std::mem::size_of_val(&src[..]) as u64, mem_flags) + .create_buffer(std::mem::size_of_val(&src[..]) as u64, usage) .unwrap(); buffer.write(&src).unwrap(); let code = include_bytes!("./shader/collatz.spv"); diff --git a/piet-gpu-hal/src/hub.rs b/piet-gpu-hal/src/hub.rs index d848d4b..d98108c 100644 --- a/piet-gpu-hal/src/hub.rs +++ b/piet-gpu-hal/src/hub.rs @@ -10,9 +10,8 @@ use std::sync::{Arc, Mutex, Weak}; use crate::vulkan; use crate::DescriptorSetBuilder as DescriptorSetBuilderTrait; use crate::PipelineBuilder as PipelineBuilderTrait; -use crate::{Device, Error, GpuInfo, SamplerParams}; +use crate::{BufferUsage, Device, Error, GpuInfo, SamplerParams}; -pub type MemFlags = ::MemFlags; pub type Semaphore = ::Semaphore; pub type Pipeline = ::Pipeline; pub type DescriptorSet = ::DescriptorSet; @@ -143,8 +142,8 @@ impl Session { )) } - pub fn create_buffer(&self, size: u64, mem_flags: MemFlags) -> Result { - let buffer = self.0.device.create_buffer(size, mem_flags)?; + pub fn create_buffer(&self, size: u64, usage: BufferUsage) -> Result { + let buffer = self.0.device.create_buffer(size, usage)?; Ok(Buffer(Arc::new(BufferInner { buffer, session: Arc::downgrade(&self.0), @@ -155,9 +154,8 @@ impl Session { &self, width: u32, height: u32, - mem_flags: MemFlags, ) -> Result { - let image = self.0.device.create_image2d(width, height, mem_flags)?; + let image = self.0.device.create_image2d(width, height)?; Ok(Image(Arc::new(ImageInner { image, session: Arc::downgrade(&self.0), diff --git a/piet-gpu-hal/src/lib.rs b/piet-gpu-hal/src/lib.rs index 9a8772d..9cf121a 100644 --- a/piet-gpu-hal/src/lib.rs +++ b/piet-gpu-hal/src/lib.rs @@ -2,6 +2,9 @@ /// /// This abstraction is inspired by gfx-hal, but is specialized to the needs of piet-gpu. /// In time, it may go away and be replaced by either gfx-hal or wgpu. + +use bitflags::bitflags; + pub mod hub; #[cfg(target_os = "windows")] @@ -31,6 +34,25 @@ pub enum SamplerParams { Linear, } +bitflags! { + /// The intended usage for this buffer. + pub struct BufferUsage: u32 { + /// The buffer can be mapped for reading CPU-side. + const MAP_READ = 0x1; + /// The buffer can be mapped for writing CPU-side. + const MAP_WRITE = 0x2; + /// The buffer can be copied from. + const COPY_SRC = 0x4; + /// The buffer can be copied to. + const COPY_DST = 0x8; + /// The buffer can be bound to a compute shader. + const STORAGE = 0x80; + /// The buffer can be used to store the results of queries. + const QUERY_RESOLVE = 0x200; + // May add other types. + } +} + #[derive(Clone, Debug)] /// Information about the GPU. pub struct GpuInfo { @@ -57,7 +79,6 @@ pub struct SubgroupSize { pub trait Device: Sized { type Buffer: 'static; type Image; - type MemFlags: MemFlags; type Pipeline; type DescriptorSet; type QueryPool; @@ -75,7 +96,7 @@ pub trait Device: Sized { /// the info. fn query_gpu_info(&self) -> GpuInfo; - fn create_buffer(&self, size: u64, mem_flags: Self::MemFlags) -> Result; + fn create_buffer(&self, size: u64, usage: BufferUsage) -> Result; /// Destroy a buffer. /// @@ -89,7 +110,6 @@ pub trait Device: Sized { &self, width: u32, height: u32, - mem_flags: Self::MemFlags, ) -> Result; /// Destroy an image. @@ -250,12 +270,6 @@ pub trait CmdBuf { unsafe fn finish_timestamps(&mut self, pool: &D::QueryPool) {} } -pub trait MemFlags: Sized + Clone + Copy { - fn device_local() -> Self; - - fn host_coherent() -> Self; -} - /// A builder for pipelines with more complex layouts. pub trait PipelineBuilder { /// Add buffers to the pipeline. Each has its own binding. diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 3339bce..65dc14b 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -9,7 +9,7 @@ use ash::extensions::{ext::DebugUtils, khr}; use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0, InstanceV1_1}; use ash::{vk, Device, Entry, Instance}; -use crate::{Device as DeviceTrait, Error, GpuInfo, ImageLayout, SamplerParams, SubgroupSize}; +use crate::{BufferUsage, Device as DeviceTrait, Error, GpuInfo, ImageLayout, SamplerParams, SubgroupSize}; pub struct VkInstance { /// Retain the dynamic lib. @@ -447,7 +447,6 @@ impl crate::Device for VkDevice { type DescriptorSet = DescriptorSet; type Pipeline = Pipeline; type QueryPool = QueryPool; - type MemFlags = MemFlags; type Fence = vk::Fence; type Semaphore = vk::Semaphore; type PipelineBuilder = PipelineBuilder; @@ -459,9 +458,19 @@ impl crate::Device for VkDevice { self.gpu_info.clone() } - fn create_buffer(&self, size: u64, mem_flags: MemFlags) -> Result { + fn create_buffer(&self, size: u64, usage: BufferUsage) -> Result { unsafe { let device = &self.device.device; + let mut vk_usage = vk::BufferUsageFlags::empty(); + if usage.contains(BufferUsage::STORAGE) { + vk_usage |= vk::BufferUsageFlags::STORAGE_BUFFER; + } + if usage.contains(BufferUsage::COPY_SRC) { + vk_usage |= vk::BufferUsageFlags::TRANSFER_SRC; + } + if usage.contains(BufferUsage::COPY_DST) { + vk_usage |= vk::BufferUsageFlags::TRANSFER_DST; + } let buffer = device.create_buffer( &vk::BufferCreateInfo::builder() .size(size) @@ -474,9 +483,10 @@ impl crate::Device for VkDevice { None, )?; let mem_requirements = device.get_buffer_memory_requirements(buffer); + let mem_flags = memory_property_flags_for_usage(usage); let mem_type = find_memory_type( mem_requirements.memory_type_bits, - mem_flags.0, + mem_flags, &self.device_mem_props, ) .unwrap(); // TODO: proper error @@ -506,7 +516,6 @@ impl crate::Device for VkDevice { &self, width: u32, height: u32, - mem_flags: Self::MemFlags, ) -> Result { let device = &self.device.device; let extent = vk::Extent3D { @@ -534,9 +543,10 @@ impl crate::Device for VkDevice { None, )?; let mem_requirements = device.get_image_memory_requirements(image); + let mem_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL; let mem_type = find_memory_type( mem_requirements.memory_type_bits, - mem_flags.0, + mem_flags, &self.device_mem_props, ) .unwrap(); // TODO: proper error @@ -1003,16 +1013,6 @@ impl crate::CmdBuf for CmdBuf { } } -impl crate::MemFlags for MemFlags { - fn device_local() -> Self { - MemFlags(vk::MemoryPropertyFlags::DEVICE_LOCAL) - } - - fn host_coherent() -> Self { - MemFlags(vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT) - } -} - impl crate::PipelineBuilder for PipelineBuilder { fn add_buffers(&mut self, n_buffers: u32) { let start = self.bindings.len() as u32; @@ -1369,6 +1369,16 @@ unsafe fn choose_compute_device( None } +fn memory_property_flags_for_usage(usage: BufferUsage) -> vk::MemoryPropertyFlags { + if usage.intersects(BufferUsage::MAP_READ | BufferUsage::MAP_WRITE) { + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT + } else { + vk::MemoryPropertyFlags::DEVICE_LOCAL + } +} + +// This could get more sophisticated about asking for CACHED when appropriate, but is +// probably going to get replaced by a gpu-alloc solution anyway. fn find_memory_type( memory_type_bits: u32, property_flags: vk::MemoryPropertyFlags, diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 646a505..53201f8 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -6,7 +6,7 @@ use clap::{App, Arg}; use piet_gpu_hal::hub; use piet_gpu_hal::vulkan::VkInstance; -use piet_gpu_hal::{CmdBuf, Error, MemFlags}; +use piet_gpu_hal::{BufferUsage, CmdBuf, Error}; use piet_gpu::{render_scene, render_svg, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; @@ -253,8 +253,9 @@ fn main() -> Result<(), Error> { //dump_scene(&scene); let renderer = Renderer::new(&session, scene, n_paths, n_pathseg, n_trans)?; + let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST; let image_buf = - session.create_buffer((WIDTH * HEIGHT * 4) as u64, MemFlags::host_coherent())?; + session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?; cmd_buf.begin(); renderer.record(&mut cmd_buf, &query_pool); diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 6c85bef..26b3c44 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -13,7 +13,7 @@ use piet::{Color, ImageFormat, RenderContext}; use piet_gpu_types::encoder::Encode; use piet_gpu_hal::hub; -use piet_gpu_hal::{CmdBuf, Error, ImageLayout, MemFlags}; +use piet_gpu_hal::{BufferUsage, CmdBuf, Error, ImageLayout}; use pico_svg::PicoSvg; @@ -237,8 +237,8 @@ impl Renderer { n_pathseg: usize, n_trans: usize, ) -> Result { - let host = MemFlags::host_coherent(); - let dev = MemFlags::device_local(); + let dev = BufferUsage::STORAGE | BufferUsage::COPY_DST; + let host_upload = BufferUsage::MAP_WRITE | BufferUsage::COPY_SRC; let n_elements = scene.len() / piet_gpu_types::scene::Element::fixed_size(); println!( @@ -247,7 +247,7 @@ impl Renderer { ); let mut scene_buf_host = session - .create_buffer(std::mem::size_of_val(&scene[..]) as u64, host) + .create_buffer(std::mem::size_of_val(&scene[..]) as u64, host_upload) .unwrap(); let scene_buf_dev = session .create_buffer(std::mem::size_of_val(&scene[..]) as u64, dev) @@ -255,10 +255,10 @@ impl Renderer { scene_buf_host.write(&scene)?; let state_buf = session.create_buffer(1 * 1024 * 1024, dev)?; - let image_dev = session.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; + let image_dev = session.create_image2d(WIDTH as u32, HEIGHT as u32)?; const CONFIG_SIZE: u64 = 10 * 4; // Size of Config in setup.h. - let mut config_buf_host = session.create_buffer(CONFIG_SIZE, host)?; + let mut config_buf_host = session.create_buffer(CONFIG_SIZE, host_upload)?; let config_buf_dev = session.create_buffer(CONFIG_SIZE, dev)?; // TODO: constants @@ -293,7 +293,7 @@ impl Renderer { trans_base as u32, ])?; - let mut memory_buf_host = session.create_buffer(2 * 4, host)?; + let mut memory_buf_host = session.create_buffer(2 * 4, host_upload)?; let memory_buf_dev = session.create_buffer(128 * 1024 * 1024, dev)?; memory_buf_host.write(&[alloc as u32, 0 /* Overflow flag */])?; @@ -485,12 +485,11 @@ impl Renderer { if format != ImageFormat::RgbaPremul { return Err("unsupported image format".into()); } - let host_mem_flags = MemFlags::host_coherent(); - let dev_mem_flags = MemFlags::device_local(); - let mut buffer = session.create_buffer(buf.len() as u64, host_mem_flags)?; + let host_upload = BufferUsage::MAP_WRITE | BufferUsage::COPY_SRC; + let mut buffer = session.create_buffer(buf.len() as u64, host_upload)?; buffer.write(buf)?; let image = - session.create_image2d(width.try_into()?, height.try_into()?, dev_mem_flags)?; + session.create_image2d(width.try_into()?, height.try_into()?)?; let mut cmd_buf = session.cmd_buf()?; cmd_buf.begin(); cmd_buf.image_barrier(