vulkano-rs / vulkano

Safe and rich Rust wrapper around the Vulkan API
Apache License 2.0
4.55k stars 438 forks source link

Does image example only work with square images? #2558

Closed minus1ms closed 3 months ago

minus1ms commented 3 months ago

Template

```rs use std::error::Error; use anyhow::Result; use glam::{Mat4, Quat, Vec3}; use std::sync::Arc; use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; use vulkano::command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferToImageInfo, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract, RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }; use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; use vulkano::descriptor_set::layout::DescriptorSetLayout; use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet}; use vulkano::device::physical::PhysicalDeviceType; use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, QueueFlags}; use vulkano::format::Format; use vulkano::image::sampler::{Filter, Sampler, SamplerCreateInfo}; use vulkano::image::view::ImageView; use vulkano::image::{Image, ImageCreateInfo, ImageType, ImageUsage}; use vulkano::instance::{Instance, InstanceCreateInfo}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::graphics::color_blend::{ AttachmentBlend, ColorBlendAttachmentState, ColorBlendState, }; use vulkano::pipeline::graphics::input_assembly::{InputAssemblyState, PrimitiveTopology}; use vulkano::pipeline::graphics::multisample::MultisampleState; use vulkano::pipeline::graphics::rasterization::RasterizationState; use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo; use vulkano::pipeline::{ DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, PipelineShaderStageCreateInfo, }; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{ acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, }; use vulkano::sync::GpuFuture; use vulkano::{swapchain, sync, DeviceSize, Validated, VulkanError, VulkanLibrary}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::{Window, WindowId}; fn main() -> Result<()> { let event_loop = EventLoop::new()?; event_loop.set_control_flow(ControlFlow::Poll); let mut app = Application::new(&event_loop)?; Ok(event_loop.run_app(&mut app)?) } /// Application state and event handling. struct Application { window: Option, instance: Arc, } impl Application { fn new(event_loop: &EventLoop<()>) -> Result { let required_extensions = Surface::required_extensions(&event_loop); let library = VulkanLibrary::new()?; let instance = Instance::new( library, InstanceCreateInfo { enabled_extensions: required_extensions, ..Default::default() }, )?; println!("vulkan version: {}", instance.api_version()); Ok(Self { instance, window: Default::default(), }) } fn create_window( &mut self, event_loop: &ActiveEventLoop, _tab_id: Option, ) -> Result<(), Box> { let window_attributes = Window::default_attributes(); let window = event_loop.create_window(window_attributes)?; let window = WindowState::new(self, window)?; self.window = Some(window); Ok(()) } } impl ApplicationHandler for Application { fn resumed(&mut self, event_loop: &ActiveEventLoop) { println!("Ready to create surfaces"); // Create initial window. self.create_window(event_loop, None) .expect("failed to create initial window"); } fn window_event( &mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent, ) { let Some(window) = self.window.as_mut() else { return; }; match event { WindowEvent::Resized(_) => { window.resize(); } WindowEvent::RedrawRequested => { if let Err(err) = window.draw() { println!("Error drawing window: {err}"); } } WindowEvent::CloseRequested => { println!("Closing Window={window_id:?}"); self.window = None; event_loop.exit(); } _ => {} } } fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { if let Some(window) = self.window.as_mut() { window.draw().unwrap(); } else { println!("No windows left, exiting..."); event_loop.exit(); } } } struct WindowState { device: Arc, queue: Arc, swapchain: Arc, memory_allocator: Arc, render_pass: Arc, desc_set: Arc, pipeline: Arc, viewport: Viewport, framebuffers: Vec>, descriptor_set_allocator: Arc, command_buffer_allocator: Arc, recreate_swapchain: bool, previous_frame_end: Option>, window: Arc, sampler: Arc, texture: Arc, uniform_buffer: SubbufferAllocator, } impl WindowState { fn new(app: &Application, window: Window) -> Result> { let window = Arc::new(window); let surface = swapchain::Surface::from_window(app.instance.clone(), window.clone())?; let device_extensions = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::empty() }; let (physical_device, queue_family_index) = app .instance .enumerate_physical_devices()? .filter(|p| p.supported_extensions().contains(&device_extensions)) .filter_map(|p| { p.queue_family_properties() .iter() .enumerate() .position(|(i, q)| { q.queue_flags.intersects(QueueFlags::GRAPHICS) && p.surface_support(i as u32, &surface).unwrap_or(false) }) .map(|i| (p, i as u32)) }) .min_by_key(|(p, _)| match p.properties().device_type { PhysicalDeviceType::DiscreteGpu => 0, PhysicalDeviceType::IntegratedGpu => 1, PhysicalDeviceType::VirtualGpu => 2, PhysicalDeviceType::Cpu => 3, PhysicalDeviceType::Other => 4, _ => 5, }) .expect("No suitable physical device found"); println!( "Using device: {} (type: {:?})", physical_device.properties().device_name, physical_device.properties().device_type, ); let (device, mut queues) = Device::new( physical_device, DeviceCreateInfo { enabled_extensions: device_extensions, queue_create_infos: vec![QueueCreateInfo { queue_family_index, ..Default::default() }], ..Default::default() }, )?; let queue = queues.next().unwrap(); let (swapchain, images) = { let surface_capabilities = device .physical_device() .surface_capabilities(&surface, Default::default())?; let image_format = device .physical_device() .surface_formats(&surface, Default::default())?[0] .0; Swapchain::new( device.clone(), surface.clone(), SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count, image_format, image_extent: window.inner_size().into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha: surface_capabilities .supported_composite_alpha .into_iter() .next() .unwrap(), ..Default::default() }, )? }; let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let render_pass = get_render_pass(device.clone(), &swapchain); let pipeline = get_pipeline(device.clone(), render_pass.clone())?; let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( device.clone(), Default::default(), )); let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( device.clone(), Default::default(), )); let sampler = Sampler::new( device.clone(), SamplerCreateInfo { mag_filter: Filter::Nearest, min_filter: Filter::Nearest, ..Default::default() }, ) .unwrap(); let mut uploads: AutoCommandBufferBuilder< PrimaryAutoCommandBuffer>, Arc, > = AutoCommandBufferBuilder::primary( &command_buffer_allocator, queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) .unwrap(); let texture = { let png_bytes = include_bytes!("test.png").as_slice(); let decoder = png::Decoder::new(png_bytes); let mut reader = decoder.read_info().unwrap(); let info = reader.info(); let extent = [info.width, info.height, 1]; let upload_buffer = Buffer::new_slice( memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::TRANSFER_SRC, ..Default::default() }, AllocationCreateInfo { memory_type_filter: MemoryTypeFilter::PREFER_HOST | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, (info.width * info.height * 4) as DeviceSize, ) .unwrap(); reader .next_frame(&mut upload_buffer.write().unwrap()) .unwrap(); let image = Image::new( memory_allocator.clone(), ImageCreateInfo { image_type: ImageType::Dim2d, format: Format::R8G8B8A8_SRGB, extent, usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, ..Default::default() }, AllocationCreateInfo::default(), ) .unwrap(); uploads .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( upload_buffer, image.clone(), )) .unwrap(); ImageView::new_default(image).unwrap() }; let uniform_buffer = SubbufferAllocator::new( memory_allocator.clone(), SubbufferAllocatorCreateInfo { buffer_usage: BufferUsage::UNIFORM_BUFFER, memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, ); let layout = pipeline.layout().set_layouts()[0].clone(); let desc_set = get_descriptor_set( descriptor_set_allocator.clone(), layout, swapchain.clone(), sampler.clone(), texture.clone(), &uniform_buffer, )?; let mut viewport = Viewport { offset: [0.0, 0.0], extent: [0.0, 0.0], depth_range: 0.0..=1.0, }; let framebuffers = get_framebuffers(&images, render_pass.clone(), &mut viewport); let previous_frame_end = Some( uploads .build() .unwrap() .execute(queue.clone()) .unwrap() .boxed(), ); Ok(Self { device, queue, swapchain, memory_allocator, render_pass, pipeline, desc_set, viewport, framebuffers, descriptor_set_allocator, command_buffer_allocator, recreate_swapchain: false, previous_frame_end, window, sampler, texture, uniform_buffer, }) } fn resize(&mut self) { self.recreate_swapchain = true } fn draw(&mut self) -> Result<(), Box> { let image_extent: [u32; 2] = self.window.inner_size().into(); if image_extent.contains(&0) { return Ok(()); // screen size is 0 } self.previous_frame_end.as_mut().unwrap().cleanup_finished(); if self.recreate_swapchain { let (new_swapchain, new_images) = self .swapchain .recreate(SwapchainCreateInfo { image_extent, ..self.swapchain.create_info() }) .expect("failed to recreate swapchain"); self.swapchain = new_swapchain; self.framebuffers = get_framebuffers(&new_images, self.render_pass.clone(), &mut self.viewport); let layout = self.pipeline.layout().set_layouts()[0].clone(); self.desc_set = get_descriptor_set( self.descriptor_set_allocator.clone(), layout, self.swapchain.clone(), self.sampler.clone(), self.texture.clone(), &self.uniform_buffer, )?; self.recreate_swapchain = false; } let (image_index, suboptimal, acquire_future) = match acquire_next_image(self.swapchain.clone(), None).map_err(Validated::unwrap) { Ok(r) => r, Err(VulkanError::OutOfDate) => { self.recreate_swapchain = true; println!("swapchain out of date"); return Ok(()); } Err(e) => panic!("failed to acquire next image: {e}"), }; if suboptimal { self.recreate_swapchain = true; } let vertex_buffer = create_vertex_buffer( self.memory_allocator.clone(), [ My2DVertex { position: [-0.5, -0.5], }, My2DVertex { position: [-0.5, 0.5], }, My2DVertex { position: [0.5, -0.5], }, My2DVertex { position: [0.5, 0.5], }, ], ); let mut builder = AutoCommandBufferBuilder::primary( &self.command_buffer_allocator.clone(), self.queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) .unwrap(); builder .begin_render_pass( RenderPassBeginInfo { clear_values: vec![Some([1.0, 1.0, 1.0, 1.0].into())], ..RenderPassBeginInfo::framebuffer( self.framebuffers[image_index as usize].clone(), ) }, SubpassBeginInfo { contents: SubpassContents::Inline, ..Default::default() }, ) .unwrap() .set_viewport(0, [self.viewport.clone()].into_iter().collect()) .unwrap() .bind_pipeline_graphics(self.pipeline.clone()) .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, self.pipeline.layout().clone(), 0, self.desc_set.clone(), ) .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) .unwrap() .draw(vertex_buffer.len() as u32, 1, 0, 0) .unwrap() .end_render_pass(Default::default()) .unwrap(); let command_buffer = builder.build().unwrap(); let future = self .previous_frame_end .take() .unwrap() .join(acquire_future) .then_execute(self.queue.clone(), command_buffer) .unwrap() .then_swapchain_present( self.queue.clone(), SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index), ) .then_signal_fence_and_flush(); match future.map_err(Validated::unwrap) { Ok(future) => { self.previous_frame_end = Some(future.boxed()); } Err(VulkanError::OutOfDate) => { println!("swapchain out of date"); self.recreate_swapchain = true; self.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { panic!("failed to flush future: {e}"); // self.previous_frame_end = Some(sync::now(device.clone()).boxed()); } } Ok(()) } } fn get_framebuffers( images: &[Arc], render_pass: Arc, viewport: &mut Viewport, ) -> Vec> { let extent = images[0].extent(); viewport.extent = [extent[0] as f32, extent[1] as f32]; images .iter() .map(|image| { let view = ImageView::new_default(image.clone()).unwrap(); Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { attachments: vec![view], ..Default::default() }, ) .unwrap() }) .collect::>() } mod vs { vulkano_shaders::shader! { ty: "vertex", src: r" #version 450 layout(location = 0) in vec2 position; layout(location = 0) out vec2 tex_coords; layout(set = 0, binding = 0) uniform Data { mat4 world; mat4 view; mat4 proj; } uniforms; void main() { mat4 worldview = uniforms.view * uniforms.world; gl_Position = uniforms.proj * worldview * vec4(position, 0.0, 1.0); tex_coords = position + vec2(0.5); } ", } } mod fs { vulkano_shaders::shader! { ty: "fragment", src: r" #version 450 layout(location = 0) in vec2 tex_coords; layout(location = 0) out vec4 f_color; layout(set = 0, binding = 1) uniform sampler s; layout(set = 0, binding = 2) uniform texture2D tex; void main() { f_color = texture(sampler2D(tex, s), tex_coords); } ", } } #[derive(BufferContents, Vertex)] #[repr(C)] pub struct My2DVertex { #[format(R32G32_SFLOAT)] position: [f32; 2], } fn create_vertex_buffer( memory_allocator: Arc, vertices: [My2DVertex; 4], ) -> Subbuffer<[My2DVertex]> { 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, ) .unwrap(); vertex_buffer } fn get_render_pass(device: Arc, swapchain: &Arc) -> Arc { vulkano::single_pass_renderpass!( device, attachments: { color: { // Set the format the same as the swapchain. format: swapchain.image_format(), samples: 1, load_op: Clear, store_op: Store, }, }, pass: { color: [color], depth_stencil: {}, }, ) .unwrap() } fn get_pipeline( device: Arc, render_pass: Arc, ) -> Result> { let vs = vs::load(device.clone())?.entry_point("main").unwrap(); let fs = fs::load(device.clone())?.entry_point("main").unwrap(); let vertex_input_state = My2DVertex::per_vertex().definition(&vs.info().input_interface)?; 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())?, )?; let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); Ok(GraphicsPipeline::new( device.clone(), None, GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), vertex_input_state: Some(vertex_input_state), input_assembly_state: Some(InputAssemblyState { topology: PrimitiveTopology::TriangleStrip, ..Default::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::alpha()), ..Default::default() }, )), dynamic_state: [DynamicState::Viewport].into_iter().collect(), subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) }, )?) } fn get_descriptor_set( descriptor_set_allocator: Arc, layout: Arc, swapchain: Arc, sampler: Arc, texture: Arc, uniform_buffer: &SubbufferAllocator, ) -> Result> { let uniform_buffer_subbuffer = { let aspect_ratio = swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; let proj = Mat4::orthographic_rh_gl(-aspect_ratio, aspect_ratio, -1.0, 1.0, -1.0, 1.0); let view = Mat4::IDENTITY; let translation = Vec3::new(0.0, 0.0, 0.0); let scale = Vec3::new(1.0, 1.0, 1.0); let world = Mat4::from_scale_rotation_translation(scale, Quat::IDENTITY, translation); let uniform_data = vs::Data { world: world.to_cols_array_2d(), view: view.to_cols_array_2d(), proj: proj.to_cols_array_2d(), }; let subbuffer = uniform_buffer.allocate_sized().unwrap(); *subbuffer.write().unwrap() = uniform_data; subbuffer }; Ok(PersistentDescriptorSet::new( &descriptor_set_allocator, layout, [ WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer), WriteDescriptorSet::sampler(1, sampler), WriteDescriptorSet::image_view(2, texture), ], [], )?) } ```

Issue

If i use attached image then it corrupts it and doesnt display it correctly, for square images it works always test

minus1ms commented 3 months ago

i forgot to mention, i use latest winit version, but it shouldnt be an issue

joxan2137 commented 3 months ago

kurwa ale wasi

marc0246 commented 3 months ago

The image you attachted is RGB, not RGBA.

-            let decoder = png::Decoder::new(png_bytes);
+            let mut decoder = png::Decoder::new(png_bytes);
+            decoder.set_transformations(png::Transformations::ALPHA);