From b38e43f0c25b2ad2435cba7f22b3d9dee3abb1b2 Mon Sep 17 00:00:00 2001 From: msiglreith Date: Thu, 30 Apr 2020 15:02:48 +0200 Subject: [PATCH 1/3] Initial work for surface support surface: handle extensions Implement swapchain creation and blit image to screen --- piet-gpu-hal/Cargo.toml | 2 + piet-gpu-hal/examples/collatz.rs | 4 +- piet-gpu-hal/src/lib.rs | 49 ++- piet-gpu-hal/src/vulkan.rs | 591 ++++++++++++++++++++++++++----- piet-gpu/Cargo.toml | 41 +-- piet-gpu/shader/kernel4.comp | 19 +- piet-gpu/src/main.rs | 235 +++++++++--- 7 files changed, 776 insertions(+), 165 deletions(-) diff --git a/piet-gpu-hal/Cargo.toml b/piet-gpu-hal/Cargo.toml index 7019002..d2edbd7 100644 --- a/piet-gpu-hal/Cargo.toml +++ b/piet-gpu-hal/Cargo.toml @@ -9,3 +9,5 @@ edition = "2018" [dependencies] ash = "0.30" once_cell = "1.3.1" +ash-window = { git = "https://github.com/norse-rs/ash-window.git", branch = "dyn_trait" } +raw-window-handle = "0.3" diff --git a/piet-gpu-hal/examples/collatz.rs b/piet-gpu-hal/examples/collatz.rs index a4777b4..fed6a1d 100644 --- a/piet-gpu-hal/examples/collatz.rs +++ b/piet-gpu-hal/examples/collatz.rs @@ -2,9 +2,9 @@ use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::{CmdBuf, Device, MemFlags}; fn main() { - let instance = VkInstance::new().unwrap(); + let (instance, _) = VkInstance::new(None).unwrap(); unsafe { - let device = instance.device().unwrap(); + let device = instance.device(None).unwrap(); let mem_flags = MemFlags::host_coherent(); let src = (0..256).map(|x| x + 1).collect::>(); let buffer = device diff --git a/piet-gpu-hal/src/lib.rs b/piet-gpu-hal/src/lib.rs index d215490..67fe18e 100644 --- a/piet-gpu-hal/src/lib.rs +++ b/piet-gpu-hal/src/lib.rs @@ -5,28 +5,49 @@ pub mod vulkan; /// This isn't great but is expedient. -type Error = Box; +pub type Error = Box; + +#[derive(Copy, Clone, Debug)] +pub enum ImageLayout { + Undefined, + Present, + BlitSrc, + BlitDst, + General, +} pub trait Device: Sized { type Buffer; + type Image; type MemFlags: MemFlags; type Pipeline; type DescriptorSet; type QueryPool; type CmdBuf: CmdBuf; + type Fence; + type Semaphore; fn create_buffer(&self, size: u64, mem_flags: Self::MemFlags) -> Result; + unsafe fn create_image2d( + &self, + width: u32, + height: u32, + mem_flags: Self::MemFlags, + ) -> Result; + unsafe fn create_simple_compute_pipeline( &self, code: &[u8], n_buffers: u32, + n_images: u32, ) -> Result; unsafe fn create_descriptor_set( &self, pipeline: &Self::Pipeline, bufs: &[&Self::Buffer], + images: &[&Self::Image], ) -> Result; fn create_cmd_buf(&self) -> Result; @@ -40,9 +61,15 @@ pub trait Device: Sized { /// /// # Safety /// All submitted commands that refer to this query pool must have completed. - unsafe fn reap_query_pool(&self, pool: Self::QueryPool) -> Result, Error>; + unsafe fn reap_query_pool(&self, pool: &Self::QueryPool) -> Result, Error>; - unsafe fn run_cmd_buf(&self, cmd_buf: &Self::CmdBuf) -> Result<(), Error>; + unsafe fn run_cmd_buf( + &self, + cmd_buf: &Self::CmdBuf, + wait_semaphores: &[Self::Semaphore], + signal_semaphores: &[Self::Semaphore], + fence: Option<&Self::Fence>, + ) -> Result<(), Error>; unsafe fn read_buffer( &self, @@ -55,6 +82,10 @@ pub trait Device: Sized { buffer: &Self::Buffer, contents: &[T], ) -> Result<(), Error>; + + unsafe fn create_semaphore(&self) -> Result; + unsafe fn create_fence(&self, signaled: bool) -> Result; + unsafe fn wait_and_reset(&self, fences: &[Self::Fence]) -> Result<(), Error>; } pub trait CmdBuf { @@ -71,6 +102,13 @@ pub trait CmdBuf { unsafe fn memory_barrier(&mut self); + unsafe fn image_barrier( + &mut self, + image: &D::Image, + src_layout: ImageLayout, + dst_layout: ImageLayout, + ); + /// Clear the buffer. /// /// This is readily supported in Vulkan, but for portability it is remarkably @@ -80,6 +118,11 @@ pub trait CmdBuf { unsafe fn copy_buffer(&self, src: &D::Buffer, dst: &D::Buffer); + unsafe fn copy_image_to_buffer(&self, src: &D::Image, dst: &D::Buffer); + + // low portability, dx12 doesn't support it natively + unsafe fn blit_image(&self, src: &D::Image, dst: &D::Image); + /// Reset the query pool. /// /// The query pool must be reset before each use, to avoid validation errors. diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 2a5abd9..cd44ec7 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -4,12 +4,12 @@ use std::borrow::Cow; use std::ffi::{CStr, CString}; use std::sync::Arc; -use ash::extensions::ext::DebugUtils; +use ash::extensions::{ext::DebugUtils, khr}; use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0}; use ash::{vk, Device, Entry, Instance}; use once_cell::sync::Lazy; -use crate::Error; +use crate::{Device as DeviceTrait, Error, ImageLayout}; pub struct VkInstance { /// Retain the dynamic lib. @@ -22,6 +22,7 @@ pub struct VkInstance { pub struct VkDevice { device: Arc, + physical_device: vk::PhysicalDevice, device_mem_props: vk::PhysicalDeviceMemoryProperties, queue: vk::Queue, qfi: u32, @@ -32,6 +33,23 @@ struct RawDevice { device: Device, } +pub struct VkSurface { + surface: vk::SurfaceKHR, + surface_fn: khr::Surface, +} + +pub struct VkSwapchain { + swapchain: vk::SwapchainKHR, + swapchain_fn: khr::Swapchain, + + present_queue: vk::Queue, + + acquisition_idx: usize, + acquisition_semaphores: Vec, // same length as `images` + images: Vec, + extent: vk::Extent2D, +} + /// A handle to a buffer. /// /// There is no lifetime tracking at this level; the caller is responsible @@ -42,6 +60,13 @@ pub struct Buffer { size: u64, } +pub struct Image { + image: vk::Image, + image_memory: vk::DeviceMemory, + image_view: vk::ImageView, + extent: vk::Extent3D, +} + pub struct Pipeline { pipeline: vk::Pipeline, descriptor_set_layout: vk::DescriptorSetLayout, @@ -88,11 +113,7 @@ unsafe extern "system" fn vulkan_debug_callback( println!( "{:?}:\n{:?} [{} ({})] : {}\n", - message_severity, - message_type, - message_id_name, - message_id_number, - message, + message_severity, message_type, message_id_name, message_id_number, message, ); vk::FALSE @@ -119,40 +140,59 @@ impl VkInstance { /// /// There's more to be done to make this suitable for integration with other /// systems, but for now the goal is to make things simple. - pub fn new() -> Result { + /// + /// The caller is responsible for making sure that window which owns the raw window handle + /// outlives the surface. + pub fn new( + window_handle: Option<&dyn raw_window_handle::HasRawWindowHandle>, + ) -> Result<(VkInstance, Option), Error> { unsafe { let app_name = CString::new("VkToy").unwrap(); let entry = Entry::new()?; - let exist_layers = entry - .enumerate_instance_layer_properties()?; - let layers = LAYERS.iter().filter_map(|&lyr| { - exist_layers - .iter() - .find(|x| - CStr::from_ptr(x.layer_name.as_ptr()) == lyr - ) - .map(|_| lyr.as_ptr()) - .or_else(|| { - println!("Unable to find layer: {}, have you installed the Vulkan SDK?", lyr.to_string_lossy()); - None - }) - }).collect::>(); + let exist_layers = entry.enumerate_instance_layer_properties()?; + let layers = LAYERS + .iter() + .filter_map(|&lyr| { + exist_layers + .iter() + .find(|x| CStr::from_ptr(x.layer_name.as_ptr()) == lyr) + .map(|_| lyr.as_ptr()) + .or_else(|| { + println!( + "Unable to find layer: {}, have you installed the Vulkan SDK?", + lyr.to_string_lossy() + ); + None + }) + }) + .collect::>(); - let exist_exts = entry - .enumerate_instance_extension_properties()?; - let exts = EXTS.iter().filter_map(|&ext| { - exist_exts - .iter() - .find(|x| - CStr::from_ptr(x.extension_name.as_ptr()) == ext - ) - .map(|_| ext.as_ptr()) - .or_else(|| { - println!("Unable to find extension: {}, have you installed the Vulkan SDK?", ext.to_string_lossy()); - None - }) - }).collect::>(); + let exist_exts = entry.enumerate_instance_extension_properties()?; + let mut exts = EXTS + .iter() + .filter_map(|&ext| { + exist_exts + .iter() + .find(|x| CStr::from_ptr(x.extension_name.as_ptr()) == ext) + .map(|_| ext.as_ptr()) + .or_else(|| { + println!( + "Unable to find extension: {}, have you installed the Vulkan SDK?", + ext.to_string_lossy() + ); + None + }) + }) + .collect::>(); + + let surface_extensions = match window_handle { + Some(ref handle) => ash_window::enumerate_required_extensions(*handle)?, + None => vec![], + }; + for extension in surface_extensions { + exts.push(extension.as_ptr()); + } let instance = entry.create_instance( &vk::InstanceCreateInfo::builder() @@ -168,7 +208,7 @@ impl VkInstance { None, )?; - let (_dbg_loader, _dbg_callbk) = if cfg!(debug_assertions) { + let (_dbg_loader, _dbg_callbk) = if false { let dbg_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() .message_severity( vk::DebugUtilsMessageSeverityFlagsEXT::ERROR @@ -185,37 +225,51 @@ impl VkInstance { (None, None) }; - Ok(VkInstance { + let vk_surface = match window_handle { + Some(handle) => Some(VkSurface { + surface: ash_window::create_surface(&entry, &instance, handle, None)?, + surface_fn: khr::Surface::new(&entry, &instance), + }), + None => None, + }; + + let vk_instance = VkInstance { entry, instance, _dbg_loader, _dbg_callbk, - }) + }; + + Ok((vk_instance, vk_surface)) } } - /// Create a device from the instance, suitable for compute. + /// Create a device from the instance, suitable for compute, with an optional surface. /// /// # Safety /// - /// The caller is responsible for making sure that the instance outlives the device. - /// We could enforce that, for example having an `Arc` of the raw instance, but for - /// now keep things simple. - pub unsafe fn device(&self) -> Result { + /// The caller is responsible for making sure that the instance outlives the device + /// and surface. We could enforce that, for example having an `Arc` of the raw instance, + /// but for now keep things simple. + pub unsafe fn device(&self, surface: Option<&VkSurface>) -> Result { let devices = self.instance.enumerate_physical_devices()?; let (pdevice, qfi) = - choose_compute_device(&self.instance, &devices).ok_or("no suitable device")?; + choose_compute_device(&self.instance, &devices, surface).ok_or("no suitable device")?; - let device = self.instance.create_device( - pdevice, - &vk::DeviceCreateInfo::builder().queue_create_infos(&[ - vk::DeviceQueueCreateInfo::builder() - .queue_family_index(qfi) - .queue_priorities(&[1.0]) - .build(), - ]), - None, - )?; + let queue_priorities = [1.0]; + let queue_create_infos = [vk::DeviceQueueCreateInfo::builder() + .queue_family_index(qfi) + .queue_priorities(&queue_priorities) + .build()]; + let extensions = match surface { + Some(_) => vec![khr::Swapchain::name().as_ptr()], + None => vec![], + }; + let create_info = vk::DeviceCreateInfo::builder() + .queue_create_infos(&queue_create_infos) + .enabled_extension_names(&extensions) + .build(); + let device = self.instance.create_device(pdevice, &create_info, None)?; let device_mem_props = self.instance.get_physical_device_memory_properties(pdevice); @@ -229,21 +283,98 @@ impl VkInstance { Ok(VkDevice { device, + physical_device: pdevice, device_mem_props, qfi, queue, timestamp_period, }) } + + pub unsafe fn swapchain( + &self, + device: &VkDevice, + surface: &VkSurface, + ) -> Result { + let formats = surface + .surface_fn + .get_physical_device_surface_formats(device.physical_device, surface.surface)?; + let surface_format = formats + .iter() + .map(|surface_fmt| match surface_fmt.format { + vk::Format::UNDEFINED => { + vk::SurfaceFormatKHR { + format: vk::Format::B8G8R8A8_UNORM, // most common format on desktop + color_space: surface_fmt.color_space, + } + } + _ => *surface_fmt, + }) + .next() + .ok_or("no surface format found")?; + + let capabilities = surface + .surface_fn + .get_physical_device_surface_capabilities(device.physical_device, surface.surface)?; + + let present_modes = surface + .surface_fn + .get_physical_device_surface_present_modes(device.physical_device, surface.surface)?; + + let present_mode = present_modes + .into_iter() + .find(|mode| mode == &vk::PresentModeKHR::MAILBOX) + .unwrap_or(vk::PresentModeKHR::FIFO); + + let image_count = 2; // TODO + let extent = capabilities.current_extent; // TODO: wayland for example will complain here .. + + let create_info = vk::SwapchainCreateInfoKHR::builder() + .surface(surface.surface) + .min_image_count(image_count) + .image_format(surface_format.format) + .image_color_space(surface_format.color_space) + .image_extent(extent) + .image_array_layers(1) + .image_usage(vk::ImageUsageFlags::TRANSFER_DST) + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY) + .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) + .present_mode(present_mode) + .clipped(true); + + let swapchain_fn = khr::Swapchain::new(&self.instance, &device.device.device); + let swapchain = swapchain_fn.create_swapchain(&create_info, None)?; + + let images = swapchain_fn.get_swapchain_images(swapchain)?; + let acquisition_semaphores = (0..images.len()) + .map(|_| device.create_semaphore()) + .collect::, Error>>()?; + + Ok(VkSwapchain { + swapchain, + swapchain_fn, + + present_queue: device.queue, + + images, + acquisition_semaphores, + acquisition_idx: 0, + extent, + }) + } } impl crate::Device for VkDevice { type Buffer = Buffer; + type Image = Image; type CmdBuf = CmdBuf; type DescriptorSet = DescriptorSet; type Pipeline = Pipeline; type QueryPool = QueryPool; type MemFlags = MemFlags; + type Fence = vk::Fence; + type Semaphore = vk::Semaphore; fn create_buffer(&self, size: u64, mem_flags: MemFlags) -> Result { unsafe { @@ -281,6 +412,96 @@ impl crate::Device for VkDevice { } } + unsafe fn create_image2d( + &self, + width: u32, + height: u32, + mem_flags: Self::MemFlags, + ) -> Result { + let device = &self.device.device; + let extent = vk::Extent3D { + width, + height, + depth: 1, + }; + let image = device.create_image( + &vk::ImageCreateInfo::builder() + .image_type(vk::ImageType::TYPE_2D) + .format(vk::Format::R8G8B8A8_UNORM) + .extent(extent) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::OPTIMAL) + .initial_layout(vk::ImageLayout::UNDEFINED) + .usage(vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC) // write in compute and blit src + .sharing_mode(vk::SharingMode::EXCLUSIVE), + None, + )?; + let mem_requirements = device.get_image_memory_requirements(image); + let mem_type = find_memory_type( + mem_requirements.memory_type_bits, + mem_flags.0, + &self.device_mem_props, + ) + .unwrap(); // TODO: proper error + let image_memory = device.allocate_memory( + &vk::MemoryAllocateInfo::builder() + .allocation_size(mem_requirements.size) + .memory_type_index(mem_type), + None, + )?; + device.bind_image_memory(image, image_memory, 0)?; + let image_view = device.create_image_view( + &vk::ImageViewCreateInfo::builder() + .view_type(vk::ImageViewType::TYPE_2D) + .image(image) + .format(vk::Format::R8G8B8A8_UNORM) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }) + .components(vk::ComponentMapping { + r: vk::ComponentSwizzle::IDENTITY, + g: vk::ComponentSwizzle::IDENTITY, + b: vk::ComponentSwizzle::IDENTITY, + a: vk::ComponentSwizzle::IDENTITY, + }) + .build(), + None, + )?; + Ok(Image { + image, + image_memory, + image_view, + extent, + }) + } + + unsafe fn create_fence(&self, signaled: bool) -> Result { + let device = &self.device.device; + let mut flags = vk::FenceCreateFlags::empty(); + if signaled { + flags |= vk::FenceCreateFlags::SIGNALED; + } + Ok(device.create_fence(&vk::FenceCreateInfo::builder().flags(flags).build(), None)?) + } + + unsafe fn create_semaphore(&self) -> Result { + let device = &self.device.device; + Ok(device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None)?) + } + + unsafe fn wait_and_reset(&self, fences: &[Self::Fence]) -> Result<(), Error> { + let device = &self.device.device; + device.wait_for_fences(fences, true, !0)?; + device.reset_fences(fences)?; + Ok(()) + } + /// This creates a pipeline that runs over the buffer. /// /// The descriptor set layout is just some number of buffers (this will change). @@ -288,18 +509,30 @@ impl crate::Device for VkDevice { &self, code: &[u8], n_buffers: u32, + n_images: u32, ) -> Result { let device = &self.device.device; - let bindings = (0..n_buffers) - .map(|i| { + let mut bindings = Vec::new(); + for i in 0..n_buffers { + bindings.push( vk::DescriptorSetLayoutBinding::builder() .binding(i) .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) .descriptor_count(1) .stage_flags(vk::ShaderStageFlags::COMPUTE) - .build() - }) - .collect::>(); + .build(), + ); + } + for i in n_buffers..n_buffers + n_images { + bindings.push( + vk::DescriptorSetLayoutBinding::builder() + .binding(i) + .descriptor_type(vk::DescriptorType::STORAGE_IMAGE) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::COMPUTE) + .build(), + ); + } let descriptor_set_layout = device.create_descriptor_set_layout( &vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings), None, @@ -344,12 +577,26 @@ impl crate::Device for VkDevice { &self, pipeline: &Pipeline, bufs: &[&Buffer], + images: &[&Image], ) -> Result { let device = &self.device.device; - let descriptor_pool_sizes = [vk::DescriptorPoolSize::builder() - .ty(vk::DescriptorType::STORAGE_BUFFER) - .descriptor_count(bufs.len() as u32) - .build()]; + let mut descriptor_pool_sizes = Vec::new(); + if !bufs.is_empty() { + descriptor_pool_sizes.push( + vk::DescriptorPoolSize::builder() + .ty(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(bufs.len() as u32) + .build(), + ); + } + if !images.is_empty() { + descriptor_pool_sizes.push( + vk::DescriptorPoolSize::builder() + .ty(vk::DescriptorType::STORAGE_IMAGE) + .descriptor_count(images.len() as u32) + .build(), + ); + } let descriptor_pool = device.create_descriptor_pool( &vk::DescriptorPoolCreateInfo::builder() .pool_sizes(&descriptor_pool_sizes) @@ -380,6 +627,23 @@ impl crate::Device for VkDevice { &[], ); } + for (i, image) in images.iter().enumerate() { + let binding = i + bufs.len(); + let image_info = vk::DescriptorImageInfo::builder() + .sampler(vk::Sampler::null()) + .image_view(image.image_view) + .image_layout(vk::ImageLayout::GENERAL) + .build(); + device.update_descriptor_sets( + &[vk::WriteDescriptorSet::builder() + .dst_set(descriptor_sets[0]) + .dst_binding(binding as u32) + .descriptor_type(vk::DescriptorType::STORAGE_IMAGE) + .image_info(&[image_info]) + .build()], + &[], + ); + } Ok(DescriptorSet { descriptor_set: descriptor_sets[0], }) @@ -390,7 +654,7 @@ impl crate::Device for VkDevice { let device = &self.device.device; let command_pool = device.create_command_pool( &vk::CommandPoolCreateInfo::builder() - .flags(vk::CommandPoolCreateFlags::empty()) + .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) .queue_family_index(self.qfi), None, )?; @@ -421,7 +685,7 @@ impl crate::Device for VkDevice { } } - unsafe fn reap_query_pool(&self, pool: Self::QueryPool) -> Result, Error> { + unsafe fn reap_query_pool(&self, pool: &Self::QueryPool) -> Result, Error> { let device = &self.device.device; let mut buf = vec![0u64; pool.n_queries as usize]; device.get_query_pool_results( @@ -431,7 +695,6 @@ impl crate::Device for VkDevice { &mut buf, vk::QueryResultFlags::TYPE_64, )?; - device.destroy_query_pool(pool.pool, None); let ts0 = buf[0]; let tsp = self.timestamp_period as f64 * 1e-9; let result = buf[1..] @@ -444,23 +707,33 @@ impl crate::Device for VkDevice { /// Run the command buffer. /// /// This version simply blocks until it's complete. - unsafe fn run_cmd_buf(&self, cmd_buf: &CmdBuf) -> Result<(), Error> { + unsafe fn run_cmd_buf( + &self, + cmd_buf: &CmdBuf, + wait_semaphores: &[Self::Semaphore], + signal_semaphores: &[Self::Semaphore], + fence: Option<&Self::Fence>, + ) -> Result<(), Error> { let device = &self.device.device; - // Run the command buffer. - let fence = device.create_fence( - &vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::empty()), - None, - )?; + let fence = match fence { + Some(fence) => *fence, + None => vk::Fence::null(), + }; + let wait_stages = wait_semaphores + .iter() + .map(|_| vk::PipelineStageFlags::ALL_COMMANDS) + .collect::>(); device.queue_submit( self.queue, &[vk::SubmitInfo::builder() .command_buffers(&[cmd_buf.cmd_buf]) + .wait_semaphores(wait_semaphores) + .signal_semaphores(signal_semaphores) + .wait_dst_stage_mask(&wait_stages) .build()], fence, )?; - device.wait_for_fences(&[fence], true, 100_000_000)?; - // TODO: handle errors better (currently leaks fence and can lead to other problems) Ok(()) } @@ -556,6 +829,37 @@ impl crate::CmdBuf for CmdBuf { ); } + unsafe fn image_barrier( + &mut self, + image: &Image, + src_layout: ImageLayout, + dst_layout: ImageLayout, + ) { + let device = &self.device.device; + device.cmd_pipeline_barrier( + self.cmd_buf, + vk::PipelineStageFlags::ALL_COMMANDS, + vk::PipelineStageFlags::ALL_COMMANDS, + vk::DependencyFlags::empty(), + &[], + &[], + &[vk::ImageMemoryBarrier::builder() + .image(image.image) + .src_access_mask(vk::AccessFlags::MEMORY_WRITE) + .dst_access_mask(vk::AccessFlags::MEMORY_READ) + .old_layout(map_image_layout(src_layout)) + .new_layout(map_image_layout(dst_layout)) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: vk::REMAINING_MIP_LEVELS, + base_array_layer: 0, + layer_count: vk::REMAINING_MIP_LEVELS, + }) + .build()], + ); + } + unsafe fn clear_buffer(&self, buffer: &Buffer) { let device = &self.device.device; device.cmd_fill_buffer(self.cmd_buf, buffer.buffer, 0, vk::WHOLE_SIZE, 0); @@ -572,14 +876,74 @@ impl crate::CmdBuf for CmdBuf { ); } + unsafe fn copy_image_to_buffer(&self, src: &Image, dst: &Buffer) { + let device = &self.device.device; + device.cmd_copy_image_to_buffer( + self.cmd_buf, + src.image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + dst.buffer, + &[vk::BufferImageCopy { + buffer_offset: 0, + buffer_row_length: 0, // tight packing + buffer_image_height: 0, // tight packing + image_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + image_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + image_extent: src.extent, + }], + ); + } + + unsafe fn blit_image(&self, src: &Image, dst: &Image) { + let device = &self.device.device; + device.cmd_blit_image( + self.cmd_buf, + src.image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + dst.image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[vk::ImageBlit { + src_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + src_offsets: [ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: src.extent.width as i32, + y: src.extent.height as i32, + z: src.extent.depth as i32, + }, + ], + dst_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + dst_offsets: [ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: dst.extent.width as i32, + y: dst.extent.height as i32, + z: dst.extent.depth as i32, + }, + ], + }], + vk::Filter::LINEAR, + ); + } + unsafe fn reset_query_pool(&mut self, pool: &QueryPool) { let device = &self.device.device; - device.cmd_reset_query_pool( - self.cmd_buf, - pool.pool, - 0, - pool.n_queries, - ); + device.cmd_reset_query_pool(self.cmd_buf, pool.pool, 0, pool.n_queries); } unsafe fn write_timestamp(&mut self, pool: &QueryPool, query: u32) { @@ -603,13 +967,68 @@ impl crate::MemFlags for MemFlags { } } +impl VkSwapchain { + pub unsafe fn next(&mut self) -> Result<(usize, vk::Semaphore), Error> { + let acquisition_semaphore = self.acquisition_semaphores[self.acquisition_idx]; + let (image_idx, _suboptimal) = self.swapchain_fn.acquire_next_image( + self.swapchain, + !0, + self.acquisition_semaphores[self.acquisition_idx], + vk::Fence::null(), + )?; + self.acquisition_idx = (self.acquisition_idx + 1) % self.acquisition_semaphores.len(); + + Ok((image_idx as usize, acquisition_semaphore)) + } + + pub unsafe fn image(&self, idx: usize) -> Image { + Image { + image: self.images[idx], + image_memory: vk::DeviceMemory::null(), + image_view: vk::ImageView::null(), + extent: vk::Extent3D { + width: self.extent.width, + height: self.extent.height, + depth: 1, + }, + } + } + + pub unsafe fn present( + &self, + image_idx: usize, + semaphores: &[vk::Semaphore], + ) -> Result { + Ok(self.swapchain_fn.queue_present( + self.present_queue, + &vk::PresentInfoKHR::builder() + .swapchains(&[self.swapchain]) + .image_indices(&[image_idx as u32]) + .wait_semaphores(semaphores) + .build(), + )?) + } +} + unsafe fn choose_compute_device( instance: &Instance, devices: &[vk::PhysicalDevice], + surface: Option<&VkSurface>, ) -> Option<(vk::PhysicalDevice, u32)> { for pdevice in devices { let props = instance.get_physical_device_queue_family_properties(*pdevice); for (ix, info) in props.iter().enumerate() { + // Check for surface presentation support + if let Some(surface) = surface { + if !surface + .surface_fn + .get_physical_device_surface_support(*pdevice, ix as u32, surface.surface) + .unwrap() + { + continue; + } + } + if info.queue_flags.contains(vk::QueueFlags::COMPUTE) { return Some((*pdevice, ix as u32)); } @@ -644,3 +1063,13 @@ fn convert_u32_vec(src: &[u8]) -> Vec { }) .collect() } + +fn map_image_layout(layout: ImageLayout) -> vk::ImageLayout { + match layout { + ImageLayout::Undefined => vk::ImageLayout::UNDEFINED, + ImageLayout::Present => vk::ImageLayout::PRESENT_SRC_KHR, + ImageLayout::BlitSrc => vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + ImageLayout::BlitDst => vk::ImageLayout::TRANSFER_DST_OPTIMAL, + ImageLayout::General => vk::ImageLayout::GENERAL, + } +} diff --git a/piet-gpu/Cargo.toml b/piet-gpu/Cargo.toml index 2555e62..d4522f0 100644 --- a/piet-gpu/Cargo.toml +++ b/piet-gpu/Cargo.toml @@ -1,20 +1,21 @@ -[package] -name = "piet-gpu" -version = "0.1.0" -authors = ["Raph Levien "] -description = "A compute-centric GPU 2D renderer." -license = "MIT/Apache-2.0" -edition = "2018" - -[dependencies.piet-gpu-hal] -path = "../piet-gpu-hal" - -[dependencies.piet-gpu-types] -path = "../piet-gpu-types" - -[dependencies] -kurbo = "0.5.11" -piet = "0.0.12" -png = "0.16.2" -rand = "0.7.3" -roxmltree = "0.11" +[package] +name = "piet-gpu" +version = "0.1.0" +authors = ["Raph Levien "] +description = "A compute-centric GPU 2D renderer." +license = "MIT/Apache-2.0" +edition = "2018" + +[dependencies.piet-gpu-hal] +path = "../piet-gpu-hal" + +[dependencies.piet-gpu-types] +path = "../piet-gpu-types" + +[dependencies] +kurbo = "0.5.11" +piet = "0.0.12" +png = "0.16.2" +rand = "0.7.3" +roxmltree = "0.11" +winit = "0.22" \ No newline at end of file diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 1754bba..f152caf 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -24,9 +24,7 @@ layout(set = 0, binding = 2) buffer FillSegBuf { uint[] fill_seg; }; -layout(set = 0, binding = 3) buffer ImageBuf { - uint[] image; -}; +layout(rgba8, set = 0, binding = 2) uniform writeonly image2D image; #include "ptcl.h" #include "segment.h" @@ -65,11 +63,11 @@ void main() { SegChunk seg_chunk = SegChunk_read(seg_chunk_ref); for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * i)); - vec2 line_vec = seg.end - seg.start; - vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; - float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); - df = min(df, length(line_vec * t - dpos)); - } + vec2 line_vec = seg.end - seg.start; + vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; + float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); + df = min(df, length(line_vec * t - dpos)); + } seg_chunk_ref = seg_chunk.next; } while (seg_chunk_ref.offset != 0); fg_rgba = unpackUnorm4x8(stroke.rgba_color).wzyx; @@ -118,8 +116,5 @@ void main() { cmd_ref.offset += Cmd_size; } - // TODO: sRGB - uvec4 s = uvec4(round(vec4(rgb, 1.0) * 255.0)); - uint rgba_packed = s.r | (s.g << 8) | (s.b << 16) | (s.a << 24); - image[xy_uint.y * IMAGE_WIDTH + xy_uint.x] = rgba_packed; + imageStore(image, ivec2(xy_uint), vec4(rgb, 1.0)); } diff --git a/piet-gpu/src/main.rs b/piet-gpu/src/main.rs index c40b4d5..0b7a6fe 100644 --- a/piet-gpu/src/main.rs +++ b/piet-gpu/src/main.rs @@ -8,7 +8,7 @@ use piet::kurbo::{BezPath, Circle, Line, Point, Vec2}; use piet::{Color, RenderContext}; use piet_gpu_hal::vulkan::VkInstance; -use piet_gpu_hal::{CmdBuf, Device, MemFlags}; +use piet_gpu_hal::{CmdBuf, Device, Error, ImageLayout, MemFlags}; mod pico_svg; mod render_ctx; @@ -16,8 +16,14 @@ mod render_ctx; use render_ctx::PietGpuRenderContext; use pico_svg::PicoSvg; -const WIDTH: usize = 2048; -const HEIGHT: usize = 1536; +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + +const WIDTH: usize = TILE_W * WIDTH_IN_TILES; +const HEIGHT: usize = TILE_H * HEIGHT_IN_TILES; const TILE_W: usize = 16; const TILE_H: usize = 16; @@ -34,6 +40,8 @@ const K2_PER_TILE_SIZE: usize = 8; const N_CIRCLES: usize = 1; +const NUM_FRAMES: usize = 2; + fn render_scene(rc: &mut impl RenderContext) { let mut rng = rand::thread_rng(); for _ in 0..N_CIRCLES { @@ -108,10 +116,35 @@ fn dump_k1_data(k1_buf: &[u32]) { } } -fn main() { - let instance = VkInstance::new().unwrap(); +fn main() -> Result<(), Error> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .with_inner_size(winit::dpi::LogicalSize { + width: (WIDTH_IN_TILES * 8) as f64, + height: (HEIGHT_IN_TILES * 8) as f64, + }) + .with_resizable(false) // currently not supported + .build(&event_loop)?; + + let (instance, surface) = VkInstance::new(Some(&window))?; unsafe { - let device = instance.device().unwrap(); + let device = instance.device(surface.as_ref())?; + let mut swapchain = instance.swapchain(&device, surface.as_ref().unwrap())?; + + let mut current_frame = 0; + let present_semaphores = (0..NUM_FRAMES) + .map(|_| device.create_semaphore()) + .collect::, Error>>()?; + let frame_fences = (0..NUM_FRAMES) + .map(|_| device.create_fence(false)) + .collect::, Error>>()?; + let mut cmd_buffers = (0..NUM_FRAMES) + .map(|_| device.create_cmd_buf()) + .collect::, Error>>()?; + let query_pools = (0..NUM_FRAMES) + .map(|_| device.create_query_pool(6)) + .collect::, Error>>()?; + let host = MemFlags::host_coherent(); let dev = MemFlags::device_local(); let mut ctx = PietGpuRenderContext::new(); @@ -124,30 +157,27 @@ fn main() { let scene_dev = device .create_buffer(std::mem::size_of_val(&scene[..]) as u64, dev) .unwrap(); - device.write_buffer(&scene_buf, &scene).unwrap(); - let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev).unwrap(); - let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev).unwrap(); - let segment_buf = device.create_buffer(64 * 1024 * 1024, dev).unwrap(); + device.write_buffer(&scene_buf, &scene)?; + let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; + let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; + let segment_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let fill_seg_buf = device.create_buffer(64 * 1024 * 1024, dev).unwrap(); - let image_buf = device - .create_buffer((WIDTH * HEIGHT * 4) as u64, host) - .unwrap(); - let image_dev = device - .create_buffer((WIDTH * HEIGHT * 4) as u64, dev) - .unwrap(); + let image_buf = device.create_buffer((WIDTH * HEIGHT * 4) as u64, host)?; + let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; - let k1_alloc_buf_host = device.create_buffer(4, host).unwrap(); - let k1_alloc_buf_dev = device.create_buffer(4, dev).unwrap(); + let k1_alloc_buf_host = device.create_buffer(4, host)?; + let k1_alloc_buf_dev = device.create_buffer(4, dev)?; let k1_alloc_start = WIDTH_IN_TILEGROUPS * HEIGHT_IN_TILEGROUPS * TILEGROUP_STRIDE; - device - .write_buffer(&k1_alloc_buf_host, &[k1_alloc_start as u32]) - .unwrap(); + device.write_buffer(&k1_alloc_buf_host, &[k1_alloc_start as u32])?; let k1_code = include_bytes!("../shader/kernel1.spv"); - let k1_pipeline = device.create_simple_compute_pipeline(k1_code, 3).unwrap(); + let k1_pipeline = device + .create_simple_compute_pipeline(k1_code, 3, 0) + .unwrap(); let k1_ds = device .create_descriptor_set( &k1_pipeline, &[&scene_dev, &tilegroup_buf, &k1_alloc_buf_dev], + &[], ) .unwrap(); @@ -158,11 +188,14 @@ fn main() { .write_buffer(&k2s_alloc_buf_host, &[k2s_alloc_start as u32]) .unwrap(); let k2s_code = include_bytes!("../shader/kernel2s.spv"); - let k2s_pipeline = device.create_simple_compute_pipeline(k2s_code, 4).unwrap(); + let k2s_pipeline = device + .create_simple_compute_pipeline(k2s_code, 4, 0) + .unwrap(); let k2s_ds = device .create_descriptor_set( &k2s_pipeline, &[&scene_dev, &tilegroup_buf, &segment_buf, &k2s_alloc_buf_dev], + &[], ) .unwrap(); @@ -193,7 +226,7 @@ fn main() { .write_buffer(&k3_alloc_buf_host, &[k3_alloc_start as u32]) .unwrap(); let k3_code = include_bytes!("../shader/kernel3.spv"); - let k3_pipeline = device.create_simple_compute_pipeline(k3_code, 6).unwrap(); + let k3_pipeline = device.create_simple_compute_pipeline(k3_code, 6, 0).unwrap(); let k3_ds = device .create_descriptor_set( &k3_pipeline, @@ -205,19 +238,16 @@ fn main() { &ptcl_buf, &k3_alloc_buf_dev, ], + &[], ) .unwrap(); let k4_code = include_bytes!("../shader/kernel4.spv"); - let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 4).unwrap(); + let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 3, 1).unwrap(); let k4_ds = device - .create_descriptor_set( - &k4_pipeline, - &[&ptcl_buf, &segment_buf, &fill_seg_buf, &image_dev], - ) + .create_descriptor_set(&k4_pipeline, &[&ptcl_buf, &segment_buf, &fill_seg_buf], &[&image_dev]) .unwrap(); - - let query_pool = device.create_query_pool(6).unwrap(); + let query_pool = &query_pools[0]; let mut cmd_buf = device.create_cmd_buf().unwrap(); cmd_buf.begin(); cmd_buf.copy_buffer(&scene_buf, &scene_dev); @@ -232,6 +262,7 @@ fn main() { cmd_buf.clear_buffer(&tilegroup_buf); cmd_buf.clear_buffer(&ptcl_buf); cmd_buf.memory_barrier(); + cmd_buf.image_barrier(&image_dev, ImageLayout::Undefined, ImageLayout::General); cmd_buf.reset_query_pool(&query_pool); cmd_buf.write_timestamp(&query_pool, 0); cmd_buf.dispatch( @@ -272,10 +303,12 @@ fn main() { ); cmd_buf.write_timestamp(&query_pool, 5); cmd_buf.memory_barrier(); - cmd_buf.copy_buffer(&image_dev, &image_buf); + cmd_buf.image_barrier(&image_dev, ImageLayout::General, ImageLayout::BlitSrc); + cmd_buf.copy_image_to_buffer(&image_dev, &image_buf); cmd_buf.finish(); - device.run_cmd_buf(&cmd_buf).unwrap(); - let timestamps = device.reap_query_pool(query_pool).unwrap(); + device.run_cmd_buf(&cmd_buf, &[], &[], Some(&frame_fences[0]))?; + device.wait_and_reset(&[frame_fences[0]])?; + let timestamps = device.reap_query_pool(&query_pool).unwrap(); println!("Kernel 1 time: {:.3}ms", timestamps[0] * 1e3); println!( "Kernel 2s time: {:.3}ms", @@ -300,21 +333,129 @@ fn main() { dump_k1_data(&k1_data); */ - let mut img_data: Vec = Default::default(); - // Note: because png can use a `&[u8]` slice, we could avoid an extra copy - // (probably passing a slice into a closure). But for now: keep it simple. - device.read_buffer(&image_buf, &mut img_data).unwrap(); + if false { + let mut img_data: Vec = Default::default(); + // Note: because png can use a `&[u8]` slice, we could avoid an extra copy + // (probably passing a slice into a closure). But for now: keep it simple. + device.read_buffer(&image_buf, &mut img_data).unwrap(); - // Write image as PNG file. - let path = Path::new("image.png"); - let file = File::create(path).unwrap(); - let ref mut w = BufWriter::new(file); + // Write image as PNG file. + let path = Path::new("image.png"); + let file = File::create(path).unwrap(); + let ref mut w = BufWriter::new(file); - let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32); - encoder.set_color(png::ColorType::RGBA); - encoder.set_depth(png::BitDepth::Eight); - let mut writer = encoder.write_header().unwrap(); + let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32); + encoder.set_color(png::ColorType::RGBA); + encoder.set_depth(png::BitDepth::Eight); + let mut writer = encoder.write_header().unwrap(); - writer.write_image_data(&img_data).unwrap(); + writer.write_image_data(&img_data).unwrap(); + } + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + match event { + Event::WindowEvent { event, window_id } if window_id == window.id() => { + match event { + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + _ => (), + } + } + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(window_id) if window_id == window.id() => { + let frame_idx = current_frame % NUM_FRAMES; + let query_pool = &query_pools[frame_idx]; + + if current_frame >= NUM_FRAMES { + device.wait_and_reset(&[frame_fences[frame_idx]]).unwrap(); + + let timestamps = device.reap_query_pool(query_pool).unwrap(); + window.set_title(&format!("k1: {:.3}ms, k2: {:.3}ms, k3: {:.3}ms, k4: {:.3}ms", + timestamps[0] * 1e3, + (timestamps[1] - timestamps[0]) * 1e3, + (timestamps[2] - timestamps[1]) * 1e3, + (timestamps[3] - timestamps[2]) * 1e3, + )); + } + + let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); + let swap_image = swapchain.image(image_idx); + let cmd_buf = &mut cmd_buffers[frame_idx]; + cmd_buf.begin(); + cmd_buf.reset_query_pool(&query_pool); + cmd_buf.copy_buffer(&scene_buf, &scene_dev); + cmd_buf.copy_buffer(&k1_alloc_buf_host, &k1_alloc_buf_dev); + cmd_buf.copy_buffer(&k2s_alloc_buf_host, &k2s_alloc_buf_dev); + cmd_buf.copy_buffer(&k3_alloc_buf_host, &k3_alloc_buf_dev); + cmd_buf.clear_buffer(&tilegroup_buf); + cmd_buf.clear_buffer(&ptcl_buf); + cmd_buf.memory_barrier(); + cmd_buf.write_timestamp(&query_pool, 0); + cmd_buf.dispatch( + &k1_pipeline, + &k1_ds, + ((WIDTH / 512) as u32, (HEIGHT / 512) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 1); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &k2s_pipeline, + &k2s_ds, + ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 2); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &k3_pipeline, + &k3_ds, + ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 3); + cmd_buf.memory_barrier(); + cmd_buf.image_barrier(&image_dev, ImageLayout::BlitSrc, ImageLayout::General); + cmd_buf.dispatch( + &k4_pipeline, + &k4_ds, + ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 4); + cmd_buf.memory_barrier(); + cmd_buf.image_barrier( + &swap_image, + ImageLayout::Undefined, + ImageLayout::BlitDst, + ); + cmd_buf.image_barrier(&image_dev, ImageLayout::General, ImageLayout::BlitSrc); + cmd_buf.blit_image(&image_dev, &swap_image); + cmd_buf.image_barrier( + &swap_image, + ImageLayout::BlitDst, + ImageLayout::Present, + ); + cmd_buf.finish(); + + device + .run_cmd_buf( + &cmd_buf, + &[acquisition_semaphore], + &[present_semaphores[frame_idx]], + Some(&frame_fences[frame_idx]), + ) + .unwrap(); + + swapchain + .present(image_idx, &[present_semaphores[frame_idx]]) + .unwrap(); + + current_frame += 1; + } + _ => (), + } + }) } } From e2ed54361de33850991a266dc376dbf0eccca9a0 Mon Sep 17 00:00:00 2001 From: msiglreith Date: Mon, 4 May 2020 17:05:54 +0200 Subject: [PATCH 2/3] Fix rebase issues and split into library and cli/winit binaries --- Cargo.lock | 688 ++++++++++++++++++++++++++++++++++- piet-gpu/Cargo.toml | 50 +-- piet-gpu/bin/cli.rs | 89 +++++ piet-gpu/bin/winit.rs | 122 +++++++ piet-gpu/shader/kernel4.comp | 2 +- piet-gpu/shader/kernel4.spv | Bin 20832 -> 20180 bytes piet-gpu/src/lib.rs | 321 ++++++++++++++++ piet-gpu/src/main.rs | 461 ----------------------- 8 files changed, 1242 insertions(+), 491 deletions(-) create mode 100644 piet-gpu/bin/cli.rs create mode 100644 piet-gpu/bin/winit.rs create mode 100644 piet-gpu/src/lib.rs delete mode 100644 piet-gpu/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index af141b5..a3e7735 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,35 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +[[package]] +name = "andrew" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" +dependencies = [ + "bitflags", + "line_drawing", + "rusttype 0.7.9", + "walkdir", + "xdg", + "xml-rs", +] + +[[package]] +name = "android_glue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" + +[[package]] +name = "approx" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayvec" version = "0.5.1" @@ -21,18 +50,50 @@ dependencies = [ "libloading", ] +[[package]] +name = "ash-window" +version = "0.3.0" +source = "git+https://github.com/norse-rs/ash-window.git?branch=dyn_trait#ff8cd779b339067f00e808add1203f6c0d074ba5" +dependencies = [ + "ash", + "raw-window-handle", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +[[package]] +name = "calloop" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160" +dependencies = [ + "mio", + "mio-extras", + "nix", +] + [[package]] name = "cc" version = "1.0.50" @@ -45,6 +106,71 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "cocoa" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + +[[package]] +name = "core-graphics" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "core-video-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc065219542086f72d1e9f7aadbbab0989e980263695d129d502082d063a9d0" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "core-graphics", + "libc", + "objc", +] + [[package]] name = "crc32fast" version = "1.2.0" @@ -64,6 +190,58 @@ dependencies = [ "byteorder", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "getrandom" version = "0.1.14" @@ -90,6 +268,31 @@ dependencies = [ "adler32", ] +[[package]] +name = "instant" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7152d2aed88aa566e7a342250f21ba2222c1ae230ad577499dbfa3c18475b80" + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "kurbo" version = "0.5.11" @@ -98,6 +301,18 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" + [[package]] name = "libc" version = "0.2.69" @@ -111,7 +326,144 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ "cc", - "winapi", + "winapi 0.3.8", +] + +[[package]] +name = "line_drawing" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" +dependencies = [ + "num-traits", +] + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "nix" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", ] [[package]] @@ -120,6 +472,45 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +[[package]] +name = "ordered-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.8", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "piet" version = "0.0.12" @@ -140,14 +531,15 @@ dependencies = [ "png", "rand", "roxmltree", + "winit", ] [[package]] name = "piet-gpu-derive" version = "0.0.0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.10", + "quote 1.0.3", "syn", ] @@ -156,7 +548,9 @@ name = "piet-gpu-hal" version = "0.1.0" dependencies = [ "ash", + "ash-window", "once_cell", + "raw-window-handle", ] [[package]] @@ -167,6 +561,12 @@ dependencies = [ "piet-gpu-derive", ] +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + [[package]] name = "png" version = "0.16.2" @@ -185,13 +585,31 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.0", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", ] [[package]] @@ -200,7 +618,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.10", ] [[package]] @@ -244,6 +662,21 @@ dependencies = [ "rand_core", ] +[[package]] +name = "raw-window-handle" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" +dependencies = [ + "libc", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + [[package]] name = "roxmltree" version = "0.11.0" @@ -253,29 +686,190 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rusttype" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" +dependencies = [ + "rusttype 0.8.3", +] + +[[package]] +name = "rusttype" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" +dependencies = [ + "approx", + "ordered-float", + "stb_truetype", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "smithay-client-toolkit" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421c8dc7acf5cb205b88160f8b4cc2c5cfabe210e43b2f80f009f4c1ef910f1d" +dependencies = [ + "andrew", + "bitflags", + "dlib", + "lazy_static", + "memmap", + "nix", + "wayland-client", + "wayland-protocols", +] + +[[package]] +name = "stb_truetype" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" +dependencies = [ + "byteorder", +] + [[package]] name = "syn" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.10", + "quote 1.0.3", + "unicode-xid 0.2.0", ] +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi 0.3.8", + "winapi-util", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wayland-client" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" +dependencies = [ + "bitflags", + "calloop", + "downcast-rs", + "libc", + "mio", + "nix", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" +dependencies = [ + "nix", + "wayland-sys", +] + +[[package]] +name = "wayland-protocols" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" +dependencies = [ + "bitflags", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" +dependencies = [ + "dlib", + "lazy_static", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.8" @@ -286,18 +880,96 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winit" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc53342d3d1a3d57f3949e0692d93d5a8adb7814d8683cef4a09c2b550e94246" +dependencies = [ + "android_glue", + "bitflags", + "cocoa", + "core-foundation", + "core-graphics", + "core-video-sys", + "dispatch", + "instant", + "lazy_static", + "libc", + "log", + "mio", + "mio-extras", + "objc", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "smithay-client-toolkit", + "wayland-client", + "winapi 0.3.8", + "x11-dl", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "x11-dl" +version = "2.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" +dependencies = [ + "lazy_static", + "libc", + "maybe-uninit", + "pkg-config", +] + +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" + +[[package]] +name = "xml-rs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + [[package]] name = "xmlparser" version = "0.13.1" diff --git a/piet-gpu/Cargo.toml b/piet-gpu/Cargo.toml index d4522f0..a922a46 100644 --- a/piet-gpu/Cargo.toml +++ b/piet-gpu/Cargo.toml @@ -1,21 +1,29 @@ -[package] -name = "piet-gpu" -version = "0.1.0" -authors = ["Raph Levien "] -description = "A compute-centric GPU 2D renderer." -license = "MIT/Apache-2.0" -edition = "2018" - -[dependencies.piet-gpu-hal] -path = "../piet-gpu-hal" - -[dependencies.piet-gpu-types] -path = "../piet-gpu-types" - -[dependencies] -kurbo = "0.5.11" -piet = "0.0.12" -png = "0.16.2" -rand = "0.7.3" -roxmltree = "0.11" -winit = "0.22" \ No newline at end of file +[package] +name = "piet-gpu" +version = "0.1.0" +authors = ["Raph Levien "] +description = "A compute-centric GPU 2D renderer." +license = "MIT/Apache-2.0" +edition = "2018" + +[[bin]] +name = "cli" +path = "bin/cli.rs" + +[[bin]] +name = "winit" +path = "bin/winit.rs" + +[dependencies.piet-gpu-hal] +path = "../piet-gpu-hal" + +[dependencies.piet-gpu-types] +path = "../piet-gpu-types" + +[dependencies] +kurbo = "0.5.11" +piet = "0.0.12" +png = "0.16.2" +rand = "0.7.3" +roxmltree = "0.11" +winit = "0.22" diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs new file mode 100644 index 0000000..1a3c37a --- /dev/null +++ b/piet-gpu/bin/cli.rs @@ -0,0 +1,89 @@ +use std::fs::File; +use std::io::BufWriter; +use std::path::Path; + + +use piet::Color; + +use piet_gpu_hal::vulkan::VkInstance; +use piet_gpu_hal::{CmdBuf, Device, Error, MemFlags}; + +use piet_gpu::{PietGpuRenderContext, Renderer, render_scene, WIDTH, HEIGHT}; + +#[allow(unused)] +fn dump_scene(buf: &[u8]) { + for i in 0..(buf.len() / 4) { + let mut buf_u32 = [0u8; 4]; + buf_u32.copy_from_slice(&buf[i * 4..i * 4 + 4]); + println!("{:4x}: {:8x}", i * 4, u32::from_le_bytes(buf_u32)); + } +} + +fn main() -> Result<(), Error> { + let (instance, _) = VkInstance::new(None)?; + unsafe { + let device = instance.device(None)?; + + let fence = device.create_fence(false)?; + let mut cmd_buf = device.create_cmd_buf()?; + let query_pool = device.create_query_pool(6)?; + + let mut ctx = PietGpuRenderContext::new(); + render_scene(&mut ctx); + let scene = ctx.get_scene_buf(); + //dump_scene(&scene); + + let renderer = Renderer::new(&device, scene)?; + let image_buf = device.create_buffer((WIDTH * HEIGHT * 4) as u64, MemFlags::host_coherent())?; + + cmd_buf.begin(); + renderer.record(&mut cmd_buf, &query_pool); + cmd_buf.copy_image_to_buffer(&renderer.image_dev, &image_buf); + cmd_buf.finish(); + device.run_cmd_buf(&cmd_buf, &[], &[], Some(&fence))?; + device.wait_and_reset(&[fence])?; + let timestamps = device.reap_query_pool(&query_pool).unwrap(); + println!("Kernel 1 time: {:.3}ms", timestamps[0] * 1e3); + println!( + "Kernel 2s time: {:.3}ms", + (timestamps[1] - timestamps[0]) * 1e3 + ); + println!( + "Kernel 2f time: {:.3}ms", + (timestamps[2] - timestamps[1]) * 1e3 + ); + println!( + "Kernel 3 time: {:.3}ms", + (timestamps[3] - timestamps[2]) * 1e3 + ); + println!( + "Render time: {:.3}ms", + (timestamps[4] - timestamps[3]) * 1e3 + ); + + /* + let mut k1_data: Vec = Default::default(); + device.read_buffer(&segment_buf, &mut k1_data).unwrap(); + dump_k1_data(&k1_data); + */ + + let mut img_data: Vec = Default::default(); + // Note: because png can use a `&[u8]` slice, we could avoid an extra copy + // (probably passing a slice into a closure). But for now: keep it simple. + device.read_buffer(&image_buf, &mut img_data).unwrap(); + + // Write image as PNG file. + let path = Path::new("image.png"); + let file = File::create(path).unwrap(); + let ref mut w = BufWriter::new(file); + + let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32); + encoder.set_color(png::ColorType::RGBA); + encoder.set_depth(png::BitDepth::Eight); + let mut writer = encoder.write_header().unwrap(); + + writer.write_image_data(&img_data).unwrap(); + } + + Ok(()) +} diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs new file mode 100644 index 0000000..e5f174a --- /dev/null +++ b/piet-gpu/bin/winit.rs @@ -0,0 +1,122 @@ +use piet_gpu_hal::vulkan::VkInstance; +use piet_gpu_hal::{CmdBuf, Device, Error, ImageLayout}; + +use piet_gpu::{PietGpuRenderContext, Renderer, render_scene, WIDTH, HEIGHT}; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + +const NUM_FRAMES: usize = 2; + +fn main() -> Result<(), Error> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .with_inner_size(winit::dpi::LogicalSize { + width: (WIDTH / 2) as f64, + height: (HEIGHT / 2) as f64, + }) + .with_resizable(false) // currently not supported + .build(&event_loop)?; + + let (instance, surface) = VkInstance::new(Some(&window))?; + unsafe { + let device = instance.device(surface.as_ref())?; + let mut swapchain = instance.swapchain(&device, surface.as_ref().unwrap())?; + + let mut current_frame = 0; + let present_semaphores = (0..NUM_FRAMES) + .map(|_| device.create_semaphore()) + .collect::, Error>>()?; + let frame_fences = (0..NUM_FRAMES) + .map(|_| device.create_fence(false)) + .collect::, Error>>()?; + let mut cmd_buffers = (0..NUM_FRAMES) + .map(|_| device.create_cmd_buf()) + .collect::, Error>>()?; + let query_pools = (0..NUM_FRAMES) + .map(|_| device.create_query_pool(6)) + .collect::, Error>>()?; + + let mut ctx = PietGpuRenderContext::new(); + render_scene(&mut ctx); + let scene = ctx.get_scene_buf(); + + let renderer = Renderer::new(&device, scene)?; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event + + match event { + Event::WindowEvent { event, window_id } if window_id == window.id() => { + match event { + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + _ => (), + } + } + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(window_id) if window_id == window.id() => { + let frame_idx = current_frame % NUM_FRAMES; + let query_pool = &query_pools[frame_idx]; + + if current_frame >= NUM_FRAMES { + device.wait_and_reset(&[frame_fences[frame_idx]]).unwrap(); + + let timestamps = device.reap_query_pool(query_pool).unwrap(); + window.set_title(&format!("k1: {:.3}ms, k2s: {:.3}ms, k2f: {:.3}ms, k3: {:.3}ms, k4: {:.3}ms", + timestamps[0] * 1e3, + (timestamps[1] - timestamps[0]) * 1e3, + (timestamps[2] - timestamps[1]) * 1e3, + (timestamps[3] - timestamps[2]) * 1e3, + (timestamps[4] - timestamps[3]) * 1e3, + )); + } + + let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); + let swap_image = swapchain.image(image_idx); + let cmd_buf = &mut cmd_buffers[frame_idx]; + cmd_buf.begin(); + cmd_buf.reset_query_pool(&query_pool); + + renderer.record(cmd_buf, &query_pool); + + // Image -> Swapchain + cmd_buf.image_barrier( + &swap_image, + ImageLayout::Undefined, + ImageLayout::BlitDst, + ); + cmd_buf.blit_image(&renderer.image_dev, &swap_image); + cmd_buf.image_barrier( + &swap_image, + ImageLayout::BlitDst, + ImageLayout::Present, + ); + cmd_buf.finish(); + + device + .run_cmd_buf( + &cmd_buf, + &[acquisition_semaphore], + &[present_semaphores[frame_idx]], + Some(&frame_fences[frame_idx]), + ) + .unwrap(); + + swapchain + .present(image_idx, &[present_semaphores[frame_idx]]) + .unwrap(); + + current_frame += 1; + } + _ => (), + } + }) + } +} diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index f152caf..2a0b7f7 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -24,7 +24,7 @@ layout(set = 0, binding = 2) buffer FillSegBuf { uint[] fill_seg; }; -layout(rgba8, set = 0, binding = 2) uniform writeonly image2D image; +layout(rgba8, set = 0, binding = 3) uniform writeonly image2D image; #include "ptcl.h" #include "segment.h" diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index db03dde9d380f2d582c27e18efbf8572d37160a0..d9dacc030717276f28521e14fcdae50bd590458b 100644 GIT binary patch delta 398 zcmYk2y-EX75QXRNPhyaSG+|xSBwDJi1SG`&)g*#>1Q8J-);2;4t2~5_tyqL>X(_&d zimi{5?F;;1bx8ixD6qvcbA54?=r)axFVWf&LC1l__%7B^v+$ delta 1038 zcmYk5O>5Lp6ozjaXRKhWE89U7b(XpnMDb%S!?d>5XVU&CA{aAPWN*X$8 z2wNe92KPMi=3uF}xY5hj`pYGI0GnIy4f<10RwxTisLX39XK#cwEXs=E_(amZPuN6l zW6jpe+i0&D%C+#s3{@UIRA&C`gXoxFE#B>V+sr2@Jq?T22heBGJW|OIqjzZOxsXq* zTSN=!QTC|speND$_<`8!eqviv>-&&>gc{k2zxsIDr%)rCSR$!&)I;=c&=Im9k`PwW zvSIOMPdjbtW4}(^p4A!qeDSNbwKxCDi78Uw*}F{5N0~;;ydpbWG0Oh2a}~3@11z^Ug(uq-QNb7h-P<*Wxh4h>>ja(=A4S(jVu~JBL2)6>rdQ+$zN6vml4Z+ zi9FZO=wcb+!TLD+F!}T9;0m#a<#FT#Vw>nN%uReA6Z=*9aawPavO)3t=#R-i{XJfq diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs new file mode 100644 index 0000000..2adbfc1 --- /dev/null +++ b/piet-gpu/src/lib.rs @@ -0,0 +1,321 @@ +mod render_ctx; +mod pico_svg; + +pub use render_ctx::PietGpuRenderContext; + +use rand::{Rng, RngCore}; + +use piet::kurbo::{BezPath, Circle, Line, Point, Vec2}; +use piet::{Color, RenderContext}; + +use piet_gpu_hal::vulkan::VkInstance; +use piet_gpu_hal::{CmdBuf, Device, Error, ImageLayout, MemFlags}; + +use pico_svg::PicoSvg; + +pub const WIDTH: usize = TILE_W * WIDTH_IN_TILES; +pub const HEIGHT: usize = TILE_H * HEIGHT_IN_TILES; + +const TILE_W: usize = 16; +const TILE_H: usize = 16; + +const WIDTH_IN_TILEGROUPS: usize = 4; +const HEIGHT_IN_TILEGROUPS: usize = 96; +const TILEGROUP_STRIDE: usize = 2048; + +const WIDTH_IN_TILES: usize = 128; +const HEIGHT_IN_TILES: usize = 96; +const PTCL_INITIAL_ALLOC: usize = 1024; + +const K2_PER_TILE_SIZE: usize = 8; + +const N_CIRCLES: usize = 1; + +pub fn render_scene(rc: &mut impl RenderContext) { + let mut rng = rand::thread_rng(); + for _ in 0..N_CIRCLES { + let color = Color::from_rgba32_u32(rng.next_u32()); + let center = Point::new( + rng.gen_range(0.0, WIDTH as f64), + rng.gen_range(0.0, HEIGHT as f64), + ); + let radius = rng.gen_range(0.0, 50.0); + let circle = Circle::new(center, radius); + rc.fill(circle, &color); + } + let mut path = BezPath::new(); + path.move_to((100.0, 1150.0)); + path.line_to((200.0, 1200.0)); + path.line_to((150.0, 1250.0)); + path.close_path(); + rc.fill(path, &Color::rgb8(128, 0, 128)); + rc.stroke( + Line::new((100.0, 100.0), (200.0, 150.0)), + &Color::WHITE, + 5.0, + ); + //render_cardioid(rc); + render_tiger(rc); +} + +#[allow(unused)] +fn render_cardioid(rc: &mut impl RenderContext) { + let n = 91; + let dth = std::f64::consts::PI * 2.0 / (n as f64); + let center = Point::new(1024.0, 768.0); + let r = 750.0; + let mut path = BezPath::new(); + for i in 1..n { + let p0 = center + Vec2::from_angle(i as f64 * dth) * r; + let p1 = center + Vec2::from_angle(((i * 2) % n) as f64 * dth) * r; + rc.fill(&Circle::new(p0, 8.0), &Color::WHITE); + path.move_to(p0); + path.line_to(p1); + //rc.stroke(Line::new(p0, p1), &Color::BLACK, 2.0); + } + rc.stroke(&path, &Color::BLACK, 2.0); +} + +fn render_tiger(rc: &mut impl RenderContext) { + let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap(); + let start = std::time::Instant::now(); + let svg = PicoSvg::load(xml_str, 8.0).unwrap(); + println!("parsing time: {:?}", start.elapsed()); + + let start = std::time::Instant::now(); + svg.render(rc); + println!("flattening and encoding time: {:?}", start.elapsed()); +} + +#[allow(unused)] +fn dump_scene(buf: &[u8]) { + for i in 0..(buf.len() / 4) { + let mut buf_u32 = [0u8; 4]; + buf_u32.copy_from_slice(&buf[i * 4..i * 4 + 4]); + println!("{:4x}: {:8x}", i * 4, u32::from_le_bytes(buf_u32)); + } +} + +#[allow(unused)] +fn dump_k1_data(k1_buf: &[u32]) { + for i in 0..k1_buf.len() { + if k1_buf[i] != 0 { + println!("{:4x}: {:8x}", i, k1_buf[i]); + } + } +} + +pub struct Renderer { + pub image_dev: D::Image, // resulting image + + scene_buf: D::Buffer, + scene_dev: D::Buffer, + + k1_alloc_buf_host: D::Buffer, + k1_alloc_buf_dev: D::Buffer, + k2s_alloc_buf_host: D::Buffer, + k2s_alloc_buf_dev: D::Buffer, + k2f_alloc_buf_host: D::Buffer, + k2f_alloc_buf_dev: D::Buffer, + k3_alloc_buf_host: D::Buffer, + k3_alloc_buf_dev: D::Buffer, + tilegroup_buf: D::Buffer, + ptcl_buf: D::Buffer, + + k1_pipeline: D::Pipeline, + k1_ds: D::DescriptorSet, + k2s_pipeline: D::Pipeline, + k2s_ds: D::DescriptorSet, + k2f_pipeline: D::Pipeline, + k2f_ds: D::DescriptorSet, + k3_pipeline: D::Pipeline, + k3_ds: D::DescriptorSet, + k4_pipeline: D::Pipeline, + k4_ds: D::DescriptorSet, +} + +impl Renderer { + pub unsafe fn new(device: &D, scene: &[u8]) -> Result { + let host = MemFlags::host_coherent(); + let dev = MemFlags::device_local(); + + let scene_buf = device + .create_buffer(std::mem::size_of_val(&scene[..]) as u64, host) + .unwrap(); + let scene_dev = device + .create_buffer(std::mem::size_of_val(&scene[..]) as u64, dev) + .unwrap(); + device.write_buffer(&scene_buf, &scene)?; + + let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; + let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; + let segment_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let fill_seg_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; + + let k1_alloc_buf_host = device.create_buffer(4, host)?; + let k1_alloc_buf_dev = device.create_buffer(4, dev)?; + let k1_alloc_start = WIDTH_IN_TILEGROUPS * HEIGHT_IN_TILEGROUPS * TILEGROUP_STRIDE; + device.write_buffer(&k1_alloc_buf_host, &[k1_alloc_start as u32])?; + let k1_code = include_bytes!("../shader/kernel1.spv"); + let k1_pipeline = device + .create_simple_compute_pipeline(k1_code, 3, 0)?; + let k1_ds = device + .create_descriptor_set( + &k1_pipeline, + &[&scene_dev, &tilegroup_buf, &k1_alloc_buf_dev], + &[], + )?; + + let k2s_alloc_buf_host = device.create_buffer(4, host)?; + let k2s_alloc_buf_dev = device.create_buffer(4, dev)?; + let k2s_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; + device + .write_buffer(&k2s_alloc_buf_host, &[k2s_alloc_start as u32]) + ?; + let k2s_code = include_bytes!("../shader/kernel2s.spv"); + let k2s_pipeline = device + .create_simple_compute_pipeline(k2s_code, 4, 0) + ?; + let k2s_ds = device + .create_descriptor_set( + &k2s_pipeline, + &[&scene_dev, &tilegroup_buf, &segment_buf, &k2s_alloc_buf_dev], + &[], + ) + ?; + + let k2f_alloc_buf_host = device.create_buffer(4, host)?; + let k2f_alloc_buf_dev = device.create_buffer(4, dev)?; + let k2f_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; + device + .write_buffer(&k2f_alloc_buf_host, &[k2f_alloc_start as u32]) + ?; + let k2f_code = include_bytes!("../shader/kernel2f.spv"); + let k2f_pipeline = device.create_simple_compute_pipeline(k2f_code, 4, 0)?; + let k2f_ds = device + .create_descriptor_set( + &k2f_pipeline, + &[ + &scene_dev, + &tilegroup_buf, + &fill_seg_buf, + &k2f_alloc_buf_dev, + ], + &[], + ) + ?; + + let k3_alloc_buf_host = device.create_buffer(4, host)?; + let k3_alloc_buf_dev = device.create_buffer(4, dev)?; + let k3_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; + device + .write_buffer(&k3_alloc_buf_host, &[k3_alloc_start as u32]) + ?; + let k3_code = include_bytes!("../shader/kernel3.spv"); + let k3_pipeline = device.create_simple_compute_pipeline(k3_code, 6, 0)?; + let k3_ds = device + .create_descriptor_set( + &k3_pipeline, + &[ + &scene_dev, + &tilegroup_buf, + &segment_buf, + &fill_seg_buf, + &ptcl_buf, + &k3_alloc_buf_dev, + ], + &[], + ) + ?; + + let k4_code = include_bytes!("../shader/kernel4.spv"); + let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 3, 1)?; + let k4_ds = device + .create_descriptor_set(&k4_pipeline, &[&ptcl_buf, &segment_buf, &fill_seg_buf], &[&image_dev]) + ?; + + Ok(Renderer { + scene_buf, + scene_dev, + image_dev, + k1_alloc_buf_host, + k1_alloc_buf_dev, + k2s_alloc_buf_host, + k2s_alloc_buf_dev, + k2f_alloc_buf_host, + k2f_alloc_buf_dev, + k3_alloc_buf_host, + k3_alloc_buf_dev, + tilegroup_buf, + ptcl_buf, + k1_pipeline, + k1_ds, + k2s_pipeline, + k2s_ds, + k2f_pipeline, + k2f_ds, + k3_pipeline, + k3_ds, + k4_pipeline, + k4_ds, + }) + } + + pub unsafe fn record(&self, cmd_buf: &mut impl CmdBuf, query_pool: &D::QueryPool) { + cmd_buf.copy_buffer(&self.scene_buf, &self.scene_dev); + // Note: we could use one alloc buf and reuse it. But we'll stick with + // multiple ones for clarity. + cmd_buf.copy_buffer(&self.k1_alloc_buf_host, &self.k1_alloc_buf_dev); + cmd_buf.copy_buffer(&self.k2s_alloc_buf_host, &self.k2s_alloc_buf_dev); + cmd_buf.copy_buffer(&self.k2f_alloc_buf_host, &self.k2f_alloc_buf_dev); + cmd_buf.copy_buffer(&self.k3_alloc_buf_host, &self.k3_alloc_buf_dev); + // Note: these clears aren't necessary, and are here to make inspection + // of the buffers cleaner. Can likely be removed. + cmd_buf.clear_buffer(&self.tilegroup_buf); + cmd_buf.clear_buffer(&self.ptcl_buf); + cmd_buf.memory_barrier(); + cmd_buf.image_barrier(&self.image_dev, ImageLayout::Undefined, ImageLayout::General); + cmd_buf.reset_query_pool(&query_pool); + cmd_buf.write_timestamp(&query_pool, 0); + cmd_buf.dispatch( + &self.k1_pipeline, + &self.k1_ds, + ((WIDTH / 512) as u32, (HEIGHT / 512) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 1); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.k2s_pipeline, + &self.k2s_ds, + ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 2); + // Note: this barrier is not necessary (k2f does not depend on + // k2s output), but I'm keeping it here to increase transparency + // of performance. + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.k2f_pipeline, + &self.k2f_ds, + ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 2), + ); + cmd_buf.write_timestamp(&query_pool, 3); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.k3_pipeline, + &self.k3_ds, + ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 3), + ); + cmd_buf.write_timestamp(&query_pool, 4); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.k4_pipeline, + &self.k4_ds, + ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 5); + cmd_buf.memory_barrier(); + cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); + } +} diff --git a/piet-gpu/src/main.rs b/piet-gpu/src/main.rs deleted file mode 100644 index 0b7a6fe..0000000 --- a/piet-gpu/src/main.rs +++ /dev/null @@ -1,461 +0,0 @@ -use std::fs::File; -use std::io::BufWriter; -use std::path::Path; - -use rand::{Rng, RngCore}; - -use piet::kurbo::{BezPath, Circle, Line, Point, Vec2}; -use piet::{Color, RenderContext}; - -use piet_gpu_hal::vulkan::VkInstance; -use piet_gpu_hal::{CmdBuf, Device, Error, ImageLayout, MemFlags}; - -mod pico_svg; -mod render_ctx; - -use render_ctx::PietGpuRenderContext; -use pico_svg::PicoSvg; - -use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; - -const WIDTH: usize = TILE_W * WIDTH_IN_TILES; -const HEIGHT: usize = TILE_H * HEIGHT_IN_TILES; - -const TILE_W: usize = 16; -const TILE_H: usize = 16; - -const WIDTH_IN_TILEGROUPS: usize = 4; -const HEIGHT_IN_TILEGROUPS: usize = 96; -const TILEGROUP_STRIDE: usize = 2048; - -const WIDTH_IN_TILES: usize = 128; -const HEIGHT_IN_TILES: usize = 96; -const PTCL_INITIAL_ALLOC: usize = 1024; - -const K2_PER_TILE_SIZE: usize = 8; - -const N_CIRCLES: usize = 1; - -const NUM_FRAMES: usize = 2; - -fn render_scene(rc: &mut impl RenderContext) { - let mut rng = rand::thread_rng(); - for _ in 0..N_CIRCLES { - let color = Color::from_rgba32_u32(rng.next_u32()); - let center = Point::new( - rng.gen_range(0.0, WIDTH as f64), - rng.gen_range(0.0, HEIGHT as f64), - ); - let radius = rng.gen_range(0.0, 50.0); - let circle = Circle::new(center, radius); - rc.fill(circle, &color); - } - let mut path = BezPath::new(); - path.move_to((100.0, 1150.0)); - path.line_to((200.0, 1200.0)); - path.line_to((150.0, 1250.0)); - path.close_path(); - rc.fill(path, &Color::rgb8(128, 0, 128)); - rc.stroke( - Line::new((100.0, 100.0), (200.0, 150.0)), - &Color::WHITE, - 5.0, - ); - //render_cardioid(rc); - render_tiger(rc); -} - -#[allow(unused)] -fn render_cardioid(rc: &mut impl RenderContext) { - let n = 91; - let dth = std::f64::consts::PI * 2.0 / (n as f64); - let center = Point::new(1024.0, 768.0); - let r = 750.0; - let mut path = BezPath::new(); - for i in 1..n { - let p0 = center + Vec2::from_angle(i as f64 * dth) * r; - let p1 = center + Vec2::from_angle(((i * 2) % n) as f64 * dth) * r; - rc.fill(&Circle::new(p0, 8.0), &Color::WHITE); - path.move_to(p0); - path.line_to(p1); - //rc.stroke(Line::new(p0, p1), &Color::BLACK, 2.0); - } - rc.stroke(&path, &Color::BLACK, 2.0); -} - -fn render_tiger(rc: &mut impl RenderContext) { - let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap(); - let start = std::time::Instant::now(); - let svg = PicoSvg::load(xml_str, 8.0).unwrap(); - println!("parsing time: {:?}", start.elapsed()); - - let start = std::time::Instant::now(); - svg.render(rc); - println!("flattening and encoding time: {:?}", start.elapsed()); -} - -#[allow(unused)] -fn dump_scene(buf: &[u8]) { - for i in 0..(buf.len() / 4) { - let mut buf_u32 = [0u8; 4]; - buf_u32.copy_from_slice(&buf[i * 4..i * 4 + 4]); - println!("{:4x}: {:8x}", i * 4, u32::from_le_bytes(buf_u32)); - } -} - -#[allow(unused)] -fn dump_k1_data(k1_buf: &[u32]) { - for i in 0..k1_buf.len() { - if k1_buf[i] != 0 { - println!("{:4x}: {:8x}", i, k1_buf[i]); - } - } -} - -fn main() -> Result<(), Error> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_inner_size(winit::dpi::LogicalSize { - width: (WIDTH_IN_TILES * 8) as f64, - height: (HEIGHT_IN_TILES * 8) as f64, - }) - .with_resizable(false) // currently not supported - .build(&event_loop)?; - - let (instance, surface) = VkInstance::new(Some(&window))?; - unsafe { - let device = instance.device(surface.as_ref())?; - let mut swapchain = instance.swapchain(&device, surface.as_ref().unwrap())?; - - let mut current_frame = 0; - let present_semaphores = (0..NUM_FRAMES) - .map(|_| device.create_semaphore()) - .collect::, Error>>()?; - let frame_fences = (0..NUM_FRAMES) - .map(|_| device.create_fence(false)) - .collect::, Error>>()?; - let mut cmd_buffers = (0..NUM_FRAMES) - .map(|_| device.create_cmd_buf()) - .collect::, Error>>()?; - let query_pools = (0..NUM_FRAMES) - .map(|_| device.create_query_pool(6)) - .collect::, Error>>()?; - - let host = MemFlags::host_coherent(); - let dev = MemFlags::device_local(); - let mut ctx = PietGpuRenderContext::new(); - render_scene(&mut ctx); - let scene = ctx.get_scene_buf(); - //dump_scene(&scene); - let scene_buf = device - .create_buffer(std::mem::size_of_val(&scene[..]) as u64, host) - .unwrap(); - let scene_dev = device - .create_buffer(std::mem::size_of_val(&scene[..]) as u64, dev) - .unwrap(); - device.write_buffer(&scene_buf, &scene)?; - let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; - let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; - let segment_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let fill_seg_buf = device.create_buffer(64 * 1024 * 1024, dev).unwrap(); - let image_buf = device.create_buffer((WIDTH * HEIGHT * 4) as u64, host)?; - let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; - - let k1_alloc_buf_host = device.create_buffer(4, host)?; - let k1_alloc_buf_dev = device.create_buffer(4, dev)?; - let k1_alloc_start = WIDTH_IN_TILEGROUPS * HEIGHT_IN_TILEGROUPS * TILEGROUP_STRIDE; - device.write_buffer(&k1_alloc_buf_host, &[k1_alloc_start as u32])?; - let k1_code = include_bytes!("../shader/kernel1.spv"); - let k1_pipeline = device - .create_simple_compute_pipeline(k1_code, 3, 0) - .unwrap(); - let k1_ds = device - .create_descriptor_set( - &k1_pipeline, - &[&scene_dev, &tilegroup_buf, &k1_alloc_buf_dev], - &[], - ) - .unwrap(); - - let k2s_alloc_buf_host = device.create_buffer(4, host).unwrap(); - let k2s_alloc_buf_dev = device.create_buffer(4, dev).unwrap(); - let k2s_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; - device - .write_buffer(&k2s_alloc_buf_host, &[k2s_alloc_start as u32]) - .unwrap(); - let k2s_code = include_bytes!("../shader/kernel2s.spv"); - let k2s_pipeline = device - .create_simple_compute_pipeline(k2s_code, 4, 0) - .unwrap(); - let k2s_ds = device - .create_descriptor_set( - &k2s_pipeline, - &[&scene_dev, &tilegroup_buf, &segment_buf, &k2s_alloc_buf_dev], - &[], - ) - .unwrap(); - - let k2f_alloc_buf_host = device.create_buffer(4, host).unwrap(); - let k2f_alloc_buf_dev = device.create_buffer(4, dev).unwrap(); - let k2f_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; - device - .write_buffer(&k2f_alloc_buf_host, &[k2f_alloc_start as u32]) - .unwrap(); - let k2f_code = include_bytes!("../shader/kernel2f.spv"); - let k2f_pipeline = device.create_simple_compute_pipeline(k2f_code, 4).unwrap(); - let k2f_ds = device - .create_descriptor_set( - &k2f_pipeline, - &[ - &scene_dev, - &tilegroup_buf, - &fill_seg_buf, - &k2f_alloc_buf_dev, - ], - ) - .unwrap(); - - let k3_alloc_buf_host = device.create_buffer(4, host).unwrap(); - let k3_alloc_buf_dev = device.create_buffer(4, dev).unwrap(); - let k3_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; - device - .write_buffer(&k3_alloc_buf_host, &[k3_alloc_start as u32]) - .unwrap(); - let k3_code = include_bytes!("../shader/kernel3.spv"); - let k3_pipeline = device.create_simple_compute_pipeline(k3_code, 6, 0).unwrap(); - let k3_ds = device - .create_descriptor_set( - &k3_pipeline, - &[ - &scene_dev, - &tilegroup_buf, - &segment_buf, - &fill_seg_buf, - &ptcl_buf, - &k3_alloc_buf_dev, - ], - &[], - ) - .unwrap(); - - let k4_code = include_bytes!("../shader/kernel4.spv"); - let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 3, 1).unwrap(); - let k4_ds = device - .create_descriptor_set(&k4_pipeline, &[&ptcl_buf, &segment_buf, &fill_seg_buf], &[&image_dev]) - .unwrap(); - let query_pool = &query_pools[0]; - let mut cmd_buf = device.create_cmd_buf().unwrap(); - cmd_buf.begin(); - cmd_buf.copy_buffer(&scene_buf, &scene_dev); - // Note: we could use one alloc buf and reuse it. But we'll stick with - // multiple ones for clarity. - cmd_buf.copy_buffer(&k1_alloc_buf_host, &k1_alloc_buf_dev); - cmd_buf.copy_buffer(&k2s_alloc_buf_host, &k2s_alloc_buf_dev); - cmd_buf.copy_buffer(&k2f_alloc_buf_host, &k2f_alloc_buf_dev); - cmd_buf.copy_buffer(&k3_alloc_buf_host, &k3_alloc_buf_dev); - // Note: these clears aren't necessary, and are here to make inspection - // of the buffers cleaner. Can likely be removed. - cmd_buf.clear_buffer(&tilegroup_buf); - cmd_buf.clear_buffer(&ptcl_buf); - cmd_buf.memory_barrier(); - cmd_buf.image_barrier(&image_dev, ImageLayout::Undefined, ImageLayout::General); - cmd_buf.reset_query_pool(&query_pool); - cmd_buf.write_timestamp(&query_pool, 0); - cmd_buf.dispatch( - &k1_pipeline, - &k1_ds, - ((WIDTH / 512) as u32, (HEIGHT / 512) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 1); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &k2s_pipeline, - &k2s_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 2); - // Note: this barrier is not necessary (k2f does not depend on - // k2s output), but I'm keeping it here to increase transparency - // of performance. - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &k2f_pipeline, - &k2f_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 2), - ); - cmd_buf.write_timestamp(&query_pool, 3); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &k3_pipeline, - &k3_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 3), - ); - cmd_buf.write_timestamp(&query_pool, 4); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &k4_pipeline, - &k4_ds, - ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 5); - cmd_buf.memory_barrier(); - cmd_buf.image_barrier(&image_dev, ImageLayout::General, ImageLayout::BlitSrc); - cmd_buf.copy_image_to_buffer(&image_dev, &image_buf); - cmd_buf.finish(); - device.run_cmd_buf(&cmd_buf, &[], &[], Some(&frame_fences[0]))?; - device.wait_and_reset(&[frame_fences[0]])?; - let timestamps = device.reap_query_pool(&query_pool).unwrap(); - println!("Kernel 1 time: {:.3}ms", timestamps[0] * 1e3); - println!( - "Kernel 2s time: {:.3}ms", - (timestamps[1] - timestamps[0]) * 1e3 - ); - println!( - "Kernel 2f time: {:.3}ms", - (timestamps[2] - timestamps[1]) * 1e3 - ); - println!( - "Kernel 3 time: {:.3}ms", - (timestamps[3] - timestamps[2]) * 1e3 - ); - println!( - "Render time: {:.3}ms", - (timestamps[4] - timestamps[3]) * 1e3 - ); - - /* - let mut k1_data: Vec = Default::default(); - device.read_buffer(&segment_buf, &mut k1_data).unwrap(); - dump_k1_data(&k1_data); - */ - - if false { - let mut img_data: Vec = Default::default(); - // Note: because png can use a `&[u8]` slice, we could avoid an extra copy - // (probably passing a slice into a closure). But for now: keep it simple. - device.read_buffer(&image_buf, &mut img_data).unwrap(); - - // Write image as PNG file. - let path = Path::new("image.png"); - let file = File::create(path).unwrap(); - let ref mut w = BufWriter::new(file); - - let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32); - encoder.set_color(png::ColorType::RGBA); - encoder.set_depth(png::BitDepth::Eight); - let mut writer = encoder.write_header().unwrap(); - - writer.write_image_data(&img_data).unwrap(); - } - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { event, window_id } if window_id == window.id() => { - match event { - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - _ => (), - } - } - Event::MainEventsCleared => { - window.request_redraw(); - } - Event::RedrawRequested(window_id) if window_id == window.id() => { - let frame_idx = current_frame % NUM_FRAMES; - let query_pool = &query_pools[frame_idx]; - - if current_frame >= NUM_FRAMES { - device.wait_and_reset(&[frame_fences[frame_idx]]).unwrap(); - - let timestamps = device.reap_query_pool(query_pool).unwrap(); - window.set_title(&format!("k1: {:.3}ms, k2: {:.3}ms, k3: {:.3}ms, k4: {:.3}ms", - timestamps[0] * 1e3, - (timestamps[1] - timestamps[0]) * 1e3, - (timestamps[2] - timestamps[1]) * 1e3, - (timestamps[3] - timestamps[2]) * 1e3, - )); - } - - let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); - let swap_image = swapchain.image(image_idx); - let cmd_buf = &mut cmd_buffers[frame_idx]; - cmd_buf.begin(); - cmd_buf.reset_query_pool(&query_pool); - cmd_buf.copy_buffer(&scene_buf, &scene_dev); - cmd_buf.copy_buffer(&k1_alloc_buf_host, &k1_alloc_buf_dev); - cmd_buf.copy_buffer(&k2s_alloc_buf_host, &k2s_alloc_buf_dev); - cmd_buf.copy_buffer(&k3_alloc_buf_host, &k3_alloc_buf_dev); - cmd_buf.clear_buffer(&tilegroup_buf); - cmd_buf.clear_buffer(&ptcl_buf); - cmd_buf.memory_barrier(); - cmd_buf.write_timestamp(&query_pool, 0); - cmd_buf.dispatch( - &k1_pipeline, - &k1_ds, - ((WIDTH / 512) as u32, (HEIGHT / 512) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 1); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &k2s_pipeline, - &k2s_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 2); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &k3_pipeline, - &k3_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 3); - cmd_buf.memory_barrier(); - cmd_buf.image_barrier(&image_dev, ImageLayout::BlitSrc, ImageLayout::General); - cmd_buf.dispatch( - &k4_pipeline, - &k4_ds, - ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 4); - cmd_buf.memory_barrier(); - cmd_buf.image_barrier( - &swap_image, - ImageLayout::Undefined, - ImageLayout::BlitDst, - ); - cmd_buf.image_barrier(&image_dev, ImageLayout::General, ImageLayout::BlitSrc); - cmd_buf.blit_image(&image_dev, &swap_image); - cmd_buf.image_barrier( - &swap_image, - ImageLayout::BlitDst, - ImageLayout::Present, - ); - cmd_buf.finish(); - - device - .run_cmd_buf( - &cmd_buf, - &[acquisition_semaphore], - &[present_semaphores[frame_idx]], - Some(&frame_fences[frame_idx]), - ) - .unwrap(); - - swapchain - .present(image_idx, &[present_semaphores[frame_idx]]) - .unwrap(); - - current_frame += 1; - } - _ => (), - } - }) - } -} From abd238bff3013de654e992c47b6e7678405f397e Mon Sep 17 00:00:00 2001 From: msiglreith Date: Tue, 5 May 2020 18:13:07 +0200 Subject: [PATCH 3/3] Address review comments --- piet-gpu-hal/src/vulkan.rs | 4 ++-- piet-gpu/shader/kernel4.comp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index cd44ec7..ff4587e 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -208,7 +208,7 @@ impl VkInstance { None, )?; - let (_dbg_loader, _dbg_callbk) = if false { + let (_dbg_loader, _dbg_callbk) = if cfg!(debug_assertions) { let dbg_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() .message_severity( vk::DebugUtilsMessageSeverityFlagsEXT::ERROR @@ -504,7 +504,7 @@ impl crate::Device for VkDevice { /// This creates a pipeline that runs over the buffer. /// - /// The descriptor set layout is just some number of buffers (this will change). + /// The descriptor set layout is just some number of storage buffers and storage images (this might change). unsafe fn create_simple_compute_pipeline( &self, code: &[u8], diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 2a0b7f7..d6f33b7 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -63,11 +63,11 @@ void main() { SegChunk seg_chunk = SegChunk_read(seg_chunk_ref); for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * i)); - vec2 line_vec = seg.end - seg.start; - vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; - float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); - df = min(df, length(line_vec * t - dpos)); - } + vec2 line_vec = seg.end - seg.start; + vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; + float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); + df = min(df, length(line_vec * t - dpos)); + } seg_chunk_ref = seg_chunk.next; } while (seg_chunk_ref.offset != 0); fg_rgba = unpackUnorm4x8(stroke.rgba_color).wzyx;