diff --git a/resources/shaders/simple_texture.fragment b/resources/shaders/simple_texture.fragment new file mode 100644 index 00000000..30cb5d7f --- /dev/null +++ b/resources/shaders/simple_texture.fragment @@ -0,0 +1,7 @@ +#version 450 +layout(location = 0) in vec2 tex_coords; +layout(location = 0) out vec4 f_color; +layout(set = 0, binding = 0) uniform sampler2D tex; +void main() { + f_color = texture(tex, tex_coords); +} \ No newline at end of file diff --git a/resources/shaders/simple_texture.vertex b/resources/shaders/simple_texture.vertex new file mode 100644 index 00000000..82553791 --- /dev/null +++ b/resources/shaders/simple_texture.vertex @@ -0,0 +1,7 @@ +#version 450 +layout(location = 0) in vec2 position; +layout(location = 0) out vec2 tex_coords; +void main() { + gl_Position = vec4(position, 0.0, 1.0); + tex_coords = position; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f6ed6eee..33ccc07b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,7 +74,7 @@ fn main() { let mut processor = vkprocessor::VkProcessor::new(&instance, &surface); processor.compile_kernel(String::from("simple-edge.compute")); - processor.compile_shaders(String::from("simple"), &surface); + processor.compile_shaders(String::from("simple_texture"), &surface); processor.load_buffers(String::from("funky-bird.jpg")); diff --git a/src/vkprocessor.rs b/src/vkprocessor.rs index 32bc91b5..4cbb8d10 100644 --- a/src/vkprocessor.rs +++ b/src/vkprocessor.rs @@ -16,7 +16,7 @@ use image::GenericImageView; use vulkano::descriptor::pipeline_layout::PipelineLayout; use image::GenericImage; use shade_runner::{ComputeLayout, CompileError, FragLayout, FragInput, FragOutput, VertInput, VertOutput, VertLayout}; -use vulkano::descriptor::descriptor_set::PersistentDescriptorSetBuf; +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}; @@ -29,7 +29,11 @@ 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::Dimensions; +use vulkano::format::Format; +use vulkano::sampler::{Sampler, Filter, MipmapMode, SamplerAddressMode}; #[derive(Default, Debug, Clone)] struct tVertex { position: [f32; 2] } @@ -93,12 +97,14 @@ unsafe impl SpecializationConstants for MySpecConstants { pub struct VkProcessor<'a> { pub instance: Arc, pub physical: PhysicalDevice<'a>, - pub pipeline: Option>, + pub graphics_pipeline: Option>, pub compute_pipeline: Option>>>, pub device: Arc, pub queues: QueuesIter, pub queue: Arc, - pub set: Option>>, ((((), PersistentDescriptorSetBuf>>), PersistentDescriptorSetBuf>>), PersistentDescriptorSetBuf>>)>>>, + pub compute_set: Option>>, ((((), PersistentDescriptorSetBuf>>), PersistentDescriptorSetBuf>>), PersistentDescriptorSetBuf>>)>>>, + pub img_set: Option, (((), PersistentDescriptorSetImg>>), PersistentDescriptorSetSampler)>>>, + pub graphics_image_buffer: Option>>, pub image_buffer: Vec, pub img_buffers: Vec>>, pub settings_buffer: Option>>, @@ -108,10 +114,11 @@ pub struct VkProcessor<'a> { pub render_pass: Option>, pub vertex_buffer: Option>, pub dynamic_state: DynamicState, - pub previous_frame: Box, } + impl<'a> VkProcessor<'a> { + pub fn new(instance: &'a Arc, surface: &'a Arc>) -> VkProcessor<'a> { let physical = PhysicalDevice::enumerate(instance).next().unwrap(); @@ -133,12 +140,14 @@ impl<'a> VkProcessor<'a> { VkProcessor { instance: instance.clone(), physical: physical.clone(), - pipeline: Option::None, + graphics_pipeline: Option::None, compute_pipeline: Option::None, device: device.clone(), queue: queue, queues: queues, - set: Option::None, + compute_set: Option::None, + img_set: Option::None, + graphics_image_buffer: None, image_buffer: Vec::new(), img_buffers: Vec::new(), settings_buffer: Option::None, @@ -148,7 +157,6 @@ impl<'a> VkProcessor<'a> { render_pass: Option::None, vertex_buffer: Option::None, dynamic_state: DynamicState { line_width: None, viewports: None, scissors: None }, - previous_frame: Box::new(sync::now(device.clone())) as Box, } } @@ -316,6 +324,25 @@ impl<'a> VkProcessor<'a> { self.render_pass = Some(render_pass); + let (texture, tex_future) = { + let image = image::load_from_memory_with_format(include_bytes!("../resources/images/funky-bird.jpg"), + ImageFormat::JPEG).unwrap().to_rgba(); + let dimensions = image.dimensions(); + let image_data = image.into_raw().clone(); + + ImmutableImage::from_iter( + image_data.iter().cloned(), + Dimensions::Dim2d { width: dimensions.0, height: dimensions.1 }, + Format::R8G8B8A8Srgb, + self.queue.clone() + ).unwrap() + }; + + let sampler = Sampler::new(self.device.clone(), Filter::Linear, Filter::Linear, + MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, + SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(); + + // 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() @@ -323,6 +350,7 @@ impl<'a> VkProcessor<'a> { // 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. @@ -332,7 +360,7 @@ impl<'a> VkProcessor<'a> { floating_point: 0.0, }) // The content of the vertex buffer describes a list of triangles. - .triangle_list() + .triangle_fan() // Use a resizable viewport set to draw over the entire window .viewports_dynamic_scissors_irrelevant(1) // See `vertex_shader`. @@ -349,7 +377,13 @@ impl<'a> VkProcessor<'a> { .unwrap(); - self.pipeline = Option::Some(Arc::new(pipeline)); + self.graphics_pipeline = Some(Arc::new(pipeline)); + + self.img_set = Some(Arc::new(PersistentDescriptorSet::start(self.graphics_pipeline.clone().unwrap().clone(), 0) + .add_sampled_image(texture.clone(), sampler.clone()).unwrap() + .build().unwrap())); + + self.graphics_image_buffer = Some(texture.clone()); } @@ -432,12 +466,15 @@ impl<'a> VkProcessor<'a> { .dispatch([self.xy.0, self.xy.1, 1], self.compute_pipeline.clone().unwrap().clone(), - self.set.clone().unwrap().clone(), ()).unwrap() + self.compute_set.clone().unwrap().clone(), ()).unwrap() + // .copy_buffer_to_image(self.img_buffers.get(0).unwrap().clone(), self.graphics_image_buffer.clone().unwrap()).unwrap() .begin_render_pass(framebuffers[image_num].clone(), false, clear_values) .unwrap() - .draw(self.pipeline.clone().unwrap().clone(), &self.dynamic_state, v, (), ()) + .draw(self.graphics_pipeline.clone().unwrap().clone(), + &self.dynamic_state, v, + self.img_set.clone().unwrap().clone(), ()) .unwrap() .end_render_pass() @@ -539,7 +576,7 @@ impl<'a> VkProcessor<'a> { .add_buffer(read_buffer.clone()).unwrap() .add_buffer(settings_buffer.clone()).unwrap(); - self.set = Some(Arc::new(set.build().unwrap())); + self.compute_set = Some(Arc::new(set.build().unwrap())); self.img_buffers.push(write_buffer); self.img_buffers.push(read_buffer); @@ -551,37 +588,16 @@ impl<'a> VkProcessor<'a> { vulkano::impl_vertex!(tVertex, position); CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::all(), [ - tVertex { position: [-0.5, -0.25] }, - tVertex { position: [0.0, 0.5] }, - tVertex { position: [0.25, -0.1] } + tVertex { position: [-1.0, -1.0 ] }, + tVertex { position: [-1.0, 1.0 ] }, + tVertex { position: [ 1.0, 1.0 ] }, + tVertex { position: [ 1.0, -1.0 ] }, ].iter().cloned()).unwrap() }; self.vertex_buffer = Some(vertex_buffer); } -// pub fn run_kernel(&mut self) { -// -// println!("Running Kernel..."); -// -// // The command buffer I think pretty much serves to define what runs where for how many times -// let command_buffer = -// AutoCommandBufferBuilder::primary_one_time_submit(self.device.clone(),self.queue.family()).unwrap() -// .dispatch([self.xy.0, self.xy.1, 1], -// self.compute_pipeline.clone().unwrap().clone(), -// self.set.clone().unwrap().clone(), ()).unwrap() -// .build().unwrap(); -// -// // Create a future for running the command buffer and then just fence it -// let future = sync::now(self.device.clone()) -// .then_execute(self.queue.clone(), command_buffer).unwrap() -// .then_signal_fence_and_flush().unwrap(); -// -// // I think this is redundant and returns immediately -// future.wait(None).unwrap(); -// println!("Done running kernel"); -// } - // pub fn read_image(&self) -> Vec { // // // The buffer is sync'd so we can just read straight from the handle diff --git a/src/vulkano_img_example.rs b/src/vulkano_img_example.rs new file mode 100644 index 00000000..a82234ee --- /dev/null +++ b/src/vulkano_img_example.rs @@ -0,0 +1,292 @@ +Skip to content + +Search or jump to… + +Pull requests +Issues +Marketplace +Explore + +@MitchellHansen +69 +1,789 192 vulkano-rs/vulkano +Code Issues 171 Pull requests 15 Security Insights +vulkano/examples/src/bin/image/main.rs +@rukai rukai Fix warnings on nightly (#1213) +fc6ac6f 15 days ago +279 lines (232 sloc) 9.86 KB + +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer}; +use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState}; +use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; +use vulkano::device::{Device, DeviceExtensions}; +use vulkano::format::Format; +use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract}; +use vulkano::image::{SwapchainImage, ImmutableImage, Dimensions}; +use vulkano::instance::{Instance, PhysicalDevice}; +use vulkano::pipeline::GraphicsPipeline; +use vulkano::pipeline::viewport::Viewport; +use vulkano::sampler::{Sampler, SamplerAddressMode, Filter, MipmapMode}; +use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError}; +use vulkano::swapchain; +use vulkano::sync::{GpuFuture, FlushError}; +use vulkano::sync; + +use vulkano_win::VkSurfaceBuild; + +use winit::{EventsLoop, Window, WindowBuilder, Event, WindowEvent}; + +use image::ImageFormat; + +use std::sync::Arc; + +fn main() { + // The start of this example is exactly the same as `triangle`. You should read the + // `triangle` example if you haven't done so yet. + + let extensions = vulkano_win::required_extensions(); + let instance = Instance::new(None, &extensions, None).unwrap(); + + let physical = PhysicalDevice::enumerate(&instance).next().unwrap(); + println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); + + let mut events_loop = EventsLoop::new(); + let surface = WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); + let window = surface.window(); + + let queue_family = physical.queue_families().find(|&q| + q.supports_graphics() && surface.is_supported(q).unwrap_or(false) + ).unwrap(); + + let device_ext = DeviceExtensions { khr_swapchain: true, .. DeviceExtensions::none() }; + let (device, mut queues) = Device::new(physical, physical.supported_features(), &device_ext, + [(queue_family, 0.5)].iter().cloned()).unwrap(); + let queue = queues.next().unwrap(); + + let (mut swapchain, images) = { + let caps = surface.capabilities(physical).unwrap(); + + let usage = caps.supported_usage_flags; + let alpha = caps.supported_composite_alpha.iter().next().unwrap(); + let format = caps.supported_formats[0].0; + + let initial_dimensions = if let Some(dimensions) = window.get_inner_size() { + // convert to physical pixels + let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into(); + [dimensions.0, dimensions.1] + } else { + // The window no longer exists so exit the application. + return; + }; + + Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format, + initial_dimensions, 1, usage, &queue, SurfaceTransform::Identity, alpha, + PresentMode::Fifo, true, None).unwrap() + }; + + + #[derive(Default, Debug, Clone)] + struct Vertex { position: [f32; 2] } + vulkano::impl_vertex!(Vertex, position); + + let vertex_buffer = CpuAccessibleBuffer::<[Vertex]>::from_iter( + device.clone(), + BufferUsage::all(), + [ + Vertex { position: [-0.5, -0.5 ] }, + Vertex { position: [-0.5, 0.5 ] }, + Vertex { position: [ 0.5, -0.5 ] }, + Vertex { position: [ 0.5, 0.5 ] }, + ].iter().cloned() + ).unwrap(); + + let vs = vs::Shader::load(device.clone()).unwrap(); + let fs = fs::Shader::load(device.clone()).unwrap(); + + let render_pass = Arc::new( + vulkano::single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: swapchain.format(), + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap() + ); + + let (texture, tex_future) = { + let image = image::load_from_memory_with_format(include_bytes!("image_img.png"), + ImageFormat::PNG).unwrap().to_rgba(); + let image_data = image.into_raw().clone(); + + ImmutableImage::from_iter( + image_data.iter().cloned(), + Dimensions::Dim2d { width: 93, height: 93 }, + Format::R8G8B8A8Srgb, + queue.clone() + ).unwrap() + }; + + let sampler = Sampler::new(device.clone(), Filter::Linear, Filter::Linear, + MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, + SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(); + + let pipeline = Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vs.main_entry_point(), ()) + .triangle_strip() + .viewports_dynamic_scissors_irrelevant(1) + .fragment_shader(fs.main_entry_point(), ()) + .blend_alpha_blending() + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap()); + + let set = Arc::new(PersistentDescriptorSet::start(pipeline.clone(), 0) + .add_sampled_image(texture.clone(), sampler.clone()).unwrap() + .build().unwrap() + ); + + let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None }; + let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut dynamic_state); + + let mut recreate_swapchain = false; + let mut previous_frame_end = Box::new(tex_future) as Box; + + loop { + previous_frame_end.cleanup_finished(); + if recreate_swapchain { + let dimensions = if let Some(dimensions) = window.get_inner_size() { + let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into(); + [dimensions.0, dimensions.1] + } else { + return; + }; + + let (new_swapchain, new_images) = match swapchain.recreate_with_dimension(dimensions) { + Ok(r) => r, + Err(SwapchainCreationError::UnsupportedDimensions) => continue, + Err(err) => panic!("{:?}", err) + }; + + swapchain = new_swapchain; + framebuffers = window_size_dependent_setup(&new_images, render_pass.clone(), &mut dynamic_state); + + recreate_swapchain = false; + } + + let (image_num, future) = match swapchain::acquire_next_image(swapchain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + recreate_swapchain = true; + continue; + } + Err(err) => panic!("{:?}", err) + }; + + let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into()); + + let cb = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family()) + .unwrap() + .begin_render_pass(framebuffers[image_num].clone(), false, clear_values).unwrap() + .draw(pipeline.clone(), &dynamic_state, vertex_buffer.clone(), set.clone(), ()).unwrap() + .end_render_pass().unwrap() + .build().unwrap(); + + let future = previous_frame_end.join(future) + .then_execute(queue.clone(), cb).unwrap() + .then_swapchain_present(queue.clone(), swapchain.clone(), image_num) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + previous_frame_end = Box::new(future) as Box<_>; + } + Err(FlushError::OutOfDate) => { + recreate_swapchain = true; + previous_frame_end = Box::new(sync::now(device.clone())) as Box<_>; + } + Err(e) => { + println!("{:?}", e); + previous_frame_end = Box::new(sync::now(device.clone())) as Box<_>; + } + } + + let mut done = false; + events_loop.poll_events(|ev| { + match ev { + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => done = true, + Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true, + _ => () + } + }); + if done { return; } + } +} + +/// This method is called once during initialization, then again whenever the window is resized +fn window_size_dependent_setup( + images: &[Arc>], + render_pass: Arc, + dynamic_state: &mut DynamicState +) -> Vec> { + let dimensions = images[0].dimensions(); + + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions: [dimensions[0] as f32, dimensions[1] as f32], + depth_range: 0.0 .. 1.0, + }; + dynamic_state.viewports = Some(vec!(viewport)); + + images.iter().map(|image| { + Arc::new( + Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap() + ) as Arc + }).collect::>() +} + +mod vs { + vulkano_shaders::shader!{ + ty: "vertex", + src: " +#version 450 +layout(location = 0) in vec2 position; +layout(location = 0) out vec2 tex_coords; +void main() { + gl_Position = vec4(position, 0.0, 1.0); + tex_coords = position + vec2(0.5); +}" + } +} + +mod fs { + vulkano_shaders::shader!{ + ty: "fragment", + src: " +#version 450 +layout(location = 0) in vec2 tex_coords; +layout(location = 0) out vec4 f_color; +layout(set = 0, binding = 0) uniform sampler2D tex; +void main() { + f_color = texture(tex, tex_coords); +}" + } +}