From 17adae8fcfa297a2a627631a50a48dab65b798c8 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 31 May 2024 19:22:20 -0400 Subject: [PATCH 01/21] Initial ash v0.38 support --- Cargo.toml | 21 +- examples/app.rs | 109 ++++++++ src/display.rs | 8 +- src/driver/accel_struct.rs | 15 +- src/driver/buffer.rs | 4 +- src/driver/cmd_buf.rs | 4 +- src/driver/compute.rs | 24 +- src/driver/descriptor_set.rs | 4 +- src/driver/device.rs | 79 +++--- src/driver/graphic.rs | 21 +- src/driver/image.rs | 75 ++---- src/driver/instance.rs | 18 +- src/driver/physical_device.rs | 67 +++-- src/driver/ray_trace.rs | 110 ++++---- src/driver/render_pass.rs | 304 +++++++++++----------- src/driver/shader.rs | 120 ++++++--- src/driver/surface.rs | 24 +- src/driver/swapchain.rs | 12 +- src/event_loop.rs | 271 ++++++++++---------- src/graph/mod.rs | 17 +- src/graph/pass_ref.rs | 18 +- src/graph/resolver.rs | 459 +++++++++++++++++----------------- src/lib.rs | 4 +- 23 files changed, 970 insertions(+), 818 deletions(-) create mode 100644 examples/app.rs diff --git a/Cargo.toml b/Cargo.toml index ce667f8..905af70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,19 +20,19 @@ profile-with-superluminal = ["profiling/profile-with-superluminal"] profile-with-tracy = ["profiling/profile-with-tracy"] [dependencies] -ash = ">=0.37.1, <0.38" -ash-window = "0.12" +ash = "0.38" +ash-window = "0.13" derive_builder = "0.20" -gpu-allocator = "0.26" +gpu-allocator = { git = "https://github.com/Traverse-Research/gpu-allocator.git", rev = "2e2296e3045f6ae83a879928c87e558830d7ab63" } #"0.26" log = "0.4" ordered-float = "4.1" parking_lot = { version = "0.12", optional = true } paste = "1.0" profiling = "1.0" -raw-window-handle = "0.5" +raw-window-handle = "0.6" spirq = "1.2" -vk-sync = { version = "0.4.0", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs -winit = { version = "0.29", features = ["rwh_05"] } +vk-sync = { git = "https://github.com/attackgoat/vk-sync-rs.git", rev = "19fc3f811cc1d38b2231cdb8840fddf271879ac1", package = "vk-sync-fork" } #version = "0.4.0", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs +winit = "0.30" [target.'cfg(target_os = "macos")'.dependencies] ash-molten = "0.17" @@ -55,8 +55,9 @@ puffin = "0.19" puffin_http = "0.16" rand = "0.8" reqwest = { version = "0.12", features = ["blocking"] } -screen-13-fx = { path = "contrib/screen-13-fx" } -screen-13-imgui = { path = "contrib/screen-13-imgui" } -screen-13-egui = { path = "contrib/screen-13-egui" } +#screen-13-fx = { path = "contrib/screen-13-fx" } +#screen-13-imgui = { path = "contrib/screen-13-imgui" } +#screen-13-egui = { path = "contrib/screen-13-egui" } tobj = "4.0" -winit_input_helper = "0.16" +winit = "0.30" +winit_input_helper = { git = "https://github.com/stefnotch/winit_input_helper.git", rev = "6e76a79d01ce836c01b9cdeaa98846a6f0955dc4" } #"0.16" diff --git a/examples/app.rs b/examples/app.rs new file mode 100644 index 0000000..010b210 --- /dev/null +++ b/examples/app.rs @@ -0,0 +1,109 @@ +use { + screen_13::{ + driver::{ + device::{Device, DeviceInfo}, + surface::Surface, + swapchain::{Swapchain, SwapchainInfo}, + }, + graph::RenderGraph, + pool::hash::HashPool, + Display, + }, + std::sync::Arc, + winit::{ + application::ApplicationHandler, + error::EventLoopError, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, + }, +}; + +fn main() -> Result<(), EventLoopError> { + EventLoop::new()?.run_app(&mut Application::default()) +} + +#[derive(Default)] +struct Application(Option); + +impl ApplicationHandler for Application { + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + self.0.as_ref().unwrap().window.request_redraw(); + } + + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_attributes = Window::default_attributes().with_title("Screen 13"); + let window = event_loop.create_window(window_attributes).unwrap(); + let device = Arc::new(Device::create_presentable(DeviceInfo::default(), &window).unwrap()); + let display_pool = Box::new(HashPool::new(&device)); + let display = Display::new(&device, display_pool, 3, 0).unwrap(); + let surface = Surface::create(&device, &window).unwrap(); + let surface_formats = Surface::formats(&surface).unwrap(); + let surface_format = Surface::linear_or_default(&surface_formats); + let window_size = window.inner_size(); + let swapchain = Swapchain::new( + &device, + surface, + SwapchainInfo::new(window_size.width, window_size.height, surface_format), + ) + .unwrap(); + + self.0 = Some(Context { + device, + display, + swapchain, + window, + }); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + let context = self.0.as_mut().unwrap(); + + match event { + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::Resized(size) => { + let mut swapchain_info = context.swapchain.info(); + swapchain_info.width = size.width; + swapchain_info.height = size.height; + context.swapchain.set_info(swapchain_info); + } + WindowEvent::RedrawRequested => { + context.draw(); + } + _ => (), + } + } +} + +struct Context { + device: Arc, + display: Display, + swapchain: Swapchain, + window: Window, +} + +impl Context { + fn draw(&mut self) { + if let Ok(swapchain_image) = self.swapchain.acquire_next_image() { + self.window.pre_present_notify(); + + let mut render_graph = RenderGraph::new(); + let swapchain_image = render_graph.bind_node(swapchain_image); + + // Rendering goes here! + render_graph.clear_color_image_value(swapchain_image, [1.0, 0.0, 1.0]); + let _ = self.device; + + let swapchain_image = self + .display + .resolve_image(render_graph, swapchain_image) + .unwrap(); + self.swapchain.present_image(swapchain_image, 0, 0); + } + } +} diff --git a/src/display.rs b/src/display.rs index 165b06a..fcc77c0 100644 --- a/src/display.rs +++ b/src/display.rs @@ -56,7 +56,7 @@ impl Display { .device .begin_command_buffer( **cmd_buf, - &vk::CommandBufferBeginInfo::builder() + &vk::CommandBufferBeginInfo::default() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), ) .map_err(|_| ()) @@ -133,7 +133,7 @@ impl Display { unsafe { Self::submit( cmd_buf, - vk::SubmitInfo::builder() + vk::SubmitInfo::default() .command_buffers(from_ref(cmd_buf)) .signal_semaphores(from_ref(&swapchain_image.rendered)) .wait_semaphores(from_ref(&swapchain_image.acquired)) @@ -154,7 +154,7 @@ impl Display { #[profiling::function] unsafe fn submit( cmd_buf: &CommandBuffer, - submit_info: vk::SubmitInfoBuilder<'_>, + submit_info: vk::SubmitInfo<'_>, ) -> Result<(), ()> { use std::slice::from_ref; @@ -166,7 +166,7 @@ impl Display { .device .queue_submit( cmd_buf.device.queues[cmd_buf.info.queue_family_index as usize][0], - from_ref(&*submit_info), + from_ref(&submit_info), cmd_buf.fence, ) .map_err(|_| ()) diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 496dc62..590a22f 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -104,7 +104,7 @@ impl AccelerationStructure { )?; let accel_struct = { - let create_info = vk::AccelerationStructureCreateInfoKHR::builder() + let create_info = vk::AccelerationStructureCreateInfoKHR::default() .ty(info.ty) .buffer(*buffer) .size(info.size); @@ -216,7 +216,7 @@ impl AccelerationStructure { .as_ref() .unwrap() .get_acceleration_structure_device_address( - &vk::AccelerationStructureDeviceAddressInfoKHR::builder() + &vk::AccelerationStructureDeviceAddressInfoKHR::default() .acceleration_structure(this.accel_struct), ) } @@ -278,7 +278,7 @@ impl AccelerationStructure { #[derive(Default)] struct Tls { - geometries: Vec, + geometries: Vec>, max_primitive_counts: Vec, } @@ -354,11 +354,13 @@ impl AccelerationStructure { tls.max_primitive_counts.push(info.max_primitive_count); } - let info = vk::AccelerationStructureBuildGeometryInfoKHR::builder() + let info = vk::AccelerationStructureBuildGeometryInfoKHR::default() .ty(info.ty) .flags(info.flags) .geometries(&tls.geometries); - let sizes = unsafe { + let mut sizes = vk::AccelerationStructureBuildSizesInfoKHR::default(); + + unsafe { device .accel_struct_ext .as_ref() @@ -367,6 +369,7 @@ impl AccelerationStructure { vk::AccelerationStructureBuildTypeKHR::HOST_OR_DEVICE, &info, tls.max_primitive_counts.as_slice(), + &mut sizes, ) }; @@ -422,7 +425,7 @@ pub struct AccelerationStructureGeometry { } impl AccelerationStructureGeometry { - pub(crate) fn into_vk(self) -> vk::AccelerationStructureGeometryKHR { + pub(crate) fn into_vk(self) -> vk::AccelerationStructureGeometryKHR<'static> { let (geometry_type, geometry) = match self.geometry { AccelerationStructureGeometryData::AABBs { stride } => ( vk::GeometryTypeKHR::AABBS, diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index c0ca6d8..983bb27 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -97,7 +97,7 @@ impl Buffer { debug_assert_ne!(info.size, 0, "Size must be non-zero"); let device = Arc::clone(device); - let buffer_info = vk::BufferCreateInfo::builder() + let buffer_info = vk::BufferCreateInfo::default() .size(info.size) .usage(info.usage) .sharing_mode(vk::SharingMode::CONCURRENT) @@ -312,7 +312,7 @@ impl Buffer { pub fn device_address(this: &Self) -> vk::DeviceAddress { unsafe { this.device.get_buffer_device_address( - &vk::BufferDeviceAddressInfo::builder().buffer(this.buffer), + &vk::BufferDeviceAddressInfo::default().buffer(this.buffer), ) } } diff --git a/src/driver/cmd_buf.rs b/src/driver/cmd_buf.rs index c2100ec..2f7f50b 100644 --- a/src/driver/cmd_buf.rs +++ b/src/driver/cmd_buf.rs @@ -26,7 +26,7 @@ impl CommandBuffer { info: CommandBufferInfo, ) -> Result { let device = Arc::clone(device); - let cmd_pool_info = vk::CommandPoolCreateInfo::builder() + let cmd_pool_info = vk::CommandPoolCreateInfo::default() .flags( vk::CommandPoolCreateFlags::TRANSIENT | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER, @@ -41,7 +41,7 @@ impl CommandBuffer { DriverError::Unsupported })? }; - let cmd_buf_info = vk::CommandBufferAllocateInfo::builder() + let cmd_buf_info = vk::CommandBufferAllocateInfo::default() .command_buffer_count(1) .command_pool(pool) .level(vk::CommandBufferLevel::PRIMARY); diff --git a/src/driver/compute.rs b/src/driver/compute.rs index c2bb079..2af0cac 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -3,7 +3,7 @@ use { super::{ device::Device, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, + shader::{align_spriv, DescriptorBindingMap, PipelineDescriptorInfo, Shader}, DriverError, }, ash::vk, @@ -83,7 +83,7 @@ impl ComputePipeline { let shader = shader.into(); // Use SPIR-V reflection to get the types and counts of all descriptors - let mut descriptor_bindings = shader.descriptor_bindings(&device)?; + let mut descriptor_bindings = shader.descriptor_bindings(); for (descriptor_info, _) in descriptor_bindings.values_mut() { if descriptor_info.binding_count() == 0 { descriptor_info.set_binding_count(info.bindless_descriptor_count); @@ -98,28 +98,22 @@ impl ComputePipeline { .collect::>(); unsafe { - let shader_module_create_info = vk::ShaderModuleCreateInfo { - code_size: shader.spirv.len(), - p_code: shader.spirv.as_ptr() as *const u32, - ..Default::default() - }; let shader_module = device - .create_shader_module(&shader_module_create_info, None) + .create_shader_module(&vk::ShaderModuleCreateInfo::default().code(align_spriv(&shader.spirv)?), None) .map_err(|err| { warn!("{err}"); DriverError::Unsupported })?; let entry_name = CString::new(shader.entry_name.as_bytes()).unwrap(); - let mut stage_create_info = vk::PipelineShaderStageCreateInfo::builder() + let mut stage_create_info = vk::PipelineShaderStageCreateInfo::default() .module(shader_module) .stage(shader.stage) .name(&entry_name); let specialization_info = shader.specialization_info.as_ref().map(|info| { - vk::SpecializationInfo::builder() + vk::SpecializationInfo::default() .map_entries(&info.map_entries) .data(&info.data) - .build() }); if let Some(specialization_info) = &specialization_info { @@ -127,7 +121,7 @@ impl ComputePipeline { } let mut layout_info = - vk::PipelineLayoutCreateInfo::builder().set_layouts(&descriptor_set_layouts); + vk::PipelineLayoutCreateInfo::default().set_layouts(&descriptor_set_layouts); let push_constants = shader.push_constant_range(); if let Some(push_constants) = &push_constants { @@ -141,13 +135,13 @@ impl ComputePipeline { DriverError::Unsupported })?; - let pipeline_info = vk::ComputePipelineCreateInfo::builder() - .stage(stage_create_info.build()) + let pipeline_info = vk::ComputePipelineCreateInfo::default() + .stage(stage_create_info) .layout(layout); let pipeline = device .create_compute_pipelines( vk::PipelineCache::null(), - from_ref(&pipeline_info.build()), + from_ref(&pipeline_info), None, ) .map_err(|(_, err)| { diff --git a/src/driver/descriptor_set.rs b/src/driver/descriptor_set.rs index bcac298..ed0ed3f 100644 --- a/src/driver/descriptor_set.rs +++ b/src/driver/descriptor_set.rs @@ -125,7 +125,7 @@ impl DescriptorPool { let descriptor_pool = unsafe { device.create_descriptor_pool( - &vk::DescriptorPoolCreateInfo::builder() + &vk::DescriptorPoolCreateInfo::default() .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET) .max_sets(info.max_sets) .pool_sizes(&pool_sizes[0..pool_size_count]), @@ -162,7 +162,7 @@ impl DescriptorPool { ) -> Result + 'a, DriverError> { use std::slice::from_ref; - let mut create_info = vk::DescriptorSetAllocateInfo::builder() + let mut create_info = vk::DescriptorSetAllocateInfo::default() .descriptor_pool(this.descriptor_pool) .set_layouts(from_ref(layout)); create_info.descriptor_set_count = count; diff --git a/src/driver/device.rs b/src/driver/device.rs index 430f0dd..3fa2976 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -2,7 +2,7 @@ use { super::{physical_device::PhysicalDevice, DriverError, Instance}, - ash::{extensions::khr, vk}, + ash::{khr, vk}, ash_window::enumerate_required_extensions, derive_builder::{Builder, UninitializedFieldError}, gpu_allocator::{ @@ -10,7 +10,7 @@ use { AllocatorDebugSettings, }, log::{error, trace, warn}, - raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}, + raw_window_handle::HasDisplayHandle, std::{ cmp::Ordering, ffi::CStr, @@ -34,7 +34,7 @@ pub type SelectPhysicalDeviceFn = dyn FnOnce(&[PhysicalDevice]) -> usize; /// Opaque handle to a device object. pub struct Device { - pub(crate) accel_struct_ext: Option, + pub(crate) accel_struct_ext: Option, pub(super) allocator: ManuallyDrop>, @@ -49,10 +49,10 @@ pub struct Device { /// The physical execution queues which all work will be submitted to. pub(crate) queues: Vec>, - pub(crate) ray_trace_ext: Option, + pub(crate) ray_trace_ext: Option, - pub(super) surface_ext: Option, - pub(super) swapchain_ext: Option, + pub(super) surface_ext: Option, + pub(super) swapchain_ext: Option, } impl Device { @@ -76,20 +76,20 @@ impl Device { let mut enabled_ext_names = Vec::with_capacity(5); if display_window { - enabled_ext_names.push(vk::KhrSwapchainFn::name().as_ptr()); + enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); } if physical_device.accel_struct_properties.is_some() { - enabled_ext_names.push(vk::KhrAccelerationStructureFn::name().as_ptr()); - enabled_ext_names.push(vk::KhrDeferredHostOperationsFn::name().as_ptr()); + enabled_ext_names.push(khr::acceleration_structure::NAME.as_ptr()); + enabled_ext_names.push(khr::deferred_host_operations::NAME.as_ptr()); } if physical_device.ray_query_features.ray_query { - enabled_ext_names.push(vk::KhrRayQueryFn::name().as_ptr()); + enabled_ext_names.push(khr::ray_query::NAME.as_ptr()); } if physical_device.ray_trace_features.ray_tracing_pipeline { - enabled_ext_names.push(vk::KhrRayTracingPipelineFn::name().as_ptr()); + enabled_ext_names.push(khr::ray_tracing_pipeline::NAME.as_ptr()); } let priorities = repeat(1.0) @@ -108,17 +108,16 @@ impl Device { .iter() .enumerate() .map(|(idx, family)| { - let mut queue_info = vk::DeviceQueueCreateInfo::builder() + let mut queue_info = vk::DeviceQueueCreateInfo::default() .queue_family_index(idx as _) - .queue_priorities(&priorities[0..family.queue_count as usize]) - .build(); + .queue_priorities(&priorities[0..family.queue_count as usize]); queue_info.queue_count = family.queue_count; queue_info }) .collect::>(); - let vk::InstanceFnV1_1 { + let ash::InstanceFnV1_1 { get_physical_device_features2, .. } = instance.fp_v1_1(); @@ -129,21 +128,19 @@ impl Device { let mut index_type_uin8_feautres = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default(); let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default(); let mut ray_trace_features = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default(); - let mut features = vk::PhysicalDeviceFeatures2::builder() + let mut features = vk::PhysicalDeviceFeatures2::default() .push_next(&mut features_v1_1) .push_next(&mut features_v1_2) .push_next(&mut acceleration_structure_features) .push_next(&mut index_type_uin8_feautres) .push_next(&mut ray_query_features) - .push_next(&mut ray_trace_features) - .build(); + .push_next(&mut ray_trace_features); unsafe { get_physical_device_features2(**physical_device, &mut features) }; - let device_create_info = vk::DeviceCreateInfo::builder() + let device_create_info = vk::DeviceCreateInfo::default() .queue_create_infos(&queue_infos) .enabled_extension_names(&enabled_ext_names) - .push_next(&mut features) - .build(); + .push_next(&mut features); create_fn(device_create_info) } @@ -205,23 +202,27 @@ impl Device { /// Constructs a new device using the given configuration. #[profiling::function] - pub fn create_display_window( + pub fn create_presentable( info: impl Into, - display_window: &(impl HasRawDisplayHandle + HasRawWindowHandle), + display_handle: &impl HasDisplayHandle, ) -> Result { let DeviceInfo { debug, select_physical_device, } = info.into(); - let required_extensions = - enumerate_required_extensions(display_window.raw_display_handle()) - .map_err(|err| { - warn!("{err}"); - - DriverError::Unsupported - })? - .iter() - .map(|ext| unsafe { CStr::from_ptr(*ext as *const _) }); + let display_handle = display_handle.display_handle().map_err(|err| { + warn!("{err}"); + + DriverError::Unsupported + })?; + let required_extensions = enumerate_required_extensions(display_handle.as_raw()) + .map_err(|err| { + warn!("{err}"); + + DriverError::Unsupported + })? + .iter() + .map(|ext| unsafe { CStr::from_ptr(*ext as *const _) }); let instance = Instance::create(debug, required_extensions)?; Self::create(instance, select_physical_device, true) @@ -234,7 +235,7 @@ impl Device { flags |= vk::FenceCreateFlags::SIGNALED; } - let create_info = vk::FenceCreateInfo::builder().flags(flags); + let create_info = vk::FenceCreateInfo::default().flags(flags); let allocation_callbacks = None; unsafe { this.create_fence(&create_info, allocation_callbacks) }.map_err(|err| { @@ -245,7 +246,7 @@ impl Device { } pub(crate) fn create_semaphore(this: &Self) -> Result { - let create_info = vk::SemaphoreCreateInfo::builder(); + let create_info = vk::SemaphoreCreateInfo::default(); let allocation_callbacks = None; unsafe { this.create_semaphore(&create_info, allocation_callbacks) }.map_err(|err| { @@ -296,17 +297,17 @@ impl Device { queues.push(queue_family); } - let surface_ext = - display_window.then(|| khr::Surface::new(Instance::entry(&instance), &instance)); - let swapchain_ext = display_window.then(|| khr::Swapchain::new(&instance, &device)); + let surface_ext = display_window + .then(|| khr::surface::Instance::new(Instance::entry(&instance), &instance)); + let swapchain_ext = display_window.then(|| khr::swapchain::Device::new(&instance, &device)); let accel_struct_ext = physical_device .accel_struct_properties .is_some() - .then(|| khr::AccelerationStructure::new(&instance, &device)); + .then(|| khr::acceleration_structure::Device::new(&instance, &device)); let ray_trace_ext = physical_device .ray_trace_features .ray_tracing_pipeline - .then(|| khr::RayTracingPipeline::new(&instance, &device)); + .then(|| khr::ray_tracing_pipeline::Device::new(&instance, &device)); Ok(Self { accel_struct_ext, diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index d91e536..b58f3b5 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -6,7 +6,7 @@ use { image::SampleCount, merge_push_constant_ranges, shader::{ - DescriptorBindingMap, DescriptorInfo, PipelineDescriptorInfo, Shader, + align_spriv, DescriptorBindingMap, DescriptorInfo, PipelineDescriptorInfo, Shader, SpecializationInfo, }, DescriptorBinding, DriverError, @@ -275,7 +275,7 @@ impl DepthStencilMode { DepthStencilModeBuilder::default() } - pub(super) fn into_vk(self) -> vk::PipelineDepthStencilStateCreateInfo { + pub(super) fn into_vk(self) -> vk::PipelineDepthStencilStateCreateInfo<'static> { vk::PipelineDepthStencilStateCreateInfo { back: self.back.into_vk(), depth_bounds_test_enable: self.bounds_test as _, @@ -454,8 +454,8 @@ impl GraphicPipeline { let mut descriptor_bindings = Shader::merge_descriptor_bindings( shaders .iter() - .map(|shader| shader.descriptor_bindings(&device)) - .collect::, _>>()?, + .map(|shader| shader.descriptor_bindings()) + ); for (descriptor_info, _) in descriptor_bindings.values_mut() { if descriptor_info.binding_count() == 0 { @@ -511,7 +511,7 @@ impl GraphicPipeline { unsafe { let layout = device .create_pipeline_layout( - &vk::PipelineLayoutCreateInfo::builder() + &vk::PipelineLayoutCreateInfo::default() .set_layouts(&descriptor_sets_layouts) .push_constant_ranges(&push_constants), None, @@ -524,13 +524,12 @@ impl GraphicPipeline { let shader_info = shaders .into_iter() .map(|shader| { - let shader_module_create_info = vk::ShaderModuleCreateInfo { - code_size: shader.spirv.len(), - p_code: shader.spirv.as_ptr() as *const u32, - ..Default::default() - }; let shader_module = device - .create_shader_module(&shader_module_create_info, None) + .create_shader_module( + &vk::ShaderModuleCreateInfo::default() + .code(align_spriv(&shader.spirv)?), + None, + ) .map_err(|err| { warn!("{err}"); diff --git a/src/driver/image.rs b/src/driver/image.rs index d21db37..164576e 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -16,7 +16,6 @@ use { fmt::{Debug, Formatter}, mem::take, ops::Deref, - ptr::null, sync::{ atomic::{AtomicU8, Ordering}, Arc, @@ -544,7 +543,7 @@ impl ImageInfo { self.into() } - fn image_create_info<'a>(self) -> vk::ImageCreateInfoBuilder<'a> { + fn image_create_info<'a>(self) -> vk::ImageCreateInfo<'a> { let (ty, extent, array_layers) = match self.ty { ImageType::Texture1D => ( vk::ImageType::TYPE_1D, @@ -615,14 +614,14 @@ impl ImageInfo { ), }; - vk::ImageCreateInfo::builder() + vk::ImageCreateInfo::default() .flags(self.flags) .image_type(ty) .format(self.fmt) .extent(extent) .mip_levels(self.mip_level_count) .array_layers(array_layers) - .samples(self.sample_count.into_vk()) + .samples(self.sample_count.into()) .tiling(self.tiling) .usage(self.usage) .sharing_mode(vk::SharingMode::CONCURRENT) @@ -792,27 +791,22 @@ impl ImageView { ) -> Result { let info = info.into(); let device = Arc::clone(device); - let create_info = vk::ImageViewCreateInfo { - s_type: vk::StructureType::IMAGE_VIEW_CREATE_INFO, - p_next: null(), - flags: vk::ImageViewCreateFlags::empty(), - view_type: info.ty.into_vk(), - format: info.fmt, - components: vk::ComponentMapping { + let create_info = vk::ImageViewCreateInfo::default().view_type(info.ty.into_vk()). + format(info.fmt). + components(vk::ComponentMapping { r: vk::ComponentSwizzle::R, g: vk::ComponentSwizzle::G, b: vk::ComponentSwizzle::B, a: vk::ComponentSwizzle::A, - }, - image, - subresource_range: vk::ImageSubresourceRange { + }). + image(image). + subresource_range(vk::ImageSubresourceRange { aspect_mask: info.aspect_mask, base_array_layer: info.base_array_layer, base_mip_level: info.base_mip_level, level_count: info.mip_level_count.unwrap_or(vk::REMAINING_MIP_LEVELS), layer_count: info.array_layer_count.unwrap_or(vk::REMAINING_ARRAY_LAYERS), - }, - }; + }); let image_view = unsafe { device.create_image_view(&create_info, None) }.map_err(|err| { @@ -997,47 +991,18 @@ pub enum SampleCount { /// Multiple image samples. Type64, - - /// Single image sample. This is the usual mode. - #[deprecated = "Use Type1"] - X1, - - /// Multiple image samples. - #[deprecated = "Use Type2"] - X2, - - /// Multiple image samples. - #[deprecated = "Use Type4"] - X4, - - /// Multiple image samples. - #[deprecated = "Use Type8"] - X8, - - /// Multiple image samples. - #[deprecated = "Use Type16"] - X16, - - /// Multiple image samples. - #[deprecated = "Use Type32"] - X32, - - /// Multiple image samples. - #[deprecated = "Use Type64"] - X64, } -impl SampleCount { - pub(super) fn into_vk(self) -> vk::SampleCountFlags { - #[allow(deprecated)] - match self { - Self::Type1 | Self::X1 => vk::SampleCountFlags::TYPE_1, - Self::Type2 | Self::X2 => vk::SampleCountFlags::TYPE_2, - Self::Type4 | Self::X4 => vk::SampleCountFlags::TYPE_4, - Self::Type8 | Self::X8 => vk::SampleCountFlags::TYPE_8, - Self::Type16 | Self::X16 => vk::SampleCountFlags::TYPE_16, - Self::Type32 | Self::X32 => vk::SampleCountFlags::TYPE_32, - Self::Type64 | Self::X64 => vk::SampleCountFlags::TYPE_64, +impl From for vk::SampleCountFlags { + fn from(sample_count: SampleCount) -> Self { + match sample_count { + SampleCount::Type1 => Self::TYPE_1, + SampleCount::Type2 => Self::TYPE_2, + SampleCount::Type4 => Self::TYPE_4, + SampleCount::Type8 => Self::TYPE_8, + SampleCount::Type16 => Self::TYPE_16, + SampleCount::Type32 => Self::TYPE_32, + SampleCount::Type64 => Self::TYPE_64, } } } diff --git a/src/driver/instance.rs b/src/driver/instance.rs index b5380d7..10f4d06 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -1,6 +1,6 @@ use { super::{physical_device::PhysicalDevice, DriverError}, - ash::{extensions::ext, vk, Entry}, + ash::{ext, vk, Entry}, log::{debug, error, info, logger, trace, warn, Level, Metadata}, std::{ env::var, @@ -87,8 +87,8 @@ unsafe extern "system" fn vulkan_debug_callback( pub struct Instance { _debug_callback: Option, #[allow(deprecated)] // TODO: Remove? Look into this.... - _debug_loader: Option, - debug_utils: Option, + _debug_loader: Option, + debug_utils: Option, entry: Entry, instance: ash::Instance, } @@ -127,8 +127,8 @@ impl Instance { .iter() .map(|raw_name| raw_name.as_ptr()) .collect(); - let app_desc = vk::ApplicationInfo::builder().api_version(vk::API_VERSION_1_2); - let instance_desc = vk::InstanceCreateInfo::builder() + let app_desc = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_2); + let instance_desc = vk::InstanceCreateInfo::default() .application_info(&app_desc) .enabled_layer_names(&layer_names) .enabled_extension_names(&instance_extensions); @@ -165,7 +165,7 @@ impl Instance { }; #[allow(deprecated)] - let debug_loader = ext::DebugReport::new(&entry, &instance); + let debug_loader = ext::debug_report::Instance::new(&entry, &instance); let debug_callback = unsafe { #[allow(deprecated)] @@ -174,7 +174,7 @@ impl Instance { .unwrap() }; - let debug_utils = ext::DebugUtils::new(&entry, &instance); + let debug_utils = ext::debug_utils::Instance::new(&entry, &instance); (Some(debug_loader), Some(debug_callback), Some(debug_utils)) } else { @@ -221,8 +221,8 @@ impl Instance { if debug { #[allow(deprecated)] - res.push(ext::DebugReport::name().as_ptr()); - res.push(vk::ExtDebugUtilsFn::name().as_ptr()); + res.push(ext::debug_report::NAME.as_ptr()); + res.push(ext::debug_utils::NAME.as_ptr()); } res diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index 1837392..97e2586 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -2,7 +2,7 @@ use { super::{DriverError, Instance}, - ash::vk, + ash::{vk, khr, ext}, log::{debug, error}, std::{ collections::HashSet, @@ -56,10 +56,10 @@ pub struct AccelerationStructureProperties { pub min_accel_struct_scratch_offset_alignment: u32, } -impl From +impl From> for AccelerationStructureProperties { - fn from(props: vk::PhysicalDeviceAccelerationStructurePropertiesKHR) -> Self { + fn from(props: vk::PhysicalDeviceAccelerationStructurePropertiesKHR<'_>) -> Self { Self { max_geometry_count: props.max_geometry_count, max_instance_count: props.max_instance_count, @@ -107,8 +107,8 @@ pub struct DepthStencilResolveProperties { pub independent_resolve: bool, } -impl From for DepthStencilResolveProperties { - fn from(props: vk::PhysicalDeviceDepthStencilResolveProperties) -> Self { +impl From> for DepthStencilResolveProperties { + fn from(props: vk::PhysicalDeviceDepthStencilResolveProperties<'_>) -> Self { Self { supported_depth_resolve_modes: props.supported_depth_resolve_modes, supported_stencil_resolve_modes: props.supported_stencil_resolve_modes, @@ -130,8 +130,8 @@ pub struct IndexTypeUint8Features { pub index_type_uint8: bool, } -impl From for IndexTypeUint8Features { - fn from(features: vk::PhysicalDeviceIndexTypeUint8FeaturesEXT) -> Self { +impl From> for IndexTypeUint8Features { + fn from(features: vk::PhysicalDeviceIndexTypeUint8FeaturesEXT<'_>) -> Self { Self { index_type_uint8: features.index_type_uint8 == vk::TRUE, } @@ -219,7 +219,7 @@ impl PhysicalDevice { let queue_families = queue_families.into(); let queue_family_indices = queue_family_indices.into(); - let vk::InstanceFnV1_1 { + let ash::InstanceFnV1_1 { get_physical_device_features2, get_physical_device_properties2, .. @@ -233,14 +233,13 @@ impl PhysicalDevice { let mut index_type_u8_features = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default(); let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default(); let mut ray_trace_features = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default(); - let mut features = vk::PhysicalDeviceFeatures2::builder() + let mut features = vk::PhysicalDeviceFeatures2::default() .push_next(&mut features_v1_1) .push_next(&mut features_v1_2) .push_next(&mut acceleration_structure_features) .push_next(&mut index_type_u8_features) .push_next(&mut ray_query_features) - .push_next(&mut ray_trace_features) - .build(); + .push_next(&mut ray_trace_features); unsafe { get_physical_device_features2(physical_device, &mut features); } @@ -258,14 +257,14 @@ impl PhysicalDevice { let mut ray_trace_properties = vk::PhysicalDeviceRayTracingPipelinePropertiesKHR::default(); let mut sampler_filter_minmax_properties = vk::PhysicalDeviceSamplerFilterMinmaxProperties::default(); - let mut properties = vk::PhysicalDeviceProperties2::builder() + let mut properties = vk::PhysicalDeviceProperties2::default() .push_next(&mut properties_v1_1) .push_next(&mut properties_v1_2) .push_next(&mut accel_struct_properties) .push_next(&mut depth_stencil_resolve_properties) .push_next(&mut ray_trace_properties) .push_next(&mut sampler_filter_minmax_properties) - .build(); + ; unsafe { get_physical_device_properties2(physical_device, &mut properties); } @@ -306,11 +305,11 @@ impl PhysicalDevice { .filter(|&extension_name| !extension_name.is_null()) .map(|extension_name| unsafe { CStr::from_ptr(extension_name) }) .collect::>(); - let supports_accel_struct = extensions.contains(vk::KhrAccelerationStructureFn::name()) - && extensions.contains(vk::KhrDeferredHostOperationsFn::name()); - let supports_index_type_uint8 = extensions.contains(vk::ExtIndexTypeUint8Fn::name()); - let supports_ray_query = extensions.contains(vk::KhrRayQueryFn::name()); - let supports_ray_trace = extensions.contains(vk::KhrRayTracingPipelineFn::name()); + let supports_accel_struct = extensions.contains(khr::acceleration_structure::NAME) + && extensions.contains(khr::deferred_host_operations::NAME); + let supports_index_type_uint8 = extensions.contains(ext::index_type_uint8::NAME); + let supports_ray_query = extensions.contains(khr::ray_query::NAME); + let supports_ray_trace = extensions.contains(khr::ray_tracing_pipeline::NAME); // Gather optional features and properties of the physical device let index_type_uint8_features = supports_index_type_uint8 @@ -377,8 +376,8 @@ pub struct RayQueryFeatures { pub ray_query: bool, } -impl From for RayQueryFeatures { - fn from(features: vk::PhysicalDeviceRayQueryFeaturesKHR) -> Self { +impl From> for RayQueryFeatures { + fn from(features: vk::PhysicalDeviceRayQueryFeaturesKHR<'_>) -> Self { Self { ray_query: features.ray_query == vk::TRUE, } @@ -417,8 +416,8 @@ pub struct RayTraceFeatures { pub ray_traversal_primitive_culling: bool, } -impl From for RayTraceFeatures { - fn from(features: vk::PhysicalDeviceRayTracingPipelineFeaturesKHR) -> Self { +impl From> for RayTraceFeatures { + fn from(features: vk::PhysicalDeviceRayTracingPipelineFeaturesKHR<'_>) -> Self { Self { ray_tracing_pipeline: features.ray_tracing_pipeline == vk::TRUE, ray_tracing_pipeline_shader_group_handle_capture_replay: features @@ -471,8 +470,8 @@ pub struct RayTraceProperties { pub max_ray_hit_attribute_size: u32, } -impl From for RayTraceProperties { - fn from(props: vk::PhysicalDeviceRayTracingPipelinePropertiesKHR) -> Self { +impl From> for RayTraceProperties { + fn from(props: vk::PhysicalDeviceRayTracingPipelinePropertiesKHR<'_>) -> Self { Self { shader_group_handle_size: props.shader_group_handle_size, max_ray_recursion_depth: props.max_ray_recursion_depth, @@ -522,8 +521,8 @@ pub struct SamplerFilterMinmaxProperties { pub single_component_formats: bool, } -impl From for SamplerFilterMinmaxProperties { - fn from(value: vk::PhysicalDeviceSamplerFilterMinmaxProperties) -> Self { +impl From> for SamplerFilterMinmaxProperties { + fn from(value: vk::PhysicalDeviceSamplerFilterMinmaxProperties<'_>) -> Self { Self { image_component_mapping: value.filter_minmax_image_component_mapping == vk::TRUE, single_component_formats: value.filter_minmax_single_component_formats == vk::TRUE, @@ -1520,8 +1519,8 @@ pub struct Vulkan11Features { pub shader_draw_parameters: bool, } -impl From for Vulkan11Features { - fn from(features: vk::PhysicalDeviceVulkan11Features) -> Self { +impl From> for Vulkan11Features { + fn from(features: vk::PhysicalDeviceVulkan11Features<'_>) -> Self { Self { storage_buffer16_bit_access: features.storage_buffer16_bit_access == vk::TRUE, uniform_and_storage_buffer16_bit_access: features @@ -1627,8 +1626,8 @@ pub struct Vulkan11Properties { pub max_memory_allocation_size: vk::DeviceSize, } -impl From for Vulkan11Properties { - fn from(props: vk::PhysicalDeviceVulkan11Properties) -> Self { +impl From> for Vulkan11Properties { + fn from(props: vk::PhysicalDeviceVulkan11Properties<'_>) -> Self { Self { device_uuid: props.device_uuid, driver_uuid: props.driver_uuid, @@ -1990,8 +1989,8 @@ pub struct Vulkan12Features { pub subgroup_broadcast_dynamic_id: bool, } -impl From for Vulkan12Features { - fn from(features: vk::PhysicalDeviceVulkan12Features) -> Self { +impl From> for Vulkan12Features { + fn from(features: vk::PhysicalDeviceVulkan12Features<'_>) -> Self { Self { sampler_mirror_clamp_to_edge: features.sampler_mirror_clamp_to_edge == vk::TRUE, draw_indirect_count: features.draw_indirect_count == vk::TRUE, @@ -2413,8 +2412,8 @@ pub struct Vulkan12Properties { pub framebuffer_integer_color_sample_counts: vk::SampleCountFlags, } -impl From for Vulkan12Properties { - fn from(properties: vk::PhysicalDeviceVulkan12Properties) -> Self { +impl From> for Vulkan12Properties { + fn from(properties: vk::PhysicalDeviceVulkan12Properties<'_>) -> Self { Self { driver_id: properties.driver_id, driver_name: vk_cstr_to_string_lossy(&properties.driver_name), diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 1f57f83..8790e80 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -5,7 +5,7 @@ use { device::Device, merge_push_constant_ranges, physical_device::RayTraceProperties, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, + shader::{align_spriv, DescriptorBindingMap, PipelineDescriptorInfo, Shader}, DriverError, }, ash::vk, @@ -132,8 +132,7 @@ impl RayTracePipeline { let mut descriptor_bindings = Shader::merge_descriptor_bindings( shaders .iter() - .map(|shader| shader.descriptor_bindings(device)) - .collect::, _>>()?, + .map(|shader| shader.descriptor_bindings()) ); for (descriptor_info, _) in descriptor_bindings.values_mut() { if descriptor_info.binding_count() == 0 { @@ -151,7 +150,7 @@ impl RayTracePipeline { unsafe { let layout = device .create_pipeline_layout( - &vk::PipelineLayoutCreateInfo::builder() + &vk::PipelineLayoutCreateInfo::default() .set_layouts(&descriptor_set_layout_handles) .push_constant_ranges(&push_constants), None, @@ -161,59 +160,58 @@ impl RayTracePipeline { DriverError::Unsupported })?; - let mut entry_points: Vec = Vec::with_capacity(shaders.len()); // Keep entry point names alive, since build() forgets references. + let entry_points: Box<[CString]> = shaders + .iter() + .map(|shader| CString::new(shader.entry_name.as_str())) + .collect::>() + .map_err(|err| { + warn!("{err}"); + + DriverError::InvalidData + })?; + let specialization_infos: Box<[Option]> = shaders + .iter() + .map(|shader| { + shader.specialization_info.as_ref().map(|info| { + vk::SpecializationInfo::default() + .data(&info.data) + .map_entries(&info.map_entries) + }) + }) + .collect(); let mut shader_stages: Vec = Vec::with_capacity(shaders.len()); - let create_shader_module = - |info: &Shader| -> Result<(vk::ShaderModule, String), DriverError> { - let shader_module_create_info = vk::ShaderModuleCreateInfo { - code_size: info.spirv.len(), - p_code: info.spirv.as_ptr() as *const u32, - ..Default::default() - }; - let shader_module = device - .create_shader_module(&shader_module_create_info, None) - .map_err(|err| { - warn!("{err}"); - - DriverError::Unsupported - })?; - - Ok((shader_module, info.entry_name.clone())) - }; - - let mut specializations = Vec::with_capacity(shaders.len()); let mut shader_modules = Vec::with_capacity(shaders.len()); - for shader in &shaders { - let res = create_shader_module(shader); - if res.is_err() { - device.destroy_pipeline_layout(layout, None); + for (idx, shader) in shaders.iter().enumerate() { + let module = device + .create_shader_module( + &vk::ShaderModuleCreateInfo::default().code(align_spriv(&shader.spirv)?), + None, + ) + .map_err(|err| { + warn!("{err}"); - for shader_module in &shader_modules { - device.destroy_shader_module(*shader_module, None); - } - } + device.destroy_pipeline_layout(layout, None); + + for module in shader_modules.drain(..) { + device.destroy_shader_module(module, None); + } + + DriverError::Unsupported + })?; - let (module, entry_point) = res?; - entry_points.push(CString::new(entry_point).unwrap()); shader_modules.push(module); - let mut stage = vk::PipelineShaderStageCreateInfo::builder() + let mut stage = vk::PipelineShaderStageCreateInfo::default() .module(module) - .name(entry_points.last().unwrap().as_ref()) + .name(entry_points[idx].as_ref()) .stage(shader.stage); - if let Some(spec_info) = &shader.specialization_info { - specializations.push( - vk::SpecializationInfo::builder() - .data(&spec_info.data) - .map_entries(&spec_info.map_entries) - .build(), - ); - stage = stage.specialization_info(specializations.last().unwrap()); + if let Some(specialization_info) = &specialization_infos[idx] { + stage = stage.specialization_info(specialization_info); } - shader_stages.push(stage.build()); + shader_stages.push(stage); } let mut dynamic_states = Vec::with_capacity(1); @@ -230,7 +228,7 @@ impl RayTracePipeline { .create_ray_tracing_pipelines( vk::DeferredOperationKHR::null(), vk::PipelineCache::null(), - &[vk::RayTracingPipelineCreateInfoKHR::builder() + &[vk::RayTracingPipelineCreateInfoKHR::default() .stages(&shader_stages) .groups(&shader_groups) .max_pipeline_ray_recursion_depth( @@ -245,19 +243,22 @@ impl RayTracePipeline { ) .layout(layout) .dynamic_state( - &vk::PipelineDynamicStateCreateInfo::builder() + &vk::PipelineDynamicStateCreateInfo::default() .dynamic_states(&dynamic_states), - ) - .build()], + )], None, ) - .map_err(|err| { + .map_err(|(pipelines, err)| { warn!("{err}"); + for pipeline in pipelines { + device.destroy_pipeline(pipeline, None); + } + device.destroy_pipeline_layout(layout, None); - for shader_module in &shader_modules { - device.destroy_shader_module(*shader_module, None); + for shader_module in shader_modules.iter().copied() { + device.destroy_shader_module(shader_module, None); } DriverError::Unsupported @@ -596,9 +597,9 @@ impl RayTraceShaderGroup { } } -impl From for vk::RayTracingShaderGroupCreateInfoKHR { +impl From for vk::RayTracingShaderGroupCreateInfoKHR<'static> { fn from(shader_group: RayTraceShaderGroup) -> Self { - vk::RayTracingShaderGroupCreateInfoKHR::builder() + vk::RayTracingShaderGroupCreateInfoKHR::default() .ty(shader_group.ty.into()) .any_hit_shader(shader_group.any_hit_shader.unwrap_or(vk::SHADER_UNUSED_KHR)) .closest_hit_shader( @@ -612,7 +613,6 @@ impl From for vk::RayTracingShaderGroupCreateInfoKHR { .intersection_shader .unwrap_or(vk::SHADER_UNUSED_KHR), ) - .build() } } diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index e7e8564..a15affc 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -25,19 +25,18 @@ pub(crate) struct AttachmentInfo { pub final_layout: vk::ImageLayout, } -impl AttachmentInfo { - pub fn into_vk(self) -> vk::AttachmentDescription2 { - vk::AttachmentDescription2::builder() - .flags(self.flags) - .format(self.fmt) - .samples(self.sample_count.into_vk()) - .load_op(self.load_op) - .store_op(self.store_op) - .stencil_load_op(self.stencil_load_op) - .stencil_store_op(self.stencil_store_op) - .initial_layout(self.initial_layout) - .final_layout(self.final_layout) - .build() +impl<'a> From for vk::AttachmentDescription2<'a> { + fn from(value: AttachmentInfo) -> Self { + vk::AttachmentDescription2::default() + .flags(value.flags) + .format(value.fmt) + .samples(value.sample_count.into()) + .load_op(value.load_op) + .store_op(value.store_op) + .stencil_load_op(value.stencil_load_op) + .stencil_store_op(value.stencil_store_op) + .initial_layout(value.initial_layout) + .final_layout(value.final_layout) } } @@ -64,12 +63,12 @@ pub(crate) struct AttachmentRef { pub layout: vk::ImageLayout, } -impl AttachmentRef { - fn into_vk(self) -> vk::AttachmentReference2Builder<'static> { - vk::AttachmentReference2::builder() - .attachment(self.attachment) - .aspect_mask(self.aspect_mask) - .layout(self.layout) +impl<'a> From for vk::AttachmentReference2<'a> { + fn from(attachment_ref: AttachmentRef) -> Self { + vk::AttachmentReference2::default() + .attachment(attachment_ref.attachment) + .aspect_mask(attachment_ref.aspect_mask) + .layout(attachment_ref.layout) } } @@ -124,34 +123,74 @@ impl RenderPass { let attachments = info .attachments .iter() - .map(|attachment| attachment.into_vk()) + .copied() + .map(Into::into) .collect::>(); + let correlated_view_masks = info + .subpasses + .iter() + .any(|subpass| subpass.view_mask != 0) + .then(|| { + info.subpasses + .iter() + .map(|subpass| subpass.correlated_view_mask) + .collect::>() + }) + .unwrap_or_default(); let dependencies = info .dependencies .iter() - .map(|dependency| dependency.into_vk()) + .copied() + .map(Into::into) .collect::>(); - // These vecs must stay alive and not be resized until the create function completes! - let mut subpass_attachments = Vec::with_capacity( - info.subpasses - .iter() - .map(|subpass| { - subpass.color_attachments.len() * 2 - + subpass.input_attachments.len() - + subpass.depth_stencil_attachment.is_some() as usize - + subpass.depth_stencil_resolve_attachment.is_some() as usize - }) - .sum(), - ); + let subpass_attachments = info + .subpasses + .iter() + .flat_map(|subpass| { + subpass + .color_attachments + .iter() + .chain(subpass.input_attachments.iter()) + .chain(subpass.color_resolve_attachments.iter()) + .chain(subpass.depth_stencil_attachment.iter()) + .chain( + subpass + .depth_stencil_resolve_attachment + .as_ref() + .map(|(resolve_attachment, _, _)| resolve_attachment) + .into_iter(), + ) + .copied() + .map(AttachmentRef::into) + }) + .collect::>(); + let mut subpass_depth_stencil_resolves = info + .subpasses + .iter() + .map(|subpass| { + subpass.depth_stencil_resolve_attachment.map( + |(_, depth_resolve_mode, stencil_resolve_mode)| { + vk::SubpassDescriptionDepthStencilResolve::default() + .depth_resolve_mode( + depth_resolve_mode.map(Into::into).unwrap_or_default(), + ) + .stencil_resolve_mode( + stencil_resolve_mode.map(Into::into).unwrap_or_default(), + ) + }, + ) + }) + .collect::>(); let mut subpasses = Vec::with_capacity(info.subpasses.len()); - let mut subpass_depth_stencil_resolves = Vec::with_capacity(info.subpasses.len()); - let mut has_viewmasks = false; - - for subpass in &info.subpasses { - has_viewmasks |= subpass.view_mask != 0; - let mut subpass_desc = vk::SubpassDescription2::builder() + let mut base_idx = 0; + for (subpass, depth_stencil_resolve) in info + .subpasses + .iter() + .zip(subpass_depth_stencil_resolves.iter_mut()) + { + let mut desc = vk::SubpassDescription2::default() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS); debug_assert_eq!( @@ -159,82 +198,48 @@ impl RenderPass { subpass.color_resolve_attachments.len() ); - let color_attachments_idx = subpass_attachments.len(); - let input_attachments_idx = color_attachments_idx + subpass.color_attachments.len(); - let resolve_color_attachments_idx = - input_attachments_idx + subpass.input_attachments.len(); - subpass_attachments.extend( - subpass - .color_attachments - .iter() - .chain(subpass.input_attachments.iter()) - .chain(subpass.color_resolve_attachments.iter()) - .map(|attachment| attachment.into_vk().build()), - ); - - let resolve_depth_stencil_attachment_idx = subpass_attachments.len(); - if let Some((resolve_attachment, depth_resolve_mode, stencil_resolve_mode)) = - subpass.depth_stencil_resolve_attachment - { - subpass_attachments.push(resolve_attachment.into_vk().build()); - subpass_depth_stencil_resolves.push( - vk::SubpassDescriptionDepthStencilResolve::builder() - .depth_stencil_resolve_attachment(subpass_attachments.last().unwrap()) - .depth_resolve_mode(ResolveMode::into_vk(depth_resolve_mode)) - .stencil_resolve_mode(ResolveMode::into_vk(stencil_resolve_mode)) - .build(), - ); - - subpass_desc = - subpass_desc.push_next(subpass_depth_stencil_resolves.last_mut().unwrap()); + let color_idx = base_idx; + let input_idx = color_idx + subpass.color_attachments.len(); + let color_resolve_idx = input_idx + subpass.input_attachments.len(); + let depth_stencil_idx = color_resolve_idx + subpass.color_resolve_attachments.len(); + let depth_stencil_resolve_idx = + depth_stencil_idx + subpass.depth_stencil_attachment.is_some() as usize; + base_idx = depth_stencil_resolve_idx + + subpass.depth_stencil_resolve_attachment.is_some() as usize; + + if subpass.depth_stencil_attachment.is_some() { + desc = desc.depth_stencil_attachment(&subpass_attachments[depth_stencil_idx]); } - if let Some(depth_stencil_attachment) = subpass.depth_stencil_attachment { - subpass_attachments.push(depth_stencil_attachment.into_vk().build()); - subpass_desc = - subpass_desc.depth_stencil_attachment(subpass_attachments.last().unwrap()); + if let Some(depth_stencil_resolve) = depth_stencil_resolve { + desc = desc.push_next(depth_stencil_resolve); } subpasses.push( - subpass_desc - .color_attachments( - &subpass_attachments[color_attachments_idx..input_attachments_idx], - ) - .input_attachments( - &subpass_attachments[input_attachments_idx..resolve_color_attachments_idx], - ) - .resolve_attachments( - &subpass_attachments - [resolve_color_attachments_idx..resolve_depth_stencil_attachment_idx], - ) + desc.color_attachments(&subpass_attachments[color_idx..input_idx]) + .input_attachments(&subpass_attachments[input_idx..color_resolve_idx]) + .resolve_attachments(&subpass_attachments[color_resolve_idx..depth_stencil_idx]) .preserve_attachments(&subpass.preserve_attachments) - .view_mask(subpass.view_mask) - .build(), + .view_mask(subpass.view_mask), ); } - let correlated_view_masks = if has_viewmasks { - info.subpasses - .iter() - .map(|subpass| subpass.correlated_view_mask) - .collect::>() - } else { - Box::new([]) - }; - let render_pass = unsafe { - device.create_render_pass2( - &vk::RenderPassCreateInfo2::builder() - .flags(vk::RenderPassCreateFlags::empty()) - .attachments(&attachments) - .dependencies(&dependencies) - .subpasses(&subpasses) - .correlated_view_masks(&correlated_view_masks), - None, - ) - }; + device + .create_render_pass2( + &vk::RenderPassCreateInfo2::default() + .attachments(&attachments) + .correlated_view_masks(&correlated_view_masks) + .dependencies(&dependencies) + .subpasses(&subpasses), + None, + ) + .map_err(|err| { + warn!("{err}"); - let render_pass = render_pass.map_err(|_| DriverError::InvalidData)?; + DriverError::Unsupported + })? + }; Ok(Self { info, @@ -273,19 +278,18 @@ impl RenderPass { .attachments .iter() .map(|attachment| { - vk::FramebufferAttachmentImageInfo::builder() + vk::FramebufferAttachmentImageInfo::default() .flags(attachment.flags) .width(attachment.width) .height(attachment.height) .layer_count(attachment.layer_count) .usage(attachment.usage) .view_formats(&attachment.view_formats) - .build() }) .collect::>(); let mut imageless_info = - vk::FramebufferAttachmentsCreateInfoKHR::builder().attachment_image_infos(&attachments); - let mut create_info = vk::FramebufferCreateInfo::builder() + vk::FramebufferAttachmentsCreateInfoKHR::default().attachment_image_infos(&attachments); + let mut create_info = vk::FramebufferCreateInfo::default() .flags(vk::FramebufferCreateFlags::IMAGELESS) .render_pass(this.render_pass) .width(key.width) @@ -338,50 +342,58 @@ impl RenderPass { .iter() .map(|_| pipeline.info.blend.into_vk()) .collect::>(); - let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder() + let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default() .attachments(&color_blend_attachment_states); let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; let dynamic_state = - vk::PipelineDynamicStateCreateInfo::builder().dynamic_states(&dynamic_states); - let multisample_state = vk::PipelineMultisampleStateCreateInfo::builder() + vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); + let multisample_state = vk::PipelineMultisampleStateCreateInfo::default() .alpha_to_coverage_enable(pipeline.state.multisample.alpha_to_coverage_enable) .alpha_to_one_enable(pipeline.state.multisample.alpha_to_one_enable) .flags(pipeline.state.multisample.flags) .min_sample_shading(pipeline.state.multisample.min_sample_shading) - .rasterization_samples(pipeline.state.multisample.rasterization_samples.into_vk()) + .rasterization_samples(pipeline.state.multisample.rasterization_samples.into()) .sample_shading_enable(pipeline.state.multisample.sample_shading_enable) .sample_mask(&pipeline.state.multisample.sample_mask); - let mut specializations = Vec::with_capacity(pipeline.state.stages.len()); - let stages = pipeline + let specializations = pipeline .state .stages .iter() .map(|stage| { - let mut info = vk::PipelineShaderStageCreateInfo::builder() + stage + .specialization_info + .as_ref() + .map(|specialization_info| { + vk::SpecializationInfo::default() + .map_entries(&specialization_info.map_entries) + .data(&specialization_info.data) + }) + }) + .collect::>(); + let stages = pipeline + .state + .stages + .iter() + .zip(specializations.iter()) + .map(|(stage, specialization)| { + let mut info = vk::PipelineShaderStageCreateInfo::default() .module(stage.module) .name(&stage.name) .stage(stage.flags); - if let Some(specialization_info) = &stage.specialization_info { - specializations.push( - vk::SpecializationInfo::builder() - .map_entries(&specialization_info.map_entries) - .data(&specialization_info.data) - .build(), - ); - - info = info.specialization_info(specializations.last().unwrap()); + if let Some(specialization) = specialization { + info = info.specialization_info(specialization); } - info.build() + info }) .collect::>(); - let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::builder() + let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default() .vertex_attribute_descriptions( &pipeline.state.vertex_input.vertex_attribute_descriptions, ) .vertex_binding_descriptions(&pipeline.state.vertex_input.vertex_binding_descriptions); - let viewport_state = vk::PipelineViewportStateCreateInfo::builder() + let viewport_state = vk::PipelineViewportStateCreateInfo::default() .viewport_count(1) .scissor_count(1); let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo { @@ -398,7 +410,7 @@ impl RenderPass { cull_mode: pipeline.info.cull_mode, ..Default::default() }; - let graphic_pipeline_info = vk::GraphicsPipelineCreateInfo::builder() + let graphic_pipeline_info = vk::GraphicsPipelineCreateInfo::default() .color_blend_state(&color_blend_state) .depth_stencil_state(&depth_stencil) .dynamic_state(&dynamic_state) @@ -422,7 +434,7 @@ impl RenderPass { .map_err(|(_, err)| { warn!( "create_graphics_pipelines: {err}\n{:#?}", - graphic_pipeline_info.build() + graphic_pipeline_info ); DriverError::Unsupported @@ -479,14 +491,13 @@ pub enum ResolveMode { SampleZero, } -impl ResolveMode { - fn into_vk(mode: Option) -> vk::ResolveModeFlags { +impl From for vk::ResolveModeFlags { + fn from(mode: ResolveMode) -> Self { match mode { - None => vk::ResolveModeFlags::NONE, - Some(ResolveMode::Average) => vk::ResolveModeFlags::AVERAGE, - Some(ResolveMode::Maximum) => vk::ResolveModeFlags::MAX, - Some(ResolveMode::Minimum) => vk::ResolveModeFlags::MIN, - Some(ResolveMode::SampleZero) => vk::ResolveModeFlags::SAMPLE_ZERO, + ResolveMode::Average => vk::ResolveModeFlags::AVERAGE, + ResolveMode::Maximum => vk::ResolveModeFlags::MAX, + ResolveMode::Minimum => vk::ResolveModeFlags::MIN, + ResolveMode::SampleZero => vk::ResolveModeFlags::SAMPLE_ZERO, } } } @@ -514,17 +525,18 @@ impl SubpassDependency { dependency_flags: vk::DependencyFlags::empty(), } } +} - pub fn into_vk(self) -> vk::SubpassDependency2 { - vk::SubpassDependency2::builder() - .src_subpass(self.src_subpass) - .dst_subpass(self.dst_subpass) - .src_stage_mask(self.src_stage_mask) - .dst_stage_mask(self.dst_stage_mask) - .src_access_mask(self.src_access_mask) - .dst_access_mask(self.dst_access_mask) - .dependency_flags(self.dependency_flags) - .build() +impl<'a> From for vk::SubpassDependency2<'a> { + fn from(value: SubpassDependency) -> Self { + vk::SubpassDependency2::default() + .src_subpass(value.src_subpass) + .dst_subpass(value.dst_subpass) + .src_stage_mask(value.src_stage_mask) + .dst_stage_mask(value.dst_stage_mask) + .src_access_mask(value.src_access_mask) + .dst_access_mask(value.dst_access_mask) + .dependency_flags(value.dependency_flags) } } diff --git a/src/driver/shader.rs b/src/driver/shader.rs index dc99f20..c16c5de 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -26,6 +26,18 @@ use { pub(crate) type DescriptorBindingMap = HashMap; +pub(crate) fn align_spriv(code: &[u8]) -> Result<&[u32], DriverError> { + let (prefix, code, suffix) = unsafe { code.align_to() }; + + if prefix.len() + suffix.len() == 0 { + Ok(code) + } else { + warn!("Invalid SPIR-V code"); + + Err(DriverError::InvalidData) + } +} + #[profiling::function] fn guess_immutable_sampler(binding_name: &str) -> SamplerInfo { const INVALID_ERR: &str = "Invalid sampler specification"; @@ -99,13 +111,13 @@ impl From<(u32, u32)> for DescriptorBinding { } } -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum DescriptorInfo { AccelerationStructure(u32), - CombinedImageSampler(u32, Sampler, bool), //count, sampler, is-manually-defined? - InputAttachment(u32, u32), //count, input index, + CombinedImageSampler(u32, SamplerInfo, bool), //count, sampler, is-manually-defined? + InputAttachment(u32, u32), //count, input index, SampledImage(u32), - Sampler(u32, Sampler, bool), //count, sampler, is-manually-defined? + Sampler(u32, SamplerInfo, bool), //count, sampler, is-manually-defined? StorageBuffer(u32), StorageImage(u32), StorageTexelBuffer(u32), @@ -114,8 +126,8 @@ pub(crate) enum DescriptorInfo { } impl DescriptorInfo { - pub fn binding_count(&self) -> u32 { - match *self { + pub fn binding_count(self) -> u32 { + match self { Self::AccelerationStructure(binding_count) => binding_count, Self::CombinedImageSampler(binding_count, ..) => binding_count, Self::InputAttachment(binding_count, _) => binding_count, @@ -129,7 +141,7 @@ impl DescriptorInfo { } } - pub fn descriptor_type(&self) -> vk::DescriptorType { + pub fn descriptor_type(self) -> vk::DescriptorType { match self { Self::AccelerationStructure(_) => vk::DescriptorType::ACCELERATION_STRUCTURE_KHR, Self::CombinedImageSampler(..) => vk::DescriptorType::COMBINED_IMAGE_SAMPLER, @@ -144,10 +156,10 @@ impl DescriptorInfo { } } - pub fn sampler(&self) -> Option<&Sampler> { + pub fn sampler_info(self) -> Option { match self { - Self::CombinedImageSampler(_, sampler, _) | Self::Sampler(_, sampler, _) => { - Some(sampler) + Self::CombinedImageSampler(_, sampler_info, _) | Self::Sampler(_, sampler_info, _) => { + Some(sampler_info) } _ => None, } @@ -192,9 +204,50 @@ impl PipelineDescriptorInfo { //trace!("descriptor_bindings: {:#?}", &descriptor_bindings); + let mut sampler_info_binding_count = HashMap::<_, u32>::with_capacity( + descriptor_bindings + .values() + .filter(|(descriptor_info, _)| descriptor_info.sampler_info().is_some()) + .count(), + ); + + for (sampler_info, binding_count) in + descriptor_bindings + .values() + .filter_map(|(descriptor_info, _)| { + descriptor_info + .sampler_info() + .map(|sampler_info| (sampler_info, descriptor_info.binding_count())) + }) + { + sampler_info_binding_count + .entry(sampler_info) + .and_modify(|sampler_info_binding_count| { + *sampler_info_binding_count = binding_count.max(*sampler_info_binding_count); + }) + .or_insert(binding_count); + } + + let samplers = sampler_info_binding_count + .keys() + .copied() + .map(|sampler_info| { + Sampler::create(device, sampler_info).map(|sampler| (sampler_info, sampler)) + }) + .collect::, _>>()?; + let immutable_samplers = sampler_info_binding_count + .iter() + .map(|(sampler_info, &binding_count)| { + ( + *sampler_info, + repeat(*samplers[sampler_info]) + .take(binding_count as _) + .collect::>(), + ) + }) + .collect::>(); + for descriptor_set_idx in 0..descriptor_set_count { - // HACK: We need to keep the immutable samplers alive until create, could be cleaner.. - let mut immutable_samplers = vec![]; let mut binding_counts = HashMap::::new(); let mut bindings = vec![]; @@ -202,23 +255,25 @@ impl PipelineDescriptorInfo { .iter() .filter(|(descriptor_binding, _)| descriptor_binding.0 == descriptor_set_idx) { - let descriptor_ty: vk::DescriptorType = descriptor_info.descriptor_type(); + let descriptor_ty = descriptor_info.descriptor_type(); *binding_counts.entry(descriptor_ty).or_default() += descriptor_info.binding_count(); - let mut binding = vk::DescriptorSetLayoutBinding::builder() + let mut binding = vk::DescriptorSetLayoutBinding::default() .binding(descriptor_binding.1) .descriptor_count(descriptor_info.binding_count()) .descriptor_type(descriptor_ty) .stage_flags(*stage_flags); - if let Some(sampler) = descriptor_info.sampler() { - let start = immutable_samplers.len(); - immutable_samplers - .extend(repeat(**sampler).take(descriptor_info.binding_count() as _)); - binding = binding.immutable_samplers(&immutable_samplers[start..]); + if let Some(immutable_samplers) = + descriptor_info.sampler_info().map(|sampler_info| { + &immutable_samplers[&sampler_info] + [0..descriptor_info.binding_count() as usize] + }) + { + binding = binding.immutable_samplers(immutable_samplers); } - bindings.push(binding.build()); + bindings.push(binding); } let pool_size = pool_sizes @@ -232,7 +287,7 @@ impl PipelineDescriptorInfo { //trace!("bindings: {:#?}", &bindings); let mut create_info = - vk::DescriptorSetLayoutCreateInfo::builder().bindings(bindings.as_slice()); + vk::DescriptorSetLayoutCreateInfo::default().bindings(&bindings); // The bindless flags have to be created for every descriptor set layout binding. // [vulkan spec](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkDescriptorSetLayoutBindingFlagsCreateInfo.html) @@ -243,7 +298,7 @@ impl PipelineDescriptorInfo { .features_v1_2 .descriptor_binding_partially_bound { - let bindless_flags = vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder() + let bindless_flags = vk::DescriptorSetLayoutBindingFlagsCreateInfo::default() .binding_flags(&bindless_flags); Some(bindless_flags) } else { @@ -284,7 +339,7 @@ impl Sampler { let sampler = unsafe { device .create_sampler( - &vk::SamplerCreateInfo::builder() + &vk::SamplerCreateInfo::default() .flags(info.flags) .mag_filter(info.mag_filter) .min_filter(info.min_filter) @@ -302,7 +357,7 @@ impl Sampler { .border_color(info.border_color) .unnormalized_coordinates(info.unnormalized_coordinates) .push_next( - &mut vk::SamplerReductionModeCreateInfo::builder() + &mut vk::SamplerReductionModeCreateInfo::default() .reduction_mode(info.reduction_mode), ), None, @@ -669,7 +724,7 @@ pub struct Shader { /// /// Although SPIR-V code is specified as `u32` values, this field uses `u8` in order to make /// loading from file simpler. You should always have a SPIR-V code length which is a multiple - /// of four bytes, or a panic will happen during pipeline creation. + /// of four bytes, or an error will be returned during pipeline creation. pub spirv: Vec, /// The shader stage this structure applies to. @@ -844,10 +899,7 @@ impl Shader { } #[profiling::function] - pub(super) fn descriptor_bindings( - &self, - device: &Arc, - ) -> Result { + pub(super) fn descriptor_bindings(&self) -> DescriptorBindingMap { let mut res = DescriptorBindingMap::default(); for (name, binding, desc_ty, binding_count) in @@ -886,7 +938,7 @@ impl Shader { DescriptorInfo::CombinedImageSampler( binding_count, - Sampler::create(device, sampler_info)?, + sampler_info, is_manually_defined, ) } @@ -898,11 +950,7 @@ impl Shader { let (sampler_info, is_manually_defined) = self.image_sampler(binding, name.as_deref().unwrap_or_default()); - DescriptorInfo::Sampler( - binding_count, - Sampler::create(device, sampler_info)?, - is_manually_defined, - ) + DescriptorInfo::Sampler(binding_count, sampler_info, is_manually_defined) } DescriptorType::StorageBuffer(_access_ty) => { DescriptorInfo::StorageBuffer(binding_count) @@ -921,7 +969,7 @@ impl Shader { res.insert(binding, (descriptor_info, self.stage)); } - Ok(res) + res } fn image_sampler(&self, binding: DescriptorBinding, name: &str) -> (SamplerInfo, bool) { diff --git a/src/driver/surface.rs b/src/driver/surface.rs index a7af147..4dada0b 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -4,8 +4,8 @@ use { super::{device::Device, DriverError, Instance}, ash::vk, ash_window::create_surface, - log::error, - raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}, + log::warn, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, std::{ fmt::{Debug, Formatter}, ops::Deref, @@ -28,21 +28,31 @@ impl Surface { #[profiling::function] pub fn create( device: &Arc, - display_window: &(impl HasRawDisplayHandle + HasRawWindowHandle), + window: &(impl HasDisplayHandle + HasWindowHandle), ) -> Result { let device = Arc::clone(device); let instance = Device::instance(&device); + let display_handle = window.display_handle().map_err(|err| { + warn!("{err}"); + + DriverError::Unsupported + })?; + let window_handle = window.window_handle().map_err(|err| { + warn!("{err}"); + + DriverError::Unsupported + })?; let surface = unsafe { create_surface( Instance::entry(instance), instance, - display_window.raw_display_handle(), - display_window.raw_window_handle(), + display_handle.as_raw(), + window_handle.as_raw(), None, ) } .map_err(|err| { - error!("Unable to create surface: {err}"); + warn!("Unable to create surface: {err}"); DriverError::Unsupported })?; @@ -60,7 +70,7 @@ impl Surface { .unwrap() .get_physical_device_surface_formats(*this.device.physical_device, this.surface) .map_err(|err| { - error!("Unable to get surface formats: {err}"); + warn!("Unable to get surface formats: {err}"); DriverError::Unsupported }) diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 27bb2c8..ea21f56 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -207,7 +207,7 @@ impl Swapchain { // We checked when handling out the swapchain image let swapchain_ext = unsafe { self.device.swapchain_ext.as_ref().unwrap_unchecked() }; - let present_info = vk::PresentInfoKHR::builder() + let present_info = vk::PresentInfoKHR::default() .wait_semaphores(slice::from_ref(&image.rendered)) .swapchains(slice::from_ref(&self.swapchain)) .image_indices(slice::from_ref(&image.image_idx)); @@ -277,9 +277,9 @@ impl Swapchain { vk::ImageUsageFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_KHR, vk::ImageUsageFlags::INPUT_ATTACHMENT, vk::ImageUsageFlags::INVOCATION_MASK_HUAWEI, - vk::ImageUsageFlags::RESERVED_16_QCOM, - vk::ImageUsageFlags::RESERVED_17_QCOM, - vk::ImageUsageFlags::RESERVED_22_EXT, + // vk::ImageUsageFlags::RESERVED_16_QCOM, + // vk::ImageUsageFlags::RESERVED_17_QCOM, + // vk::ImageUsageFlags::RESERVED_22_EXT, // vk::ImageUsageFlags::RESERVED_23_EXT, vk::ImageUsageFlags::SAMPLED, vk::ImageUsageFlags::SAMPLE_BLOCK_MATCH_QCOM, @@ -384,7 +384,7 @@ impl Swapchain { DriverError::Unsupported })?; - let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder() + let swapchain_create_info = vk::SwapchainCreateInfoKHR::default() .surface(*self.surface) .min_image_count(desired_image_count) .image_color_space(self.info.surface.color_space) @@ -400,7 +400,7 @@ impl Swapchain { .present_mode(present_mode) .clipped(true) .image_array_layers(1) - .build(); + ; let swapchain = unsafe { swapchain_ext.create_swapchain(&swapchain_create_info, None) } .map_err(|err| { warn!("{err}"); diff --git a/src/event_loop.rs b/src/event_loop.rs index 9965759..1d61396 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -20,7 +20,7 @@ use { winit::{ event::{Event, WindowEvent}, monitor::MonitorHandle, - window::{Fullscreen, Window, WindowBuilder}, + window::{Fullscreen, Window, WindowAttributes}, }, }; @@ -270,7 +270,7 @@ pub struct EventLoopBuilder { resolver_pool: Option>, surface_format_fn: Option>, swapchain_info: SwapchainInfoBuilder, - window: WindowBuilder, + window: WindowAttributes, } impl Debug for EventLoopBuilder { @@ -294,10 +294,10 @@ impl Default for EventLoopBuilder { } impl EventLoopBuilder { - /// Returns the list of all the monitors available on the system. - pub fn available_monitors(&self) -> impl Iterator { - self.event_loop.available_monitors() - } + // /// Returns the list of all the monitors available on the system. + // pub fn available_monitors(&self) -> impl Iterator { + // self.event_loop.available_monitors() + // } /// Specifies the number of in-flight command buffers, which should be greater /// than or equal to the desired swapchain image count. @@ -348,61 +348,61 @@ impl EventLoopBuilder { /// There are additional options offered by `winit` which can be accessed using the `window` /// function. pub fn fullscreen_mode(mut self, mode: FullscreenMode) -> Self { - let inner_size; - self.window = self - .window - .with_decorations(false) - .with_maximized(true) - .with_fullscreen(Some(match mode { - FullscreenMode::Borderless => { - info!("Using borderless fullscreen"); - - inner_size = None; - - Fullscreen::Borderless(None) - } - FullscreenMode::Exclusive => { - if let Some(video_mode) = - self.event_loop.primary_monitor().and_then(|monitor| { - let monitor_size = monitor.size(); - monitor.video_modes().find(|mode| { - let mode_size = mode.size(); - - // Don't pick a mode which has greater resolution than the monitor is - // currently using: it causes a panic on x11 in winit - mode_size.height <= monitor_size.height - && mode_size.width <= monitor_size.width - }) - }) - { - info!( - "Using {}x{} {}bpp @ {}hz exclusive fullscreen", - video_mode.size().width, - video_mode.size().height, - video_mode.bit_depth(), - video_mode.refresh_rate_millihertz() / 1_000 - ); - - inner_size = Some(video_mode.size()); - - Fullscreen::Exclusive(video_mode) - } else { - warn!("Using borderless fullscreen"); - - inner_size = None; - - Fullscreen::Borderless(None) - } - } - })); - - if let Some(inner_size) = inner_size.or_else(|| { - self.event_loop - .primary_monitor() - .map(|monitor| monitor.size()) - }) { - self.window = self.window.with_inner_size(inner_size); - } + // let inner_size; + // self.window = self + // .window + // .with_decorations(false) + // .with_maximized(true) + // .with_fullscreen(Some(match mode { + // FullscreenMode::Borderless => { + // info!("Using borderless fullscreen"); + + // inner_size = None; + + // Fullscreen::Borderless(None) + // } + // FullscreenMode::Exclusive => { + // if let Some(video_mode) = + // self.event_loop.primary_monitor().and_then(|monitor| { + // let monitor_size = monitor.size(); + // monitor.video_modes().find(|mode| { + // let mode_size = mode.size(); + + // // Don't pick a mode which has greater resolution than the monitor is + // // currently using: it causes a panic on x11 in winit + // mode_size.height <= monitor_size.height + // && mode_size.width <= monitor_size.width + // }) + // }) + // { + // info!( + // "Using {}x{} {}bpp @ {}hz exclusive fullscreen", + // video_mode.size().width, + // video_mode.size().height, + // video_mode.bit_depth(), + // video_mode.refresh_rate_millihertz() / 1_000 + // ); + + // inner_size = Some(video_mode.size()); + + // Fullscreen::Exclusive(video_mode) + // } else { + // warn!("Using borderless fullscreen"); + + // inner_size = None; + + // Fullscreen::Borderless(None) + // } + // } + // })); + + // if let Some(inner_size) = inner_size.or_else(|| { + // self.event_loop + // .primary_monitor() + // .map(|monitor| monitor.size()) + // }) { + // self.window = self.window.with_inner_size(inner_size); + // } self } @@ -428,9 +428,9 @@ impl EventLoopBuilder { /// ## Platform-specific /// /// **Wayland:** Always returns `None`. - pub fn primary_monitor(&self) -> Option { - self.event_loop.primary_monitor() - } + // pub fn primary_monitor(&self) -> Option { + // self.event_loop.primary_monitor() + // } /// Allows for specification of a custom pool implementation. /// @@ -443,7 +443,7 @@ impl EventLoopBuilder { /// Allows deeper customization of the window, if needed. pub fn window(mut self, window_fn: WindowFn) -> Self where - WindowFn: FnOnce(WindowBuilder) -> WindowBuilder, + WindowFn: FnOnce(WindowAttributes) -> WindowAttributes, { self.window = window_fn(self.window); self @@ -464,75 +464,76 @@ impl EventLoopBuilder { impl EventLoopBuilder { /// Builds a new `EventLoop`. pub fn build(mut self) -> Result { - // Create an operating system window via Winit - let window = self.window; - - #[cfg(not(target_os = "macos"))] - let window = window.with_visible(false); - - let window = window.build(&self.event_loop).map_err(|err| { - warn!("{err}"); - - DriverError::Unsupported - })?; - let (width, height) = { - let inner_size = window.inner_size(); - (inner_size.width, inner_size.height) - }; - self.swapchain_info = self.swapchain_info.width(width).height(height); - - // Load the GPU driver (thin Vulkan device and swapchain smart pointers) - let device_info = self.device_info.build(); - let device = Arc::new(Device::create_display_window(device_info, &window)?); - - // TODO: Select a better index - let queue_family_index = 0; - - // Create a display that is cached using the given pool implementation - let pool = self - .resolver_pool - .unwrap_or_else(|| Box::new(HashPool::new(&device))); - let display = Display::new(&device, pool, self.cmd_buf_count, queue_family_index)?; - - let surface = Surface::create(&device, &window)?; - let surface_formats = Surface::formats(&surface)?; - - if surface_formats.is_empty() { - warn!("invalid surface formats"); - - return Err(DriverError::Unsupported); - } - - for surface in &surface_formats { - debug!( - "surface: {:#?} ({:#?})", - surface.format, surface.color_space - ); - } - - let surface_format_fn = self - .surface_format_fn - .unwrap_or_else(|| Box::new(Surface::linear_or_default)); - let surface_format = surface_format_fn(&surface_formats); - let swapchain = Swapchain::new( - &device, - surface, - self.swapchain_info.surface(surface_format), - )?; - - info!( - "Window dimensions: {}x{} ({}x scale)", - width, - height, - window.scale_factor() as f32, - ); - - Ok(EventLoop { - device, - display, - event_loop: self.event_loop, - swapchain, - window, - }) + // // Create an operating system window via Winit + // let window = self.window; + + // #[cfg(not(target_os = "macos"))] + // let window = window.with_visible(false); + + // let window = window.build(&self.event_loop).map_err(|err| { + // warn!("{err}"); + + // DriverError::Unsupported + // })?; + // let (width, height) = { + // let inner_size = window.inner_size(); + // (inner_size.width, inner_size.height) + // }; + // self.swapchain_info = self.swapchain_info.width(width).height(height); + + // // Load the GPU driver (thin Vulkan device and swapchain smart pointers) + // let device_info = self.device_info.build(); + // let device = Arc::new(Device::create_display_window(device_info, &window)?); + + // // TODO: Select a better index + // let queue_family_index = 0; + + // // Create a display that is cached using the given pool implementation + // let pool = self + // .resolver_pool + // .unwrap_or_else(|| Box::new(HashPool::new(&device))); + // let display = Display::new(&device, pool, self.cmd_buf_count, queue_family_index)?; + + // let surface = Surface::create(&device, &window)?; + // let surface_formats = Surface::formats(&surface)?; + + // if surface_formats.is_empty() { + // warn!("invalid surface formats"); + + // return Err(DriverError::Unsupported); + // } + + // for surface in &surface_formats { + // debug!( + // "surface: {:#?} ({:#?})", + // surface.format, surface.color_space + // ); + // } + + // let surface_format_fn = self + // .surface_format_fn + // .unwrap_or_else(|| Box::new(Surface::linear_or_default)); + // let surface_format = surface_format_fn(&surface_formats); + // let swapchain = Swapchain::new( + // &device, + // surface, + // self.swapchain_info.surface(surface_format), + // )?; + + // info!( + // "Window dimensions: {}x{} ({}x scale)", + // width, + // height, + // window.scale_factor() as f32, + // ); + + // Ok(EventLoop { + // device, + // display, + // event_loop: self.event_loop, + // swapchain, + // window, + // }) + todo!() } } diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 3088db4..f44b17b 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -121,20 +121,33 @@ impl Attachment { #[derive(Clone, Copy, Debug)] pub struct ClearColorValue(pub [f32; 4]); +impl From<[f32; 3]> for ClearColorValue { + fn from(color: [f32; 3]) -> Self { + [color[0], color[1], color[2], 1.0].into() + } +} + impl From<[f32; 4]> for ClearColorValue { fn from(color: [f32; 4]) -> Self { Self(color) } } +impl From<[u8; 3]> for ClearColorValue { + fn from(color: [u8; 3]) -> Self { + [color[0], color[1], color[2], u8::MAX].into() + } +} + impl From<[u8; 4]> for ClearColorValue { fn from(color: [u8; 4]) -> Self { - Self([ + [ color[0] as f32 / u8::MAX as f32, color[1] as f32 / u8::MAX as f32, color[2] as f32 / u8::MAX as f32, color[3] as f32 / u8::MAX as f32, - ]) + ] + .into() } } diff --git a/src/graph/pass_ref.rs b/src/graph/pass_ref.rs index fbe9c61..e035b71 100644 --- a/src/graph/pass_ref.rs +++ b/src/graph/pass_ref.rs @@ -166,7 +166,7 @@ impl<'a> Acceleration<'a> { unsafe { #[derive(Default)] struct Tls { - geometries: Vec, + geometries: Vec>, max_primitive_counts: Vec, } @@ -183,7 +183,7 @@ impl<'a> Acceleration<'a> { tls.max_primitive_counts.push(info.max_primitive_count); } - let info = vk::AccelerationStructureBuildGeometryInfoKHR::builder() + let info = vk::AccelerationStructureBuildGeometryInfoKHR::default() .ty(build_info.ty) .flags(build_info.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) @@ -231,7 +231,7 @@ impl<'a> Acceleration<'a> { unsafe { #[derive(Default)] struct Tls { - geometries: Vec, + geometries: Vec>, max_primitive_counts: Vec, } @@ -248,7 +248,7 @@ impl<'a> Acceleration<'a> { tls.max_primitive_counts.push(info.max_primitive_count); } - let info = vk::AccelerationStructureBuildGeometryInfoKHR::builder() + let info = vk::AccelerationStructureBuildGeometryInfoKHR::default() .ty(build_info.ty) .flags(build_info.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) @@ -3975,8 +3975,6 @@ impl<'a> RayTrace<'a> { callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, indirect_device_address: vk::DeviceAddress, ) -> &Self { - use std::slice::from_ref; - unsafe { // Safely use unchecked because ray_trace_ext is checked during pipeline creation self.device @@ -3985,10 +3983,10 @@ impl<'a> RayTrace<'a> { .unwrap_unchecked() .cmd_trace_rays_indirect( self.cmd_buf, - from_ref(raygen_shader_binding_table), - from_ref(miss_shader_binding_table), - from_ref(hit_shader_binding_table), - from_ref(callable_shader_binding_table), + raygen_shader_binding_table, + miss_shader_binding_table, + hit_shader_binding_table, + callable_shader_binding_table, indirect_device_address, ) } diff --git a/src/graph/resolver.rs b/src/graph/resolver.rs index 69c5647..2a05853 100644 --- a/src/graph/resolver.rs +++ b/src/graph/resolver.rs @@ -613,7 +613,7 @@ impl Resolver { unsafe { cmd_buf.device.cmd_begin_render_pass( **cmd_buf, - &vk::RenderPassBeginInfo::builder() + &vk::RenderPassBeginInfo::default() .render_pass(***render_pass) .framebuffer(framebuffer) .render_area(vk::Rect2D { @@ -628,7 +628,7 @@ impl Resolver { }) .clear_values(clear_values) .push_next( - &mut vk::RenderPassAttachmentBeginInfoKHR::builder() + &mut vk::RenderPassAttachmentBeginInfoKHR::default() .attachments(image_views), ), vk::SubpassContents::INLINE, @@ -2733,7 +2733,7 @@ impl Resolver { .device .begin_command_buffer( **cmd_buf, - &vk::CommandBufferBeginInfo::builder() + &vk::CommandBufferBeginInfo::default() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), ) .map_err(|_| DriverError::OutOfMemory)?; @@ -2754,7 +2754,7 @@ impl Resolver { .device .queue_submit( cmd_buf.device.queues[queue_family_index][queue_index], - from_ref(&vk::SubmitInfo::builder().command_buffers(from_ref(&cmd_buf))), + from_ref(&vk::SubmitInfo::default().command_buffers(from_ref(&cmd_buf))), cmd_buf.fence, ) .map_err(|_| DriverError::OutOfMemory)?; @@ -2785,277 +2785,276 @@ impl Resolver { pass: &Pass, physical_pass: &PhysicalPass, ) -> Result<(), DriverError> { - thread_local! { - static TLS: RefCell = Default::default(); - } - - struct IndexWrite { + struct IndexWrite<'a> { idx: usize, - write: vk::WriteDescriptorSet, + write: vk::WriteDescriptorSet<'a>, } #[derive(Default)] - struct Tls { - accel_struct_infos: Vec, - accel_struct_writes: Vec, + struct Tls<'a> { + accel_struct_infos: Vec>, + accel_struct_writes: Vec>, buffer_infos: Vec, - buffer_writes: Vec, - descriptors: Vec, + buffer_writes: Vec>, + descriptors: Vec>, image_infos: Vec, - image_writes: Vec, + image_writes: Vec>, } - TLS.with_borrow_mut(|tls| { - // Initialize TLS from a previous call - tls.accel_struct_infos.clear(); - tls.accel_struct_writes.clear(); - tls.buffer_infos.clear(); - tls.buffer_writes.clear(); - tls.descriptors.clear(); - tls.image_infos.clear(); - tls.image_writes.clear(); - - for (exec_idx, exec, pipeline) in pass - .execs - .iter() - .enumerate() - .filter_map(|(exec_idx, exec)| { - exec.pipeline - .as_ref() - .map(|pipeline| (exec_idx, exec, pipeline)) - }) - .filter(|(.., pipeline)| !pipeline.descriptor_info().layouts.is_empty()) - { - let descriptor_sets = &physical_pass.exec_descriptor_sets[&exec_idx]; + let mut tls = Tls::default(); + + for (exec_idx, exec, pipeline) in pass + .execs + .iter() + .enumerate() + .filter_map(|(exec_idx, exec)| { + exec.pipeline + .as_ref() + .map(|pipeline| (exec_idx, exec, pipeline)) + }) + .filter(|(.., pipeline)| !pipeline.descriptor_info().layouts.is_empty()) + { + let descriptor_sets = &physical_pass.exec_descriptor_sets[&exec_idx]; - // Write the manually bound things (access, read, and write functions) - for (descriptor, (node_idx, view_info)) in exec.bindings.iter() { - let (descriptor_set_idx, dst_binding, binding_offset) = descriptor.into_tuple(); - let (descriptor_info, _) = pipeline + // Write the manually bound things (access, read, and write functions) + for (descriptor, (node_idx, view_info)) in exec.bindings.iter() { + let (descriptor_set_idx, dst_binding, binding_offset) = descriptor.into_tuple(); + let (descriptor_info, _) = pipeline .descriptor_bindings() .get(&DescriptorBinding(descriptor_set_idx, dst_binding)) .unwrap_or_else(|| panic!("descriptor {descriptor_set_idx}.{dst_binding}[{binding_offset}] specified in recorded execution of pass \"{}\" was not discovered through shader reflection", &pass.name)); - let descriptor_type = descriptor_info.descriptor_type(); - let bound_node = &bindings[*node_idx]; - if let Some(image) = bound_node.as_driver_image() { - let view_info = view_info.as_ref().unwrap(); - let mut image_view_info = *view_info.as_image().unwrap(); - - // Handle default views which did not specify a particaular aspect - if image_view_info.aspect_mask.is_empty() { - image_view_info.aspect_mask = format_aspect_mask(image.info.fmt); - } + let descriptor_type = descriptor_info.descriptor_type(); + let bound_node = &bindings[*node_idx]; + if let Some(image) = bound_node.as_driver_image() { + let view_info = view_info.as_ref().unwrap(); + let mut image_view_info = *view_info.as_image().unwrap(); + + // Handle default views which did not specify a particaular aspect + if image_view_info.aspect_mask.is_empty() { + image_view_info.aspect_mask = format_aspect_mask(image.info.fmt); + } - let sampler = descriptor_info.sampler().map(|sampler| **sampler).unwrap_or_default(); - let image_view = Image::view(image, image_view_info)?; - let image_layout = match descriptor_type { - vk::DescriptorType::COMBINED_IMAGE_SAMPLER | vk::DescriptorType::SAMPLED_IMAGE => { - if image_view_info.aspect_mask.contains( - vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL, - ) { - vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL - } else { - vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL - } + let image_view = Image::view(image, image_view_info)?; + let image_layout = match descriptor_type { + vk::DescriptorType::COMBINED_IMAGE_SAMPLER + | vk::DescriptorType::SAMPLED_IMAGE => { + if image_view_info.aspect_mask.contains( + vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL, + ) { + vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL + } else { + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL } - vk::DescriptorType::STORAGE_IMAGE => vk::ImageLayout::GENERAL, - _ => unimplemented!("{descriptor_type:?}"), - }; - - if binding_offset == 0 { - tls.image_writes.push(IndexWrite { - idx: tls.image_infos.len(), - write: vk::WriteDescriptorSet { - dst_set: *descriptor_sets[descriptor_set_idx as usize], - dst_binding, - descriptor_type, - descriptor_count: 1, - ..Default::default() - }, - } - ); - } else { - tls.image_writes.last_mut().unwrap().write.descriptor_count += 1; } + vk::DescriptorType::STORAGE_IMAGE => vk::ImageLayout::GENERAL, + _ => unimplemented!("{descriptor_type:?}"), + }; - tls.image_infos.push(vk::DescriptorImageInfo { - image_layout, - image_view, - sampler, + if binding_offset == 0 { + tls.image_writes.push(IndexWrite { + idx: tls.image_infos.len(), + write: vk::WriteDescriptorSet { + dst_set: *descriptor_sets[descriptor_set_idx as usize], + dst_binding, + descriptor_type, + descriptor_count: 1, + ..Default::default() + }, }); - } else if let Some(buffer) = bound_node.as_driver_buffer() { - let view_info = view_info.as_ref().unwrap(); - let buffer_view_info = view_info.as_buffer().unwrap(); + } else { + tls.image_writes.last_mut().unwrap().write.descriptor_count += 1; + } - if binding_offset == 0 { - tls.buffer_writes.push(IndexWrite { - idx: tls.buffer_infos.len(), - write: vk::WriteDescriptorSet { - dst_set: *descriptor_sets[descriptor_set_idx as usize], - dst_binding, - descriptor_type, - descriptor_count: 1, - ..Default::default() - }, - } - ); - } else { - tls.buffer_writes.last_mut().unwrap().write.descriptor_count += 1; - } + tls.image_infos.push( + vk::DescriptorImageInfo::default() + .image_layout(image_layout) + .image_view(image_view), + ); + } else if let Some(buffer) = bound_node.as_driver_buffer() { + let view_info = view_info.as_ref().unwrap(); + let buffer_view_info = view_info.as_buffer().unwrap(); - tls.buffer_infos.push(vk::DescriptorBufferInfo { - buffer: **buffer, - offset: buffer_view_info.start, - range: buffer_view_info.end - buffer_view_info.start, + if binding_offset == 0 { + tls.buffer_writes.push(IndexWrite { + idx: tls.buffer_infos.len(), + write: vk::WriteDescriptorSet { + dst_set: *descriptor_sets[descriptor_set_idx as usize], + dst_binding, + descriptor_type, + descriptor_count: 1, + ..Default::default() + }, }); - } else if let Some(accel_struct) = bound_node.as_driver_acceleration_structure() { - if binding_offset == 0 { - tls.accel_struct_writes.push(IndexWrite { - idx: tls.accel_struct_infos.len(), - write: vk::WriteDescriptorSet { - dst_set: *descriptor_sets[descriptor_set_idx as usize], - dst_binding, - descriptor_type, - descriptor_count: 1, - ..Default::default() - }, - }); - } else { - tls.accel_struct_writes.last_mut().unwrap().write.descriptor_count += 1; - } + } else { + tls.buffer_writes.last_mut().unwrap().write.descriptor_count += 1; + } - tls.accel_struct_infos.push(vk::WriteDescriptorSetAccelerationStructureKHR::builder().acceleration_structures(std::slice::from_ref(accel_struct)).build()); + tls.buffer_infos.push( + vk::DescriptorBufferInfo::default() + .buffer(**buffer) + .offset(buffer_view_info.start) + .range(buffer_view_info.end - buffer_view_info.start), + ); + } else if let Some(accel_struct) = bound_node.as_driver_acceleration_structure() { + if binding_offset == 0 { + tls.accel_struct_writes.push(IndexWrite { + idx: tls.accel_struct_infos.len(), + write: vk::WriteDescriptorSet::default() + .dst_set(*descriptor_sets[descriptor_set_idx as usize]) + .dst_binding(dst_binding) + .descriptor_type(descriptor_type) + .descriptor_count(1), + }); } else { - unimplemented!(); + tls.accel_struct_writes + .last_mut() + .unwrap() + .write + .descriptor_count += 1; } + + tls.accel_struct_infos.push( + vk::WriteDescriptorSetAccelerationStructureKHR::default() + .acceleration_structures(std::slice::from_ref(accel_struct)), + ); + } else { + unimplemented!(); } + } - if let ExecutionPipeline::Graphic(pipeline) = pipeline { - for descriptor_binding @ DescriptorBinding(descriptor_set_idx, dst_binding) in pipeline.separate_samplers.iter().copied() { - tls.image_writes.push(IndexWrite { - idx: tls.image_infos.len(), - write: vk::WriteDescriptorSet { + if let ExecutionPipeline::Graphic(pipeline) = pipeline { + for descriptor_binding @ DescriptorBinding(descriptor_set_idx, dst_binding) in + pipeline.separate_samplers.iter().copied() + { + tls.image_writes.push(IndexWrite { + idx: tls.image_infos.len(), + write: vk::WriteDescriptorSet::default() + .dst_set(*descriptor_sets[descriptor_set_idx as usize]) + .dst_binding(dst_binding) + .descriptor_type(vk::DescriptorType::SAMPLER) + .descriptor_count(1), + }); + tls.image_infos.push(vk::DescriptorImageInfo::default()); + } + + // Write graphic render pass input attachments (they're automatic) + if exec_idx > 0 { + for ( + &DescriptorBinding(descriptor_set_idx, dst_binding), + (descriptor_info, _), + ) in &pipeline.descriptor_bindings + { + if let DescriptorInfo::InputAttachment(_, attachment_idx) = *descriptor_info + { + let is_random_access = exec.color_stores.contains_key(&attachment_idx) + || exec.color_resolves.contains_key(&attachment_idx); + let (attachment, write_exec) = pass.execs[0..exec_idx] + .iter() + .rev() + .find_map(|exec| { + exec.color_stores + .get(&attachment_idx) + .copied() + .map(|attachment| (attachment, exec)) + .or_else(|| { + exec.color_resolves.get(&attachment_idx).map( + |(resolved_attachment, _)| { + (*resolved_attachment, exec) + }, + ) + }) + }) + .expect("input attachment not written"); + let [_, late] = &write_exec.accesses[&attachment.target]; + let image_subresource = + late.subresource.as_ref().unwrap().unwrap_image(); + let image_binding = &bindings[attachment.target]; + let image = image_binding.as_driver_image().unwrap(); + let image_view_info = ImageViewInfo { + array_layer_count: image_subresource.array_layer_count, + aspect_mask: attachment.aspect_mask, + base_array_layer: image_subresource.base_array_layer, + base_mip_level: image_subresource.base_mip_level, + fmt: attachment.format, + mip_level_count: image_subresource.mip_level_count, + ty: image.info.ty, + }; + let image_view = Image::view(image, image_view_info)?; + + tls.image_writes.push(IndexWrite { + idx: tls.image_infos.len(), + write: vk::WriteDescriptorSet { dst_set: *descriptor_sets[descriptor_set_idx as usize], dst_binding, - descriptor_type: vk::DescriptorType::SAMPLER, + descriptor_type: vk::DescriptorType::INPUT_ATTACHMENT, descriptor_count: 1, ..Default::default() }, - } - ); - tls.image_infos.push(vk::DescriptorImageInfo { - image_layout: Default::default(), - image_view: Default::default(), - sampler: **pipeline.descriptor_bindings[&descriptor_binding].0.sampler().unwrap(), - }); - } - - // Write graphic render pass input attachments (they're automatic) - if exec_idx > 0 { - for (&DescriptorBinding(descriptor_set_idx, dst_binding), (descriptor_info, _)) in - &pipeline.descriptor_bindings - { - if let DescriptorInfo::InputAttachment(_, attachment_idx) = *descriptor_info { - let is_random_access = exec.color_stores.contains_key(&attachment_idx) - || exec.color_resolves.contains_key(&attachment_idx); - let (attachment, write_exec) = pass.execs[0..exec_idx] - .iter() - .rev() - .find_map(|exec| { - exec.color_stores.get(&attachment_idx).copied() - .map(|attachment| { - (attachment, exec) - }) - .or_else(|| { - exec.color_resolves.get(&attachment_idx) - .map( - |(resolved_attachment, _)| { - (*resolved_attachment, exec) - }, - ) - }) - }) - .expect("input attachment not written"); - let [_, late] = &write_exec.accesses[&attachment.target]; - let image_subresource = late.subresource.as_ref().unwrap().unwrap_image(); - let image_binding = &bindings[attachment.target]; - let image = image_binding.as_driver_image().unwrap(); - let image_view_info = ImageViewInfo { - array_layer_count: image_subresource.array_layer_count, - aspect_mask: attachment.aspect_mask, - base_array_layer: image_subresource.base_array_layer, - base_mip_level: image_subresource.base_mip_level, - fmt: attachment.format, - mip_level_count: image_subresource.mip_level_count, - ty: image.info.ty, - }; - let image_view = Image::view(image, image_view_info)?; - let sampler = descriptor_info.sampler().map(|sampler| **sampler).unwrap_or_else(vk::Sampler::null); - - tls.image_writes.push(IndexWrite { - idx: tls.image_infos.len(), - write: vk::WriteDescriptorSet { - dst_set: *descriptor_sets[descriptor_set_idx as usize], - dst_binding, - descriptor_type: vk::DescriptorType::INPUT_ATTACHMENT, - descriptor_count: 1, - ..Default::default() - }, - } - ); + }); - tls.image_infos.push(vk::DescriptorImageInfo { - image_layout: Self::attachment_layout( - attachment.aspect_mask, - is_random_access, - true, - ), - image_view, - sampler, - }); - } + tls.image_infos.push(vk::DescriptorImageInfo { + image_layout: Self::attachment_layout( + attachment.aspect_mask, + is_random_access, + true, + ), + image_view, + sampler: vk::Sampler::null(), + }); } } } } + } - // NOTE: We assign the below pointers after the above insertions so they remain stable! + // NOTE: We assign the below pointers after the above insertions so they remain stable! - tls.descriptors.extend(tls.accel_struct_writes.drain(..).map(|IndexWrite { idx, mut write }| unsafe { - write.p_next = tls.accel_struct_infos.as_ptr().add(idx) as *const _; - write - })); - tls.descriptors.extend(tls.buffer_writes.drain(..).map(|IndexWrite { idx, mut write }| unsafe { + tls.descriptors + .extend(tls.accel_struct_writes.drain(..).map( + |IndexWrite { idx, mut write }| unsafe { + write.p_next = tls.accel_struct_infos.as_ptr().add(idx) as *const _; + write + }, + )); + tls.descriptors.extend(tls.buffer_writes.drain(..).map( + |IndexWrite { idx, mut write }| unsafe { write.p_buffer_info = tls.buffer_infos.as_ptr().add(idx); write - })); - tls.descriptors.extend(tls.image_writes.drain(..).map(|IndexWrite { idx, mut write }| unsafe { + }, + )); + tls.descriptors.extend(tls.image_writes.drain(..).map( + |IndexWrite { idx, mut write }| unsafe { write.p_image_info = tls.image_infos.as_ptr().add(idx); write - })); + }, + )); - if !tls.descriptors.is_empty() { - trace!(" writing {} descriptors ({} buffers, {} images)", tls.descriptors.len(), tls.buffer_infos.len(), tls.image_infos.len()); + if !tls.descriptors.is_empty() { + trace!( + " writing {} descriptors ({} buffers, {} images)", + tls.descriptors.len(), + tls.buffer_infos.len(), + tls.image_infos.len() + ); - unsafe { - cmd_buf - .device - .update_descriptor_sets(tls.descriptors.as_slice(), &[]); - } + unsafe { + cmd_buf + .device + .update_descriptor_sets(tls.descriptors.as_slice(), &[]); } + } - Ok(()) - }) + Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index ab5962e..64f12d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -398,10 +398,10 @@ pub mod prelude { dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, event::{Event, MouseButton, WindowEvent}, keyboard::KeyCode, - monitor::{MonitorHandle, VideoMode}, + monitor::{MonitorHandle, VideoModeHandle}, window::{ BadIcon, CursorGrabMode, CursorIcon, Fullscreen, Icon, Theme, UserAttentionType, - Window, WindowBuilder, WindowId, + Window, WindowAttributes, WindowId, }, }, }; From 305bb9839b2e5af269f38c7730d43bd847a9cbfc Mon Sep 17 00:00:00 2001 From: John Wells Date: Mon, 3 Jun 2024 14:05:38 -0400 Subject: [PATCH 02/21] Create new window crate --- CHANGELOG.md | 11 + Cargo.toml | 6 +- contrib/screen-13-fx/Cargo.toml | 1 + contrib/screen-13-fx/src/image_loader.rs | 9 +- contrib/screen-13-fx/src/transition.rs | 1 + contrib/screen-13-window/Cargo.toml | 15 + .../screen-13-window/examples/hello_world.rs | 12 + .../screen-13-window/src}/frame.rs | 5 +- contrib/screen-13-window/src/lib.rs | 551 ++++++++++++++++++ examples/README.md | 2 +- examples/app.rs | 2 +- examples/bindless.rs | 16 +- examples/cpu_readback.rs | 2 +- examples/font_bmp.rs | 18 +- examples/hello_world.rs | 15 - examples/msaa.rs | 47 +- examples/triangle.rs | 20 +- examples/vsm_omni.rs | 25 +- src/display.rs | 5 +- src/driver/compute.rs | 11 +- src/driver/device.rs | 11 +- src/driver/graphic.rs | 5 +- src/driver/image.rs | 21 +- src/driver/mod.rs | 2 +- src/driver/physical_device.rs | 5 +- src/driver/ray_trace.rs | 4 +- src/driver/shader.rs | 3 +- src/driver/swapchain.rs | 3 +- src/event_loop.rs | 539 ----------------- src/graph/resolver.rs | 2 +- src/lib.rs | 128 ++-- 31 files changed, 776 insertions(+), 721 deletions(-) create mode 100644 contrib/screen-13-window/Cargo.toml create mode 100644 contrib/screen-13-window/examples/hello_world.rs rename {src => contrib/screen-13-window/src}/frame.rs (96%) create mode 100644 contrib/screen-13-window/src/lib.rs delete mode 100644 examples/hello_world.rs delete mode 100644 src/event_loop.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 220152a..7e6076d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Updated `ash` to v0.38 +- Updated `winit` to v0.30 (_and moved related functionality to new `screen-13-window` crate_) + +### Removed + +- `log` and `winit` are no longer exported by `use screen_13::prelude::*` + ## [0.11.3] - 2024-05-29 ### Added diff --git a/Cargo.toml b/Cargo.toml index 905af70..c3ad84f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "screen-13" -version = "0.11.3" +version = "0.12.0" authors = ["John Wells "] edition = "2021" license = "MIT OR Apache-2.0" @@ -32,7 +32,6 @@ profiling = "1.0" raw-window-handle = "0.6" spirq = "1.2" vk-sync = { git = "https://github.com/attackgoat/vk-sync-rs.git", rev = "19fc3f811cc1d38b2231cdb8840fddf271879ac1", package = "vk-sync-fork" } #version = "0.4.0", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs -winit = "0.30" [target.'cfg(target_os = "macos")'.dependencies] ash-molten = "0.17" @@ -55,9 +54,10 @@ puffin = "0.19" puffin_http = "0.16" rand = "0.8" reqwest = { version = "0.12", features = ["blocking"] } -#screen-13-fx = { path = "contrib/screen-13-fx" } +screen-13-fx = { path = "contrib/screen-13-fx" } #screen-13-imgui = { path = "contrib/screen-13-imgui" } #screen-13-egui = { path = "contrib/screen-13-egui" } +screen-13-window = { path = "contrib/screen-13-window" } tobj = "4.0" winit = "0.30" winit_input_helper = { git = "https://github.com/stefnotch/winit_input_helper.git", rev = "6e76a79d01ce836c01b9cdeaa98846a6f0955dc4" } #"0.16" diff --git a/contrib/screen-13-fx/Cargo.toml b/contrib/screen-13-fx/Cargo.toml index 8195392..e067d2a 100644 --- a/contrib/screen-13-fx/Cargo.toml +++ b/contrib/screen-13-fx/Cargo.toml @@ -27,6 +27,7 @@ bmfont = { version = "0.3", default-features = false } bytemuck = "1.14" parking_lot = "0.12" inline-spirv = "0.2" +log = "0.4" screen-13 = { path = "../.."} anyhow = "1.0" glam = "0.27" diff --git a/contrib/screen-13-fx/src/image_loader.rs b/contrib/screen-13-fx/src/image_loader.rs index b176c84..003fb1d 100644 --- a/contrib/screen-13-fx/src/image_loader.rs +++ b/contrib/screen-13-fx/src/image_loader.rs @@ -1,6 +1,11 @@ use { - super::BitmapFont, anyhow::Context, bmfont::BMFont, inline_spirv::include_spirv, - screen_13::prelude::*, std::sync::Arc, + super::BitmapFont, + anyhow::Context, + bmfont::BMFont, + inline_spirv::include_spirv, + log::{info, warn}, + screen_13::prelude::*, + std::sync::Arc, }; fn align_up_u32(val: u32, atom: u32) -> u32 { diff --git a/contrib/screen-13-fx/src/transition.rs b/contrib/screen-13-fx/src/transition.rs index 9031c87..c4427bc 100644 --- a/contrib/screen-13-fx/src/transition.rs +++ b/contrib/screen-13-fx/src/transition.rs @@ -5,6 +5,7 @@ use { inline_spirv::include_spirv, screen_13::prelude::*, + log::trace, std::{collections::HashMap, sync::Arc}, }; diff --git a/contrib/screen-13-window/Cargo.toml b/contrib/screen-13-window/Cargo.toml new file mode 100644 index 0000000..4fd49a3 --- /dev/null +++ b/contrib/screen-13-window/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "screen-13-window" +version = "0.1.0" +authors = ["John Wells "] +edition = "2021" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[dependencies] +log = "0.4" +screen-13 = { path = "../.." } +winit = "0.30" + +[dev-dependencies] +pretty_env_logger = "0.5" diff --git a/contrib/screen-13-window/examples/hello_world.rs b/contrib/screen-13-window/examples/hello_world.rs new file mode 100644 index 0000000..93abe25 --- /dev/null +++ b/contrib/screen-13-window/examples/hello_world.rs @@ -0,0 +1,12 @@ +use screen_13_window::{Window, WindowError}; + +/// This example requires a color graphics adapter. +fn main() -> Result<(), WindowError> { + pretty_env_logger::init(); + + Window::new()?.run(|frame| { + frame + .render_graph + .clear_color_image_value(frame.swapchain_image, [100u8, 149, 237]); + }) +} diff --git a/src/frame.rs b/contrib/screen-13-window/src/frame.rs similarity index 96% rename from src/frame.rs rename to contrib/screen-13-window/src/frame.rs index 2a25de9..7773181 100644 --- a/src/frame.rs +++ b/contrib/screen-13-window/src/frame.rs @@ -1,5 +1,5 @@ use { - crate::{ + screen_13::{ driver::device::Device, graph::{node::SwapchainImageNode, RenderGraph}, }, @@ -26,9 +26,6 @@ pub struct FrameContext<'a> { /// The device this frame belongs to. pub device: &'a Arc, - /// The elapsed seconds since the previous frame. - pub dt: f32, - /// A slice of events that have occurred since the previous frame. pub events: &'a [Event<()>], diff --git a/contrib/screen-13-window/src/lib.rs b/contrib/screen-13-window/src/lib.rs new file mode 100644 index 0000000..d695eae --- /dev/null +++ b/contrib/screen-13-window/src/lib.rs @@ -0,0 +1,551 @@ +mod frame; + +use { + self::frame::FrameContext, + log::{info, warn}, + screen_13::{ + driver::{ + ash::vk, + device::{Device, DeviceInfo}, + surface::Surface, + swapchain::{Swapchain, SwapchainInfo}, + DriverError, + }, + graph::RenderGraph, + pool::hash::HashPool, + Display, + }, + std::{error, fmt, sync::Arc}, + winit::{ + application::ApplicationHandler, + error::EventLoopError, + event::{DeviceEvent, DeviceId, Event, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoop}, + monitor::MonitorHandle, + window::{WindowAttributes, WindowId}, + }, +}; + +/// Describes a screen mode for display. +#[derive(Clone, Copy, Debug)] +pub enum FullscreenMode { + /// A display mode which retains other operating system windows behind the current window. + Borderless, + + /// Seems to be the only way for stutter-free rendering on Nvidia + Win10. + Exclusive, +} + +// #[derive(Debug)] +pub struct Window { + data: WindowData, + pub device: Arc, + event_loop: EventLoop<()>, +} + +impl Window { + pub fn new() -> Result { + Self::builder()?.build() + } + + pub fn builder() -> Result { + WindowBuilder::new() + } + + pub fn run(self, draw_fn: F) -> Result<(), WindowError> + where + F: FnMut(FrameContext), + { + struct Application { + active_window: Option, + data: WindowData, + device: Arc, + draw_fn: F, + error: Option, + primary_monitor: Option, + } + + impl Application { + fn create_display_swapchain( + &mut self, + window: &winit::window::Window, + ) -> Result<(Display, Swapchain), DriverError> { + let display_pool = Box::new(HashPool::new(&self.device)); + let display = Display::new(&self.device, display_pool, self.data.cmd_buf_count, 0)?; + let surface = Surface::create(&self.device, &window)?; + let surface_formats = Surface::formats(&surface)?; + let surface_format = self + .data + .surface_format_fn + .as_ref() + .map(|f| f(&surface_formats)) + .unwrap_or_else(|| Surface::linear_or_default(&surface_formats)); + let window_size = window.inner_size(); + + let mut swapchain_info = + SwapchainInfo::new(window_size.width, window_size.height, surface_format) + .to_builder(); + + if let Some(image_count) = self.data.image_count { + swapchain_info = swapchain_info.desired_image_count(image_count); + } + + if let Some(v_sync) = self.data.v_sync { + swapchain_info = swapchain_info.sync_display(v_sync); + } + + let swapchain = Swapchain::new(&self.device, surface, swapchain_info)?; + + info!("Created swapchain"); + + Ok((display, swapchain)) + } + + fn window_mode_attributes( + &self, + attributes: WindowAttributes, + window_mode_override: Option>, + ) -> WindowAttributes { + match window_mode_override { + Some(Some(mode)) => { + let inner_size; + let attributes = attributes + .with_decorations(false) + .with_maximized(true) + .with_fullscreen(Some(match mode { + FullscreenMode::Borderless => { + info!("Using borderless fullscreen"); + + inner_size = None; + + winit::window::Fullscreen::Borderless(None) + } + FullscreenMode::Exclusive => { + if let Some(video_mode) = + self.primary_monitor.as_ref().and_then(|monitor| { + let monitor_size = monitor.size(); + monitor.video_modes().find(|mode| { + let mode_size = mode.size(); + + // Don't pick a mode which has greater resolution than the monitor is + // currently using: it causes a panic on x11 in winit + mode_size.height <= monitor_size.height + && mode_size.width <= monitor_size.width + }) + }) + { + info!( + "Using {}x{} {}bpp @ {}hz exclusive fullscreen", + video_mode.size().width, + video_mode.size().height, + video_mode.bit_depth(), + video_mode.refresh_rate_millihertz() / 1_000 + ); + + inner_size = Some(video_mode.size()); + + winit::window::Fullscreen::Exclusive(video_mode) + } else { + warn!("Using borderless fullscreen"); + + inner_size = None; + + winit::window::Fullscreen::Borderless(None) + } + } + })); + + if let Some(inner_size) = inner_size + .or_else(|| self.primary_monitor.as_ref().map(|monitor| monitor.size())) + { + attributes.with_inner_size(inner_size) + } else { + attributes + } + } + Some(None) => attributes.with_fullscreen(None), + _ => attributes, + } + } + } + + impl ApplicationHandler for Application + where + F: FnMut(FrameContext), + { + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + if let Some(ActiveWindow { window, .. }) = self.active_window.as_ref() { + window.request_redraw(); + + info!("Redraw requested"); + } + } + + fn device_event( + &mut self, + _event_loop: &ActiveEventLoop, + device_id: DeviceId, + event: DeviceEvent, + ) { + if let Some(ActiveWindow { events, .. }) = self.active_window.as_mut() { + events.push(Event::DeviceEvent { device_id, event }); + } + } + + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + info!("Resumed"); + + self.data.attributes = self.window_mode_attributes( + self.data.attributes.clone(), + self.data.window_mode_override, + ); + + let window = match event_loop.create_window(self.data.attributes.clone()) { + Err(err) => { + warn!("Unable to create window: {err}"); + + self.error = Some(EventLoopError::Os(err).into()); + event_loop.exit(); + + return; + } + Ok(res) => res, + }; + let (display, swapchain) = match self.create_display_swapchain(&window) { + Err(err) => { + warn!("Unable to create swapchain: {err}"); + + self.error = Some(err.into()); + event_loop.exit(); + + return; + } + Ok(res) => res, + }; + + let mut active_window = ActiveWindow { + display, + events: vec![], + swapchain, + window, + }; + + if !active_window.draw(&self.device, &mut self.draw_fn) { + event_loop.exit(); + } + + self.active_window = Some(active_window); + } + + fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: ()) { + if let Some(ActiveWindow { events, .. }) = self.active_window.as_mut() { + events.push(Event::UserEvent(event)); + } + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { + info!("Window event: {event:?}"); + + if let Some(active_window) = self.active_window.as_mut() { + match &event { + WindowEvent::CloseRequested => { + info!("Close requested"); + + event_loop.exit(); + } + WindowEvent::RedrawRequested => { + if !active_window.draw(&self.device, &mut self.draw_fn) { + event_loop.exit(); + } + } + WindowEvent::Resized(size) => { + let mut swapchain_info = active_window.swapchain.info(); + swapchain_info.width = size.width; + swapchain_info.height = size.height; + active_window.swapchain.set_info(swapchain_info); + } + _ => (), + } + + active_window + .events + .push(Event::WindowEvent { window_id, event }); + } + } + } + + struct ActiveWindow { + display: Display, + events: Vec>, + swapchain: Swapchain, + window: winit::window::Window, + } + + impl ActiveWindow { + fn draw(&mut self, device: &Arc, mut f: impl FnMut(FrameContext)) -> bool { + if let Ok(swapchain_image) = self.swapchain.acquire_next_image() { + self.window.pre_present_notify(); + + let mut render_graph = RenderGraph::new(); + let swapchain_image = render_graph.bind_node(swapchain_image); + let swapchain_info = self.swapchain.info(); + + let mut will_exit = false; + + info!("Drawing"); + + f(FrameContext { + device, + events: &self.events, + height: swapchain_info.height, + render_graph: &mut render_graph, + swapchain_image, + width: swapchain_info.width, + will_exit: &mut will_exit, + window: &self.window, + }); + + self.events.clear(); + + if will_exit { + info!("Exit requested"); + + return false; + } + + match self.display.resolve_image(render_graph, swapchain_image) { + Err(err) => { + warn!("Unable to resolve swapchain image: {err}"); + + return false; + } + Ok(swapchain_image) => self.swapchain.present_image(swapchain_image, 0, 0), + } + } else { + warn!("Failed to acquire swapchain image"); + } + + self.window.request_redraw(); + + true + } + } + + let mut app = Application { + active_window: None, + data: self.data, + device: self.device, + draw_fn, + error: None, + primary_monitor: None, + }; + + self.event_loop.run_app(&mut app)?; + + info!("Window closed"); + + if let Some(err) = app.error { + Err(err) + } else { + Ok(()) + } + } +} + +pub struct WindowBuilder { + attributes: WindowAttributes, + cmd_buf_count: usize, + device_info: DeviceInfo, + image_count: Option, + surface_format_fn: Option vk::SurfaceFormatKHR>>, + v_sync: Option, + window_mode_override: Option>, +} + +impl WindowBuilder { + pub fn new() -> Result { + Ok(Self { + attributes: Default::default(), + cmd_buf_count: 5, + device_info: Default::default(), + image_count: None, + surface_format_fn: None, + v_sync: None, + window_mode_override: None, + }) + } + + pub fn build(self) -> Result { + let event_loop = EventLoop::new()?; + let device = Arc::new(Device::create_display(self.device_info, &event_loop)?); + + Ok(Window { + data: WindowData { + attributes: self.attributes, + cmd_buf_count: self.cmd_buf_count, + image_count: self.image_count, + surface_format_fn: self.surface_format_fn, + v_sync: self.v_sync, + window_mode_override: self.window_mode_override, + }, + device, + event_loop, + }) + } + + /// Specifies the number of in-flight command buffers, which should be greater + /// than or equal to the desired swapchain image count. + /// + /// More command buffers mean less time waiting for previously submitted frames to complete, but + /// more memory in use. + /// + /// Generally a value of one or two greater than desired image count produces the smoothest + /// animation. + pub fn command_buffer_count(mut self, count: usize) -> Self { + self.cmd_buf_count = count; + self + } + + /// Enables Vulkan graphics debugging layers. + /// + /// _NOTE:_ Any valdation warnings or errors will cause the current thread to park itself after + /// describing the error using the `log` crate. This makes it easy to attach a debugger and see + /// what is causing the issue directly. + /// + /// ## Platform-specific + /// + /// **macOS:** Has no effect. + pub fn debug(mut self, enabled: bool) -> Self { + self.device_info.debug = enabled; + self + } + + /// A function to select the desired swapchain surface image format. + /// + /// By default linear color space will be selected unless it is not available. + pub fn desired_surface_format(mut self, f: F) -> Self + where + F: 'static + Fn(&[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR, + { + self.surface_format_fn = Some(Box::new(f)); + self + } + + /// The desired, but not guaranteed, number of images that will be in the created swapchain. + /// + /// More images introduces more display lag, but smoother animation. + pub fn desired_image_count(mut self, count: u32) -> Self { + self.image_count = Some(count); + self + } + + /// Sets up fullscreen mode. In addition, decorations are set to `false` and maximized is set to + /// `true`. + /// + /// # Note + /// + /// There are additional options offered by `winit` which can be accessed using the `window` + /// function. + pub fn fullscreen_mode(mut self, mode: FullscreenMode) -> Self { + self.window_mode_override = Some(Some(mode)); + self + } + + /// When `true` specifies that the presentation engine does not wait for a vertical blanking + /// period to update the current image, meaning this mode may result in visible tearing. + /// + /// # Note + /// + /// Applies only to exlcusive fullscreen mode. + pub fn v_sync(mut self, enabled: bool) -> Self { + self.v_sync = Some(enabled); + self + } + + /// Allows deeper customization of the window, if needed. + pub fn window(mut self, f: WindowFn) -> Self + where + WindowFn: FnOnce(WindowAttributes) -> WindowAttributes, + { + self.attributes = f(self.attributes); + self + } + + /// Sets up "windowed" mode, which is the opposite of fullscreen. + /// + /// # Note + /// + /// There are additional options offered by `winit` which can be accessed using the `window` + /// function. + pub fn window_mode(mut self) -> Self { + self.window_mode_override = Some(None); + self + } +} + +impl fmt::Debug for WindowBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WindowBuilder") + .field("attributes", &self.attributes) + .field("cmd_buffer_count", &self.cmd_buf_count) + .field("device_info", &self.device_info) + .field("image_count", &self.image_count) + .field( + "surface_format_fn", + &self.surface_format_fn.as_ref().map(|_| ()), + ) + .field("v_sync", &self.v_sync) + .field("window_mode_override", &self.window_mode_override) + .finish() + } +} + +struct WindowData { + attributes: WindowAttributes, + cmd_buf_count: usize, + image_count: Option, + surface_format_fn: Option vk::SurfaceFormatKHR>>, + v_sync: Option, + window_mode_override: Option>, +} + +#[derive(Debug)] +pub enum WindowError { + Driver(DriverError), + EventLoop(EventLoopError), +} + +impl error::Error for WindowError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match self { + Self::Driver(err) => err, + Self::EventLoop(err) => err, + }) + } +} + +impl fmt::Display for WindowError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Driver(err) => err.fmt(f), + Self::EventLoop(err) => err.fmt(f), + } + } +} + +impl From for WindowError { + fn from(err: DriverError) -> Self { + Self::Driver(err) + } +} + +impl From for WindowError { + fn from(err: EventLoopError) -> Self { + Self::EventLoop(err) + } +} diff --git a/examples/README.md b/examples/README.md index 1807424..a6c8a95 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,7 +15,7 @@ Example | Instructions | Preview [cpu_readback.rs](cpu_readback.rs) |
cargo run --example cpu_readback
| _See console output_ [debugger.rs](debugger.rs) |
cargo run --example debugger
| _See console output_ [subgroup_ops.rs](subgroup_ops.rs) |
cargo run --example subgroup_ops
| _See console output_ -[hello_world.rs](hello_world.rs) |
cargo run --example hello_world
| Preview +[hello_world.rs](../contrib/screen-13-window/examples/hello_world.rs) |
cargo run --manifest-path contrib/screen-13-window/Cargo.toml --example hello_world
| Preview [triangle.rs](triangle.rs) |
cargo run --example triangle
| Preview [vertex_layout.rs](vertex_layout.rs) |
cargo run --example vertex_layout
| Preview [bindless.rs](bindless.rs) |
cargo run --example bindless
| Preview diff --git a/examples/app.rs b/examples/app.rs index 010b210..98cbbdb 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -34,7 +34,7 @@ impl ApplicationHandler for Application { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window_attributes = Window::default_attributes().with_title("Screen 13"); let window = event_loop.create_window(window_attributes).unwrap(); - let device = Arc::new(Device::create_presentable(DeviceInfo::default(), &window).unwrap()); + let device = Arc::new(Device::create_display(DeviceInfo::default(), &window).unwrap()); let display_pool = Box::new(HashPool::new(&device)); let display = Display::new(&device, display_pool, 3, 0).unwrap(); let surface = Surface::create(&device, &window).unwrap(); diff --git a/examples/bindless.rs b/examples/bindless.rs index 8af4b83..1aaa17a 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -4,21 +4,23 @@ use { bytemuck::{cast_slice, Pod, Zeroable}, inline_spirv::inline_spirv, screen_13::prelude::*, + screen_13_window::{Window, WindowError}, std::sync::Arc, + winit::dpi::LogicalSize, }; -fn main() -> Result<(), DisplayError> { +fn main() -> Result<(), WindowError> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new() + let window = Window::builder()? .window(|window| window.with_inner_size(LogicalSize::new(512, 512))) .build()?; - let images = create_images(&event_loop.device)?; - let pipeline = create_graphic_pipeline(&event_loop.device)?; - let draw_buf = create_indirect_buffer(&event_loop.device)?; + let images = create_images(&window.device)?; + let pipeline = create_graphic_pipeline(&window.device)?; + let draw_buf = create_indirect_buffer(&window.device)?; - event_loop.run(|frame| { + window.run(|frame| { let draw_buf_node = frame.render_graph.bind_node(&draw_buf); let mut pass = frame @@ -40,7 +42,7 @@ fn main() -> Result<(), DisplayError> { }) } -fn create_images(device: &Arc) -> Result>, DisplayError> { +fn create_images(device: &Arc) -> Result>, DriverError> { let mut textures = Vec::with_capacity(64); let (b, a) = (0.0, 1.0); diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index d454400..0a6bc7d 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -8,7 +8,7 @@ use { fn main() -> Result<(), DriverError> { pretty_env_logger::init(); - // For this example we directly create a device, but the same thing works using an event loop + // For this example we create a headless device, but the same thing works using a window let device = Arc::new(Device::create_headless(DeviceInfo::default())?); let mut render_graph = RenderGraph::new(); diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index ecfc4f4..f1e4db9 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -6,6 +6,7 @@ use { inline_spirv::inline_spirv, screen_13::prelude::*, screen_13_fx::*, + screen_13_window::Window, std::{io::Cursor, sync::Arc, time::Instant}, }; @@ -14,10 +15,10 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); // Standard Screen 13 stuff - let event_loop = EventLoop::new().build()?; - let display = GraphicPresenter::new(&event_loop.device)?; - let mut image_loader = ImageLoader::new(&event_loop.device)?; - let mut pool = HashPool::new(&event_loop.device); + let window = Window::new()?; + let display = GraphicPresenter::new(&window.device)?; + let mut image_loader = ImageLoader::new(&window.device)?; + let mut pool = HashPool::new(&window.device); // Load a bitmapped font let small_10px_font = BMFont::new( @@ -43,10 +44,9 @@ fn main() -> anyhow::Result<()> { )?; // A neato smoke effect just for fun - let Vulkan11Properties { subgroup_size, .. } = - event_loop.device.physical_device.properties_v1_1; + let Vulkan11Properties { subgroup_size, .. } = window.device.physical_device.properties_v1_1; let start_time = Instant::now(); - let smoke_pipeline = Arc::new(ComputePipeline::create(&event_loop.device, + let smoke_pipeline = Arc::new(ComputePipeline::create(&window.device, ComputePipelineInfo::default(), Shader::new_compute( inline_spirv!( @@ -118,7 +118,7 @@ fn main() -> anyhow::Result<()> { }), )?); - event_loop.run(|frame| { + window.run(|frame| { let image_node = frame.render_graph.bind_node( pool.lease(ImageInfo::image_2d( 320, @@ -156,7 +156,7 @@ fn main() -> anyhow::Result<()> { let x = 320f32 * 0.5 / scale - width as f32 * 0.5; let y = 200f32 * 0.5 / scale - height as f32 * 0.5; let color = [196, 172, 230u8]; - small_10px_font.print_scale(frame.render_graph, image_node, x, y, color, text, scale); + //small_10px_font.print_scale(frame.render_graph, image_node, x, y, color, text, scale); display.present_image(frame.render_graph, image_node, frame.swapchain_image); })?; diff --git a/examples/hello_world.rs b/examples/hello_world.rs deleted file mode 100644 index 1416812..0000000 --- a/examples/hello_world.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod profile_with_puffin; - -use screen_13::{DisplayError, EventLoop}; - -/// This example requires a color graphics adapter. -fn main() -> Result<(), DisplayError> { - pretty_env_logger::init(); - profile_with_puffin::init(); - - EventLoop::new().build()?.run(|frame| { - frame - .render_graph - .clear_color_image_value(frame.swapchain_image, [100u8, 149, 237, 255]); - }) -} diff --git a/examples/msaa.rs b/examples/msaa.rs index 006c0a1..48123ec 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -5,7 +5,9 @@ use { glam::{Mat4, Vec3}, inline_spirv::inline_spirv, screen_13::prelude::*, + screen_13_window::Window, std::{mem::size_of, sync::Arc}, + winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; @@ -22,25 +24,39 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let mut input = WinitInputHelper::default(); - let event_loop = EventLoop::new().build()?; - let depth_format = best_depth_format(&event_loop.device); - let sample_count = max_supported_sample_count(&event_loop.device); - let mesh_msaa_pipeline = create_mesh_pipeline(&event_loop.device, sample_count)?; - let mesh_noaa_pipeline = create_mesh_pipeline(&event_loop.device, SampleCount::Type1)?; - let cube_mesh = load_cube_mesh(&event_loop.device)?; - let mut pool = FifoPool::new(&event_loop.device); + let window = Window::new()?; + let depth_format = best_depth_format(&window.device); + let sample_count = max_supported_sample_count(&window.device); + let mesh_msaa_pipeline = create_mesh_pipeline(&window.device, sample_count)?; + let mesh_noaa_pipeline = create_mesh_pipeline(&window.device, SampleCount::Type1)?; + let cube_mesh = load_cube_mesh(&window.device)?; + let mut pool = FifoPool::new(&window.device); let mut angle = 0f32; - event_loop.run(|frame| { - for event in frame.events { - input.update(event); - } + window.run(|frame| { + input.step_with_window_events( + &frame + .events + .iter() + .filter_map(|event| { + if let Event::WindowEvent { event, .. } = event { + Some(event.clone()) + } else { + None + } + }) + .collect::>(), + ); // Hold the tab key to render in non-multisample mode let will_render_msaa = !input.key_held(KeyCode::Tab) && sample_count != SampleCount::Type1; - angle += frame.dt * 0.1; + angle += input + .delta_time() + .map(|dt| dt.as_secs_f32()) + .unwrap_or(0.016) + * 0.1; let world_transform = Mat4::from_rotation_x(angle) * Mat4::from_rotation_y(angle * 0.61) * Mat4::from_rotation_z(angle * 0.22); @@ -63,6 +79,7 @@ fn main() -> anyhow::Result<()> { 10.0, ), light_dir: Vec3::Y, + _pad: 0, }), ); @@ -309,6 +326,7 @@ fn create_mesh_pipeline( mat4 view; mat4 projection; vec3 light_dir; + uint pad; } scene; layout(location = 0) in vec3 position; @@ -369,11 +387,10 @@ struct Model { } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, NoUninit)] struct SceneUniformBuffer { view: Mat4, projection: Mat4, light_dir: Vec3, + _pad: u32, } - -unsafe impl NoUninit for SceneUniformBuffer {} diff --git a/examples/triangle.rs b/examples/triangle.rs index ffba89b..6b5ef47 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -1,15 +1,21 @@ mod profile_with_puffin; -use {bytemuck::cast_slice, inline_spirv::inline_spirv, screen_13::prelude::*, std::sync::Arc}; +use { + bytemuck::cast_slice, + inline_spirv::inline_spirv, + screen_13::prelude::*, + screen_13_window::{Window, WindowError}, + std::sync::Arc, +}; // A Vulkan triangle using a graphic pipeline, vertex/fragment shaders, and index/vertex buffers. -fn main() -> Result<(), DisplayError> { +fn main() -> Result<(), WindowError> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new().build()?; + let window = Window::new()?; let triangle_pipeline = Arc::new(GraphicPipeline::create( - &event_loop.device, + &window.device, GraphicPipelineInfo::default(), [ Shader::new_vertex( @@ -52,13 +58,13 @@ fn main() -> Result<(), DisplayError> { )?); let index_buf = Arc::new(Buffer::create_from_slice( - &event_loop.device, + &window.device, vk::BufferUsageFlags::INDEX_BUFFER, cast_slice(&[0u16, 1, 2]), )?); let vertex_buf = Arc::new(Buffer::create_from_slice( - &event_loop.device, + &window.device, vk::BufferUsageFlags::VERTEX_BUFFER, cast_slice(&[ 1.0f32, 1.0, 0.0, // v1 @@ -70,7 +76,7 @@ fn main() -> Result<(), DisplayError> { ]), )?); - event_loop.run(|frame| { + window.run(|frame| { let index_node = frame.render_graph.bind_node(&index_buf); let vertex_node = frame.render_graph.bind_node(&vertex_buf); diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 40e1131..07dfbdd 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -6,6 +6,7 @@ use { inline_spirv::inline_spirv, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, screen_13::prelude::*, + screen_13_window::Window, std::{ env::current_exe, fs::{metadata, write}, @@ -13,6 +14,7 @@ use { sync::Arc, }, tobj::{load_obj, GPU_LOAD_OPTIONS}, + winit::{dpi::LogicalSize, event::Event, keyboard::KeyCode, window::Fullscreen}, winit_input_helper::WinitInputHelper, }; @@ -44,7 +46,7 @@ fn main() -> anyhow::Result<()> { let cube_transform = Mat4::from_scale(Vec3::splat(10.0)).to_cols_array(); let mut input = WinitInputHelper::default(); - let event_loop = EventLoop::new() + let event_loop = Window::builder()? .window(|window| window.with_inner_size(LogicalSize::new(800, 600))) .build()?; @@ -97,13 +99,26 @@ fn main() -> anyhow::Result<()> { let mut elapsed = 0.0; event_loop.run(|frame| { - for event in frame.events { - input.update(event); - } + input.step_with_window_events( + &frame + .events + .iter() + .filter_map(|event| { + if let Event::WindowEvent { event, .. } = event { + Some(event.clone()) + } else { + None + } + }) + .collect::>(), + ); // Hold spacebar to stop the light if !input.key_held(KeyCode::Space) { - elapsed += frame.dt; + elapsed += input + .delta_time() + .map(|dt| dt.as_secs_f32()) + .unwrap_or(0.016); } // Hit F11 to enable borderless fullscreen diff --git a/src/display.rs b/src/display.rs index fcc77c0..a760e56 100644 --- a/src/display.rs +++ b/src/display.rs @@ -152,10 +152,7 @@ impl Display { } #[profiling::function] - unsafe fn submit( - cmd_buf: &CommandBuffer, - submit_info: vk::SubmitInfo<'_>, - ) -> Result<(), ()> { + unsafe fn submit(cmd_buf: &CommandBuffer, submit_info: vk::SubmitInfo<'_>) -> Result<(), ()> { use std::slice::from_ref; cmd_buf diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 2af0cac..cfecf19 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -99,7 +99,10 @@ impl ComputePipeline { unsafe { let shader_module = device - .create_shader_module(&vk::ShaderModuleCreateInfo::default().code(align_spriv(&shader.spirv)?), None) + .create_shader_module( + &vk::ShaderModuleCreateInfo::default().code(align_spriv(&shader.spirv)?), + None, + ) .map_err(|err| { warn!("{err}"); @@ -139,11 +142,7 @@ impl ComputePipeline { .stage(stage_create_info) .layout(layout); let pipeline = device - .create_compute_pipelines( - vk::PipelineCache::null(), - from_ref(&pipeline_info), - None, - ) + .create_compute_pipelines(vk::PipelineCache::null(), from_ref(&pipeline_info), None) .map_err(|(_, err)| { warn!("{err}"); diff --git a/src/driver/device.rs b/src/driver/device.rs index 3fa2976..4efa4d8 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -202,7 +202,7 @@ impl Device { /// Constructs a new device using the given configuration. #[profiling::function] - pub fn create_presentable( + pub fn create_display( info: impl Into, display_handle: &impl HasDisplayHandle, ) -> Result { @@ -590,6 +590,15 @@ impl DeviceInfo { } } +impl Debug for DeviceInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DeviceInfo") + .field("debug", &self.debug) + .field("select_physical_device", &"fn") + .finish() + } +} + impl Default for DeviceInfo { fn default() -> Self { Self { diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index b58f3b5..a2ee84e 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -452,10 +452,7 @@ impl GraphicPipeline { ); let mut descriptor_bindings = Shader::merge_descriptor_bindings( - shaders - .iter() - .map(|shader| shader.descriptor_bindings()) - + shaders.iter().map(|shader| shader.descriptor_bindings()), ); for (descriptor_info, _) in descriptor_bindings.values_mut() { if descriptor_info.binding_count() == 0 { diff --git a/src/driver/image.rs b/src/driver/image.rs index 164576e..b29bc21 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -791,16 +791,17 @@ impl ImageView { ) -> Result { let info = info.into(); let device = Arc::clone(device); - let create_info = vk::ImageViewCreateInfo::default().view_type(info.ty.into_vk()). - format(info.fmt). - components(vk::ComponentMapping { + let create_info = vk::ImageViewCreateInfo::default() + .view_type(info.ty.into_vk()) + .format(info.fmt) + .components(vk::ComponentMapping { r: vk::ComponentSwizzle::R, g: vk::ComponentSwizzle::G, b: vk::ComponentSwizzle::B, a: vk::ComponentSwizzle::A, - }). - image(image). - subresource_range(vk::ImageSubresourceRange { + }) + .image(image) + .subresource_range(vk::ImageSubresourceRange { aspect_mask: info.aspect_mask, base_array_layer: info.base_array_layer, base_mip_level: info.base_mip_level, @@ -996,10 +997,10 @@ pub enum SampleCount { impl From for vk::SampleCountFlags { fn from(sample_count: SampleCount) -> Self { match sample_count { - SampleCount::Type1 => Self::TYPE_1, - SampleCount::Type2 => Self::TYPE_2, - SampleCount::Type4 => Self::TYPE_4, - SampleCount::Type8 => Self::TYPE_8, + SampleCount::Type1 => Self::TYPE_1, + SampleCount::Type2 => Self::TYPE_2, + SampleCount::Type4 => Self::TYPE_4, + SampleCount::Type8 => Self::TYPE_8, SampleCount::Type16 => Self::TYPE_16, SampleCount::Type32 => Self::TYPE_32, SampleCount::Type64 => Self::TYPE_64, diff --git a/src/driver/mod.rs b/src/driver/mod.rs index f640cd9..a85b432 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -413,7 +413,7 @@ fn merge_push_constant_ranges(pcr: &[vk::PushConstantRange]) -> Vec vk::SurfaceFormatKHR; - -/// Describes a screen mode for display. -pub enum FullscreenMode { - /// A display mode which retains other operating system windows behind the current window. - Borderless, - - /// Seems to be the only way for stutter-free rendering on Nvidia + Win10. - Exclusive, -} - -/// Pumps an operating system event loop in order to handle input and other events -/// while drawing to the screen, continuously. -#[derive(Debug)] -pub struct EventLoop { - /// Provides access to the current graphics device. - pub device: Arc, - - display: Display, - event_loop: winit::event_loop::EventLoop<()>, - swapchain: Swapchain, - - /// Provides access to the current operating system window. - pub window: Window, -} - -impl EventLoop { - /// Specifies an event loop. - #[allow(clippy::new_ret_no_self)] - pub fn new() -> EventLoopBuilder { - Default::default() - } - - /// Current window height, in pixels. - pub fn height(&self) -> u32 { - self.window.inner_size().height - } - - /// Begins running a windowed event loop, providing `frame_fn` with a context of the current - /// frame. - pub fn run(mut self, mut frame_fn: FrameFn) -> Result<(), DisplayError> - where - FrameFn: FnMut(FrameContext), - { - let mut events = Vec::new(); - let mut will_exit = false; - let mut last_swapchain_err = None; - let mut run_result = Ok(()); - - // Use the same delta-time smoothing as Kajiya; but start it off with a reasonable - // guess so the following updates are even smoother - const STANDARD_REFRESH_RATE_MHZ: u32 = 60_000; - let refresh_rate = (self - .window - .fullscreen() - .map(|mode| match mode { - Fullscreen::Exclusive(mode) => mode.refresh_rate_millihertz(), - Fullscreen::Borderless(Some(monitor)) => monitor - .video_modes() - .next() - .map(|mode| mode.refresh_rate_millihertz()) - .unwrap_or(STANDARD_REFRESH_RATE_MHZ), - _ => STANDARD_REFRESH_RATE_MHZ, - }) - .unwrap_or(STANDARD_REFRESH_RATE_MHZ) - .clamp(STANDARD_REFRESH_RATE_MHZ, STANDARD_REFRESH_RATE_MHZ << 2) - / 1_000) as f32; - let mut last_frame = Instant::now(); - let mut dt_filtered = 1.0 / refresh_rate; - last_frame -= Duration::from_secs_f32(dt_filtered); - - debug!("first frame dt: {}", dt_filtered); - - self.window.set_visible(true); - - self.event_loop - .run(|event, window| { - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - window.exit(); - } - Event::WindowEvent { - event: WindowEvent::Resized(size), - .. - } => { - let mut swapchain_info = self.swapchain.info(); - swapchain_info.width = size.width; - swapchain_info.height = size.height; - self.swapchain.set_info(swapchain_info); - } - Event::AboutToWait => { - self.window.request_redraw(); - return; - } - _ => {} - } - - if !matches!( - event, - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } - ) { - events.push(event); - return; - } - - trace!("πŸŸ₯🟩🟦 Event::RedrawRequested"); - profiling::scope!("Frame"); - - if !events.is_empty() { - trace!("received {} events", events.len(),); - } - - let now = Instant::now(); - - // Filter the frame time before passing it to the application and renderer. - // Fluctuations in frame rendering times cause stutter in animations, - // and time-dependent effects (such as motion blur). - // - // Should applications need unfiltered delta time, they can calculate - // it themselves, but it's good to pass the filtered time so users - // don't need to worry about it. - { - profiling::scope!("Calculate dt"); - - let dt_duration = now - last_frame; - last_frame = now; - - let dt_raw = dt_duration.as_secs_f32(); - dt_filtered = dt_filtered + (dt_raw - dt_filtered) / 10.0; - }; - - // Note: Errors when acquiring swapchain images are not considered fatal - match self.swapchain.acquire_next_image() { - Err(err) => { - if last_swapchain_err == Some(err) { - // Generally ignore repeated errors as the window may take some time to get - // back to a workable state - debug!("Unable to acquire swapchain image: {err:?}"); - } else { - // The error has changed - this may happen during some window events - warn!("Unable to acquire swapchain image: {err:?}"); - last_swapchain_err = Some(err); - } - } - Ok(swapchain_image) => { - last_swapchain_err = None; - - let height = swapchain_image.info.height; - let width = swapchain_image.info.width; - let mut render_graph = RenderGraph::new(); - let swapchain_image = render_graph.bind_node(swapchain_image); - - { - profiling::scope!("Frame callback"); - - frame_fn(FrameContext { - device: &self.device, - dt: dt_filtered, - height, - render_graph: &mut render_graph, - events: &events, - swapchain_image, - width, - window: &self.window, - will_exit: &mut will_exit, - }); - - if will_exit { - window.exit(); - return; - } - } - - let elapsed = Instant::now() - now; - - trace!( - "βœ…βœ…βœ… render graph construction: {} ΞΌs ({}% load)", - elapsed.as_micros(), - ((elapsed.as_secs_f32() / refresh_rate) * 100.0) as usize, - ); - - match self.display.resolve_image(render_graph, swapchain_image) { - Err(err) => { - // This is considered a fatal error and will be thrown back to the - // caller - error!("Unable to resolve swapchain image: {err}"); - run_result = Err(err); - window.exit(); - } - Ok(swapchain_image) => { - self.window.pre_present_notify(); - self.swapchain.present_image(swapchain_image, 0, 0); - - profiling::finish_frame!(); - } - } - } - } - - events.clear(); - }) - .map_err(|err| { - error!("Unable to run event loop: {err}"); - - DisplayError::Driver(DriverError::Unsupported) - })?; - - run_result?; - - self.window.set_visible(false); - - Ok(()) - } - - /// Current window width, in pixels. - pub fn width(&self) -> u32 { - self.window.inner_size().width - } - - /// Current window. - pub fn window(&self) -> &Window { - &self.window - } -} - -impl AsRef> for EventLoop { - fn as_ref(&self) -> &winit::event_loop::EventLoop<()> { - &self.event_loop - } -} - -/// Builder for `EventLoop`. -pub struct EventLoopBuilder { - cmd_buf_count: usize, - device_info: DeviceInfoBuilder, - event_loop: winit::event_loop::EventLoop<()>, - resolver_pool: Option>, - surface_format_fn: Option>, - swapchain_info: SwapchainInfoBuilder, - window: WindowAttributes, -} - -impl Debug for EventLoopBuilder { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("EventLoopBuilder") - } -} - -impl Default for EventLoopBuilder { - fn default() -> Self { - Self { - cmd_buf_count: 5, - device_info: DeviceInfoBuilder::default(), - event_loop: winit::event_loop::EventLoop::new().expect("Unable to build event loop"), - resolver_pool: None, - surface_format_fn: None, - swapchain_info: SwapchainInfoBuilder::default(), - window: Default::default(), - } - } -} - -impl EventLoopBuilder { - // /// Returns the list of all the monitors available on the system. - // pub fn available_monitors(&self) -> impl Iterator { - // self.event_loop.available_monitors() - // } - - /// Specifies the number of in-flight command buffers, which should be greater - /// than or equal to the desired swapchain image count. - /// - /// More command buffers mean less time waiting for previously submitted frames to complete, but - /// more memory in use. - /// - /// Generally a value of one or two greater than desired image count produces the smoothest - /// animation. - pub fn command_buffer_count(mut self, cmd_buf_count: usize) -> Self { - self.cmd_buf_count = cmd_buf_count; - self - } - - /// A function to select the desired swapchain surface image format. - /// - /// By default linear color space will be selected unless it is not available. - pub fn desired_surface_format(mut self, surface_format_fn: F) -> Self - where - F: 'static + FnOnce(&[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR, - { - let surface_format_fn = Box::new(surface_format_fn); - self.surface_format_fn = Some(surface_format_fn); - self - } - - /// The desired, but not guaranteed, number of images that will be in the created swapchain. - /// - /// More images introduces more display lag, but smoother animation. - pub fn desired_swapchain_image_count(mut self, desired_swapchain_image_count: u32) -> Self { - self.swapchain_info = self - .swapchain_info - .desired_image_count(desired_swapchain_image_count); - self - } - - /// Set to `true` to enable vsync in exclusive fullscreen video modes. - pub fn sync_display(mut self, sync_display: bool) -> Self { - self.swapchain_info = self.swapchain_info.sync_display(sync_display); - self - } - - /// Sets up fullscreen mode. In addition, decorations are set to `false` and maximized is set to - /// `true`. - /// - /// # Note - /// - /// There are additional options offered by `winit` which can be accessed using the `window` - /// function. - pub fn fullscreen_mode(mut self, mode: FullscreenMode) -> Self { - // let inner_size; - // self.window = self - // .window - // .with_decorations(false) - // .with_maximized(true) - // .with_fullscreen(Some(match mode { - // FullscreenMode::Borderless => { - // info!("Using borderless fullscreen"); - - // inner_size = None; - - // Fullscreen::Borderless(None) - // } - // FullscreenMode::Exclusive => { - // if let Some(video_mode) = - // self.event_loop.primary_monitor().and_then(|monitor| { - // let monitor_size = monitor.size(); - // monitor.video_modes().find(|mode| { - // let mode_size = mode.size(); - - // // Don't pick a mode which has greater resolution than the monitor is - // // currently using: it causes a panic on x11 in winit - // mode_size.height <= monitor_size.height - // && mode_size.width <= monitor_size.width - // }) - // }) - // { - // info!( - // "Using {}x{} {}bpp @ {}hz exclusive fullscreen", - // video_mode.size().width, - // video_mode.size().height, - // video_mode.bit_depth(), - // video_mode.refresh_rate_millihertz() / 1_000 - // ); - - // inner_size = Some(video_mode.size()); - - // Fullscreen::Exclusive(video_mode) - // } else { - // warn!("Using borderless fullscreen"); - - // inner_size = None; - - // Fullscreen::Borderless(None) - // } - // } - // })); - - // if let Some(inner_size) = inner_size.or_else(|| { - // self.event_loop - // .primary_monitor() - // .map(|monitor| monitor.size()) - // }) { - // self.window = self.window.with_inner_size(inner_size); - // } - - self - } - - /// Enables Vulkan graphics debugging layers. - /// - /// _NOTE:_ Any valdation warnings or errors will cause the current thread to park itself after - /// describing the error using the `log` crate. This makes it easy to attach a debugger and see - /// what is causing the issue directly. - /// - /// ## Platform-specific - /// - /// **macOS:** Has no effect. - pub fn debug(mut self, debug: bool) -> Self { - self.device_info = self.device_info.debug(debug); - self - } - - /// Returns the primary monitor of the system. - /// - /// Returns `None` if it can't identify any monitor as a primary one. - /// - /// ## Platform-specific - /// - /// **Wayland:** Always returns `None`. - // pub fn primary_monitor(&self) -> Option { - // self.event_loop.primary_monitor() - // } - - /// Allows for specification of a custom pool implementation. - /// - /// This pool will hold leases for Vulkan objects needed by [`Display`]. - pub fn resolver_pool(mut self, pool: Box) -> Self { - self.resolver_pool = Some(pool); - self - } - - /// Allows deeper customization of the window, if needed. - pub fn window(mut self, window_fn: WindowFn) -> Self - where - WindowFn: FnOnce(WindowAttributes) -> WindowAttributes, - { - self.window = window_fn(self.window); - self - } - - /// Sets up "windowed" mode, which is the opposite of fullscreen. - /// - /// # Note - /// - /// There are additional options offered by `winit` which can be accessed using the `window` - /// function. - pub fn window_mode(mut self) -> Self { - self.window = self.window.with_fullscreen(None); - self - } -} - -impl EventLoopBuilder { - /// Builds a new `EventLoop`. - pub fn build(mut self) -> Result { - // // Create an operating system window via Winit - // let window = self.window; - - // #[cfg(not(target_os = "macos"))] - // let window = window.with_visible(false); - - // let window = window.build(&self.event_loop).map_err(|err| { - // warn!("{err}"); - - // DriverError::Unsupported - // })?; - // let (width, height) = { - // let inner_size = window.inner_size(); - // (inner_size.width, inner_size.height) - // }; - // self.swapchain_info = self.swapchain_info.width(width).height(height); - - // // Load the GPU driver (thin Vulkan device and swapchain smart pointers) - // let device_info = self.device_info.build(); - // let device = Arc::new(Device::create_display_window(device_info, &window)?); - - // // TODO: Select a better index - // let queue_family_index = 0; - - // // Create a display that is cached using the given pool implementation - // let pool = self - // .resolver_pool - // .unwrap_or_else(|| Box::new(HashPool::new(&device))); - // let display = Display::new(&device, pool, self.cmd_buf_count, queue_family_index)?; - - // let surface = Surface::create(&device, &window)?; - // let surface_formats = Surface::formats(&surface)?; - - // if surface_formats.is_empty() { - // warn!("invalid surface formats"); - - // return Err(DriverError::Unsupported); - // } - - // for surface in &surface_formats { - // debug!( - // "surface: {:#?} ({:#?})", - // surface.format, surface.color_space - // ); - // } - - // let surface_format_fn = self - // .surface_format_fn - // .unwrap_or_else(|| Box::new(Surface::linear_or_default)); - // let surface_format = surface_format_fn(&surface_formats); - // let swapchain = Swapchain::new( - // &device, - // surface, - // self.swapchain_info.surface(surface_format), - // )?; - - // info!( - // "Window dimensions: {}x{} ({}x scale)", - // width, - // height, - // window.scale_factor() as f32, - // ); - - // Ok(EventLoop { - // device, - // display, - // event_loop: self.event_loop, - // swapchain, - // window, - // }) - todo!() - } -} diff --git a/src/graph/resolver.rs b/src/graph/resolver.rs index 2a05853..cef937d 100644 --- a/src/graph/resolver.rs +++ b/src/graph/resolver.rs @@ -2933,7 +2933,7 @@ impl Resolver { } if let ExecutionPipeline::Graphic(pipeline) = pipeline { - for descriptor_binding @ DescriptorBinding(descriptor_set_idx, dst_binding) in + for DescriptorBinding(descriptor_set_idx, dst_binding) in pipeline.separate_samplers.iter().copied() { tls.image_writes.push(IndexWrite { diff --git a/src/lib.rs b/src/lib.rs index 64f12d4..a3262de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,90 +325,68 @@ pub mod graph; pub mod pool; mod display; -mod event_loop; -mod frame; /// Things which are used in almost every single _Screen 13_ program. pub mod prelude { - pub use { - super::{ - display::{Display, DisplayError, ResolverPool}, - driver::{ - accel_struct::{ - AccelerationStructure, AccelerationStructureGeometry, - AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, - AccelerationStructureInfo, AccelerationStructureInfoBuilder, - AccelerationStructureSize, DeviceOrHostAddress, - }, - buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresource}, - compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, - device::{Device, DeviceInfo, DeviceInfoBuilder}, - graphic::{ - BlendMode, BlendModeBuilder, DepthStencilMode, DepthStencilModeBuilder, - GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode, - }, - image::{ - Image, ImageInfo, ImageInfoBuilder, ImageSubresource, ImageType, ImageViewInfo, - ImageViewInfoBuilder, SampleCount, - }, - physical_device::{ - AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures, - RayTraceFeatures, RayTraceProperties, Vulkan10Features, Vulkan10Limits, - Vulkan10Properties, Vulkan11Features, Vulkan11Properties, Vulkan12Features, - Vulkan12Properties, - }, - ray_trace::{ - RayTracePipeline, RayTracePipelineInfo, RayTracePipelineInfoBuilder, - RayTraceShaderGroup, RayTraceShaderGroupType, - }, - render_pass::ResolveMode, - shader::{ - SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, ShaderCode, - SpecializationInfo, - }, - surface::Surface, - swapchain::{ - Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, - }, - AccessType, CommandBuffer, DriverError, Instance, + pub use super::{ + display::{Display, DisplayError, ResolverPool}, + driver::{ + accel_struct::{ + AccelerationStructure, AccelerationStructureGeometry, + AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, + AccelerationStructureInfo, AccelerationStructureInfoBuilder, + AccelerationStructureSize, DeviceOrHostAddress, }, - event_loop::{EventLoop, EventLoopBuilder, FullscreenMode}, - frame::{center_cursor, set_cursor_position, FrameContext}, - graph::{ - node::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, - AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, - BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, - }, - pass_ref::{PassRef, PipelinePassRef}, - Bind, ClearColorValue, RenderGraph, Unbind, + ash::vk, + buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresource}, + compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, + device::{Device, DeviceInfo, DeviceInfoBuilder}, + graphic::{ + BlendMode, BlendModeBuilder, DepthStencilMode, DepthStencilModeBuilder, + GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode, }, - pool::{ - alias::{Alias, AliasPool}, - fifo::FifoPool, - hash::HashPool, - lazy::LazyPool, - Lease, Pool, PoolInfo, PoolInfoBuilder, + image::{ + Image, ImageInfo, ImageInfoBuilder, ImageSubresource, ImageType, ImageViewInfo, + ImageViewInfoBuilder, SampleCount, }, + physical_device::{ + AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures, + RayTraceFeatures, RayTraceProperties, Vulkan10Features, Vulkan10Limits, + Vulkan10Properties, Vulkan11Features, Vulkan11Properties, Vulkan12Features, + Vulkan12Properties, + }, + ray_trace::{ + RayTracePipeline, RayTracePipelineInfo, RayTracePipelineInfoBuilder, + RayTraceShaderGroup, RayTraceShaderGroupType, + }, + render_pass::ResolveMode, + shader::{ + SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, ShaderCode, + SpecializationInfo, + }, + surface::Surface, + swapchain::{ + Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, + }, + AccessType, CommandBuffer, DriverError, Instance, }, - ash::vk, - log::{debug, error, info, logger, trace, warn}, // Everyone wants a log - winit::{ - self, - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, - event::{Event, MouseButton, WindowEvent}, - keyboard::KeyCode, - monitor::{MonitorHandle, VideoModeHandle}, - window::{ - BadIcon, CursorGrabMode, CursorIcon, Fullscreen, Icon, Theme, UserAttentionType, - Window, WindowAttributes, WindowId, + graph::{ + node::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, + AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, + BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, }, + pass_ref::{PassRef, PipelinePassRef}, + Bind, ClearColorValue, RenderGraph, Unbind, + }, + pool::{ + alias::{Alias, AliasPool}, + fifo::FifoPool, + hash::HashPool, + lazy::LazyPool, + Lease, Pool, PoolInfo, PoolInfoBuilder, }, }; } -pub use self::{ - display::{Display, DisplayError, ResolverPool}, - event_loop::{EventLoop, EventLoopBuilder, FullscreenMode}, - frame::FrameContext, -}; +pub use self::display::{Display, DisplayError, ResolverPool}; From 7021e0aebd47560897564fdfd1019df5d5c5c4c7 Mon Sep 17 00:00:00 2001 From: John Wells Date: Mon, 3 Jun 2024 22:54:01 -0400 Subject: [PATCH 03/21] Fix most examples --- Cargo.toml | 4 +- README.md | 8 +- contrib/screen-13-fx/src/image_loader.rs | 12 +- contrib/screen-13-window/Cargo.toml | 1 + contrib/screen-13-window/src/lib.rs | 41 ++--- examples/bindless.rs | 2 +- examples/font_bmp.rs | 2 +- examples/image_sampler.rs | 11 +- examples/min_max.rs | 1 + examples/msaa.rs | 1 + examples/multipass.rs | 45 ++--- examples/multithread.rs | 12 +- examples/ray_omni.rs | 20 ++- examples/ray_trace.rs | 49 ++++-- examples/rt_triangle.rs | 47 +++--- examples/shader-toy/src/main.rs | 1 - examples/transitions.rs | 15 +- examples/vertex_layout.rs | 23 +-- examples/vsm_omni.rs | 17 +- src/driver/device.rs | 12 +- src/driver/graphic.rs | 8 +- src/driver/image.rs | 2 +- src/driver/mod.rs | 2 +- src/driver/shader.rs | 78 +++++---- src/driver/swapchain.rs | 200 +++++++++++++---------- src/graph/resolver.rs | 21 +-- src/graph/swapchain.rs | 17 +- 27 files changed, 372 insertions(+), 280 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c3ad84f..5ed9610 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,8 +55,8 @@ puffin_http = "0.16" rand = "0.8" reqwest = { version = "0.12", features = ["blocking"] } screen-13-fx = { path = "contrib/screen-13-fx" } -#screen-13-imgui = { path = "contrib/screen-13-imgui" } -#screen-13-egui = { path = "contrib/screen-13-egui" } +# screen-13-imgui = { path = "contrib/screen-13-imgui" } +# screen-13-egui = { path = "contrib/screen-13-egui" } screen-13-window = { path = "contrib/screen-13-window" } tobj = "4.0" winit = "0.30" diff --git a/README.md b/README.md index adb21a8..0c59e0d 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,13 @@ screen-13 = "0.11" _Screen 13_ provides a high performance [Vulkan](https://www.vulkan.org/) driver using smart pointers. The driver may be created manually for headless rendering or automatically using the -built-in event loop abstraction: +built-in window abstraction: ```rust -use screen_13::prelude::*; +use screen_13_window::{Window, WindowError}; -fn main() -> Result<(), DisplayError> { - EventLoop::new().build()?.run(|frame| { +fn main() -> Result<(), WindowError> { + Window::new()?.run(|frame| { // It's time to do some graphics! 😲 }) } diff --git a/contrib/screen-13-fx/src/image_loader.rs b/contrib/screen-13-fx/src/image_loader.rs index 003fb1d..412c5b0 100644 --- a/contrib/screen-13-fx/src/image_loader.rs +++ b/contrib/screen-13-fx/src/image_loader.rs @@ -1,13 +1,11 @@ use { - super::BitmapFont, - anyhow::Context, - bmfont::BMFont, - inline_spirv::include_spirv, - log::{info, warn}, - screen_13::prelude::*, - std::sync::Arc, + super::BitmapFont, anyhow::Context, bmfont::BMFont, inline_spirv::include_spirv, log::info, + screen_13::prelude::*, std::sync::Arc, }; +#[cfg(debug_assertions)] +use log::warn; + fn align_up_u32(val: u32, atom: u32) -> u32 { (val + atom - 1) & !(atom - 1) } diff --git a/contrib/screen-13-window/Cargo.toml b/contrib/screen-13-window/Cargo.toml index 4fd49a3..6782eb8 100644 --- a/contrib/screen-13-window/Cargo.toml +++ b/contrib/screen-13-window/Cargo.toml @@ -8,6 +8,7 @@ readme = "README.md" [dependencies] log = "0.4" +profiling = "1.0" screen-13 = { path = "../.." } winit = "0.30" diff --git a/contrib/screen-13-window/src/lib.rs b/contrib/screen-13-window/src/lib.rs index d695eae..31f1257 100644 --- a/contrib/screen-13-window/src/lib.rs +++ b/contrib/screen-13-window/src/lib.rs @@ -1,7 +1,8 @@ mod frame; +pub use self::frame::FrameContext; + use { - self::frame::FrameContext, log::{info, warn}, screen_13::{ driver::{ @@ -45,11 +46,11 @@ pub struct Window { impl Window { pub fn new() -> Result { - Self::builder()?.build() + Self::builder().build() } - pub fn builder() -> Result { - WindowBuilder::new() + pub fn builder() -> WindowBuilder { + WindowBuilder::default() } pub fn run(self, draw_fn: F) -> Result<(), WindowError> @@ -176,8 +177,6 @@ impl Window { fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { if let Some(ActiveWindow { window, .. }) = self.active_window.as_ref() { window.request_redraw(); - - info!("Redraw requested"); } } @@ -249,8 +248,6 @@ impl Window { window_id: WindowId, event: WindowEvent, ) { - info!("Window event: {event:?}"); - if let Some(active_window) = self.active_window.as_mut() { match &event { WindowEvent::CloseRequested => { @@ -330,6 +327,8 @@ impl Window { warn!("Failed to acquire swapchain image"); } + profiling::finish_frame!(); + self.window.request_redraw(); true @@ -368,18 +367,6 @@ pub struct WindowBuilder { } impl WindowBuilder { - pub fn new() -> Result { - Ok(Self { - attributes: Default::default(), - cmd_buf_count: 5, - device_info: Default::default(), - image_count: None, - surface_format_fn: None, - v_sync: None, - window_mode_override: None, - }) - } - pub fn build(self) -> Result { let event_loop = EventLoop::new()?; let device = Arc::new(Device::create_display(self.device_info, &event_loop)?); @@ -505,6 +492,20 @@ impl fmt::Debug for WindowBuilder { } } +impl Default for WindowBuilder { + fn default() -> Self { + Self { + attributes: Default::default(), + cmd_buf_count: 5, + device_info: Default::default(), + image_count: None, + surface_format_fn: None, + v_sync: None, + window_mode_override: None, + } + } +} + struct WindowData { attributes: WindowAttributes, cmd_buf_count: usize, diff --git a/examples/bindless.rs b/examples/bindless.rs index 1aaa17a..f096816 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -13,7 +13,7 @@ fn main() -> Result<(), WindowError> { pretty_env_logger::init(); profile_with_puffin::init(); - let window = Window::builder()? + let window = Window::builder() .window(|window| window.with_inner_size(LogicalSize::new(512, 512))) .build()?; let images = create_images(&window.device)?; diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index f1e4db9..5d578f3 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -156,7 +156,7 @@ fn main() -> anyhow::Result<()> { let x = 320f32 * 0.5 / scale - width as f32 * 0.5; let y = 200f32 * 0.5 / scale - height as f32 * 0.5; let color = [196, 172, 230u8]; - //small_10px_font.print_scale(frame.render_graph, image_node, x, y, color, text, scale); + small_10px_font.print_scale(frame.render_graph, image_node, x, y, color, text, scale); display.present_image(frame.render_graph, image_node, frame.swapchain_image); })?; diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index e31c3a2..4511e01 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -5,6 +5,7 @@ use { hassle_rs::compile_hlsl, inline_spirv::inline_spirv, screen_13::prelude::*, + screen_13_window::Window, std::{ path::{Path, PathBuf}, sync::Arc, @@ -29,8 +30,8 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new().build()?; - let gulf_image = read_image(&event_loop.device, "examples/res/image/gulf.jpg")?; + let window = Window::new()?; + let gulf_image = read_image(&window.device, "examples/res/image/gulf.jpg")?; // Sampler info contains the full definition of Vulkan sampler settings using a builder struct let edge_edge = SamplerInfoBuilder::default() @@ -48,14 +49,14 @@ fn main() -> anyhow::Result<()> { // Image samplers are part of the shader pipeline and so we will create three pipelines total let pipelines = [edge_edge, border_edge_black, edge_border_white] .into_iter() - .map(|sampler_info| create_pipeline(&event_loop.device, sampler_info)) + .map(|sampler_info| create_pipeline(&window.device, sampler_info)) .collect::, _>>()?; let mut pipeline_index = 0; let mut pipeline_time = 0.0; - event_loop.run(|frame| { + window.run(|frame| { // Periodically change the active pipeline index - pipeline_time += frame.dt; + pipeline_time += 0.016; if pipeline_time > 2.0 { pipeline_time = 0.0; pipeline_index += 1; diff --git a/examples/min_max.rs b/examples/min_max.rs index e4eaa0f..dcded73 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -1,6 +1,7 @@ use { bytemuck::cast_slice, inline_spirv::inline_spirv, + log::warn, screen_13::prelude::*, std::{mem::size_of, sync::Arc}, }; diff --git a/examples/msaa.rs b/examples/msaa.rs index 48123ec..68341f2 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -4,6 +4,7 @@ use { bytemuck::{bytes_of, cast_slice, NoUninit}, glam::{Mat4, Vec3}, inline_spirv::inline_spirv, + log::warn, screen_13::prelude::*, screen_13_window::Window, std::{mem::size_of, sync::Arc}, diff --git a/examples/multipass.rs b/examples/multipass.rs index 934dfb2..9daaae1 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -5,6 +5,7 @@ use { glam::{vec3, Mat4, Vec3, Vec4}, inline_spirv::inline_spirv, screen_13::prelude::*, + screen_13_window::Window, std::sync::Arc, }; @@ -41,20 +42,20 @@ const GOLD: Material = Material { /// - Basic PBR rendering (from Sascha Willems) /// - Depth/stencil buffer usage /// - Multiple rendering passes with a transient image -fn main() -> Result<(), DisplayError> { +fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new().build().unwrap(); - let depth_stencil_format = best_depth_stencil_format(&event_loop.device); - let mut pool = LazyPool::new(&event_loop.device); - let fill_background = create_fill_background_pipeline(&event_loop.device); - let pbr = create_pbr_pipeline(&event_loop.device); - let funky_shape = create_funky_shape(&event_loop, &mut pool)?; + let window = Window::new()?; + let depth_stencil_format = best_depth_stencil_format(&window.device); + let mut pool = LazyPool::new(&window.device); + let fill_background = create_fill_background_pipeline(&window.device); + let pbr = create_pbr_pipeline(&window.device); + let funky_shape = create_funky_shape(&window.device, &mut pool)?; let mut t = 0.0; - event_loop.run(|mut frame| { - t += frame.dt; + window.run(|frame| { + t += 0.016; let index_buf = frame.render_graph.bind_node(&funky_shape.index_buf); let vertex_buf = frame.render_graph.bind_node(&funky_shape.vertex_buf); @@ -75,8 +76,8 @@ fn main() -> Result<(), DisplayError> { let obj_pos = Vec3::ZERO; let material = GOLD; - let camera_buf = bind_camera_buf(&mut frame, &mut pool, camera, model); - let light_buf = bind_light_buf(&mut frame, &mut pool); + let camera_buf = bind_camera_buf(frame.render_graph, &mut pool, camera, model); + let light_buf = bind_light_buf(frame.render_graph, &mut pool); let push_const_data = write_push_consts(obj_pos, material); let mut write = DepthStencilMode::DEPTH_WRITE; @@ -131,7 +132,9 @@ fn main() -> Result<(), DisplayError> { .record_subpass(move |subpass, _| { subpass.draw(6, 1, 0, 0); }); - }) + })?; + + Ok(()) } fn best_depth_stencil_format(device: &Device) -> vk::Format { @@ -159,7 +162,7 @@ fn best_depth_stencil_format(device: &Device) -> vk::Format { } fn bind_camera_buf( - frame: &mut FrameContext, + render_graph: &mut RenderGraph, pool: &mut LazyPool, camera: Camera, model: Mat4, @@ -172,10 +175,10 @@ fn bind_camera_buf( .unwrap(); write_camera_buf(&mut buf, camera, model); - frame.render_graph.bind_node(buf) + render_graph.bind_node(buf) } -fn bind_light_buf(frame: &mut FrameContext, pool: &mut LazyPool) -> BufferLeaseNode { +fn bind_light_buf(render_graph: &mut RenderGraph, pool: &mut LazyPool) -> BufferLeaseNode { let mut buf = pool .lease(BufferInfo::host_mem( 64, @@ -184,7 +187,7 @@ fn bind_light_buf(frame: &mut FrameContext, pool: &mut LazyPool) -> BufferLeaseN .unwrap(); write_light_buf(&mut buf); - frame.render_graph.bind_node(buf) + render_graph.bind_node(buf) } fn write_push_consts(obj_pos: Vec3, material: Material) -> [u8; 32] { @@ -217,33 +220,33 @@ fn camera(width: u32, height: u32) -> Camera { /// Returns ready-to-use index and vertex buffers. Index count is also returned. The shape data uses /// temporary staging buffers which are not required but are fun. -fn create_funky_shape(event_loop: &EventLoop, pool: &mut LazyPool) -> Result { +fn create_funky_shape(device: &Arc, pool: &mut LazyPool) -> Result { // Static index/vertex data courtesy of the polyhedron-ops library let (indices, vertices) = funky_shape_data(); let index_count = indices.len() as u32; // Create host-accessible buffers let index_buf_host = Buffer::create_from_slice( - &event_loop.device, + device, vk::BufferUsageFlags::TRANSFER_SRC, cast_slice(&indices), )?; let vertex_buf_host = Buffer::create_from_slice( - &event_loop.device, + device, vk::BufferUsageFlags::TRANSFER_SRC, cast_slice(&vertices), )?; // Create GPU-only buffers let index_buf = Arc::new(Buffer::create( - &event_loop.device, + device, BufferInfo::device_mem( index_buf_host.info.size, vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, ), )?); let vertex_buf = Arc::new(Buffer::create( - &event_loop.device, + device, BufferInfo::device_mem( vertex_buf_host.info.size, vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, diff --git a/examples/multithread.rs b/examples/multithread.rs index d051dda..d92decc 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -3,8 +3,10 @@ mod profile_with_puffin; use { bmfont::{BMFont, OrdinateOrientation}, image::io::Reader, + log::info, screen_13::prelude::*, screen_13_fx::BitmapFont, + screen_13_window::Window, std::{ collections::VecDeque, io::Cursor, @@ -34,8 +36,7 @@ fn main() -> anyhow::Result<()> { let started_at = Instant::now(); // For this example we don't use V-Sync so that we are able to submit work as often as possible - let sync_display = false; - let event_loop = EventLoop::new().sync_display(sync_display).build()?; + let event_loop = Window::builder().v_sync(false).build()?; // We want to create one hardware queue for each CPU, or at least two let desired_queue_count = available_parallelism() @@ -132,7 +133,12 @@ fn main() -> anyhow::Result<()> { let mut font = load_font(&event_loop.device)?; let mut images = VecDeque::new(); + let mut previous_frame = Instant::now(); event_loop.run(|frame| { + let current_frame = Instant::now(); + let elapsed = current_frame - previous_frame; + previous_frame = current_frame; + if let Ok(image) = rx.recv_timeout(Duration::from_nanos(1)) { images.push_front(image); @@ -179,7 +185,7 @@ fn main() -> anyhow::Result<()> { ); } - let fps = (1.0 / frame.dt).round(); + let fps = (1.0 / elapsed.as_secs_f32()).round(); let message = format!("FPS: {fps}"); font.print_scale( frame.render_graph, diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index fe110dc..368794c 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -4,8 +4,10 @@ use { bytemuck::{bytes_of, cast_slice, Pod, Zeroable}, glam::{vec3, vec4, Mat4, Vec3, Vec4}, inline_spirv::inline_spirv, + log::info, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, screen_13::prelude::*, + screen_13_window::Window, std::{ env::current_exe, fs::{metadata, write}, @@ -20,26 +22,26 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new().build()?; - let mut pool = LazyPool::new(&event_loop.device); + let window = Window::new()?; + let mut pool = LazyPool::new(&window.device); let depth_fmt = best_2d_optimal_format( - &event_loop.device, + &window.device, &[vk::Format::D32_SFLOAT, vk::Format::D16_UNORM], vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, vk::ImageCreateFlags::empty(), ); - let ground_mesh = load_ground_mesh(&event_loop.device)?; + let ground_mesh = load_ground_mesh(&window.device)?; let model_path = download_model_from_github("happy.obj")?; - let model_mesh = load_model_mesh(&event_loop.device, model_path)?; - let scene_blas = create_blas(&event_loop.device, &[&ground_mesh, &model_mesh])?; - let gfx_pipeline = create_pipeline(&event_loop.device)?; + let model_mesh = load_model_mesh(&window.device, model_path)?; + let scene_blas = create_blas(&window.device, &[&ground_mesh, &model_mesh])?; + let gfx_pipeline = create_pipeline(&window.device)?; let mut angle = 0f32; - event_loop.run(|frame| { - angle += frame.dt; + window.run(|frame| { + angle += 0.016; let scene_tlas = create_tlas(frame.device, &mut pool, frame.render_graph, &scene_blas).unwrap(); diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 4a6dc9e..9349860 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -3,9 +3,12 @@ mod profile_with_puffin; use { bytemuck::cast_slice, inline_spirv::inline_spirv, + log::warn, screen_13::prelude::*, + screen_13_window::Window, std::{io::BufReader, mem::size_of, sync::Arc}, tobj::{load_mtl_buf, load_obj_buf, GPU_LOAD_OPTIONS}, + winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; @@ -487,8 +490,8 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new().build()?; - let mut cache = HashPool::new(&event_loop.device); + let window = Window::new()?; + let mut cache = HashPool::new(&window.device); // ------------------------------------------------------------------------------------------ // // Setup the ray tracing pipeline @@ -499,13 +502,13 @@ fn main() -> anyhow::Result<()> { shader_group_handle_alignment, shader_group_handle_size, .. - } = event_loop + } = window .device .physical_device .ray_trace_properties .as_ref() .unwrap(); - let ray_trace_pipeline = create_ray_trace_pipeline(&event_loop.device)?; + let ray_trace_pipeline = create_ray_trace_pipeline(&window.device)?; // ------------------------------------------------------------------------------------------ // // Setup a shader binding table @@ -517,7 +520,7 @@ fn main() -> anyhow::Result<()> { let sbt_miss_size = 2 * sbt_handle_size; let sbt_buf = Arc::new({ let mut buf = Buffer::create( - &event_loop.device, + &window.device, BufferInfo::host_mem( (sbt_rgen_size + sbt_hit_size + sbt_miss_size) as _, vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR @@ -567,7 +570,7 @@ fn main() -> anyhow::Result<()> { // ------------------------------------------------------------------------------------------ // let (index_buf, vertex_buf, triangle_count, vertex_count, material_id_buf, material_buf) = - load_scene_buffers(&event_loop.device)?; + load_scene_buffers(&window.device)?; // ------------------------------------------------------------------------------------------ // // Create the bottom level acceleration structure @@ -592,9 +595,9 @@ fn main() -> anyhow::Result<()> { }, }], }; - let blas_size = AccelerationStructure::size_of(&event_loop.device, &blas_geometry_info); + let blas_size = AccelerationStructure::size_of(&window.device, &blas_geometry_info); let blas = Arc::new(AccelerationStructure::create( - &event_loop.device, + &window.device, AccelerationStructureInfo::blas(blas_size.create_size), )?); let blas_device_address = AccelerationStructure::device_address(&blas); @@ -623,7 +626,7 @@ fn main() -> anyhow::Result<()> { let instance_data = AccelerationStructure::instance_slice(&instances); let instance_buf = Arc::new({ let mut buffer = Buffer::create( - &event_loop.device, + &window.device, BufferInfo::host_mem( instance_data.len() as _, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR @@ -651,9 +654,9 @@ fn main() -> anyhow::Result<()> { }, }], }; - let tlas_size = AccelerationStructure::size_of(&event_loop.device, &tlas_geometry_info); + let tlas_size = AccelerationStructure::size_of(&window.device, &tlas_geometry_info); let tlas = Arc::new(AccelerationStructure::create( - &event_loop.device, + &window.device, AccelerationStructureInfo::tlas(tlas_size.create_size), )?); @@ -662,7 +665,7 @@ fn main() -> anyhow::Result<()> { // ------------------------------------------------------------------------------------------ // { - let accel_struct_scratch_offset_alignment = event_loop + let accel_struct_scratch_offset_alignment = window .device .physical_device .accel_struct_properties @@ -677,7 +680,7 @@ fn main() -> anyhow::Result<()> { { let scratch_buf = render_graph.bind_node(Buffer::create( - &event_loop.device, + &window.device, BufferInfo::device_mem( blas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -710,7 +713,7 @@ fn main() -> anyhow::Result<()> { { let scratch_buf = render_graph.bind_node(Buffer::create( - &event_loop.device, + &window.device, BufferInfo::device_mem( tlas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -764,7 +767,7 @@ fn main() -> anyhow::Result<()> { // - Update the camera uniform buffer // - Trace the image // - Copy image to the swapchain - event_loop.run(|frame| { + window.run(|frame| { if image.is_none() { image = Some(Arc::new( cache @@ -783,9 +786,19 @@ fn main() -> anyhow::Result<()> { let image_node = frame.render_graph.bind_node(image.as_ref().unwrap()); { - for event in frame.events { - input.update(event); - } + input.step_with_window_events( + &frame + .events + .iter() + .filter_map(|event| { + if let Event::WindowEvent { event, .. } = event { + Some(event.clone()) + } else { + None + } + }) + .collect::>(), + ); const SPEED: f32 = 0.1f32; diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 391ddde..0e46209 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -1,6 +1,12 @@ mod profile_with_puffin; -use {bytemuck::cast_slice, inline_spirv::inline_spirv, screen_13::prelude::*, std::sync::Arc}; +use { + bytemuck::{cast_slice, NoUninit}, + inline_spirv::inline_spirv, + screen_13::prelude::*, + screen_13_window::Window, + std::sync::Arc, +}; static SHADER_RAY_GEN: &[u32] = inline_spirv!( r#" @@ -95,10 +101,8 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new() - .desired_surface_format(Surface::linear_or_default) - .build()?; - let mut pool = HashPool::new(&event_loop.device); + let window = Window::new()?; + let mut pool = HashPool::new(&window.device); // ------------------------------------------------------------------------------------------ // // Setup the ray tracing pipeline @@ -109,13 +113,13 @@ fn main() -> anyhow::Result<()> { shader_group_handle_alignment, shader_group_handle_size, .. - } = event_loop + } = window .device .physical_device .ray_trace_properties .as_ref() .unwrap(); - let ray_trace_pipeline = create_ray_trace_pipeline(&event_loop.device)?; + let ray_trace_pipeline = create_ray_trace_pipeline(&window.device)?; // ------------------------------------------------------------------------------------------ // // Setup a shader binding table @@ -127,7 +131,7 @@ fn main() -> anyhow::Result<()> { let sbt_miss_size = 2 * sbt_handle_size; let sbt_buf = Arc::new({ let mut buf = Buffer::create( - &event_loop.device, + &window.device, BufferInfo::host_mem( (sbt_rgen_size + sbt_hit_size + sbt_miss_size) as _, vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR @@ -180,15 +184,12 @@ fn main() -> anyhow::Result<()> { let vertex_count = triangle_count * 3; #[repr(C)] - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone, Copy, NoUninit)] #[allow(dead_code)] struct Vertex { pos: [f32; 3], } - unsafe impl bytemuck::Pod for Vertex {} - unsafe impl bytemuck::Zeroable for Vertex {} - const VERTICES: [Vertex; 3] = [ Vertex { pos: [-1.0, 1.0, 0.0], @@ -206,7 +207,7 @@ fn main() -> anyhow::Result<()> { let index_buf = { let data = cast_slice(&INDICES); let mut buf = Buffer::create( - &event_loop.device, + &window.device, BufferInfo::host_mem( data.len() as _, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR @@ -220,7 +221,7 @@ fn main() -> anyhow::Result<()> { let vertex_buf = { let data = cast_slice(&VERTICES); let mut buf = Buffer::create( - &event_loop.device, + &window.device, BufferInfo::host_mem( data.len() as _, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR @@ -254,9 +255,9 @@ fn main() -> anyhow::Result<()> { }, }], }; - let blas_size = AccelerationStructure::size_of(&event_loop.device, &blas_geometry_info); + let blas_size = AccelerationStructure::size_of(&window.device, &blas_geometry_info); let blas = Arc::new(AccelerationStructure::create( - &event_loop.device, + &window.device, AccelerationStructureInfo::blas(blas_size.create_size), )?); let blas_device_address = AccelerationStructure::device_address(&blas); @@ -285,7 +286,7 @@ fn main() -> anyhow::Result<()> { let instance_data = AccelerationStructure::instance_slice(&instances); let instance_buf = Arc::new({ let mut buffer = Buffer::create( - &event_loop.device, + &window.device, BufferInfo::host_mem( instance_data.len() as _, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR @@ -313,9 +314,9 @@ fn main() -> anyhow::Result<()> { }, }], }; - let tlas_size = AccelerationStructure::size_of(&event_loop.device, &tlas_geometry_info); + let tlas_size = AccelerationStructure::size_of(&window.device, &tlas_geometry_info); let tlas = Arc::new(AccelerationStructure::create( - &event_loop.device, + &window.device, AccelerationStructureInfo::tlas(tlas_size.create_size), )?); @@ -324,7 +325,7 @@ fn main() -> anyhow::Result<()> { // ------------------------------------------------------------------------------------------ // { - let accel_struct_scratch_offset_alignment = event_loop + let accel_struct_scratch_offset_alignment = window .device .physical_device .accel_struct_properties @@ -339,7 +340,7 @@ fn main() -> anyhow::Result<()> { { let scratch_buf = render_graph.bind_node(Buffer::create( - &event_loop.device, + &window.device, BufferInfo::device_mem( blas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -373,7 +374,7 @@ fn main() -> anyhow::Result<()> { { let instance_node = render_graph.bind_node(instance_buf); let scratch_buf = render_graph.bind_node(Buffer::create( - &event_loop.device, + &window.device, BufferInfo::device_mem( tlas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -415,7 +416,7 @@ fn main() -> anyhow::Result<()> { // The event loop consists of: // - Trace the image // - Copy image to the swapchain - event_loop.run(|frame| { + window.run(|frame| { let blas_node = frame.render_graph.bind_node(&blas); let tlas_node = frame.render_graph.bind_node(&tlas); let sbt_node = frame.render_graph.bind_node(&sbt_buf); diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 7851f18..624e7f8 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -46,7 +46,6 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); let event_loop = EventLoop::new() - .debug(false) .desired_swapchain_image_count(3) .window(|builder| builder.with_inner_size(LogicalSize::new(1280.0f64, 720.0f64))) .build() diff --git a/examples/transitions.rs b/examples/transitions.rs index dbf016a..fb68b96 100644 --- a/examples/transitions.rs +++ b/examples/transitions.rs @@ -5,7 +5,9 @@ use { screen_13::prelude::*, screen_13_fx::*, screen_13_imgui::prelude::*, + screen_13_window::Window, std::{io::Cursor, time::Instant}, + winit::dpi::LogicalSize, }; fn main() -> anyhow::Result<()> { @@ -13,14 +15,13 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); // Create Screen 13 things any similar program might need - let event_loop = EventLoop::new() + let window = Window::builder() .window(|builder| builder.with_inner_size(LogicalSize::new(1024.0f64, 768.0f64))) - .desired_surface_format(Surface::linear_or_default) .build()?; - let display = ComputePresenter::new(&event_loop.device)?; - let mut imgui = ImGui::new(&event_loop.device); - let mut image_loader = ImageLoader::new(&event_loop.device)?; - let mut transition_pipeline = TransitionPipeline::new(&event_loop.device); + let display = ComputePresenter::new(&window.device)?; + let mut imgui = ImGui::new(&window.device); + let mut image_loader = ImageLoader::new(&window.device)?; + let mut transition_pipeline = TransitionPipeline::new(&window.device); // Load two images for the demo to blend between let bart_image = image_loader.decode_linear( @@ -54,7 +55,7 @@ fn main() -> anyhow::Result<()> { let mut curr_transition_idx = 0; let mut start_time = Instant::now(); - event_loop.run(|mut frame| { + window.run(|mut frame| { // Update the demo "state" let now = Instant::now(); let elapsed = (now - start_time).as_secs_f32(); diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index f448ebe..8fc5a49 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -5,6 +5,7 @@ use { half::f16, inline_spirv::inline_spirv, screen_13::prelude::*, + screen_13_window::{FrameContext, Window}, std::{mem::size_of, sync::Arc}, }; @@ -14,16 +15,16 @@ use { /// /// Most hardware will support 64 bit values, so we first check for support and if that fails /// we fall back to 16 bit values. -fn main() -> Result<(), DisplayError> { +fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); // NOTE: This example uses the 64-bit rules defined in the Vulkan spec, they're not obvious: // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fxvertex-attrib - let event_loop = EventLoop::new().build()?; + let window = Window::new()?; - let f16_pipeline = create_f16_pipeline(&event_loop.device).ok(); + let f16_pipeline = create_f16_pipeline(&window.device).ok(); let f16_vertex_buf = { #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] @@ -32,7 +33,7 @@ fn main() -> Result<(), DisplayError> { let vec2 = |x, y| [f16::from_f32(x), f16::from_f32(y)]; Arc::new(Buffer::create_from_slice( - &event_loop.device, + &window.device, vk::BufferUsageFlags::VERTEX_BUFFER, cast_slice(&[ Vertex(vec2(-1.0, -1.0), [1.0, 0.0, 0.0]), @@ -42,14 +43,14 @@ fn main() -> Result<(), DisplayError> { )?) }; - let f32_pipeline = create_f32_pipeline(&event_loop.device)?; + let f32_pipeline = create_f32_pipeline(&window.device)?; let f32_vertex_buf = { #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct Vertex([f32; 2], [f32; 3]); Arc::new(Buffer::create_from_slice( - &event_loop.device, + &window.device, vk::BufferUsageFlags::VERTEX_BUFFER, cast_slice(&[ Vertex([-1f32, -1.0], [1.0, 0.0, 0.0]), @@ -59,14 +60,14 @@ fn main() -> Result<(), DisplayError> { )?) }; - let f64_pipeline = create_f64_pipeline(&event_loop.device).ok(); + let f64_pipeline = create_f64_pipeline(&window.device).ok(); let f64_vertex_buf = { #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct Vertex([f64; 2], [f32; 3], u32); Arc::new(Buffer::create_from_slice( - &event_loop.device, + &window.device, vk::BufferUsageFlags::VERTEX_BUFFER, cast_slice(&[ Vertex([-1.0, -1.0], [1.0, 0.0, 0.0], 0), @@ -76,7 +77,7 @@ fn main() -> Result<(), DisplayError> { )?) }; - event_loop.run(|mut frame| { + window.run(|mut frame| { draw_triangle(&mut frame, &f32_pipeline, &f32_vertex_buf); // (Fun fact: Screen 13 turns these two passes into one renderpass with a second subpass!) @@ -86,7 +87,9 @@ fn main() -> Result<(), DisplayError> { } else if let Some(f16_pipeline) = &f16_pipeline { draw_triangle(&mut frame, f16_pipeline, &f16_vertex_buf); } - }) + })?; + + Ok(()) } fn draw_triangle( diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 07dfbdd..e378a88 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -4,6 +4,7 @@ use { bytemuck::{bytes_of, cast_slice, NoUninit, Pod, Zeroable}, glam::{vec3, Mat4, Quat, Vec3}, inline_spirv::inline_spirv, + log::info, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, screen_13::prelude::*, screen_13_window::Window, @@ -46,7 +47,7 @@ fn main() -> anyhow::Result<()> { let cube_transform = Mat4::from_scale(Vec3::splat(10.0)).to_cols_array(); let mut input = WinitInputHelper::default(); - let event_loop = Window::builder()? + let event_loop = Window::builder() .window(|window| window.with_inner_size(LogicalSize::new(800, 600))) .build()?; @@ -1321,7 +1322,7 @@ fn load_model( face_fn: fn(a: Vec3, b: Vec3, c: Vec3) -> [T; 3], ) -> anyhow::Result where - T: Default + Pod, + T: Default + NoUninit, { let (models, _) = load_obj(path.as_ref(), &GPU_LOAD_OPTIONS)?; let mut vertices = @@ -1388,16 +1389,12 @@ where /// Loads an .obj model as indexed position and normal vertices fn load_model_mesh(device: &Arc, path: impl AsRef) -> anyhow::Result { #[repr(C)] - #[derive(Clone, Copy, Default)] + #[derive(Clone, Copy, Default, NoUninit)] struct Vertex { position: Vec3, normal: Vec3, } - unsafe impl Pod for Vertex {} - - unsafe impl Zeroable for Vertex {} - load_model(device, path, |a, b, c| { let u = b - a; let v = c - a; @@ -1429,15 +1426,11 @@ fn load_model_mesh(device: &Arc, path: impl AsRef) -> anyhow::Resu /// Loads an .obj model as indexed position vertices fn load_model_shadow(device: &Arc, path: impl AsRef) -> anyhow::Result { #[repr(C)] - #[derive(Clone, Copy, Default)] + #[derive(Clone, Copy, Default, NoUninit)] struct Vertex { position: Vec3, } - unsafe impl Pod for Vertex {} - - unsafe impl Zeroable for Vertex {} - load_model(device, path, |a, b, c| { // Make faces CCW [ diff --git a/src/driver/device.rs b/src/driver/device.rs index 4efa4d8..ffd1043 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -2,7 +2,7 @@ use { super::{physical_device::PhysicalDevice, DriverError, Instance}, - ash::{khr, vk}, + ash::{ext, khr, vk}, ash_window::enumerate_required_extensions, derive_builder::{Builder, UninitializedFieldError}, gpu_allocator::{ @@ -73,7 +73,7 @@ impl Device { where F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult, { - let mut enabled_ext_names = Vec::with_capacity(5); + let mut enabled_ext_names = Vec::with_capacity(6); if display_window { enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); @@ -92,6 +92,10 @@ impl Device { enabled_ext_names.push(khr::ray_tracing_pipeline::NAME.as_ptr()); } + if physical_device.index_type_uint8_features.index_type_uint8 { + enabled_ext_names.push(ext::index_type_uint8::NAME.as_ptr()); + } + let priorities = repeat(1.0) .take( physical_device @@ -125,14 +129,14 @@ impl Device { let mut features_v1_2 = vk::PhysicalDeviceVulkan12Features::default(); let mut acceleration_structure_features = vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default(); - let mut index_type_uin8_feautres = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default(); + let mut index_type_uint8_features = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default(); let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default(); let mut ray_trace_features = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default(); let mut features = vk::PhysicalDeviceFeatures2::default() .push_next(&mut features_v1_1) .push_next(&mut features_v1_2) .push_next(&mut acceleration_structure_features) - .push_next(&mut index_type_uin8_feautres) + .push_next(&mut index_type_uint8_features) .push_next(&mut ray_query_features) .push_next(&mut ray_trace_features); unsafe { get_physical_device_features2(**physical_device, &mut features) }; diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index a2ee84e..8054bb8 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -6,10 +6,10 @@ use { image::SampleCount, merge_push_constant_ranges, shader::{ - align_spriv, DescriptorBindingMap, DescriptorInfo, PipelineDescriptorInfo, Shader, - SpecializationInfo, + align_spriv, Descriptor, DescriptorBindingMap, DescriptorInfo, PipelineDescriptorInfo, + Shader, SpecializationInfo, }, - DescriptorBinding, DriverError, + DriverError, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, @@ -366,7 +366,7 @@ pub struct GraphicPipeline { pub name: Option, pub(crate) push_constants: Vec, - pub(crate) separate_samplers: Box<[DescriptorBinding]>, + pub(crate) separate_samplers: Box<[Descriptor]>, pub(crate) shader_modules: Vec, pub(super) state: GraphicPipelineState, } diff --git a/src/driver/image.rs b/src/driver/image.rs index b29bc21..dab703a 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -61,7 +61,7 @@ use std::sync::Mutex; /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name pub struct Image { allocation: Option, // None when we don't own the image (Swapchain images) - device: Arc, + pub(super) device: Arc, image: vk::Image, #[allow(clippy::type_complexity)] image_view_cache: Mutex>, diff --git a/src/driver/mod.rs b/src/driver/mod.rs index a85b432..ec57a13 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -62,7 +62,7 @@ pub(crate) use self::{ AttachmentInfo, AttachmentRef, FramebufferAttachmentImageInfo, FramebufferInfo, RenderPass, RenderPassInfo, SubpassDependency, SubpassInfo, }, - shader::{DescriptorBinding, DescriptorBindingMap, DescriptorInfo}, + shader::{Descriptor, DescriptorBindingMap, DescriptorInfo}, surface::Surface, }; diff --git a/src/driver/shader.rs b/src/driver/shader.rs index d9db16a..0e3c5c3 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -23,8 +23,7 @@ use { }, }; -pub(crate) type DescriptorBindingMap = - HashMap; +pub(crate) type DescriptorBindingMap = HashMap; pub(crate) fn align_spriv(code: &[u8]) -> Result<&[u32], DriverError> { let (prefix, code, suffix) = unsafe { code.align_to() }; @@ -97,17 +96,23 @@ fn guess_immutable_sampler(binding_name: &str) -> SamplerInfo { /// This is a generic representation of the descriptor binding point within the shader and not a /// bound descriptor reference. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct DescriptorBinding(pub u32, pub u32); +pub struct Descriptor { + /// Descriptor set index + pub set: u32, -impl From for DescriptorBinding { - fn from(binding_idx: u32) -> Self { - Self(0, binding_idx) + /// Descriptor binding index + pub binding: u32, +} + +impl From for Descriptor { + fn from(binding: u32) -> Self { + Self { set: 0, binding } } } -impl From<(u32, u32)> for DescriptorBinding { - fn from((descriptor_set_idx, binding_idx): (u32, u32)) -> Self { - Self(descriptor_set_idx, binding_idx) +impl From<(u32, u32)> for Descriptor { + fn from((set, binding): (u32, u32)) -> Self { + Self { set, binding } } } @@ -185,6 +190,9 @@ impl DescriptorInfo { pub(crate) struct PipelineDescriptorInfo { pub layouts: BTreeMap, pub pool_sizes: HashMap>, + + #[allow(dead_code)] + samplers: Box<[Sampler]>, } impl PipelineDescriptorInfo { @@ -195,9 +203,9 @@ impl PipelineDescriptorInfo { ) -> Result { let descriptor_set_count = descriptor_bindings .keys() + .map(|descriptor| descriptor.set) .max() - .copied() - .map(|descriptor_binding| descriptor_binding.0 + 1) + .map(|set| set + 1) .unwrap_or_default(); let mut layouts = BTreeMap::new(); let mut pool_sizes = HashMap::new(); @@ -228,7 +236,7 @@ impl PipelineDescriptorInfo { .or_insert(binding_count); } - let samplers = sampler_info_binding_count + let mut samplers = sampler_info_binding_count .keys() .copied() .map(|sampler_info| { @@ -251,15 +259,15 @@ impl PipelineDescriptorInfo { let mut binding_counts = HashMap::::new(); let mut bindings = vec![]; - for (descriptor_binding, (descriptor_info, stage_flags)) in descriptor_bindings + for (descriptor, (descriptor_info, stage_flags)) in descriptor_bindings .iter() - .filter(|(descriptor_binding, _)| descriptor_binding.0 == descriptor_set_idx) + .filter(|(descriptor, _)| descriptor.set == descriptor_set_idx) { let descriptor_ty = descriptor_info.descriptor_type(); *binding_counts.entry(descriptor_ty).or_default() += descriptor_info.binding_count(); let mut binding = vk::DescriptorSetLayoutBinding::default() - .binding(descriptor_binding.1) + .binding(descriptor.binding) .descriptor_count(descriptor_info.binding_count()) .descriptor_type(descriptor_ty) .stage_flags(*stage_flags); @@ -314,12 +322,18 @@ impl PipelineDescriptorInfo { ); } + let samplers = samplers + .drain() + .map(|(_, sampler)| sampler) + .collect::>(); + //trace!("layouts {:#?}", &layouts); // trace!("pool_sizes {:#?}", &pool_sizes); Ok(Self { layouts, pool_sizes, + samplers, }) } } @@ -733,7 +747,7 @@ pub struct Shader { entry_point: EntryPoint, #[builder(default, private)] - image_samplers: HashMap, + image_samplers: HashMap, #[builder(default, private, setter(strip_option))] vertex_input_state: Option, @@ -901,7 +915,7 @@ impl Shader { pub(super) fn descriptor_bindings(&self) -> DescriptorBindingMap { let mut res = DescriptorBindingMap::default(); - for (name, binding, desc_ty, binding_count) in + for (name, descriptor, desc_ty, binding_count) in self.entry_point.vars.iter().filter_map(|var| match var { Variable::Descriptor { name, @@ -911,7 +925,10 @@ impl Shader { .. } => Some(( name, - DescriptorBinding(desc_bind.set(), desc_bind.bind()), + Descriptor { + set: desc_bind.set(), + binding: desc_bind.bind(), + }, desc_ty, *nbind, )), @@ -919,10 +936,10 @@ impl Shader { }) { trace!( - "binding {}: {}.{} = {:?}[{}]", + "descriptor {}: {}.{} = {:?}[{}]", name.as_deref().unwrap_or_default(), - binding.0, - binding.1, + descriptor.set, + descriptor.binding, *desc_ty, binding_count ); @@ -933,7 +950,7 @@ impl Shader { } DescriptorType::CombinedImageSampler() => { let (sampler_info, is_manually_defined) = - self.image_sampler(binding, name.as_deref().unwrap_or_default()); + self.image_sampler(descriptor, name.as_deref().unwrap_or_default()); DescriptorInfo::CombinedImageSampler( binding_count, @@ -947,7 +964,7 @@ impl Shader { DescriptorType::SampledImage() => DescriptorInfo::SampledImage(binding_count), DescriptorType::Sampler() => { let (sampler_info, is_manually_defined) = - self.image_sampler(binding, name.as_deref().unwrap_or_default()); + self.image_sampler(descriptor, name.as_deref().unwrap_or_default()); DescriptorInfo::Sampler(binding_count, sampler_info, is_manually_defined) } @@ -965,15 +982,15 @@ impl Shader { DescriptorInfo::UniformTexelBuffer(binding_count) } }; - res.insert(binding, (descriptor_info, self.stage)); + res.insert(descriptor, (descriptor_info, self.stage)); } res } - fn image_sampler(&self, binding: DescriptorBinding, name: &str) -> (SamplerInfo, bool) { + fn image_sampler(&self, descriptor: Descriptor, name: &str) -> (SamplerInfo, bool) { self.image_samplers - .get(&binding) + .get(&descriptor) .copied() .map(|sampler_info| (sampler_info, true)) .unwrap_or_else(|| (guess_immutable_sampler(name), false)) @@ -1365,17 +1382,20 @@ impl ShaderBuilder { #[profiling::function] pub fn image_sampler( mut self, - binding: impl Into, + descriptor: impl Into, info: impl Into, ) -> Self { - let binding = binding.into(); + let descriptor = descriptor.into(); let info = info.into(); if self.image_samplers.is_none() { self.image_samplers = Some(Default::default()); } - self.image_samplers.as_mut().unwrap().insert(binding, info); + self.image_samplers + .as_mut() + .unwrap() + .insert(descriptor, info); self } diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index a5d595d..cebf63b 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -16,7 +16,7 @@ use { #[derive(Debug)] pub struct Swapchain { device: Arc, - images: Vec>, + images: Vec>, info: SwapchainInfo, suboptimal: bool, surface: Surface, @@ -59,17 +59,61 @@ impl Swapchain { self.suboptimal = false; } - let Synchronization { - acquired, - ready, - rendered, - } = self.syncs[self.sync_idx]; + let mut acquired = vk::Semaphore::null(); + let mut ready = vk::Fence::null(); - unsafe { self.device.reset_fences(slice::from_ref(&ready)) }.map_err(|err| { - warn!("{err}"); + for idx in 0..self.syncs.len() { + self.sync_idx += 1; + self.sync_idx %= self.syncs.len(); + let sync = &self.syncs[idx]; - SwapchainError::SurfaceLost - })?; + unsafe { + match self.device.get_fence_status(sync.ready) { + Ok(true) => { + if self + .device + .reset_fences(slice::from_ref(&sync.ready)) + .is_err() + { + self.suboptimal = true; + return Err(SwapchainError::DeviceLost); + } + + acquired = sync.acquired; + ready = sync.ready; + + // info!("Sync idx {}", self.sync_idx); + } + Ok(false) => continue, + Err(_) => { + self.suboptimal = true; + return Err(SwapchainError::DeviceLost); + } + } + + if self + .device + .reset_fences(slice::from_ref(&sync.ready)) + .is_err() + { + self.suboptimal = true; + return Err(SwapchainError::DeviceLost); + } + } + } + + if acquired == vk::Semaphore::null() { + let sync = Synchronization::create(&self.device).map_err(|err| { + warn!("{err}"); + + SwapchainError::DeviceLost + })?; + acquired = sync.acquired; + ready = sync.ready; + + self.sync_idx = self.syncs.len(); + self.syncs.push(sync); + } let image_idx = unsafe { // We checked during recreate_swapchain @@ -87,22 +131,15 @@ impl Swapchain { match image_idx { Ok(image_idx) => { - self.sync_idx += 1; - self.sync_idx %= self.syncs.len(); - - let image = self.images[image_idx as usize].take().ok_or_else(|| { + let mut image = self.images[image_idx as usize].take().ok_or_else(|| { self.suboptimal = true; SwapchainError::Suboptimal })?; - Ok(SwapchainImage { - acquired, - image, - image_idx, - ready, - rendered, - }) + image.acquired = acquired; + + Ok(image) } Err(err) if err == vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT @@ -162,6 +199,19 @@ impl Swapchain { self.swapchain = vk::SwapchainKHR::null(); } + + for Synchronization { acquired, ready } in self.syncs.drain(..) { + // if let Err(err) = Device::wait_for_fence(&self.device, &ready) { + // warn!("{err}"); + // } + + unsafe { + self.device.destroy_semaphore(acquired, None); + self.device.destroy_fence(ready, None); + } + } + + self.images.clear(); } /// Gets information about this swapchain. @@ -189,21 +239,6 @@ impl Swapchain { "Queue index must be within the range of the available queues created by the device." ); - { - profiling::scope!("Wait for presentation ready"); - - // This does not use Device::wait_for_fence because we don't want to spam the logs - // (This is expected to commonly take multiple milliseconds) - if let Err(err) = unsafe { - self.device - .wait_for_fences(slice::from_ref(&image.ready), false, u64::MAX) - } { - warn!("Unable to wait for presentation ready fence: {err}"); - - return; - } - } - // We checked when handling out the swapchain image let swapchain_ext = unsafe { self.device.swapchain_ext.as_ref().unwrap_unchecked() }; @@ -237,9 +272,11 @@ impl Swapchain { } } - debug_assert!(self.images[image.image_idx as usize].is_none()); + let image_idx = image.image_idx as usize; - self.images[image.image_idx as usize] = Some(image.image); + debug_assert!(self.images[image_idx].is_none()); + + self.images[image_idx] = Some(image); } #[profiling::function] @@ -277,10 +314,6 @@ impl Swapchain { vk::ImageUsageFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_KHR, vk::ImageUsageFlags::INPUT_ATTACHMENT, vk::ImageUsageFlags::INVOCATION_MASK_HUAWEI, - // vk::ImageUsageFlags::RESERVED_16_QCOM, - // vk::ImageUsageFlags::RESERVED_17_QCOM, - // vk::ImageUsageFlags::RESERVED_22_EXT, - // vk::ImageUsageFlags::RESERVED_23_EXT, vk::ImageUsageFlags::SAMPLED, vk::ImageUsageFlags::SAMPLE_BLOCK_MATCH_QCOM, vk::ImageUsageFlags::SAMPLE_WEIGHT_QCOM, @@ -407,7 +440,7 @@ impl Swapchain { DriverError::Unsupported })?; - let vk_images = + let images = unsafe { swapchain_ext.get_swapchain_images(swapchain) }.map_err(|err| match err { vk::Result::INCOMPLETE => DriverError::InvalidData, vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => { @@ -415,13 +448,13 @@ impl Swapchain { } _ => DriverError::Unsupported, })?; - let images: Vec> = vk_images + let images = images .into_iter() .enumerate() - .map(|(idx, vk_image)| { + .map(|(image_idx, image)| { let mut image = Image::from_raw( &self.device, - vk_image, + image, ImageInfo::image_2d( surface_width, surface_height, @@ -429,10 +462,20 @@ impl Swapchain { surface_capabilities.supported_usage_flags, ), ); - image.name = Some(format!("swapchain{idx}")); - Some(image) + + let image_idx = image_idx as u32; + image.name = Some(format!("swapchain{image_idx}")); + + let rendered = Device::create_semaphore(&self.device)?; + + Ok(Some(SwapchainImage { + image, + image_idx, + acquired: vk::Semaphore::null(), + rendered, + })) }) - .collect(); + .collect::, _>>()?; debug_assert_eq!(desired_image_count, images.len() as u32); @@ -450,10 +493,6 @@ impl Swapchain { self.images.len(), ); - for _ in 0..self.images.len() { - self.syncs.push(Synchronization::create(&self.device)?); - } - Ok(()) } @@ -475,23 +514,6 @@ impl Drop for Swapchain { return; } - for Synchronization { - acquired, - ready, - rendered, - } in self.syncs.drain(..) - { - if let Err(err) = Device::wait_for_fence(&self.device, &ready) { - warn!("{err}"); - } - - unsafe { - self.device.destroy_semaphore(acquired, None); - self.device.destroy_fence(ready, None); - self.device.destroy_semaphore(rendered, None); - } - } - self.destroy(); } } @@ -502,30 +524,44 @@ pub struct SwapchainImage { pub(crate) acquired: vk::Semaphore, image: Image, image_idx: u32, - ready: vk::Fence, pub(crate) rendered: vk::Semaphore, } -impl Clone for SwapchainImage { - fn clone(&self) -> Self { - let &Self { +impl SwapchainImage { + pub(crate) fn unbind(&mut self) -> Self { + let &mut Self { acquired, image_idx, - ready, rendered, .. } = self; + self.rendered = vk::Semaphore::null(); + Self { acquired, image: Image::clone_raw(&self.image), image_idx, - ready, rendered, } } } +impl Drop for SwapchainImage { + #[profiling::function] + fn drop(&mut self) { + if panicking() { + return; + } + + if self.rendered != vk::Semaphore::null() { + unsafe { + self.image.device.destroy_semaphore(self.rendered, None); + } + } + } +} + impl Deref for SwapchainImage { type Target = Image; @@ -639,24 +675,18 @@ impl From for SwapchainInfoBuilderError { } } -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] struct Synchronization { acquired: vk::Semaphore, ready: vk::Fence, - rendered: vk::Semaphore, } impl Synchronization { fn create(device: &Device) -> Result { let acquired = Device::create_semaphore(device)?; - let ready = Device::create_fence(device, true)?; - let rendered = Device::create_semaphore(device)?; + let ready = Device::create_fence(device, false)?; - Ok(Self { - acquired, - ready, - rendered, - }) + Ok(Self { acquired, ready }) } } diff --git a/src/graph/resolver.rs b/src/graph/resolver.rs index cef937d..be77638 100644 --- a/src/graph/resolver.rs +++ b/src/graph/resolver.rs @@ -13,9 +13,9 @@ use { image::{Image, ImageViewInfo}, image_access_layout, is_framebuffer_access, is_read_access, is_write_access, pipeline_stage_access_flags, AttachmentInfo, AttachmentRef, CommandBuffer, - CommandBufferInfo, DescriptorBinding, DescriptorInfo, DescriptorPool, - DescriptorPoolInfo, DescriptorSet, DriverError, FramebufferAttachmentImageInfo, - FramebufferInfo, RenderPass, RenderPassInfo, SubpassDependency, SubpassInfo, + CommandBufferInfo, Descriptor, DescriptorInfo, DescriptorPool, DescriptorPoolInfo, + DescriptorSet, DriverError, FramebufferAttachmentImageInfo, FramebufferInfo, + RenderPass, RenderPassInfo, SubpassDependency, SubpassInfo, }, pool::{Lease, Pool}, }, @@ -2821,7 +2821,7 @@ impl Resolver { let (descriptor_set_idx, dst_binding, binding_offset) = descriptor.into_tuple(); let (descriptor_info, _) = pipeline .descriptor_bindings() - .get(&DescriptorBinding(descriptor_set_idx, dst_binding)) + .get(&Descriptor { set: descriptor_set_idx, binding: dst_binding }) .unwrap_or_else(|| panic!("descriptor {descriptor_set_idx}.{dst_binding}[{binding_offset}] specified in recorded execution of pass \"{}\" was not discovered through shader reflection", &pass.name)); let descriptor_type = descriptor_info.descriptor_type(); let bound_node = &bindings[*node_idx]; @@ -2933,14 +2933,12 @@ impl Resolver { } if let ExecutionPipeline::Graphic(pipeline) = pipeline { - for DescriptorBinding(descriptor_set_idx, dst_binding) in - pipeline.separate_samplers.iter().copied() - { + for Descriptor { set, binding } in pipeline.separate_samplers.iter().copied() { tls.image_writes.push(IndexWrite { idx: tls.image_infos.len(), write: vk::WriteDescriptorSet::default() - .dst_set(*descriptor_sets[descriptor_set_idx as usize]) - .dst_binding(dst_binding) + .dst_set(*descriptor_sets[set as usize]) + .dst_binding(binding) .descriptor_type(vk::DescriptorType::SAMPLER) .descriptor_count(1), }); @@ -2950,7 +2948,10 @@ impl Resolver { // Write graphic render pass input attachments (they're automatic) if exec_idx > 0 { for ( - &DescriptorBinding(descriptor_set_idx, dst_binding), + &Descriptor { + set: descriptor_set_idx, + binding: dst_binding, + }, (descriptor_info, _), ) in &pipeline.descriptor_bindings { diff --git a/src/graph/swapchain.rs b/src/graph/swapchain.rs index f603adf..b4b48a7 100644 --- a/src/graph/swapchain.rs +++ b/src/graph/swapchain.rs @@ -30,14 +30,27 @@ impl Binding { unreachable!(); } } + + pub(super) fn as_swapchain_image_mut(&mut self) -> Option<&mut SwapchainImage> { + if let Self::SwapchainImage(binding, true) = self { + Some(binding) + } else if let Self::SwapchainImage(_, false) = self { + // User code might try this - but it is a programmer error + // to access a binding after it has been unbound so dont + None + } else { + // The private code in this module should prevent this branch + unreachable!(); + } + } } impl Unbind for SwapchainImageNode { // We allow the resolver to unbind a swapchain node directly into a shared image fn unbind(self, graph: &mut Resolver) -> SwapchainImage { graph.graph.bindings[self.idx] - .as_swapchain_image() + .as_swapchain_image_mut() .unwrap() - .clone() + .unbind() } } From 6c4117a0d9083d2367733d9c1c0ba1b83749a92b Mon Sep 17 00:00:00 2001 From: John Wells Date: Tue, 16 Jul 2024 22:58:46 -0400 Subject: [PATCH 04/21] cargo fmt --- contrib/screen-13-fx/src/transition.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/screen-13-fx/src/transition.rs b/contrib/screen-13-fx/src/transition.rs index c4427bc..7f2b5cd 100644 --- a/contrib/screen-13-fx/src/transition.rs +++ b/contrib/screen-13-fx/src/transition.rs @@ -4,8 +4,8 @@ use { inline_spirv::include_spirv, - screen_13::prelude::*, log::trace, + screen_13::prelude::*, std::{collections::HashMap, sync::Arc}, }; From c5f9e81eaf47bf0619dc98d5dbdb3daaca73eb04 Mon Sep 17 00:00:00 2001 From: John Wells Date: Tue, 16 Jul 2024 23:03:47 -0400 Subject: [PATCH 05/21] gpu-allocator v0.27 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5ed9610..f74905f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ profile-with-tracy = ["profiling/profile-with-tracy"] ash = "0.38" ash-window = "0.13" derive_builder = "0.20" -gpu-allocator = { git = "https://github.com/Traverse-Research/gpu-allocator.git", rev = "2e2296e3045f6ae83a879928c87e558830d7ab63" } #"0.26" +gpu-allocator = "0.27" log = "0.4" ordered-float = "4.1" parking_lot = { version = "0.12", optional = true } From b219252a8214828e8e194ace29bc9d21a6513376 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 10:52:43 -0400 Subject: [PATCH 06/21] Fix egui example --- Cargo.toml | 2 +- contrib/screen-13-egui/Cargo.toml | 7 +++---- contrib/screen-13-egui/src/lib.rs | 12 ++++-------- contrib/screen-13-window/src/lib.rs | 6 ++++++ examples/egui.rs | 24 ++++++++++++++---------- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f74905f..3317de8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ rand = "0.8" reqwest = { version = "0.12", features = ["blocking"] } screen-13-fx = { path = "contrib/screen-13-fx" } # screen-13-imgui = { path = "contrib/screen-13-imgui" } -# screen-13-egui = { path = "contrib/screen-13-egui" } +screen-13-egui = { path = "contrib/screen-13-egui" } screen-13-window = { path = "contrib/screen-13-window" } tobj = "4.0" winit = "0.30" diff --git a/contrib/screen-13-egui/Cargo.toml b/contrib/screen-13-egui/Cargo.toml index 1538c12..b66a5e0 100644 --- a/contrib/screen-13-egui/Cargo.toml +++ b/contrib/screen-13-egui/Cargo.toml @@ -8,10 +8,9 @@ readme = "README.md" [dependencies] bytemuck = "1.14" -egui = { version = "0.26", features = [ - "bytemuck" -] } -egui-winit = "0.26" +# TODO: Waiting for egui to update winit version +egui = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #{ version = "0.28", features = ["bytemuck"] } +egui-winit = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #"0.28" inline-spirv = "0.2" screen-13 = { path = "../.." } screen-13-fx = { path = "../screen-13-fx" } diff --git a/contrib/screen-13-egui/src/lib.rs b/contrib/screen-13-egui/src/lib.rs index ac87f82..8b75f03 100644 --- a/contrib/screen-13-egui/src/lib.rs +++ b/contrib/screen-13-egui/src/lib.rs @@ -6,6 +6,7 @@ pub use egui; use { bytemuck::cast_slice, + egui_winit::winit::{event::Event, event_loop::EventLoop, window::Window}, screen_13::prelude::*, std::{borrow::Cow, collections::HashMap, sync::Arc}, }; @@ -21,10 +22,7 @@ pub struct Egui { } impl Egui { - pub fn new( - device: &Arc, - event_loop: &egui_winit::winit::event_loop::EventLoopWindowTarget<()>, - ) -> Self { + pub fn new(device: &Arc, event_loop: &EventLoop<()>) -> Self { let ppl = Arc::new( GraphicPipeline::create( device, @@ -58,9 +56,6 @@ impl Egui { ); let ctx = egui::Context::default(); - let native_pixels_per_point = event_loop - .primary_monitor() - .map(|monitor| monitor.scale_factor() as f32); let max_texture_side = Some( device .physical_device @@ -72,7 +67,8 @@ impl Egui { ctx.clone(), egui::ViewportId::ROOT, event_loop, - native_pixels_per_point, + None, + None, max_texture_side, ); diff --git a/contrib/screen-13-window/src/lib.rs b/contrib/screen-13-window/src/lib.rs index 31f1257..eb10bc4 100644 --- a/contrib/screen-13-window/src/lib.rs +++ b/contrib/screen-13-window/src/lib.rs @@ -356,6 +356,12 @@ impl Window { } } +impl AsRef> for Window { + fn as_ref(&self) -> &EventLoop<()> { + &self.event_loop + } +} + pub struct WindowBuilder { attributes: WindowAttributes, cmd_buf_count: usize, diff --git a/examples/egui.rs b/examples/egui.rs index 42b64ba..7abe5d2 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -1,21 +1,23 @@ mod profile_with_puffin; -use {screen_13::prelude::*, screen_13_egui::prelude::*}; +use { + screen_13::prelude::*, screen_13_egui::prelude::*, screen_13_window::Window, + winit::dpi::LogicalSize, +}; -fn main() -> Result<(), DisplayError> { +fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - let event_loop = EventLoop::new() - .desired_swapchain_image_count(2) - .desired_surface_format(Surface::linear_or_default) - .window(|window| window.with_transparent(false)) + let window = Window::builder() + .v_sync(false) + .window(|window| window.with_inner_size(LogicalSize::new(1024, 768))) .build()?; - let mut egui = Egui::new(&event_loop.device, event_loop.as_ref()); + let mut egui = Egui::new(&window.device, window.as_ref()); - let mut cache = LazyPool::new(&event_loop.device); + let mut cache = LazyPool::new(&window.device); - event_loop.run(|frame| { + window.run(|frame| { let img = frame.render_graph.bind_node( cache .lease(ImageInfo::image_2d( @@ -52,5 +54,7 @@ fn main() -> Result<(), DisplayError> { }); }, ); - }) + })?; + + Ok(()) } From 5fb6ab66b45013b2785cb9c3bb0f8875f15d2ceb Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 11:00:21 -0400 Subject: [PATCH 07/21] Fix debugger example --- examples/debugger.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/debugger.rs b/examples/debugger.rs index b250133..e319332 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -23,8 +23,8 @@ To continue, uncomment line 30. */ -fn main() -> Result<(), screen_13::DisplayError> { - use {screen_13::prelude::*, std::sync::Arc}; +fn main() -> Result<(), screen_13_window::WindowError> { + use {log::debug, screen_13::prelude::*, screen_13_window::Window, std::sync::Arc}; // πŸ‘‹, 🌎! //pretty_env_logger::init(); @@ -36,7 +36,7 @@ fn main() -> Result<(), screen_13::DisplayError> { - If you did not install the SDK, you must goto line 8, above. - If you have a recent SDK installed, you may advance the function pointer. */ - EventLoop::new().debug(true).build()?.run(|frame| { + Window::builder().debug(true).build()?.run(|frame| { /* You have now entered the per-frame callback. Everything is happening *so* fast. We just executed line two of our program! @@ -119,7 +119,7 @@ fn main() -> Result<(), screen_13::DisplayError> { u32::MAX, u32::MAX, vk::Format::UNDEFINED, - vk::ImageUsageFlags::RESERVED_22_EXT, + vk::ImageUsageFlags::default(), ), ) .unwrap(), From 7178e7c251c69dd30455d5b7097fdddfa2bf8ad0 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 11:12:36 -0400 Subject: [PATCH 08/21] Fix imgui example --- Cargo.toml | 2 +- contrib/screen-13-imgui/Cargo.toml | 4 +- contrib/screen-13-imgui/src/lib.rs | 19 ++------- examples/imgui.rs | 64 +++++++++++++++++------------- 4 files changed, 43 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3317de8..d2db2e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ puffin_http = "0.16" rand = "0.8" reqwest = { version = "0.12", features = ["blocking"] } screen-13-fx = { path = "contrib/screen-13-fx" } -# screen-13-imgui = { path = "contrib/screen-13-imgui" } +screen-13-imgui = { path = "contrib/screen-13-imgui" } screen-13-egui = { path = "contrib/screen-13-egui" } screen-13-window = { path = "contrib/screen-13-window" } tobj = "4.0" diff --git a/contrib/screen-13-imgui/Cargo.toml b/contrib/screen-13-imgui/Cargo.toml index 10bcbd9..5df1fbe 100644 --- a/contrib/screen-13-imgui/Cargo.toml +++ b/contrib/screen-13-imgui/Cargo.toml @@ -9,8 +9,8 @@ readme = "README.md" [dependencies] bytemuck = "1.14" -imgui = { git = "https://github.com/imgui-rs/imgui-rs", rev = "ca05418cb449dadaabf014487c5c965908dfcbdd" } -imgui-winit-support = { git = "https://github.com/imgui-rs/imgui-rs", rev = "ca05418cb449dadaabf014487c5c965908dfcbdd" } +imgui = "0.12" +imgui-winit-support = { git = "https://github.com/julcst/imgui-winit-support.git", rev = "29584c863c5517a7f30e09205b84e1601f4ddb92" } # TODO: https://github.com/imgui-rs/imgui-rs/issues/781 inline-spirv = "0.2" screen-13 = { path = "../.." } diff --git a/contrib/screen-13-imgui/src/lib.rs b/contrib/screen-13-imgui/src/lib.rs index db681fe..0237ce5 100644 --- a/contrib/screen-13-imgui/src/lib.rs +++ b/contrib/screen-13-imgui/src/lib.rs @@ -7,7 +7,10 @@ pub use imgui::{self, Condition, Ui}; use { bytemuck::cast_slice, imgui::{Context, DrawCmd, DrawCmdParams}, - imgui_winit_support::{HiDpiMode, WinitPlatform}, + imgui_winit_support::{ + winit::{event::Event, window::Window}, + {HiDpiMode, WinitPlatform}, + }, inline_spirv::include_spirv, screen_13::prelude::*, std::{sync::Arc, time::Duration}, @@ -216,20 +219,6 @@ impl ImGui { image } - pub fn draw_frame( - &mut self, - frame: &mut FrameContext<'_>, - ui_func: impl FnOnce(&mut Ui), - ) -> ImageLeaseNode { - self.draw( - frame.dt, - frame.events, - frame.window, - frame.render_graph, - ui_func, - ) - } - fn lease_font_atlas_image(&mut self, render_graph: &mut RenderGraph) { use imgui::{FontConfig, FontGlyphRanges, FontSource}; diff --git a/examples/imgui.rs b/examples/imgui.rs index 88a623e..6dc4359 100644 --- a/examples/imgui.rs +++ b/examples/imgui.rs @@ -4,26 +4,28 @@ use { screen_13::prelude::*, screen_13_fx::*, screen_13_imgui::{Condition, ImGui}, + screen_13_window::{Window, WindowError}, + winit::dpi::LogicalSize, }; -fn main() -> Result<(), DisplayError> { +fn main() -> Result<(), WindowError> { pretty_env_logger::init(); profile_with_puffin::init(); // Screen 13 things we need for this demo - let event_loop = EventLoop::new() - .desired_surface_format(Surface::linear_or_default) - .desired_swapchain_image_count(2) + let window = Window::builder() + .v_sync(false) + .window(|window| window.with_inner_size(LogicalSize::new(1024, 768))) .build()?; - let display = ComputePresenter::new(&event_loop.device)?; - let mut imgui = ImGui::new(&event_loop.device); - let mut pool = LazyPool::new(&event_loop.device); + let display = ComputePresenter::new(&window.device)?; + let mut imgui = ImGui::new(&window.device); + let mut pool = LazyPool::new(&window.device); // Some example state to make the demo more interesting let mut value = 0; let choices = ["test test this is 1", "test test this is 2"]; - event_loop.run(|mut frame| { + window.run(|frame| { // Lease and clear an image as a stand-in for some real game or program output let app_image = frame.render_graph.bind_node( pool.lease(ImageInfo::image_2d( @@ -41,27 +43,33 @@ fn main() -> Result<(), DisplayError> { .clear_color_image_value(app_image, [0.2, 0.22, 0.2, 1.0]); // Use the draw function callback to do some fun meant-for-debug-mode GUI stuff - let gui_image = imgui.draw_frame(&mut frame, |ui| { - ui.window("Hello world") - .position([10.0, 10.0], Condition::FirstUseEver) - .size([340.0, 250.0], Condition::FirstUseEver) - .build(|| { - ui.text_wrapped("Hello world!"); - ui.text_wrapped("γ“γ‚“γ«γ‘γ―δΈ–η•ŒοΌ"); - if ui.button(choices[value]) { - value += 1; - value %= 2; - } + let gui_image = imgui.draw( + 0.016, + frame.events, + frame.window, + frame.render_graph, + |ui| { + ui.window("Hello world") + .position([10.0, 10.0], Condition::FirstUseEver) + .size([340.0, 250.0], Condition::FirstUseEver) + .build(|| { + ui.text_wrapped("Hello world!"); + ui.text_wrapped("γ“γ‚“γ«γ‘γ―δΈ–η•ŒοΌ"); + if ui.button(choices[value]) { + value += 1; + value %= 2; + } - ui.button("This...is...imgui-rs!"); - ui.separator(); - let mouse_pos = ui.io().mouse_pos; - ui.text(format!( - "Mouse Position: ({:.1},{:.1})", - mouse_pos[0], mouse_pos[1] - )); - }); - }); + ui.button("This...is...imgui-rs!"); + ui.separator(); + let mouse_pos = ui.io().mouse_pos; + ui.text(format!( + "Mouse Position: ({:.1},{:.1})", + mouse_pos[0], mouse_pos[1] + )); + }); + }, + ); // Present "gui_image" on top of "app_image" onto "frame.swapchain" display.present_images( From 4f4dc1b1fc3d6e5a959629a646fe330d59ed0e64 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 11:15:00 -0400 Subject: [PATCH 09/21] Fix transitions example --- examples/transitions.rs | 54 +++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/examples/transitions.rs b/examples/transitions.rs index fb68b96..aecaab0 100644 --- a/examples/transitions.rs +++ b/examples/transitions.rs @@ -1,8 +1,8 @@ mod profile_with_puffin; use { - image::io::Reader, - screen_13::prelude::*, + image::ImageReader, + log::info, screen_13_fx::*, screen_13_imgui::prelude::*, screen_13_window::Window, @@ -27,7 +27,7 @@ fn main() -> anyhow::Result<()> { let bart_image = image_loader.decode_linear( 0, 0, - Reader::new(Cursor::new(include_bytes!("res/image/bart.jpg").as_slice())) + ImageReader::new(Cursor::new(include_bytes!("res/image/bart.jpg").as_slice())) .with_guessed_format()? .decode()? .into_rgb8() @@ -40,7 +40,7 @@ fn main() -> anyhow::Result<()> { let gulf_image = image_loader.decode_linear( 0, 0, - Reader::new(Cursor::new(include_bytes!("res/image/gulf.jpg").as_slice())) + ImageReader::new(Cursor::new(include_bytes!("res/image/gulf.jpg").as_slice())) .with_guessed_format()? .decode()? .into_rgb8() @@ -55,7 +55,7 @@ fn main() -> anyhow::Result<()> { let mut curr_transition_idx = 0; let mut start_time = Instant::now(); - window.run(|mut frame| { + window.run(|frame| { // Update the demo "state" let now = Instant::now(); let elapsed = (now - start_time).as_secs_f32(); @@ -87,26 +87,32 @@ fn main() -> anyhow::Result<()> { ); // Draw UI: TODO: Sliders and value setters? That would be fun. - let gui_image = imgui.draw_frame(&mut frame, |ui| { - ui.window("Transitions example") - .position([10.0, 10.0], Condition::FirstUseEver) - .size([340.0, 250.0], Condition::FirstUseEver) - .no_decoration() - .build(|| { - if ui.button("Next") { - curr_transition_idx += 1; - if curr_transition_idx == TRANSITIONS.len() { - curr_transition_idx = 0; - } + let gui_image = imgui.draw( + 0.016, + frame.events, + frame.window, + frame.render_graph, + |ui| { + ui.window("Transitions example") + .position([10.0, 10.0], Condition::FirstUseEver) + .size([340.0, 250.0], Condition::FirstUseEver) + .no_decoration() + .build(|| { + if ui.button("Next") { + curr_transition_idx += 1; + if curr_transition_idx == TRANSITIONS.len() { + curr_transition_idx = 0; + } - info!( - "{curr_transition_idx}: {:?}", - TRANSITIONS[curr_transition_idx] - ); - } - ui.text_wrapped(format!("{:?}", TRANSITIONS[curr_transition_idx])); - }); - }); + info!( + "{curr_transition_idx}: {:?}", + TRANSITIONS[curr_transition_idx] + ); + } + ui.text_wrapped(format!("{:?}", TRANSITIONS[curr_transition_idx])); + }); + }, + ); // Display the GUI + Blend images on screen display.present_images( From aa1d35b1c8e2d317a924270b72b7aded2617e33d Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 11:18:19 -0400 Subject: [PATCH 10/21] Comment out fuzzer example --- examples/fuzzer.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index e7cb24a..56c8507 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -21,6 +21,8 @@ Also helpful to run with valgrind: cargo build --example fuzzer && valgrind target/debug/examples/fuzzer */ +fn main() {} +/* NEEDS UPDATES TO LATEST CRATE CHANGES use { inline_spirv::inline_spirv, rand::{seq::SliceRandom, thread_rng}, @@ -319,17 +321,17 @@ fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { inline_spirv!( r#" #version 460 core - + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; - + layout(constant_id = 0) const uint LAYER_COUNT = 1; - + layout(push_constant) uniform PushConstants { layout(offset = 0) float offset; } push_const; - + layout(set = 0, binding = 0) uniform sampler2D layer_images_sampler_llr[LAYER_COUNT]; - + void main() { } "#, @@ -402,15 +404,15 @@ fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { r#" #version 460 core #extension GL_EXT_nonuniform_qualifier : require - + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; layout(push_constant) uniform PushConstants { layout(offset = 0) uint count; } push_const; - + layout(set = 0, binding = 0, rgba8) writeonly uniform image2D dst[]; - + void main() { for (uint idx = 0; idx < push_const.count; idx++) { imageStore( @@ -501,7 +503,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { inline_spirv!( r#" #version 460 core - + void main() { } "#, @@ -598,7 +600,7 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { inline_spirv!( r#" #version 460 core - + void main() { } "#, @@ -1094,3 +1096,4 @@ fn graphic_vert_frag_pipeline( ) }) } +*/ From 26fb2810477085a7f745a64c0a41ba3be62fe4b8 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 11:28:13 -0400 Subject: [PATCH 11/21] Fix screen-13-hot --- contrib/screen-13-hot/Cargo.toml | 2 ++ contrib/screen-13-hot/examples/glsl.rs | 17 ++++++++++------- contrib/screen-13-hot/examples/hlsl.rs | 15 ++++++++++----- contrib/screen-13-hot/src/compute.rs | 1 + contrib/screen-13-hot/src/graphic.rs | 1 + contrib/screen-13-hot/src/lib.rs | 1 + contrib/screen-13-hot/src/ray_trace.rs | 1 + contrib/screen-13-hot/src/shader.rs | 1 + 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/contrib/screen-13-hot/Cargo.toml b/contrib/screen-13-hot/Cargo.toml index a5ece15..4adb339 100644 --- a/contrib/screen-13-hot/Cargo.toml +++ b/contrib/screen-13-hot/Cargo.toml @@ -14,8 +14,10 @@ description = "Hot-reloading shader pipelines for Screen-13" [dependencies] anyhow = "1.0" derive_builder = "0.13" +log = "0.4" notify = "6.1" screen-13 = { path = "../.."} +screen-13-window = { path = "../screen-13-window" } shader-prepper = "0.3.0-pre.3" shaderc = "0.8" diff --git a/contrib/screen-13-hot/examples/glsl.rs b/contrib/screen-13-hot/examples/glsl.rs index a05bb63..af4704d 100644 --- a/contrib/screen-13-hot/examples/glsl.rs +++ b/contrib/screen-13-hot/examples/glsl.rs @@ -1,28 +1,31 @@ -use {screen_13::prelude::*, screen_13_hot::prelude::*, std::path::PathBuf}; +use { + screen_13::prelude::*, + screen_13_hot::prelude::*, + screen_13_window::{Window, WindowError}, + std::path::PathBuf, +}; /// This program draws a noise signal to the swapchain - make changes to fill_image.comp or the /// noise.glsl file it includes to see those changes update while the program is still running. /// /// Run with RUST_LOG=info to get notification of shader compilations. -fn main() -> Result<(), DisplayError> { +fn main() -> Result<(), WindowError> { pretty_env_logger::init(); - let event_loop = EventLoop::new() - .desired_surface_format(Surface::linear_or_default) - .build()?; + let window = Window::new()?; // Create a compute pipeline - the same as normal except for "Hot" prefixes and we provide the // shader source code path instead of the shader source code bytes let cargo_manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut pipeline = HotComputePipeline::create( - &event_loop.device, + &window.device, ComputePipelineInfo::default(), HotShader::new_compute(cargo_manifest_dir.join("examples/res/fill_image.comp")), )?; let mut frame_index: u32 = 0; - event_loop.run(|frame| { + window.run(|frame| { frame .render_graph .begin_pass("make some noise") diff --git a/contrib/screen-13-hot/examples/hlsl.rs b/contrib/screen-13-hot/examples/hlsl.rs index 0daa042..a4cfca3 100644 --- a/contrib/screen-13-hot/examples/hlsl.rs +++ b/contrib/screen-13-hot/examples/hlsl.rs @@ -1,20 +1,25 @@ -use {screen_13::prelude::*, screen_13_hot::prelude::*, std::path::PathBuf}; +use { + screen_13::prelude::*, + screen_13_hot::prelude::*, + screen_13_window::{Window, WindowError}, + std::path::PathBuf, +}; /// This program draws a noise signal to the swapchain - make changes to fill_image.hlsl or the /// noise.hlsl file it includes to see those changes update while the program is still running. /// /// Run with RUST_LOG=info to get notification of shader compilations. -fn main() -> Result<(), DisplayError> { +fn main() -> Result<(), WindowError> { pretty_env_logger::init(); - let event_loop = EventLoop::new().build()?; + let window = Window::new()?; // Create a graphic pipeline - the same as normal except for "Hot" prefixes and we provide the // shader source code path instead of the shader source code bytes let cargo_manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fill_image_path = cargo_manifest_dir.join("examples/res/fill_image.hlsl"); let mut pipeline = HotGraphicPipeline::create( - &event_loop.device, + &window.device, GraphicPipelineInfo::default(), [ HotShader::new_vertex(&fill_image_path).entry_name("vertex_main".to_string()), @@ -24,7 +29,7 @@ fn main() -> Result<(), DisplayError> { let mut frame_index: u32 = 0; - event_loop.run(|frame| { + window.run(|frame| { frame .render_graph .begin_pass("make some noise") diff --git a/contrib/screen-13-hot/src/compute.rs b/contrib/screen-13-hot/src/compute.rs index 413bb19..59258f7 100644 --- a/contrib/screen-13-hot/src/compute.rs +++ b/contrib/screen-13-hot/src/compute.rs @@ -1,5 +1,6 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, + log::info, notify::RecommendedWatcher, screen_13::prelude::*, std::sync::{ diff --git a/contrib/screen-13-hot/src/graphic.rs b/contrib/screen-13-hot/src/graphic.rs index c6e188c..92ca7b1 100644 --- a/contrib/screen-13-hot/src/graphic.rs +++ b/contrib/screen-13-hot/src/graphic.rs @@ -1,5 +1,6 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, + log::info, notify::RecommendedWatcher, screen_13::prelude::*, std::sync::{ diff --git a/contrib/screen-13-hot/src/lib.rs b/contrib/screen-13-hot/src/lib.rs index 5e19ade..0a856d9 100644 --- a/contrib/screen-13-hot/src/lib.rs +++ b/contrib/screen-13-hot/src/lib.rs @@ -14,6 +14,7 @@ pub mod prelude { use { self::shader::HotShader, + log::{error, info}, notify::{recommended_watcher, Event, EventKind, RecommendedWatcher}, screen_13::prelude::*, shader_prepper::{ diff --git a/contrib/screen-13-hot/src/ray_trace.rs b/contrib/screen-13-hot/src/ray_trace.rs index 71b7475..c7a8ab7 100644 --- a/contrib/screen-13-hot/src/ray_trace.rs +++ b/contrib/screen-13-hot/src/ray_trace.rs @@ -1,5 +1,6 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, + log::info, notify::RecommendedWatcher, screen_13::prelude::*, std::sync::{ diff --git a/contrib/screen-13-hot/src/shader.rs b/contrib/screen-13-hot/src/shader.rs index 5d8ce0b..b47fe25 100644 --- a/contrib/screen-13-hot/src/shader.rs +++ b/contrib/screen-13-hot/src/shader.rs @@ -3,6 +3,7 @@ pub use shaderc::{OptimizationLevel, SourceLanguage, SpirvVersion}; use { super::{compile_shader, guess_shader_source_language}, derive_builder::{Builder, UninitializedFieldError}, + log::{debug, error}, notify::{RecommendedWatcher, RecursiveMode, Watcher}, screen_13::prelude::*, shaderc::{CompileOptions, EnvVersion, ShaderKind, TargetEnv}, From faab69eecd452cc715f5c7d113fc3ab05c386a84 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 12:16:58 -0400 Subject: [PATCH 12/21] Fix shader-toy example --- examples/shader-toy/Cargo.toml | 3 ++- examples/shader-toy/src/main.rs | 45 +++++++++++---------------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/examples/shader-toy/Cargo.toml b/examples/shader-toy/Cargo.toml index 775630a..1fd967f 100644 --- a/examples/shader-toy/Cargo.toml +++ b/examples/shader-toy/Cargo.toml @@ -18,7 +18,8 @@ pak = "0.5" pretty_env_logger = "0.5" screen-13 = { path = "../.." } screen-13-fx = { path = "../../contrib/screen-13-fx" } -winit_input_helper = "0.16" +screen-13-window = { path = "../../contrib/screen-13-window" } +winit = "0.30" [build-dependencies] anyhow = "1.0" diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 624e7f8..d7e8f8f 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -38,21 +38,21 @@ use { pak::{Pak, PakBuf}, screen_13::prelude::*, screen_13_fx::*, + screen_13_window::Window, std::{sync::Arc, time::Instant}, - winit_input_helper::WinitInputHelper, + winit::dpi::PhysicalSize, }; fn main() -> anyhow::Result<()> { pretty_env_logger::init(); - let event_loop = EventLoop::new() - .desired_swapchain_image_count(3) - .window(|builder| builder.with_inner_size(LogicalSize::new(1280.0f64, 720.0f64))) - .build() - .context("Event loop")?; - let display = GraphicPresenter::new(&event_loop.device).context("Presenter")?; - let mut cache = LazyPool::new(&event_loop.device); - let mut image_loader = ImageLoader::new(&event_loop.device).context("Loader")?; + let window = Window::builder() + .desired_image_count(3) + .window(|builder| builder.with_inner_size(PhysicalSize::new(1280.0f64, 720.0f64))) + .build()?; + let display = GraphicPresenter::new(&window.device).context("Presenter")?; + let mut cache = LazyPool::new(&window.device); + let mut image_loader = ImageLoader::new(&window.device).context("Loader")?; // Load source images: PakBuf -> BitmapBuf -> ImageBinding (here) -> ImageNode (during loop) let mut data = data::open().context("Pak")?; @@ -93,7 +93,7 @@ fn main() -> anyhow::Result<()> { // one-sided let buffer_pipeline = Arc::new( GraphicPipeline::create( - &event_loop.device, + &window.device, GraphicPipelineInfo::default(), [ Shader::new_vertex(res::shader::QUAD_VERT), @@ -104,7 +104,7 @@ fn main() -> anyhow::Result<()> { ); let image_pipeline = Arc::new( GraphicPipeline::create( - &event_loop.device, + &window.device, GraphicPipelineInfo::default(), [ Shader::new_vertex(res::shader::QUAD_VERT), @@ -126,7 +126,7 @@ fn main() -> anyhow::Result<()> { .context("Blank image")?, ); - let (width, height) = (event_loop.width(), event_loop.height()); + let (width, height) = (1280, 720); let framebuffer_image = render_graph.bind_node( cache .lease(ImageInfo::image_2d( @@ -166,22 +166,17 @@ fn main() -> anyhow::Result<()> { render_graph.resolve().submit(&mut cache, 0, 0)?; let started_at = Instant::now(); - let mut input = WinitInputHelper::default(); let mut count = 0i32; let framebuffer_info = framebuffer_image_binding.as_ref().unwrap().info; let flowers_image_info = flowers_image_binding.as_ref().unwrap().info; let noise_image_info = noise_image_binding.as_ref().unwrap().info; let blank_image_info = blank_image_binding.as_ref().unwrap().info; - event_loop + window .run(|frame| { // Update the stuff any shader toy shader would want to know each frame let elapsed = Instant::now() - started_at; - for event in frame.events { - input.update(event); - } - count += 1; // Bind things to this graph (the graph will own our things until we unbind them) @@ -239,23 +234,13 @@ fn main() -> anyhow::Result<()> { } // Each pipeline gets the same constant data - let (cursor_x, cursor_y) = input - .mouse_held(MouseButton::Left) - .then(|| input.cursor()) - .flatten() - .unwrap_or_default(); let push_consts = PushConstants { resolution: [frame.width as f32, frame.height as _, 1.0], _pad_1: Default::default(), date: [1970.0, 1.0, 1.0, elapsed.as_secs_f32()], - mouse: [ - cursor_x, - cursor_y, - input.mouse_held(MouseButton::Left) as usize as f32, - input.mouse_held(MouseButton::Left) as usize as f32, - ], + mouse: [0.0, 0.0, 0.0, 0.0], time: elapsed.as_secs_f32(), - time_delta: frame.dt, + time_delta: 0.016, frame: count, sample_rate: 44100.0, channel_time: [ From 317f6a9f38a84785cbf979aaaec5de9cd8e1e638 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 12:19:22 -0400 Subject: [PATCH 13/21] Fix example lints --- examples/font_bmp.rs | 4 ++-- examples/multithread.rs | 4 ++-- examples/profile_with_puffin/mod.rs | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index 5d578f3..7fbdd31 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -2,7 +2,7 @@ mod profile_with_puffin; use { bmfont::{BMFont, OrdinateOrientation}, - image::io::Reader, + image::ImageReader, inline_spirv::inline_spirv, screen_13::prelude::*, screen_13_fx::*, @@ -30,7 +30,7 @@ fn main() -> anyhow::Result<()> { 0, small_10px_font, [( - Reader::new(Cursor::new( + ImageReader::new(Cursor::new( include_bytes!("res/font/small/small_10px_0.png").as_slice(), )) .with_guessed_format()? diff --git a/examples/multithread.rs b/examples/multithread.rs index d92decc..6a9717c 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -2,7 +2,7 @@ mod profile_with_puffin; use { bmfont::{BMFont, OrdinateOrientation}, - image::io::Reader, + image::ImageReader, log::info, screen_13::prelude::*, screen_13_fx::BitmapFont, @@ -219,7 +219,7 @@ fn load_font(device: &Arc) -> anyhow::Result { let temp_buf = Buffer::create_from_slice( device, vk::BufferUsageFlags::TRANSFER_SRC, - Reader::new(Cursor::new( + ImageReader::new(Cursor::new( include_bytes!("res/font/small/small_10px_0.png").as_slice(), )) .with_guessed_format()? diff --git a/examples/profile_with_puffin/mod.rs b/examples/profile_with_puffin/mod.rs index 78f3deb..e3e97c6 100644 --- a/examples/profile_with_puffin/mod.rs +++ b/examples/profile_with_puffin/mod.rs @@ -23,6 +23,7 @@ use { /// ```bash /// cargo install puffin_viewer /// ``` +#[allow(unused)] pub fn init() { #[cfg(feature = "profile-with-puffin")] { From bce2ce6b1cd1a212e1bc3bf3d18bc3f4470b7118 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 12:23:14 -0400 Subject: [PATCH 14/21] Fix skeletal-anim example --- examples/skeletal-anim/Cargo.toml | 1 + examples/skeletal-anim/src/main.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/skeletal-anim/Cargo.toml b/examples/skeletal-anim/Cargo.toml index 6e75994..d4512c5 100644 --- a/examples/skeletal-anim/Cargo.toml +++ b/examples/skeletal-anim/Cargo.toml @@ -12,6 +12,7 @@ glam = { version = "0.27", features = ["bytemuck"] } pak = "0.5" pretty_env_logger = "0.5" screen-13 = { path = "../.." } +screen-13-window = { path = "../../contrib/screen-13-window" } [build-dependencies] anyhow = "1.0" diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 46168ce..56f4dca 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -8,6 +8,7 @@ use { Pak, PakBuf, }, screen_13::prelude::*, + screen_13_window::{Window, WindowError}, std::{ cmp::Ordering, env::current_exe, @@ -20,14 +21,14 @@ use { // This blog has a really good overview of what is happening here: // https://vladh.net/game-engine-skeletal-animation -fn main() -> Result<(), DisplayError> { +fn main() -> Result<(), WindowError> { pretty_env_logger::init(); let pak_path = current_exe().unwrap().parent().unwrap().join("res.pak"); let mut pak = PakBuf::open(pak_path).unwrap(); - let event_loop = EventLoop::new().build()?; - let device = &event_loop.device; + let window = Window::new()?; + let device = &window.device; let pipeline = create_pipeline(device, &mut pak)?; let human_female = load_texture(device, &mut pak, "animated_characters_3/human_female")?; @@ -41,7 +42,7 @@ fn main() -> Result<(), DisplayError> { let mut pool = LazyPool::new(device); let started = Instant::now(); - event_loop.run(|frame| { + window.run(|frame| { let elapsed = (Instant::now() - started).as_secs_f32(); let index_buf = frame.render_graph.bind_node(&character.index_buf); @@ -95,7 +96,7 @@ fn main() -> Result<(), DisplayError> { t if t < 1.0 => &mut run, _ => &mut idle, }; - let joints = animation.update(frame.dt); + let joints = animation.update(0.016); let mut buf = pool .lease(BufferInfo::host_mem( size_of_val(joints) as _, From 4f3d8b3525eea3d7dd38541ede8a80489d91b424 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 12:27:41 -0400 Subject: [PATCH 15/21] Fix vr example --- examples/vr/Cargo.toml | 1 + examples/vr/src/driver/instance.rs | 20 +++++++++----------- examples/vr/src/main.rs | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/vr/Cargo.toml b/examples/vr/Cargo.toml index 7ad7aa3..b67bf1c 100644 --- a/examples/vr/Cargo.toml +++ b/examples/vr/Cargo.toml @@ -14,6 +14,7 @@ bytemuck = { version = "1.14", features = ["derive"] } ctrlc = "3.4" glam = { version = "0.27", features = ["bytemuck", "mint"] } image = "0.25" +log = "0.4" meshopt = "0.2" mikktspace = "0.3" mint = "0.5" diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index f5d0112..3ed1bff 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -1,15 +1,13 @@ use { + log::{debug, error}, openxr as xr, - screen_13::{ - driver::{ - ash::{ - self, - vk::{self, Handle as _}, - }, - device::Device, - physical_device::PhysicalDevice, + screen_13::driver::{ + ash::{ + self, + vk::{self, Handle as _}, }, - prelude::{debug, error}, + device::Device, + physical_device::PhysicalDevice, }, std::{ fmt::{Debug, Formatter}, @@ -119,8 +117,8 @@ impl Instance { return Err(InstanceCreateError::VulkanUnsupported); } - let app_info = vk::ApplicationInfo::builder().api_version(Self::VK_TARGET_VERSION); - let create_info = vk::InstanceCreateInfo::builder().application_info(&app_info); + let app_info = vk::ApplicationInfo::default().api_version(Self::VK_TARGET_VERSION); + let create_info = vk::InstanceCreateInfo::default().application_info(&app_info); unsafe { let vk_entry = ash::Entry::load().map_err(|err| { diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 54e9a80..a4f9c78 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -4,6 +4,7 @@ use { self::driver::{Instance, Swapchain}, bytemuck::{bytes_of, cast_slice, Pod, Zeroable}, glam::{vec3, vec4, Mat3, Mat4, Quat, Vec2, Vec3}, + log::{debug, error, trace}, meshopt::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, openxr::{self as xr, EnvironmentBlendMode, ViewConfigurationType}, screen_13::{ @@ -17,7 +18,6 @@ use { }, graph::RenderGraph, pool::{lazy::LazyPool, Pool as _}, - prelude::{debug, error, trace}, }, screen_13_hot::{graphic::HotGraphicPipeline, shader::HotShader}, std::{ From 05eaee11b729cbe9607b076e065c87e015c225ef Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 12:44:04 -0400 Subject: [PATCH 16/21] Fix doctest --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a3262de..6aaa053 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,15 +16,15 @@ using the provided [`FrameContext`] closure. The [`EventLoop`] builder handles c of the [`Device`] driver, however you may construct one manually for headless rendering. ```no_run -use screen_13::prelude::*; +use screen_13_window::{Window, WindowError}; -fn main() -> Result<(), DisplayError> { - let event_loop = EventLoop::new().build()?; +fn main() -> Result<(), WindowError> { + let window = Window::new()?; // Use the device to create resources and pipelines before running - let device = &event_loop.device; + let device = &window.device; - event_loop.run(|frame| { + window.run(|frame| { // You may also create resources and pipelines while running let device = &frame.device; }) From 7bccfeb57431bd29c1eefcd12d3b4b3b241be749 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 12:48:54 -0400 Subject: [PATCH 17/21] Improve rel-mgmt/check script --- contrib/rel-mgmt/check | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/contrib/rel-mgmt/check b/contrib/rel-mgmt/check index acf4d00..3d3e1bf 100755 --- a/contrib/rel-mgmt/check +++ b/contrib/rel-mgmt/check @@ -25,14 +25,25 @@ cargo fmt --manifest-path examples/skeletal-anim/Cargo.toml && diff || fail "Unf cargo fmt --manifest-path examples/vr/Cargo.toml && diff || fail "Unformatted rust code (vr)" # Rust code errors +echo "Checking screen-13" cargo check --all-targets +echo "Checking screen-13 (w/ parking_lot)" cargo check --all-targets --features parking_lot +echo "Checking contrib/screen-13-egui" cargo check --manifest-path contrib/screen-13-egui/Cargo.toml --all-targets --all-features +echo "Checking contrib/screen-13-fx" cargo check --manifest-path contrib/screen-13-fx/Cargo.toml --all-targets --all-features +echo "Checking contrib/screen-13-hot" cargo check --manifest-path contrib/screen-13-hot/Cargo.toml --all-targets --all-features +echo "Checking contrib/screen-13-imgui" cargo check --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features +echo "Checking contrib/screen-13-window" +cargo check --manifest-path contrib/screen-13-window/Cargo.toml --all-targets --all-features +echo "Checking examples/shader-toy" cargo check --manifest-path examples/shader-toy/Cargo.toml --all-targets --all-features +echo "Checking examples/skeletal-anim" cargo check --manifest-path examples/skeletal-anim/Cargo.toml --all-targets --all-features +echo "Checking examples/vr" cargo check --manifest-path examples/vr/Cargo.toml --all-targets --all-features # Rust code lints From bdf74e376c4e487fa16c4bfd606a9e993b054924 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 13:06:33 -0400 Subject: [PATCH 18/21] Fix apple platforms --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d2db2e7..d37ddf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ spirq = "1.2" vk-sync = { git = "https://github.com/attackgoat/vk-sync-rs.git", rev = "19fc3f811cc1d38b2231cdb8840fddf271879ac1", package = "vk-sync-fork" } #version = "0.4.0", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs [target.'cfg(target_os = "macos")'.dependencies] -ash-molten = "0.17" +ash-molten = "0.19" [dev-dependencies] anyhow = "1.0" From f36add7fdbf12900fc7f51e58bca333f4e619e0f Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 13:11:34 -0400 Subject: [PATCH 19/21] Skip imgui checks --- contrib/rel-mgmt/check | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/rel-mgmt/check b/contrib/rel-mgmt/check index 3d3e1bf..2bf3112 100755 --- a/contrib/rel-mgmt/check +++ b/contrib/rel-mgmt/check @@ -35,8 +35,8 @@ echo "Checking contrib/screen-13-fx" cargo check --manifest-path contrib/screen-13-fx/Cargo.toml --all-targets --all-features echo "Checking contrib/screen-13-hot" cargo check --manifest-path contrib/screen-13-hot/Cargo.toml --all-targets --all-features -echo "Checking contrib/screen-13-imgui" -cargo check --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features +#echo "Checking contrib/screen-13-imgui" +#cargo check --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features echo "Checking contrib/screen-13-window" cargo check --manifest-path contrib/screen-13-window/Cargo.toml --all-targets --all-features echo "Checking examples/shader-toy" @@ -52,7 +52,7 @@ cargo clippy --all-targets --features parking_lot cargo clippy --manifest-path contrib/screen-13-egui/Cargo.toml --all-targets --all-features cargo clippy --manifest-path contrib/screen-13-fx/Cargo.toml --all-targets --all-features cargo clippy --manifest-path contrib/screen-13-hot/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features +#cargo clippy --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features cargo clippy --manifest-path examples/shader-toy/Cargo.toml --all-targets --all-features cargo clippy --manifest-path examples/skeletal-anim/Cargo.toml --all-targets --all-features cargo clippy --manifest-path examples/vr/Cargo.toml --all-targets --all-features From 7c63ef17a529662a7e0392cc7239ba250277e206 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 13:43:00 -0400 Subject: [PATCH 20/21] Clippy lint --- examples/vr/src/driver/instance.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index 3ed1bff..dd45f00 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -10,6 +10,7 @@ use { physical_device::PhysicalDevice, }, std::{ + ffi::c_void, fmt::{Debug, Formatter}, mem::transmute, ops::Deref, @@ -126,7 +127,15 @@ impl Instance { InstanceCreateError::VulkanUnsupported })?; - let get_instance_proc_addr = transmute(vk_entry.static_fn().get_instance_proc_addr); + + let get_instance_proc_addr = { + type Fn = + unsafe extern "system" fn(T, *const i8) -> Option; + type AshFn = Fn; + type OpenXrFn = Fn<*const c_void>; + transmute::(vk_entry.static_fn().get_instance_proc_addr) + }; + let vk_instance = { let vk_instance = xr_instance .create_vulkan_instance( From a65fddee829406844b3062f9eb12a230f99e1641 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 29 Aug 2024 14:55:01 -0400 Subject: [PATCH 21/21] Fix run-all-examples script --- contrib/rel-mgmt/run-all-examples | 5 +++-- examples/skeletal-anim/Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/rel-mgmt/run-all-examples b/contrib/rel-mgmt/run-all-examples index 98afd74..4aa6a4f 100755 --- a/contrib/rel-mgmt/run-all-examples +++ b/contrib/rel-mgmt/run-all-examples @@ -5,6 +5,7 @@ set -e # Update everything cargo update cargo update --manifest-path contrib/screen-13-hot/Cargo.toml +cargo update --manifest-path contrib/screen-13-window/Cargo.toml cargo update --manifest-path examples/skeletal-anim/Cargo.toml cargo update --manifest-path examples/shader-toy/Cargo.toml cargo update --manifest-path examples/vr/Cargo.toml @@ -13,10 +14,10 @@ cargo update --manifest-path examples/vr/Cargo.toml cargo build --examples # Run the "test" example first -# cargo run --example fuzzer +cargo run --example fuzzer # Run all regular examples, in debug mode, next -cargo run --example hello_world +cargo run --manifest-path contrib/screen-13-window/Cargo.toml --example hello_world cargo run --example aliasing cargo run --example cpu_readback cargo run --example subgroup_ops diff --git a/examples/skeletal-anim/Cargo.toml b/examples/skeletal-anim/Cargo.toml index d4512c5..7e5d532 100644 --- a/examples/skeletal-anim/Cargo.toml +++ b/examples/skeletal-anim/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" [dependencies] bytemuck = "1.14" glam = { version = "0.27", features = ["bytemuck"] } -pak = "0.5" +pak = "=0.5.0" pretty_env_logger = "0.5" screen-13 = { path = "../.." } screen-13-window = { path = "../../contrib/screen-13-window" } @@ -17,6 +17,6 @@ screen-13-window = { path = "../../contrib/screen-13-window" } [build-dependencies] anyhow = "1.0" log = "0.4" -pak = { version = "0.5", features = ["bake"] } +pak = { version = "=0.5.0", features = ["bake"] } shaderc = "0.8" simplelog = "0.12"