diff --git a/librashader-runtime-vk/src/draw_quad.rs b/librashader-runtime-vk/src/draw_quad.rs index ffe6861..eaea5d7 100644 --- a/librashader-runtime-vk/src/draw_quad.rs +++ b/librashader-runtime-vk/src/draw_quad.rs @@ -3,7 +3,7 @@ use crate::vulkan_primitives::VulkanBuffer; use ash::vk; #[rustfmt::skip] -static VBO_OFFSCREEN: &[f32; 16] = &[ +pub(crate) static VBO_OFFSCREEN: &[f32; 16] = &[ // Offscreen -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 1.0, @@ -12,7 +12,7 @@ static VBO_OFFSCREEN: &[f32; 16] = &[ ]; #[rustfmt::skip] -static VBO_DEFAULT_FINAL: &[f32; 16] = &[ +pub(crate) static VBO_DEFAULT_FINAL: &[f32; 16] = &[ // Final 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, diff --git a/librashader-runtime-vk/src/filter_chain.rs b/librashader-runtime-vk/src/filter_chain.rs index 04fb218..8613a2b 100644 --- a/librashader-runtime-vk/src/filter_chain.rs +++ b/librashader-runtime-vk/src/filter_chain.rs @@ -1,13 +1,13 @@ -use crate::error; +use crate::{error, util}; use crate::filter_pass::FilterPass; use crate::luts::LutTexture; use crate::samplers::SamplerSet; -use crate::texture::VulkanImage; +use crate::texture::{OwnedTexture, Texture, VulkanImage}; use crate::ubo_ring::VkUboRing; use crate::vulkan_state::VulkanGraphicsPipeline; use ash::vk::{CommandPoolCreateFlags, PFN_vkGetInstanceProcAddr, Queue, StaticFn}; use ash::{vk, Device}; -use librashader_common::ImageFormat; +use librashader_common::{ImageFormat, Size}; use librashader_preprocess::ShaderSource; use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig}; use librashader_reflect::back::targets::SpirV; @@ -22,6 +22,9 @@ use librashader_runtime::uniforms::UniformStorage; use rustc_hash::FxHashMap; use std::error::Error; use std::path::Path; +use crate::draw_quad::{VBO_DEFAULT_FINAL, VBO_OFFSCREEN}; +use crate::framebuffer::OutputFramebuffer; +use crate::rendertarget::RenderTarget; pub struct Vulkan { // physical_device: vk::PhysicalDevice, @@ -124,7 +127,7 @@ pub struct FilterChainVulkan { pub(crate) common: FilterCommon, pub(crate) passes: Box<[FilterPass]>, pub(crate) vulkan: Vulkan, - // pub(crate) output_framebuffers: Box<[OwnedFramebuffer]>, + pub(crate) output_framebuffers: Box<[OwnedTexture]>, // pub(crate) feedback_framebuffers: Box<[OwnedFramebuffer]>, // pub(crate) history_framebuffers: VecDeque, // pub(crate) draw_quad: DrawQuad, @@ -172,6 +175,18 @@ impl FilterChainVulkan { let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?; let samplers = SamplerSet::new(&device.device)?; + let mut output_framebuffers = Vec::new(); + output_framebuffers.resize_with(filters.len(), || { + OwnedTexture::new( + &device, + Size::new(1, 1), + ImageFormat::R8G8B8A8Unorm, + 1 + ) + }); + + let output_framebuffers: error::Result> = output_framebuffers.into_iter().collect(); + eprintln!("filters initialized ok."); Ok(FilterChainVulkan { common: FilterCommon { @@ -188,6 +203,7 @@ impl FilterChainVulkan { }, passes: filters, vulkan: device, + output_framebuffers: output_framebuffers?.into_boxed_slice(), }) } @@ -379,16 +395,87 @@ impl FilterChainVulkan { count: usize, viewport: &vk::Viewport, input: &VulkanImage, + cmd: vk::CommandBuffer, options: Option<()>, - command_buffer: vk::CommandBuffer, ) -> error::Result<()> { // limit number of passes to those enabled. let passes = &mut self.passes[0..self.common.config.passes_enabled]; + unsafe { + // todo: see if we can find a less conservative transition, + // but this ensures that the image is rendered at least + util::vulkan_image_layout_transition_levels( + &self.vulkan.device, + cmd, + input.image, + 1, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::GENERAL, + vk::AccessFlags::empty(), + vk::AccessFlags::SHADER_READ | vk::AccessFlags::COLOR_ATTACHMENT_READ, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::QUEUE_FAMILY_IGNORED, + vk::QUEUE_FAMILY_IGNORED + ); + } + + let original_image_view = unsafe { + let create_info = vk::ImageViewCreateInfo::builder() + .image(input.image) + .format(input.format) + .view_type(vk::ImageViewType::TYPE_2D) + .subresource_range(vk::ImageSubresourceRange::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .level_count(1) + .layer_count(1) + .build()) + .components(vk::ComponentMapping::builder() + .r(vk::ComponentSwizzle::R) + .g(vk::ComponentSwizzle::G) + .b(vk::ComponentSwizzle::B) + .a(vk::ComponentSwizzle::A) + .build()) + .build(); + + self.vulkan.device.create_image_view(&create_info, None)? + }; + + let filter = passes[0].config.filter; + let wrap_mode = passes[0].config.wrap_mode; + + let original = Texture { + image: input.clone(), + image_view: original_image_view, + wrap_mode, + filter_mode: filter, + mip_filter: filter, + }; + + let mut source = original.clone(); + + // rescale render buffers to ensure all bindings are valid. + for (index, pass) in passes.iter_mut().enumerate() { + self.output_framebuffers[index].scale( + pass.config.scaling.clone(), + pass.get_format(), + &viewport.into(), + &original, + &source, + )?; + } + // - // for (index, pass) in passes.iter_mut().enumerate() { - // pass.draw(index, &self.common, count as u32, 0, viewport, &Default::default(), &Texture {}, &Texture {}) - // } + for (index, pass) in passes.iter_mut().enumerate() { + let target = &self.output_framebuffers[index]; + // todo: use proper mode + let out = RenderTarget { + mvp: VBO_DEFAULT_FINAL, + output: OutputFramebuffer::new(&self.vulkan, &pass.graphics_pipeline.render_pass, target.image.image, target.image.size)?, + }; + + pass.draw(cmd, index, &self.common, count as u32, 0, viewport, &original, &source, &out)?; + } // unsafe { // self.vulkan.device.queue_submit(self.vulkan.queue, &[vk::SubmitInfo::builder() diff --git a/librashader-runtime-vk/src/filter_pass.rs b/librashader-runtime-vk/src/filter_pass.rs index fbd5474..9e65922 100644 --- a/librashader-runtime-vk/src/filter_pass.rs +++ b/librashader-runtime-vk/src/filter_pass.rs @@ -6,7 +6,7 @@ use crate::texture::Texture; use crate::ubo_ring::VkUboRing; use crate::vulkan_state::VulkanGraphicsPipeline; use ash::vk; -use librashader_common::Size; +use librashader_common::{ImageFormat, Size}; use librashader_preprocess::ShaderSource; use librashader_presets::ShaderPassConfig; use librashader_reflect::back::ShaderCompilerOutput; @@ -57,6 +57,16 @@ impl FilterPass { } } + pub fn get_format(&self) -> ImageFormat { + let mut fb_format = ImageFormat::R8G8B8A8Unorm; + if self.config.srgb_framebuffer { + fb_format = ImageFormat::R8G8B8A8Srgb; + } else if self.config.float_framebuffer { + fb_format = ImageFormat::R16G16B16A16Sfloat; + } + fb_format + } + pub(crate) fn draw( &mut self, cmd: vk::CommandBuffer, diff --git a/librashader-runtime-vk/src/framebuffer.rs b/librashader-runtime-vk/src/framebuffer.rs index cf26107..d020223 100644 --- a/librashader-runtime-vk/src/framebuffer.rs +++ b/librashader-runtime-vk/src/framebuffer.rs @@ -20,55 +20,56 @@ impl Drop for VulkanFramebuffer { } } } +// +// pub struct OwnedFramebuffer { +// pub size: Size, +// pub image: OwnedTexture, +// pub render_pass: VulkanRenderPass, +// pub framebuffer: VulkanFramebuffer, +// pub view: vk::ImageView, +// } +// +// impl OwnedFramebuffer { +// pub fn new( +// vulkan: &Vulkan, +// size: Size, +// render_pass: VulkanRenderPass, +// max_miplevels: u32, +// ) -> error::Result { +// let image = OwnedTexture::new(vulkan, size, render_pass.format, max_miplevels)?; +// let fb_view = image.create_texture_view()?; +// let framebuffer = unsafe { +// vulkan.device.create_framebuffer( +// &vk::FramebufferCreateInfo::builder() +// .render_pass(render_pass.handle) +// .attachments(&[image.image_view]) +// .width(image.image.size.width) +// .height(image.image.size.height) +// .layers(1) +// .build(), +// None, +// )? +// }; +// +// Ok(OwnedFramebuffer { +// size, +// image, +// view: fb_view, +// render_pass, +// framebuffer: VulkanFramebuffer { +// device: vulkan.device.clone(), +// framebuffer, +// }, +// }) +// } +// } -pub struct OwnedFramebuffer { - pub size: Size, - pub image: OwnedTexture, - pub render_pass: VulkanRenderPass, - pub framebuffer: VulkanFramebuffer, - pub view: vk::ImageView, -} - -impl OwnedFramebuffer { - pub fn new( - vulkan: &Vulkan, - size: Size, - render_pass: VulkanRenderPass, - max_miplevels: u32, - ) -> error::Result { - let image = OwnedTexture::new(vulkan, size, render_pass.format, max_miplevels)?; - let fb_view = image.create_texture_view()?; - let framebuffer = unsafe { - vulkan.device.create_framebuffer( - &vk::FramebufferCreateInfo::builder() - .render_pass(render_pass.handle) - .attachments(&[image.image_view]) - .width(image.image.size.width) - .height(image.image.size.height) - .layers(1) - .build(), - None, - )? - }; - - Ok(OwnedFramebuffer { - size, - image, - view: fb_view, - render_pass, - framebuffer: VulkanFramebuffer { - device: vulkan.device.clone(), - framebuffer, - }, - }) - } -} - -#[derive(Debug, Clone)] +#[derive(Clone)] pub(crate) struct OutputFramebuffer { pub framebuffer: vk::Framebuffer, pub size: Size, - pub viewport: vk::Viewport, + device: ash::Device, + image_view: vk::ImageView, } // @@ -81,66 +82,77 @@ pub(crate) struct OutputFramebuffer { // pub image_view: vk::ImageView, // } // -// impl<'a> OutputFramebuffer<'a> { -// pub fn new(vulkan: &Vulkan, render_pass: &'a VulkanRenderPass, image: vk::Image, size: Size) -> error::Result> { -// let image_subresource = vk::ImageSubresourceRange::builder() -// .base_mip_level(0) -// .base_array_layer(0) -// .level_count(1) -// .layer_count(1) -// .aspect_mask(ImageAspectFlags::COLOR) -// .build(); -// -// let swizzle_components = vk::ComponentMapping::builder() -// .r(vk::ComponentSwizzle::R) -// .g(vk::ComponentSwizzle::G) -// .b(vk::ComponentSwizzle::B) -// .a(vk::ComponentSwizzle::A) -// .build(); -// -// let mut view_info = vk::ImageViewCreateInfo::builder() -// .view_type(ImageViewType::TYPE_2D) -// .format(render_pass.format.into()) -// .image(image.clone()) -// .subresource_range(image_subresource) -// .components(swizzle_components) -// .build(); -// -// let image_view = unsafe { vulkan.device.create_image_view(&view_info, None)? }; -// -// let framebuffer = unsafe { -// vulkan.device.create_framebuffer( -// &vk::FramebufferCreateInfo::builder() -// .render_pass(render_pass.handle) -// .attachments(&[image_view]) -// .width(size.width) -// .height(size.height) -// .layers(1) -// .build(), -// None, -// )? -// }; -// -// Ok(OutputFramebuffer { -// device: vulkan.device.clone(), -// image, -// image_view, -// render_pass, -// size, -// handle: framebuffer, -// }) -// } -// -// pub fn get_renderpass_begin_info(&self, area: vk::Rect2D, clear: Option<&[vk::ClearValue]>) -> vk::RenderPassBeginInfo { -// let mut builder = vk::RenderPassBeginInfo::builder() -// .render_pass(self.render_pass.handle) -// .framebuffer(self.handle) -// .render_area(area); -// -// if let Some(clear) = clear { -// builder = builder.clear_values(clear) -// } -// -// builder.build() -// } -// } +impl OutputFramebuffer { + pub fn new(vulkan: &Vulkan, render_pass: &VulkanRenderPass, image: vk::Image, size: Size) -> error::Result { + let image_subresource = vk::ImageSubresourceRange::builder() + .base_mip_level(0) + .base_array_layer(0) + .level_count(1) + .layer_count(1) + .aspect_mask(ImageAspectFlags::COLOR) + .build(); + + let swizzle_components = vk::ComponentMapping::builder() + .r(vk::ComponentSwizzle::R) + .g(vk::ComponentSwizzle::G) + .b(vk::ComponentSwizzle::B) + .a(vk::ComponentSwizzle::A) + .build(); + + let mut view_info = vk::ImageViewCreateInfo::builder() + .view_type(ImageViewType::TYPE_2D) + .format(render_pass.format.into()) + .image(image.clone()) + .subresource_range(image_subresource) + .components(swizzle_components) + .build(); + + let image_view = unsafe { vulkan.device.create_image_view(&view_info, None)? }; + + let framebuffer = unsafe { + vulkan.device.create_framebuffer( + &vk::FramebufferCreateInfo::builder() + .render_pass(render_pass.handle) + .attachments(&[image_view]) + .width(size.width) + .height(size.height) + .layers(1) + .build(), + None, + )? + }; + + Ok(OutputFramebuffer { + device: vulkan.device.clone(), + size, + framebuffer, + image_view, + }) + } + + // pub fn get_renderpass_begin_info(&self, area: vk::Rect2D, clear: Option<&[vk::ClearValue]>) -> vk::RenderPassBeginInfo { + // let mut builder = vk::RenderPassBeginInfo::builder() + // .render_pass(self.render_pass.handle) + // .framebuffer(self.handle) + // .render_area(area); + // + // if let Some(clear) = clear { + // builder = builder.clear_values(clear) + // } + // + // builder.build() + // } +} + +impl Drop for OutputFramebuffer { + fn drop(&mut self) { + unsafe { + if self.framebuffer != vk::Framebuffer::null() { + self.device.destroy_framebuffer(self.framebuffer, None); + } + if self.image_view != vk::ImageView::null() { + self.device.destroy_image_view(self.image_view, None); + } + } + } +} \ No newline at end of file diff --git a/librashader-runtime-vk/src/hello_triangle/mod.rs b/librashader-runtime-vk/src/hello_triangle/mod.rs index de4f05c..72b4c44 100644 --- a/librashader-runtime-vk/src/hello_triangle/mod.rs +++ b/librashader-runtime-vk/src/hello_triangle/mod.rs @@ -22,6 +22,7 @@ use ash::vk::RenderingInfo; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; use winit::platform::windows::EventLoopBuilderExtWindows; +use crate::texture::VulkanImage; // Constants const WINDOW_TITLE: &'static str = "librashader Vulkan"; @@ -173,6 +174,20 @@ impl VulkanWindow { Self::record_command_buffer(vulkan, framebuffer, cmd); + filter.frame(0, &vk::Viewport { + x: 0.0, + y: 0.0, + width: vulkan.swapchain.extent.width as f32, + height: vulkan.swapchain.extent.height as f32, + min_depth: 0.0, + max_depth: 1.0, + }, &VulkanImage { + size: vulkan.swapchain.extent.into(), + image: framebuffer_image, + format: vulkan.swapchain.format.format, + }, cmd, None) + .unwrap(); + util::vulkan_image_layout_transition_levels( &vulkan.base.device, cmd, diff --git a/librashader-runtime-vk/src/rendertarget.rs b/librashader-runtime-vk/src/rendertarget.rs index 23b8959..d0f7c98 100644 --- a/librashader-runtime-vk/src/rendertarget.rs +++ b/librashader-runtime-vk/src/rendertarget.rs @@ -1,7 +1,7 @@ use crate::framebuffer::OutputFramebuffer; use ash::vk; -#[derive(Debug, Clone)] +#[derive(Clone)] pub(crate) struct RenderTarget<'a> { pub mvp: &'a [f32; 16], pub output: OutputFramebuffer, diff --git a/librashader-runtime-vk/src/texture.rs b/librashader-runtime-vk/src/texture.rs index ac1ccb6..a316e80 100644 --- a/librashader-runtime-vk/src/texture.rs +++ b/librashader-runtime-vk/src/texture.rs @@ -8,10 +8,12 @@ use ash::vk::{ SampleCountFlags, SharingMode, }; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; -use librashader_runtime::scaling::MipmapSize; +use librashader_presets::Scale2D; +use librashader_runtime::scaling::{MipmapSize, ViewportSize}; pub struct OwnedTexture { pub device: ash::Device, + pub mem_props: vk::PhysicalDeviceMemoryProperties, pub image_view: vk::ImageView, pub image: VulkanImage, pub memory: VulkanImageMemory, @@ -19,8 +21,9 @@ pub struct OwnedTexture { } impl OwnedTexture { - pub fn new( - vulkan: &Vulkan, + fn new_internal( + device: ash::Device, + mem_props: vk::PhysicalDeviceMemoryProperties, size: Size, format: ImageFormat, max_miplevels: u32, @@ -43,20 +46,20 @@ impl OwnedTexture { .initial_layout(ImageLayout::UNDEFINED) .build(); - let image = unsafe { vulkan.device.create_image(&image_create_info, None)? }; - let mem_reqs = unsafe { vulkan.device.get_image_memory_requirements(image.clone()) }; + let image = unsafe { device.create_image(&image_create_info, None)? }; + let mem_reqs = unsafe { device.get_image_memory_requirements(image.clone()) }; let alloc_info = vk::MemoryAllocateInfo::builder() .allocation_size(mem_reqs.size) .memory_type_index(find_vulkan_memory_type( - &vulkan.memory_properties, + &mem_props, mem_reqs.memory_type_bits, vk::MemoryPropertyFlags::DEVICE_LOCAL, )) .build(); // todo: optimize by reusing existing memory. - let memory = VulkanImageMemory::new(&vulkan.device, &alloc_info)?; + let memory = VulkanImageMemory::new(&device, &alloc_info)?; memory.bind(&image)?; let image_subresource = vk::ImageSubresourceRange::builder() @@ -82,10 +85,11 @@ impl OwnedTexture { .components(swizzle_components) .build(); - let image_view = unsafe { vulkan.device.create_image_view(&view_info, None)? }; + let image_view = unsafe { device.create_image_view(&view_info, None)? }; Ok(OwnedTexture { - device: vulkan.device.clone(), + device, + mem_props, image_view, image: VulkanImage { image, @@ -97,6 +101,40 @@ impl OwnedTexture { }) } + pub fn new( + vulkan: &Vulkan, + size: Size, + format: ImageFormat, + max_miplevels: u32, + ) -> error::Result { + Self::new_internal(vulkan.device.clone(), vulkan.memory_properties, size, format, max_miplevels) + } + + pub(crate) fn scale( + &mut self, + scaling: Scale2D, + format: ImageFormat, + viewport_size: &Size, + _original: &Texture, + source: &Texture, + ) -> error::Result> { + + + let size = source.image.size.scale_viewport(scaling, *viewport_size); + + if self.image.size != size { + let mut new = OwnedTexture::new_internal(self.device.clone(), self.mem_props, size, if format == ImageFormat::Unknown { + ImageFormat::R8G8B8A8Unorm + } else { + format + }, self.max_miplevels)?; + + std::mem::swap(self, &mut new) + } + Ok(size) + } + + pub fn create_texture_view(&self) -> error::Result { let image_subresource = vk::ImageSubresourceRange::builder() .base_mip_level(0) @@ -139,12 +177,14 @@ impl Drop for OwnedTexture { } } +#[derive(Clone)] pub struct VulkanImage { pub size: Size, pub image: vk::Image, pub format: vk::Format, } +#[derive(Clone)] pub struct Texture { pub image: VulkanImage, pub image_view: vk::ImageView,