vk: implement load luts
This commit is contained in:
parent
d5f0bece0b
commit
fe573618f8
|
@ -1,5 +1,5 @@
|
|||
use ash::vk;
|
||||
use crate::ImageFormat;
|
||||
use crate::{ImageFormat, Size};
|
||||
|
||||
impl From<ImageFormat> for vk::Format {
|
||||
fn from(format: ImageFormat) -> Self {
|
||||
|
@ -76,4 +76,14 @@ impl From<vk::Format> for ImageFormat {
|
|||
_ => ImageFormat::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Size<u32>> for vk::Extent3D {
|
||||
fn from(value: Size<u32>) -> Self {
|
||||
vk::Extent3D {
|
||||
width: value.width,
|
||||
height: value.height,
|
||||
depth: 1,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use windows::core::Interface;
|
|||
use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
|
||||
use windows::Win32::Graphics::Direct3D11::{ID3D11Device, ID3D11RenderTargetView, ID3D11ShaderResourceView, ID3D11Texture2D, D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CPU_ACCESS_WRITE, D3D11_FORMAT_SUPPORT_RENDER_TARGET, D3D11_FORMAT_SUPPORT_SHADER_SAMPLE, D3D11_FORMAT_SUPPORT_TEXTURE2D, D3D11_RENDER_TARGET_VIEW_DESC, D3D11_RENDER_TARGET_VIEW_DESC_0, D3D11_RTV_DIMENSION_TEXTURE2D, D3D11_SHADER_RESOURCE_VIEW_DESC, D3D11_SHADER_RESOURCE_VIEW_DESC_0, D3D11_TEX2D_RTV, D3D11_TEX2D_SRV, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11_VIEWPORT, D3D11_RESOURCE_MISC_GENERATE_MIPS, ID3D11DeviceContext, D3D11_BOX};
|
||||
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
|
||||
use librashader_runtime::scaling::ViewportSize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OwnedFramebuffer {
|
||||
|
@ -60,7 +61,7 @@ impl OwnedFramebuffer {
|
|||
return Ok(self.size);
|
||||
}
|
||||
|
||||
let size = librashader_runtime::scaling::scale(scaling, source.view.size, *viewport_size);
|
||||
let size = source.view.size.scale_viewport(scaling, *viewport_size);
|
||||
|
||||
if self.size != size {
|
||||
self.size = size;
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::viewport::Viewport;
|
|||
use gl::types::{GLenum, GLint, GLsizei, GLuint};
|
||||
use librashader_common::{ImageFormat, Size};
|
||||
use librashader_presets::Scale2D;
|
||||
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Gl3Framebuffer;
|
||||
|
@ -63,8 +64,7 @@ impl FramebufferInterface for Gl3Framebuffer {
|
|||
return Ok(fb.size);
|
||||
}
|
||||
|
||||
let size =
|
||||
librashader_runtime::scaling::scale(scaling, source.image.size, viewport.output.size);
|
||||
let size = source.image.size.scale_viewport(scaling, viewport.output.size);
|
||||
|
||||
if fb.size != size {
|
||||
fb.size = size;
|
||||
|
@ -196,7 +196,7 @@ impl FramebufferInterface for Gl3Framebuffer {
|
|||
size.height = 1;
|
||||
}
|
||||
|
||||
fb.mip_levels = librashader_runtime::scaling::calc_miplevel(size);
|
||||
fb.mip_levels = size.calculate_miplevels();
|
||||
if fb.mip_levels > fb.max_levels {
|
||||
fb.mip_levels = fb.max_levels;
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ impl FramebufferInterface for Gl3Framebuffer {
|
|||
gl::GenTextures(1, &mut fb.image);
|
||||
gl::BindTexture(gl::TEXTURE_2D, fb.image);
|
||||
|
||||
fb.mip_levels = librashader_runtime::scaling::calc_miplevel(size);
|
||||
fb.mip_levels = size.calculate_miplevels();
|
||||
if fb.mip_levels > fb.max_levels {
|
||||
fb.mip_levels = fb.max_levels;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use librashader_runtime::image::{Image, UVDirection};
|
|||
use librashader_common::Size;
|
||||
use librashader_presets::TextureConfig;
|
||||
use rustc_hash::FxHashMap;
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
|
||||
pub struct Gl3LutLoad;
|
||||
impl LoadLut for Gl3LutLoad {
|
||||
|
@ -19,9 +20,9 @@ impl LoadLut for Gl3LutLoad {
|
|||
};
|
||||
|
||||
for (index, texture) in textures.iter().enumerate() {
|
||||
let image = Image::load(&texture.path, UVDirection::BottomLeft)?;
|
||||
let image: Image = Image::load(&texture.path, UVDirection::BottomLeft)?;
|
||||
let levels = if texture.mipmap {
|
||||
librashader_runtime::scaling::calc_miplevel(image.size)
|
||||
image.size.calculate_miplevels()
|
||||
} else {
|
||||
1u32
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::viewport::Viewport;
|
|||
use gl::types::{GLenum, GLint, GLsizei, GLuint};
|
||||
use librashader_common::{ImageFormat, Size};
|
||||
use librashader_presets::Scale2D;
|
||||
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Gl46Framebuffer;
|
||||
|
@ -61,8 +62,7 @@ impl FramebufferInterface for Gl46Framebuffer {
|
|||
return Ok(fb.size);
|
||||
}
|
||||
|
||||
let size =
|
||||
librashader_runtime::scaling::scale(scaling, source.image.size, viewport.output.size);
|
||||
let size = source.image.size.scale_viewport(scaling, viewport.output.size);
|
||||
|
||||
if fb.size != size {
|
||||
fb.size = size;
|
||||
|
@ -146,7 +146,7 @@ impl FramebufferInterface for Gl46Framebuffer {
|
|||
size.height = 1;
|
||||
}
|
||||
|
||||
fb.mip_levels = librashader_runtime::scaling::calc_miplevel(size);
|
||||
fb.mip_levels = size.calculate_miplevels();
|
||||
if fb.mip_levels > fb.max_levels {
|
||||
fb.mip_levels = fb.max_levels;
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ impl FramebufferInterface for Gl46Framebuffer {
|
|||
gl::DeleteTextures(1, &fb.image);
|
||||
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut fb.image);
|
||||
|
||||
fb.mip_levels = librashader_runtime::scaling::calc_miplevel(size);
|
||||
fb.mip_levels = size.calculate_miplevels();
|
||||
if fb.mip_levels > fb.max_levels {
|
||||
fb.mip_levels = fb.max_levels;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use librashader_runtime::image::{Image, UVDirection};
|
|||
use librashader_common::Size;
|
||||
use librashader_presets::TextureConfig;
|
||||
use rustc_hash::FxHashMap;
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
|
||||
pub struct Gl46LutLoad;
|
||||
impl LoadLut for Gl46LutLoad {
|
||||
|
@ -23,9 +24,9 @@ impl LoadLut for Gl46LutLoad {
|
|||
}
|
||||
|
||||
for (index, texture) in textures.iter().enumerate() {
|
||||
let image = Image::load(&texture.path, UVDirection::BottomLeft)?;
|
||||
let image: Image = Image::load(&texture.path, UVDirection::BottomLeft)?;
|
||||
let levels = if texture.mipmap {
|
||||
librashader_runtime::scaling::calc_miplevel(image.size)
|
||||
image.size.calculate_miplevels()
|
||||
} else {
|
||||
1u32
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::error::Error;
|
||||
use std::path::Path;
|
||||
use ash::vk;
|
||||
use ash::vk::{PFN_vkGetInstanceProcAddr, StaticFn};
|
||||
use ash::{Device, vk};
|
||||
use ash::vk::{CommandPoolCreateFlags, PFN_vkGetInstanceProcAddr, Queue, StaticFn};
|
||||
use rustc_hash::FxHashMap;
|
||||
use librashader_common::ImageFormat;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
|
@ -11,18 +11,21 @@ use librashader_reflect::back::targets::SpirV;
|
|||
use librashader_reflect::front::shaderc::GlslangCompilation;
|
||||
use librashader_reflect::reflect::ReflectShader;
|
||||
use librashader_reflect::reflect::semantics::{Semantic, ShaderSemantics, TextureSemantics, UniformBinding, UniformSemantic, UniqueSemantics};
|
||||
use librashader_runtime::image::{Image, UVDirection};
|
||||
use librashader_runtime::uniforms::UniformStorage;
|
||||
use crate::error;
|
||||
use crate::filter_pass::FilterPass;
|
||||
use crate::luts::LutTexture;
|
||||
use crate::vulkan_state::VulkanGraphicsPipeline;
|
||||
|
||||
pub struct Vulkan {
|
||||
physical_device: vk::PhysicalDevice,
|
||||
device: ash::Device,
|
||||
instance: ash::Instance,
|
||||
// physical_device: vk::PhysicalDevice,
|
||||
pub(crate) device: ash::Device,
|
||||
// instance: ash::Instance,
|
||||
queue: vk::Queue,
|
||||
command_pool: vk::CommandPool,
|
||||
pipelines: vk::PipelineCache,
|
||||
pipeline_cache: vk::PipelineCache,
|
||||
pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties,
|
||||
}
|
||||
|
||||
type ShaderPassMeta = (
|
||||
|
@ -36,7 +39,7 @@ type ShaderPassMeta = (
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct VulkanInfo<'a> {
|
||||
physical_device: &'a vk::PhysicalDevice,
|
||||
// physical_device: &'a vk::PhysicalDevice,
|
||||
device: &'a vk::Device,
|
||||
instance: &'a vk::Instance,
|
||||
queue: &'a vk::Queue,
|
||||
|
@ -44,13 +47,65 @@ pub struct VulkanInfo<'a> {
|
|||
get_instance_proc_addr: PFN_vkGetInstanceProcAddr
|
||||
}
|
||||
|
||||
impl From<VulkanInfo<'_>> for ash::Device {
|
||||
fn from(vulkan: VulkanInfo) -> Self {
|
||||
impl TryFrom<VulkanInfo<'_>> for Vulkan {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn try_from(vulkan: VulkanInfo) -> Result<Self, Box<dyn Error>> {
|
||||
unsafe {
|
||||
let instance = ash::Instance::load(&StaticFn {
|
||||
get_instance_proc_addr: vulkan.get_instance_proc_addr,
|
||||
}, vulkan.instance.clone());
|
||||
ash::Device::load(instance.fp_v1_0(), vulkan.device.clone())
|
||||
|
||||
let device = ash::Device::load(instance.fp_v1_0(), vulkan.device.clone());
|
||||
|
||||
let pipeline_cache = unsafe {
|
||||
device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(),
|
||||
None)?
|
||||
};
|
||||
|
||||
let command_pool = unsafe {
|
||||
device.create_command_pool(&vk::CommandPoolCreateInfo::builder()
|
||||
.flags(CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
|
||||
.build(), None)?
|
||||
};
|
||||
|
||||
Ok(Vulkan {
|
||||
device,
|
||||
// instance,
|
||||
queue: vulkan.queue.clone(),
|
||||
command_pool,
|
||||
pipeline_cache,
|
||||
memory_properties: vulkan.memory_properties.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(ash::Device, vk::Queue, vk::PhysicalDeviceMemoryProperties)> for Vulkan {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn try_from(value: (Device, Queue, vk::PhysicalDeviceMemoryProperties)) -> error::Result<Self> {
|
||||
unsafe {
|
||||
let device = value.0;
|
||||
|
||||
let pipeline_cache = unsafe {
|
||||
device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(),
|
||||
None)?
|
||||
};
|
||||
|
||||
let command_pool = unsafe {
|
||||
device.create_command_pool(&vk::CommandPoolCreateInfo::builder()
|
||||
.flags(CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
|
||||
.build(), None)?
|
||||
};
|
||||
|
||||
Ok(Vulkan {
|
||||
device,
|
||||
queue: value.1,
|
||||
command_pool,
|
||||
pipeline_cache,
|
||||
memory_properties: value.2
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +126,7 @@ pub(crate) struct FilterCommon {
|
|||
// pub feedback_textures: Box<[Option<Texture>]>,
|
||||
// pub history_textures: Box<[Option<Texture>]>,
|
||||
// pub config: FilterMutable,
|
||||
luts: FxHashMap<usize, LutTexture>,
|
||||
}
|
||||
|
||||
pub type FilterChainOptionsVulkan = ();
|
||||
|
@ -78,7 +134,7 @@ pub type FilterChainOptionsVulkan = ();
|
|||
impl FilterChainVulkan {
|
||||
/// Load the shader preset at the given path into a filter chain.
|
||||
pub fn load_from_path(
|
||||
vulkan: impl Into<ash::Device>,
|
||||
vulkan: impl TryInto<Vulkan, Error=Box<dyn Error>>,
|
||||
path: impl AsRef<Path>,
|
||||
options: Option<&FilterChainOptionsVulkan>,
|
||||
) -> error::Result<FilterChainVulkan> {
|
||||
|
@ -88,19 +144,24 @@ impl FilterChainVulkan {
|
|||
}
|
||||
|
||||
pub fn load_from_preset(
|
||||
vulkan: impl Into<ash::Device>,
|
||||
vulkan: impl TryInto<Vulkan, Error=Box<dyn Error>>,
|
||||
preset: ShaderPreset,
|
||||
options: Option<&FilterChainOptionsVulkan>,
|
||||
) -> error::Result<FilterChainVulkan> {
|
||||
let (passes, semantics) = FilterChainVulkan::load_preset(preset.shaders, &preset.textures)?;
|
||||
let device = vulkan.into();
|
||||
let device = vulkan.try_into()?;
|
||||
|
||||
// initialize passes
|
||||
let filters = Self::init_passes(&device, passes, &semantics, 3)?;
|
||||
|
||||
let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?;
|
||||
|
||||
eprintln!("filters initialized ok.");
|
||||
Ok(FilterChainVulkan {
|
||||
common: FilterCommon {},
|
||||
passes: filters
|
||||
common: FilterCommon {
|
||||
luts
|
||||
},
|
||||
passes: filters,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -157,18 +218,13 @@ impl FilterChainVulkan {
|
|||
}
|
||||
|
||||
fn init_passes(
|
||||
device: &ash::Device,
|
||||
vulkan: &Vulkan,
|
||||
passes: Vec<ShaderPassMeta>,
|
||||
semantics: &ShaderSemantics,
|
||||
images: u32,
|
||||
) -> error::Result<Box<[FilterPass]>> {
|
||||
let mut filters = Vec::new();
|
||||
|
||||
let pipeline_cache = unsafe {
|
||||
device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(),
|
||||
None)?
|
||||
};
|
||||
|
||||
// initialize passes
|
||||
for (index, (config, mut source, mut reflect)) in passes.into_iter().enumerate() {
|
||||
let reflection = reflect.reflect(index, semantics)?;
|
||||
|
@ -206,9 +262,10 @@ impl FilterChainVulkan {
|
|||
source.format = ImageFormat::R8G8B8A8Unorm
|
||||
}
|
||||
|
||||
let graphics_pipeline = VulkanGraphicsPipeline::new(device,
|
||||
&pipeline_cache,
|
||||
let graphics_pipeline = VulkanGraphicsPipeline::new(&vulkan.device,
|
||||
&vulkan.pipeline_cache,
|
||||
&spirv_words, &reflection, source.format, images)?;
|
||||
|
||||
// shader_vulkan: 2026
|
||||
filters.push(FilterPass {
|
||||
compiled: spirv_words,
|
||||
|
@ -223,4 +280,51 @@ impl FilterChainVulkan {
|
|||
|
||||
Ok(filters.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn load_luts(
|
||||
vulkan: &Vulkan,
|
||||
textures: &[TextureConfig],
|
||||
) -> error::Result<FxHashMap<usize, LutTexture>> {
|
||||
let mut luts = FxHashMap::default();
|
||||
let command_buffer = unsafe {
|
||||
// panic safety: command buffer count = 1
|
||||
vulkan.device.allocate_command_buffers(&vk::CommandBufferAllocateInfo::builder()
|
||||
.command_pool(vulkan.command_pool)
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_buffer_count(1)
|
||||
.build())?[0]
|
||||
};
|
||||
|
||||
unsafe {
|
||||
vulkan.device.begin_command_buffer(command_buffer, &vk::CommandBufferBeginInfo::builder()
|
||||
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT)
|
||||
.build())?
|
||||
}
|
||||
|
||||
for (index, texture) in textures.iter().enumerate() {
|
||||
let image = Image::load(&texture.path, UVDirection::TopLeft)?;
|
||||
|
||||
let texture = LutTexture::new(
|
||||
vulkan,
|
||||
&command_buffer,
|
||||
image,
|
||||
texture,
|
||||
)?;
|
||||
luts.insert(index, texture);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
vulkan.device.end_command_buffer(command_buffer)?;
|
||||
|
||||
let buffers = [command_buffer];
|
||||
let submits = [vk::SubmitInfo::builder()
|
||||
.command_buffers(&buffers).build()];
|
||||
|
||||
vulkan.device.queue_submit(vulkan.queue, &submits, vk::Fence::null())?;
|
||||
vulkan.device.queue_wait_idle(vulkan.queue)?;
|
||||
vulkan.device.free_command_buffers(vulkan.command_pool, &buffers);
|
||||
}
|
||||
Ok(luts)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use ash::vk;
|
||||
use ash::vk::{Extent3D, ImageAspectFlags, ImageLayout, ImageTiling, ImageType, ImageUsageFlags, ImageViewType, SampleCountFlags, SharingMode};
|
||||
use librashader_common::Size;
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
use crate::error;
|
||||
use crate::renderpass::VulkanRenderPass;
|
||||
use crate::util::find_vulkan_memory_type;
|
||||
|
@ -64,12 +65,8 @@ impl Framebuffer {
|
|||
let image_create_info = vk::ImageCreateInfo::builder()
|
||||
.image_type(ImageType::TYPE_2D)
|
||||
.format(self.render_pass.format.into())
|
||||
.extent(Extent3D {
|
||||
width: self.size.width,
|
||||
height: self.size.height,
|
||||
depth: 1
|
||||
})
|
||||
.mip_levels(std::cmp::min(self.max_levels, librashader_runtime::scaling::calc_miplevel(self.size)))
|
||||
.extent(self.size.into())
|
||||
.mip_levels(std::cmp::min(self.max_levels, self.size.calculate_miplevels()))
|
||||
.array_layers(1)
|
||||
.samples(SampleCountFlags::TYPE_1)
|
||||
.tiling(ImageTiling::OPTIMAL)
|
||||
|
|
|
@ -256,7 +256,7 @@ impl ExampleBase {
|
|||
.application_version(0)
|
||||
.engine_name(app_name)
|
||||
.engine_version(0)
|
||||
.api_version(vk::make_api_version(0, 1, 0, 0));
|
||||
.api_version(vk::make_api_version(0, 1, 3, 0));
|
||||
|
||||
let create_flags = if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||
vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
|
||||
|
|
|
@ -11,6 +11,7 @@ mod vulkan_state;
|
|||
mod draw_quad;
|
||||
mod renderpass;
|
||||
mod vulkan_primitives;
|
||||
mod luts;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -19,12 +20,19 @@ mod tests {
|
|||
#[test]
|
||||
fn triangle_vk() {
|
||||
let base = hello_triangle::ExampleBase::new(900, 600);
|
||||
// let mut filter = FilterChainVulkan::load_from_path(
|
||||
// (base.device.clone(), base.present_queue.clone(), base.device_memory_properties.clone()),
|
||||
// "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
|
||||
// None
|
||||
// )
|
||||
|
||||
let mut filter = FilterChainVulkan::load_from_path(
|
||||
base.device.clone(),
|
||||
(base.device.clone(), base.present_queue.clone(), base.device_memory_properties.clone()),
|
||||
"../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
|
||||
None
|
||||
)
|
||||
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
|
||||
|
||||
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
|
||||
.unwrap();
|
||||
hello_triangle::main(base);
|
||||
}
|
||||
|
|
212
librashader-runtime-vk/src/luts.rs
Normal file
212
librashader-runtime-vk/src/luts.rs
Normal file
|
@ -0,0 +1,212 @@
|
|||
use ash::vk;
|
||||
use ash::vk::ImageSubresourceLayers;
|
||||
use glfw::Key::P;
|
||||
use librashader_common::{FilterMode, WrapMode};
|
||||
use librashader_presets::TextureConfig;
|
||||
use librashader_runtime::image::{BGRA8, Image};
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
use crate::filter_chain::Vulkan;
|
||||
use crate::{error, util};
|
||||
use crate::vulkan_primitives::{VulkanBuffer, VulkanImageMemory};
|
||||
|
||||
pub struct LutTexture {
|
||||
pub texture: vk::Image,
|
||||
pub texture_view: vk::ImageView,
|
||||
pub memory: VulkanImageMemory,
|
||||
pub staging: VulkanBuffer,
|
||||
pub filter_mode: FilterMode,
|
||||
pub wrap_mode: WrapMode,
|
||||
pub mipmap: bool,
|
||||
}
|
||||
|
||||
impl LutTexture {
|
||||
pub fn new(vulkan: &Vulkan, cmd: &vk::CommandBuffer, image: Image<BGRA8>, config: &TextureConfig) -> error::Result<LutTexture> {
|
||||
|
||||
// todo: might need to use bgra8
|
||||
let image_info = vk::ImageCreateInfo::builder()
|
||||
.image_type(vk::ImageType::TYPE_2D)
|
||||
.format(vk::Format::B8G8R8A8_UNORM)
|
||||
.extent(image.size.into())
|
||||
.mip_levels(if config.mipmap {
|
||||
image.size.calculate_miplevels()
|
||||
} else {
|
||||
1
|
||||
})
|
||||
.array_layers(1)
|
||||
.samples(vk::SampleCountFlags::TYPE_1)
|
||||
.tiling(vk::ImageTiling::OPTIMAL)
|
||||
.usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED)
|
||||
.build();
|
||||
|
||||
let texture = unsafe {
|
||||
vulkan.device.create_image(&image_info, None)?
|
||||
};
|
||||
|
||||
let memory = unsafe {
|
||||
let mem_reqs = vulkan.device.get_image_memory_requirements(texture.clone());
|
||||
let mem_type = util::find_vulkan_memory_type(&vulkan.memory_properties, mem_reqs.memory_type_bits, vk::MemoryPropertyFlags::DEVICE_LOCAL);
|
||||
crate::vulkan_primitives::VulkanImageMemory::new(&vulkan.device, &vk::MemoryAllocateInfo::builder()
|
||||
.memory_type_index(mem_type)
|
||||
.allocation_size(mem_reqs.size))?
|
||||
};
|
||||
|
||||
memory.bind(&texture)?;
|
||||
|
||||
let image_subresource = vk::ImageSubresourceRange::builder()
|
||||
.level_count(image_info.mip_levels)
|
||||
.layer_count(1)
|
||||
.aspect_mask(vk::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(vk::ImageViewType::TYPE_2D)
|
||||
.format(vk::Format::B8G8R8A8_UNORM)
|
||||
.image(texture.clone())
|
||||
.subresource_range(image_subresource)
|
||||
.components(swizzle_components)
|
||||
.build();
|
||||
|
||||
let texture_view = unsafe {
|
||||
vulkan.device.create_image_view(&view_info, None)?
|
||||
};
|
||||
|
||||
let mut staging = VulkanBuffer::new(&vulkan.device, &vulkan.memory_properties, vk::BufferUsageFlags::TRANSFER_SRC, image.bytes.len())?;
|
||||
unsafe {
|
||||
let mut handle = staging.map()?;
|
||||
handle.copy_from(&image.bytes)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
util::vulkan_image_layout_transition_levels(&vulkan.device, *cmd, texture,
|
||||
vk::REMAINING_MIP_LEVELS,
|
||||
vk::ImageLayout::UNDEFINED,
|
||||
if config.mipmap {
|
||||
vk::ImageLayout::GENERAL
|
||||
} else {
|
||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL
|
||||
},
|
||||
vk::AccessFlags::empty(),
|
||||
vk::AccessFlags::TRANSFER_WRITE,
|
||||
vk::PipelineStageFlags::TOP_OF_PIPE,
|
||||
vk::PipelineStageFlags::TRANSFER,
|
||||
vk::QUEUE_FAMILY_IGNORED,
|
||||
vk::QUEUE_FAMILY_IGNORED
|
||||
);
|
||||
|
||||
vulkan.device.cmd_copy_buffer_to_image(*cmd,
|
||||
staging.handle,
|
||||
texture,
|
||||
if config.mipmap { vk::ImageLayout::GENERAL } else { vk::ImageLayout::TRANSFER_DST_OPTIMAL },
|
||||
&[vk::BufferImageCopy::builder()
|
||||
.image_subresource(vk::ImageSubresourceLayers::builder()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.mip_level(0)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1)
|
||||
.build())
|
||||
.image_extent(image.size.into()).build()])
|
||||
|
||||
}
|
||||
|
||||
// generate mipmaps
|
||||
for level in 1..image_info.mip_levels {
|
||||
let source_size = image.size.scale_mipmap(level - 1);
|
||||
let target_size = image.size.scale_mipmap(level);
|
||||
|
||||
let src_offsets = [
|
||||
vk::Offset3D {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
vk::Offset3D {
|
||||
x: source_size.width as i32,
|
||||
y: source_size.height as i32,
|
||||
z: 1,
|
||||
},
|
||||
];
|
||||
|
||||
let dst_offsets = [
|
||||
vk::Offset3D {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
vk::Offset3D {
|
||||
x: target_size.width as i32,
|
||||
y: target_size.height as i32,
|
||||
z: 1,
|
||||
},
|
||||
];
|
||||
let src_subresource = ImageSubresourceLayers::builder()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.mip_level(level - 1)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1)
|
||||
.build();
|
||||
|
||||
let dst_subresource = ImageSubresourceLayers::builder()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.mip_level(level)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1)
|
||||
.build();
|
||||
|
||||
let image_blit = [vk::ImageBlit::builder()
|
||||
.src_subresource(src_subresource)
|
||||
.src_offsets(src_offsets)
|
||||
.dst_subresource(dst_subresource)
|
||||
.dst_offsets(dst_offsets)
|
||||
.build()];
|
||||
|
||||
unsafe {
|
||||
util::vulkan_image_layout_transition_levels(&vulkan.device, *cmd, texture,
|
||||
vk::REMAINING_MIP_LEVELS,
|
||||
vk::ImageLayout::GENERAL,
|
||||
vk::ImageLayout::GENERAL,
|
||||
vk::AccessFlags::TRANSFER_WRITE,
|
||||
vk::AccessFlags::TRANSFER_READ,
|
||||
vk::PipelineStageFlags::TRANSFER,
|
||||
vk::PipelineStageFlags::TRANSFER,
|
||||
vk::QUEUE_FAMILY_IGNORED,
|
||||
vk::QUEUE_FAMILY_IGNORED
|
||||
);
|
||||
|
||||
// todo: respect mipmap filter?
|
||||
vulkan.device.cmd_blit_image(*cmd, texture, vk::ImageLayout::GENERAL, texture, vk::ImageLayout::GENERAL, &image_blit, vk::Filter::LINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
util::vulkan_image_layout_transition_levels(&vulkan.device, *cmd, texture,
|
||||
vk::REMAINING_MIP_LEVELS,
|
||||
if config.mipmap { vk::ImageLayout::GENERAL } else { vk::ImageLayout::TRANSFER_DST_OPTIMAL },
|
||||
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
|
||||
vk::AccessFlags::TRANSFER_WRITE,
|
||||
vk::AccessFlags::TRANSFER_READ,
|
||||
vk::PipelineStageFlags::TRANSFER,
|
||||
vk::PipelineStageFlags::FRAGMENT_SHADER,
|
||||
vk::QUEUE_FAMILY_IGNORED,
|
||||
vk::QUEUE_FAMILY_IGNORED
|
||||
);
|
||||
}
|
||||
|
||||
Ok(LutTexture {
|
||||
texture,
|
||||
texture_view,
|
||||
memory,
|
||||
staging,
|
||||
filter_mode: config.filter_mode,
|
||||
wrap_mode: config.wrap_mode,
|
||||
mipmap: config.mipmap
|
||||
})
|
||||
}
|
||||
}
|
0
librashader-runtime-vk/src/samplers.rs
Normal file
0
librashader-runtime-vk/src/samplers.rs
Normal file
|
@ -1,5 +1,8 @@
|
|||
use ash::vk;
|
||||
use ash::vk::{AccessFlags, Extent3D, ImageAspectFlags};
|
||||
use librashader_common::Size;
|
||||
use librashader_reflect::reflect::semantics::BindingStage;
|
||||
use crate::error;
|
||||
|
||||
pub fn binding_stage_to_vulkan_stage(stage_mask: BindingStage) -> vk::ShaderStageFlags {
|
||||
let mut mask = vk::ShaderStageFlags::default();
|
||||
|
@ -27,4 +30,38 @@ pub fn find_vulkan_memory_type(props: &vk::PhysicalDeviceMemoryProperties, devic
|
|||
} else {
|
||||
find_vulkan_memory_type(props, device_reqs, vk::MemoryPropertyFlags::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn vulkan_image_layout_transition_levels(device: &ash::Device,
|
||||
cmd: vk::CommandBuffer,
|
||||
image: vk::Image,
|
||||
levels: u32, old_layout: vk::ImageLayout,
|
||||
new_layout: vk::ImageLayout,
|
||||
src_access: vk::AccessFlags,
|
||||
dst_access: vk::AccessFlags,
|
||||
src_stage: vk::PipelineStageFlags,
|
||||
dst_stage: vk::PipelineStageFlags,
|
||||
|
||||
|
||||
src_queue_family_index: u32,
|
||||
dst_queue_family_index: u32,
|
||||
|
||||
) {
|
||||
let mut barrier = vk::ImageMemoryBarrier::default();
|
||||
barrier.s_type = vk::StructureType::IMAGE_MEMORY_BARRIER;
|
||||
barrier.p_next = std::ptr::null();
|
||||
barrier.src_access_mask = src_access;
|
||||
barrier.dst_access_mask = dst_access;
|
||||
barrier.old_layout = old_layout;
|
||||
barrier.new_layout = new_layout;
|
||||
barrier.src_queue_family_index = src_queue_family_index;
|
||||
barrier.dst_queue_family_index = dst_queue_family_index;
|
||||
barrier.image = image.clone();
|
||||
barrier.subresource_range.aspect_mask = vk::ImageAspectFlags::COLOR;
|
||||
barrier.subresource_range.base_array_layer = 0;
|
||||
barrier.subresource_range.level_count = levels;
|
||||
barrier.subresource_range.layer_count = vk::REMAINING_ARRAY_LAYERS;
|
||||
device.cmd_pipeline_barrier(cmd, src_stage, dst_stage, vk::DependencyFlags::empty(),
|
||||
&[], &[], &[barrier])
|
||||
}
|
||||
|
|
|
@ -1,12 +1,36 @@
|
|||
use std::marker::PhantomData;
|
||||
use librashader_common::Size;
|
||||
pub use image::ImageError;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Image {
|
||||
pub struct Image<P: PixelFormat = RGBA8> {
|
||||
pub bytes: Vec<u8>,
|
||||
pub size: Size<u32>,
|
||||
pub pitch: usize,
|
||||
_pd: PhantomData<P>
|
||||
}
|
||||
|
||||
pub struct RGBA8;
|
||||
pub struct BGRA8;
|
||||
|
||||
pub trait PixelFormat {
|
||||
#[doc(hidden)]
|
||||
fn convert(pixels: &mut Vec<u8>);
|
||||
}
|
||||
|
||||
impl PixelFormat for RGBA8 {
|
||||
fn convert(_pixels: &mut Vec<u8>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelFormat for BGRA8 {
|
||||
fn convert(pixels: &mut Vec<u8>) {
|
||||
for [r, _g, b, _a] in pixels.array_chunks_mut::<4>() {
|
||||
std::mem::swap(b, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The direction of UV coordinates to load the image for.
|
||||
|
@ -18,7 +42,7 @@ pub enum UVDirection {
|
|||
BottomLeft,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
impl<P: PixelFormat> Image<P> {
|
||||
/// Load the image from the path as RGBA8.
|
||||
pub fn load(path: impl AsRef<Path>, direction: UVDirection) -> Result<Self, ImageError> {
|
||||
let mut image = image::open(path.as_ref())?;
|
||||
|
@ -36,10 +60,13 @@ impl Image {
|
|||
.height_stride
|
||||
.max(image.sample_layout().width_stride);
|
||||
|
||||
let mut bytes = image.into_raw();
|
||||
P::convert(&mut bytes);
|
||||
Ok(Image {
|
||||
bytes: image.into_raw(),
|
||||
bytes,
|
||||
pitch,
|
||||
size: Size { height, width },
|
||||
_pd: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![feature(array_chunks)]
|
||||
//! Helpers and shared logic for librashader runtime implementations.
|
||||
|
||||
/// Scaling helpers.
|
||||
|
|
|
@ -2,9 +2,53 @@ use librashader_common::Size;
|
|||
use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
|
||||
use num_traits::AsPrimitive;
|
||||
use std::ops::Mul;
|
||||
use crate::scaling;
|
||||
|
||||
/// Produce a `Size<T>` scaled with the input scaling options.
|
||||
pub fn scale<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>) -> Size<T>
|
||||
pub trait ViewportSize<T>
|
||||
where
|
||||
T: Mul<ScaleFactor, Output = f32> + Copy + 'static,
|
||||
f32: AsPrimitive<T>
|
||||
{
|
||||
/// Produce a `Size<T>` scaled with the input scaling options.
|
||||
fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>) -> Size<T>;
|
||||
}
|
||||
|
||||
impl<T> ViewportSize<T> for Size<T>
|
||||
where
|
||||
T: Mul<ScaleFactor, Output = f32> + Copy + 'static,
|
||||
f32: AsPrimitive<T>{
|
||||
fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>) -> Size<T> where T: Mul<ScaleFactor, Output=f32> + Copy + 'static, f32: AsPrimitive<T> {
|
||||
scaling::scale(scaling, self, viewport)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MipmapSize<T> {
|
||||
/// Calculate the number of mipmap levels for a given size.
|
||||
fn calculate_miplevels(self) -> T;
|
||||
|
||||
fn scale_mipmap(self, miplevel: T) -> Size<T>;
|
||||
}
|
||||
|
||||
impl MipmapSize<u32> for Size<u32> {
|
||||
fn calculate_miplevels(self) -> u32 {
|
||||
let mut size = std::cmp::max(self.width, self.height);
|
||||
let mut levels = 0;
|
||||
while size != 0 {
|
||||
levels += 1;
|
||||
size >>= 1;
|
||||
}
|
||||
|
||||
levels
|
||||
}
|
||||
|
||||
fn scale_mipmap(self, miplevel: u32) -> Size<u32> {
|
||||
let scaled_width = std::cmp::max(self.width >> miplevel, 1);
|
||||
let scaled_height = std::cmp::max(self.height >> miplevel, 1);
|
||||
Size::new(scaled_width, scaled_height)
|
||||
}
|
||||
}
|
||||
|
||||
fn scale<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>) -> Size<T>
|
||||
where
|
||||
T: Mul<ScaleFactor, Output = f32> + Copy + 'static,
|
||||
f32: AsPrimitive<T>,
|
||||
|
@ -45,8 +89,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Calculate the number of mipmap levels for a given size.
|
||||
pub fn calc_miplevel(size: Size<u32>) -> u32 {
|
||||
pub fn calculate_miplevels(size: Size<u32>) -> u32 {
|
||||
let mut size = std::cmp::max(size.width, size.height);
|
||||
let mut levels = 0;
|
||||
while size != 0 {
|
||||
|
|
Loading…
Reference in a new issue