From 321f30b4cc665e7ce59c20ee8c637358001207cb Mon Sep 17 00:00:00 2001 From: mitchellhansen Date: Sat, 20 Jul 2019 04:45:49 -0700 Subject: [PATCH] The entry point takes a borrow of the shader module which is then cloned by the pipeline. So I gotta keep the shader, entry, and modules in a high scope --- src/vkprocessor.rs | 7 +- src/vkprocessor/compute_kernel.rs | 4 +- src/vkprocessor/shader_kernels.rs | 300 ++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 src/vkprocessor/shader_kernels.rs diff --git a/src/vkprocessor.rs b/src/vkprocessor.rs index 9c3c63e7..ebf08644 100644 --- a/src/vkprocessor.rs +++ b/src/vkprocessor.rs @@ -40,6 +40,10 @@ use image::flat::NormalForm::ColumnMajorPacked; mod compute_kernel; use crate::vkprocessor::compute_kernel::ComputeKernel; +mod shader_kernels; +use crate::vkprocessor::shader_kernels::ShaderKernels; + + #[derive(Default, Debug, Clone)] struct tVertex { position: [f32; 2] } @@ -68,7 +72,7 @@ fn window_size_dependent_setup( } #[repr(C)] -#[derive(Clone)] +#[derive(Default, Debug, Clone)] struct SimpleSpecializationConstants { first_constant: i32, second_constant: u32, @@ -176,7 +180,6 @@ impl<'a> VkProcessor<'a> { pub fn compile_kernel(&mut self, filename: String) { - self.compute_kernel = Some(ComputeKernel::new(filename, self.device.clone())); self.compute_pipeline = Some(self.compute_kernel.clone().unwrap().get_pipeline()); } diff --git a/src/vkprocessor/compute_kernel.rs b/src/vkprocessor/compute_kernel.rs index 63fcabf4..120e8dea 100644 --- a/src/vkprocessor/compute_kernel.rs +++ b/src/vkprocessor/compute_kernel.rs @@ -116,8 +116,8 @@ impl ComputeKernel { } } - pub fn recompile_kernel(&mut self) { - self.compile_kernel(String::from(self.compute_kernel_path.clone().to_str().unwrap())); + pub fn recompile_kernel(&mut self) -> std::sync::Arc>> { + self.compile_kernel(String::from(self.compute_kernel_path.clone().to_str().unwrap())) } pub fn compile_kernel(&mut self, filename: String) -> std::sync::Arc>> { diff --git a/src/vkprocessor/shader_kernels.rs b/src/vkprocessor/shader_kernels.rs new file mode 100644 index 00000000..2a5da587 --- /dev/null +++ b/src/vkprocessor/shader_kernels.rs @@ -0,0 +1,300 @@ +use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer, ImmutableBuffer, BufferAccess}; +use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState}; +use vulkano::descriptor::descriptor_set::{PersistentDescriptorSet, StdDescriptorPoolAlloc}; +use vulkano::device::{Device, DeviceExtensions, QueuesIter, Queue}; +use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice, QueueFamily}; +use vulkano::pipeline::{ComputePipeline, GraphicsPipeline, GraphicsPipelineAbstract}; +use vulkano::sync::{GpuFuture, FlushError}; +use vulkano::sync; +use std::time::SystemTime; +use std::sync::Arc; +use std::ffi::CStr; +use std::path::PathBuf; +use shade_runner as sr; +use image::{DynamicImage, ImageBuffer}; +use image::GenericImageView; +use vulkano::descriptor::pipeline_layout::PipelineLayout; +use image::GenericImage; +use shade_runner::{ComputeLayout, CompileError, FragLayout, FragInput, FragOutput, VertInput, VertOutput, VertLayout, CompiledShaders, Entry}; +use vulkano::descriptor::descriptor_set::{PersistentDescriptorSetBuf, PersistentDescriptorSetImg, PersistentDescriptorSetSampler}; +use shaderc::CompileOptions; +use vulkano::framebuffer::{Subpass, RenderPass, RenderPassAbstract, Framebuffer, FramebufferAbstract}; +use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule, GraphicsEntryPoint, SpecializationConstants, SpecializationMapEntry}; +use vulkano::swapchain::{Swapchain, PresentMode, SurfaceTransform, Surface, SwapchainCreationError, AcquireError}; +use vulkano::swapchain::acquire_next_image; +use vulkano::image::swapchain::SwapchainImage; +use winit::{EventsLoop, WindowBuilder, Window, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; +use vulkano::pipeline::vertex::{SingleBufferDefinition, Vertex}; +use vulkano::descriptor::PipelineLayoutAbstract; +use std::alloc::Layout; +use vulkano::pipeline::viewport::Viewport; +use image::ImageFormat; +use vulkano::image::immutable::ImmutableImage; +use vulkano::image::attachment::AttachmentImage; +use vulkano::image::{Dimensions, ImageUsage}; +use vulkano::format::Format; +use vulkano::sampler::{Sampler, Filter, MipmapMode, SamplerAddressMode}; +use image::flat::NormalForm::ColumnMajorPacked; +use crate::vkprocessor::SimpleSpecializationConstants; + +struct EntryPoint<'a> { + compiled_shaders: CompiledShaders, + frag_entry_point: Option>, + vertex_entry_point: Option>, + vertex_shader_module: Arc, + fragment_shader_module: Arc, +} + + +#[derive(Default, Debug, Clone)] +struct tVertex { position: [f32; 2] } + +pub struct ShaderKernels<'a> { + swapchain : Arc>, + swapchain_images: Vec>>, // Surface which is drawn to + pub physical: PhysicalDevice<'a>, + + shader: CompiledShaders, + + options: CompileOptions<'a>, + + pub render_pass: Arc, + pub graphics_pipeline: Option>, + + device: Arc, + + entry_point: EntryPoint<'a>, +} + +// return the frame buffers +/* +let mut framebuffers = +window_size_dependent_setup(&self.images.clone().unwrap().clone(), + self.render_pass.clone().unwrap().clone(), + &mut self.dynamic_state); +*/ +impl<'a> ShaderKernels<'a> { + + fn get_path(filename: String) -> (PathBuf, PathBuf) { + + let project_root = + std::env::current_dir() + .expect("failed to get root directory"); + + let mut shader_path = project_root.clone(); + shader_path.push(PathBuf::from("resources/shaders/")); + + let mut vertex_shader_path = project_root.clone(); + vertex_shader_path.push(PathBuf::from("resources/shaders/")); + vertex_shader_path.push(PathBuf::from(filename.clone() + ".vertex")); + + let mut fragment_shader_path = project_root.clone(); + fragment_shader_path.push(PathBuf::from("resources/shaders/")); + fragment_shader_path.push(PathBuf::from(filename.clone() + ".fragment")); + + (vertex_shader_path, fragment_shader_path) + } + + pub fn get_pipeline(&mut self) -> Arc { + + match self.graphics_pipeline.clone() { + Some(t) => t, + None => { + self.graphics_pipeline = Some(Arc::new( + GraphicsPipeline::start() + // We need to indicate the layout of the vertices. + // The type `SingleBufferDefinition` actually contains a template parameter corresponding + // to the type of each vertex. But in this code it is automatically inferred. + .vertex_input_single_buffer::() + + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. The `main` word of `main_entry_point` actually corresponds to the name of + // the entry point. + .vertex_shader(self.entry_point.vertex_entry_point.clone().unwrap(), SimpleSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + // The content of the vertex buffer describes a list of triangles. + .triangle_fan() + // Use a resizable viewport set to draw over the entire window + .viewports_dynamic_scissors_irrelevant(1) + // See `vertex_shader`. + .fragment_shader(self.entry_point.frag_entry_point.clone().unwrap(), SimpleSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + .render_pass(Subpass::from(self.render_pass.clone(), 0).unwrap()) + // Now that our builder is filled, we call `build()` to obtain an actual pipeline. + .build(self.device.clone()) + .unwrap() + )); + self.graphics_pipeline.clone().unwrap() + } + } + } + + pub fn new(filename: String, + surface: &'a Arc>, + queue: Arc, + physical: PhysicalDevice<'a>, + device: Arc) -> ShaderKernels<'a> { + + let (mut swapchain, images) = { + let capabilities = surface.capabilities(physical).unwrap(); + let usage = capabilities.supported_usage_flags; + let alpha = capabilities.supported_composite_alpha.iter().next().unwrap(); + // Choosing the internal format that the images will have. + let format = capabilities.supported_formats[0].0; + + // Set the swapchains window dimensions + let initial_dimensions = if let Some(dimensions) = surface.window().get_inner_size() { + // convert to physical pixels + let dimensions: (u32, u32) = dimensions.to_physical(surface.window().get_hidpi_factor()).into(); + [dimensions.0, dimensions.1] + } else { + // The window no longer exists so exit the application. + panic!("window closed"); + }; + + Swapchain::new(device.clone(), + surface.clone(), + capabilities.min_image_count, + format, + initial_dimensions, + 1, // Layers + usage, + &queue, + SurfaceTransform::Identity, + alpha, + PresentMode::Fifo, true, None).unwrap() + }; + + let filenames = ShaderKernels::get_path(filename.clone()); + + // TODO: better compile message, run til successful compile + let shader = sr::load(filenames.0, filenames.1) + .expect("Shader didn't compile"); + + let vulkano_entry = + sr::parse(&shader) + .expect("failed to parse"); + + let fragment_shader_module: Arc = unsafe { + vulkano::pipeline::shader::ShaderModule::from_words(device.clone(), &shader.fragment.clone()) + }.unwrap(); + + let vertex_shader_module: Arc = unsafe { + vulkano::pipeline::shader::ShaderModule::from_words(device.clone(), &shader.vertex.clone()) + }.unwrap(); + + let filenames = ShaderKernels::get_path(filename.clone()); + let mut entry_point = EntryPoint { + compiled_shaders: sr::load(filenames.0, filenames.1) + .expect("Shader didn't compile"), + fragment_shader_module: fragment_shader_module, + vertex_shader_module: vertex_shader_module, + frag_entry_point: None, + vertex_entry_point: None, + }; + + entry_point.frag_entry_point = unsafe { + Some(entry_point.fragment_shader_module.graphics_entry_point(CStr::from_bytes_with_nul_unchecked(b"main\0"), + vulkano_entry.frag_input, + vulkano_entry.frag_output, + vulkano_entry.frag_layout, + GraphicsShaderType::Fragment)) + }; + + entry_point.vertex_entry_point = unsafe { + Some(entry_point.vertex_shader_module.graphics_entry_point(CStr::from_bytes_with_nul_unchecked(b"main\0"), + vulkano_entry.vert_input, + vulkano_entry.vert_output, + vulkano_entry.vert_layout, + GraphicsShaderType::Vertex)) + }; + + let render_pass = Arc::new(vulkano::single_pass_renderpass!( + device.clone(), + attachments: { + // `color` is a custom name we give to the first and only attachment. + color: { + // `load: Clear` means that we ask the GPU to clear the content of this + // attachment at the start of the drawing. + load: Clear, + // `store: Store` means that we ask the GPU to store the output of the draw + // in the actual image. We could also ask it to discard the result. + store: Store, + // `format: ` indicates the type of the format of the image. This has to + // be one of the types of the `vulkano::format` module (or alternatively one + // of your structs that implements the `FormatDesc` trait). Here we use the + // same format as the swapchain. + format: swapchain.clone().format(), + // TODO: + samples: 1, + } + }, + pass: { + // We use the attachment named `color` as the one and only color attachment. + color: [color], + // No depth-stencil attachment is indicated with empty brackets. + depth_stencil: {} + } + ).unwrap()); + + vulkano::impl_vertex!(tVertex, position); + // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL + // program, but much more specific. + let pipeline = GraphicsPipeline::start() + // We need to indicate the layout of the vertices. + // The type `SingleBufferDefinition` actually contains a template parameter corresponding + // to the type of each vertex. But in this code it is automatically inferred. + .vertex_input_single_buffer::() + + // A Vulkan shader can in theory contain multiple entry points, so we have to specify + // which one. The `main` word of `main_entry_point` actually corresponds to the name of + // the entry point. + .vertex_shader(entry_point.vertex_entry_point.clone().unwrap(), SimpleSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + // The content of the vertex buffer describes a list of triangles. + .triangle_fan() + // Use a resizable viewport set to draw over the entire window + .viewports_dynamic_scissors_irrelevant(1) + // See `vertex_shader`. + .fragment_shader(entry_point.frag_entry_point.clone().unwrap(), SimpleSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + // Now that our builder is filled, we call `build()` to obtain an actual pipeline. + .build(device.clone()) + .unwrap(); + + + + ShaderKernels { + swapchain: swapchain, + swapchain_images: images, + physical: physical, + + shader: shader, + + options: CompileOptions::new().ok_or(CompileError::CreateCompiler).unwrap(), + render_pass: render_pass, + graphics_pipeline: Some(Arc::new(pipeline)), + device: device, + entry_point: entry_point, + } + } + +} \ No newline at end of file