From 465bf4b07084c6ad09566d9e36b8bb9f581ad3cd Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:40:13 +0200 Subject: [PATCH] Task graph [7/10]: descriptor sets --- Cargo.lock | 5 +- Cargo.toml | 2 +- examples/async-update/main.rs | 236 +-- examples/bloom/bloom.rs | 46 +- examples/bloom/downsample.glsl | 17 +- examples/bloom/main.rs | 255 ++- examples/bloom/scene.rs | 16 +- examples/bloom/tonemap.glsl | 8 +- examples/bloom/tonemap.rs | 33 +- examples/bloom/upsample.glsl | 20 +- examples/deferred/Cargo.toml | 1 + examples/deferred/deferred.rs | 742 +++++++++ .../deferred/frame/ambient_lighting_system.rs | 252 --- .../frame/directional_lighting_system.rs | 279 ---- examples/deferred/frame/mod.rs | 19 - .../deferred/frame/point_lighting_system.rs | 312 ---- examples/deferred/frame/system.rs | 578 ------- examples/deferred/main.rs | 479 ++++-- examples/deferred/scene.rs | 207 +++ examples/deferred/triangle_draw_system.rs | 199 --- examples/ray-tracing/main.rs | 145 +- examples/ray-tracing/rgen.glsl | 62 +- examples/ray-tracing/scene.rs | 195 +-- include | 1 + vulkano-shaders/include/vulkano.glsl | 1466 +++++++++++++++++ vulkano-shaders/src/codegen.rs | 7 + vulkano-shaders/src/structs.rs | 33 +- vulkano-taskgraph/Cargo.toml | 1 + .../src/command_buffer/commands/bind_push.rs | 38 +- vulkano-taskgraph/src/command_buffer/mod.rs | 169 +- vulkano-taskgraph/src/descriptor_set.rs | 1109 +++++++++++++ vulkano-taskgraph/src/graph/compile.rs | 4 +- vulkano-taskgraph/src/graph/execute.rs | 112 +- vulkano-taskgraph/src/lib.rs | 3 +- vulkano-taskgraph/src/resource.rs | 274 ++- 35 files changed, 4832 insertions(+), 2493 deletions(-) create mode 100644 examples/deferred/deferred.rs delete mode 100644 examples/deferred/frame/ambient_lighting_system.rs delete mode 100644 examples/deferred/frame/directional_lighting_system.rs delete mode 100644 examples/deferred/frame/mod.rs delete mode 100644 examples/deferred/frame/point_lighting_system.rs delete mode 100644 examples/deferred/frame/system.rs create mode 100644 examples/deferred/scene.rs delete mode 100644 examples/deferred/triangle_draw_system.rs create mode 120000 include create mode 100644 vulkano-shaders/include/vulkano.glsl create mode 100644 vulkano-taskgraph/src/descriptor_set.rs diff --git a/Cargo.lock b/Cargo.lock index b8897d9300..096182c375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,8 +298,7 @@ dependencies = [ [[package]] name = "concurrent-slotmap" version = "0.1.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288086ecfcd80978f795a7ba8ffd9777d6dfcb6e5c6c74bba10e3d9a1eb52169" +source = "git+https://github.com/vulkano-rs/concurrent-slotmap?rev=db6810ab699b99fd79052252c057851fa80117cf#db6810ab699b99fd79052252c057851fa80117cf" dependencies = [ "virtual-buffer", ] @@ -394,6 +393,7 @@ dependencies = [ "glam", "vulkano", "vulkano-shaders", + "vulkano-taskgraph", "winit", ] @@ -1881,6 +1881,7 @@ name = "vulkano-taskgraph" version = "0.35.0" dependencies = [ "ash", + "bytemuck", "concurrent-slotmap", "foldhash", "parking_lot", diff --git a/Cargo.toml b/Cargo.toml index 9a3839283d..9ea9484a37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ path = "vulkano-util" # https://github.com/KhronosGroup/Vulkan-Headers/commits/main/registry/vk.xml ash = "0.38.0" bytemuck = "1.9" -concurrent-slotmap = "0.1.0-alpha.1" +concurrent-slotmap = { git = "https://github.com/vulkano-rs/concurrent-slotmap", rev = "db6810ab699b99fd79052252c057851fa80117cf" } crossbeam-queue = "0.3" foldhash = "0.1" half = "2.0" diff --git a/examples/async-update/main.rs b/examples/async-update/main.rs index 6f8a30a487..1545548cc1 100644 --- a/examples/async-update/main.rs +++ b/examples/async-update/main.rs @@ -1,27 +1,13 @@ // This example showcases how you can most effectively update a resource asynchronously, such that -// your rendering or any other tasks can use the resource without any latency at the same time as -// it's being updated. +// it's being updated. The resource being updated asynchronously here is a large texture, which +// needs to be updated partially at the request of the user. // -// There are two kinds of resources that are updated asynchronously here: -// -// - A uniform buffer, which needs to be updated every frame. -// - A large texture, which needs to be updated partially at the request of the user. -// -// For the first, since the data needs to be updated every frame, we have to use one buffer per -// frame in flight. The swapchain most commonly has multiple images that are all processed at the -// same time, therefore writing the same buffer during each frame in flight would result in one of -// two things: either you would have to synchronize the writes from the host and reads from the -// device such that only one of the images in the swapchain is actually processed at any point in -// time (bad), or a race condition (bad). Therefore we are left with no choice but to use a -// different buffer for each frame in flight. This is best suited to very small pieces of data that -// change rapidly, and where the data of one frame doesn't depend on data from a previous one. -// -// For the second, since this texture is rather large, we can't afford to overwrite the entire -// texture every time a part of it needs to be updated. Also, we don't need as many textures as -// there are frames in flight since the texture doesn't need to be updated every frame, but we -// still need at least two textures. That way we can write one of the textures at the same time as -// reading the other, swapping them after the write is done such that the newly updated one is read -// and the now out-of-date one can be written to next time, known as *eventual consistency*. +// Since this texture is rather large, we can't afford to overwrite the entire texture every time a +// part of it needs to be updated. Also, we don't need as many textures as there are frames in +// flight since the texture doesn't need to be updated every frame, but we still need at least two +// textures. That way we can write one of the textures at the same time as reading the other, +// swapping them after the write is done such that the newly updated one is read and the now +// out-of-date one can be written to next time, known as *eventual consistency*. // // In an eventually consistent system, a number of *replicas* are used, all of which represent the // same data but their consistency is not strict. A replica might be out-of-date for some time @@ -41,18 +27,14 @@ use std::{ }; use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, - }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, }, format::Format, image::{ - sampler::{Sampler, SamplerCreateInfo}, - view::ImageView, - Image, ImageAspects, ImageCreateInfo, ImageSubresourceLayers, ImageType, ImageUsage, + sampler::SamplerCreateInfo, view::ImageViewCreateInfo, Image, ImageAspects, + ImageCreateInfo, ImageLayout, ImageSubresourceLayers, ImageType, ImageUsage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter}, @@ -66,9 +48,7 @@ use vulkano::{ viewport::{Viewport, ViewportState}, GraphicsPipelineCreateInfo, }, - layout::PipelineDescriptorSetLayoutCreateInfo, - DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, - PipelineShaderStageCreateInfo, + DynamicState, GraphicsPipeline, Pipeline, PipelineShaderStageCreateInfo, }, swapchain::{Surface, Swapchain, SwapchainCreateInfo}, sync::Sharing, @@ -78,8 +58,11 @@ use vulkano_taskgraph::{ command_buffer::{ BufferImageCopy, ClearColorImageInfo, CopyBufferToImageInfo, RecordingCommandBuffer, }, + descriptor_set::{BindlessContext, SampledImageId, SamplerId}, graph::{AttachmentInfo, CompileInfo, ExecutableTaskGraph, ExecuteError, TaskGraph}, - resource::{AccessTypes, Flight, HostAccessType, ImageLayoutType, Resources}, + resource::{ + AccessTypes, Flight, HostAccessType, ImageLayoutType, Resources, ResourcesCreateInfo, + }, resource_map, ClearValues, Id, QueueFamilyType, Task, TaskContext, TaskResult, }; use winit::{ @@ -111,7 +94,6 @@ struct App { resources: Arc, graphics_flight_id: Id, vertex_buffer_id: Id, - uniform_buffer_ids: [Id; MAX_FRAMES_IN_FLIGHT as usize], texture_ids: [Id; 2], current_texture_index: Arc, channel: mpsc::Sender<()>, @@ -126,7 +108,6 @@ struct RenderContext { task_graph: ExecutableTaskGraph, virtual_swapchain_id: Id, virtual_texture_id: Id, - virtual_uniform_buffer_id: Id, } impl App { @@ -145,12 +126,16 @@ impl App { let device_extensions = DeviceExtensions { khr_swapchain: true, - ..DeviceExtensions::empty() + ..BindlessContext::required_extensions(&instance) }; + let device_features = BindlessContext::required_features(&instance); let (physical_device, graphics_family_index) = instance .enumerate_physical_devices() .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter(|p| { + p.supported_extensions().contains(&device_extensions) + && p.supported_features().contains(&device_features) + }) .filter_map(|p| { p.queue_family_properties() .iter() @@ -240,6 +225,7 @@ impl App { physical_device, DeviceCreateInfo { enabled_extensions: device_extensions, + enabled_features: device_features, queue_create_infos, ..Default::default() }, @@ -258,7 +244,14 @@ impl App { {transfer_family_index} for transfers", ); - let resources = Resources::new(&device, &Default::default()); + let resources = Resources::new( + &device, + &ResourcesCreateInfo { + bindless_context: Some(&Default::default()), + ..Default::default() + }, + ) + .unwrap(); let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); let transfer_flight_id = resources.create_flight(1).unwrap(); @@ -292,25 +285,6 @@ impl App { ) .unwrap(); - // Create a pool of uniform buffers, one per frame in flight. This way we always have an - // available buffer to write during each frame while reusing them as much as possible. - let uniform_buffer_ids = [(); MAX_FRAMES_IN_FLIGHT as usize].map(|_| { - resources - .create_buffer( - BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - DeviceLayout::new_sized::(), - ) - .unwrap() - }); - // Create two textures, where at any point in time one is used exclusively for reading and // one is used exclusively for writing, swapping the two after each update. let texture_ids = [(); 2].map(|_| { @@ -402,7 +376,6 @@ impl App { resources, graphics_flight_id, vertex_buffer_id, - uniform_buffer_ids, texture_ids, current_texture_index, channel, @@ -413,6 +386,8 @@ impl App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let bcx = self.resources.bindless_context().unwrap(); + let window = Arc::new( event_loop .create_window(Window::default_attributes()) @@ -467,13 +442,7 @@ impl ApplicationHandler for App { PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; - let layout = PipelineLayout::new( - self.device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(self.device.clone()) - .unwrap(), - ) - .unwrap(); + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); let viewport = Viewport { offset: [0.0, 0.0], @@ -481,52 +450,24 @@ impl ApplicationHandler for App { depth_range: 0.0..=1.0, }; - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - self.device.clone(), - Default::default(), - )); - - // A byproduct of always using the same set of uniform buffers is that we can also create - // one descriptor set for each, reusing them in the same way as the buffers. - let uniform_buffer_sets = self.uniform_buffer_ids.map(|buffer_id| { - let buffer_state = self.resources.buffer(buffer_id).unwrap(); - let buffer = buffer_state.buffer(); - - DescriptorSet::new( - descriptor_set_allocator.clone(), - layout.set_layouts()[0].clone(), - [WriteDescriptorSet::buffer(0, buffer.clone().into())], - [], - ) - .unwrap() - }); - - // Create the descriptor sets for sampling the textures. - let sampler = Sampler::new( - self.device.clone(), - SamplerCreateInfo::simple_repeat_linear(), - ) - .unwrap(); - let sampler_sets = self.texture_ids.map(|texture_id| { + let sampler_id = bcx + .global_set() + .create_sampler(SamplerCreateInfo::simple_repeat_linear()) + .unwrap(); + let sampled_image_ids = self.texture_ids.map(|texture_id| { let texture_state = self.resources.image(texture_id).unwrap(); let texture = texture_state.image(); - DescriptorSet::new( - descriptor_set_allocator.clone(), - layout.set_layouts()[1].clone(), - [ - WriteDescriptorSet::sampler(0, sampler.clone()), - WriteDescriptorSet::image_view( - 1, - ImageView::new_default(texture.clone()).unwrap(), - ), - ], - [], - ) - .unwrap() + bcx.global_set() + .create_sampled_image( + texture_id, + ImageViewCreateInfo::from_image(texture), + ImageLayout::ShaderReadOnlyOptimal, + ) + .unwrap() }); - let mut task_graph = TaskGraph::new(&self.resources, 1, 4); + let mut task_graph = TaskGraph::new(&self.resources, 1, 3); let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo { image_format: swapchain_format, @@ -544,11 +485,8 @@ impl ApplicationHandler for App { }, ..Default::default() }); - let virtual_uniform_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default()); let virtual_framebuffer_id = task_graph.add_framebuffer(); - task_graph.add_host_buffer_access(virtual_uniform_buffer_id, HostAccessType::Write); - let render_node_id = task_graph .create_task_node( "Render", @@ -558,9 +496,8 @@ impl ApplicationHandler for App { vertex_buffer_id: self.vertex_buffer_id, current_texture_index: self.current_texture_index.clone(), pipeline: None, - uniform_buffer_id: virtual_uniform_buffer_id, - uniform_buffer_sets, - sampler_sets, + sampler_id, + sampled_image_ids, }, ) .framebuffer(virtual_framebuffer_id) @@ -579,10 +516,6 @@ impl ApplicationHandler for App { AccessTypes::FRAGMENT_SHADER_SAMPLED_READ, ImageLayoutType::Optimal, ) - .buffer_access( - virtual_uniform_buffer_id, - AccessTypes::VERTEX_SHADER_UNIFORM_READ, - ) .build(); let mut task_graph = unsafe { @@ -638,7 +571,6 @@ impl ApplicationHandler for App { task_graph, virtual_swapchain_id, virtual_texture_id, - virtual_uniform_buffer_id, }); } @@ -689,14 +621,12 @@ impl ApplicationHandler for App { rcx.recreate_swapchain = false; } - let frame_index = flight.current_frame_index(); let texture_index = self.current_texture_index.load(Ordering::Relaxed); let resource_map = resource_map!( &rcx.task_graph, rcx.virtual_swapchain_id => rcx.swapchain_id, rcx.virtual_texture_id => self.texture_ids[texture_index as usize], - rcx.virtual_uniform_buffer_id => self.uniform_buffer_ids[frame_index as usize], ) .unwrap(); @@ -740,12 +670,15 @@ mod vs { ty: "vertex", src: r" #version 450 + #include layout(location = 0) in vec2 position; layout(location = 0) out vec2 tex_coords; - layout(set = 0, binding = 0) uniform Data { + layout(push_constant) uniform PushConstants { mat4 transform; + SamplerId sampler_id; + SampledImageId texture_id; }; void main() { @@ -761,15 +694,19 @@ mod fs { ty: "fragment", src: r" #version 450 + #include layout(location = 0) in vec2 tex_coords; layout(location = 0) out vec4 f_color; - layout(set = 1, binding = 0) uniform sampler s; - layout(set = 1, binding = 1) uniform texture2D tex; + layout(push_constant) uniform PushConstants { + mat4 transform; + SamplerId sampler_id; + SampledImageId texture_id; + }; void main() { - f_color = texture(sampler2D(tex, s), tex_coords); + f_color = texture(vko_sampler2D(texture_id, sampler_id), tex_coords); } ", } @@ -780,9 +717,8 @@ struct RenderTask { vertex_buffer_id: Id, current_texture_index: Arc, pipeline: Option>, - uniform_buffer_id: Id, - uniform_buffer_sets: [Arc; MAX_FRAMES_IN_FLIGHT as usize], - sampler_sets: [Arc; 2], + sampler_id: SamplerId, + sampled_image_ids: [SampledImageId; 2], } impl Task for RenderTask { @@ -795,52 +731,42 @@ impl Task for RenderTask { unsafe fn execute( &self, cbf: &mut RecordingCommandBuffer<'_>, - tcx: &mut TaskContext<'_>, + _tcx: &mut TaskContext<'_>, rcx: &Self::World, ) -> TaskResult { - let frame_index = tcx.current_frame_index(); - - // Write to the uniform buffer designated for this frame. - *tcx.write_buffer(self.uniform_buffer_id, ..)? = vs::Data { - transform: { - const DURATION: f64 = 5.0; - - let elapsed = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs_f64(); - let remainder = elapsed.rem_euclid(DURATION); - let delta = (remainder / DURATION) as f32; - let angle = delta * std::f32::consts::PI * 2.0; - - Mat4::from_rotation_z(angle).to_cols_array_2d() - }, + let transform = { + const DURATION: f64 = 5.0; + + let elapsed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs_f64(); + let remainder = elapsed.rem_euclid(DURATION); + let delta = (remainder / DURATION) as f32; + let angle = delta * std::f32::consts::PI * 2.0; + + Mat4::from_rotation_z(angle).to_cols_array_2d() }; let pipeline = self.pipeline.as_ref().unwrap(); cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?; cbf.bind_pipeline_graphics(pipeline)?; - cbf.as_raw().bind_descriptor_sets( - PipelineBindPoint::Graphics, + cbf.push_constants( pipeline.layout(), 0, - &[ - // Bind the uniform buffer designated for this frame. - self.uniform_buffer_sets[frame_index as usize].as_raw(), + &fs::PushConstants { + transform, + sampler_id: self.sampler_id, // Bind the currently most up-to-date texture. - self.sampler_sets[self.current_texture_index.load(Ordering::Relaxed) as usize] - .as_raw(), - ], - &[], + texture_id: self.sampled_image_ids + [self.current_texture_index.load(Ordering::Relaxed) as usize], + }, )?; cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?; unsafe { cbf.draw(4, 1, 0, 0) }?; - cbf.destroy_objects(self.uniform_buffer_sets.iter().cloned()); - cbf.destroy_objects(self.sampler_sets.iter().cloned()); - Ok(()) } } diff --git a/examples/bloom/bloom.rs b/examples/bloom/bloom.rs index d4894f3a12..0926a187df 100644 --- a/examples/bloom/bloom.rs +++ b/examples/bloom/bloom.rs @@ -1,9 +1,10 @@ use crate::{App, RenderContext}; +use core::slice; use std::sync::Arc; use vulkano::{ image::{mip_level_extent, Image}, pipeline::{ - compute::ComputePipelineCreateInfo, ComputePipeline, PipelineBindPoint, PipelineLayout, + compute::ComputePipelineCreateInfo, ComputePipeline, Pipeline, PipelineShaderStageCreateInfo, }, sync::{AccessFlags, PipelineStages}, @@ -24,22 +25,23 @@ pub struct BloomTask { } impl BloomTask { - pub fn new( - app: &App, - pipeline_layout: &Arc, - virtual_bloom_image_id: Id, - ) -> Self { + pub fn new(app: &App, virtual_bloom_image_id: Id) -> Self { + let bcx = app.resources.bindless_context().unwrap(); + let downsample_pipeline = { let cs = downsample::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); let stage = PipelineShaderStageCreateInfo::new(cs); + let layout = bcx + .pipeline_layout_from_stages(slice::from_ref(&stage)) + .unwrap(); ComputePipeline::new( app.device.clone(), None, - ComputePipelineCreateInfo::new(stage, pipeline_layout.clone()), + ComputePipelineCreateInfo::new(stage, layout), ) .unwrap() }; @@ -50,11 +52,14 @@ impl BloomTask { .entry_point("main") .unwrap(); let stage = PipelineShaderStageCreateInfo::new(cs); + let layout = bcx + .pipeline_layout_from_stages(slice::from_ref(&stage)) + .unwrap(); ComputePipeline::new( app.device.clone(), None, - ComputePipelineCreateInfo::new(stage, pipeline_layout.clone()), + ComputePipelineCreateInfo::new(stage, layout), ) .unwrap() }; @@ -76,14 +81,6 @@ impl Task for BloomTask { tcx: &mut TaskContext<'_>, rcx: &Self::World, ) -> TaskResult { - cbf.as_raw().bind_descriptor_sets( - PipelineBindPoint::Compute, - &rcx.pipeline_layout, - 0, - &[rcx.descriptor_set.as_raw()], - &[], - )?; - let bloom_image = tcx.image(self.bloom_image_id)?.image(); let dependency_info = DependencyInfo { @@ -102,12 +99,15 @@ impl Task for BloomTask { for src_mip_level in 0..bloom_image.mip_levels() - 1 { let dst_mip_level = src_mip_level + 1; let dst_extent = mip_level_extent(bloom_image.extent(), dst_mip_level).unwrap(); - let group_counts = dst_extent.map(|c| (c + 7) / 8); + let group_counts = dst_extent.map(|c| c.div_ceil(8)); cbf.push_constants( - &rcx.pipeline_layout, + self.downsample_pipeline.layout(), 0, &downsample::PushConstants { + sampler_id: rcx.bloom_sampler_id, + texture_id: rcx.bloom_sampled_image_id, + dst_mip_image_id: rcx.bloom_storage_image_ids[dst_mip_level as usize], dst_mip_level, threshold: THRESHOLD, knee: KNEE, @@ -123,12 +123,15 @@ impl Task for BloomTask { for dst_mip_level in (0..bloom_image.mip_levels() - 1).rev() { let dst_extent = mip_level_extent(bloom_image.extent(), dst_mip_level).unwrap(); - let group_counts = dst_extent.map(|c| (c + 7) / 8); + let group_counts = dst_extent.map(|c| c.div_ceil(8)); cbf.push_constants( - &rcx.pipeline_layout, + self.upsample_pipeline.layout(), 0, &upsample::PushConstants { + sampler_id: rcx.bloom_sampler_id, + texture_id: rcx.bloom_sampled_image_id, + dst_mip_image_id: rcx.bloom_storage_image_ids[dst_mip_level as usize], dst_mip_level, intensity: INTENSITY, }, @@ -141,9 +144,6 @@ impl Task for BloomTask { } } - cbf.destroy_object(bloom_image.clone()); - cbf.destroy_object(rcx.descriptor_set.clone()); - Ok(()) } } diff --git a/examples/bloom/downsample.glsl b/examples/bloom/downsample.glsl index 99fb723a50..1d646e519b 100644 --- a/examples/bloom/downsample.glsl +++ b/examples/bloom/downsample.glsl @@ -1,16 +1,15 @@ #version 450 +#include #include -const uint MAX_BLOOM_MIP_LEVELS = 6; const float EPSILON = 1.19209290e-07; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; -layout(set = 0, binding = 0) uniform sampler bloom_sampler; -layout(set = 0, binding = 1) uniform texture2D bloom_texture; -layout(set = 0, binding = 2, r32ui) uniform uimage2D bloom_mip_chain[MAX_BLOOM_MIP_LEVELS]; - layout(push_constant) uniform PushConstants { + SamplerId sampler_id; + SampledImageId texture_id; + StorageImageId dst_mip_image_id; uint dst_mip_level; float threshold; float knee; @@ -33,7 +32,7 @@ vec3 prefilter(vec3 color) { } vec3 sample1(vec2 uv) { - return textureLod(sampler2D(bloom_texture, bloom_sampler), uv, src_mip_level).rgb; + return textureLod(vko_sampler2D(texture_id, sampler_id), uv, src_mip_level).rgb; } // 13-tap box filter. @@ -71,18 +70,18 @@ vec3 downsampleBox13(vec2 uv, vec2 src_texel_size) { void store(ivec2 dst_coord, vec3 color) { uint packed = convertToSharedExponent(color); - imageStore(bloom_mip_chain[dst_mip_level], dst_coord, uvec4(packed, 0, 0, 0)); + imageStore(vko_uimage2D_r32ui(dst_mip_image_id), dst_coord, uvec4(packed, 0, 0, 0)); } void main() { ivec2 dst_coord = ivec2(gl_GlobalInvocationID.xy); - ivec2 dst_size = imageSize(bloom_mip_chain[dst_mip_level]); + ivec2 dst_size = imageSize(vko_uimage2D_r32ui(dst_mip_image_id)); if (dst_coord.x > dst_size.x || dst_coord.y > dst_size.y) { return; } - ivec2 src_size = textureSize(sampler2D(bloom_texture, bloom_sampler), int(src_mip_level)); + ivec2 src_size = textureSize(vko_texture2D(texture_id), int(src_mip_level)); vec2 src_texel_size = 1.0 / vec2(src_size); vec2 uv = (vec2(dst_coord) + 0.5) / vec2(dst_size); vec3 color = downsampleBox13(uv, src_texel_size); diff --git a/examples/bloom/main.rs b/examples/bloom/main.rs index aff931b88d..d1abe79ec1 100644 --- a/examples/bloom/main.rs +++ b/examples/bloom/main.rs @@ -2,17 +2,9 @@ use bloom::BloomTask; use scene::SceneTask; -use std::{cmp, error::Error, sync::Arc}; +use std::{array, cmp, error::Error, sync::Arc}; use tonemap::TonemapTask; use vulkano::{ - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, - layout::{ - DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, - DescriptorType, - }, - DescriptorImageViewInfo, DescriptorSet, WriteDescriptorSet, - }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceOwned, Queue, QueueCreateInfo, QueueFlags, @@ -20,25 +12,21 @@ use vulkano::{ format::{Format, NumericFormat}, image::{ max_mip_levels, - sampler::{Filter, Sampler, SamplerCreateInfo, SamplerMipmapMode, LOD_CLAMP_NONE}, - view::{ImageView, ImageViewCreateInfo}, + sampler::{Filter, SamplerCreateInfo, SamplerMipmapMode, LOD_CLAMP_NONE}, + view::ImageViewCreateInfo, Image, ImageAspects, ImageCreateFlags, ImageCreateInfo, ImageLayout, ImageSubresourceRange, ImageType, ImageUsage, }, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, memory::allocator::AllocationCreateInfo, - pipeline::{ - graphics::viewport::Viewport, - layout::{PipelineLayoutCreateInfo, PushConstantRange}, - PipelineLayout, - }, - shader::ShaderStages, + pipeline::graphics::viewport::Viewport, swapchain::{ColorSpace, Surface, Swapchain, SwapchainCreateInfo}, Validated, Version, VulkanError, VulkanLibrary, }; use vulkano_taskgraph::{ + descriptor_set::{BindlessContext, SampledImageId, SamplerId, StorageImageId}, graph::{AttachmentInfo, CompileInfo, ExecutableTaskGraph, ExecuteError, TaskGraph}, - resource::{AccessTypes, Flight, ImageLayoutType, Resources}, + resource::{AccessTypes, Flight, ImageLayoutType, Resources, ResourcesCreateInfo}, resource_map, Id, QueueFamilyType, }; use winit::{ @@ -76,11 +64,10 @@ pub struct RenderContext { swapchain_id: Id, bloom_image_id: Id, viewport: Viewport, - pipeline_layout: Arc, recreate_swapchain: bool, - descriptor_set_allocator: Arc, - sampler: Arc, - descriptor_set: Arc, + bloom_sampler_id: SamplerId, + bloom_sampled_image_id: SampledImageId, + bloom_storage_image_ids: [StorageImageId; MAX_BLOOM_MIP_LEVELS as usize], task_graph: ExecutableTaskGraph, virtual_swapchain_id: Id, virtual_bloom_image_id: Id, @@ -102,15 +89,19 @@ impl App { let mut device_extensions = DeviceExtensions { khr_swapchain: true, - ..DeviceExtensions::empty() + ..BindlessContext::required_extensions(&instance) }; + let device_features = BindlessContext::required_features(&instance); let (physical_device, queue_family_index) = instance .enumerate_physical_devices() .unwrap() .filter(|p| { p.api_version() >= Version::V1_1 || p.supported_extensions().khr_maintenance2 }) - .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter(|p| { + p.supported_extensions().contains(&device_extensions) + && p.supported_features().contains(&device_features) + }) .filter_map(|p| { p.queue_family_properties() .iter() @@ -152,6 +143,7 @@ impl App { physical_device, DeviceCreateInfo { enabled_extensions: device_extensions, + enabled_features: device_features, queue_create_infos: vec![QueueCreateInfo { queue_family_index, ..Default::default() @@ -163,7 +155,14 @@ impl App { let queue = queues.next().unwrap(); - let resources = Resources::new(&device, &Default::default()); + let resources = Resources::new( + &device, + &ResourcesCreateInfo { + bindless_context: Some(&Default::default()), + ..Default::default() + }, + ) + .unwrap(); let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); @@ -180,6 +179,8 @@ impl App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let bcx = self.resources.bindless_context().unwrap(); + let window = Arc::new( event_loop .create_window(Window::default_attributes()) @@ -233,76 +234,19 @@ impl ApplicationHandler for App { depth_range: 0.0..=1.0, }; - let pipeline_layout = PipelineLayout::new( - self.device.clone(), - PipelineLayoutCreateInfo { - set_layouts: vec![DescriptorSetLayout::new( - self.device.clone(), - DescriptorSetLayoutCreateInfo { - bindings: [ - ( - 0, - DescriptorSetLayoutBinding { - stages: ShaderStages::FRAGMENT | ShaderStages::COMPUTE, - ..DescriptorSetLayoutBinding::new(DescriptorType::Sampler) - }, - ), - ( - 1, - DescriptorSetLayoutBinding { - stages: ShaderStages::FRAGMENT | ShaderStages::COMPUTE, - ..DescriptorSetLayoutBinding::new(DescriptorType::SampledImage) - }, - ), - ( - 2, - DescriptorSetLayoutBinding { - stages: ShaderStages::COMPUTE, - descriptor_count: MAX_BLOOM_MIP_LEVELS, - ..DescriptorSetLayoutBinding::new(DescriptorType::StorageImage) - }, - ), - ] - .into_iter() - .collect(), - ..Default::default() - }, - ) - .unwrap()], - push_constant_ranges: vec![PushConstantRange { - stages: ShaderStages::FRAGMENT | ShaderStages::COMPUTE, - offset: 0, - size: 12, - }], - ..Default::default() - }, - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - self.device.clone(), - Default::default(), - )); - - let sampler = Sampler::new( - self.device.clone(), - SamplerCreateInfo { + let bloom_sampler_id = bcx + .global_set() + .create_sampler(SamplerCreateInfo { mag_filter: Filter::Linear, min_filter: Filter::Linear, mipmap_mode: SamplerMipmapMode::Nearest, lod: 0.0..=LOD_CLAMP_NONE, ..Default::default() - }, - ) - .unwrap(); + }) + .unwrap(); - let (bloom_image_id, descriptor_set) = window_size_dependent_setup( - &self.resources, - swapchain_id, - &pipeline_layout, - &sampler, - &descriptor_set_allocator, - ); + let (bloom_image_id, bloom_sampled_image_id, bloom_storage_image_ids) = + window_size_dependent_setup(&self.resources, swapchain_id); let mut task_graph = TaskGraph::new(&self.resources, 3, 2); @@ -338,7 +282,7 @@ impl ApplicationHandler for App { .create_task_node( "Bloom", QueueFamilyType::Compute, - BloomTask::new(self, &pipeline_layout, virtual_bloom_image_id), + BloomTask::new(self, virtual_bloom_image_id), ) .image_access( virtual_bloom_image_id, @@ -382,25 +326,24 @@ impl ApplicationHandler for App { .task_mut() .downcast_mut::() .unwrap() - .create_pipeline(&pipeline_layout, subpass); + .create_pipeline(self, subpass); let tonemap_node = task_graph.task_node_mut(tonemap_node_id).unwrap(); let subpass = tonemap_node.subpass().unwrap().clone(); tonemap_node .task_mut() .downcast_mut::() .unwrap() - .create_pipeline(&pipeline_layout, subpass); + .create_pipeline(self, subpass); self.rcx = Some(RenderContext { window, swapchain_id, bloom_image_id, viewport, - pipeline_layout, recreate_swapchain: false, - sampler, - descriptor_set_allocator, - descriptor_set, + bloom_sampler_id, + bloom_sampled_image_id, + bloom_storage_image_ids, task_graph, virtual_swapchain_id, virtual_bloom_image_id, @@ -414,6 +357,7 @@ impl ApplicationHandler for App { event: WindowEvent, ) { let rcx = self.rcx.as_mut().unwrap(); + let bcx = self.resources.bindless_context().unwrap(); match event { WindowEvent::CloseRequested => { @@ -442,15 +386,28 @@ impl ApplicationHandler for App { rcx.viewport.extent = window_size.into(); + // FIXME(taskgraph): safe resource destruction + flight + .wait_for_frame(flight.current_frame() - 1, None) + .unwrap(); + unsafe { self.resources.remove_image(rcx.bloom_image_id) }.unwrap(); - (rcx.bloom_image_id, rcx.descriptor_set) = window_size_dependent_setup( - &self.resources, - rcx.swapchain_id, - &rcx.pipeline_layout, - &rcx.sampler, - &rcx.descriptor_set_allocator, - ); + unsafe { + bcx.global_set() + .remove_sampled_image(rcx.bloom_sampled_image_id) + } + .unwrap(); + + for &id in &rcx.bloom_storage_image_ids { + let _ = unsafe { bcx.global_set().remove_storage_image(id) }; + } + + ( + rcx.bloom_image_id, + rcx.bloom_sampled_image_id, + rcx.bloom_storage_image_ids, + ) = window_size_dependent_setup(&self.resources, rcx.swapchain_id); rcx.recreate_swapchain = false; } @@ -494,11 +451,13 @@ impl ApplicationHandler for App { fn window_size_dependent_setup( resources: &Resources, swapchain_id: Id, - pipeline_layout: &Arc, - sampler: &Arc, - descriptor_set_allocator: &Arc, -) -> (Id, Arc) { +) -> ( + Id, + SampledImageId, + [StorageImageId; MAX_BLOOM_MIP_LEVELS as usize], +) { let device = resources.device(); + let bcx = resources.bindless_context().unwrap(); let swapchain_state = resources.swapchain(swapchain_id).unwrap(); let images = swapchain_state.images(); let extent = images[0].extent(); @@ -537,60 +496,44 @@ fn window_size_dependent_setup( let bloom_image_state = resources.image(bloom_image_id).unwrap(); let bloom_image = bloom_image_state.image(); - let bloom_texture_view = ImageView::new( - bloom_image.clone(), - ImageViewCreateInfo { - format: Format::E5B9G9R9_UFLOAT_PACK32, - subresource_range: bloom_image.subresource_range(), - usage: ImageUsage::SAMPLED, - ..Default::default() - }, - ) - .unwrap(); - - let bloom_mip_chain_views = (0..MAX_BLOOM_MIP_LEVELS).map(|mip_level| { - let mip_level = cmp::min(mip_level, max_mip_levels(extent) - 1); - - ImageView::new( - bloom_image.clone(), + let bloom_sampled_image_id = bcx + .global_set() + .create_sampled_image( + bloom_image_id, ImageViewCreateInfo { - format: Format::R32_UINT, - subresource_range: ImageSubresourceRange { - aspects: ImageAspects::COLOR, - mip_levels: mip_level..mip_level + 1, - array_layers: 0..1, - }, - usage: ImageUsage::STORAGE, + format: Format::E5B9G9R9_UFLOAT_PACK32, + subresource_range: bloom_image.subresource_range(), + usage: ImageUsage::SAMPLED, ..Default::default() }, + ImageLayout::General, ) - .unwrap() - }); + .unwrap(); - let descriptor_set = DescriptorSet::new( - descriptor_set_allocator.clone(), - pipeline_layout.set_layouts()[0].clone(), - [ - WriteDescriptorSet::sampler(0, sampler.clone()), - WriteDescriptorSet::image_view_with_layout( - 1, - DescriptorImageViewInfo { - image_view: bloom_texture_view, - image_layout: ImageLayout::General, + let bloom_storage_image_ids = array::from_fn(|mip_level| { + let mip_level = cmp::min(mip_level as u32, max_mip_levels(extent) - 1); + + bcx.global_set() + .create_storage_image( + bloom_image_id, + ImageViewCreateInfo { + format: Format::R32_UINT, + subresource_range: ImageSubresourceRange { + aspects: ImageAspects::COLOR, + mip_levels: mip_level..mip_level + 1, + array_layers: 0..1, + }, + usage: ImageUsage::STORAGE, + ..Default::default() }, - ), - WriteDescriptorSet::image_view_with_layout_array( - 2, - 0, - bloom_mip_chain_views.map(|image_view| DescriptorImageViewInfo { - image_view, - image_layout: ImageLayout::General, - }), - ), - ], - [], - ) - .unwrap(); + ImageLayout::General, + ) + .unwrap() + }); - (bloom_image_id, descriptor_set) + ( + bloom_image_id, + bloom_sampled_image_id, + bloom_storage_image_ids, + ) } diff --git a/examples/bloom/scene.rs b/examples/bloom/scene.rs index b8decdd880..1cd21b34b5 100644 --- a/examples/bloom/scene.rs +++ b/examples/bloom/scene.rs @@ -2,7 +2,6 @@ use crate::{App, RenderContext}; use std::{slice, sync::Arc}; use vulkano::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, - device::DeviceOwned, image::Image, memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter}, pipeline::{ @@ -15,7 +14,7 @@ use vulkano::{ viewport::ViewportState, GraphicsPipelineCreateInfo, }, - DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, + DynamicState, GraphicsPipeline, PipelineShaderStageCreateInfo, }, render_pass::Subpass, }; @@ -84,13 +83,15 @@ impl SceneTask { } } - pub fn create_pipeline(&mut self, pipeline_layout: &Arc, subpass: Subpass) { + pub fn create_pipeline(&mut self, app: &App, subpass: Subpass) { + let bcx = app.resources.bindless_context().unwrap(); + let pipeline = { - let vs = vs::load(pipeline_layout.device().clone()) + let vs = vs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(pipeline_layout.device().clone()) + let fs = fs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -99,9 +100,10 @@ impl SceneTask { PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); GraphicsPipeline::new( - pipeline_layout.device().clone(), + app.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -116,7 +118,7 @@ impl SceneTask { )), dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::new(pipeline_layout.clone()) + ..GraphicsPipelineCreateInfo::new(layout) }, ) .unwrap() diff --git a/examples/bloom/tonemap.glsl b/examples/bloom/tonemap.glsl index cc9bc0e05d..fe4fbbc3c4 100644 --- a/examples/bloom/tonemap.glsl +++ b/examples/bloom/tonemap.glsl @@ -1,13 +1,13 @@ #version 450 +#include layout(location = 0) in vec2 v_tex_coords; layout(location = 0) out vec4 f_color; -layout(set = 0, binding = 0) uniform sampler bloom_sampler; -layout(set = 0, binding = 1) uniform texture2D bloom_texture; - layout(push_constant) uniform PushConstants { + SamplerId sampler_id; + SampledImageId texture_id; float exposure; }; @@ -30,7 +30,7 @@ vec3 rrtAndOdtFit(vec3 v) { } void main() { - vec4 hdr_color = exposure * texture(sampler2D(bloom_texture, bloom_sampler), v_tex_coords); + vec4 hdr_color = exposure * texture(vko_sampler2D(texture_id, sampler_id), v_tex_coords); vec3 color = ACES_INPUT_MATRIX * hdr_color.rgb; color = rrtAndOdtFit(color); diff --git a/examples/bloom/tonemap.rs b/examples/bloom/tonemap.rs index 22fb28dbe5..a350e61ca1 100644 --- a/examples/bloom/tonemap.rs +++ b/examples/bloom/tonemap.rs @@ -1,7 +1,6 @@ use crate::{App, RenderContext}; use std::{slice, sync::Arc}; use vulkano::{ - device::DeviceOwned, pipeline::{ graphics::{ color_blend::{ColorBlendAttachmentState, ColorBlendState}, @@ -12,8 +11,7 @@ use vulkano::{ viewport::ViewportState, GraphicsPipelineCreateInfo, }, - DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout, - PipelineShaderStageCreateInfo, + DynamicState, GraphicsPipeline, Pipeline, PipelineShaderStageCreateInfo, }, render_pass::Subpass, }; @@ -30,13 +28,15 @@ impl TonemapTask { TonemapTask { pipeline: None } } - pub fn create_pipeline(&mut self, pipeline_layout: &Arc, subpass: Subpass) { + pub fn create_pipeline(&mut self, app: &App, subpass: Subpass) { + let bcx = app.resources.bindless_context().unwrap(); + let pipeline = { - let vs = vs::load(pipeline_layout.device().clone()) + let vs = vs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); - let fs = fs::load(pipeline_layout.device().clone()) + let fs = fs::load(app.device.clone()) .unwrap() .entry_point("main") .unwrap(); @@ -44,9 +44,10 @@ impl TonemapTask { PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(fs), ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); GraphicsPipeline::new( - pipeline_layout.device().clone(), + app.device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), @@ -61,7 +62,7 @@ impl TonemapTask { )), dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::new(pipeline_layout.clone()) + ..GraphicsPipelineCreateInfo::new(layout) }, ) .unwrap() @@ -80,20 +81,16 @@ impl Task for TonemapTask { _tcx: &mut TaskContext<'_>, rcx: &Self::World, ) -> TaskResult { - cbf.as_raw().bind_descriptor_sets( - PipelineBindPoint::Graphics, - &rcx.pipeline_layout, - 0, - &[rcx.descriptor_set.as_raw()], - &[], - )?; - cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?; cbf.bind_pipeline_graphics(self.pipeline.as_ref().unwrap())?; cbf.push_constants( - &rcx.pipeline_layout, + self.pipeline.as_ref().unwrap().layout(), 0, - &fs::PushConstants { exposure: EXPOSURE }, + &fs::PushConstants { + sampler_id: rcx.bloom_sampler_id, + texture_id: rcx.bloom_sampled_image_id, + exposure: EXPOSURE, + }, )?; unsafe { cbf.draw(3, 1, 0, 0) }?; diff --git a/examples/bloom/upsample.glsl b/examples/bloom/upsample.glsl index 42b018ec18..5281519318 100644 --- a/examples/bloom/upsample.glsl +++ b/examples/bloom/upsample.glsl @@ -1,15 +1,13 @@ #version 450 +#include #include -const uint MAX_BLOOM_MIP_LEVELS = 6; - layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; -layout(set = 0, binding = 0) uniform sampler bloom_sampler; -layout(set = 0, binding = 1) uniform texture2D bloom_texture; -layout(set = 0, binding = 2, r32ui) uniform uimage2D bloom_mip_chain[MAX_BLOOM_MIP_LEVELS]; - layout(push_constant) uniform PushConstants { + SamplerId sampler_id; + SampledImageId texture_id; + StorageImageId dst_mip_image_id; uint dst_mip_level; float intensity; }; @@ -17,7 +15,7 @@ layout(push_constant) uniform PushConstants { uint src_mip_level = dst_mip_level + 1; vec3 sample1(vec2 uv) { - return textureLod(sampler2D(bloom_texture, bloom_sampler), uv, src_mip_level).rgb; + return textureLod(vko_sampler2D(texture_id, sampler_id), uv, src_mip_level).rgb; } // 9-tap tent filter. @@ -37,20 +35,20 @@ vec3 upsampleTent9(vec2 uv, vec2 src_texel_size) { } void blend(vec2 uv, ivec2 dst_coord, vec3 color) { - color += textureLod(sampler2D(bloom_texture, bloom_sampler), uv, dst_mip_level).rgb; + color += textureLod(vko_sampler2D(texture_id, sampler_id), uv, dst_mip_level).rgb; uint packed = convertToSharedExponent(color); - imageStore(bloom_mip_chain[dst_mip_level], dst_coord, uvec4(packed, 0, 0, 0)); + imageStore(vko_uimage2D_r32ui(dst_mip_image_id), dst_coord, uvec4(packed, 0, 0, 0)); } void main() { ivec2 dst_coord = ivec2(gl_GlobalInvocationID.xy); - ivec2 dst_size = imageSize(bloom_mip_chain[dst_mip_level]); + ivec2 dst_size = imageSize(vko_uimage2D_r32ui(dst_mip_image_id)); if (dst_coord.x > dst_size.x || dst_coord.y > dst_size.y) { return; } - ivec2 src_size = textureSize(sampler2D(bloom_texture, bloom_sampler), int(src_mip_level)); + ivec2 src_size = textureSize(vko_texture2D(texture_id), int(src_mip_level)); vec2 src_texel_size = 1.0 / vec2(src_size); vec2 uv = (vec2(dst_coord) + 0.5) / vec2(dst_size); vec3 color = upsampleTent9(uv, src_texel_size); diff --git a/examples/deferred/Cargo.toml b/examples/deferred/Cargo.toml index ae769dcae1..598f5b20cc 100644 --- a/examples/deferred/Cargo.toml +++ b/examples/deferred/Cargo.toml @@ -15,4 +15,5 @@ doc = false glam = { workspace = true } vulkano = { workspace = true, default-features = true } vulkano-shaders = { workspace = true } +vulkano-taskgraph = { workspace = true } winit = { workspace = true, default-features = true } diff --git a/examples/deferred/deferred.rs b/examples/deferred/deferred.rs new file mode 100644 index 0000000000..6b608c8256 --- /dev/null +++ b/examples/deferred/deferred.rs @@ -0,0 +1,742 @@ +use crate::{App, RenderContext}; +use glam::{Mat4, Vec3}; +use std::{slice, sync::Arc}; +use vulkano::{ + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter}, + pipeline::{ + graphics::{ + color_blend::{ + AttachmentBlend, BlendFactor, BlendOp, ColorBlendAttachmentState, ColorBlendState, + }, + input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, + vertex_input::{Vertex, VertexDefinition}, + viewport::ViewportState, + GraphicsPipelineCreateInfo, + }, + DynamicState, GraphicsPipeline, Pipeline, PipelineShaderStageCreateInfo, + }, + render_pass::Subpass, + swapchain::Swapchain, +}; +use vulkano_taskgraph::{ + command_buffer::RecordingCommandBuffer, resource::HostAccessType, ClearValues, Id, Task, + TaskContext, TaskResult, +}; + +pub struct DeferredTask { + swapchain_id: Id, + ambient_lighting_pipeline: AmbientLightingPipeline, + directional_lighting_pipeline: DirectionalLightingPipeline, + point_lighting_pipeline: PointLightingPipeline, +} + +impl DeferredTask { + pub fn new(app: &App, virtual_swapchain_id: Id) -> Self { + DeferredTask { + swapchain_id: virtual_swapchain_id, + ambient_lighting_pipeline: AmbientLightingPipeline::new(app), + directional_lighting_pipeline: DirectionalLightingPipeline::new(app), + point_lighting_pipeline: PointLightingPipeline::new(app), + } + } + + pub fn create_pipelines(&mut self, app: &App, subpass: Subpass) { + self.ambient_lighting_pipeline + .create_pipeline(app, subpass.clone()); + self.directional_lighting_pipeline + .create_pipeline(app, subpass.clone()); + self.point_lighting_pipeline.create_pipeline(app, subpass); + } +} + +impl Task for DeferredTask { + type World = RenderContext; + + fn clear_values(&self, clear_values: &mut ClearValues<'_>) { + clear_values.set(self.swapchain_id.current_image_id(), [0.0; 4]); + } + + unsafe fn execute( + &self, + cbf: &mut RecordingCommandBuffer<'_>, + _tcx: &mut TaskContext<'_>, + rcx: &Self::World, + ) -> TaskResult { + cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?; + + let world_to_screen = Mat4::IDENTITY; + let screen_to_world = world_to_screen.inverse(); + + self.ambient_lighting_pipeline.draw(cbf, [0.1, 0.1, 0.1])?; + self.directional_lighting_pipeline.draw( + cbf, + Vec3::new(0.2, -0.1, -0.7), + [0.6, 0.6, 0.6], + )?; + self.point_lighting_pipeline.draw( + cbf, + screen_to_world, + Vec3::new(0.5, -0.5, -0.1), + [1.0, 0.0, 0.0], + )?; + self.point_lighting_pipeline.draw( + cbf, + screen_to_world, + Vec3::new(-0.9, 0.2, -0.15), + [0.0, 1.0, 0.0], + )?; + self.point_lighting_pipeline.draw( + cbf, + screen_to_world, + Vec3::new(0.0, 0.5, -0.05), + [0.0, 0.0, 1.0], + )?; + + Ok(()) + } +} + +#[derive(Clone, Copy, BufferContents, Vertex)] +#[repr(C)] +struct LightingVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], +} + +/// Allows applying an ambient lighting to a scene. +struct AmbientLightingPipeline { + vertex_buffer_id: Id, + pipeline: Option>, +} + +impl AmbientLightingPipeline { + fn new(app: &App) -> Self { + let vertices = [ + LightingVertex { + position: [-1.0, -1.0], + }, + LightingVertex { + position: [-1.0, 3.0], + }, + LightingVertex { + position: [3.0, -1.0], + }, + ]; + let vertex_buffer_id = app + .resources + .create_buffer( + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + DeviceLayout::for_value(vertices.as_slice()).unwrap(), + ) + .unwrap(); + + unsafe { + vulkano_taskgraph::execute( + &app.queue, + &app.resources, + app.flight_id, + |_cbf, tcx| { + tcx.write_buffer::<[LightingVertex]>(vertex_buffer_id, ..)? + .copy_from_slice(&vertices); + + Ok(()) + }, + [(vertex_buffer_id, HostAccessType::Write)], + [], + [], + ) + } + .unwrap(); + + AmbientLightingPipeline { + vertex_buffer_id, + pipeline: None, + } + } + + fn create_pipeline(&mut self, app: &App, subpass: Subpass) { + let bcx = app.resources.bindless_context().unwrap(); + + let pipeline = { + let vs = ambient_lighting_vs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = ambient_lighting_fs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = LightingVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); + + GraphicsPipeline::new( + app.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend { + color_blend_op: BlendOp::Add, + src_color_blend_factor: BlendFactor::One, + dst_color_blend_factor: BlendFactor::One, + alpha_blend_op: BlendOp::Max, + src_alpha_blend_factor: BlendFactor::One, + dst_alpha_blend_factor: BlendFactor::One, + }), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.clone().into()), + ..GraphicsPipelineCreateInfo::new(layout) + }, + ) + .unwrap() + }; + + self.pipeline = Some(pipeline); + } + + /// Records a draw command that applies ambient lighting. + /// + /// This draw will read the diffuse image, multiply it with `ambient_color` and write the + /// output to the current swapchain image with additive blending (in other words the value will + /// be added to the existing value in the swapchain image, and not replace the existing value). + /// + /// The viewport must have been set beforehand. + /// + /// # Arguments + /// + /// - `cbf` is the command buffer to record to. + /// - `viewport_dimensions` contains the dimensions of the current framebuffer. + /// - `color_input` is an image containing the albedo of each object of the scene. It is the + /// result of the deferred pass. + /// - `ambient_color` is the color to apply. + unsafe fn draw( + &self, + cbf: &mut RecordingCommandBuffer<'_>, + ambient_color: [f32; 3], + ) -> TaskResult { + cbf.bind_pipeline_graphics(self.pipeline.as_ref().unwrap())?; + cbf.push_constants( + self.pipeline.as_ref().unwrap().layout(), + 0, + &ambient_lighting_fs::PushConstants { + color: [ambient_color[0], ambient_color[1], ambient_color[2], 1.0], + }, + )?; + cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?; + + unsafe { cbf.draw(3, 1, 0, 0) }?; + + Ok(()) + } +} + +mod ambient_lighting_vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } +} + +mod ambient_lighting_fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + #define VKO_INPUT_ATTACHMENT_ENABLED 1 + #include + + #define u_diffuse vko_subpassInput(0) + + layout(push_constant) uniform PushConstants { + // The `ambient_color` parameter of the `draw` method. + vec4 color; + } push_constants; + + layout(location = 0) out vec4 f_color; + + void main() { + // Load the value at the current pixel. + vec3 in_diffuse = subpassLoad(u_diffuse).rgb; + f_color.rgb = push_constants.color.rgb * in_diffuse; + f_color.a = 1.0; + } + ", + } +} + +/// Allows applying a directional light source to a scene. +struct DirectionalLightingPipeline { + vertex_buffer_id: Id, + pipeline: Option>, +} + +impl DirectionalLightingPipeline { + fn new(app: &App) -> Self { + let vertices = [ + LightingVertex { + position: [-1.0, -1.0], + }, + LightingVertex { + position: [-1.0, 3.0], + }, + LightingVertex { + position: [3.0, -1.0], + }, + ]; + let vertex_buffer_id = app + .resources + .create_buffer( + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + DeviceLayout::for_value(vertices.as_slice()).unwrap(), + ) + .unwrap(); + + unsafe { + vulkano_taskgraph::execute( + &app.queue, + &app.resources, + app.flight_id, + |_cbf, tcx| { + tcx.write_buffer::<[LightingVertex]>(vertex_buffer_id, ..)? + .copy_from_slice(&vertices); + + Ok(()) + }, + [(vertex_buffer_id, HostAccessType::Write)], + [], + [], + ) + } + .unwrap(); + + DirectionalLightingPipeline { + vertex_buffer_id, + pipeline: None, + } + } + + fn create_pipeline(&mut self, app: &App, subpass: Subpass) { + let bcx = app.resources.bindless_context().unwrap(); + + let pipeline = { + let vs = directional_lighting_vs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = directional_lighting_fs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = LightingVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); + + GraphicsPipeline::new( + app.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend { + color_blend_op: BlendOp::Add, + src_color_blend_factor: BlendFactor::One, + dst_color_blend_factor: BlendFactor::One, + alpha_blend_op: BlendOp::Max, + src_alpha_blend_factor: BlendFactor::One, + dst_alpha_blend_factor: BlendFactor::One, + }), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.clone().into()), + ..GraphicsPipelineCreateInfo::new(layout) + }, + ) + .unwrap() + }; + + self.pipeline = Some(pipeline); + } + + /// Records a draw command that applies directional lighting. + /// + /// This draw will read the diffuse image and normals image, and multiply the color with + /// `color` and the dot product of the `direction` with the normal. It then writes the output + /// to the current swapchain image with additive blending (in other words the value will be + /// added to the existing value in the swapchain image, and not replace the existing value). + /// + /// Since the normals image contains normals in world coordinates, `direction` should also be + /// in world coordinates. + /// + /// The viewport must have been set beforehand. + /// + /// # Arguments + /// + /// - `cbf` is the command buffer to record to. + /// - `direction` is the direction of the light in world coordinates. + /// - `color` is the color to apply. + unsafe fn draw( + &self, + cbf: &mut RecordingCommandBuffer<'_>, + direction: Vec3, + color: [f32; 3], + ) -> TaskResult { + cbf.bind_pipeline_graphics(self.pipeline.as_ref().unwrap())?; + cbf.push_constants( + self.pipeline.as_ref().unwrap().layout(), + 0, + &directional_lighting_fs::PushConstants { + color: [color[0], color[1], color[2], 1.0], + direction: direction.extend(0.0).into(), + }, + )?; + cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?; + + unsafe { cbf.draw(3, 1, 0, 0) }?; + + Ok(()) + } +} + +mod directional_lighting_vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } +} + +mod directional_lighting_fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + #define VKO_INPUT_ATTACHMENT_ENABLED 1 + #include + + #define u_diffuse vko_subpassInput(0) + #define u_normals vko_subpassInput(1) + + layout(push_constant) uniform PushConstants { + // The `color` parameter of the `draw` method. + vec4 color; + // The `direction` parameter of the `draw` method. + vec4 direction; + } push_constants; + + layout(location = 0) out vec4 f_color; + + void main() { + vec3 in_normal = normalize(subpassLoad(u_normals).rgb); + + // If the normal is perpendicular to the direction of the lighting, then + // `light_percent` will be 0. If the normal is parallel to the direction of the + // lighting, then `light_percent` will be 1. Any other angle will yield an + // intermediate value. + float light_percent = -dot(push_constants.direction.xyz, in_normal); + // `light_percent` must not go below 0.0. There's no such thing as negative + // lighting. + light_percent = max(light_percent, 0.0); + + vec3 in_diffuse = subpassLoad(u_diffuse).rgb; + f_color.rgb = light_percent * push_constants.color.rgb * in_diffuse; + f_color.a = 1.0; + } + ", + } +} + +/// Allows applying a point light to a scene. +struct PointLightingPipeline { + vertex_buffer_id: Id, + pipeline: Option>, +} + +impl PointLightingPipeline { + fn new(app: &App) -> Self { + let vertices = [ + LightingVertex { + position: [-1.0, -1.0], + }, + LightingVertex { + position: [-1.0, 3.0], + }, + LightingVertex { + position: [3.0, -1.0], + }, + ]; + let vertex_buffer_id = app + .resources + .create_buffer( + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + DeviceLayout::for_value(vertices.as_slice()).unwrap(), + ) + .unwrap(); + + unsafe { + vulkano_taskgraph::execute( + &app.queue, + &app.resources, + app.flight_id, + |_cbf, tcx| { + tcx.write_buffer::<[LightingVertex]>(vertex_buffer_id, ..)? + .copy_from_slice(&vertices); + + Ok(()) + }, + [(vertex_buffer_id, HostAccessType::Write)], + [], + [], + ) + } + .unwrap(); + + PointLightingPipeline { + vertex_buffer_id, + pipeline: None, + } + } + + fn create_pipeline(&mut self, app: &App, subpass: Subpass) { + let bcx = app.resources.bindless_context().unwrap(); + + let pipeline = { + let vs = point_lighting_vs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = point_lighting_fs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = LightingVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); + + GraphicsPipeline::new( + app.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState { + blend: Some(AttachmentBlend { + color_blend_op: BlendOp::Add, + src_color_blend_factor: BlendFactor::One, + dst_color_blend_factor: BlendFactor::One, + alpha_blend_op: BlendOp::Max, + src_alpha_blend_factor: BlendFactor::One, + dst_alpha_blend_factor: BlendFactor::One, + }), + ..Default::default() + }, + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.clone().into()), + ..GraphicsPipelineCreateInfo::new(layout) + }, + ) + .unwrap() + }; + + self.pipeline = Some(pipeline); + } + + /// Records a draw command that applies a point lighting. + /// + /// This draw will read the depth image and rebuild the world position of the pixel currently + /// being processed (modulo rounding errors). It will then compare this position with + /// `position`, and process the lighting based on the distance and orientation (similar to the + /// directional lighting pipeline). + /// + /// It then writes the output to the current swapchain image with additive blending (in other + /// words the value will be added to the existing value in the swapchain image, and not replace + /// the existing value). + /// + /// Note that in a real-world application, you probably want to pass additional parameters + /// such as some way to indicate the distance at which the lighting decrease. In this example + /// this value is hardcoded in the shader. + /// + /// The viewport must have been set beforehand. + /// + /// # Arguments + /// + /// - `cbf` is the command buffer to record to. + /// - `screen_to_world` is a matrix that turns coordinates from framebuffer space into world + /// space. This matrix is used alongside with `depth_input` to determine the world + /// coordinates of each pixel being processed. + /// - `position` is the position of the spot light in world coordinates. + /// - `color` is the color of the light. + unsafe fn draw( + &self, + cbf: &mut RecordingCommandBuffer<'_>, + screen_to_world: Mat4, + position: Vec3, + color: [f32; 3], + ) -> TaskResult { + cbf.bind_pipeline_graphics(self.pipeline.as_ref().unwrap())?; + cbf.push_constants( + self.pipeline.as_ref().unwrap().layout(), + 0, + &point_lighting_fs::PushConstants { + screen_to_world: screen_to_world.to_cols_array_2d(), + color: [color[0], color[1], color[2], 1.0], + position: position.extend(0.0).into(), + }, + )?; + cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?; + + unsafe { cbf.draw(3, 1, 0, 0) }?; + + Ok(()) + } +} + +mod point_lighting_vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 v_screen_coords; + + void main() { + v_screen_coords = position; + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } +} + +mod point_lighting_fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + #define VKO_INPUT_ATTACHMENT_ENABLED 1 + #include + + #define u_diffuse vko_subpassInput(0) + #define u_normals vko_subpassInput(1) + #define u_depth vko_subpassInput(2) + + layout(push_constant) uniform PushConstants { + // The `screen_to_world` parameter of the `draw` method. + mat4 screen_to_world; + // The `color` parameter of the `draw` method. + vec4 color; + // The `position` parameter of the `draw` method. + vec4 position; + } push_constants; + + layout(location = 0) in vec2 v_screen_coords; + layout(location = 0) out vec4 f_color; + + void main() { + float in_depth = subpassLoad(u_depth).x; + + // Any depth superior or equal to 1.0 means that the pixel has been untouched by + // the deferred pass. We don't want to deal with them. + if (in_depth >= 1.0) { + discard; + } + + // Find the world coordinates of the current pixel. + vec4 world = push_constants.screen_to_world * vec4(v_screen_coords, in_depth, 1.0); + world /= world.w; + + vec3 in_normal = normalize(subpassLoad(u_normals).rgb); + vec3 light_direction = normalize(push_constants.position.xyz - world.xyz); + + // Calculate the percent of lighting that is received based on the orientation of + // the normal and the direction of the light. + float light_percent = max(-dot(light_direction, in_normal), 0.0); + + float light_distance = length(push_constants.position.xyz - world.xyz); + // Further decrease light_percent based on the distance with the light position. + light_percent *= 1.0 / exp(light_distance); + + vec3 in_diffuse = subpassLoad(u_diffuse).rgb; + f_color.rgb = push_constants.color.rgb * light_percent * in_diffuse; + f_color.a = 1.0; + } + ", + } +} diff --git a/examples/deferred/frame/ambient_lighting_system.rs b/examples/deferred/frame/ambient_lighting_system.rs deleted file mode 100644 index c8a4fe14f9..0000000000 --- a/examples/deferred/frame/ambient_lighting_system.rs +++ /dev/null @@ -1,252 +0,0 @@ -use super::LightingVertex; -use std::sync::Arc; -use vulkano::{ - buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}, - command_buffer::{ - allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, - CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer, - }, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, - }, - device::Queue, - image::view::ImageView, - memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, - pipeline::{ - graphics::{ - color_blend::{ - AttachmentBlend, BlendFactor, BlendOp, ColorBlendAttachmentState, ColorBlendState, - }, - input_assembly::InputAssemblyState, - multisample::MultisampleState, - rasterization::RasterizationState, - vertex_input::{Vertex, VertexDefinition}, - viewport::{Viewport, ViewportState}, - GraphicsPipelineCreateInfo, - }, - layout::PipelineDescriptorSetLayoutCreateInfo, - DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, - PipelineShaderStageCreateInfo, - }, - render_pass::Subpass, -}; - -/// Allows applying an ambient lighting to a scene. -pub struct AmbientLightingSystem { - gfx_queue: Arc, - vertex_buffer: Subbuffer<[LightingVertex]>, - subpass: Subpass, - pipeline: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, -} - -impl AmbientLightingSystem { - /// Initializes the ambient lighting system. - pub fn new( - gfx_queue: Arc, - subpass: Subpass, - memory_allocator: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, - ) -> AmbientLightingSystem { - let vertices = [ - LightingVertex { - position: [-1.0, -1.0], - }, - LightingVertex { - position: [-1.0, 3.0], - }, - LightingVertex { - position: [3.0, -1.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .expect("failed to create buffer"); - - let pipeline = { - let device = gfx_queue.device(); - let vs = vs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let fs = fs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let vertex_input_state = LightingVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend { - color_blend_op: BlendOp::Add, - src_color_blend_factor: BlendFactor::One, - dst_color_blend_factor: BlendFactor::One, - alpha_blend_op: BlendOp::Max, - src_alpha_blend_factor: BlendFactor::One, - dst_alpha_blend_factor: BlendFactor::One, - }), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.clone().into()), - ..GraphicsPipelineCreateInfo::new(layout) - }, - ) - .unwrap() - }; - - AmbientLightingSystem { - gfx_queue, - vertex_buffer, - subpass, - pipeline, - command_buffer_allocator, - descriptor_set_allocator, - } - } - - /// Builds a secondary command buffer that applies ambient lighting. - /// - /// This secondary command buffer will read `color_input`, multiply it with `ambient_color` - /// and write the output to the current framebuffer with additive blending (in other words - /// the value will be added to the existing value in the framebuffer, and not replace the - /// existing value). - /// - /// - `viewport_dimensions` contains the dimensions of the current framebuffer. - /// - `color_input` is an image containing the albedo of each object of the scene. It is the - /// result of the deferred pass. - /// - `ambient_color` is the color to apply. - pub fn draw( - &self, - viewport_dimensions: [u32; 2], - color_input: Arc, - ambient_color: [f32; 3], - ) -> Arc { - let push_constants = fs::PushConstants { - color: [ambient_color[0], ambient_color[1], ambient_color[2], 1.0], - }; - - let layout = &self.pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [WriteDescriptorSet::image_view(0, color_input)], - [], - ) - .unwrap(); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32], - depth_range: 0.0..=1.0, - }; - - let mut builder = AutoCommandBufferBuilder::secondary( - self.command_buffer_allocator.clone(), - self.gfx_queue.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(self.subpass.clone().into()), - ..Default::default() - }, - ) - .unwrap(); - - builder - .set_viewport(0, [viewport].into_iter().collect()) - .unwrap() - .bind_pipeline_graphics(self.pipeline.clone()) - .unwrap() - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - self.pipeline.layout().clone(), - 0, - descriptor_set, - ) - .unwrap() - .push_constants(self.pipeline.layout().clone(), 0, push_constants) - .unwrap() - .bind_vertex_buffers(0, self.vertex_buffer.clone()) - .unwrap(); - unsafe { builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0) }.unwrap(); - - builder.build().unwrap() - } -} - -mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", - } -} - -mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - - // The `color_input` parameter of the `draw` method. - layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput u_diffuse; - - layout(push_constant) uniform PushConstants { - // The `ambient_color` parameter of the `draw` method. - vec4 color; - } push_constants; - - layout(location = 0) out vec4 f_color; - - void main() { - // Load the value at the current pixel. - vec3 in_diffuse = subpassLoad(u_diffuse).rgb; - f_color.rgb = push_constants.color.rgb * in_diffuse; - f_color.a = 1.0; - } - ", - } -} diff --git a/examples/deferred/frame/directional_lighting_system.rs b/examples/deferred/frame/directional_lighting_system.rs deleted file mode 100644 index 16ee83e952..0000000000 --- a/examples/deferred/frame/directional_lighting_system.rs +++ /dev/null @@ -1,279 +0,0 @@ -use super::LightingVertex; -use glam::f32::Vec3; -use std::sync::Arc; -use vulkano::{ - buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}, - command_buffer::{ - allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, - CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer, - }, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, - }, - device::Queue, - image::view::ImageView, - memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, - pipeline::{ - graphics::{ - color_blend::{ - AttachmentBlend, BlendFactor, BlendOp, ColorBlendAttachmentState, ColorBlendState, - }, - input_assembly::InputAssemblyState, - multisample::MultisampleState, - rasterization::RasterizationState, - vertex_input::{Vertex, VertexDefinition}, - viewport::{Viewport, ViewportState}, - GraphicsPipelineCreateInfo, - }, - layout::PipelineDescriptorSetLayoutCreateInfo, - DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, - PipelineShaderStageCreateInfo, - }, - render_pass::Subpass, -}; - -/// Allows applying a directional light source to a scene. -pub struct DirectionalLightingSystem { - gfx_queue: Arc, - vertex_buffer: Subbuffer<[LightingVertex]>, - subpass: Subpass, - pipeline: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, -} - -impl DirectionalLightingSystem { - /// Initializes the directional lighting system. - pub fn new( - gfx_queue: Arc, - subpass: Subpass, - memory_allocator: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, - ) -> DirectionalLightingSystem { - let vertices = [ - LightingVertex { - position: [-1.0, -1.0], - }, - LightingVertex { - position: [-1.0, 3.0], - }, - LightingVertex { - position: [3.0, -1.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .expect("failed to create buffer"); - - let pipeline = { - let device = gfx_queue.device(); - let vs = vs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let fs = fs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let vertex_input_state = LightingVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend { - color_blend_op: BlendOp::Add, - src_color_blend_factor: BlendFactor::One, - dst_color_blend_factor: BlendFactor::One, - alpha_blend_op: BlendOp::Max, - src_alpha_blend_factor: BlendFactor::One, - dst_alpha_blend_factor: BlendFactor::One, - }), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.clone().into()), - ..GraphicsPipelineCreateInfo::new(layout) - }, - ) - .unwrap() - }; - - DirectionalLightingSystem { - gfx_queue, - vertex_buffer, - subpass, - pipeline, - command_buffer_allocator, - descriptor_set_allocator, - } - } - - /// Builds a secondary command buffer that applies directional lighting. - /// - /// This secondary command buffer will read `color_input` and `normals_input`, and multiply the - /// color with `color` and the dot product of the `direction` with the normal. - /// It then writes the output to the current framebuffer with additive blending (in other words - /// the value will be added to the existing value in the framebuffer, and not replace the - /// existing value). - /// - /// Since `normals_input` contains normals in world coordinates, `direction` should also be in - /// world coordinates. - /// - /// - `viewport_dimensions` contains the dimensions of the current framebuffer. - /// - `color_input` is an image containing the albedo of each object of the scene. It is the - /// result of the deferred pass. - /// - `normals_input` is an image containing the normals of each object of the scene. It is the - /// result of the deferred pass. - /// - `direction` is the direction of the light in world coordinates. - /// - `color` is the color to apply. - pub fn draw( - &self, - viewport_dimensions: [u32; 2], - color_input: Arc, - normals_input: Arc, - direction: Vec3, - color: [f32; 3], - ) -> Arc { - let push_constants = fs::PushConstants { - color: [color[0], color[1], color[2], 1.0], - direction: direction.extend(0.0).into(), - }; - - let layout = &self.pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [ - WriteDescriptorSet::image_view(0, color_input), - WriteDescriptorSet::image_view(1, normals_input), - ], - [], - ) - .unwrap(); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32], - depth_range: 0.0..=1.0, - }; - - let mut builder = AutoCommandBufferBuilder::secondary( - self.command_buffer_allocator.clone(), - self.gfx_queue.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(self.subpass.clone().into()), - ..Default::default() - }, - ) - .unwrap(); - - builder - .set_viewport(0, [viewport].into_iter().collect()) - .unwrap() - .bind_pipeline_graphics(self.pipeline.clone()) - .unwrap() - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - self.pipeline.layout().clone(), - 0, - descriptor_set, - ) - .unwrap() - .push_constants(self.pipeline.layout().clone(), 0, push_constants) - .unwrap() - .bind_vertex_buffers(0, self.vertex_buffer.clone()) - .unwrap(); - unsafe { builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0) }.unwrap(); - - builder.build().unwrap() - } -} - -mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", - } -} - -mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - - // The `color_input` parameter of the `draw` method. - layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput u_diffuse; - // The `normals_input` parameter of the `draw` method. - layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput u_normals; - - layout(push_constant) uniform PushConstants { - // The `color` parameter of the `draw` method. - vec4 color; - // The `direction` parameter of the `draw` method. - vec4 direction; - } push_constants; - - layout(location = 0) out vec4 f_color; - - void main() { - vec3 in_normal = normalize(subpassLoad(u_normals).rgb); - - // If the normal is perpendicular to the direction of the lighting, then - // `light_percent` will be 0. If the normal is parallel to the direction of the - // lightin, then `light_percent` will be 1. Any other angle will yield an - // intermediate value. - float light_percent = -dot(push_constants.direction.xyz, in_normal); - // `light_percent` must not go below 0.0. There's no such thing as negative lighting. - light_percent = max(light_percent, 0.0); - - vec3 in_diffuse = subpassLoad(u_diffuse).rgb; - f_color.rgb = light_percent * push_constants.color.rgb * in_diffuse; - f_color.a = 1.0; - } - ", - } -} diff --git a/examples/deferred/frame/mod.rs b/examples/deferred/frame/mod.rs deleted file mode 100644 index 9955683ada..0000000000 --- a/examples/deferred/frame/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -// This module exposes what is needed in order to draw with a deferred rendering system. -// -// The main code is in the `system` module, while the other modules implement the different kinds -// of lighting sources. - -pub use self::system::{FrameSystem, Pass}; -use vulkano::{buffer::BufferContents, pipeline::graphics::vertex_input::Vertex}; - -mod ambient_lighting_system; -mod directional_lighting_system; -mod point_lighting_system; -mod system; - -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct LightingVertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], -} diff --git a/examples/deferred/frame/point_lighting_system.rs b/examples/deferred/frame/point_lighting_system.rs deleted file mode 100644 index 2f7d7aebbf..0000000000 --- a/examples/deferred/frame/point_lighting_system.rs +++ /dev/null @@ -1,312 +0,0 @@ -use super::LightingVertex; -use glam::f32::{Mat4, Vec3}; -use std::sync::Arc; -use vulkano::{ - buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}, - command_buffer::{ - allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, - CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer, - }, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, - }, - device::Queue, - image::view::ImageView, - memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, - pipeline::{ - graphics::{ - color_blend::{ - AttachmentBlend, BlendFactor, BlendOp, ColorBlendAttachmentState, ColorBlendState, - }, - input_assembly::InputAssemblyState, - multisample::MultisampleState, - rasterization::RasterizationState, - vertex_input::{Vertex, VertexDefinition}, - viewport::{Viewport, ViewportState}, - GraphicsPipelineCreateInfo, - }, - layout::PipelineDescriptorSetLayoutCreateInfo, - DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, - PipelineShaderStageCreateInfo, - }, - render_pass::Subpass, -}; - -pub struct PointLightingSystem { - gfx_queue: Arc, - vertex_buffer: Subbuffer<[LightingVertex]>, - subpass: Subpass, - pipeline: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, -} - -impl PointLightingSystem { - /// Initializes the point lighting system. - pub fn new( - gfx_queue: Arc, - subpass: Subpass, - memory_allocator: Arc, - command_buffer_allocator: Arc, - descriptor_set_allocator: Arc, - ) -> PointLightingSystem { - let vertices = [ - LightingVertex { - position: [-1.0, -1.0], - }, - LightingVertex { - position: [-1.0, 3.0], - }, - LightingVertex { - position: [3.0, -1.0], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .expect("failed to create buffer"); - - let pipeline = { - let device = gfx_queue.device(); - let vs = vs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let fs = fs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let vertex_input_state = LightingVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState { - blend: Some(AttachmentBlend { - color_blend_op: BlendOp::Add, - src_color_blend_factor: BlendFactor::One, - dst_color_blend_factor: BlendFactor::One, - alpha_blend_op: BlendOp::Max, - src_alpha_blend_factor: BlendFactor::One, - dst_alpha_blend_factor: BlendFactor::One, - }), - ..Default::default() - }, - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.clone().into()), - ..GraphicsPipelineCreateInfo::new(layout) - }, - ) - .unwrap() - }; - - PointLightingSystem { - gfx_queue, - vertex_buffer, - subpass, - pipeline, - command_buffer_allocator, - descriptor_set_allocator, - } - } - - /// Builds a secondary command buffer that applies a point lighting. - /// - /// This secondary command buffer will read `depth_input` and rebuild the world position of the - /// pixel currently being processed (modulo rounding errors). It will then compare this - /// position with `position`, and process the lighting based on the distance and orientation - /// (similar to the directional lighting system). - /// - /// It then writes the output to the current framebuffer with additive blending (in other words - /// the value will be added to the existing value in the framebuffer, and not replace the - /// existing value). - /// - /// Note that in a real-world application, you probably want to pass additional parameters - /// such as some way to indicate the distance at which the lighting decrease. In this example - /// this value is hardcoded in the shader. - /// - /// - `viewport_dimensions` contains the dimensions of the current framebuffer. - /// - `color_input` is an image containing the albedo of each object of the scene. It is the - /// result of the deferred pass. - /// - `normals_input` is an image containing the normals of each object of the scene. It is the - /// result of the deferred pass. - /// - `depth_input` is an image containing the depth value of each pixel of the scene. It is - /// the result of the deferred pass. - /// - `screen_to_world` is a matrix that turns coordinates from framebuffer space into world - /// space. This matrix is used alongside with `depth_input` to determine the world - /// coordinates of each pixel being processed. - /// - `position` is the position of the spot light in world coordinates. - /// - `color` is the color of the light. - #[allow(clippy::too_many_arguments)] - pub fn draw( - &self, - viewport_dimensions: [u32; 2], - color_input: Arc, - normals_input: Arc, - depth_input: Arc, - screen_to_world: Mat4, - position: Vec3, - color: [f32; 3], - ) -> Arc { - let push_constants = fs::PushConstants { - screen_to_world: screen_to_world.to_cols_array_2d(), - color: [color[0], color[1], color[2], 1.0], - position: position.extend(0.0).into(), - }; - - let layout = &self.pipeline.layout().set_layouts()[0]; - let descriptor_set = DescriptorSet::new( - self.descriptor_set_allocator.clone(), - layout.clone(), - [ - WriteDescriptorSet::image_view(0, color_input), - WriteDescriptorSet::image_view(1, normals_input), - WriteDescriptorSet::image_view(2, depth_input), - ], - [], - ) - .unwrap(); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32], - depth_range: 0.0..=1.0, - }; - - let mut builder = AutoCommandBufferBuilder::secondary( - self.command_buffer_allocator.clone(), - self.gfx_queue.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(self.subpass.clone().into()), - ..Default::default() - }, - ) - .unwrap(); - - builder - .set_viewport(0, [viewport].into_iter().collect()) - .unwrap() - .bind_pipeline_graphics(self.pipeline.clone()) - .unwrap() - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - self.pipeline.layout().clone(), - 0, - descriptor_set, - ) - .unwrap() - .push_constants(self.pipeline.layout().clone(), 0, push_constants) - .unwrap() - .bind_vertex_buffers(0, self.vertex_buffer.clone()) - .unwrap(); - unsafe { builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0) }.unwrap(); - - builder.build().unwrap() - } -} - -mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - layout(location = 0) out vec2 v_screen_coords; - - void main() { - v_screen_coords = position; - gl_Position = vec4(position, 0.0, 1.0); - } - ", - } -} - -mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - - // The `color_input` parameter of the `draw` method. - layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput u_diffuse; - // The `normals_input` parameter of the `draw` method. - layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput u_normals; - // The `depth_input` parameter of the `draw` method. - layout(input_attachment_index = 2, set = 0, binding = 2) uniform subpassInput u_depth; - - layout(push_constant) uniform PushConstants { - // The `screen_to_world` parameter of the `draw` method. - mat4 screen_to_world; - // The `color` parameter of the `draw` method. - vec4 color; - // The `position` parameter of the `draw` method. - vec4 position; - } push_constants; - - layout(location = 0) in vec2 v_screen_coords; - layout(location = 0) out vec4 f_color; - - void main() { - float in_depth = subpassLoad(u_depth).x; - - // Any depth superior or equal to 1.0 means that the pixel has been untouched by - // the deferred pass. We don't want to deal with them. - if (in_depth >= 1.0) { - discard; - } - - // Find the world coordinates of the current pixel. - vec4 world = push_constants.screen_to_world * vec4(v_screen_coords, in_depth, 1.0); - world /= world.w; - - vec3 in_normal = normalize(subpassLoad(u_normals).rgb); - vec3 light_direction = normalize(push_constants.position.xyz - world.xyz); - - // Calculate the percent of lighting that is received based on the orientation of - // the normal and the direction of the light. - float light_percent = max(-dot(light_direction, in_normal), 0.0); - - float light_distance = length(push_constants.position.xyz - world.xyz); - // Further decrease light_percent based on the distance with the light position. - light_percent *= 1.0 / exp(light_distance); - - vec3 in_diffuse = subpassLoad(u_diffuse).rgb; - f_color.rgb = push_constants.color.rgb * light_percent * in_diffuse; - f_color.a = 1.0; - } - ", - } -} diff --git a/examples/deferred/frame/system.rs b/examples/deferred/frame/system.rs deleted file mode 100644 index c99c533c8d..0000000000 --- a/examples/deferred/frame/system.rs +++ /dev/null @@ -1,578 +0,0 @@ -use super::{ - ambient_lighting_system::AmbientLightingSystem, - directional_lighting_system::DirectionalLightingSystem, - point_lighting_system::PointLightingSystem, -}; -use glam::f32::{Mat4, Vec3}; -use std::sync::Arc; -use vulkano::{ - command_buffer::{ - allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - PrimaryAutoCommandBuffer, RenderPassBeginInfo, SecondaryAutoCommandBuffer, - SubpassBeginInfo, SubpassContents, - }, - descriptor_set::allocator::StandardDescriptorSetAllocator, - device::Queue, - format::Format, - image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage}, - memory::allocator::{AllocationCreateInfo, StandardMemoryAllocator}, - render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - sync::GpuFuture, -}; - -/// System that contains the necessary facilities for rendering a single frame. -pub struct FrameSystem { - // Queue to use to render everything. - gfx_queue: Arc, - - // Render pass used for the drawing. See the `new` method for the actual render pass content. - // We need to keep it in `FrameSystem` because we may want to recreate the intermediate buffers - // in of a change in the dimensions. - render_pass: Arc, - - memory_allocator: Arc, - command_buffer_allocator: Arc, - - // Intermediate render target that will contain the albedo of each pixel of the scene. - diffuse_buffer: Arc, - // Intermediate render target that will contain the normal vector in world coordinates of each - // pixel of the scene. - // The normal vector is the vector perpendicular to the surface of the object at this point. - normals_buffer: Arc, - // Intermediate render target that will contain the depth of each pixel of the scene. - // This is a traditional depth buffer. `0.0` means "near", and `1.0` means "far". - depth_buffer: Arc, - - // Will allow us to add an ambient lighting to a scene during the second subpass. - ambient_lighting_system: AmbientLightingSystem, - // Will allow us to add a directional light to a scene during the second subpass. - directional_lighting_system: DirectionalLightingSystem, - // Will allow us to add a spot light source to a scene during the second subpass. - point_lighting_system: PointLightingSystem, -} - -impl FrameSystem { - /// Initializes the frame system. - /// - /// Should be called at initialization, as it can take some time to build. - /// - /// - `gfx_queue` is the queue that will be used to perform the main rendering. - /// - `final_output_format` is the format of the image that will later be passed to the - /// `frame()` method. We need to know that in advance. If that format ever changes, we have - /// to create a new `FrameSystem`. - pub fn new( - gfx_queue: Arc, - final_output_format: Format, - memory_allocator: Arc, - command_buffer_allocator: Arc, - ) -> FrameSystem { - // Creating the render pass. - // - // The render pass has two subpasses. In the first subpass, we draw all the objects of the - // scene. Note that it is not the `FrameSystem` that is responsible for the drawing, - // instead it only provides an API that allows the user to do so. - // - // The drawing of the objects will write to the `diffuse`, `normals` and `depth` - // attachments. - // - // Then in the second subpass, we read these three attachments as input attachments and - // draw to `final_color`. Each draw operation performed in this second subpass has its - // value added to `final_color` and not replaced, thanks to blending. - // - // > **Warning**: If the red, green or blue component of the final image goes over `1.0` - // > then it will be clamped. For example a pixel of `[2.0, 1.0, 1.0]` (which is red) will - // > be clamped to `[1.0, 1.0, 1.0]` (which is white) instead of being converted to - // > `[1.0, 0.5, 0.5]` as desired. In a real-life application you want to use an additional - // > intermediate image with a floating-point format, then perform additional passes to - // > convert all the colors in the correct range. These techniques are known as HDR and - // > tone mapping. - // - // Input attachments are a special kind of way to read images. You can only read from them - // from a fragment shader, and you can only read the pixel corresponding to the pixel - // currently being processed by the fragment shader. If you want to read from attachments - // but can't deal with these restrictions, then you should create multiple render passes - // instead. - let render_pass = vulkano::ordered_passes_renderpass!( - gfx_queue.device().clone(), - attachments: { - // The image that will contain the final rendering (in this example the swapchain - // image, but it could be another image). - final_color: { - format: final_output_format, - samples: 1, - load_op: Clear, - store_op: Store, - }, - // Will be bound to `self.diffuse_buffer`. - diffuse: { - format: Format::A2B10G10R10_UNORM_PACK32, - samples: 1, - load_op: Clear, - store_op: DontCare, - }, - // Will be bound to `self.normals_buffer`. - normals: { - format: Format::R16G16B16A16_SFLOAT, - samples: 1, - load_op: Clear, - store_op: DontCare, - }, - // Will be bound to `self.depth_buffer`. - depth_stencil: { - format: Format::D16_UNORM, - samples: 1, - load_op: Clear, - store_op: DontCare, - }, - }, - passes: [ - // Write to the diffuse, normals and depth attachments. - { - color: [diffuse, normals], - depth_stencil: {depth_stencil}, - input: [], - }, - // Apply lighting by reading these three attachments and writing to `final_color`. - { - color: [final_color], - depth_stencil: {}, - input: [diffuse, normals, depth_stencil], - }, - ], - ) - .unwrap(); - - // For now we create three temporary images with a dimension of 1 by 1 pixel. These images - // will be replaced the first time we call `frame()`. - let diffuse_buffer = ImageView::new_default( - Image::new( - memory_allocator.clone(), - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::A2B10G10R10_UNORM_PACK32, - extent: [1, 1, 1], - usage: ImageUsage::COLOR_ATTACHMENT - | ImageUsage::TRANSIENT_ATTACHMENT - | ImageUsage::INPUT_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - ) - .unwrap(), - ) - .unwrap(); - let normals_buffer = ImageView::new_default( - Image::new( - memory_allocator.clone(), - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::R16G16B16A16_SFLOAT, - extent: [1, 1, 1], - usage: ImageUsage::TRANSIENT_ATTACHMENT | ImageUsage::INPUT_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - ) - .unwrap(), - ) - .unwrap(); - let depth_buffer = ImageView::new_default( - Image::new( - memory_allocator.clone(), - ImageCreateInfo { - image_type: ImageType::Dim2d, - format: Format::D16_UNORM, - extent: [1, 1, 1], - usage: ImageUsage::TRANSIENT_ATTACHMENT | ImageUsage::INPUT_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - ) - .unwrap(), - ) - .unwrap(); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - gfx_queue.device().clone(), - Default::default(), - )); - - // Initialize the three lighting systems. Note that we need to pass to them the subpass - // where they will be executed. - let lighting_subpass = Subpass::from(render_pass.clone(), 1).unwrap(); - let ambient_lighting_system = AmbientLightingSystem::new( - gfx_queue.clone(), - lighting_subpass.clone(), - memory_allocator.clone(), - command_buffer_allocator.clone(), - descriptor_set_allocator.clone(), - ); - let directional_lighting_system = DirectionalLightingSystem::new( - gfx_queue.clone(), - lighting_subpass.clone(), - memory_allocator.clone(), - command_buffer_allocator.clone(), - descriptor_set_allocator.clone(), - ); - let point_lighting_system = PointLightingSystem::new( - gfx_queue.clone(), - lighting_subpass, - memory_allocator.clone(), - command_buffer_allocator.clone(), - descriptor_set_allocator, - ); - - FrameSystem { - gfx_queue, - render_pass, - memory_allocator, - command_buffer_allocator, - diffuse_buffer, - normals_buffer, - depth_buffer, - ambient_lighting_system, - directional_lighting_system, - point_lighting_system, - } - } - - /// Returns the subpass of the render pass where the rendering should write info to gbuffers. - /// - /// Has two outputs: the diffuse color (3 components) and the normals in world coordinates - /// (3 components). Also has a depth attachment. - /// - /// This method is necessary in order to initialize the pipelines that will draw the objects - /// of the scene. - #[inline] - pub fn deferred_subpass(&self) -> Subpass { - Subpass::from(self.render_pass.clone(), 0).unwrap() - } - - /// Starts drawing a new frame. - /// - /// - `before_future` is the future after which the main rendering should be executed. - /// - `final_image` is the image we are going to draw to. - /// - `world_to_framebuffer` is the matrix that will be used to convert from 3D coordinates in - /// the world into 2D coordinates on the framebuffer. - pub fn frame( - &mut self, - before_future: F, - final_image_view: Arc, - world_to_framebuffer: Mat4, - ) -> Frame - where - F: GpuFuture + 'static, - { - // First of all we recreate `self.diffuse_buffer`, `self.normals_buffer` and - // `self.depth_buffer` if their extent doesn't match the extent of the final image. - let extent = final_image_view.image().extent(); - if self.diffuse_buffer.image().extent() != extent { - // Note that we create "transient" images here. This means that the content of the - // image is only defined when within a render pass. In other words you can draw to - // them in a subpass then read them in another subpass, but as soon as you leave the - // render pass their content becomes undefined. - self.diffuse_buffer = ImageView::new_default( - Image::new( - self.memory_allocator.clone(), - ImageCreateInfo { - extent, - format: Format::A2B10G10R10_UNORM_PACK32, - usage: ImageUsage::COLOR_ATTACHMENT - | ImageUsage::TRANSIENT_ATTACHMENT - | ImageUsage::INPUT_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - ) - .unwrap(), - ) - .unwrap(); - self.normals_buffer = ImageView::new_default( - Image::new( - self.memory_allocator.clone(), - ImageCreateInfo { - extent, - format: Format::R16G16B16A16_SFLOAT, - usage: ImageUsage::COLOR_ATTACHMENT - | ImageUsage::TRANSIENT_ATTACHMENT - | ImageUsage::INPUT_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - ) - .unwrap(), - ) - .unwrap(); - self.depth_buffer = ImageView::new_default( - Image::new( - self.memory_allocator.clone(), - ImageCreateInfo { - extent, - format: Format::D16_UNORM, - usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT - | ImageUsage::TRANSIENT_ATTACHMENT - | ImageUsage::INPUT_ATTACHMENT, - ..Default::default() - }, - AllocationCreateInfo::default(), - ) - .unwrap(), - ) - .unwrap(); - } - - // Build the framebuffer. The image must be attached in the same order as they were defined - // with the `ordered_passes_renderpass!` macro. - let framebuffer = Framebuffer::new( - self.render_pass.clone(), - FramebufferCreateInfo { - attachments: vec![ - final_image_view, - self.diffuse_buffer.clone(), - self.normals_buffer.clone(), - self.depth_buffer.clone(), - ], - ..Default::default() - }, - ) - .unwrap(); - - // Start the command buffer builder that will be filled throughout the frame handling. - let mut command_buffer_builder = AutoCommandBufferBuilder::primary( - self.command_buffer_allocator.clone(), - self.gfx_queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - command_buffer_builder - .begin_render_pass( - RenderPassBeginInfo { - clear_values: vec![ - Some([0.0, 0.0, 0.0, 0.0].into()), - Some([0.0, 0.0, 0.0, 0.0].into()), - Some([0.0, 0.0, 0.0, 0.0].into()), - Some(1.0f32.into()), - ], - ..RenderPassBeginInfo::framebuffer(framebuffer.clone()) - }, - SubpassBeginInfo { - contents: SubpassContents::SecondaryCommandBuffers, - ..Default::default() - }, - ) - .unwrap(); - - Frame { - system: self, - before_main_cb_future: Some(Box::new(before_future)), - framebuffer, - num_pass: 0, - command_buffer_builder: Some(command_buffer_builder), - world_to_framebuffer, - } - } -} - -/// Represents the active process of rendering a frame. -/// -/// This struct mutably borrows the `FrameSystem`. -pub struct Frame<'a> { - // The `FrameSystem`. - system: &'a mut FrameSystem, - - // The active pass we are in. This keeps track of the step we are in. - // - If `num_pass` is 0, then we haven't start anything yet. - // - If `num_pass` is 1, then we have finished drawing all the objects of the scene. - // - If `num_pass` is 2, then we have finished applying lighting. - // - Otherwise the frame is finished. - // In a more complex application you can have dozens of passes, in which case you probably - // don't want to document them all here. - num_pass: u8, - - // Future to wait upon before the main rendering. - before_main_cb_future: Option>, - // Framebuffer that was used when starting the render pass. - framebuffer: Arc, - // The command buffer builder that will be built during the lifetime of this object. - command_buffer_builder: Option>, - // Matrix that was passed to `frame()`. - world_to_framebuffer: Mat4, -} - -impl<'a> Frame<'a> { - /// Returns an enumeration containing the next pass of the rendering. - pub fn next_pass<'f>(&'f mut self) -> Option> { - // This function reads `num_pass` increments its value, and returns a struct corresponding - // to that pass that the user will be able to manipulate in order to customize the pass. - let current_pass = self.num_pass; - self.num_pass += 1; - - match current_pass { - 0 => { - // If we are in the pass 0 then we haven't start anything yet. - // We already called `begin_render_pass` (in the `frame()` method), and that's the - // state we are in. - // We return an object that will allow the user to draw objects on the scene. - Some(Pass::Deferred(DrawPass { frame: self })) - } - - 1 => { - // If we are in pass 1 then we have finished drawing the objects on the scene. - // Going to the next subpass. - self.command_buffer_builder - .as_mut() - .unwrap() - .next_subpass( - Default::default(), - SubpassBeginInfo { - contents: SubpassContents::SecondaryCommandBuffers, - ..Default::default() - }, - ) - .unwrap(); - - // And returning an object that will allow the user to apply lighting to the scene. - Some(Pass::Lighting(LightingPass { frame: self })) - } - - 2 => { - // If we are in pass 2 then we have finished applying lighting. - // We take the builder, call `end_render_pass()`, and then `build()` it to obtain - // an actual command buffer. - self.command_buffer_builder - .as_mut() - .unwrap() - .end_render_pass(Default::default()) - .unwrap(); - let command_buffer = self.command_buffer_builder.take().unwrap().build().unwrap(); - - // Extract `before_main_cb_future` and append the command buffer execution to it. - let after_main_cb = self - .before_main_cb_future - .take() - .unwrap() - .then_execute(self.system.gfx_queue.clone(), command_buffer) - .unwrap(); - // We obtain `after_main_cb`, which we give to the user. - Some(Pass::Finished(Box::new(after_main_cb))) - } - - // If the pass is over 2 then the frame is in the finished state and can't do anything - // more. - _ => None, - } - } -} - -/// Struct provided to the user that allows them to customize or handle the pass. -pub enum Pass<'f, 's: 'f> { - /// We are in the pass where we draw objects on the scene. The `DrawPass` allows the user to - /// draw the objects. - Deferred(DrawPass<'f, 's>), - - /// We are in the pass where we add lighting to the scene. The `LightingPass` allows the user - /// to add light sources. - Lighting(LightingPass<'f, 's>), - - /// The frame has been fully prepared, and here is the future that will perform the drawing - /// on the image. - Finished(Box), -} - -/// Allows the user to draw objects on the scene. -pub struct DrawPass<'f, 's: 'f> { - frame: &'f mut Frame<'s>, -} - -impl<'f, 's: 'f> DrawPass<'f, 's> { - /// Appends a command that executes a secondary command buffer that performs drawing. - pub fn execute(&mut self, command_buffer: Arc) { - self.frame - .command_buffer_builder - .as_mut() - .unwrap() - .execute_commands(command_buffer) - .unwrap(); - } - - /// Returns the dimensions in pixels of the viewport. - pub fn viewport_dimensions(&self) -> [u32; 2] { - self.frame.framebuffer.extent() - } - - /// Returns the 4x4 matrix that turns world coordinates into 2D coordinates on the framebuffer. - #[allow(dead_code)] - pub fn world_to_framebuffer_matrix(&self) -> Mat4 { - self.frame.world_to_framebuffer - } -} - -/// Allows the user to apply lighting on the scene. -pub struct LightingPass<'f, 's: 'f> { - frame: &'f mut Frame<'s>, -} - -impl<'f, 's: 'f> LightingPass<'f, 's> { - /// Applies an ambient lighting to the scene. - /// - /// All the objects will be colored with an intensity of `color`. - pub fn ambient_light(&mut self, color: [f32; 3]) { - let command_buffer = self.frame.system.ambient_lighting_system.draw( - self.frame.framebuffer.extent(), - self.frame.system.diffuse_buffer.clone(), - color, - ); - self.frame - .command_buffer_builder - .as_mut() - .unwrap() - .execute_commands(command_buffer) - .unwrap(); - } - - /// Applies an directional lighting to the scene. - /// - /// All the objects will be colored with an intensity varying between `[0, 0, 0]` and `color`, - /// depending on the dot product of their normal and `direction`. - pub fn directional_light(&mut self, direction: Vec3, color: [f32; 3]) { - let command_buffer = self.frame.system.directional_lighting_system.draw( - self.frame.framebuffer.extent(), - self.frame.system.diffuse_buffer.clone(), - self.frame.system.normals_buffer.clone(), - direction, - color, - ); - self.frame - .command_buffer_builder - .as_mut() - .unwrap() - .execute_commands(command_buffer) - .unwrap(); - } - - /// Applies a spot lighting to the scene. - /// - /// All the objects will be colored with an intensity varying between `[0, 0, 0]` and `color`, - /// depending on their distance with `position`. Objects that aren't facing `position` won't - /// receive any light. - pub fn point_light(&mut self, position: Vec3, color: [f32; 3]) { - let command_buffer = { - self.frame.system.point_lighting_system.draw( - self.frame.framebuffer.extent(), - self.frame.system.diffuse_buffer.clone(), - self.frame.system.normals_buffer.clone(), - self.frame.system.depth_buffer.clone(), - self.frame.world_to_framebuffer.inverse(), - position, - color, - ) - }; - - self.frame - .command_buffer_builder - .as_mut() - .unwrap() - .execute_commands(command_buffer) - .unwrap(); - } -} diff --git a/examples/deferred/main.rs b/examples/deferred/main.rs index b9bba12fce..0907b234c1 100644 --- a/examples/deferred/main.rs +++ b/examples/deferred/main.rs @@ -16,29 +16,28 @@ // expensive otherwise. It has some drawbacks, which are the fact that transparent objects must be // drawn after the lighting, and that the whole process consumes more memory. -use crate::{ - frame::{FrameSystem, Pass}, - triangle_draw_system::TriangleDrawSystem, -}; -use glam::f32::{Mat4, Vec3}; +use deferred::DeferredTask; +use scene::SceneTask; use std::{error::Error, sync::Arc}; use vulkano::{ - command_buffer::allocator::{ - StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo, - }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags, }, - image::{view::ImageView, ImageUsage}, + format::Format, + image::{Image, ImageCreateInfo, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}, - memory::allocator::StandardMemoryAllocator, - swapchain::{ - acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, - }, - sync::{self, GpuFuture}, + memory::allocator::AllocationCreateInfo, + pipeline::graphics::viewport::Viewport, + swapchain::{Surface, Swapchain, SwapchainCreateInfo}, Validated, VulkanError, VulkanLibrary, }; +use vulkano_taskgraph::{ + descriptor_set::{BindlessContext, BindlessContextCreateInfo, LocalDescriptorSetCreateInfo}, + graph::{AttachmentInfo, CompileInfo, ExecutableTaskGraph, ExecuteError, TaskGraph}, + resource::{AccessTypes, Flight, ImageLayoutType, Resources, ResourcesCreateInfo}, + resource_map, Id, QueueFamilyType, +}; use winit::{ application::ApplicationHandler, event::WindowEvent, @@ -46,8 +45,10 @@ use winit::{ window::{Window, WindowId}, }; -mod frame; -mod triangle_draw_system; +mod deferred; +mod scene; + +const MAX_FRAMES_IN_FLIGHT: u32 = 2; fn main() -> Result<(), impl Error> { // Basic initialization. See the triangle example if you want more details about this. @@ -62,19 +63,24 @@ struct App { instance: Arc, device: Arc, queue: Arc, - memory_allocator: Arc, - command_buffer_allocator: Arc, + resources: Arc, + flight_id: Id, rcx: Option, } -struct RenderContext { +pub struct RenderContext { window: Arc, - swapchain: Arc, - images: Vec>, - frame_system: FrameSystem, - triangle_draw_system: TriangleDrawSystem, + swapchain_id: Id, + diffuse_image_id: Id, + normals_image_id: Id, + depth_image_id: Id, + viewport: Viewport, recreate_swapchain: bool, - previous_frame_end: Option>, + task_graph: ExecutableTaskGraph, + virtual_swapchain_id: Id, + virtual_diffuse_image_id: Id, + virtual_normals_image_id: Id, + virtual_depth_image_id: Id, } impl App { @@ -93,12 +99,16 @@ impl App { let device_extensions = DeviceExtensions { khr_swapchain: true, - ..DeviceExtensions::empty() + ..BindlessContext::required_extensions(&instance) }; + let device_features = BindlessContext::required_features(&instance); let (physical_device, queue_family_index) = instance .enumerate_physical_devices() .unwrap() - .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter(|p| { + p.supported_extensions().contains(&device_extensions) + && p.supported_features().contains(&device_features) + }) .filter_map(|p| { p.queue_family_properties() .iter() @@ -129,6 +139,7 @@ impl App { physical_device, DeviceCreateInfo { enabled_extensions: device_extensions, + enabled_features: device_features, queue_create_infos: vec![QueueCreateInfo { queue_family_index, ..Default::default() @@ -140,21 +151,26 @@ impl App { let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - StandardCommandBufferAllocatorCreateInfo { - secondary_buffer_count: 32, + let resources = Resources::new( + &device, + &ResourcesCreateInfo { + bindless_context: Some(&BindlessContextCreateInfo { + local_set: Some(&LocalDescriptorSetCreateInfo::default()), + ..Default::default() + }), ..Default::default() }, - )); + ) + .unwrap(); + + let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); App { instance, device, queue, - memory_allocator, - command_buffer_allocator, + resources, + flight_id, rcx: None, } } @@ -170,68 +186,211 @@ impl ApplicationHandler for App { let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); let window_size = window.inner_size(); - let (swapchain, images) = { + let swapchain_format; + let swapchain_id = { let surface_capabilities = self .device .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - let (image_format, _) = self + (swapchain_format, _) = self .device .physical_device() .surface_formats(&surface, Default::default()) .unwrap()[0]; - let (swapchain, images) = Swapchain::new( - self.device.clone(), - surface, - SwapchainCreateInfo { - min_image_count: surface_capabilities.min_image_count.max(2), - image_format, - image_extent: window_size.into(), - image_usage: ImageUsage::COLOR_ATTACHMENT, - composite_alpha: surface_capabilities - .supported_composite_alpha - .into_iter() - .next() - .unwrap(), + self.resources + .create_swapchain( + self.flight_id, + surface, + SwapchainCreateInfo { + min_image_count: surface_capabilities.min_image_count.max(2), + image_format: swapchain_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + ..Default::default() + }, + ) + .unwrap() + }; + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let (diffuse_image_id, normals_image_id, depth_image_id) = + window_size_dependent_setup(&self.resources, swapchain_id); + + let mut task_graph = TaskGraph::new(&self.resources, 2, 4); + + let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo { + image_format: swapchain_format, + ..Default::default() + }); + // We create the virtual images with the `TRANSIENT_ATTACHMENT` usage to allow the task + // graph compiler to optimize based on it. The physical images that are later assigned must + // have been created with this usage. + let virtual_diffuse_image_id = task_graph.add_image(&ImageCreateInfo { + format: Format::A2B10G10R10_UNORM_PACK32, + usage: ImageUsage::TRANSIENT_ATTACHMENT, + ..Default::default() + }); + let virtual_normals_image_id = task_graph.add_image(&ImageCreateInfo { + format: Format::R16G16B16A16_SFLOAT, + usage: ImageUsage::TRANSIENT_ATTACHMENT, + ..Default::default() + }); + let virtual_depth_image_id = task_graph.add_image(&ImageCreateInfo { + format: Format::D16_UNORM, + usage: ImageUsage::TRANSIENT_ATTACHMENT, + ..Default::default() + }); + let virtual_framebuffer_id = task_graph.add_framebuffer(); + + let scene_node_id = task_graph + .create_task_node( + "Scene", + QueueFamilyType::Graphics, + SceneTask::new( + self, + virtual_diffuse_image_id, + virtual_normals_image_id, + virtual_depth_image_id, + ), + ) + .framebuffer(virtual_framebuffer_id) + // We only need `COLOR_ATTACHMENT_WRITE` for the color attachments here because the + // scene pipeline has color blending disabled, which would otherwise be a read as well. + .color_attachment( + virtual_diffuse_image_id, + AccessTypes::COLOR_ATTACHMENT_WRITE, + ImageLayoutType::Optimal, + &AttachmentInfo { + index: 0, + clear: true, ..Default::default() }, ) - .unwrap(); + .color_attachment( + virtual_normals_image_id, + AccessTypes::COLOR_ATTACHMENT_WRITE, + ImageLayoutType::Optimal, + &AttachmentInfo { + index: 1, + clear: true, + ..Default::default() + }, + ) + // We need both `DEPTH_STENCIL_ATTACHMENT_READ` and `DEPTH_STENCIL_ATTACHMENT_WRITE` + // for the depth/stencil attachment, as the scene pipeline has both depth tests (read) + // and depth writes (write) enabled. + .depth_stencil_attachment( + virtual_depth_image_id, + AccessTypes::DEPTH_STENCIL_ATTACHMENT_READ + | AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE, + ImageLayoutType::Optimal, + &AttachmentInfo { + clear: true, + ..Default::default() + }, + ) + .build(); + let deferred_node_id = task_graph + .create_task_node( + "Deferred", + QueueFamilyType::Graphics, + DeferredTask::new(self, virtual_swapchain_id), + ) + .framebuffer(virtual_framebuffer_id) + // The deferred lighting pipelines have color blending enabled, so we need both + // `COLOR_ATTACHMENT_READ` and `COLOR_ATTACHMENT_WRITE`. + .color_attachment( + virtual_swapchain_id.current_image_id(), + AccessTypes::COLOR_ATTACHMENT_READ | AccessTypes::COLOR_ATTACHMENT_WRITE, + ImageLayoutType::Optimal, + &AttachmentInfo { + clear: true, + ..Default::default() + }, + ) + .input_attachment( + virtual_diffuse_image_id, + AccessTypes::FRAGMENT_SHADER_COLOR_INPUT_ATTACHMENT_READ, + ImageLayoutType::Optimal, + &AttachmentInfo { + index: 0, + ..Default::default() + }, + ) + .input_attachment( + virtual_normals_image_id, + AccessTypes::FRAGMENT_SHADER_COLOR_INPUT_ATTACHMENT_READ, + ImageLayoutType::Optimal, + &AttachmentInfo { + index: 1, + ..Default::default() + }, + ) + .input_attachment( + virtual_depth_image_id, + AccessTypes::FRAGMENT_SHADER_DEPTH_STENCIL_INPUT_ATTACHMENT_READ, + ImageLayoutType::Optimal, + &AttachmentInfo { + index: 2, + ..Default::default() + }, + ) + .build(); - (swapchain, images) - }; + task_graph + .add_edge(scene_node_id, deferred_node_id) + .unwrap(); - let images = images - .into_iter() - .map(|image| ImageView::new_default(image).unwrap()) - .collect::>(); - - // Here is the basic initialization for the deferred system. - let frame_system = FrameSystem::new( - self.queue.clone(), - swapchain.image_format(), - self.memory_allocator.clone(), - self.command_buffer_allocator.clone(), - ); - let triangle_draw_system = TriangleDrawSystem::new( - self.queue.clone(), - frame_system.deferred_subpass(), - self.memory_allocator.clone(), - self.command_buffer_allocator.clone(), - ); + let mut task_graph = unsafe { + task_graph.compile(&CompileInfo { + queues: &[&self.queue], + present_queue: Some(&self.queue), + flight_id: self.flight_id, + ..Default::default() + }) + } + .unwrap(); - let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + let scene_node = task_graph.task_node_mut(scene_node_id).unwrap(); + let subpass = scene_node.subpass().unwrap().clone(); + scene_node + .task_mut() + .downcast_mut::() + .unwrap() + .create_pipeline(self, subpass); + let deferred_node = task_graph.task_node_mut(deferred_node_id).unwrap(); + let subpass = deferred_node.subpass().unwrap().clone(); + deferred_node + .task_mut() + .downcast_mut::() + .unwrap() + .create_pipelines(self, subpass); self.rcx = Some(RenderContext { window, - swapchain, - images, - frame_system, - triangle_draw_system, + swapchain_id, + diffuse_image_id, + normals_image_id, + depth_image_id, + viewport, recreate_swapchain: false, - previous_frame_end, + task_graph, + virtual_swapchain_id, + virtual_diffuse_image_id, + virtual_normals_image_id, + virtual_depth_image_id, }); } @@ -257,91 +416,61 @@ impl ApplicationHandler for App { return; } - rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + let flight = self.resources.flight(self.flight_id).unwrap(); if rcx.recreate_swapchain { - let (new_swapchain, new_images) = rcx - .swapchain - .recreate(SwapchainCreateInfo { + rcx.swapchain_id = self + .resources + .recreate_swapchain(rcx.swapchain_id, |create_info| SwapchainCreateInfo { image_extent: window_size.into(), - ..rcx.swapchain.create_info() + ..create_info }) .expect("failed to recreate swapchain"); - let new_images = new_images - .into_iter() - .map(|image| ImageView::new_default(image).unwrap()) - .collect::>(); - rcx.swapchain = new_swapchain; - rcx.images = new_images; - rcx.recreate_swapchain = false; - } + rcx.viewport.extent = window_size.into(); - let (image_index, suboptimal, acquire_future) = match acquire_next_image( - rcx.swapchain.clone(), - None, - ) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - rcx.recreate_swapchain = true; - return; - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; + // FIXME(taskgraph): safe resource destruction + flight + .wait_for_frame(flight.current_frame() - 1, None) + .unwrap(); - if suboptimal { - rcx.recreate_swapchain = true; - } + unsafe { self.resources.remove_image(rcx.diffuse_image_id) }.unwrap(); + unsafe { self.resources.remove_image(rcx.normals_image_id) }.unwrap(); + unsafe { self.resources.remove_image(rcx.depth_image_id) }.unwrap(); - let future = rcx.previous_frame_end.take().unwrap().join(acquire_future); - let mut frame = rcx.frame_system.frame( - future, - rcx.images[image_index as usize].clone(), - Mat4::IDENTITY, - ); - let mut after_future = None; - while let Some(pass) = frame.next_pass() { - match pass { - Pass::Deferred(mut draw_pass) => { - let cb = rcx - .triangle_draw_system - .draw(draw_pass.viewport_dimensions()); - draw_pass.execute(cb); - } - Pass::Lighting(mut lighting) => { - lighting.ambient_light([0.1, 0.1, 0.1]); - lighting.directional_light(Vec3::new(0.2, -0.1, -0.7), [0.6, 0.6, 0.6]); - lighting.point_light(Vec3::new(0.5, -0.5, -0.1), [1.0, 0.0, 0.0]); - lighting.point_light(Vec3::new(-0.9, 0.2, -0.15), [0.0, 1.0, 0.0]); - lighting.point_light(Vec3::new(0.0, 0.5, -0.05), [0.0, 0.0, 1.0]); - } - Pass::Finished(af) => { - after_future = Some(af); - } - } + ( + rcx.diffuse_image_id, + rcx.normals_image_id, + rcx.depth_image_id, + ) = window_size_dependent_setup(&self.resources, rcx.swapchain_id); + + rcx.recreate_swapchain = false; } - let future = after_future - .unwrap() - .then_swapchain_present( - self.queue.clone(), - SwapchainPresentInfo::new(rcx.swapchain.clone(), image_index), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - rcx.previous_frame_end = Some(future.boxed()); - } - Err(VulkanError::OutOfDate) => { + flight.wait(None).unwrap(); + + let resource_map = resource_map!( + &rcx.task_graph, + rcx.virtual_swapchain_id => rcx.swapchain_id, + rcx.virtual_diffuse_image_id => rcx.diffuse_image_id, + rcx.virtual_normals_image_id => rcx.normals_image_id, + rcx.virtual_depth_image_id => rcx.depth_image_id, + ) + .unwrap(); + + match unsafe { + rcx.task_graph + .execute(resource_map, rcx, || rcx.window.pre_present_notify()) + } { + Ok(()) => {} + Err(ExecuteError::Swapchain { + error: Validated::Error(VulkanError::OutOfDate), + .. + }) => { rcx.recreate_swapchain = true; - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { - println!("failed to flush future: {e}"); - rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + panic!("failed to execute next frame: {e:?}"); } } } @@ -354,3 +483,59 @@ impl ApplicationHandler for App { rcx.window.request_redraw(); } } + +/// This function is called once during initialization, then again whenever the window is resized. +fn window_size_dependent_setup( + resources: &Resources, + swapchain_id: Id, +) -> (Id, Id, Id) { + let swapchain_state = resources.swapchain(swapchain_id).unwrap(); + let images = swapchain_state.images(); + let extent = images[0].extent(); + + // Note that we create "transient" images here. This means that the content of the image is + // only defined when within a render pass. In other words you can draw to them in a subpass + // then read them in another subpass, but as soon as you leave the render pass their content + // becomes undefined. + let diffuse_image_id = resources + .create_image( + ImageCreateInfo { + extent, + format: Format::A2B10G10R10_UNORM_PACK32, + usage: ImageUsage::COLOR_ATTACHMENT + | ImageUsage::TRANSIENT_ATTACHMENT + | ImageUsage::INPUT_ATTACHMENT, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap(); + let normals_image_id = resources + .create_image( + ImageCreateInfo { + extent, + format: Format::R16G16B16A16_SFLOAT, + usage: ImageUsage::COLOR_ATTACHMENT + | ImageUsage::TRANSIENT_ATTACHMENT + | ImageUsage::INPUT_ATTACHMENT, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap(); + let depth_image_id = resources + .create_image( + ImageCreateInfo { + extent, + format: Format::D16_UNORM, + usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::TRANSIENT_ATTACHMENT + | ImageUsage::INPUT_ATTACHMENT, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap(); + + (diffuse_image_id, normals_image_id, depth_image_id) +} diff --git a/examples/deferred/scene.rs b/examples/deferred/scene.rs new file mode 100644 index 0000000000..4206ea050b --- /dev/null +++ b/examples/deferred/scene.rs @@ -0,0 +1,207 @@ +use crate::{App, RenderContext}; +use std::{slice, sync::Arc}; +use vulkano::{ + buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, + image::Image, + memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter}, + pipeline::{ + graphics::{ + color_blend::{ColorBlendAttachmentState, ColorBlendState}, + depth_stencil::{DepthState, DepthStencilState}, + input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, + vertex_input::{Vertex, VertexDefinition}, + viewport::ViewportState, + GraphicsPipelineCreateInfo, + }, + DynamicState, GraphicsPipeline, PipelineShaderStageCreateInfo, + }, + render_pass::Subpass, +}; +use vulkano_taskgraph::{ + command_buffer::RecordingCommandBuffer, resource::HostAccessType, ClearValues, Id, Task, + TaskContext, TaskResult, +}; + +pub struct SceneTask { + pipeline: Option>, + vertex_buffer_id: Id, + diffuse_image_id: Id, + normals_image_id: Id, + depth_image_id: Id, +} + +impl SceneTask { + pub fn new( + app: &App, + virtual_diffuse_image_id: Id, + virtual_normals_image_id: Id, + virtual_depth_image_id: Id, + ) -> Self { + let vertices = [ + TriangleVertex { + position: [-0.5, -0.25], + }, + TriangleVertex { + position: [0.0, 0.5], + }, + TriangleVertex { + position: [0.25, -0.1], + }, + ]; + let vertex_buffer_id = app + .resources + .create_buffer( + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + DeviceLayout::for_value(vertices.as_slice()).unwrap(), + ) + .unwrap(); + + unsafe { + vulkano_taskgraph::execute( + &app.queue, + &app.resources, + app.flight_id, + |_cbf, tcx| { + tcx.write_buffer::<[TriangleVertex]>(vertex_buffer_id, ..)? + .copy_from_slice(&vertices); + + Ok(()) + }, + [(vertex_buffer_id, HostAccessType::Write)], + [], + [], + ) + } + .unwrap(); + + SceneTask { + pipeline: None, + vertex_buffer_id, + diffuse_image_id: virtual_diffuse_image_id, + normals_image_id: virtual_normals_image_id, + depth_image_id: virtual_depth_image_id, + } + } + + pub fn create_pipeline(&mut self, app: &App, subpass: Subpass) { + let bcx = app.resources.bindless_context().unwrap(); + + let pipeline = { + let vs = vs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(app.device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let vertex_input_state = TriangleVertex::per_vertex().definition(&vs).unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); + + GraphicsPipeline::new( + app.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + depth_stencil_state: Some(DepthStencilState { + depth: Some(DepthState::simple()), + ..Default::default() + }), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.clone().into()), + ..GraphicsPipelineCreateInfo::new(layout) + }, + ) + .unwrap() + }; + + self.pipeline = Some(pipeline); + } +} + +impl Task for SceneTask { + type World = RenderContext; + + fn clear_values(&self, clear_values: &mut ClearValues<'_>) { + clear_values.set(self.diffuse_image_id, [0.0; 4]); + clear_values.set(self.normals_image_id, [0.0; 4]); + clear_values.set(self.depth_image_id, 1.0); + } + + unsafe fn execute( + &self, + cbf: &mut RecordingCommandBuffer<'_>, + _tcx: &mut TaskContext<'_>, + rcx: &Self::World, + ) -> TaskResult { + cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?; + cbf.bind_pipeline_graphics(self.pipeline.as_ref().unwrap())?; + cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?; + + unsafe { cbf.draw(3, 1, 0, 0) }?; + + Ok(()) + } +} + +#[derive(Clone, Copy, BufferContents, Vertex)] +#[repr(C)] +struct TriangleVertex { + #[format(R32G32_SFLOAT)] + position: [f32; 2], +} + +mod vs { + vulkano_shaders::shader! { + ty: "vertex", + src: r" + #version 450 + + layout(location = 0) in vec2 position; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + } + ", + } +} + +mod fs { + vulkano_shaders::shader! { + ty: "fragment", + src: r" + #version 450 + + layout(location = 0) out vec4 f_color; + layout(location = 1) out vec4 f_normal; + + void main() { + f_color = vec4(1.0, 1.0, 1.0, 1.0); + f_normal = vec4(0.0, 0.0, 1.0, 0.0); + } + ", + } +} diff --git a/examples/deferred/triangle_draw_system.rs b/examples/deferred/triangle_draw_system.rs deleted file mode 100644 index a50422aafb..0000000000 --- a/examples/deferred/triangle_draw_system.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::sync::Arc; -use vulkano::{ - buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}, - command_buffer::{ - allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, - CommandBufferInheritanceInfo, CommandBufferUsage, SecondaryAutoCommandBuffer, - }, - device::Queue, - memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, - pipeline::{ - graphics::{ - color_blend::{ColorBlendAttachmentState, ColorBlendState}, - depth_stencil::{DepthState, DepthStencilState}, - input_assembly::InputAssemblyState, - multisample::MultisampleState, - rasterization::RasterizationState, - vertex_input::{Vertex, VertexDefinition}, - viewport::{Viewport, ViewportState}, - GraphicsPipelineCreateInfo, - }, - layout::PipelineDescriptorSetLayoutCreateInfo, - DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, - }, - render_pass::Subpass, -}; - -pub struct TriangleDrawSystem { - gfx_queue: Arc, - vertex_buffer: Subbuffer<[TriangleVertex]>, - subpass: Subpass, - pipeline: Arc, - command_buffer_allocator: Arc, -} - -impl TriangleDrawSystem { - /// Initializes a triangle drawing system. - pub fn new( - gfx_queue: Arc, - subpass: Subpass, - memory_allocator: Arc, - command_buffer_allocator: Arc, - ) -> TriangleDrawSystem { - let vertices = [ - TriangleVertex { - position: [-0.5, -0.25], - }, - TriangleVertex { - position: [0.0, 0.5], - }, - TriangleVertex { - position: [0.25, -0.1], - }, - ]; - let vertex_buffer = Buffer::from_iter( - memory_allocator, - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - vertices, - ) - .expect("failed to create buffer"); - - let pipeline = { - let device = gfx_queue.device(); - let vs = vs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let fs = fs::load(device.clone()) - .expect("failed to create shader module") - .entry_point("main") - .expect("shader entry point not found"); - let vertex_input_state = TriangleVertex::per_vertex().definition(&vs).unwrap(); - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - let layout = PipelineLayout::new( - device.clone(), - PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) - .into_pipeline_layout_create_info(device.clone()) - .unwrap(), - ) - .unwrap(); - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - depth_stencil_state: Some(DepthStencilState { - depth: Some(DepthState::simple()), - ..Default::default() - }), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.num_color_attachments(), - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.clone().into()), - ..GraphicsPipelineCreateInfo::new(layout) - }, - ) - .unwrap() - }; - - TriangleDrawSystem { - gfx_queue, - vertex_buffer, - subpass, - pipeline, - command_buffer_allocator, - } - } - - /// Builds a secondary command buffer that draws the triangle on the current subpass. - pub fn draw(&self, viewport_dimensions: [u32; 2]) -> Arc { - let mut builder = AutoCommandBufferBuilder::secondary( - self.command_buffer_allocator.clone(), - self.gfx_queue.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(self.subpass.clone().into()), - ..Default::default() - }, - ) - .unwrap(); - - builder - .set_viewport( - 0, - [Viewport { - offset: [0.0, 0.0], - extent: [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32], - depth_range: 0.0..=1.0, - }] - .into_iter() - .collect(), - ) - .unwrap() - .bind_pipeline_graphics(self.pipeline.clone()) - .unwrap() - .bind_vertex_buffers(0, self.vertex_buffer.clone()) - .unwrap(); - unsafe { builder.draw(self.vertex_buffer.len() as u32, 1, 0, 0) }.unwrap(); - - builder.build().unwrap() - } -} - -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct TriangleVertex { - #[format(R32G32_SFLOAT)] - position: [f32; 2], -} - -mod vs { - vulkano_shaders::shader! { - ty: "vertex", - src: r" - #version 450 - - layout(location = 0) in vec2 position; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - ", - } -} - -mod fs { - vulkano_shaders::shader! { - ty: "fragment", - src: r" - #version 450 - - layout(location = 0) out vec4 f_color; - layout(location = 1) out vec4 f_normal; - - void main() { - f_color = vec4(1.0, 1.0, 1.0, 1.0); - f_normal = vec4(0.0, 0.0, 1.0, 0.0); - } - ", - } -} diff --git a/examples/ray-tracing/main.rs b/examples/ray-tracing/main.rs index 9b74d24c07..493e1d0010 100644 --- a/examples/ray-tracing/main.rs +++ b/examples/ray-tracing/main.rs @@ -4,28 +4,20 @@ use scene::SceneTask; use std::{error::Error, sync::Arc}; use vulkano::{ command_buffer::allocator::StandardCommandBufferAllocator, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, - layout::{ - DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, - DescriptorType, - }, - }, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, }, - image::{ImageFormatInfo, ImageUsage}, + image::{view::ImageView, ImageFormatInfo, ImageLayout, ImageUsage}, instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, memory::allocator::StandardMemoryAllocator, - pipeline::{layout::PipelineLayoutCreateInfo, PipelineLayout}, - shader::ShaderStages, swapchain::{Surface, Swapchain, SwapchainCreateInfo}, Validated, Version, VulkanError, VulkanLibrary, }; use vulkano_taskgraph::{ - graph::{CompileInfo, ExecutableTaskGraph, ExecuteError, NodeId, TaskGraph}, - resource::{AccessTypes, Flight, ImageLayoutType, Resources}, + descriptor_set::{BindlessContext, StorageImageId}, + graph::{CompileInfo, ExecutableTaskGraph, ExecuteError, TaskGraph}, + resource::{AccessTypes, Flight, ImageLayoutType, Resources, ResourcesCreateInfo}, resource_map, Id, QueueFamilyType, }; use winit::{ @@ -58,9 +50,9 @@ struct App { pub struct RenderContext { window: Arc, swapchain_id: Id, + swapchain_storage_image_ids: Vec, recreate_swapchain: bool, task_graph: ExecutableTaskGraph, - scene_node_id: NodeId, virtual_swapchain_id: Id, } @@ -88,14 +80,15 @@ impl App { khr_synchronization2: true, khr_deferred_host_operations: true, khr_acceleration_structure: true, - ..DeviceExtensions::empty() + ..BindlessContext::required_extensions(&instance) }; let device_features = DeviceFeatures { acceleration_structure: true, ray_tracing_pipeline: true, buffer_device_address: true, synchronization2: true, - ..Default::default() + descriptor_binding_acceleration_structure_update_after_bind: true, + ..BindlessContext::required_features(&instance) }; let (physical_device, queue_family_index) = instance .enumerate_physical_devices() @@ -136,11 +129,11 @@ impl App { physical_device, DeviceCreateInfo { enabled_extensions: device_extensions, + enabled_features: device_features, queue_create_infos: vec![QueueCreateInfo { queue_family_index, ..Default::default() }], - enabled_features: device_features, ..Default::default() }, ) @@ -148,7 +141,14 @@ impl App { let queue = queues.next().unwrap(); - let resources = Resources::new(&device, &Default::default()); + let resources = Resources::new( + &device, + &ResourcesCreateInfo { + bindless_context: Some(&Default::default()), + ..Default::default() + }, + ) + .unwrap(); let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap(); @@ -222,66 +222,10 @@ impl ApplicationHandler for App { .unwrap() }; - let pipeline_layout = PipelineLayout::new( - self.device.clone(), - PipelineLayoutCreateInfo { - set_layouts: vec![ - DescriptorSetLayout::new( - self.device.clone(), - DescriptorSetLayoutCreateInfo { - bindings: [ - ( - 0, - DescriptorSetLayoutBinding { - stages: ShaderStages::RAYGEN, - ..DescriptorSetLayoutBinding::new( - DescriptorType::AccelerationStructure, - ) - }, - ), - ( - 1, - DescriptorSetLayoutBinding { - stages: ShaderStages::RAYGEN, - ..DescriptorSetLayoutBinding::new( - DescriptorType::UniformBuffer, - ) - }, - ), - ] - .into_iter() - .collect(), - ..Default::default() - }, - ) - .unwrap(), - DescriptorSetLayout::new( - self.device.clone(), - DescriptorSetLayoutCreateInfo { - bindings: [( - 0, - DescriptorSetLayoutBinding { - stages: ShaderStages::RAYGEN, - ..DescriptorSetLayoutBinding::new(DescriptorType::StorageImage) - }, - )] - .into_iter() - .collect(), - ..Default::default() - }, - ) - .unwrap(), - ], - ..Default::default() - }, - ) - .unwrap(); + let swapchain_storage_image_ids = + window_size_dependent_setup(&self.resources, swapchain_id); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(self.device.clone())); - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - self.device.clone(), - Default::default(), - )); let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( self.device.clone(), Default::default(), @@ -291,17 +235,14 @@ impl ApplicationHandler for App { let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default()); - let scene_node_id = task_graph + task_graph .create_task_node( "Scene", QueueFamilyType::Graphics, SceneTask::new( self, - pipeline_layout.clone(), - swapchain_id, virtual_swapchain_id, memory_allocator, - descriptor_set_allocator, command_buffer_allocator, ), ) @@ -309,8 +250,7 @@ impl ApplicationHandler for App { virtual_swapchain_id.current_image_id(), AccessTypes::RAY_TRACING_SHADER_STORAGE_WRITE, ImageLayoutType::General, - ) - .build(); + ); let task_graph = unsafe { task_graph.compile(&CompileInfo { @@ -325,10 +265,10 @@ impl ApplicationHandler for App { self.rcx = Some(RenderContext { window, swapchain_id, + swapchain_storage_image_ids, virtual_swapchain_id, recreate_swapchain: false, task_graph, - scene_node_id, }); } @@ -339,6 +279,7 @@ impl ApplicationHandler for App { event: WindowEvent, ) { let rcx = self.rcx.as_mut().unwrap(); + let bcx = self.resources.bindless_context().unwrap(); match event { WindowEvent::CloseRequested => { @@ -365,13 +306,17 @@ impl ApplicationHandler for App { }) .expect("failed to recreate swapchain"); - rcx.task_graph - .task_node_mut(rcx.scene_node_id) - .unwrap() - .task_mut() - .downcast_mut::() - .unwrap() - .handle_resize(&self.resources, rcx.swapchain_id); + // FIXME(taskgraph): safe resource destruction + flight + .wait_for_frame(flight.current_frame() - 1, None) + .unwrap(); + + for &id in &rcx.swapchain_storage_image_ids { + unsafe { bcx.global_set().remove_storage_image(id) }; + } + + rcx.swapchain_storage_image_ids = + window_size_dependent_setup(&self.resources, rcx.swapchain_id); rcx.recreate_swapchain = false; } @@ -409,3 +354,25 @@ impl ApplicationHandler for App { rcx.window.request_redraw(); } } + +/// This function is called once during initialization, then again whenever the window is resized. +fn window_size_dependent_setup( + resources: &Resources, + swapchain_id: Id, +) -> Vec { + let bcx = resources.bindless_context().unwrap(); + let swapchain_state = resources.swapchain(swapchain_id).unwrap(); + let images = swapchain_state.images(); + + let swapchain_storage_image_ids = images + .iter() + .map(|image| { + let image_view = ImageView::new_default(image.clone()).unwrap(); + + bcx.global_set() + .add_storage_image(image_view, ImageLayout::General) + }) + .collect(); + + swapchain_storage_image_ids +} diff --git a/examples/ray-tracing/rgen.glsl b/examples/ray-tracing/rgen.glsl index 0f41f248a3..999841e037 100644 --- a/examples/ray-tracing/rgen.glsl +++ b/examples/ray-tracing/rgen.glsl @@ -1,15 +1,26 @@ #version 460 #extension GL_EXT_ray_tracing : require +#define VKO_ACCELERATION_STRUCTURE_ENABLED 1 +#include layout(location = 0) rayPayloadEXT vec3 hit_value; -layout(set = 0, binding = 0) uniform accelerationStructureEXT top_level_as; -layout(set = 0, binding = 1) uniform Camera { - mat4 view_proj; // Camera view * projection - mat4 view_inverse; // Camera inverse view matrix - mat4 proj_inverse; // Camera inverse projection matrix -} camera; -layout(set = 1, binding = 0, rgba32f) uniform image2D image; +VKO_DECLARE_STORAGE_BUFFER(camera, Camera { + // Camera view * projection + mat4 view_proj; + // Camera inverse view matrix + mat4 view_inverse; + // Camera inverse projection matrix + mat4 proj_inverse; +}) + +layout(push_constant) uniform PushConstants { + StorageImageId image_id; + AccelerationStructureId acceleration_structure_id; + StorageBufferId camera_buffer_id; +}; + +#define camera vko_buffer(camera, camera_buffer_id) void main() { const vec2 pixel_center = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); @@ -25,17 +36,28 @@ void main() { float t_max = 10000.0; traceRayEXT( - top_level_as, // acceleration structure - ray_flags, // rayFlags - 0xFF, // cullMask - 0, // sbtRecordOffset - 0, // sbtRecordStride - 0, // missIndex - origin.xyz, // ray origin - t_min, // ray min range - direction.xyz, // ray direction - t_max, // ray max range - 0); // payload (location = 0) - - imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hit_value, 1.0)); + // acceleration structure + vko_accelerationStructureEXT(acceleration_structure_id), + // rayFlags + ray_flags, + // cullMask + 0xFF, + // sbtRecordOffset + 0, + // sbtRecordStride + 0, + // missIndex + 0, + // ray origin + origin.xyz, + // ray min range + t_min, + // ray direction + direction.xyz, + // ray max range + t_max, + // payload (location = 0) + 0); + + imageStore(vko_image2D_rgba32f(image_id), ivec2(gl_LaunchIDEXT.xy), vec4(hit_value, 1.0)); } diff --git a/examples/ray-tracing/scene.rs b/examples/ray-tracing/scene.rs index e3b1020374..12a954858d 100644 --- a/examples/ray-tracing/scene.rs +++ b/examples/ray-tracing/scene.rs @@ -15,51 +15,48 @@ use vulkano::{ allocator::CommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, }, - descriptor_set::{ - allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, - }, device::{Device, Queue}, format::Format, - image::view::ImageView, - memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}, + memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryAllocator, MemoryTypeFilter}, pipeline::{ graphics::vertex_input::Vertex, ray_tracing::{ RayTracingPipeline, RayTracingPipelineCreateInfo, RayTracingShaderGroupCreateInfo, ShaderBindingTable, }, - PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, + Pipeline, PipelineShaderStageCreateInfo, }, swapchain::Swapchain, sync::GpuFuture, + DeviceSize, }; use vulkano_taskgraph::{ - command_buffer::RecordingCommandBuffer, resource::Resources, Id, Task, TaskContext, TaskResult, + command_buffer::RecordingCommandBuffer, + descriptor_set::{AccelerationStructureId, StorageBufferId}, + resource::HostAccessType, + Id, Task, TaskContext, TaskResult, }; pub struct SceneTask { - descriptor_set: Arc, - swapchain_image_sets: Vec<(Arc, Arc)>, - pipeline_layout: Arc, - descriptor_set_allocator: Arc, - virtual_swapchain_id: Id, + swapchain_id: Id, + acceleration_structure_id: AccelerationStructureId, + camera_storage_buffer_id: StorageBufferId, shader_binding_table: ShaderBindingTable, pipeline: Arc, - blas: Arc, - tlas: Arc, - uniform_buffer: Subbuffer, + // The bottom-level acceleration structure is required to be kept alive as we reference it in + // the top-level acceleration structure. + _blas: Arc, } impl SceneTask { pub fn new( app: &App, - pipeline_layout: Arc, - swapchain_id: Id, virtual_swapchain_id: Id, memory_allocator: Arc, - descriptor_set_allocator: Arc, command_buffer_allocator: Arc, ) -> Self { + let bcx = app.resources.bindless_context().unwrap(); + let pipeline = { let raygen = raygen::load(app.device.clone()) .unwrap() @@ -92,6 +89,8 @@ impl SceneTask { }, ]; + let layout = bcx.pipeline_layout_from_stages(&stages).unwrap(); + RayTracingPipeline::new( app.device.clone(), None, @@ -99,7 +98,7 @@ impl SceneTask { stages: stages.into_iter().collect(), groups: groups.into_iter().collect(), max_pipeline_ray_recursion_depth: 1, - ..RayTracingPipelineCreateInfo::new(pipeline_layout.clone()) + ..RayTracingPipelineCreateInfo::new(layout) }, ) .unwrap() @@ -167,68 +166,66 @@ impl SceneTask { Vec3::new(0.0, -1.0, 0.0), ); - let uniform_buffer = Buffer::from_data( - memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - raygen::Camera { - view_proj: (proj * view).to_cols_array_2d(), - view_inverse: view.inverse().to_cols_array_2d(), - proj_inverse: proj.inverse().to_cols_array_2d(), - }, - ) - .unwrap(); + let camera_buffer_id = app + .resources + .create_buffer( + BufferCreateInfo { + usage: BufferUsage::STORAGE_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + DeviceLayout::new_sized::(), + ) + .unwrap(); - let descriptor_set = DescriptorSet::new( - descriptor_set_allocator.clone(), - pipeline_layout.set_layouts()[0].clone(), - [ - WriteDescriptorSet::acceleration_structure(0, tlas.clone()), - WriteDescriptorSet::buffer(1, uniform_buffer.clone()), - ], - [], - ) + unsafe { + vulkano_taskgraph::execute( + &app.queue, + &app.resources, + app.flight_id, + |_cbf, tcx| { + *tcx.write_buffer(camera_buffer_id, ..)? = raygen::Camera { + view_proj: (proj * view).to_cols_array_2d(), + view_inverse: view.inverse().to_cols_array_2d(), + proj_inverse: proj.inverse().to_cols_array_2d(), + }; + + Ok(()) + }, + [(camera_buffer_id, HostAccessType::Write)], + [], + [], + ) + } .unwrap(); - let swapchain_image_sets = window_size_dependent_setup( - &app.resources, - swapchain_id, - &pipeline_layout, - &descriptor_set_allocator, - ); + let acceleration_structure_id = bcx.global_set().add_acceleration_structure(tlas); + + let camera_storage_buffer_id = bcx + .global_set() + .create_storage_buffer( + camera_buffer_id, + 0, + size_of::() as DeviceSize, + ) + .unwrap(); let shader_binding_table = ShaderBindingTable::new(memory_allocator.clone(), &pipeline).unwrap(); SceneTask { - descriptor_set, - swapchain_image_sets, - descriptor_set_allocator, - pipeline_layout, - virtual_swapchain_id, + swapchain_id: virtual_swapchain_id, + acceleration_structure_id, + camera_storage_buffer_id, shader_binding_table, pipeline, - blas, - tlas, - uniform_buffer, + _blas: blas, } } - - pub fn handle_resize(&mut self, resources: &Resources, swapchain_id: Id) { - self.swapchain_image_sets = window_size_dependent_setup( - resources, - swapchain_id, - &self.pipeline_layout, - &self.descriptor_set_allocator, - ); - } } impl Task for SceneTask { @@ -238,36 +235,25 @@ impl Task for SceneTask { &self, cbf: &mut RecordingCommandBuffer<'_>, tcx: &mut TaskContext<'_>, - _rcx: &Self::World, + rcx: &Self::World, ) -> TaskResult { - let swapchain_state = tcx.swapchain(self.virtual_swapchain_id)?; + let swapchain_state = tcx.swapchain(self.swapchain_id)?; let image_index = swapchain_state.current_image_index().unwrap(); + let extent = swapchain_state.images()[0].extent(); - cbf.as_raw().bind_descriptor_sets( - PipelineBindPoint::RayTracing, - &self.pipeline_layout, + cbf.push_constants( + self.pipeline.layout(), 0, - &[ - self.descriptor_set.as_raw(), - self.swapchain_image_sets[image_index as usize].1.as_raw(), - ], - &[], + &raygen::PushConstants { + image_id: rcx.swapchain_storage_image_ids[image_index as usize], + acceleration_structure_id: self.acceleration_structure_id, + camera_buffer_id: self.camera_storage_buffer_id, + }, )?; cbf.bind_pipeline_ray_tracing(&self.pipeline)?; - let extent = self.swapchain_image_sets[0].0.image().extent(); - unsafe { cbf.trace_rays(self.shader_binding_table.addresses(), extent) }?; - for (_, descriptor_set) in self.swapchain_image_sets.iter() { - cbf.destroy_object(descriptor_set.clone()); - } - - cbf.destroy_object(self.blas.clone()); - cbf.destroy_object(self.tlas.clone()); - cbf.destroy_object(self.uniform_buffer.clone().into()); - cbf.destroy_object(self.descriptor_set.clone()); - Ok(()) } } @@ -303,35 +289,6 @@ struct MyVertex { position: [f32; 3], } -/// This function is called once during initialization, then again whenever the window is resized. -fn window_size_dependent_setup( - resources: &Resources, - swapchain_id: Id, - pipeline_layout: &Arc, - descriptor_set_allocator: &Arc, -) -> Vec<(Arc, Arc)> { - let swapchain_state = resources.swapchain(swapchain_id).unwrap(); - let images = swapchain_state.images(); - - let swapchain_image_sets = images - .iter() - .map(|image| { - let image_view = ImageView::new_default(image.clone()).unwrap(); - let descriptor_set = DescriptorSet::new( - descriptor_set_allocator.clone(), - pipeline_layout.set_layouts()[1].clone(), - [WriteDescriptorSet::image_view(0, image_view.clone())], - [], - ) - .unwrap(); - - (image_view, descriptor_set) - }) - .collect(); - - swapchain_image_sets -} - /// A helper function to build a acceleration structure and wait for its completion. /// /// # Safety diff --git a/include b/include new file mode 120000 index 0000000000..31ef078c85 --- /dev/null +++ b/include @@ -0,0 +1 @@ +vulkano-shaders/include \ No newline at end of file diff --git a/vulkano-shaders/include/vulkano.glsl b/vulkano-shaders/include/vulkano.glsl new file mode 100644 index 0000000000..7f009fac7f --- /dev/null +++ b/vulkano-shaders/include/vulkano.glsl @@ -0,0 +1,1466 @@ +#ifndef _VULKANO_HEADER +#define _VULKANO_HEADER + +#extension GL_EXT_nonuniform_qualifier : enable +#extension GL_EXT_samplerless_texture_functions : enable + +#if VKO_STORAGE_IMAGE_WITHOUT_FORMAT_ENABLED +#extension GL_EXT_shader_image_load_formatted : enable +#endif // VKO_STORAGE_IMAGE_WITHOUT_FORMAT_ENABLED + +#if VKO_IMAGE_INT64_ATOMICS_ENABLED +#extension GL_EXT_shader_image_int64 : enable +#endif // VKO_IMAGE_INT64_ATOMICS_ENABLED + +// NOTE(Marc): The index getter macros below may look strange, because they are crafted such that +// they enforce type safety while also preserving `nonuniformEXT` qualifiers that the IDs may have. +// That means in particular that we can't use a function which returns the index, as the qualifier +// doesn't get propagated through function calls, however it does get propagated through struct +// decomposition. The hope here is that this leads to the least surprising behavior for the user. + +struct SamplerId { + uint _private_index; + uint _private_generation; +}; + +void _vko_assert_is_sampler_id(SamplerId id) {} + +#define _vko_sampler_index(id) \ + (_vko_assert_is_sampler_id(id), id._private_index) + +struct SampledImageId { + uint _private_index; + uint _private_generation; +}; + +void _vko_assert_is_sampled_image_id(SampledImageId id) {} + +#define _vko_sampled_image_index(id) \ + (_vko_assert_is_sampled_image_id(id), id._private_index) + +struct StorageImageId { + uint _private_index; + uint _private_generation; +}; + +void _vko_assert_is_storage_image_id(StorageImageId id) {} + +#define _vko_storage_image_index(id) \ + (_vko_assert_is_storage_image_id(id), id._private_index) + +struct StorageBufferId { + uint _private_index; + uint _private_generation; +}; + +void _vko_assert_is_storage_buffer_id(StorageBufferId id) {} + +#define _vko_storage_buffer_index(id) \ + (_vko_assert_is_storage_buffer_id(id), id._private_index) + +struct AccelerationStructureId { + uint _private_index; + uint _private_generation; +}; + +void _vko_assert_is_acceleration_structure_id(AccelerationStructureId id) {} + +#define _vko_acceleration_structure_index(id) \ + (_vko_assert_is_acceleration_structure_id(id), id._private_index) + +// NOTE(Marc): The following constants must match the definitions in +// vulkano_taskgraph/src/descriptor_set.rs! + +#define VKO_GLOBAL_SET 0 +#define VKO_SAMPLER_BINDING 0 +#define VKO_SAMPLED_IMAGE_BINDING 1 +#define VKO_STORAGE_IMAGE_BINDING 2 +#define VKO_STORAGE_BUFFER_BINDING 3 +#define VKO_ACCELERATION_STRUCTURE_BINDING 4 + +#define VKO_LOCAL_SET 1 +#define VKO_INPUT_ATTACHMENT_BINDING 0 + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define _VKO_DECLARE_SAMPLER(TYPE) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_SAMPLER_BINDING) \ + uniform TYPE _vko_samplers_##TYPE[]; + +_VKO_DECLARE_SAMPLER(sampler) +#define vko_sampler(id) \ + _vko_samplers_sampler[_vko_sampler_index(id)] + +_VKO_DECLARE_SAMPLER(samplerShadow) +#define vko_samplerShadow(id) \ + _vko_samplers_samplerShadow[_vko_sampler_index(id)] + +#undef _VKO_DECLARE_SAMPLER + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define _VKO_DECLARE_SAMPLED_IMAGE(TYPE) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_SAMPLED_IMAGE_BINDING) \ + uniform TYPE _vko_sampled_images_##TYPE[]; + +#define _VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(DIMENSION) \ + _VKO_DECLARE_SAMPLED_IMAGE(texture##DIMENSION) \ + _VKO_DECLARE_SAMPLED_IMAGE(itexture##DIMENSION) \ + _VKO_DECLARE_SAMPLED_IMAGE(utexture##DIMENSION) + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(1D) +#define vko_texture1D(id) \ + _vko_sampled_images_texture1D[_vko_sampled_image_index(id)] +#define vko_itexture1D(id) \ + _vko_sampled_images_itexture1D[_vko_sampled_image_index(id)] +#define vko_utexture1D(id) \ + _vko_sampled_images_utexture1D[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(1DArray) +#define vko_texture1DArray(id) \ + _vko_sampled_images_texture1DArray[_vko_sampled_image_index(id)] +#define vko_itexture1DArray(id) \ + _vko_sampled_images_itexture1DArray[_vko_sampled_image_index(id)] +#define vko_utexture1DArray(id) \ + _vko_sampled_images_utexture1DArray[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(2D) +#define vko_texture2D(id) \ + _vko_sampled_images_texture2D[_vko_sampled_image_index(id)] +#define vko_itexture2D(id) \ + _vko_sampled_images_itexture2D[_vko_sampled_image_index(id)] +#define vko_utexture2D(id) \ + _vko_sampled_images_utexture2D[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(2DArray) +#define vko_texture2DArray(id) \ + _vko_sampled_images_texture2DArray[_vko_sampled_image_index(id)] +#define vko_itexture2DArray(id) \ + _vko_sampled_images_itexture2DArray[_vko_sampled_image_index(id)] +#define vko_utexture2DArray(id) \ + _vko_sampled_images_utexture2DArray[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(2DMS) +#define vko_texture2DMS(id) \ + _vko_sampled_images_texture2DMS[_vko_sampled_image_index(id)] +#define vko_itexture2DMS(id) \ + _vko_sampled_images_itexture2DMS[_vko_sampled_image_index(id)] +#define vko_utexture2DMS(id) \ + _vko_sampled_images_utexture2DMS[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(2DMSArray) +#define vko_texture2DMSArray(id) \ + _vko_sampled_images_texture2DMSArray[_vko_sampled_image_index(id)] +#define vko_itexture2DMSArray(id) \ + _vko_sampled_images_itexture2DMSArray[_vko_sampled_image_index(id)] +#define vko_utexture2DMSArray(id) \ + _vko_sampled_images_utexture2DMSArray[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(3D) +#define vko_texture3D(id) \ + _vko_sampled_images_texture3D[_vko_sampled_image_index(id)] +#define vko_itexture3D(id) \ + _vko_sampled_images_itexture3D[_vko_sampled_image_index(id)] +#define vko_utexture3D(id) \ + _vko_sampled_images_utexture3D[_vko_sampled_image_index(id)] + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(Cube) +#define vko_textureCube(id) \ + _vko_sampled_images_textureCube[_vko_sampled_image_index(id)] +#define vko_itextureCube(id) \ + _vko_sampled_images_itextureCube[_vko_sampled_image_index(id)] +#define vko_utextureCube(id) \ + _vko_sampled_images_utextureCube[_vko_sampled_image_index(id)] + +#if VKO_IMAGE_CUBE_ARRAY_ENABLED + +_VKO_DECLARE_SAMPLED_IMAGE_DIMENSION(CubeArray) +#define vko_textureCubeArray(id) \ + _vko_sampled_images_textureCubeArray[_vko_sampled_image_index(id)] +#define vko_itextureCubeArray(id) \ + _vko_sampled_images_itextureCubeArray[_vko_sampled_image_index(id)] +#define vko_utextureCubeArray(id) \ + _vko_sampled_images_utextureCubeArray[_vko_sampled_image_index(id)] + +#endif // VKO_IMAGE_CUBE_ARRAY_ENABLED + +#undef _VKO_DECLARE_SAMPLED_IMAGE_DIMENSION +#undef _VKO_DECLARE_SAMPLED_IMAGE_TYPE + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define vko_sampler1D(sampled_image_id, sampler_id) \ + sampler1D( \ + vko_texture1D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler1D(sampled_image_id, sampler_id) \ + isampler1D( \ + vko_itexture1D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler1D(sampled_image_id, sampler_id) \ + usampler1D( \ + vko_utexture1D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler1DShadow(sampled_image_id, sampler_id) \ + sampler1DShadow( \ + vko_texture1D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler1DShadow(sampled_image_id, sampler_id) \ + isampler1DShadow( \ + vko_itexture1D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler1DShadow(sampled_image_id, sampler_id) \ + usampler1DShadow( \ + vko_utexture1D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_sampler1DArray(sampled_image_id, sampler_id) \ + sampler1DArray( \ + vko_texture1DArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler1DArray(sampled_image_id, sampler_id) \ + isampler1DArray( \ + vko_itexture1DArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler1DArray(sampled_image_id, sampler_id) \ + usampler1DArray( \ + vko_utexture1DArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler1DArrayShadow(sampled_image_id, sampler_id) \ + sampler1DArrayShadow( \ + vko_texture1DArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler1DArrayShadow(sampled_image_id, sampler_id) \ + isampler1DArrayShadow( \ + vko_itexture1DArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler1DArrayShadow(sampled_image_id, sampler_id) \ + usampler1DArrayShadow( \ + vko_utexture1DArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_sampler2D(sampled_image_id, sampler_id) \ + sampler2D( \ + vko_texture2D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler2D(sampled_image_id, sampler_id) \ + isampler2D( \ + vko_itexture2D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler2D(sampled_image_id, sampler_id) \ + usampler2D( \ + vko_utexture2D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler2DShadow(sampled_image_id, sampler_id) \ + sampler2DShadow( \ + vko_texture2D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler2DShadow(sampled_image_id, sampler_id) \ + isampler2DShadow( \ + vko_itexture2D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler2DShadow(sampled_image_id, sampler_id) \ + usampler2DShadow( \ + vko_utexture2D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_sampler2DArray(sampled_image_id, sampler_id) \ + sampler2DArray( \ + vko_texture2DArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler2DArray(sampled_image_id, sampler_id) \ + isampler2DArray( \ + vko_itexture2DArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler2DArray(sampled_image_id, sampler_id) \ + usampler2DArray( \ + vko_utexture2DArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler2DArrayShadow(sampled_image_id, sampler_id) \ + sampler2DArrayShadow( \ + vko_texture2DArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler2DArrayShadow(sampled_image_id, sampler_id) \ + isampler2DArrayShadow( \ + vko_itexture2DArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler2DArrayShadow(sampled_image_id, sampler_id) \ + usampler2DArrayShadow( \ + vko_utexture2DArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_sampler2DMS(sampled_image_id, sampler_id) \ + sampler2DMS( \ + vko_texture2DMS(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler2DMS(sampled_image_id, sampler_id) \ + isampler2DMS( \ + vko_itexture2DMS(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler2DMS(sampled_image_id, sampler_id) \ + usampler2DMS( \ + vko_utexture2DMS(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler2DMSShadow(sampled_image_id, sampler_id) \ + sampler2DMSShadow( \ + vko_texture2DMS(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler2DMSShadow(sampled_image_id, sampler_id) \ + isampler2DMSShadow( \ + vko_itexture2DMS(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler2DMSShadow(sampled_image_id, sampler_id) \ + usampler2DMSShadow( \ + vko_utexture2DMS(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_sampler2DMSArray(sampled_image_id, sampler_id) \ + sampler2DMSArray( \ + vko_texture2DMSArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler2DMSArray(sampled_image_id, sampler_id) \ + isampler2DMSArray( \ + vko_itexture2DMSArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler2DMSArray(sampled_image_id, sampler_id) \ + usampler2DMSArray( \ + vko_utexture2DMSArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler2DMSArrayShadow(sampled_image_id, sampler_id) \ + sampler2DMSArrayShadow( \ + vko_texture2DMSArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler2DMSArrayShadow(sampled_image_id, sampler_id) \ + isampler2DMSArrayShadow( \ + vko_itexture2DMSArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler2DMSArrayShadow(sampled_image_id, sampler_id) \ + usampler2DMSArrayShadow( \ + vko_utexture2DMSArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_sampler3D(sampled_image_id, sampler_id) \ + sampler3D( \ + vko_texture3D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isampler3D(sampled_image_id, sampler_id) \ + isampler3D( \ + vko_itexture3D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usampler3D(sampled_image_id, sampler_id) \ + usampler3D( \ + vko_utexture3D(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_sampler3DShadow(sampled_image_id, sampler_id) \ + sampler3DShadow( \ + vko_texture3D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isampler3DShadow(sampled_image_id, sampler_id) \ + isampler3DShadow( \ + vko_itexture3D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usampler3DShadow(sampled_image_id, sampler_id) \ + usampler3DShadow( \ + vko_utexture3D(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#define vko_samplerCube(sampled_image_id, sampler_id) \ + samplerCube( \ + vko_textureCube(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isamplerCube(sampled_image_id, sampler_id) \ + isamplerCube( \ + vko_itextureCube(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usamplerCube(sampled_image_id, sampler_id) \ + usamplerCube( \ + vko_utextureCube(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_samplerCubeShadow(sampled_image_id, sampler_id) \ + samplerCubeShadow( \ + vko_textureCube(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isamplerCubeShadow(sampled_image_id, sampler_id) \ + isamplerCubeShadow( \ + vko_itextureCube(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usamplerCubeShadow(sampled_image_id, sampler_id) \ + usamplerCubeShadow( \ + vko_utextureCube(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#if VKO_IMAGE_CUBE_ARRAY_ENABLED + +#define vko_samplerCubeArray(sampled_image_id, sampler_id) \ + samplerCubeArray( \ + vko_textureCubeArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_isamplerCubeArray(sampled_image_id, sampler_id) \ + isamplerCubeArray( \ + vko_itextureCubeArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_usamplerCubeArray(sampled_image_id, sampler_id) \ + usamplerCubeArray( \ + vko_utextureCubeArray(sampled_image_id), \ + vko_sampler(sampler_id)) +#define vko_samplerCubeArrayShadow(sampled_image_id, sampler_id) \ + samplerCubeArrayShadow( \ + vko_textureCubeArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_isamplerCubeArrayShadow(sampled_image_id, sampler_id) \ + isamplerCubeArrayShadow( \ + vko_itextureCubeArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) +#define vko_usamplerCubeArrayShadow(sampled_image_id, sampler_id) \ + usamplerCubeArrayShadow( \ + vko_utextureCubeArray(sampled_image_id), \ + vko_samplerShadow(sampler_id)) + +#endif // VKO_IMAGE_CUBE_ARRAY_ENABLED + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define VKO_DECLARE_STORAGE_IMAGE(NAME, TYPE, FORMAT) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_IMAGE_BINDING, FORMAT) \ + uniform TYPE _vko_##NAME##_storage_images[]; + +#define VKO_DECLARE_STORAGE_IMAGE_WITHOUT_FORMAT(NAME, TYPE) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_IMAGE_BINDING) \ + uniform TYPE _vko_##NAME##_storage_images[]; + +#define vko_image(NAME, id) \ + _vko_##NAME##_storage_images[_vko_storage_image_index(id)] + +#define _VKO_DECLARE_STORAGE_IMAGE(TYPE, FORMAT) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_IMAGE_BINDING, FORMAT) \ + uniform TYPE _vko_storage_images_##TYPE##_##FORMAT[]; + +#define _VKO_DECLARE_STORAGE_IMAGE_DIMENSION(DIMENSION) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgba32f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgba16f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rg32f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rg16f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r11f_g11f_b10f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r32f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r16f) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgba16) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgb10_a2) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgba8) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rg16) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rg8) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r16) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r8) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgba16_snorm) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rgba8_snorm) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rg16_snorm) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, rg8_snorm) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r16_snorm) \ + _VKO_DECLARE_STORAGE_IMAGE(image##DIMENSION, r8_snorm) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, rgba32i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, rgba16i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, rgba8i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, rg32i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, rg16i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, rg8i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, r32i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, r16i) \ + _VKO_DECLARE_STORAGE_IMAGE(iimage##DIMENSION, r8i) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rgba32ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rgba16ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rgb10_a2ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rgba8ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rg32ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rg16ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, rg8ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, r32ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, r16ui) \ + _VKO_DECLARE_STORAGE_IMAGE(uimage##DIMENSION, r8ui) + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(1D) +#define vko_image1D_rgba32f(id) \ + _vko_storage_images_image1D_rgba32f[_vko_storage_image_index(id)] +#define vko_image1D_rgba16f(id) \ + _vko_storage_images_image1D_rgba16f[_vko_storage_image_index(id)] +#define vko_image1D_rg32f(id) \ + _vko_storage_images_image1D_rg32f[_vko_storage_image_index(id)] +#define vko_image1D_rg16f(id) \ + _vko_storage_images_image1D_rg16f[_vko_storage_image_index(id)] +#define vko_image1D_r11f_g11f_b10f(id) \ + _vko_storage_images_image1D_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image1D_r32f(id) \ + _vko_storage_images_image1D_r32f[_vko_storage_image_index(id)] +#define vko_image1D_r16f(id) \ + _vko_storage_images_image1D_r16f[_vko_storage_image_index(id)] +#define vko_image1D_rgba16(id) \ + _vko_storage_images_image1D_rgba16[_vko_storage_image_index(id)] +#define vko_image1D_rgb10_a2(id) \ + _vko_storage_images_image1D_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image1D_rgba8(id) \ + _vko_storage_images_image1D_rgba8[_vko_storage_image_index(id)] +#define vko_image1D_rg16(id) \ + _vko_storage_images_image1D_rg16[_vko_storage_image_index(id)] +#define vko_image1D_rg8(id) \ + _vko_storage_images_image1D_rg8[_vko_storage_image_index(id)] +#define vko_image1D_r16(id) \ + _vko_storage_images_image1D_r16[_vko_storage_image_index(id)] +#define vko_image1D_r8(id) \ + _vko_storage_images_image1D_r8[_vko_storage_image_index(id)] +#define vko_image1D_rgba16_snorm(id) \ + _vko_storage_images_image1D_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image1D_rgba8_snorm(id) \ + _vko_storage_images_image1D_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image1D_rg16_snorm(id) \ + _vko_storage_images_image1D_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image1D_rg8_snorm(id) \ + _vko_storage_images_image1D_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image1D_r16_snorm(id) \ + _vko_storage_images_image1D_r16_snorm[_vko_storage_image_index(id)] +#define vko_image1D_r8_snorm(id) \ + _vko_storage_images_image1D_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage1D_rgba32i(id) \ + _vko_storage_images_iimage1D_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage1D_rgba16i(id) \ + _vko_storage_images_iimage1D_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage1D_rgba8i(id) \ + _vko_storage_images_iimage1D_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage1D_rg32i(id) \ + _vko_storage_images_iimage1D_rg32i[_vko_storage_image_index(id)] +#define vko_iimage1D_rg16i(id) \ + _vko_storage_images_iimage1D_rg16i[_vko_storage_image_index(id)] +#define vko_iimage1D_rg8i(id) \ + _vko_storage_images_iimage1D_rg8i[_vko_storage_image_index(id)] +#define vko_iimage1D_r32i(id) \ + _vko_storage_images_iimage1D_r32i[_vko_storage_image_index(id)] +#define vko_iimage1D_r16i(id) \ + _vko_storage_images_iimage1D_r16i[_vko_storage_image_index(id)] +#define vko_iimage1D_r8i(id) \ + _vko_storage_images_iimage1D_r8i[_vko_storage_image_index(id)] +#define vko_uimage1D_rgba32ui(id) \ + _vko_storage_images_uimage1D_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage1D_rgba16ui(id) \ + _vko_storage_images_uimage1D_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage1D_rgb10_a2ui(id) \ + _vko_storage_images_uimage1D_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage1D_rgba8ui(id) \ + _vko_storage_images_uimage1D_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage1D_rg32ui(id) \ + _vko_storage_images_uimage1D_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage1D_rg16ui(id) \ + _vko_storage_images_uimage1D_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage1D_rg8ui(id) \ + _vko_storage_images_uimage1D_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage1D_r32ui(id) \ + _vko_storage_images_uimage1D_r32ui[_vko_storage_image_index(id)] +#define vko_uimage1D_r16ui(id) \ + _vko_storage_images_uimage1D_r16ui[_vko_storage_image_index(id)] +#define vko_uimage1D_r8ui(id) \ + _vko_storage_images_uimage1D_r8ui[_vko_storage_image_index(id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(1DArray) +#define vko_image1DArray_rgba32f(id) \ + _vko_storage_images_image1DArray_rgba32f[_vko_storage_image_index(id)] +#define vko_image1DArray_rgba16f(id) \ + _vko_storage_images_image1DArray_rgba16f[_vko_storage_image_index(id)] +#define vko_image1DArray_rg32f(id) \ + _vko_storage_images_image1DArray_rg32f[_vko_storage_image_index(id)] +#define vko_image1DArray_rg16f(id) \ + _vko_storage_images_image1DArray_rg16f[_vko_storage_image_index(id)] +#define vko_image1DArray_r11f_g11f_b10f(id) \ + _vko_storage_images_image1DArray_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image1DArray_r32f(id) \ + _vko_storage_images_image1DArray_r32f[_vko_storage_image_index(id)] +#define vko_image1DArray_r16f(id) \ + _vko_storage_images_image1DArray_r16f[_vko_storage_image_index(id)] +#define vko_image1DArray_rgba16(id) \ + _vko_storage_images_image1DArray_rgba16[_vko_storage_image_index(id)] +#define vko_image1DArray_rgb10_a2(id) \ + _vko_storage_images_image1DArray_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image1DArray_rgba8(id) \ + _vko_storage_images_image1DArray_rgba8[_vko_storage_image_index(id)] +#define vko_image1DArray_rg16(id) \ + _vko_storage_images_image1DArray_rg16[_vko_storage_image_index(id)] +#define vko_image1DArray_rg8(id) \ + _vko_storage_images_image1DArray_rg8[_vko_storage_image_index(id)] +#define vko_image1DArray_r16(id) \ + _vko_storage_images_image1DArray_r16[_vko_storage_image_index(id)] +#define vko_image1DArray_r8(id) \ + _vko_storage_images_image1DArray_r8[_vko_storage_image_index(id)] +#define vko_image1DArray_rgba16_snorm(id) \ + _vko_storage_images_image1DArray_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image1DArray_rgba8_snorm(id) \ + _vko_storage_images_image1DArray_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image1DArray_rg16_snorm(id) \ + _vko_storage_images_image1DArray_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image1DArray_rg8_snorm(id) \ + _vko_storage_images_image1DArray_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image1DArray_r16_snorm(id) \ + _vko_storage_images_image1DArray_r16_snorm[_vko_storage_image_index(id)] +#define vko_image1DArray_r8_snorm(id) \ + _vko_storage_images_image1DArray_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage1DArray_rgba32i(id) \ + _vko_storage_images_iimage1DArray_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_rgba16i(id) \ + _vko_storage_images_iimage1DArray_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_rgba8i(id) \ + _vko_storage_images_iimage1DArray_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_rg32i(id) \ + _vko_storage_images_iimage1DArray_rg32i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_rg16i(id) \ + _vko_storage_images_iimage1DArray_rg16i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_rg8i(id) \ + _vko_storage_images_iimage1DArray_rg8i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_r32i(id) \ + _vko_storage_images_iimage1DArray_r32i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_r16i(id) \ + _vko_storage_images_iimage1DArray_r16i[_vko_storage_image_index(id)] +#define vko_iimage1DArray_r8i(id) \ + _vko_storage_images_iimage1DArray_r8i[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rgba32ui(id) \ + _vko_storage_images_uimage1DArray_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rgba16ui(id) \ + _vko_storage_images_uimage1DArray_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rgb10_a2ui(id) \ + _vko_storage_images_uimage1DArray_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rgba8ui(id) \ + _vko_storage_images_uimage1DArray_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rg32ui(id) \ + _vko_storage_images_uimage1DArray_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rg16ui(id) \ + _vko_storage_images_uimage1DArray_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_rg8ui(id) \ + _vko_storage_images_uimage1DArray_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_r32ui(id) \ + _vko_storage_images_uimage1DArray_r32ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_r16ui(id) \ + _vko_storage_images_uimage1DArray_r16ui[_vko_storage_image_index(id)] +#define vko_uimage1DArray_r8ui(id) \ + _vko_storage_images_uimage1DArray_r8ui[_vko_storage_image_index(id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(2D) +#define vko_image2D_rgba32f(id) \ + _vko_storage_images_image2D_rgba32f[_vko_storage_image_index(id)] +#define vko_image2D_rgba16f(id) \ + _vko_storage_images_image2D_rgba16f[_vko_storage_image_index(id)] +#define vko_image2D_rg32f(id) \ + _vko_storage_images_image2D_rg32f[_vko_storage_image_index(id)] +#define vko_image2D_rg16f(id) \ + _vko_storage_images_image2D_rg16f[_vko_storage_image_index(id)] +#define vko_image2D_r11f_g11f_b10f(id) \ + _vko_storage_images_image2D_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image2D_r32f(id) \ + _vko_storage_images_image2D_r32f[_vko_storage_image_index(id)] +#define vko_image2D_r16f(id) \ + _vko_storage_images_image2D_r16f[_vko_storage_image_index(id)] +#define vko_image2D_rgba16(id) \ + _vko_storage_images_image2D_rgba16[_vko_storage_image_index(id)] +#define vko_image2D_rgb10_a2(id) \ + _vko_storage_images_image2D_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image2D_rgba8(id) \ + _vko_storage_images_image2D_rgba8[_vko_storage_image_index(id)] +#define vko_image2D_rg16(id) \ + _vko_storage_images_image2D_rg16[_vko_storage_image_index(id)] +#define vko_image2D_rg8(id) \ + _vko_storage_images_image2D_rg8[_vko_storage_image_index(id)] +#define vko_image2D_r16(id) \ + _vko_storage_images_image2D_r16[_vko_storage_image_index(id)] +#define vko_image2D_r8(id) \ + _vko_storage_images_image2D_r8[_vko_storage_image_index(id)] +#define vko_image2D_rgba16_snorm(id) \ + _vko_storage_images_image2D_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image2D_rgba8_snorm(id) \ + _vko_storage_images_image2D_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image2D_rg16_snorm(id) \ + _vko_storage_images_image2D_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image2D_rg8_snorm(id) \ + _vko_storage_images_image2D_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image2D_r16_snorm(id) \ + _vko_storage_images_image2D_r16_snorm[_vko_storage_image_index(id)] +#define vko_image2D_r8_snorm(id) \ + _vko_storage_images_image2D_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage2D_rgba32i(id) \ + _vko_storage_images_iimage2D_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage2D_rgba16i(id) \ + _vko_storage_images_iimage2D_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage2D_rgba8i(id) \ + _vko_storage_images_iimage2D_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage2D_rg32i(id) \ + _vko_storage_images_iimage2D_rg32i[_vko_storage_image_index(id)] +#define vko_iimage2D_rg16i(id) \ + _vko_storage_images_iimage2D_rg16i[_vko_storage_image_index(id)] +#define vko_iimage2D_rg8i(id) \ + _vko_storage_images_iimage2D_rg8i[_vko_storage_image_index(id)] +#define vko_iimage2D_r32i(id) \ + _vko_storage_images_iimage2D_r32i[_vko_storage_image_index(id)] +#define vko_iimage2D_r16i(id) \ + _vko_storage_images_iimage2D_r16i[_vko_storage_image_index(id)] +#define vko_iimage2D_r8i(id) \ + _vko_storage_images_iimage2D_r8i[_vko_storage_image_index(id)] +#define vko_uimage2D_rgba32ui(id) \ + _vko_storage_images_uimage2D_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage2D_rgba16ui(id) \ + _vko_storage_images_uimage2D_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage2D_rgb10_a2ui(id) \ + _vko_storage_images_uimage2D_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage2D_rgba8ui(id) \ + _vko_storage_images_uimage2D_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage2D_rg32ui(id) \ + _vko_storage_images_uimage2D_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage2D_rg16ui(id) \ + _vko_storage_images_uimage2D_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage2D_rg8ui(id) \ + _vko_storage_images_uimage2D_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage2D_r32ui(id) \ + _vko_storage_images_uimage2D_r32ui[_vko_storage_image_index(id)] +#define vko_uimage2D_r16ui(id) \ + _vko_storage_images_uimage2D_r16ui[_vko_storage_image_index(id)] +#define vko_uimage2D_r8ui(id) \ + _vko_storage_images_uimage2D_r8ui[_vko_storage_image_index(id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(2DArray) +#define vko_image2DArray_rgba32f(id) \ + _vko_storage_images_image2DArray_rgba32f[_vko_storage_image_index(id)] +#define vko_image2DArray_rgba16f(id) \ + _vko_storage_images_image2DArray_rgba16f[_vko_storage_image_index(id)] +#define vko_image2DArray_rg32f(id) \ + _vko_storage_images_image2DArray_rg32f[_vko_storage_image_index(id)] +#define vko_image2DArray_rg16f(id) \ + _vko_storage_images_image2DArray_rg16f[_vko_storage_image_index(id)] +#define vko_image2DArray_r11f_g11f_b10f(id) \ + _vko_storage_images_image2DArray_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image2DArray_r32f(id) \ + _vko_storage_images_image2DArray_r32f[_vko_storage_image_index(id)] +#define vko_image2DArray_r16f(id) \ + _vko_storage_images_image2DArray_r16f[_vko_storage_image_index(id)] +#define vko_image2DArray_rgba16(id) \ + _vko_storage_images_image2DArray_rgba16[_vko_storage_image_index(id)] +#define vko_image2DArray_rgb10_a2(id) \ + _vko_storage_images_image2DArray_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image2DArray_rgba8(id) \ + _vko_storage_images_image2DArray_rgba8[_vko_storage_image_index(id)] +#define vko_image2DArray_rg16(id) \ + _vko_storage_images_image2DArray_rg16[_vko_storage_image_index(id)] +#define vko_image2DArray_rg8(id) \ + _vko_storage_images_image2DArray_rg8[_vko_storage_image_index(id)] +#define vko_image2DArray_r16(id) \ + _vko_storage_images_image2DArray_r16[_vko_storage_image_index(id)] +#define vko_image2DArray_r8(id) \ + _vko_storage_images_image2DArray_r8[_vko_storage_image_index(id)] +#define vko_image2DArray_rgba16_snorm(id) \ + _vko_storage_images_image2DArray_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image2DArray_rgba8_snorm(id) \ + _vko_storage_images_image2DArray_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image2DArray_rg16_snorm(id) \ + _vko_storage_images_image2DArray_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image2DArray_rg8_snorm(id) \ + _vko_storage_images_image2DArray_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image2DArray_r16_snorm(id) \ + _vko_storage_images_image2DArray_r16_snorm[_vko_storage_image_index(id)] +#define vko_image2DArray_r8_snorm(id) \ + _vko_storage_images_image2DArray_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage2DArray_rgba32i(id) \ + _vko_storage_images_iimage2DArray_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_rgba16i(id) \ + _vko_storage_images_iimage2DArray_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_rgba8i(id) \ + _vko_storage_images_iimage2DArray_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_rg32i(id) \ + _vko_storage_images_iimage2DArray_rg32i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_rg16i(id) \ + _vko_storage_images_iimage2DArray_rg16i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_rg8i(id) \ + _vko_storage_images_iimage2DArray_rg8i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_r32i(id) \ + _vko_storage_images_iimage2DArray_r32i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_r16i(id) \ + _vko_storage_images_iimage2DArray_r16i[_vko_storage_image_index(id)] +#define vko_iimage2DArray_r8i(id) \ + _vko_storage_images_iimage2DArray_r8i[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rgba32ui(id) \ + _vko_storage_images_uimage2DArray_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rgba16ui(id) \ + _vko_storage_images_uimage2DArray_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rgb10_a2ui(id) \ + _vko_storage_images_uimage2DArray_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rgba8ui(id) \ + _vko_storage_images_uimage2DArray_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rg32ui(id) \ + _vko_storage_images_uimage2DArray_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rg16ui(id) \ + _vko_storage_images_uimage2DArray_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_rg8ui(id) \ + _vko_storage_images_uimage2DArray_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_r32ui(id) \ + _vko_storage_images_uimage2DArray_r32ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_r16ui(id) \ + _vko_storage_images_uimage2DArray_r16ui[_vko_storage_image_index(id)] +#define vko_uimage2DArray_r8ui(id) \ + _vko_storage_images_uimage2DArray_r8ui[_vko_storage_image_index(id)] + +#if VKO_STORAGE_IMAGE_MULTISAMPLE_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(2DMS) +#define vko_image2DMS_rgba32f(id) \ + _vko_storage_images_image2DMS_rgba32f[_vko_storage_image_index(id)] +#define vko_image2DMS_rgba16f(id) \ + _vko_storage_images_image2DMS_rgba16f[_vko_storage_image_index(id)] +#define vko_image2DMS_rg32f(id) \ + _vko_storage_images_image2DMS_rg32f[_vko_storage_image_index(id)] +#define vko_image2DMS_rg16f(id) \ + _vko_storage_images_image2DMS_rg16f[_vko_storage_image_index(id)] +#define vko_image2DMS_r11f_g11f_b10f(id) \ + _vko_storage_images_image2DMS_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image2DMS_r32f(id) \ + _vko_storage_images_image2DMS_r32f[_vko_storage_image_index(id)] +#define vko_image2DMS_r16f(id) \ + _vko_storage_images_image2DMS_r16f[_vko_storage_image_index(id)] +#define vko_image2DMS_rgba16(id) \ + _vko_storage_images_image2DMS_rgba16[_vko_storage_image_index(id)] +#define vko_image2DMS_rgb10_a2(id) \ + _vko_storage_images_image2DMS_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image2DMS_rgba8(id) \ + _vko_storage_images_image2DMS_rgba8[_vko_storage_image_index(id)] +#define vko_image2DMS_rg16(id) \ + _vko_storage_images_image2DMS_rg16[_vko_storage_image_index(id)] +#define vko_image2DMS_rg8(id) \ + _vko_storage_images_image2DMS_rg8[_vko_storage_image_index(id)] +#define vko_image2DMS_r16(id) \ + _vko_storage_images_image2DMS_r16[_vko_storage_image_index(id)] +#define vko_image2DMS_r8(id) \ + _vko_storage_images_image2DMS_r8[_vko_storage_image_index(id)] +#define vko_image2DMS_rgba16_snorm(id) \ + _vko_storage_images_image2DMS_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image2DMS_rgba8_snorm(id) \ + _vko_storage_images_image2DMS_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image2DMS_rg16_snorm(id) \ + _vko_storage_images_image2DMS_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image2DMS_rg8_snorm(id) \ + _vko_storage_images_image2DMS_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image2DMS_r16_snorm(id) \ + _vko_storage_images_image2DMS_r16_snorm[_vko_storage_image_index(id)] +#define vko_image2DMS_r8_snorm(id) \ + _vko_storage_images_image2DMS_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage2DMS_rgba32i(id) \ + _vko_storage_images_iimage2DMS_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_rgba16i(id) \ + _vko_storage_images_iimage2DMS_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_rgba8i(id) \ + _vko_storage_images_iimage2DMS_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_rg32i(id) \ + _vko_storage_images_iimage2DMS_rg32i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_rg16i(id) \ + _vko_storage_images_iimage2DMS_rg16i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_rg8i(id) \ + _vko_storage_images_iimage2DMS_rg8i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_r32i(id) \ + _vko_storage_images_iimage2DMS_r32i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_r16i(id) \ + _vko_storage_images_iimage2DMS_r16i[_vko_storage_image_index(id)] +#define vko_iimage2DMS_r8i(id) \ + _vko_storage_images_iimage2DMS_r8i[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rgba32ui(id) \ + _vko_storage_images_uimage2DMS_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rgba16ui(id) \ + _vko_storage_images_uimage2DMS_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rgb10_a2ui(id) \ + _vko_storage_images_uimage2DMS_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rgba8ui(id) \ + _vko_storage_images_uimage2DMS_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rg32ui(id) \ + _vko_storage_images_uimage2DMS_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rg16ui(id) \ + _vko_storage_images_uimage2DMS_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_rg8ui(id) \ + _vko_storage_images_uimage2DMS_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_r32ui(id) \ + _vko_storage_images_uimage2DMS_r32ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_r16ui(id) \ + _vko_storage_images_uimage2DMS_r16ui[_vko_storage_image_index(id)] +#define vko_uimage2DMS_r8ui(id) \ + _vko_storage_images_uimage2DMS_r8ui[_vko_storage_image_index(id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(2DMSArray) +#define vko_image2DMSArray_rgba32f(id) \ + _vko_storage_images_image2DMSArray_rgba32f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rgba16f(id) \ + _vko_storage_images_image2DMSArray_rgba16f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rg32f(id) \ + _vko_storage_images_image2DMSArray_rg32f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rg16f(id) \ + _vko_storage_images_image2DMSArray_rg16f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r11f_g11f_b10f(id) \ + _vko_storage_images_image2DMSArray_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r32f(id) \ + _vko_storage_images_image2DMSArray_r32f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r16f(id) \ + _vko_storage_images_image2DMSArray_r16f[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rgba16(id) \ + _vko_storage_images_image2DMSArray_rgba16[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rgb10_a2(id) \ + _vko_storage_images_image2DMSArray_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rgba8(id) \ + _vko_storage_images_image2DMSArray_rgba8[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rg16(id) \ + _vko_storage_images_image2DMSArray_rg16[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rg8(id) \ + _vko_storage_images_image2DMSArray_rg8[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r16(id) \ + _vko_storage_images_image2DMSArray_r16[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r8(id) \ + _vko_storage_images_image2DMSArray_r8[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rgba16_snorm(id) \ + _vko_storage_images_image2DMSArray_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rgba8_snorm(id) \ + _vko_storage_images_image2DMSArray_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rg16_snorm(id) \ + _vko_storage_images_image2DMSArray_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image2DMSArray_rg8_snorm(id) \ + _vko_storage_images_image2DMSArray_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r16_snorm(id) \ + _vko_storage_images_image2DMSArray_r16_snorm[_vko_storage_image_index(id)] +#define vko_image2DMSArray_r8_snorm(id) \ + _vko_storage_images_image2DMSArray_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_rgba32i(id) \ + _vko_storage_images_iimage2DMSArray_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_rgba16i(id) \ + _vko_storage_images_iimage2DMSArray_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_rgba8i(id) \ + _vko_storage_images_iimage2DMSArray_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_rg32i(id) \ + _vko_storage_images_iimage2DMSArray_rg32i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_rg16i(id) \ + _vko_storage_images_iimage2DMSArray_rg16i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_rg8i(id) \ + _vko_storage_images_iimage2DMSArray_rg8i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_r32i(id) \ + _vko_storage_images_iimage2DMSArray_r32i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_r16i(id) \ + _vko_storage_images_iimage2DMSArray_r16i[_vko_storage_image_index(id)] +#define vko_iimage2DMSArray_r8i(id) \ + _vko_storage_images_iimage2DMSArray_r8i[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rgba32ui(id) \ + _vko_storage_images_uimage2DMSArray_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rgba16ui(id) \ + _vko_storage_images_uimage2DMSArray_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rgb10_a2ui(id) \ + _vko_storage_images_uimage2DMSArray_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rgba8ui(id) \ + _vko_storage_images_uimage2DMSArray_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rg32ui(id) \ + _vko_storage_images_uimage2DMSArray_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rg16ui(id) \ + _vko_storage_images_uimage2DMSArray_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_rg8ui(id) \ + _vko_storage_images_uimage2DMSArray_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_r32ui(id) \ + _vko_storage_images_uimage2DMSArray_r32ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_r16ui(id) \ + _vko_storage_images_uimage2DMSArray_r16ui[_vko_storage_image_index(id)] +#define vko_uimage2DMSArray_r8ui(id) \ + _vko_storage_images_uimage2DMSArray_r8ui[_vko_storage_image_index(id)] + +#endif // VKO_STORAGE_IMAGE_MULTISAMPLE_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(3D) +#define vko_image3D_rgba32f(id) \ + _vko_storage_images_image3D_rgba32f[_vko_storage_image_index(id)] +#define vko_image3D_rgba16f(id) \ + _vko_storage_images_image3D_rgba16f[_vko_storage_image_index(id)] +#define vko_image3D_rg32f(id) \ + _vko_storage_images_image3D_rg32f[_vko_storage_image_index(id)] +#define vko_image3D_rg16f(id) \ + _vko_storage_images_image3D_rg16f[_vko_storage_image_index(id)] +#define vko_image3D_r11f_g11f_b10f(id) \ + _vko_storage_images_image3D_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_image3D_r32f(id) \ + _vko_storage_images_image3D_r32f[_vko_storage_image_index(id)] +#define vko_image3D_r16f(id) \ + _vko_storage_images_image3D_r16f[_vko_storage_image_index(id)] +#define vko_image3D_rgba16(id) \ + _vko_storage_images_image3D_rgba16[_vko_storage_image_index(id)] +#define vko_image3D_rgb10_a2(id) \ + _vko_storage_images_image3D_rgb10_a2[_vko_storage_image_index(id)] +#define vko_image3D_rgba8(id) \ + _vko_storage_images_image3D_rgba8[_vko_storage_image_index(id)] +#define vko_image3D_rg16(id) \ + _vko_storage_images_image3D_rg16[_vko_storage_image_index(id)] +#define vko_image3D_rg8(id) \ + _vko_storage_images_image3D_rg8[_vko_storage_image_index(id)] +#define vko_image3D_r16(id) \ + _vko_storage_images_image3D_r16[_vko_storage_image_index(id)] +#define vko_image3D_r8(id) \ + _vko_storage_images_image3D_r8[_vko_storage_image_index(id)] +#define vko_image3D_rgba16_snorm(id) \ + _vko_storage_images_image3D_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_image3D_rgba8_snorm(id) \ + _vko_storage_images_image3D_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_image3D_rg16_snorm(id) \ + _vko_storage_images_image3D_rg16_snorm[_vko_storage_image_index(id)] +#define vko_image3D_rg8_snorm(id) \ + _vko_storage_images_image3D_rg8_snorm[_vko_storage_image_index(id)] +#define vko_image3D_r16_snorm(id) \ + _vko_storage_images_image3D_r16_snorm[_vko_storage_image_index(id)] +#define vko_image3D_r8_snorm(id) \ + _vko_storage_images_image3D_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimage3D_rgba32i(id) \ + _vko_storage_images_iimage3D_rgba32i[_vko_storage_image_index(id)] +#define vko_iimage3D_rgba16i(id) \ + _vko_storage_images_iimage3D_rgba16i[_vko_storage_image_index(id)] +#define vko_iimage3D_rgba8i(id) \ + _vko_storage_images_iimage3D_rgba8i[_vko_storage_image_index(id)] +#define vko_iimage3D_rg32i(id) \ + _vko_storage_images_iimage3D_rg32i[_vko_storage_image_index(id)] +#define vko_iimage3D_rg16i(id) \ + _vko_storage_images_iimage3D_rg16i[_vko_storage_image_index(id)] +#define vko_iimage3D_rg8i(id) \ + _vko_storage_images_iimage3D_rg8i[_vko_storage_image_index(id)] +#define vko_iimage3D_r32i(id) \ + _vko_storage_images_iimage3D_r32i[_vko_storage_image_index(id)] +#define vko_iimage3D_r16i(id) \ + _vko_storage_images_iimage3D_r16i[_vko_storage_image_index(id)] +#define vko_iimage3D_r8i(id) \ + _vko_storage_images_iimage3D_r8i[_vko_storage_image_index(id)] +#define vko_uimage3D_rgba32ui(id) \ + _vko_storage_images_uimage3D_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimage3D_rgba16ui(id) \ + _vko_storage_images_uimage3D_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimage3D_rgb10_a2ui(id) \ + _vko_storage_images_uimage3D_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimage3D_rgba8ui(id) \ + _vko_storage_images_uimage3D_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimage3D_rg32ui(id) \ + _vko_storage_images_uimage3D_rg32ui[_vko_storage_image_index(id)] +#define vko_uimage3D_rg16ui(id) \ + _vko_storage_images_uimage3D_rg16ui[_vko_storage_image_index(id)] +#define vko_uimage3D_rg8ui(id) \ + _vko_storage_images_uimage3D_rg8ui[_vko_storage_image_index(id)] +#define vko_uimage3D_r32ui(id) \ + _vko_storage_images_uimage3D_r32ui[_vko_storage_image_index(id)] +#define vko_uimage3D_r16ui(id) \ + _vko_storage_images_uimage3D_r16ui[_vko_storage_image_index(id)] +#define vko_uimage3D_r8ui(id) \ + _vko_storage_images_uimage3D_r8ui[_vko_storage_image_index(id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(Cube) +#define vko_imageCube_rgba32f(id) \ + _vko_storage_images_imageCube_rgba32f[_vko_storage_image_index(id)] +#define vko_imageCube_rgba16f(id) \ + _vko_storage_images_imageCube_rgba16f[_vko_storage_image_index(id)] +#define vko_imageCube_rg32f(id) \ + _vko_storage_images_imageCube_rg32f[_vko_storage_image_index(id)] +#define vko_imageCube_rg16f(id) \ + _vko_storage_images_imageCube_rg16f[_vko_storage_image_index(id)] +#define vko_imageCube_r11f_g11f_b10f(id) \ + _vko_storage_images_imageCube_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_imageCube_r32f(id) \ + _vko_storage_images_imageCube_r32f[_vko_storage_image_index(id)] +#define vko_imageCube_r16f(id) \ + _vko_storage_images_imageCube_r16f[_vko_storage_image_index(id)] +#define vko_imageCube_rgba16(id) \ + _vko_storage_images_imageCube_rgba16[_vko_storage_image_index(id)] +#define vko_imageCube_rgb10_a2(id) \ + _vko_storage_images_imageCube_rgb10_a2[_vko_storage_image_index(id)] +#define vko_imageCube_rgba8(id) \ + _vko_storage_images_imageCube_rgba8[_vko_storage_image_index(id)] +#define vko_imageCube_rg16(id) \ + _vko_storage_images_imageCube_rg16[_vko_storage_image_index(id)] +#define vko_imageCube_rg8(id) \ + _vko_storage_images_imageCube_rg8[_vko_storage_image_index(id)] +#define vko_imageCube_r16(id) \ + _vko_storage_images_imageCube_r16[_vko_storage_image_index(id)] +#define vko_imageCube_r8(id) \ + _vko_storage_images_imageCube_r8[_vko_storage_image_index(id)] +#define vko_imageCube_rgba16_snorm(id) \ + _vko_storage_images_imageCube_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_imageCube_rgba8_snorm(id) \ + _vko_storage_images_imageCube_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_imageCube_rg16_snorm(id) \ + _vko_storage_images_imageCube_rg16_snorm[_vko_storage_image_index(id)] +#define vko_imageCube_rg8_snorm(id) \ + _vko_storage_images_imageCube_rg8_snorm[_vko_storage_image_index(id)] +#define vko_imageCube_r16_snorm(id) \ + _vko_storage_images_imageCube_r16_snorm[_vko_storage_image_index(id)] +#define vko_imageCube_r8_snorm(id) \ + _vko_storage_images_imageCube_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimageCube_rgba32i(id) \ + _vko_storage_images_iimageCube_rgba32i[_vko_storage_image_index(id)] +#define vko_iimageCube_rgba16i(id) \ + _vko_storage_images_iimageCube_rgba16i[_vko_storage_image_index(id)] +#define vko_iimageCube_rgba8i(id) \ + _vko_storage_images_iimageCube_rgba8i[_vko_storage_image_index(id)] +#define vko_iimageCube_rg32i(id) \ + _vko_storage_images_iimageCube_rg32i[_vko_storage_image_index(id)] +#define vko_iimageCube_rg16i(id) \ + _vko_storage_images_iimageCube_rg16i[_vko_storage_image_index(id)] +#define vko_iimageCube_rg8i(id) \ + _vko_storage_images_iimageCube_rg8i[_vko_storage_image_index(id)] +#define vko_iimageCube_r32i(id) \ + _vko_storage_images_iimageCube_r32i[_vko_storage_image_index(id)] +#define vko_iimageCube_r16i(id) \ + _vko_storage_images_iimageCube_r16i[_vko_storage_image_index(id)] +#define vko_iimageCube_r8i(id) \ + _vko_storage_images_iimageCube_r8i[_vko_storage_image_index(id)] +#define vko_uimageCube_rgba32ui(id) \ + _vko_storage_images_uimageCube_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimageCube_rgba16ui(id) \ + _vko_storage_images_uimageCube_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimageCube_rgb10_a2ui(id) \ + _vko_storage_images_uimageCube_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimageCube_rgba8ui(id) \ + _vko_storage_images_uimageCube_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimageCube_rg32ui(id) \ + _vko_storage_images_uimageCube_rg32ui[_vko_storage_image_index(id)] +#define vko_uimageCube_rg16ui(id) \ + _vko_storage_images_uimageCube_rg16ui[_vko_storage_image_index(id)] +#define vko_uimageCube_rg8ui(id) \ + _vko_storage_images_uimageCube_rg8ui[_vko_storage_image_index(id)] +#define vko_uimageCube_r32ui(id) \ + _vko_storage_images_uimageCube_r32ui[_vko_storage_image_index(id)] +#define vko_uimageCube_r16ui(id) \ + _vko_storage_images_uimageCube_r16ui[_vko_storage_image_index(id)] +#define vko_uimageCube_r8ui(id) \ + _vko_storage_images_uimageCube_r8ui[_vko_storage_image_index(id)] + +#if VKO_IMAGE_CUBE_ARRAY_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION(CubeArray) +#define vko_imageCubeArray_rgba32f(id) \ + _vko_storage_images_imageCubeArray_rgba32f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rgba16f(id) \ + _vko_storage_images_imageCubeArray_rgba16f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rg32f(id) \ + _vko_storage_images_imageCubeArray_rg32f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rg16f(id) \ + _vko_storage_images_imageCubeArray_rg16f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r11f_g11f_b10f(id) \ + _vko_storage_images_imageCubeArray_r11f_g11f_b10f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r32f(id) \ + _vko_storage_images_imageCubeArray_r32f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r16f(id) \ + _vko_storage_images_imageCubeArray_r16f[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rgba16(id) \ + _vko_storage_images_imageCubeArray_rgba16[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rgb10_a2(id) \ + _vko_storage_images_imageCubeArray_rgb10_a2[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rgba8(id) \ + _vko_storage_images_imageCubeArray_rgba8[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rg16(id) \ + _vko_storage_images_imageCubeArray_rg16[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rg8(id) \ + _vko_storage_images_imageCubeArray_rg8[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r16(id) \ + _vko_storage_images_imageCubeArray_r16[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r8(id) \ + _vko_storage_images_imageCubeArray_r8[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rgba16_snorm(id) \ + _vko_storage_images_imageCubeArray_rgba16_snorm[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rgba8_snorm(id) \ + _vko_storage_images_imageCubeArray_rgba8_snorm[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rg16_snorm(id) \ + _vko_storage_images_imageCubeArray_rg16_snorm[_vko_storage_image_index(id)] +#define vko_imageCubeArray_rg8_snorm(id) \ + _vko_storage_images_imageCubeArray_rg8_snorm[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r16_snorm(id) \ + _vko_storage_images_imageCubeArray_r16_snorm[_vko_storage_image_index(id)] +#define vko_imageCubeArray_r8_snorm(id) \ + _vko_storage_images_imageCubeArray_r8_snorm[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_rgba32i(id) \ + _vko_storage_images_iimageCubeArray_rgba32i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_rgba16i(id) \ + _vko_storage_images_iimageCubeArray_rgba16i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_rgba8i(id) \ + _vko_storage_images_iimageCubeArray_rgba8i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_rg32i(id) \ + _vko_storage_images_iimageCubeArray_rg32i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_rg16i(id) \ + _vko_storage_images_iimageCubeArray_rg16i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_rg8i(id) \ + _vko_storage_images_iimageCubeArray_rg8i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_r32i(id) \ + _vko_storage_images_iimageCubeArray_r32i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_r16i(id) \ + _vko_storage_images_iimageCubeArray_r16i[_vko_storage_image_index(id)] +#define vko_iimageCubeArray_r8i(id) \ + _vko_storage_images_iimageCubeArray_r8i[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rgba32ui(id) \ + _vko_storage_images_uimageCubeArray_rgba32ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rgba16ui(id) \ + _vko_storage_images_uimageCubeArray_rgba16ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rgb10_a2ui(id) \ + _vko_storage_images_uimageCubeArray_rgb10_a2ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rgba8ui(id) \ + _vko_storage_images_uimageCubeArray_rgba8ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rg32ui(id) \ + _vko_storage_images_uimageCubeArray_rg32ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rg16ui(id) \ + _vko_storage_images_uimageCubeArray_rg16ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_rg8ui(id) \ + _vko_storage_images_uimageCubeArray_rg8ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_r32ui(id) \ + _vko_storage_images_uimageCubeArray_r32ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_r16ui(id) \ + _vko_storage_images_uimageCubeArray_r16ui[_vko_storage_image_index(id)] +#define vko_uimageCubeArray_r8ui(id) \ + _vko_storage_images_uimageCubeArray_r8ui[_vko_storage_image_index(id)] + +#endif // VKO_IMAGE_CUBE_ARRAY_ENABLED + +#undef _VKO_DECLARE_STORAGE_IMAGE +#undef _VKO_DECLARE_STORAGE_IMAGE_DIMENSION + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if VKO_IMAGE_INT64_ATOMICS_ENABLED + +#define _VKO_DECLARE_STORAGE_IMAGE_INT64(TYPE, FORMAT) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_IMAGE_BINDING, FORMAT) \ + uniform TYPE _vko_storage_images_##TYPE##_##FORMAT[]; + +#define _VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(DIMENSION) \ + _VKO_DECLARE_STORAGE_IMAGE_INT64(i64image##DIMENSION, r64i) \ + _VKO_DECLARE_STORAGE_IMAGE_INT64(u64image##DIMENSION, r64ui) + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(1D) +#define vko_i64image1D_r64i(storage_image_id) \ + _vko_images_i64image1D_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image1D_r64ui(storage_image_id) \ + _vko_images_u64image1D_r64ui[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(1DArray) +#define vko_i64image1DArray_r64i(storage_image_id) \ + _vko_images_i64image1DArray_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image1DArray_r64ui(storage_image_id) \ + _vko_images_u64image1DArray_r64ui[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(2D) +#define vko_i64image2D_r64i(storage_image_id) \ + _vko_images_i64image2D_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image2D_r64ui(storage_image_id) \ + _vko_images_u64image2D_r64ui[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(2DArray) +#define vko_i64image2DArray_r64i(storage_image_id) \ + _vko_images_i64image2DArray_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image2DArray_r64ui(storage_image_id) \ + _vko_images_u64image2DArray_r64ui[_vko_storage_image_index(storage_image_id)] + +#if VKO_STORAGE_IMAGE_MULTISAMPLE_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(2DMS) +#define vko_i64image2DMS_r64i(storage_image_id) \ + _vko_images_i64image2DMS_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image2DMS_r64ui(storage_image_id) \ + _vko_images_u64image2DMS_r64ui[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(2DMSArray) +#define vko_i64image2DMSArray_r64i(storage_image_id) \ + _vko_images_i64image2DMSArray_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image2DMSArray_r64ui(storage_image_id) \ + _vko_images_u64image2DMSArray_r64ui[_vko_storage_image_index(storage_image_id)] + +#endif // VKO_STORAGE_IMAGE_MULTISAMPLE_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(3D) +#define vko_i64image3D_r64i(storage_image_id) \ + _vko_images_i64image3D_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64image3D_r64ui(storage_image_id) \ + _vko_images_u64image3D_r64ui[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(Cube) +#define vko_i64imageCube_r64i(storage_image_id) \ + _vko_images_i64imageCube_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64imageCube_r64ui(storage_image_id) \ + _vko_images_u64imageCube_r64ui[_vko_storage_image_index(storage_image_id)] + +#if VKO_IMAGE_CUBE_ARRAY_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_INT64_DIMENSION(CubeArray) +#define vko_i64imageCubeArray_r64i(storage_image_id) \ + _vko_images_i64imageCubeArray_r64i[_vko_storage_image_index(storage_image_id)] +#define vko_u64imageCubeArray_r64ui(storage_image_id) \ + _vko_images_u64imageCubeArray_r64ui[_vko_storage_image_index(storage_image_id)] + +#endif // VKO_IMAGE_CUBE_ARRAY_ENABLED + +#undef _VKO_DECLARE_IMAGE_INT64 +#undef _VKO_DECLARE_IMAGE_INT64_DIMENSION + +#endif // VKO_IMAGE_INT64_ATOMICS_ENABLED + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if VKO_STORAGE_IMAGE_WITHOUT_FORMAT_ENABLED + +#define _VKO_DECLARE_STORAGE_IMAGE_WITHOUT_FORMAT(TYPE) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_IMAGE_BINDING) \ + uniform TYPE _vko_storage_images_##TYPE[]; + +#define _VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(DIMENSION) \ + _VKO_DECLARE_STORAGE_IMAGE_WITHOUT_FORMAT(image##DIMENSION) \ + _VKO_DECLARE_STORAGE_IMAGE_WITHOUT_FORMAT(iimage##DIMENSION) \ + _VKO_DECLARE_STORAGE_IMAGE_WITHOUT_FORMAT(uimage##DIMENSION) + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(1D) +#define vko_image1D(storage_image_id) \ + _vko_storage_images_image1D[_vko_storage_image_index(storage_image_id)] +#define vko_iimage1D(storage_image_id) \ + _vko_storage_images_iimage1D[_vko_storage_image_index(storage_image_id)] +#define vko_uimage1D(storage_image_id) \ + _vko_storage_images_uimage1D[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(1DArray) +#define vko_image1DArray(storage_image_id) \ + _vko_storage_images_image1DArray[_vko_storage_image_index(storage_image_id)] +#define vko_iimage1DArray(storage_image_id) \ + _vko_storage_images_iimage1DArray[_vko_storage_image_index(storage_image_id)] +#define vko_uimage1DArray(storage_image_id) \ + _vko_storage_images_uimage1DArray[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(2D) +#define vko_image2D(storage_image_id) \ + _vko_storage_images_image2D[_vko_storage_image_index(storage_image_id)] +#define vko_iimage2D(storage_image_id) \ + _vko_storage_images_iimage2D[_vko_storage_image_index(storage_image_id)] +#define vko_uimage2D(storage_image_id) \ + _vko_storage_images_uimage2D[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(2DArray) +#define vko_image2DArray(storage_image_id) \ + _vko_storage_images_image2DArray[_vko_storage_image_index(storage_image_id)] +#define vko_iimage2DArray(storage_image_id) \ + _vko_storage_images_iimage2DArray[_vko_storage_image_index(storage_image_id)] +#define vko_uimage2DArray(storage_image_id) \ + _vko_storage_images_uimage2DArray[_vko_storage_image_index(storage_image_id)] + +#if VKO_STORAGE_IMAGE_MULTISAMPLE_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(2DMS) +#define vko_image2DMS(storage_image_id) \ + _vko_storage_images_image2DMS[_vko_storage_image_index(storage_image_id)] +#define vko_iimage2DMS(storage_image_id) \ + _vko_storage_images_iimage2DMS[_vko_storage_image_index(storage_image_id)] +#define vko_uimage2DMS(storage_image_id) \ + _vko_storage_images_uimage2DMS[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(2DMSArray) +#define vko_image2DMSArray(storage_image_id) \ + _vko_storage_images_image2DMSArray[_vko_storage_image_index(storage_image_id)] +#define vko_iimage2DMSArray(storage_image_id) \ + _vko_storage_images_iimage2DMSArray[_vko_storage_image_index(storage_image_id)] +#define vko_uimage2DMSArray(storage_image_id) \ + _vko_storage_images_uimage2DMSArray[_vko_storage_image_index(storage_image_id)] + +#endif // VKO_STORAGE_IMAGE_MULTISAMPLE_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(3D) +#define vko_image3D(storage_image_id) \ + _vko_storage_images_image3D[_vko_storage_image_index(storage_image_id)] +#define vko_iimage3D(storage_image_id) \ + _vko_storage_images_iimage3D[_vko_storage_image_index(storage_image_id)] +#define vko_uimage3D(storage_image_id) \ + _vko_storage_images_uimage3D[_vko_storage_image_index(storage_image_id)] + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(Cube) +#define vko_imageCube(storage_image_id) \ + _vko_storage_images_imageCube[_vko_storage_image_index(storage_image_id)] +#define vko_iimageCube(storage_image_id) \ + _vko_storage_images_iimageCube[_vko_storage_image_index(storage_image_id)] +#define vko_uimageCube(storage_image_id) \ + _vko_storage_images_uimageCube[_vko_storage_image_index(storage_image_id)] + +#if VKO_IMAGE_CUBE_ARRAY_ENABLED + +_VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT(CubeArray) +#define vko_imageCubeArray(storage_image_id) \ + _vko_storage_images_imageCubeArray[_vko_storage_image_index(storage_image_id)] +#define vko_iimageCubeArray(storage_image_id) \ + _vko_storage_images_iimageCubeArray[_vko_storage_image_index(storage_image_id)] +#define vko_uimageCubeArray(storage_image_id) \ + _vko_storage_images_uimageCubeArray[_vko_storage_image_index(storage_image_id)] + +#endif // VKO_IMAGE_CUBE_ARRAY_ENABLED + +#undef _VKO_DECLARE_STORAGE_IMAGE_WITHOUT_FORMAT +#undef _VKO_DECLARE_STORAGE_IMAGE_DIMENSION_WITHOUT_FORMAT + +#endif // VKO_STORAGE_IMAGE_WITHOUT_FORMAT_ENABLED + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define VKO_DECLARE_STORAGE_BUFFER(NAME, BLOCK) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_BUFFER_BINDING) \ + buffer BLOCK _vko_##NAME##_storage_buffers[]; + +#define VKO_DECLARE_STORAGE_BUFFER_WITH_LAYOUT(NAME, BLOCK, LAYOUT) \ + layout(set = VKO_GLOBAL_SET, binding = VKO_STORAGE_BUFFER_BINDING, LAYOUT) \ + buffer BLOCK _vko_##NAME##_storage_buffers[]; + +#define vko_buffer(NAME, id) \ + _vko_##NAME##_storage_buffers[_vko_storage_buffer_index(id)] + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if VKO_ACCELERATION_STRUCTURE_ENABLED + +layout(set = VKO_GLOBAL_SET, binding = VKO_ACCELERATION_STRUCTURE_BINDING) + uniform accelerationStructureEXT _vko_acceleration_structures_accelerationStructureEXT[]; + +#define vko_accelerationStructureEXT(id) \ + _vko_acceleration_structures_accelerationStructureEXT[_vko_acceleration_structure_index(id)] + +#endif // VKO_ACCELERATION_STRUCTURE_ENABLED + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if VKO_INPUT_ATTACHMENT_ENABLED + +#define _VKO_DECLARE_INPUT_ATTACHMENT(TYPE) \ + layout(set = VKO_LOCAL_SET, binding = VKO_INPUT_ATTACHMENT_BINDING, input_attachment_index = 0)\ + uniform TYPE _vko_input_attachments_##TYPE[]; + +_VKO_DECLARE_INPUT_ATTACHMENT(subpassInput) +#define vko_subpassInput(INDEX) \ + _vko_input_attachments_subpassInput[INDEX] + +_VKO_DECLARE_INPUT_ATTACHMENT(subpassInputMS) +#define vko_subpassInputMS(INDEX) \ + _vko_input_attachments_subpassInputMS[INDEX] + +_VKO_DECLARE_INPUT_ATTACHMENT(isubpassInput) +#define vko_isubpassInput(INDEX) \ + _vko_input_attachments_isubpassInput[INDEX] + +_VKO_DECLARE_INPUT_ATTACHMENT(isubpassInputMS) +#define vko_isubpassInputMS(INDEX) \ + _vko_input_attachments_isubpassInputMS[INDEX] + +_VKO_DECLARE_INPUT_ATTACHMENT(usubpassInput) +#define vko_usubpassInput(INDEX) \ + _vko_input_attachments_usubpassInput[INDEX] + +_VKO_DECLARE_INPUT_ATTACHMENT(usubpassInputMS) +#define vko_usubpassInputMS(INDEX) \ + _vko_input_attachments_usubpassInputMS[INDEX] + +#undef _VKO_DECLARE_INPUT_ATTACHMENT + +#endif // VKO_INPUT_ATTACHMENT_ENABLED + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif // _VULKANO_HEADER diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index e9722de96a..f68862297b 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -33,6 +33,13 @@ fn include_callback( base_path: &Path, includes: &mut Vec, ) -> Result { + if requested_source_path_raw == "vulkano.glsl" { + return Ok(ResolvedInclude { + resolved_name: "vulkano.glsl".to_owned(), + content: include_str!("../include/vulkano.glsl").to_owned(), + }); + } + let file_to_include = match directive_type { IncludeType::Relative => { let requested_source_path = Path::new(requested_source_path_raw); diff --git a/vulkano-shaders/src/structs.rs b/vulkano-shaders/src/structs.rs index eb72b47db3..806c324052 100644 --- a/vulkano-shaders/src/structs.rs +++ b/vulkano-shaders/src/structs.rs @@ -116,6 +116,10 @@ pub(super) fn write_structs( continue; } + if struct_ty.is_bindless_id() { + continue; + } + let custom_derives = if struct_ty.size().is_some() { input.custom_derives.as_slice() } else { @@ -871,6 +875,27 @@ impl TypeStruct { .max() .unwrap_or(Alignment::A1) } + + fn is_bindless_id(&self) -> bool { + self.members.len() == 2 + && self.members.iter().all(|member| { + matches!( + member.ty, + Type::Scalar(TypeScalar::Int(TypeInt { + width: IntWidth::W32, + signed: false, + })), + ) + }) + && matches!( + self.ident.to_string().as_str(), + "SamplerId" + | "SampledImageId" + | "StorageImageId" + | "StorageBufferId" + | "AccelerationStructureId", + ) + } } #[derive(Clone, Debug)] @@ -899,7 +924,13 @@ impl ToTokens for Serializer<'_, Type> { Type::Vector(ty) => Serializer(ty, self.1).to_tokens(tokens), Type::Matrix(ty) => Serializer(ty, self.1).to_tokens(tokens), Type::Array(ty) => Serializer(ty, self.1).to_tokens(tokens), - Type::Struct(ty) => tokens.append(ty.ident.clone()), + Type::Struct(ty) => { + if ty.is_bindless_id() { + tokens.extend(quote! { ::vulkano_taskgraph::descriptor_set:: }); + } + + tokens.append(ty.ident.clone()); + } } } } diff --git a/vulkano-taskgraph/Cargo.toml b/vulkano-taskgraph/Cargo.toml index ab0ed4ecf0..72da6c8788 100644 --- a/vulkano-taskgraph/Cargo.toml +++ b/vulkano-taskgraph/Cargo.toml @@ -15,6 +15,7 @@ categories = { workspace = true } [dependencies] ash = { workspace = true } +bytemuck = { workspace = true } concurrent-slotmap = { workspace = true } foldhash = { workspace = true } parking_lot = { workspace = true } diff --git a/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs b/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs index 2729d5e08a..48bd520195 100644 --- a/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs +++ b/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs @@ -1,5 +1,6 @@ use crate::{ command_buffer::{RecordingCommandBuffer, Result}, + descriptor_set::LOCAL_SET, Id, }; use ash::vk; @@ -10,7 +11,8 @@ use vulkano::{ buffer::{Buffer, BufferContents, IndexType}, device::DeviceOwned, pipeline::{ - ray_tracing::RayTracingPipeline, ComputePipeline, GraphicsPipeline, PipelineLayout, + ray_tracing::RayTracingPipeline, ComputePipeline, GraphicsPipeline, Pipeline, + PipelineBindPoint, PipelineLayout, }, DeviceSize, Version, VulkanObject, }; @@ -86,6 +88,16 @@ impl RecordingCommandBuffer<'_> { ) }; + let invalidate_from = self + .state + .invalidate_descriptor_sets(PipelineBindPoint::Compute, pipeline.layout()); + + if let Some(first_set) = invalidate_from { + if first_set <= LOCAL_SET { + self.bind_bindless_sets(PipelineBindPoint::Compute, pipeline.layout(), first_set); + } + } + self.death_row.push(pipeline.clone()); self @@ -112,6 +124,16 @@ impl RecordingCommandBuffer<'_> { ) }; + let invalidate_from = self + .state + .invalidate_descriptor_sets(PipelineBindPoint::Graphics, pipeline.layout()); + + if let Some(first_set) = invalidate_from { + if first_set <= LOCAL_SET { + self.bind_bindless_sets(PipelineBindPoint::Graphics, pipeline.layout(), first_set); + } + } + self.death_row.push(pipeline.clone()); self @@ -138,6 +160,20 @@ impl RecordingCommandBuffer<'_> { ) }; + let invalidate_from = self + .state + .invalidate_descriptor_sets(PipelineBindPoint::RayTracing, pipeline.layout()); + + if let Some(first_set) = invalidate_from { + if first_set <= LOCAL_SET { + self.bind_bindless_sets( + PipelineBindPoint::RayTracing, + pipeline.layout(), + first_set, + ); + } + } + self.death_row.push(pipeline.clone()); self diff --git a/vulkano-taskgraph/src/command_buffer/mod.rs b/vulkano-taskgraph/src/command_buffer/mod.rs index 6e68dad057..1305e79661 100644 --- a/vulkano-taskgraph/src/command_buffer/mod.rs +++ b/vulkano-taskgraph/src/command_buffer/mod.rs @@ -2,15 +2,23 @@ #[allow(unused_imports)] // everything is exported for future-proofing pub use self::commands::{clear::*, copy::*, dynamic_state::*, pipeline::*, sync::*}; -use crate::{graph::ResourceMap, resource::DeathRow, Id}; +use crate::{ + descriptor_set::{LocalDescriptorSet, GLOBAL_SET, LOCAL_SET}, + graph::ResourceMap, + resource::DeathRow, + Id, +}; use ash::vk; +use smallvec::SmallVec; use std::{any::Any, sync::Arc}; use vulkano::{ buffer::Buffer, command_buffer as raw, device::{Device, DeviceOwned}, image::Image, - VulkanObject, + pipeline::{PipelineBindPoint, PipelineLayout}, + render_pass::Framebuffer, + VulkanError, VulkanObject, }; mod commands; @@ -23,6 +31,7 @@ mod commands; /// [the raw `RecordingCommandBuffer`]: raw::RecordingCommandBuffer pub struct RecordingCommandBuffer<'a> { inner: &'a mut raw::RecordingCommandBuffer, + state: &'a mut CommandBufferState, accesses: ResourceAccesses<'a>, death_row: &'a mut DeathRow, } @@ -31,14 +40,28 @@ struct ResourceAccesses<'a> { resource_map: &'a ResourceMap<'a>, } +#[derive(Default)] +pub(crate) struct CommandBufferState { + descriptor_sets_graphics: Option, + descriptor_sets_compute: Option, + descriptor_sets_ray_tracing: Option, + local_descriptor_set: Option>, +} + +struct DescriptorSetState { + pipeline_layout: Arc, +} + impl<'a> RecordingCommandBuffer<'a> { pub(crate) unsafe fn new( inner: &'a mut raw::RecordingCommandBuffer, + state: &'a mut CommandBufferState, resource_map: &'a ResourceMap<'a>, death_row: &'a mut DeathRow, ) -> Self { RecordingCommandBuffer { inner, + state, accesses: ResourceAccesses { resource_map }, death_row, } @@ -71,6 +94,51 @@ impl<'a> RecordingCommandBuffer<'a> { self.death_row .extend(objects.into_iter().map(|object| object as _)); } + + fn bind_bindless_sets( + &mut self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &Arc, + first_set: u32, + ) { + let Some(bcx) = self.accesses.resource_map.resources().bindless_context() else { + return; + }; + + if !pipeline_layout + .set_layouts() + .get(GLOBAL_SET as usize) + .is_some_and(|layout| layout == bcx.global_set_layout()) + { + return; + } + + let mut descriptor_sets = SmallVec::<[_; 2]>::new(); + + if first_set == GLOBAL_SET { + descriptor_sets.push(bcx.global_set().as_raw()); + } + + if let Some(local_set) = &self.state.local_descriptor_set { + if pipeline_layout + .set_layouts() + .get(LOCAL_SET as usize) + .is_some_and(|layout| layout == local_set.as_raw().layout()) + { + descriptor_sets.push(local_set.as_raw()); + } + } + + unsafe { + self.inner.bind_descriptor_sets_unchecked( + pipeline_bind_point, + pipeline_layout, + first_set, + &descriptor_sets, + &[], + ) + }; + } } unsafe impl DeviceOwned for RecordingCommandBuffer<'_> { @@ -125,4 +193,101 @@ impl<'a> ResourceAccesses<'a> { } } +impl CommandBufferState { + pub(crate) fn reset(&mut self) { + *self = Self::default(); + } + + fn invalidate_descriptor_sets( + &mut self, + pipeline_bind_point: PipelineBindPoint, + pipeline_layout: &Arc, + ) -> Option { + let state = match pipeline_bind_point { + PipelineBindPoint::Graphics => &mut self.descriptor_sets_graphics, + PipelineBindPoint::Compute => &mut self.descriptor_sets_compute, + PipelineBindPoint::RayTracing => &mut self.descriptor_sets_ray_tracing, + _ => unreachable!(), + }; + + match state { + None => { + *state = Some(DescriptorSetState { + pipeline_layout: pipeline_layout.clone(), + }); + + Some(0) + } + Some(state) => { + if state.pipeline_layout == *pipeline_layout { + // If we're still using the exact same layout, then of course it's compatible. + None + } else if state.pipeline_layout.push_constant_ranges() + != pipeline_layout.push_constant_ranges() + { + state.pipeline_layout = pipeline_layout.clone(); + + // If the push constant ranges don't match, all bound descriptor sets are + // disturbed. + Some(0) + } else { + state.pipeline_layout = pipeline_layout.clone(); + + let old_layouts = state.pipeline_layout.set_layouts(); + let new_layouts = pipeline_layout.set_layouts(); + + // Find the first descriptor set layout in the current pipeline layout that + // isn't compatible with the corresponding set in the new pipeline layout. + // If an incompatible set was found, all bound sets from that slot onwards will + // be disturbed. + old_layouts + .iter() + .zip(new_layouts) + .position(|(old_layout, new_layout)| { + !old_layout.is_compatible_with(new_layout) + }) + .map(|num| num as u32) + } + } + } + } + + pub(crate) unsafe fn set_local_set( + &mut self, + resource_map: &ResourceMap<'_>, + death_row: &mut DeathRow, + framebuffer: &Framebuffer, + subpass_index: usize, + ) -> Result<(), VulkanError> { + let resources = resource_map.resources(); + let Some(bcx) = resources.bindless_context() else { + return Ok(()); + }; + let Some(local_set_layout) = bcx.local_set_layout() else { + return Ok(()); + }; + + let render_pass = framebuffer.render_pass(); + let subpass_description = &render_pass.subpasses()[subpass_index]; + + if subpass_description.input_attachments.is_empty() { + self.local_descriptor_set = None; + return Ok(()); + } + + let local_set = unsafe { + LocalDescriptorSet::new(resources, local_set_layout, framebuffer, subpass_index) + }?; + + self.local_descriptor_set = Some(local_set.clone()); + death_row.push(local_set); + + Ok(()) + } + + pub(crate) fn reset_local_set(&mut self) { + self.local_descriptor_set = None; + } +} + type Result> = ::std::result::Result; diff --git a/vulkano-taskgraph/src/descriptor_set.rs b/vulkano-taskgraph/src/descriptor_set.rs new file mode 100644 index 0000000000..64f1e4068a --- /dev/null +++ b/vulkano-taskgraph/src/descriptor_set.rs @@ -0,0 +1,1109 @@ +use crate::{ + resource::{ResourceStorage, Resources}, + Id, Ref, +}; +use ash::vk; +use bytemuck::{Pod, Zeroable}; +use concurrent_slotmap::{epoch, SlotId, SlotMap}; +use foldhash::HashMap; +use std::{collections::BTreeMap, iter, sync::Arc}; +use vulkano::{ + acceleration_structure::AccelerationStructure, + buffer::{Buffer, Subbuffer}, + descriptor_set::{ + allocator::{AllocationHandle, DescriptorSetAlloc, DescriptorSetAllocator}, + layout::{ + DescriptorBindingFlags, DescriptorSetLayout, DescriptorSetLayoutBinding, + DescriptorSetLayoutCreateFlags, DescriptorSetLayoutCreateInfo, DescriptorType, + }, + pool::{ + DescriptorPool, DescriptorPoolCreateFlags, DescriptorPoolCreateInfo, + DescriptorSetAllocateInfo, + }, + sys::RawDescriptorSet, + DescriptorImageViewInfo, WriteDescriptorSet, + }, + device::{Device, DeviceExtensions, DeviceFeatures, DeviceOwned}, + image::{ + sampler::{Sampler, SamplerCreateInfo}, + view::{ImageView, ImageViewCreateInfo}, + Image, ImageLayout, + }, + instance::Instance, + pipeline::{ + layout::{PipelineLayoutCreateInfo, PushConstantRange}, + PipelineLayout, PipelineShaderStageCreateInfo, + }, + render_pass::Framebuffer, + shader::ShaderStages, + DeviceSize, Validated, Version, VulkanError, VulkanObject, +}; + +// NOTE(Marc): The following constants must match the definitions in include/vulkano.glsl! + +/// The set number of the [`GlobalDescriptorSet`]. +pub const GLOBAL_SET: u32 = 0; + +/// The binding number of samplers in the [`GlobalDescriptorSet`]. +pub const SAMPLER_BINDING: u32 = 0; + +/// The binding number of sampled images in the [`GlobalDescriptorSet`]. +pub const SAMPLED_IMAGE_BINDING: u32 = 1; + +/// The binding number of storage images in the [`GlobalDescriptorSet`]. +pub const STORAGE_IMAGE_BINDING: u32 = 2; + +/// The binding number of storage buffers in the [`GlobalDescriptorSet`]. +pub const STORAGE_BUFFER_BINDING: u32 = 3; + +/// The binding number of acceleration structures in the [`GlobalDescriptorSet`]. +pub const ACCELERATION_STRUCTURE_BINDING: u32 = 4; + +/// The set number of the local descriptor set. +pub const LOCAL_SET: u32 = 1; + +/// The binding number of input attachments in the local descriptor set. +pub const INPUT_ATTACHMENT_BINDING: u32 = 0; + +#[derive(Debug)] +pub struct BindlessContext { + global_set: GlobalDescriptorSet, + local_set_layout: Option>, +} + +impl BindlessContext { + /// Returns the device extensions required to create a bindless context. + pub fn required_extensions(instance: &Instance) -> DeviceExtensions { + let mut extensions = DeviceExtensions::default(); + + if instance.api_version() < Version::V1_2 { + extensions.ext_descriptor_indexing = true; + } + + extensions + } + + /// Returns the device features required to create a bindless context. + pub fn required_features(_instance: &Instance) -> DeviceFeatures { + DeviceFeatures { + shader_sampled_image_array_dynamic_indexing: true, + shader_storage_image_array_dynamic_indexing: true, + shader_storage_buffer_array_dynamic_indexing: true, + descriptor_binding_sampled_image_update_after_bind: true, + descriptor_binding_storage_image_update_after_bind: true, + descriptor_binding_storage_buffer_update_after_bind: true, + descriptor_binding_update_unused_while_pending: true, + descriptor_binding_partially_bound: true, + runtime_descriptor_array: true, + ..DeviceFeatures::default() + } + } + + pub(crate) fn new( + resources: &Arc, + create_info: &BindlessContextCreateInfo<'_>, + ) -> Result> { + let global_set_layout = + GlobalDescriptorSet::create_layout(resources, create_info.global_set)?; + + let global_set = GlobalDescriptorSet::new(resources, &global_set_layout)?; + + let local_set_layout = create_info + .local_set + .map(|local_set| LocalDescriptorSet::create_layout(resources, local_set)) + .transpose()?; + + Ok(BindlessContext { + global_set, + local_set_layout, + }) + } + + /// Returns the layout of the [`GlobalDescriptorSet`]. + #[inline] + pub fn global_set_layout(&self) -> &Arc { + self.global_set.inner.layout() + } + + /// Returns the layout of the local descriptor set. + /// + /// Returns `None` if [`BindlessContextCreateInfo::local_set`] was not specified when creating + /// the bindless context. + #[inline] + pub fn local_set_layout(&self) -> Option<&Arc> { + self.local_set_layout.as_ref() + } + + /// Creates a new bindless pipeline layout from the union of the push constant requirements of + /// each stage in `stages` for push constant ranges and the [global descriptor set layout] and + /// optionally the [local descriptor set layout] for set layouts. + /// + /// All pipelines that you bind must have been created with a layout created like this or with + /// a compatible layout for the bindless system to be able to bind its descriptor sets. + /// + /// It is recommended that you share the same pipeline layout object with as many pipelines as + /// possible in order to reduce the amount of descriptor set (re)binding that is needed. + /// + /// See also [`pipeline_layout_create_info_from_stages`]. + /// + /// [global descriptor set layout]: Self::global_set_layout + /// [local descriptor set layout]: Self::local_set_layout + /// [`pipeline_layout_create_info_from_stages`]: Self::pipeline_layout_create_info_from_stages + pub fn pipeline_layout_from_stages<'a>( + &self, + stages: impl IntoIterator, + ) -> Result, Validated> { + PipelineLayout::new( + self.device().clone(), + self.pipeline_layout_create_info_from_stages(stages), + ) + } + + /// Creates a new bindless pipeline layout create info from the union of the push constant + /// requirements of each stage in `stages` for push constant ranges and the [global descriptor + /// set layout] and optionally the [local descriptor set layout] for set layouts. + /// + /// All pipelines that you bind must have been created with a layout created like this or with + /// a compatible layout for the bindless system to be able to bind its descriptor sets. + /// + /// It is recommended that you share the same pipeline layout object with as many pipelines as + /// possible in order to reduce the amount of descriptor set (re)binding that is needed. + /// + /// See also [`pipeline_layout_from_stages`]. + /// + /// [global descriptor set layout]: Self::global_set_layout + /// [local descriptor set layout]: Self::local_set_layout + /// [`pipeline_layout_from_stages`]: Self::pipeline_layout_from_stages + pub fn pipeline_layout_create_info_from_stages<'a>( + &self, + stages: impl IntoIterator, + ) -> PipelineLayoutCreateInfo { + let mut push_constant_ranges = Vec::::new(); + + for stage in stages { + let entry_point_info = stage.entry_point.info(); + + if let Some(range) = &entry_point_info.push_constant_requirements { + if let Some(existing_range) = + push_constant_ranges.iter_mut().find(|existing_range| { + existing_range.offset == range.offset && existing_range.size == range.size + }) + { + // If this range was already used before, add our stage to it. + existing_range.stages |= range.stages; + } else { + // If this range is new, insert it. + push_constant_ranges.push(*range); + } + } + } + + let mut set_layouts = vec![self.global_set_layout().clone()]; + + if let Some(local_set_layout) = &self.local_set_layout { + set_layouts.push(local_set_layout.clone()); + } + + PipelineLayoutCreateInfo { + set_layouts, + push_constant_ranges, + ..Default::default() + } + } + + /// Returns the `GlobalDescriptorSet`. + #[inline] + pub fn global_set(&self) -> &GlobalDescriptorSet { + &self.global_set + } +} + +unsafe impl DeviceOwned for BindlessContext { + #[inline] + fn device(&self) -> &Arc { + self.global_set.device() + } +} + +/// Parameters to create a new [`BindlessContext`]. +#[derive(Clone, Debug)] +pub struct BindlessContextCreateInfo<'a> { + /// Parameters to create the [`GlobalDescriptorSet`]. + /// + /// The default value is `&GlobalDescriptorSetCreateInfo::new()`. + pub global_set: &'a GlobalDescriptorSetCreateInfo<'a>, + + /// Parameters to create a local descriptor set. + /// + /// If set to `Some`, enables the use of bindless input attachments. + /// + /// The default value is `None`. + pub local_set: Option<&'a LocalDescriptorSetCreateInfo<'a>>, + + pub _ne: crate::NonExhaustive<'a>, +} + +impl Default for BindlessContextCreateInfo<'_> { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl BindlessContextCreateInfo<'_> { + /// Returns a default `BindlessContextCreateInfo`. + #[inline] + pub const fn new() -> Self { + Self { + global_set: &const { GlobalDescriptorSetCreateInfo::new() }, + local_set: None, + _ne: crate::NE, + } + } +} + +#[derive(Debug)] +pub struct GlobalDescriptorSet { + // DO NOT change the order of these fields! `ResourceStorage` must be dropped first because + // that guarantees that all flights are waited on before the descriptor set is destroyed. + resources: Arc, + inner: RawDescriptorSet, + + samplers: SlotMap, + sampled_images: SlotMap, + storage_images: SlotMap, + storage_buffers: SlotMap, + acceleration_structures: SlotMap, +} + +#[derive(Debug)] +pub struct SamplerDescriptor { + sampler: Arc, +} + +#[derive(Debug)] +pub struct SampledImageDescriptor { + image_view: Arc, + image_layout: ImageLayout, +} + +#[derive(Debug)] +pub struct StorageImageDescriptor { + image_view: Arc, + image_layout: ImageLayout, +} + +#[derive(Debug)] +pub struct StorageBufferDescriptor { + buffer: Arc, + offset: DeviceSize, + size: DeviceSize, +} + +#[derive(Debug)] +pub struct AccelerationStructureDescriptor { + acceleration_structure: Arc, +} + +impl GlobalDescriptorSet { + fn new( + resources: &Arc, + layout: &Arc, + ) -> Result { + let device = resources.device(); + + let allocator = Arc::new(GlobalDescriptorSetAllocator::new(device)); + let inner = RawDescriptorSet::new(allocator, layout, 0).map_err(Validated::unwrap)?; + + let global = resources.global(); + + let descriptor_count = |n| layout.bindings().get(&n).map_or(0, |b| b.descriptor_count); + let max_samplers = descriptor_count(SAMPLER_BINDING); + let max_sampled_images = descriptor_count(SAMPLED_IMAGE_BINDING); + let max_storage_images = descriptor_count(STORAGE_IMAGE_BINDING); + let max_storage_buffers = descriptor_count(STORAGE_BUFFER_BINDING); + let max_acceleration_structures = descriptor_count(ACCELERATION_STRUCTURE_BINDING); + + Ok(GlobalDescriptorSet { + resources: resources.clone(), + inner, + samplers: SlotMap::with_global(max_samplers, global.clone()), + sampled_images: SlotMap::with_global(max_sampled_images, global.clone()), + storage_images: SlotMap::with_global(max_storage_images, global.clone()), + storage_buffers: SlotMap::with_global(max_storage_buffers, global.clone()), + acceleration_structures: SlotMap::with_global( + max_acceleration_structures, + global.clone(), + ), + }) + } + + fn create_layout( + resources: &Arc, + create_info: &GlobalDescriptorSetCreateInfo<'_>, + ) -> Result, Validated> { + let device = resources.device(); + + let binding_flags = DescriptorBindingFlags::UPDATE_AFTER_BIND + | DescriptorBindingFlags::UPDATE_UNUSED_WHILE_PENDING + | DescriptorBindingFlags::PARTIALLY_BOUND; + + let stages = get_all_supported_shader_stages(device); + + let mut bindings = BTreeMap::from([ + ( + SAMPLER_BINDING, + DescriptorSetLayoutBinding { + binding_flags, + descriptor_count: create_info.max_samplers, + stages, + ..DescriptorSetLayoutBinding::new(DescriptorType::Sampler) + }, + ), + ( + SAMPLED_IMAGE_BINDING, + DescriptorSetLayoutBinding { + binding_flags, + descriptor_count: create_info.max_sampled_images, + stages, + ..DescriptorSetLayoutBinding::new(DescriptorType::SampledImage) + }, + ), + ( + STORAGE_IMAGE_BINDING, + DescriptorSetLayoutBinding { + binding_flags, + descriptor_count: create_info.max_storage_images, + stages, + ..DescriptorSetLayoutBinding::new(DescriptorType::StorageImage) + }, + ), + ( + STORAGE_BUFFER_BINDING, + DescriptorSetLayoutBinding { + binding_flags, + descriptor_count: create_info.max_storage_buffers, + stages, + ..DescriptorSetLayoutBinding::new(DescriptorType::StorageBuffer) + }, + ), + ]); + + if device.enabled_features().acceleration_structure { + bindings.insert( + ACCELERATION_STRUCTURE_BINDING, + DescriptorSetLayoutBinding { + binding_flags, + descriptor_count: create_info.max_acceleration_structures, + stages, + ..DescriptorSetLayoutBinding::new(DescriptorType::AccelerationStructure) + }, + ); + } + + let layout = DescriptorSetLayout::new( + device.clone(), + DescriptorSetLayoutCreateInfo { + flags: DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL, + bindings, + ..Default::default() + }, + )?; + + Ok(layout) + } + + /// Returns the underlying raw descriptor set. + #[inline] + pub fn as_raw(&self) -> &RawDescriptorSet { + &self.inner + } + + pub fn create_sampler( + &self, + create_info: SamplerCreateInfo, + ) -> Result> { + let sampler = Sampler::new(self.device().clone(), create_info)?; + + Ok(self.add_sampler(sampler)) + } + + pub fn create_sampled_image( + &self, + image_id: Id, + create_info: ImageViewCreateInfo, + image_layout: ImageLayout, + ) -> Result> { + let image_state = self.resources.image(image_id).unwrap(); + let image_view = ImageView::new(image_state.image().clone(), create_info)?; + + Ok(self.add_sampled_image(image_view, image_layout)) + } + + pub fn create_storage_image( + &self, + image_id: Id, + create_info: ImageViewCreateInfo, + image_layout: ImageLayout, + ) -> Result> { + let image_state = self.resources.image(image_id).unwrap(); + let image_view = ImageView::new(image_state.image().clone(), create_info)?; + + Ok(self.add_storage_image(image_view, image_layout)) + } + + pub fn create_storage_buffer( + &self, + buffer_id: Id, + offset: DeviceSize, + size: DeviceSize, + ) -> Result> { + let buffer_state = self.resources.buffer(buffer_id).unwrap(); + let buffer = buffer_state.buffer().clone(); + + Ok(self.add_storage_buffer(buffer, offset, size)) + } + + pub fn add_sampler(&self, sampler: Arc) -> SamplerId { + let descriptor = SamplerDescriptor { + sampler: sampler.clone(), + }; + let slot = self.samplers.insert(descriptor, self.resources.pin()); + + let write = + WriteDescriptorSet::sampler_array(SAMPLER_BINDING, slot.index(), iter::once(sampler)); + + unsafe { self.inner.update_unchecked(&[write], &[]) }; + + SamplerId::new(slot) + } + + pub fn add_sampled_image( + &self, + image_view: Arc, + image_layout: ImageLayout, + ) -> SampledImageId { + assert!(matches!( + image_layout, + ImageLayout::General + | ImageLayout::DepthStencilReadOnlyOptimal + | ImageLayout::ShaderReadOnlyOptimal + | ImageLayout::DepthReadOnlyStencilAttachmentOptimal + | ImageLayout::DepthAttachmentStencilReadOnlyOptimal, + )); + + let descriptor = SampledImageDescriptor { + image_view: image_view.clone(), + image_layout, + }; + let slot = self.sampled_images.insert(descriptor, self.resources.pin()); + + let write = WriteDescriptorSet::image_view_with_layout_array( + SAMPLED_IMAGE_BINDING, + slot.index(), + iter::once(DescriptorImageViewInfo { + image_view, + image_layout, + }), + ); + + unsafe { self.inner.update_unchecked(&[write], &[]) }; + + SampledImageId::new(slot) + } + + pub fn add_storage_image( + &self, + image_view: Arc, + image_layout: ImageLayout, + ) -> StorageImageId { + assert_eq!(image_layout, ImageLayout::General); + + let descriptor = StorageImageDescriptor { + image_view: image_view.clone(), + image_layout, + }; + let slot = self.storage_images.insert(descriptor, self.resources.pin()); + + let write = WriteDescriptorSet::image_view_with_layout_array( + STORAGE_IMAGE_BINDING, + slot.index(), + iter::once(DescriptorImageViewInfo { + image_view, + image_layout, + }), + ); + + unsafe { self.inner.update_unchecked(&[write], &[]) }; + + StorageImageId::new(slot) + } + + pub fn add_storage_buffer( + &self, + buffer: Arc, + offset: DeviceSize, + size: DeviceSize, + ) -> StorageBufferId { + let subbuffer = Subbuffer::from(buffer.clone()).slice(offset..offset + size); + + let descriptor = StorageBufferDescriptor { + buffer, + offset, + size, + }; + let slot = self + .storage_buffers + .insert(descriptor, self.resources.pin()); + + let write = WriteDescriptorSet::buffer_array( + STORAGE_BUFFER_BINDING, + slot.index(), + iter::once(subbuffer), + ); + + unsafe { self.inner.update_unchecked(&[write], &[]) }; + + StorageBufferId::new(slot) + } + + pub fn add_acceleration_structure( + &self, + acceleration_structure: Arc, + ) -> AccelerationStructureId { + let descriptor = AccelerationStructureDescriptor { + acceleration_structure: acceleration_structure.clone(), + }; + let slot = self + .acceleration_structures + .insert(descriptor, self.resources.pin()); + + let write = WriteDescriptorSet::acceleration_structure_array( + ACCELERATION_STRUCTURE_BINDING, + slot.index(), + iter::once(acceleration_structure), + ); + + unsafe { self.inner.update_unchecked(&[write], &[]) }; + + AccelerationStructureId::new(slot) + } + + pub unsafe fn remove_sampler(&self, id: SamplerId) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.samplers.remove(slot, self.resources.pin()).map(Ref) + } + + pub unsafe fn remove_sampled_image( + &self, + id: SampledImageId, + ) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.sampled_images + .remove(slot, self.resources.pin()) + .map(Ref) + } + + pub unsafe fn remove_storage_image( + &self, + id: StorageImageId, + ) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.storage_images + .remove(slot, self.resources.pin()) + .map(Ref) + } + + pub unsafe fn remove_storage_buffer( + &self, + id: StorageBufferId, + ) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.storage_buffers + .remove(slot, self.resources.pin()) + .map(Ref) + } + + pub unsafe fn remove_acceleration_structure( + &self, + id: AccelerationStructureId, + ) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.acceleration_structures + .remove(slot, self.resources.pin()) + .map(Ref) + } + + #[inline] + pub fn sampler(&self, id: SamplerId) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.samplers.get(slot, self.resources.pin()).map(Ref) + } + + #[inline] + pub fn sampled_image(&self, id: SampledImageId) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.sampled_images.get(slot, self.resources.pin()).map(Ref) + } + + #[inline] + pub fn storage_image(&self, id: StorageImageId) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.storage_images.get(slot, self.resources.pin()).map(Ref) + } + + #[inline] + pub fn storage_buffer(&self, id: StorageBufferId) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.storage_buffers + .get(slot, self.resources.pin()) + .map(Ref) + } + + #[inline] + pub fn acceleration_structure( + &self, + id: AccelerationStructureId, + ) -> Option> { + let slot = SlotId::new(id.index, id.generation); + + self.acceleration_structures + .get(slot, self.resources.pin()) + .map(Ref) + } + + pub(crate) fn try_collect(&self, guard: &epoch::Guard<'_>) { + self.samplers.try_collect(guard); + self.sampled_images.try_collect(guard); + self.storage_images.try_collect(guard); + self.storage_buffers.try_collect(guard); + self.acceleration_structures.try_collect(guard); + } +} + +unsafe impl VulkanObject for GlobalDescriptorSet { + type Handle = vk::DescriptorSet; + + #[inline] + fn handle(&self) -> Self::Handle { + self.inner.handle() + } +} + +unsafe impl DeviceOwned for GlobalDescriptorSet { + #[inline] + fn device(&self) -> &Arc { + self.inner.device() + } +} + +impl SamplerDescriptor { + #[inline] + pub fn sampler(&self) -> &Arc { + &self.sampler + } +} + +impl SampledImageDescriptor { + #[inline] + pub fn image_view(&self) -> &Arc { + &self.image_view + } + + #[inline] + pub fn image_layout(&self) -> ImageLayout { + self.image_layout + } +} + +impl StorageImageDescriptor { + #[inline] + pub fn image_view(&self) -> &Arc { + &self.image_view + } + + #[inline] + pub fn image_layout(&self) -> ImageLayout { + self.image_layout + } +} + +impl StorageBufferDescriptor { + #[inline] + pub fn buffer(&self) -> &Arc { + &self.buffer + } + + #[inline] + pub fn offset(&self) -> DeviceSize { + self.offset + } + + #[inline] + pub fn size(&self) -> DeviceSize { + self.size + } +} + +impl AccelerationStructureDescriptor { + #[inline] + pub fn acceleration_structure(&self) -> &Arc { + &self.acceleration_structure + } +} + +/// Parameters to create a new [`GlobalDescriptorSet`]. +#[derive(Clone, Debug)] +pub struct GlobalDescriptorSetCreateInfo<'a> { + /// The maximum number of [`Sampler`] descriptors that the collection can hold at once. + /// + /// The default value is `256` (28). + pub max_samplers: u32, + + /// The maximum number of sampled [`Image`] descriptors that the collection can hold at once. + /// + /// The default value is `1048576` (220). + pub max_sampled_images: u32, + + /// The maximum number of storage [`Image`] descriptors that the collection can hold at once. + /// + /// The default value is `1048576` (220). + pub max_storage_images: u32, + + /// The maximum number of storage [`Buffer`] descriptors that the collection can hold at once. + /// + /// The default value is `1048576` (220). + pub max_storage_buffers: u32, + + /// The maximum number of [`AccelerationStructure`] descriptors that the collection can hold at + /// once. + /// + /// The default value is `1048576` (220). + pub max_acceleration_structures: u32, + + pub _ne: crate::NonExhaustive<'a>, +} + +impl Default for GlobalDescriptorSetCreateInfo<'_> { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl GlobalDescriptorSetCreateInfo<'_> { + /// Returns a default `GlobalDescriptorSetCreateInfo`. + #[inline] + pub const fn new() -> Self { + Self { + max_samplers: 1 << 8, + max_sampled_images: 1 << 20, + max_storage_images: 1 << 20, + max_storage_buffers: 1 << 20, + max_acceleration_structures: 1 << 20, + _ne: crate::NE, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct SamplerId { + index: u32, + generation: u32, +} + +unsafe impl Pod for SamplerId {} +unsafe impl Zeroable for SamplerId {} + +impl SamplerId { + /// An ID that's guaranteed to be invalid. + pub const INVALID: Self = Self::new(SlotId::INVALID); + + const fn new(slot: SlotId) -> Self { + Self { + index: slot.index(), + generation: slot.generation(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct SampledImageId { + index: u32, + generation: u32, +} + +unsafe impl Pod for SampledImageId {} +unsafe impl Zeroable for SampledImageId {} + +impl SampledImageId { + /// An ID that's guaranteed to be invalid. + pub const INVALID: Self = Self::new(SlotId::INVALID); + + const fn new(slot: SlotId) -> Self { + Self { + index: slot.index(), + generation: slot.generation(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct StorageImageId { + index: u32, + generation: u32, +} + +unsafe impl Pod for StorageImageId {} +unsafe impl Zeroable for StorageImageId {} + +impl StorageImageId { + /// An ID that's guaranteed to be invalid. + pub const INVALID: Self = Self::new(SlotId::INVALID); + + const fn new(slot: SlotId) -> Self { + Self { + index: slot.index(), + generation: slot.generation(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct StorageBufferId { + index: u32, + generation: u32, +} + +unsafe impl Pod for StorageBufferId {} +unsafe impl Zeroable for StorageBufferId {} + +impl StorageBufferId { + /// An ID that's guaranteed to be invalid. + pub const INVALID: Self = Self::new(SlotId::INVALID); + + const fn new(slot: SlotId) -> Self { + Self { + index: slot.index(), + generation: slot.generation(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct AccelerationStructureId { + index: u32, + generation: u32, +} + +unsafe impl Pod for AccelerationStructureId {} +unsafe impl Zeroable for AccelerationStructureId {} + +impl AccelerationStructureId { + /// An ID that's guaranteed to be invalid. + pub const INVALID: Self = Self::new(SlotId::INVALID); + + const fn new(slot: SlotId) -> Self { + Self { + index: slot.index(), + generation: slot.generation(), + } + } +} + +struct GlobalDescriptorSetAllocator { + device: Arc, +} + +impl GlobalDescriptorSetAllocator { + fn new(device: &Arc) -> Self { + GlobalDescriptorSetAllocator { + device: device.clone(), + } + } +} + +unsafe impl DescriptorSetAllocator for GlobalDescriptorSetAllocator { + fn allocate( + &self, + layout: &Arc, + _variable_count: u32, + ) -> Result> { + let mut pool_sizes = HashMap::default(); + + for binding in layout.bindings().values() { + *pool_sizes.entry(binding.descriptor_type).or_insert(0) += binding.descriptor_count; + } + + let pool = Arc::new(DescriptorPool::new( + layout.device().clone(), + DescriptorPoolCreateInfo { + flags: DescriptorPoolCreateFlags::UPDATE_AFTER_BIND, + max_sets: 1, + pool_sizes, + ..Default::default() + }, + )?); + + let allocate_info = DescriptorSetAllocateInfo::new(layout.clone()); + + let inner = unsafe { pool.allocate_descriptor_sets(iter::once(allocate_info)) }? + .next() + .unwrap(); + + Ok(DescriptorSetAlloc { + inner, + pool, + handle: AllocationHandle::null(), + }) + } + + unsafe fn deallocate(&self, _allocation: DescriptorSetAlloc) {} +} + +unsafe impl DeviceOwned for GlobalDescriptorSetAllocator { + fn device(&self) -> &Arc { + &self.device + } +} + +pub(crate) struct LocalDescriptorSet { + inner: RawDescriptorSet, +} + +impl LocalDescriptorSet { + // FIXME: allocation + pub(crate) unsafe fn new( + resources: &Arc, + layout: &Arc, + framebuffer: &Framebuffer, + subpass_index: usize, + ) -> Result, VulkanError> { + let allocator = resources.descriptor_set_allocator().clone(); + let inner = RawDescriptorSet::new(allocator, layout, 0).map_err(Validated::unwrap)?; + + let render_pass = framebuffer.render_pass(); + let subpass_description = &render_pass.subpasses()[subpass_index]; + let input_attachments = &subpass_description.input_attachments; + let mut writes = Vec::new(); + + for (input_attachment_index, attachment_reference) in input_attachments.iter().enumerate() { + let Some(attachment_reference) = attachment_reference else { + continue; + }; + let attachment = &framebuffer.attachments()[attachment_reference.attachment as usize]; + + writes.push(WriteDescriptorSet::image_view_with_layout_array( + INPUT_ATTACHMENT_BINDING, + input_attachment_index as u32, + iter::once(DescriptorImageViewInfo { + image_view: attachment.clone(), + image_layout: attachment_reference.layout, + }), + )); + } + + unsafe { inner.update_unchecked(&writes, &[]) }; + + Ok(Arc::new(LocalDescriptorSet { inner })) + } + + fn create_layout( + resources: &Arc, + create_info: &LocalDescriptorSetCreateInfo<'_>, + ) -> Result, Validated> { + let device = resources.device(); + + let layout = DescriptorSetLayout::new( + device.clone(), + DescriptorSetLayoutCreateInfo { + bindings: BTreeMap::from([( + INPUT_ATTACHMENT_BINDING, + DescriptorSetLayoutBinding { + binding_flags: DescriptorBindingFlags::PARTIALLY_BOUND, + descriptor_count: create_info.max_input_attachments, + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::new(DescriptorType::InputAttachment) + }, + )]), + ..Default::default() + }, + )?; + + Ok(layout) + } + + pub(crate) fn as_raw(&self) -> &RawDescriptorSet { + &self.inner + } +} + +/// Parameters to create a local descriptor set. +#[derive(Clone, Debug)] +pub struct LocalDescriptorSetCreateInfo<'a> { + /// The maximum number of input attachment descriptors that the collection can hold at once. + /// + /// This also determines the maximum input attachment index: `max_input_attachments - 1`. + /// + /// The default value is `8`. + pub max_input_attachments: u32, + + pub _ne: crate::NonExhaustive<'a>, +} + +impl Default for LocalDescriptorSetCreateInfo<'_> { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl LocalDescriptorSetCreateInfo<'_> { + /// Returns a default `LocalDescriptorSetCreateInfo`. + #[inline] + pub const fn new() -> Self { + Self { + max_input_attachments: 8, + _ne: crate::NE, + } + } +} + +fn get_all_supported_shader_stages(device: &Arc) -> ShaderStages { + let mut stages = ShaderStages::all_graphics() | ShaderStages::COMPUTE; + + if device.enabled_extensions().khr_ray_tracing_pipeline + || device.enabled_extensions().nv_ray_tracing + { + stages |= ShaderStages::RAYGEN + | ShaderStages::ANY_HIT + | ShaderStages::CLOSEST_HIT + | ShaderStages::MISS + | ShaderStages::INTERSECTION + | ShaderStages::CALLABLE; + } + + if device.enabled_extensions().ext_mesh_shader || device.enabled_extensions().nv_mesh_shader { + stages |= ShaderStages::TASK | ShaderStages::MESH; + } + + if device.enabled_extensions().huawei_subpass_shading { + stages |= ShaderStages::SUBPASS_SHADING; + } + + stages +} diff --git a/vulkano-taskgraph/src/graph/compile.rs b/vulkano-taskgraph/src/graph/compile.rs index 7d75ab869c..13560c1e41 100644 --- a/vulkano-taskgraph/src/graph/compile.rs +++ b/vulkano-taskgraph/src/graph/compile.rs @@ -2111,7 +2111,7 @@ mod tests { .buffer_access(buffer, AccessTypes::RAY_TRACING_SHADER_STORAGE_READ) .image_access( image, - AccessTypes::RAY_TRACING_SHADER_COLOR_INPUT_ATTACHMENT_READ, + AccessTypes::RAY_TRACING_SHADER_SAMPLED_READ, ImageLayoutType::General, ) .build(); @@ -2144,7 +2144,7 @@ mod tests { src_stage_mask: FRAGMENT_SHADER, src_access_mask: SHADER_STORAGE_WRITE, dst_stage_mask: FRAGMENT_SHADER | RAY_TRACING_SHADER, - dst_access_mask: INPUT_ATTACHMENT_READ, + dst_access_mask: INPUT_ATTACHMENT_READ | SHADER_SAMPLED_READ, old_layout: General, new_layout: General, resource: image, diff --git a/vulkano-taskgraph/src/graph/execute.rs b/vulkano-taskgraph/src/graph/execute.rs index e4870e6b5c..10058081b9 100644 --- a/vulkano-taskgraph/src/graph/execute.rs +++ b/vulkano-taskgraph/src/graph/execute.rs @@ -3,7 +3,7 @@ use super::{ RenderPassIndex, ResourceAccess, SemaphoreIndex, }; use crate::{ - command_buffer::RecordingCommandBuffer, + command_buffer::{CommandBufferState, RecordingCommandBuffer}, linear_map::LinearMap, resource::{ BufferAccess, BufferState, DeathRow, ImageAccess, ImageState, Resources, SwapchainState, @@ -167,9 +167,11 @@ impl ExecutableTaskGraph { // SAFETY: We checked that `resource_map` maps the virtual IDs exhaustively. unsafe { self.update_resource_state(&resource_map, &self.last_accesses) }; - resource_map - .physical_resources - .try_advance_global_and_collect(&resource_map.guard); + if resource_map.guard.try_advance_global() { + resource_map + .physical_resources + .try_collect(&resource_map.guard); + } res } @@ -376,7 +378,7 @@ impl ExecutableTaskGraph { state.begin_render_pass(render_pass_index)?; } Instruction::NextSubpass => { - state.next_subpass(); + state.next_subpass()?; } Instruction::EndRenderPass => { state.end_render_pass(); @@ -479,7 +481,7 @@ impl ExecutableTaskGraph { state.begin_render_pass(render_pass_index)?; } Instruction::NextSubpass => { - state.next_subpass(); + state.next_subpass()?; } Instruction::EndRenderPass => { state.end_render_pass(); @@ -813,9 +815,13 @@ struct ExecuteState2<'a, W: ?Sized + 'static> { per_submits: SmallVec<[PerSubmitInfo2; 4]>, current_per_submit: PerSubmitInfo2, current_command_buffer: Option, + command_buffer_state: CommandBufferState, command_buffers: Vec>, current_buffer_barriers_vk: Vec>, current_image_barriers_vk: Vec>, + current_render_pass_index: RenderPassIndex, + current_framebuffer_index: usize, + current_subpass_index: usize, clear_values: LinearMap>, clear_values_vk: Vec, clear_attachments_vk: Vec, @@ -862,9 +868,13 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> { per_submits: SmallVec::new(), current_per_submit: PerSubmitInfo2::default(), current_command_buffer: None, + command_buffer_state: CommandBufferState::default(), command_buffers: Vec::new(), current_buffer_barriers_vk: Vec::new(), current_image_barriers_vk: Vec::new(), + current_render_pass_index: 0, + current_framebuffer_index: 0, + current_subpass_index: 0, clear_values: LinearMap::new(), clear_values_vk: Vec::new(), clear_attachments_vk: Vec::new(), @@ -1039,6 +1049,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> { let mut current_command_buffer = unsafe { RecordingCommandBuffer::new( current_command_buffer!(self), + &mut self.command_buffer_state, self.resource_map, self.death_row, ) @@ -1144,8 +1155,22 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> { } let render_pass_state = &self.executable.render_passes.borrow()[render_pass_index]; - let framebuffer = &render_pass_state.framebuffers - [unsafe { framebuffer_index(self.resource_map, &self.executable.swapchains) }]; + let framebuffer_index = + unsafe { framebuffer_index(self.resource_map, &self.executable.swapchains) }; + let framebuffer = &render_pass_state.framebuffers[framebuffer_index]; + + self.current_render_pass_index = render_pass_index; + self.current_framebuffer_index = framebuffer_index; + self.current_subpass_index = 0; + + unsafe { + self.command_buffer_state.set_local_set( + self.resource_map, + self.death_row, + framebuffer, + 0, + ) + }?; // FIXME: let mut render_area_vk = vk::Rect2D::default(); @@ -1181,7 +1206,22 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> { Ok(()) } - fn next_subpass(&mut self) { + fn next_subpass(&mut self) -> Result { + self.current_subpass_index += 1; + + let render_pass_state = + &self.executable.render_passes.borrow()[self.current_render_pass_index]; + let framebuffer = &render_pass_state.framebuffers[self.current_framebuffer_index]; + + unsafe { + self.command_buffer_state.set_local_set( + self.resource_map, + self.death_row, + framebuffer, + self.current_subpass_index, + ) + }?; + let fns = self.executable.device().fns(); unsafe { (fns.v1_0.cmd_next_subpass)( @@ -1189,9 +1229,13 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> { vk::SubpassContents::INLINE, ) }; + + Ok(()) } fn end_render_pass(&mut self) { + self.command_buffer_state.reset_local_set(); + let fns = self.executable.device().fns(); unsafe { (fns.v1_0.cmd_end_render_pass)(self.current_command_buffer.as_ref().unwrap().handle()) @@ -1348,6 +1392,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> { .command_buffer_infos_vk .push(vk::CommandBufferSubmitInfo::default().command_buffer(command_buffer.handle())); self.death_row.push(Arc::new(command_buffer)); + self.command_buffer_state.reset(); Ok(()) } @@ -1366,11 +1411,15 @@ struct ExecuteState<'a, W: ?Sized + 'static> { per_submits: SmallVec<[PerSubmitInfo; 4]>, current_per_submit: PerSubmitInfo, current_command_buffer: Option, + command_buffer_state: CommandBufferState, command_buffers: Vec>, current_buffer_barriers_vk: Vec>, current_image_barriers_vk: Vec>, current_src_stage_mask_vk: vk::PipelineStageFlags, current_dst_stage_mask_vk: vk::PipelineStageFlags, + current_render_pass_index: RenderPassIndex, + current_framebuffer_index: usize, + current_subpass_index: usize, clear_values: LinearMap>, clear_values_vk: Vec, clear_attachments_vk: Vec, @@ -1411,11 +1460,15 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> { per_submits: SmallVec::new(), current_per_submit: PerSubmitInfo::default(), current_command_buffer: None, + command_buffer_state: CommandBufferState::default(), command_buffers: Vec::new(), current_buffer_barriers_vk: Vec::new(), current_image_barriers_vk: Vec::new(), current_src_stage_mask_vk: vk::PipelineStageFlags::empty(), current_dst_stage_mask_vk: vk::PipelineStageFlags::empty(), + current_render_pass_index: 0, + current_framebuffer_index: 0, + current_subpass_index: 0, clear_values: LinearMap::new(), clear_values_vk: Vec::new(), clear_attachments_vk: Vec::new(), @@ -1595,6 +1648,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> { let mut current_command_buffer = unsafe { RecordingCommandBuffer::new( current_command_buffer!(self), + &mut self.command_buffer_state, self.resource_map, self.death_row, ) @@ -1703,8 +1757,22 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> { } let render_pass_state = &self.executable.render_passes.borrow()[render_pass_index]; - let framebuffer = &render_pass_state.framebuffers - [unsafe { framebuffer_index(self.resource_map, &self.executable.swapchains) }]; + let framebuffer_index = + unsafe { framebuffer_index(self.resource_map, &self.executable.swapchains) }; + let framebuffer = &render_pass_state.framebuffers[framebuffer_index]; + + self.current_render_pass_index = render_pass_index; + self.current_framebuffer_index = framebuffer_index; + self.current_subpass_index = 0; + + unsafe { + self.command_buffer_state.set_local_set( + self.resource_map, + self.death_row, + framebuffer, + 0, + ) + }?; // FIXME: let mut render_area_vk = vk::Rect2D::default(); @@ -1740,7 +1808,22 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> { Ok(()) } - fn next_subpass(&mut self) { + fn next_subpass(&mut self) -> Result { + self.current_subpass_index += 1; + + let render_pass_state = + &self.executable.render_passes.borrow()[self.current_render_pass_index]; + let framebuffer = &render_pass_state.framebuffers[self.current_framebuffer_index]; + + unsafe { + self.command_buffer_state.set_local_set( + self.resource_map, + self.death_row, + framebuffer, + self.current_subpass_index, + ) + }?; + let fns = self.executable.device().fns(); unsafe { (fns.v1_0.cmd_next_subpass)( @@ -1748,9 +1831,13 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> { vk::SubpassContents::INLINE, ) }; + + Ok(()) } fn end_render_pass(&mut self) { + self.command_buffer_state.reset_local_set(); + let fns = self.executable.device().fns(); unsafe { (fns.v1_0.cmd_end_render_pass)(self.current_command_buffer.as_ref().unwrap().handle()) @@ -1919,6 +2006,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> { .command_buffers_vk .push(command_buffer.handle()); self.death_row.push(Arc::new(command_buffer)); + self.command_buffer_state.reset(); Ok(()) } diff --git a/vulkano-taskgraph/src/lib.rs b/vulkano-taskgraph/src/lib.rs index a452bc25b6..e2539436da 100644 --- a/vulkano-taskgraph/src/lib.rs +++ b/vulkano-taskgraph/src/lib.rs @@ -34,6 +34,7 @@ use vulkano::{ }; pub mod command_buffer; +pub mod descriptor_set; pub mod graph; mod linear_map; pub mod resource; @@ -958,7 +959,7 @@ mod tests { }; ( - $crate::resource::Resources::new(&device, &Default::default()), + $crate::resource::Resources::new(&device, &Default::default()).unwrap(), queues.collect::>(), ) }}; diff --git a/vulkano-taskgraph/src/resource.rs b/vulkano-taskgraph/src/resource.rs index a78c609b9a..56a5b90326 100644 --- a/vulkano-taskgraph/src/resource.rs +++ b/vulkano-taskgraph/src/resource.rs @@ -1,6 +1,9 @@ //! Synchronization state tracking of all resources. -use crate::{Id, InvalidSlotError, Object, Ref}; +use crate::{ + descriptor_set::{BindlessContext, BindlessContextCreateInfo}, + Id, InvalidSlotError, Object, Ref, +}; use ash::vk; use concurrent_slotmap::{epoch, SlotMap}; use parking_lot::{Mutex, RwLock}; @@ -20,6 +23,7 @@ use thread_local::ThreadLocal; use vulkano::{ buffer::{AllocateBufferError, Buffer, BufferCreateInfo}, command_buffer::allocator::StandardCommandBufferAllocator, + descriptor_set::allocator::StandardDescriptorSetAllocator, device::{Device, DeviceOwned}, image::{AllocateImageError, Image, ImageCreateInfo, ImageLayout, ImageMemory}, memory::allocator::{AllocationCreateInfo, DeviceLayout, StandardMemoryAllocator}, @@ -42,9 +46,18 @@ static REGISTERED_DEVICES: Mutex> = Mutex::new(Vec::new()); // FIXME: Custom collector #[derive(Debug)] pub struct Resources { + // DO NOT change the order of these fields! `ResourceStorage` must be dropped first because + // that guarantees that all flights are waited on before the descriptor set is destroyed. + storage: Arc, + bindless_context: Option, +} + +#[derive(Debug)] +pub(crate) struct ResourceStorage { device: Arc, memory_allocator: Arc, command_buffer_allocator: Arc, + descriptor_set_allocator: Arc, global: epoch::GlobalHandle, locals: ThreadLocal, @@ -121,8 +134,10 @@ impl Resources { /// # Panics /// /// - Panics if `device` already has a `Resources` collection associated with it. - #[must_use] - pub fn new(device: &Arc, create_info: &ResourcesCreateInfo<'_>) -> Arc { + pub fn new( + device: &Arc, + create_info: &ResourcesCreateInfo<'_>, + ) -> Result, Validated> { let mut registered_devices = REGISTERED_DEVICES.lock(); let device_addr = Arc::as_ptr(device) as usize; @@ -132,33 +147,64 @@ impl Resources { ); registered_devices.push(device_addr); + drop(registered_devices); let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( device.clone(), Default::default(), )); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); let global = epoch::GlobalHandle::new(); - - Arc::new(Resources { + let storage = Arc::new(ResourceStorage { device: device.clone(), memory_allocator, command_buffer_allocator, + descriptor_set_allocator, locals: ThreadLocal::new(), buffers: SlotMap::with_global(create_info.max_buffers, global.clone()), images: SlotMap::with_global(create_info.max_images, global.clone()), swapchains: SlotMap::with_global(create_info.max_swapchains, global.clone()), flights: SlotMap::with_global(create_info.max_flights, global.clone()), global, - }) + }); + let bindless_context = create_info + .bindless_context + .map(|bindless_info| BindlessContext::new(&storage, bindless_info)) + .transpose()?; + + Ok(Arc::new(Resources { + storage, + bindless_context, + })) } /// Returns the standard memory allocator. #[inline] #[must_use] pub fn memory_allocator(&self) -> &Arc { - &self.memory_allocator + &self.storage.memory_allocator + } + + pub(crate) fn command_buffer_allocator(&self) -> &Arc { + &self.storage.command_buffer_allocator + } + + pub(crate) fn descriptor_set_allocator(&self) -> &Arc { + &self.storage.descriptor_set_allocator + } + + /// Returns the `BindlessContext`. + /// + /// Returns `None` if [`ResourcesCreateInfo::bindless_context`] was not specified when creating + /// the collection. + #[inline] + pub fn bindless_context(&self) -> Option<&BindlessContext> { + self.bindless_context.as_ref() } /// Creates a new buffer and adds it to the collection. @@ -177,7 +223,7 @@ impl Resources { layout: DeviceLayout, ) -> Result, Validated> { let buffer = Buffer::new( - self.memory_allocator.clone(), + self.storage.memory_allocator.clone(), create_info, allocation_info, layout, @@ -197,7 +243,11 @@ impl Resources { create_info: ImageCreateInfo, allocation_info: AllocationCreateInfo, ) -> Result, Validated> { - let image = Image::new(self.memory_allocator.clone(), create_info, allocation_info)?; + let image = Image::new( + self.storage.memory_allocator.clone(), + create_info, + allocation_info, + )?; // SAFETY: We just created the image. Ok(unsafe { self.add_image_unchecked(image) }) @@ -274,8 +324,9 @@ impl Resources { }; let slot = self + .storage .flights - .insert_with_tag(flight, Flight::TAG, self.pin()); + .insert_with_tag(flight, Flight::TAG, self.storage.pin()); Ok(unsafe { Id::new(slot) }) } @@ -300,7 +351,10 @@ impl Resources { last_access: Mutex::new(BufferAccess::NONE), }; - let slot = self.buffers.insert_with_tag(state, Buffer::TAG, self.pin()); + let slot = self + .storage + .buffers + .insert_with_tag(state, Buffer::TAG, self.storage.pin()); unsafe { Id::new(slot) } } @@ -332,7 +386,10 @@ impl Resources { last_access: Mutex::new(ImageAccess::NONE), }; - let slot = self.images.insert_with_tag(state, Image::TAG, self.pin()); + let slot = self + .storage + .images + .insert_with_tag(state, Image::TAG, self.storage.pin()); unsafe { Id::new(slot) } } @@ -416,9 +473,9 @@ impl Resources { swapchain: Arc, images: Vec>, ) -> Result, VulkanError> { - let guard = &self.pin(); + let guard = &self.storage.pin(); - let frames_in_flight = unsafe { self.flight_unprotected(flight_id) } + let frames_in_flight = unsafe { self.storage.flight_unprotected(flight_id) } .unwrap() .frame_count(); @@ -451,6 +508,7 @@ impl Resources { }; let slot = self + .storage .swapchains .insert_with_tag(state, Swapchain::TAG, guard); @@ -477,12 +535,12 @@ impl Resources { id: Id, f: impl FnOnce(SwapchainCreateInfo) -> SwapchainCreateInfo, ) -> Result, Validated> { - let guard = self.pin(); + let guard = self.storage.pin(); - let state = unsafe { self.swapchain_unprotected(id) }.unwrap(); + let state = unsafe { self.storage.swapchain_unprotected(id) }.unwrap(); let swapchain = state.swapchain(); let flight_id = state.flight_id; - let flight = unsafe { self.flight_unprotected_unchecked(flight_id) }; + let flight = unsafe { self.storage.flight_unprotected_unchecked(flight_id) }; let mut flight_state = flight.state.try_lock().unwrap(); let (new_swapchain, new_images) = swapchain.recreate(f(swapchain.create_info()))?; @@ -504,6 +562,7 @@ impl Resources { }; let slot = self + .storage .swapchains .insert_with_tag(new_state, Swapchain::TAG, guard); @@ -520,8 +579,9 @@ impl Resources { /// pending command buffer, and if it is used in any command buffer that's in the executable /// or recording state, that command buffer must never be executed. pub unsafe fn remove_buffer(&self, id: Id) -> Result> { - self.buffers - .remove(id.slot, self.pin()) + self.storage + .buffers + .remove(id.slot, self.storage.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } @@ -534,8 +594,9 @@ impl Resources { /// command buffer, and if it is used in any command buffer that's in the executable or /// recording state, that command buffer must never be executed. pub unsafe fn remove_image(&self, id: Id) -> Result> { - self.images - .remove(id.slot, self.pin()) + self.storage + .images + .remove(id.slot, self.storage.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } @@ -548,8 +609,9 @@ impl Resources { /// pending command buffer, and if it is used in any command buffer that's in the executable /// or recording state, that command buffer must never be executed. pub unsafe fn remove_swapchain(&self, id: Id) -> Result> { - self.swapchains - .remove(id.slot, self.pin()) + self.storage + .swapchains + .remove(id.slot, self.storage.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } @@ -557,19 +619,109 @@ impl Resources { /// Returns the buffer corresponding to `id`. #[inline] pub fn buffer(&self, id: Id) -> Result> { + self.storage.buffer(id) + } + + pub(crate) unsafe fn buffer_unprotected(&self, id: Id) -> Result<&BufferState> { + // SAFETY: Enforced by the caller. + unsafe { self.storage.buffer_unprotected(id) } + } + + pub(crate) unsafe fn buffer_unchecked_unprotected(&self, id: Id) -> &BufferState { + // SAFETY: Enforced by the caller. + unsafe { self.storage.buffer_unchecked_unprotected(id) } + } + + /// Returns the image corresponding to `id`. + #[inline] + pub fn image(&self, id: Id) -> Result> { + self.storage.image(id) + } + + pub(crate) unsafe fn image_unprotected(&self, id: Id) -> Result<&ImageState> { + // SAFETY: Enforced by the caller. + unsafe { self.storage.image_unprotected(id) } + } + + pub(crate) unsafe fn image_unchecked_unprotected(&self, id: Id) -> &ImageState { + // SAFETY: Enforced by the caller. + unsafe { self.storage.image_unchecked_unprotected(id) } + } + + /// Returns the swapchain corresponding to `id`. + #[inline] + pub fn swapchain(&self, id: Id) -> Result> { + self.storage.swapchain(id) + } + + pub(crate) unsafe fn swapchain_unprotected( + &self, + id: Id, + ) -> Result<&SwapchainState> { + // SAFETY: Enforced by the caller. + unsafe { self.storage.swapchain_unprotected(id) } + } + + pub(crate) unsafe fn swapchain_unchecked_unprotected( + &self, + id: Id, + ) -> &SwapchainState { + // SAFETY: Enforced by the caller. + unsafe { self.storage.swapchain_unchecked_unprotected(id) } + } + + /// Returns the [flight] corresponding to `id`. + #[inline] + pub fn flight(&self, id: Id) -> Result> { + self.storage.flight(id) + } + + pub(crate) unsafe fn flight_unprotected(&self, id: Id) -> Result<&Flight> { + // SAFETY: Enforced by the caller. + unsafe { self.storage.flight_unprotected(id) } + } + + pub(crate) fn pin(&self) -> epoch::Guard<'_> { + self.storage.pin() + } + + pub(crate) fn try_collect(&self, guard: &epoch::Guard<'_>) { + self.storage.try_collect(guard); + + if let Some(bindless_context) = &self.bindless_context { + bindless_context.global_set().try_collect(guard); + } + } +} + +unsafe impl DeviceOwned for Resources { + #[inline] + fn device(&self) -> &Arc { + &self.storage.device + } +} + +impl ResourceStorage { + pub(crate) fn global(&self) -> &epoch::GlobalHandle { + &self.global + } + + pub(crate) fn pin(&self) -> epoch::Guard<'_> { + self.locals.get_or(|| self.global.register_local()).pin() + } + + pub(crate) fn buffer(&self, id: Id) -> Result> { self.buffers .get(id.slot, self.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn buffer_unprotected(&self, id: Id) -> Result<&BufferState> { // SAFETY: Enforced by the caller. unsafe { self.buffers.get_unprotected(id.slot) }.ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn buffer_unchecked_unprotected(&self, id: Id) -> &BufferState { #[cfg(debug_assertions)] if unsafe { self.buffers.get_unprotected(id.slot) }.is_none() { @@ -580,22 +732,18 @@ impl Resources { unsafe { self.buffers.index_unchecked_unprotected(id.index()) } } - /// Returns the image corresponding to `id`. - #[inline] - pub fn image(&self, id: Id) -> Result> { + pub(crate) fn image(&self, id: Id) -> Result> { self.images .get(id.slot, self.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn image_unprotected(&self, id: Id) -> Result<&ImageState> { // SAFETY: Enforced by the caller. unsafe { self.images.get_unprotected(id.slot) }.ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn image_unchecked_unprotected(&self, id: Id) -> &ImageState { #[cfg(debug_assertions)] if unsafe { self.images.get_unprotected(id.slot) }.is_none() { @@ -606,16 +754,13 @@ impl Resources { unsafe { self.images.index_unchecked_unprotected(id.index()) } } - /// Returns the swapchain corresponding to `id`. - #[inline] - pub fn swapchain(&self, id: Id) -> Result> { + pub(crate) fn swapchain(&self, id: Id) -> Result> { self.swapchains .get(id.slot, self.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn swapchain_unprotected( &self, id: Id, @@ -624,7 +769,6 @@ impl Resources { unsafe { self.swapchains.get_unprotected(id.slot) }.ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn swapchain_unchecked_unprotected( &self, id: Id, @@ -638,47 +782,38 @@ impl Resources { unsafe { self.swapchains.index_unchecked_unprotected(id.index()) } } - /// Returns the [flight] corresponding to `id`. - #[inline] - pub fn flight(&self, id: Id) -> Result> { + pub(crate) fn flight(&self, id: Id) -> Result> { self.flights .get(id.slot, self.pin()) .map(Ref) .ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn flight_unprotected(&self, id: Id) -> Result<&Flight> { // SAFETY: Enforced by the caller. unsafe { self.flights.get_unprotected(id.slot) }.ok_or(InvalidSlotError::new(id)) } - #[inline] pub(crate) unsafe fn flight_unprotected_unchecked(&self, id: Id) -> &Flight { // SAFETY: Enforced by the caller. unsafe { self.flights.index_unchecked_unprotected(id.slot.index()) } } - #[inline] - pub(crate) fn pin(&self) -> epoch::Guard<'_> { - self.locals.get_or(|| self.global.register_local()).pin() - } - - pub(crate) fn command_buffer_allocator(&self) -> &Arc { - &self.command_buffer_allocator + pub(crate) fn try_collect(&self, guard: &epoch::Guard<'_>) { + self.buffers.try_collect(guard); + self.images.try_collect(guard); + self.swapchains.try_collect(guard); + self.flights.try_collect(guard); } +} - pub(crate) fn try_advance_global_and_collect(&self, guard: &epoch::Guard<'_>) { - if guard.try_advance_global() { - self.buffers.try_collect(guard); - self.images.try_collect(guard); - self.swapchains.try_collect(guard); - self.flights.try_collect(guard); - } +unsafe impl DeviceOwned for ResourceStorage { + fn device(&self) -> &Arc { + &self.device } } -impl Drop for Resources { +impl Drop for ResourceStorage { fn drop(&mut self) { for (flight_id, flight) in &mut self.flights { let prev_frame_index = flight.previous_frame_index(); @@ -713,13 +848,6 @@ impl Drop for Resources { } } -unsafe impl DeviceOwned for Resources { - #[inline] - fn device(&self) -> &Arc { - &self.device - } -} - impl BufferState { /// Returns the buffer. #[inline] @@ -1071,6 +1199,15 @@ pub struct ResourcesCreateInfo<'a> { /// The default value is `256` (28). pub max_flights: u32, + /// Parameters to create a new [`BindlessContext`]. + /// + /// If set, the device extensions given by [`BindlessContext::required_extensions`] and the + /// device features given by [`BindlessContext::required_features`] must be enabled on the + /// device. + /// + /// The default value is `None`. + pub bindless_context: Option<&'a BindlessContextCreateInfo<'a>>, + pub _ne: crate::NonExhaustive<'a>, } @@ -1090,6 +1227,7 @@ impl ResourcesCreateInfo<'_> { max_images: 1 << 24, max_swapchains: 1 << 8, max_flights: 1 << 8, + bindless_context: None, _ne: crate::NE, } } @@ -1490,18 +1628,6 @@ access_types! { image_layout: Undefined, } - RAY_TRACING_SHADER_COLOR_INPUT_ATTACHMENT_READ { - stage_mask: RAY_TRACING_SHADER, - access_mask: INPUT_ATTACHMENT_READ, - image_layout: ShaderReadOnlyOptimal, - } - - RAY_TRACING_SHADER_DEPTH_STENCIL_INPUT_ATTACHMENT_READ { - stage_mask: RAY_TRACING_SHADER, - access_mask: INPUT_ATTACHMENT_READ, - image_layout: DepthStencilReadOnlyOptimal, - } - RAY_TRACING_SHADER_SAMPLED_READ { stage_mask: RAY_TRACING_SHADER, access_mask: SHADER_SAMPLED_READ,