vk: implement load luts
This commit is contained in:
parent
d5f0bece0b
commit
fe573618f8
16 changed files with 496 additions and 54 deletions
|
@ -1,5 +1,5 @@
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
use crate::ImageFormat;
|
use crate::{ImageFormat, Size};
|
||||||
|
|
||||||
impl From<ImageFormat> for vk::Format {
|
impl From<ImageFormat> for vk::Format {
|
||||||
fn from(format: ImageFormat) -> Self {
|
fn from(format: ImageFormat) -> Self {
|
||||||
|
@ -77,3 +77,13 @@ impl From<vk::Format> for ImageFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::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::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 windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
|
||||||
|
use librashader_runtime::scaling::ViewportSize;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct OwnedFramebuffer {
|
pub(crate) struct OwnedFramebuffer {
|
||||||
|
@ -60,7 +61,7 @@ impl OwnedFramebuffer {
|
||||||
return Ok(self.size);
|
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 {
|
if self.size != size {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::viewport::Viewport;
|
||||||
use gl::types::{GLenum, GLint, GLsizei, GLuint};
|
use gl::types::{GLenum, GLint, GLsizei, GLuint};
|
||||||
use librashader_common::{ImageFormat, Size};
|
use librashader_common::{ImageFormat, Size};
|
||||||
use librashader_presets::Scale2D;
|
use librashader_presets::Scale2D;
|
||||||
|
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Gl3Framebuffer;
|
pub struct Gl3Framebuffer;
|
||||||
|
@ -63,8 +64,7 @@ impl FramebufferInterface for Gl3Framebuffer {
|
||||||
return Ok(fb.size);
|
return Ok(fb.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size =
|
let size = source.image.size.scale_viewport(scaling, viewport.output.size);
|
||||||
librashader_runtime::scaling::scale(scaling, source.image.size, viewport.output.size);
|
|
||||||
|
|
||||||
if fb.size != size {
|
if fb.size != size {
|
||||||
fb.size = size;
|
fb.size = size;
|
||||||
|
@ -196,7 +196,7 @@ impl FramebufferInterface for Gl3Framebuffer {
|
||||||
size.height = 1;
|
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 {
|
if fb.mip_levels > fb.max_levels {
|
||||||
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::GenTextures(1, &mut fb.image);
|
||||||
gl::BindTexture(gl::TEXTURE_2D, 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 {
|
if fb.mip_levels > fb.max_levels {
|
||||||
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_common::Size;
|
||||||
use librashader_presets::TextureConfig;
|
use librashader_presets::TextureConfig;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use librashader_runtime::scaling::MipmapSize;
|
||||||
|
|
||||||
pub struct Gl3LutLoad;
|
pub struct Gl3LutLoad;
|
||||||
impl LoadLut for Gl3LutLoad {
|
impl LoadLut for Gl3LutLoad {
|
||||||
|
@ -19,9 +20,9 @@ impl LoadLut for Gl3LutLoad {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (index, texture) in textures.iter().enumerate() {
|
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 {
|
let levels = if texture.mipmap {
|
||||||
librashader_runtime::scaling::calc_miplevel(image.size)
|
image.size.calculate_miplevels()
|
||||||
} else {
|
} else {
|
||||||
1u32
|
1u32
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::viewport::Viewport;
|
||||||
use gl::types::{GLenum, GLint, GLsizei, GLuint};
|
use gl::types::{GLenum, GLint, GLsizei, GLuint};
|
||||||
use librashader_common::{ImageFormat, Size};
|
use librashader_common::{ImageFormat, Size};
|
||||||
use librashader_presets::Scale2D;
|
use librashader_presets::Scale2D;
|
||||||
|
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Gl46Framebuffer;
|
pub struct Gl46Framebuffer;
|
||||||
|
@ -61,8 +62,7 @@ impl FramebufferInterface for Gl46Framebuffer {
|
||||||
return Ok(fb.size);
|
return Ok(fb.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size =
|
let size = source.image.size.scale_viewport(scaling, viewport.output.size);
|
||||||
librashader_runtime::scaling::scale(scaling, source.image.size, viewport.output.size);
|
|
||||||
|
|
||||||
if fb.size != size {
|
if fb.size != size {
|
||||||
fb.size = size;
|
fb.size = size;
|
||||||
|
@ -146,7 +146,7 @@ impl FramebufferInterface for Gl46Framebuffer {
|
||||||
size.height = 1;
|
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 {
|
if fb.mip_levels > fb.max_levels {
|
||||||
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::DeleteTextures(1, &fb.image);
|
||||||
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut 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 {
|
if fb.mip_levels > fb.max_levels {
|
||||||
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_common::Size;
|
||||||
use librashader_presets::TextureConfig;
|
use librashader_presets::TextureConfig;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use librashader_runtime::scaling::MipmapSize;
|
||||||
|
|
||||||
pub struct Gl46LutLoad;
|
pub struct Gl46LutLoad;
|
||||||
impl LoadLut for Gl46LutLoad {
|
impl LoadLut for Gl46LutLoad {
|
||||||
|
@ -23,9 +24,9 @@ impl LoadLut for Gl46LutLoad {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index, texture) in textures.iter().enumerate() {
|
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 {
|
let levels = if texture.mipmap {
|
||||||
librashader_runtime::scaling::calc_miplevel(image.size)
|
image.size.calculate_miplevels()
|
||||||
} else {
|
} else {
|
||||||
1u32
|
1u32
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use ash::vk;
|
use ash::{Device, vk};
|
||||||
use ash::vk::{PFN_vkGetInstanceProcAddr, StaticFn};
|
use ash::vk::{CommandPoolCreateFlags, PFN_vkGetInstanceProcAddr, Queue, StaticFn};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use librashader_common::ImageFormat;
|
use librashader_common::ImageFormat;
|
||||||
use librashader_preprocess::ShaderSource;
|
use librashader_preprocess::ShaderSource;
|
||||||
|
@ -11,18 +11,21 @@ use librashader_reflect::back::targets::SpirV;
|
||||||
use librashader_reflect::front::shaderc::GlslangCompilation;
|
use librashader_reflect::front::shaderc::GlslangCompilation;
|
||||||
use librashader_reflect::reflect::ReflectShader;
|
use librashader_reflect::reflect::ReflectShader;
|
||||||
use librashader_reflect::reflect::semantics::{Semantic, ShaderSemantics, TextureSemantics, UniformBinding, UniformSemantic, UniqueSemantics};
|
use librashader_reflect::reflect::semantics::{Semantic, ShaderSemantics, TextureSemantics, UniformBinding, UniformSemantic, UniqueSemantics};
|
||||||
|
use librashader_runtime::image::{Image, UVDirection};
|
||||||
use librashader_runtime::uniforms::UniformStorage;
|
use librashader_runtime::uniforms::UniformStorage;
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::filter_pass::FilterPass;
|
use crate::filter_pass::FilterPass;
|
||||||
|
use crate::luts::LutTexture;
|
||||||
use crate::vulkan_state::VulkanGraphicsPipeline;
|
use crate::vulkan_state::VulkanGraphicsPipeline;
|
||||||
|
|
||||||
pub struct Vulkan {
|
pub struct Vulkan {
|
||||||
physical_device: vk::PhysicalDevice,
|
// physical_device: vk::PhysicalDevice,
|
||||||
device: ash::Device,
|
pub(crate) device: ash::Device,
|
||||||
instance: ash::Instance,
|
// instance: ash::Instance,
|
||||||
queue: vk::Queue,
|
queue: vk::Queue,
|
||||||
command_pool: vk::CommandPool,
|
command_pool: vk::CommandPool,
|
||||||
pipelines: vk::PipelineCache,
|
pipeline_cache: vk::PipelineCache,
|
||||||
|
pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShaderPassMeta = (
|
type ShaderPassMeta = (
|
||||||
|
@ -36,7 +39,7 @@ type ShaderPassMeta = (
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VulkanInfo<'a> {
|
pub struct VulkanInfo<'a> {
|
||||||
physical_device: &'a vk::PhysicalDevice,
|
// physical_device: &'a vk::PhysicalDevice,
|
||||||
device: &'a vk::Device,
|
device: &'a vk::Device,
|
||||||
instance: &'a vk::Instance,
|
instance: &'a vk::Instance,
|
||||||
queue: &'a vk::Queue,
|
queue: &'a vk::Queue,
|
||||||
|
@ -44,13 +47,65 @@ pub struct VulkanInfo<'a> {
|
||||||
get_instance_proc_addr: PFN_vkGetInstanceProcAddr
|
get_instance_proc_addr: PFN_vkGetInstanceProcAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VulkanInfo<'_>> for ash::Device {
|
impl TryFrom<VulkanInfo<'_>> for Vulkan {
|
||||||
fn from(vulkan: VulkanInfo) -> Self {
|
type Error = Box<dyn Error>;
|
||||||
|
|
||||||
|
fn try_from(vulkan: VulkanInfo) -> Result<Self, Box<dyn Error>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let instance = ash::Instance::load(&StaticFn {
|
let instance = ash::Instance::load(&StaticFn {
|
||||||
get_instance_proc_addr: vulkan.get_instance_proc_addr,
|
get_instance_proc_addr: vulkan.get_instance_proc_addr,
|
||||||
}, vulkan.instance.clone());
|
}, 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 feedback_textures: Box<[Option<Texture>]>,
|
||||||
// pub history_textures: Box<[Option<Texture>]>,
|
// pub history_textures: Box<[Option<Texture>]>,
|
||||||
// pub config: FilterMutable,
|
// pub config: FilterMutable,
|
||||||
|
luts: FxHashMap<usize, LutTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FilterChainOptionsVulkan = ();
|
pub type FilterChainOptionsVulkan = ();
|
||||||
|
@ -78,7 +134,7 @@ pub type FilterChainOptionsVulkan = ();
|
||||||
impl FilterChainVulkan {
|
impl FilterChainVulkan {
|
||||||
/// Load the shader preset at the given path into a filter chain.
|
/// Load the shader preset at the given path into a filter chain.
|
||||||
pub fn load_from_path(
|
pub fn load_from_path(
|
||||||
vulkan: impl Into<ash::Device>,
|
vulkan: impl TryInto<Vulkan, Error=Box<dyn Error>>,
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
options: Option<&FilterChainOptionsVulkan>,
|
options: Option<&FilterChainOptionsVulkan>,
|
||||||
) -> error::Result<FilterChainVulkan> {
|
) -> error::Result<FilterChainVulkan> {
|
||||||
|
@ -88,19 +144,24 @@ impl FilterChainVulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_from_preset(
|
pub fn load_from_preset(
|
||||||
vulkan: impl Into<ash::Device>,
|
vulkan: impl TryInto<Vulkan, Error=Box<dyn Error>>,
|
||||||
preset: ShaderPreset,
|
preset: ShaderPreset,
|
||||||
options: Option<&FilterChainOptionsVulkan>,
|
options: Option<&FilterChainOptionsVulkan>,
|
||||||
) -> error::Result<FilterChainVulkan> {
|
) -> error::Result<FilterChainVulkan> {
|
||||||
let (passes, semantics) = FilterChainVulkan::load_preset(preset.shaders, &preset.textures)?;
|
let (passes, semantics) = FilterChainVulkan::load_preset(preset.shaders, &preset.textures)?;
|
||||||
let device = vulkan.into();
|
let device = vulkan.try_into()?;
|
||||||
|
|
||||||
// initialize passes
|
// initialize passes
|
||||||
let filters = Self::init_passes(&device, passes, &semantics, 3)?;
|
let filters = Self::init_passes(&device, passes, &semantics, 3)?;
|
||||||
|
|
||||||
|
let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?;
|
||||||
|
|
||||||
eprintln!("filters initialized ok.");
|
eprintln!("filters initialized ok.");
|
||||||
Ok(FilterChainVulkan {
|
Ok(FilterChainVulkan {
|
||||||
common: FilterCommon {},
|
common: FilterCommon {
|
||||||
passes: filters
|
luts
|
||||||
|
},
|
||||||
|
passes: filters,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,18 +218,13 @@ impl FilterChainVulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_passes(
|
fn init_passes(
|
||||||
device: &ash::Device,
|
vulkan: &Vulkan,
|
||||||
passes: Vec<ShaderPassMeta>,
|
passes: Vec<ShaderPassMeta>,
|
||||||
semantics: &ShaderSemantics,
|
semantics: &ShaderSemantics,
|
||||||
images: u32,
|
images: u32,
|
||||||
) -> error::Result<Box<[FilterPass]>> {
|
) -> error::Result<Box<[FilterPass]>> {
|
||||||
let mut filters = Vec::new();
|
let mut filters = Vec::new();
|
||||||
|
|
||||||
let pipeline_cache = unsafe {
|
|
||||||
device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(),
|
|
||||||
None)?
|
|
||||||
};
|
|
||||||
|
|
||||||
// initialize passes
|
// initialize passes
|
||||||
for (index, (config, mut source, mut reflect)) in passes.into_iter().enumerate() {
|
for (index, (config, mut source, mut reflect)) in passes.into_iter().enumerate() {
|
||||||
let reflection = reflect.reflect(index, semantics)?;
|
let reflection = reflect.reflect(index, semantics)?;
|
||||||
|
@ -206,9 +262,10 @@ impl FilterChainVulkan {
|
||||||
source.format = ImageFormat::R8G8B8A8Unorm
|
source.format = ImageFormat::R8G8B8A8Unorm
|
||||||
}
|
}
|
||||||
|
|
||||||
let graphics_pipeline = VulkanGraphicsPipeline::new(device,
|
let graphics_pipeline = VulkanGraphicsPipeline::new(&vulkan.device,
|
||||||
&pipeline_cache,
|
&vulkan.pipeline_cache,
|
||||||
&spirv_words, &reflection, source.format, images)?;
|
&spirv_words, &reflection, source.format, images)?;
|
||||||
|
|
||||||
// shader_vulkan: 2026
|
// shader_vulkan: 2026
|
||||||
filters.push(FilterPass {
|
filters.push(FilterPass {
|
||||||
compiled: spirv_words,
|
compiled: spirv_words,
|
||||||
|
@ -223,4 +280,51 @@ impl FilterChainVulkan {
|
||||||
|
|
||||||
Ok(filters.into_boxed_slice())
|
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;
|
||||||
use ash::vk::{Extent3D, ImageAspectFlags, ImageLayout, ImageTiling, ImageType, ImageUsageFlags, ImageViewType, SampleCountFlags, SharingMode};
|
use ash::vk::{Extent3D, ImageAspectFlags, ImageLayout, ImageTiling, ImageType, ImageUsageFlags, ImageViewType, SampleCountFlags, SharingMode};
|
||||||
use librashader_common::Size;
|
use librashader_common::Size;
|
||||||
|
use librashader_runtime::scaling::MipmapSize;
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::renderpass::VulkanRenderPass;
|
use crate::renderpass::VulkanRenderPass;
|
||||||
use crate::util::find_vulkan_memory_type;
|
use crate::util::find_vulkan_memory_type;
|
||||||
|
@ -64,12 +65,8 @@ impl Framebuffer {
|
||||||
let image_create_info = vk::ImageCreateInfo::builder()
|
let image_create_info = vk::ImageCreateInfo::builder()
|
||||||
.image_type(ImageType::TYPE_2D)
|
.image_type(ImageType::TYPE_2D)
|
||||||
.format(self.render_pass.format.into())
|
.format(self.render_pass.format.into())
|
||||||
.extent(Extent3D {
|
.extent(self.size.into())
|
||||||
width: self.size.width,
|
.mip_levels(std::cmp::min(self.max_levels, self.size.calculate_miplevels()))
|
||||||
height: self.size.height,
|
|
||||||
depth: 1
|
|
||||||
})
|
|
||||||
.mip_levels(std::cmp::min(self.max_levels, librashader_runtime::scaling::calc_miplevel(self.size)))
|
|
||||||
.array_layers(1)
|
.array_layers(1)
|
||||||
.samples(SampleCountFlags::TYPE_1)
|
.samples(SampleCountFlags::TYPE_1)
|
||||||
.tiling(ImageTiling::OPTIMAL)
|
.tiling(ImageTiling::OPTIMAL)
|
||||||
|
|
|
@ -256,7 +256,7 @@ impl ExampleBase {
|
||||||
.application_version(0)
|
.application_version(0)
|
||||||
.engine_name(app_name)
|
.engine_name(app_name)
|
||||||
.engine_version(0)
|
.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")) {
|
let create_flags = if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||||
vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
|
vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
|
||||||
|
|
|
@ -11,6 +11,7 @@ mod vulkan_state;
|
||||||
mod draw_quad;
|
mod draw_quad;
|
||||||
mod renderpass;
|
mod renderpass;
|
||||||
mod vulkan_primitives;
|
mod vulkan_primitives;
|
||||||
|
mod luts;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -19,12 +20,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn triangle_vk() {
|
fn triangle_vk() {
|
||||||
let base = hello_triangle::ExampleBase::new(900, 600);
|
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(
|
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",
|
"../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
|
||||||
None
|
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();
|
.unwrap();
|
||||||
hello_triangle::main(base);
|
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;
|
||||||
|
use ash::vk::{AccessFlags, Extent3D, ImageAspectFlags};
|
||||||
|
use librashader_common::Size;
|
||||||
use librashader_reflect::reflect::semantics::BindingStage;
|
use librashader_reflect::reflect::semantics::BindingStage;
|
||||||
|
use crate::error;
|
||||||
|
|
||||||
pub fn binding_stage_to_vulkan_stage(stage_mask: BindingStage) -> vk::ShaderStageFlags {
|
pub fn binding_stage_to_vulkan_stage(stage_mask: BindingStage) -> vk::ShaderStageFlags {
|
||||||
let mut mask = vk::ShaderStageFlags::default();
|
let mut mask = vk::ShaderStageFlags::default();
|
||||||
|
@ -28,3 +31,37 @@ pub fn find_vulkan_memory_type(props: &vk::PhysicalDeviceMemoryProperties, devic
|
||||||
find_vulkan_memory_type(props, device_reqs, vk::MemoryPropertyFlags::empty())
|
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;
|
use librashader_common::Size;
|
||||||
pub use image::ImageError;
|
pub use image::ImageError;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub struct Image {
|
pub struct Image<P: PixelFormat = RGBA8> {
|
||||||
pub bytes: Vec<u8>,
|
pub bytes: Vec<u8>,
|
||||||
pub size: Size<u32>,
|
pub size: Size<u32>,
|
||||||
pub pitch: usize,
|
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.
|
/// The direction of UV coordinates to load the image for.
|
||||||
|
@ -18,7 +42,7 @@ pub enum UVDirection {
|
||||||
BottomLeft,
|
BottomLeft,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl<P: PixelFormat> Image<P> {
|
||||||
/// Load the image from the path as RGBA8.
|
/// Load the image from the path as RGBA8.
|
||||||
pub fn load(path: impl AsRef<Path>, direction: UVDirection) -> Result<Self, ImageError> {
|
pub fn load(path: impl AsRef<Path>, direction: UVDirection) -> Result<Self, ImageError> {
|
||||||
let mut image = image::open(path.as_ref())?;
|
let mut image = image::open(path.as_ref())?;
|
||||||
|
@ -36,10 +60,13 @@ impl Image {
|
||||||
.height_stride
|
.height_stride
|
||||||
.max(image.sample_layout().width_stride);
|
.max(image.sample_layout().width_stride);
|
||||||
|
|
||||||
|
let mut bytes = image.into_raw();
|
||||||
|
P::convert(&mut bytes);
|
||||||
Ok(Image {
|
Ok(Image {
|
||||||
bytes: image.into_raw(),
|
bytes,
|
||||||
pitch,
|
pitch,
|
||||||
size: Size { height, width },
|
size: Size { height, width },
|
||||||
|
_pd: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(array_chunks)]
|
||||||
//! Helpers and shared logic for librashader runtime implementations.
|
//! Helpers and shared logic for librashader runtime implementations.
|
||||||
|
|
||||||
/// Scaling helpers.
|
/// Scaling helpers.
|
||||||
|
|
|
@ -2,9 +2,53 @@ use librashader_common::Size;
|
||||||
use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
|
use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
|
||||||
use num_traits::AsPrimitive;
|
use num_traits::AsPrimitive;
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
use crate::scaling;
|
||||||
|
|
||||||
/// Produce a `Size<T>` scaled with the input scaling options.
|
pub trait ViewportSize<T>
|
||||||
pub fn scale<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>) -> Size<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
|
where
|
||||||
T: Mul<ScaleFactor, Output = f32> + Copy + 'static,
|
T: Mul<ScaleFactor, Output = f32> + Copy + 'static,
|
||||||
f32: AsPrimitive<T>,
|
f32: AsPrimitive<T>,
|
||||||
|
@ -45,8 +89,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the number of mipmap levels for a given size.
|
pub fn calculate_miplevels(size: Size<u32>) -> u32 {
|
||||||
pub fn calc_miplevel(size: Size<u32>) -> u32 {
|
|
||||||
let mut size = std::cmp::max(size.width, size.height);
|
let mut size = std::cmp::max(size.width, size.height);
|
||||||
let mut levels = 0;
|
let mut levels = 0;
|
||||||
while size != 0 {
|
while size != 0 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue