vk: implement load luts

This commit is contained in:
chyyran 2022-12-21 21:13:35 -05:00
parent d5f0bece0b
commit fe573618f8
16 changed files with 496 additions and 54 deletions

View file

@ -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,
}
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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
};

View file

@ -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;
}

View file

@ -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
};

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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

View file

@ -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);
}

View 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
})
}
}

View file

View 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])
}

View file

@ -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(),
})
}
}

View file

@ -1,3 +1,4 @@
#![feature(array_chunks)]
//! Helpers and shared logic for librashader runtime implementations.
/// Scaling helpers.

View file

@ -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 {