hack in the vulkano raytracing example

This commit is contained in:
2025-12-14 00:50:54 +00:00
parent e8d74385c7
commit 1aca901fe1
8 changed files with 785 additions and 151 deletions
+4
View File
@@ -7,6 +7,10 @@ glslangValidator --spirv-val -gVS -S vert --target-env vulkan1.3 -o shaders_out/
glslangValidator --spirv-val -gVS -S frag --target-env vulkan1.3 -o shaders_out/lighting.frag.spv src/shaders/lighting.frag.glsl
glslangValidator --spirv-val -gVS -S vert --target-env vulkan1.3 -o shaders_out/trace.vert.spv src/shaders/trace.vert.glsl
glslangValidator --spirv-val -gVS -S rgen --target-env vulkan1.3 -o shaders_out/rgen.spv src/shaders/rgen.glsl
glslangValidator --spirv-val -gVS -S rchit --target-env vulkan1.3 -o shaders_out/rchit.spv src/shaders/rchit.glsl
glslangValidator --spirv-val -gVS -S rmiss --target-env vulkan1.3 -o shaders_out/rmiss.spv src/shaders/rmiss.glsl
echo src/shaders/trace.frag.glsl
glslangValidator --spirv-val --spirv-dis -g0 -S frag --target-env vulkan1.3 --quiet src/shaders/trace.frag.glsl > shaders_out/trace.spv-dis
sed -i '/%11 =/,/OpFunctionEnd/d' shaders_out/trace.spv-dis
+377 -85
View File
@@ -12,6 +12,9 @@ const DEFAULT_SUBDIVISION: u32 = 16;
const MAXIMUM_SUBDIVISION: u32 = 64;
const MINUMUM_SUBDIVISION: u32 = 8;
const RUN_STANDARD_PIPELINE: bool = false;
const RUN_RT_PIPELINE: bool = true;
use std::{
error::Error,
fs::{File, remove_file, rename},
@@ -50,15 +53,15 @@ use vulkano::{
},
format::{ClearValue, Format, FormatFeatures},
image::{
Image, ImageAspects, ImageCreateInfo, ImageSubresourceRange, ImageType, ImageUsage,
SampleCount,
Image, ImageAspects, ImageCreateInfo, ImageFormatInfo, ImageSubresourceRange, ImageType,
ImageUsage, SampleCount,
view::{ImageView, ImageViewCreateInfo},
},
instance::{Instance, InstanceCreateInfo, InstanceExtensions},
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
pipeline::{
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineCreateFlags,
PipelineLayout, PipelineShaderStageCreateInfo,
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
PipelineShaderStageCreateInfo,
cache::{PipelineCache, PipelineCacheCreateInfo},
graphics::{
GraphicsPipelineCreateInfo,
@@ -70,6 +73,10 @@ use vulkano::{
viewport::Viewport,
},
layout::PipelineDescriptorSetLayoutCreateInfo,
ray_tracing::{
RayTracingPipeline, RayTracingPipelineCreateInfo, RayTracingShaderGroupCreateInfo,
ShaderBindingTable,
},
},
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::{ShaderModule, ShaderModuleCreateInfo, SpecializationConstant},
@@ -86,7 +93,6 @@ use vulkano::{
use winit::platform::x11::EventLoopBuilderExtX11;
use winit::{
application::ApplicationHandler,
dpi::PhysicalSize,
event::{DeviceEvent, ElementState, MouseButton, WindowEvent},
event_loop::{ActiveEventLoop, EventLoop},
keyboard::{KeyCode, PhysicalKey},
@@ -96,6 +102,7 @@ use winit::{
mod gui;
use crate::{
gui::*,
scene::Scene,
threads::{WorkComplete, WorkItem},
trace_vs::{Camera, Lights, PushConstantData},
};
@@ -113,6 +120,8 @@ mod vm;
mod threads;
mod scene;
fn main() -> Result<(), impl Error> {
CombinedLogger::init(vec![
TermLogger::new(
@@ -228,13 +237,16 @@ struct ShaderModules {
trace_vs: Arc<ShaderModule>,
fullscreen_vs: Arc<ShaderModule>,
lighting_fs: Arc<ShaderModule>,
raygen: Arc<ShaderModule>,
closest_hit: Arc<ShaderModule>,
miss: Arc<ShaderModule>,
}
struct RenderContext {
window: Arc<Window>,
swapchain: Arc<Swapchain>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
framebuffers: Vec<(Arc<ImageView>, Arc<Framebuffer>)>,
color_buffer: Arc<ImageView>,
material_buffer: Arc<ImageView>,
ao_r_m_buffer: Arc<ImageView>,
@@ -246,6 +258,9 @@ struct RenderContext {
lights_buffers_host_visible: Vec<Subbuffer<Lights>>,
shader_modules: ShaderModules,
lighting_pipeline: Arc<GraphicsPipeline>,
rt_pipeline: Arc<RayTracingPipeline>,
rt_shader_binding_table: ShaderBindingTable,
scene: Scene,
viewport: Viewport,
recreate_swapchain: bool,
recreate_pipelines: bool,
@@ -271,6 +286,7 @@ impl App {
library,
InstanceCreateInfo {
enabled_extensions: InstanceExtensions {
ext_swapchain_colorspace: true,
..required_extensions
},
engine_name: Some("Tape Drive".to_owned()),
@@ -284,19 +300,45 @@ impl App {
let device_extensions = DeviceExtensions {
khr_swapchain: true,
khr_ray_tracing_pipeline: true,
khr_ray_tracing_maintenance1: true,
khr_synchronization2: true,
khr_deferred_host_operations: true,
khr_acceleration_structure: true,
..DeviceExtensions::empty()
};
let device_features = DeviceFeatures {
shader_int16: true,
shader_int8: true,
storage_buffer8_bit_access: true,
storage_buffer16_bit_access: true,
maintenance4: true,
fill_mode_non_solid: true,
shader_draw_parameters: true,
shader_demote_to_helper_invocation: true,
acceleration_structure: true,
ray_tracing_pipeline: true,
buffer_device_address: true,
synchronization2: true,
..DeviceFeatures::empty()
};
let (physical_device, (graphics_queue_index, transfer_queue_index)) = instance
.enumerate_physical_devices()
.unwrap()
.filter(|p| p.supported_extensions().contains(&device_extensions))
.filter(|p| p.api_version() >= Version::V1_3)
.filter(|p| {
p.supported_extensions().contains(&device_extensions)
&& p.supported_features().contains(&device_features)
})
.filter_map(|p| {
p.queue_family_properties()
.iter()
.enumerate()
.position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::GRAPHICS)
q.queue_flags
.contains(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
&& p.presentation_support(i as u32, event_loop)
.unwrap_or(false)
})
@@ -305,11 +347,11 @@ impl App {
.iter()
.enumerate()
.position(|(i, q)| {
q.queue_flags.intersects(QueueFlags::TRANSFER) && i != graphics
q.queue_flags.contains(QueueFlags::TRANSFER) && i != graphics
})
.or_else(|| {
p.queue_family_properties().iter().enumerate().position(
|(_i, q)| q.queue_flags.intersects(QueueFlags::TRANSFER),
|(_i, q)| q.queue_flags.contains(QueueFlags::TRANSFER),
)
})
.map(|i| (graphics as u32, i as u32))
@@ -384,20 +426,7 @@ impl App {
..Default::default()
},
],
enabled_features: DeviceFeatures {
mesh_shader: false,
task_shader: false,
//shader_float16: true,
shader_int16: true,
shader_int8: true,
storage_buffer8_bit_access: true,
storage_buffer16_bit_access: true,
maintenance4: true,
fill_mode_non_solid: true,
shader_draw_parameters: true,
shader_demote_to_helper_invocation: true,
..DeviceFeatures::empty()
},
enabled_features: device_features,
..Default::default()
},
)
@@ -456,6 +485,7 @@ impl App {
&memory_allocator,
&mut Cursor::new(PLATONIC_SOLIDS[0].1),
PLATONIC_SOLIDS[0].0.to_string(),
BufferUsage::empty(),
)
.unwrap(),
);
@@ -618,6 +648,33 @@ mod lighting_fs {
}
}
mod raygen {
vulkano_shaders::shader! {
bytes: "shaders_out/rgen.spv",
vulkan_version: "1.3",
spirv_version: "1.6",
custom_derives: [Debug, Clone, Copy],
}
}
mod closest_hit {
vulkano_shaders::shader! {
bytes: "shaders_out/rchit.spv",
vulkan_version: "1.3",
spirv_version: "1.6",
custom_derives: [Debug, Clone, Copy],
}
}
mod miss {
vulkano_shaders::shader! {
bytes: "shaders_out/rmiss.spv",
vulkan_version: "1.3",
spirv_version: "1.6",
custom_derives: [Debug, Clone, Copy],
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = Arc::new(
@@ -663,11 +720,24 @@ impl ApplicationHandler for App {
)
.unwrap();
let (image_format, _) = self
let (image_format, image_color_space) = self
.device
.physical_device()
.surface_formats(&surface, Default::default())
.unwrap()[0];
.unwrap()
.into_iter()
.find(|(format, _)| {
self.device
.physical_device()
.image_format_properties(ImageFormatInfo {
format: *format,
usage: ImageUsage::STORAGE,
..Default::default()
})
.unwrap()
.is_some()
})
.unwrap();
Swapchain::new(
self.device.clone(),
@@ -678,9 +748,12 @@ impl ApplicationHandler for App {
.min(surface_capabilities.max_image_count.unwrap_or(u32::MAX)),
image_format,
image_color_space,
image_extent: window_size.into(),
image_usage: ImageUsage::COLOR_ATTACHMENT | ImageUsage::TRANSFER_DST,
image_usage: ImageUsage::COLOR_ATTACHMENT
| ImageUsage::TRANSFER_DST
| ImageUsage::STORAGE,
composite_alpha: surface_capabilities
.supported_composite_alpha
@@ -725,17 +798,40 @@ impl ApplicationHandler for App {
::std::sync::Arc<::vulkano::shader::ShaderModule>,
::vulkano::Validated<::vulkano::VulkanError>,
>),
((raygen::load)
as fn(
::std::sync::Arc<::vulkano::device::Device>,
) -> Result<
::std::sync::Arc<::vulkano::shader::ShaderModule>,
::vulkano::Validated<::vulkano::VulkanError>,
>),
((closest_hit::load)
as fn(
::std::sync::Arc<::vulkano::device::Device>,
) -> Result<
::std::sync::Arc<::vulkano::shader::ShaderModule>,
::vulkano::Validated<::vulkano::VulkanError>,
>),
((miss::load)
as fn(
::std::sync::Arc<::vulkano::device::Device>,
) -> Result<
::std::sync::Arc<::vulkano::shader::ShaderModule>,
::vulkano::Validated<::vulkano::VulkanError>,
>),
];
let pariter = loaders
let mut pariter = loaders
.iter()
.map(|load| load(self.device.clone()).unwrap())
.collect::<Vec<_>>();
.map(|load| load(self.device.clone()).unwrap());
let shader_modules = ShaderModules {
trace_vs: pariter[0].clone(),
fullscreen_vs: pariter[1].clone(),
lighting_fs: pariter[2].clone(),
trace_vs: pariter.next().unwrap(),
fullscreen_vs: pariter.next().unwrap(),
lighting_fs: pariter.next().unwrap(),
raygen: pariter.next().unwrap(),
closest_hit: pariter.next().unwrap(),
miss: pariter.next().unwrap(),
};
drop(pariter);
@@ -794,11 +890,6 @@ impl ApplicationHandler for App {
color: [final_color],
depth_stencil: {},
input: [color, normals, ao_r_m, depth, material],
},
{
color: [final_color],
depth_stencil: {},
input: [],
}]
)
.unwrap();
@@ -823,13 +914,21 @@ impl ApplicationHandler for App {
self.depth_format,
);
let lighting_pipeline = pipeline_recompile(
let (lighting_pipeline, rt_pipeline, rt_shader_binding_table) = pipeline_recompile(
&self.memory_allocator,
&render_pass,
&self.pipeline_cache,
&shader_modules,
&self.gstate.debug,
);
let scene = Scene::new(
&self.device,
&self.graphics_queue,
&self.memory_allocator,
&self.command_buffer_allocator,
);
let (
camera_buffers,
camera_buffers_host_visible,
@@ -856,11 +955,10 @@ impl ApplicationHandler for App {
let previous_frame_end = (0..swapchain.image_count()).map(|_| None).collect();
// Create an egui GUI
let gui = Gui::new_with_subpass(
let gui = Gui::new(
event_loop,
surface.clone(),
self.graphics_queue.clone(),
Subpass::from(render_pass.clone(), 3).unwrap(),
images[0].format(),
GuiConfig {
allow_srgb_render_target: true,
@@ -885,6 +983,9 @@ impl ApplicationHandler for App {
lights_buffers_host_visible,
shader_modules,
lighting_pipeline,
rt_pipeline,
rt_shader_binding_table,
scene,
viewport,
recreate_swapchain: false,
recreate_pipelines: false,
@@ -1073,6 +1174,8 @@ impl App {
let uniform_data = trace_vs::Camera {
proj_view: (proj * view).to_cols_array_2d(),
inv_proj_view: (proj * view).inverse().to_cols_array_2d(),
inv_proj: (proj).inverse().to_cols_array_2d(),
inv_view: (view).inverse().to_cols_array_2d(),
campos_and_time: [
self.cstate.position.x,
self.cstate.position.y,
@@ -1146,6 +1249,9 @@ impl App {
Option<Arc<DescriptorSet>>,
Arc<DescriptorSet>,
Arc<DescriptorSet>,
Arc<DescriptorSet>,
Arc<DescriptorSet>,
Arc<DescriptorSet>,
) {
let trace_set1 = if self.csg_count > 0 {
let trace_layout = &self.gstate.csg[0]
@@ -1243,12 +1349,51 @@ impl App {
)
.unwrap();
let rt_layout1 = &rcx.rt_pipeline.layout().set_layouts()[0];
let rt_set1 = DescriptorSet::new(
self.descriptor_set_allocator.clone(),
rt_layout1.clone(),
[WriteDescriptorSet::buffer(
1,
rcx.camera_buffers[index].clone(),
)],
[],
)
.unwrap();
let rt_layout2 = &rcx.rt_pipeline.layout().set_layouts()[1];
let rt_set2 = DescriptorSet::new(
self.descriptor_set_allocator.clone(),
rt_layout2.clone(),
[WriteDescriptorSet::acceleration_structure(
0,
rcx.scene.tlas.clone(),
)],
[],
)
.unwrap();
let rt_layout3 = &rcx.rt_pipeline.layout().set_layouts()[2];
let rt_set3 = DescriptorSet::new(
self.descriptor_set_allocator.clone(),
rt_layout3.clone(),
[WriteDescriptorSet::image_view(
0,
rcx.framebuffers[index].0.clone(),
)],
[],
)
.unwrap();
(
trace_set1,
normals_set1,
normals_set3,
lighting_set1,
lighting_set2,
rt_set1,
rt_set2,
rt_set3,
)
}
@@ -1267,21 +1412,6 @@ impl App {
.unwrap();
}
fn add_commands_secondary_subpass(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
) {
builder
.next_subpass(
Default::default(),
SubpassBeginInfo {
contents: SubpassContents::SecondaryCommandBuffers,
..Default::default()
},
)
.unwrap();
}
fn add_commands_depth_pass(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
@@ -1377,14 +1507,29 @@ impl App {
unsafe { builder.draw(3, 1, 0, 0) }.unwrap();
}
fn add_commands_gui(
fn add_commands_raytrace(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
rcx: &mut RenderContext,
window_size: PhysicalSize<u32>,
rt_set1: Arc<DescriptorSet>,
rt_set2: Arc<DescriptorSet>,
rt_set3: Arc<DescriptorSet>,
) {
let guicb = rcx.gui.draw_on_subpass_image(window_size.into());
builder.execute_commands(guicb).unwrap();
builder
.bind_descriptor_sets(
PipelineBindPoint::RayTracing,
rcx.rt_pipeline.layout().clone(),
0,
vec![rt_set1, rt_set2, rt_set3],
)
.unwrap()
.bind_pipeline_ray_tracing(rcx.rt_pipeline.clone())
.unwrap();
let extent = rcx.framebuffers[0].0.image().extent();
unsafe { builder.trace_rays(rcx.rt_shader_binding_table.addresses().clone(), extent) }
.unwrap();
}
fn handle_work_complete(&mut self, work: WorkComplete) {
@@ -1437,6 +1582,27 @@ impl App {
}
let lighting_frag = lighting_frag.unwrap();
let raygen = read_spirv_words_from_file("rgen.spv");
if raygen.is_err() {
error!("Could not read raygen file");
return;
}
let raygen = raygen.unwrap();
let closest_hit = read_spirv_words_from_file("rchit.spv");
if closest_hit.is_err() {
error!("Could not read closest hit file");
return;
}
let closest_hit = closest_hit.unwrap();
let miss = read_spirv_words_from_file("rmiss.spv");
if miss.is_err() {
error!("Could not read miss file");
return;
}
let miss = miss.unwrap();
let mut trace_loader = rspirv::dr::Loader::new();
rspirv::binary::parse_words(trace_frag, &mut trace_loader).unwrap();
@@ -1467,10 +1633,37 @@ impl App {
.unwrap()
};
let raygen = unsafe {
::vulkano::shader::ShaderModule::new(
self.device.clone(),
ShaderModuleCreateInfo::new(&raygen),
)
.unwrap()
};
let closest_hit = unsafe {
::vulkano::shader::ShaderModule::new(
self.device.clone(),
ShaderModuleCreateInfo::new(&closest_hit),
)
.unwrap()
};
let miss = unsafe {
::vulkano::shader::ShaderModule::new(
self.device.clone(),
ShaderModuleCreateInfo::new(&miss),
)
.unwrap()
};
rcx.shader_modules = ShaderModules {
trace_vs: trace_vert,
fullscreen_vs: fullscreen_vert,
lighting_fs: lighting_frag,
raygen,
closest_hit,
miss,
};
self.trace_module = trace_loader.module();
@@ -1500,7 +1693,12 @@ impl App {
))
.unwrap();
}
rcx.lighting_pipeline = pipeline_recompile(
(
rcx.lighting_pipeline,
rcx.rt_pipeline,
rcx.rt_shader_binding_table,
) = pipeline_recompile(
&self.memory_allocator,
&rcx.render_pass,
&self.pipeline_cache,
&rcx.shader_modules,
@@ -1715,8 +1913,16 @@ impl App {
.then_signal_semaphore_and_flush()
.unwrap();
let (trace_set, normals_set1, normals_set2, lighting_set1, lighting_set2) =
self.get_descriptor_sets(rcx, image_index);
let (
trace_set,
normals_set1,
normals_set2,
lighting_set1,
lighting_set2,
rt_set1,
rt_set2,
rt_set3,
) = self.get_descriptor_sets(rcx, image_index);
let mut builder = AutoCommandBufferBuilder::primary(
self.command_buffer_allocator.clone(),
@@ -1725,6 +1931,13 @@ impl App {
)
.unwrap();
let mut futures = camera_and_lights.boxed();
if RUN_RT_PIPELINE {
self.add_commands_raytrace(&mut builder, rcx, rt_set1, rt_set2, rt_set3);
}
if RUN_STANDARD_PIPELINE {
let depth_clear = if self.depth_format.numeric_format_stencil().is_some() {
Some((0.0, 0).into())
} else {
@@ -1742,7 +1955,7 @@ impl App {
Some([0.0, 0.0, 0.0, 1.0].into()),
depth_clear,
],
..RenderPassBeginInfo::framebuffer(rcx.framebuffers[image_index].clone())
..RenderPassBeginInfo::framebuffer(rcx.framebuffers[image_index].1.clone())
},
SubpassBeginInfo {
contents: SubpassContents::Inline,
@@ -1772,14 +1985,13 @@ impl App {
}
}
let futures = if let Some(f) = push_constant_future {
f.then_signal_semaphore_and_flush()
if let Some(f) = push_constant_future {
futures = f
.then_signal_semaphore_and_flush()
.unwrap()
.join(camera_and_lights)
.boxed()
} else {
camera_and_lights.boxed()
};
.join(futures)
.boxed();
}
if self.gstate.csg.len() > 0 {
self.add_commands_depth_pass(
@@ -1801,12 +2013,10 @@ impl App {
}
self.add_commands_primary_subpass(&mut builder);
self.add_commands_lighting_pass(&mut builder, rcx, lighting_set1, lighting_set2);
self.add_commands_secondary_subpass(&mut builder);
if self.draw_gui {
self.add_commands_gui(&mut builder, rcx, window_size);
}
builder.end_render_pass(Default::default()).unwrap();
}
let command_buffer = builder.build().unwrap();
for future in rcx.previous_frame_end.iter_mut().flatten() {
@@ -1821,7 +2031,16 @@ impl App {
.join(acquire_future)
.join(futures)
.then_execute(self.graphics_queue.clone(), command_buffer)
.unwrap()
.unwrap();
let future = if self.draw_gui {
rcx.gui
.draw_on_image(future, rcx.framebuffers[image_index].0.clone())
} else {
future.boxed()
};
let future = future
.then_swapchain_present(
self.graphics_queue.clone(),
SwapchainPresentInfo::swapchain_image_index(
@@ -1867,7 +2086,7 @@ fn framebuffer_generation(
allocator: &Arc<StandardMemoryAllocator>,
depth_format: Format,
) -> (
Vec<Arc<Framebuffer>>,
Vec<(Arc<ImageView>, Arc<Framebuffer>)>,
Arc<ImageView>,
Arc<ImageView>,
Arc<ImageView>,
@@ -1978,12 +2197,13 @@ fn framebuffer_generation(
let framebuffers = images
.iter()
.map(|image| {
let view = ImageView::new_default(image.clone()).unwrap();
Framebuffer::new(
let image_view = ImageView::new_default(image.clone()).unwrap();
let framebuffer = Framebuffer::new(
render_pass.clone(),
FramebufferCreateInfo {
attachments: vec![
view,
image_view.clone(),
material_buffer.clone(),
color_buffer.clone(),
ao_r_m_buffer.clone(),
@@ -1993,7 +2213,9 @@ fn framebuffer_generation(
..Default::default()
},
)
.unwrap()
.unwrap();
(image_view, framebuffer)
})
.collect::<Vec<_>>();
@@ -2066,11 +2288,16 @@ fn get_spec_constants(debug: &PreviousDebug) -> HashMap<u32, SpecializationConst
}
fn pipeline_recompile(
memory_allocator: &Arc<StandardMemoryAllocator>,
render_pass: &Arc<RenderPass>,
cache: &Arc<PipelineCache>,
shader_modules: &ShaderModules,
debug: &PreviousDebug,
) -> Arc<GraphicsPipeline> {
) -> (
Arc<GraphicsPipeline>,
Arc<RayTracingPipeline>,
ShaderBindingTable,
) {
let device = cache.device();
let specs = get_spec_constants(debug);
@@ -2115,11 +2342,11 @@ fn pipeline_recompile(
device.clone(),
Some(cache.clone()),
GraphicsPipelineCreateInfo {
flags: PipelineCreateFlags::DISABLE_OPTIMIZATION,
//flags: PipelineCreateFlags::DISABLE_OPTIMIZATION,
stages,
vertex_input_state: Some(vertex_input_state),
input_assembly_state: Some(InputAssemblyState::default()),
dynamic_state,
dynamic_state: dynamic_state.clone(),
viewport_state: Some(Default::default()),
rasterization_state: Some(RasterizationState {
front_face: FrontFace::Clockwise,
@@ -2140,9 +2367,74 @@ fn pipeline_recompile(
.unwrap()
};
let rt_pipeline = {
let raygen = shader_modules
.raygen
.specialize(specs.clone())
.unwrap()
.single_entry_point()
.unwrap();
let closest_hit = shader_modules
.closest_hit
.specialize(specs.clone())
.unwrap()
.single_entry_point()
.unwrap();
let miss = shader_modules
.miss
.specialize(specs.clone())
.unwrap()
.single_entry_point()
.unwrap();
// Make a list of the shader stages that the pipeline will have.
let stages = [
&PipelineShaderStageCreateInfo::new(raygen),
&PipelineShaderStageCreateInfo::new(miss),
&PipelineShaderStageCreateInfo::new(closest_hit),
];
let layout = PipelineLayout::new(
device.clone(),
PipelineDescriptorSetLayoutCreateInfo::from_stages(stages)
.into_pipeline_layout_create_info(device.clone())
.unwrap(),
)
.unwrap();
// Define the shader groups that will eventually turn into the shader binding
// table. The numbers are the indices of the stages in the `stages`
// array.
let groups = [
RayTracingShaderGroupCreateInfo::General { general_shader: 0 },
RayTracingShaderGroupCreateInfo::General { general_shader: 1 },
RayTracingShaderGroupCreateInfo::TrianglesHit {
closest_hit_shader: Some(2),
any_hit_shader: None,
},
];
RayTracingPipeline::new(
device.clone(),
Some(cache.clone()),
RayTracingPipelineCreateInfo {
//flags: PipelineCreateFlags::DISABLE_OPTIMIZATION,
stages: stages.into_iter().cloned().collect(),
groups: groups.into_iter().collect(),
max_pipeline_ray_recursion_depth: 1,
//dynamic_state: dynamic_state.clone(),
..RayTracingPipelineCreateInfo::layout(layout)
},
)
.unwrap()
};
let shader_binding_table =
ShaderBindingTable::new(memory_allocator.clone(), &rt_pipeline).unwrap();
dump_pipeline_cache(cache.clone());
lighting_pipeline
(lighting_pipeline, rt_pipeline, shader_binding_table)
}
fn gpu_upload<T>(
+3 -2
View File
@@ -63,6 +63,7 @@ pub(crate) fn load_obj(
memory_allocator: &Arc<StandardMemoryAllocator>,
input: &mut dyn Read,
name: String,
additional_usage: BufferUsage,
) -> Result<Vec<Mesh>, ObjError> {
let object = ObjData::load_buf_with_config(input, LoadConfig::default())?;
@@ -99,7 +100,7 @@ pub(crate) fn load_obj(
let vertex_buffer = Buffer::from_iter(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::VERTEX_BUFFER,
usage: BufferUsage::VERTEX_BUFFER | additional_usage,
..Default::default()
},
AllocationCreateInfo {
@@ -115,7 +116,7 @@ pub(crate) fn load_obj(
let index_buffer = Buffer::from_iter(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::INDEX_BUFFER,
usage: BufferUsage::INDEX_BUFFER | additional_usage,
..Default::default()
},
AllocationCreateInfo {
+275
View File
@@ -0,0 +1,275 @@
use std::{io::Cursor, iter, mem::size_of, sync::Arc};
use vulkano::{
Packed24_8,
acceleration_structure::{
AccelerationStructure, AccelerationStructureBuildGeometryInfo,
AccelerationStructureBuildRangeInfo, AccelerationStructureBuildType,
AccelerationStructureCreateInfo, AccelerationStructureGeometries,
AccelerationStructureGeometryInstancesData, AccelerationStructureGeometryInstancesDataType,
AccelerationStructureGeometryTrianglesData, AccelerationStructureInstance,
AccelerationStructureType, BuildAccelerationStructureFlags, BuildAccelerationStructureMode,
GeometryInstanceFlags,
},
buffer::{Buffer, BufferCreateInfo, BufferUsage, IndexBuffer, Subbuffer},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
allocator::{CommandBufferAllocator, StandardCommandBufferAllocator},
},
device::{Device, Queue},
format::Format,
memory::allocator::{
AllocationCreateInfo, DeviceLayout, MemoryAllocator, MemoryTypeFilter,
StandardMemoryAllocator,
},
sync::GpuFuture,
};
use crate::objects::{Mesh, OVertex, PLATONIC_SOLIDS, load_obj};
pub struct Scene {
// The bottom-level acceleration structure is required to be kept alive
// as we reference it in the top-level acceleration structure.
pub _blas: Arc<AccelerationStructure>,
pub tlas: Arc<AccelerationStructure>,
}
impl Scene {
pub fn new(
device: &Arc<Device>,
graphics_queue: &Arc<Queue>,
memory_allocator: &Arc<StandardMemoryAllocator>,
command_buffer_allocator: &Arc<StandardCommandBufferAllocator>,
) -> Self {
let mesh = load_obj(
&memory_allocator,
&mut Cursor::new(PLATONIC_SOLIDS[0].1),
PLATONIC_SOLIDS[0].0.to_string(),
BufferUsage::SHADER_DEVICE_ADDRESS
| BufferUsage::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY,
)
.unwrap()
.into_iter()
.next()
.unwrap();
// Build the bottom-level acceleration structure and then the top-level
// acceleration structure. Acceleration structures are used to
// accelerate ray tracing. The bottom-level acceleration structure
// contains the geometry data. The top-level acceleration structure
// contains the instances of the bottom-level acceleration structures. In our
// shader, we will trace rays against the top-level acceleration
// structure.
let blas = unsafe {
build_acceleration_structure_triangles(
&mesh,
memory_allocator.clone(),
command_buffer_allocator.clone(),
device.clone(),
graphics_queue.clone(),
)
};
let tlas = unsafe {
build_top_level_acceleration_structure(
vec![AccelerationStructureInstance {
instance_shader_binding_table_record_offset_and_flags: Packed24_8::new(
0,
GeometryInstanceFlags::TRIANGLE_FACING_CULL_DISABLE.into(),
),
acceleration_structure_reference: blas.device_address().into(),
..Default::default()
}],
memory_allocator.clone(),
command_buffer_allocator.clone(),
device.clone(),
graphics_queue.clone(),
)
};
Scene { _blas: blas, tlas }
}
}
/// A helper function to build a acceleration structure and wait for its
/// completion.
///
/// # Safety
///
/// - If you are referencing a bottom-level acceleration structure in a
/// top-level acceleration structure, you must ensure that the bottom-level
/// acceleration structure is kept alive.
unsafe fn build_acceleration_structure_common(
geometries: AccelerationStructureGeometries,
primitive_count: u32,
ty: AccelerationStructureType,
memory_allocator: Arc<dyn MemoryAllocator>,
command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
device: Arc<Device>,
queue: Arc<Queue>,
) -> Arc<AccelerationStructure> {
let min_acceleration_structure_scratch_offset_alignment = device
.physical_device()
.properties()
.min_acceleration_structure_scratch_offset_alignment
.unwrap_or(1);
let mut as_build_geometry_info = AccelerationStructureBuildGeometryInfo {
mode: BuildAccelerationStructureMode::Build,
flags: BuildAccelerationStructureFlags::PREFER_FAST_TRACE,
..AccelerationStructureBuildGeometryInfo::new(geometries)
};
let as_build_sizes_info = device
.acceleration_structure_build_sizes(
AccelerationStructureBuildType::Device,
&as_build_geometry_info,
&[primitive_count],
)
.unwrap();
// We create a new scratch buffer for each acceleration structure for
// simplicity. You may want to reuse scratch buffers if you need to build
// many acceleration structures.
let scratch_buffer = Subbuffer::<[u8]>::new(
Buffer::new(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::SHADER_DEVICE_ADDRESS | BufferUsage::STORAGE_BUFFER,
..Default::default()
},
AllocationCreateInfo::default(),
DeviceLayout::from_size_alignment(
as_build_sizes_info.build_scratch_size,
min_acceleration_structure_scratch_offset_alignment as u64,
)
.unwrap(),
)
.unwrap(),
);
let as_create_info = AccelerationStructureCreateInfo {
ty,
..AccelerationStructureCreateInfo::new(
Buffer::new_slice::<u8>(
memory_allocator,
BufferCreateInfo {
usage: BufferUsage::ACCELERATION_STRUCTURE_STORAGE
| BufferUsage::SHADER_DEVICE_ADDRESS,
..Default::default()
},
AllocationCreateInfo::default(),
as_build_sizes_info.acceleration_structure_size,
)
.unwrap(),
)
};
let acceleration = unsafe { AccelerationStructure::new(device, as_create_info) }.unwrap();
as_build_geometry_info.dst_acceleration_structure = Some(acceleration.clone());
as_build_geometry_info.scratch_data = Some(scratch_buffer);
let as_build_range_info = AccelerationStructureBuildRangeInfo {
primitive_count,
..Default::default()
};
// For simplicity, we build a single command buffer that builds the acceleration
// structure, then waits for its execution to complete.
let mut builder = AutoCommandBufferBuilder::primary(
command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
builder
.build_acceleration_structure(
as_build_geometry_info,
iter::once(as_build_range_info).collect(),
)
.unwrap();
builder
.build()
.unwrap()
.execute(queue)
.unwrap()
.then_signal_fence_and_flush()
.unwrap()
.wait(None)
.unwrap();
acceleration
}
unsafe fn build_acceleration_structure_triangles(
mesh: &Mesh,
memory_allocator: Arc<dyn MemoryAllocator>,
command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
device: Arc<Device>,
queue: Arc<Queue>,
) -> Arc<AccelerationStructure> {
let primitive_count = (mesh.vertices.len() / 3) as u32;
let as_geometry_triangles_data = AccelerationStructureGeometryTrianglesData {
max_vertex: mesh.vertices.len() as _,
vertex_data: Some(mesh.vertices.clone().into_bytes()),
vertex_stride: size_of::<OVertex>() as _,
index_data: Some(IndexBuffer::U32(mesh.indices.clone())),
..AccelerationStructureGeometryTrianglesData::new(Format::R32G32B32_SFLOAT)
};
let geometries = AccelerationStructureGeometries::Triangles(vec![as_geometry_triangles_data]);
build_acceleration_structure_common(
geometries,
primitive_count,
AccelerationStructureType::BottomLevel,
memory_allocator,
command_buffer_allocator,
device,
queue,
)
}
unsafe fn build_top_level_acceleration_structure(
as_instances: Vec<AccelerationStructureInstance>,
allocator: Arc<dyn MemoryAllocator>,
command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
device: Arc<Device>,
queue: Arc<Queue>,
) -> Arc<AccelerationStructure> {
let primitive_count = as_instances.len() as u32;
let instance_buffer = Buffer::from_iter(
allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::SHADER_DEVICE_ADDRESS
| BufferUsage::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
as_instances,
)
.unwrap();
let as_geometry_instances_data = AccelerationStructureGeometryInstancesData::new(
AccelerationStructureGeometryInstancesDataType::Values(Some(instance_buffer)),
);
let geometries = AccelerationStructureGeometries::Instances(as_geometry_instances_data);
build_acceleration_structure_common(
geometries,
primitive_count,
AccelerationStructureType::TopLevel,
allocator,
command_buffer_allocator,
device,
queue,
)
}
+2
View File
@@ -29,6 +29,8 @@ layout(set = 0, binding = 0) uniform Lights {
layout(set = 0, binding = 1) uniform Camera {
mat4 proj_view;
mat4 inv_proj_view;
mat4 inv_proj;
mat4 inv_view;
vec4 campos_and_time;
float specular;
+12
View File
@@ -0,0 +1,12 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive:require
#include "include.glsl"
layout(location = 0) rayPayloadInEXT vec3 hit_value;
hitAttributeEXT vec2 attribs;
void main() {
vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
hit_value = barycentrics;
}
+38
View File
@@ -0,0 +1,38 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive:require
#include "include.glsl"
layout(location = 0) rayPayloadEXT vec3 hit_value;
layout(set = 1, binding = 0) uniform accelerationStructureEXT top_level_as;
layout(set = 2, binding = 0, rgba32f) uniform image2D image;
void main() {
const vec2 pixel_center = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
const vec2 in_uv = pixel_center / vec2(gl_LaunchSizeEXT.xy);
vec2 d = in_uv * 2.0 - 1.0;
vec4 origin = camera_uniforms.inv_view * vec4(0, 0, 0, 1);
vec4 target = camera_uniforms.inv_proj * vec4(d.x, d.y, 1, 1);
vec4 direction = camera_uniforms.inv_view * vec4(normalize(target.xyz), 0);
uint ray_flags = gl_RayFlagsOpaqueEXT;
float t_min = 0.001;
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));
}
+10
View File
@@ -0,0 +1,10 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive:require
#include "include.glsl"
layout(location = 0) rayPayloadInEXT vec3 hit_value;
void main() {
hit_value = vec3(0.0, 0.0, 0.2);
}