doc: completely doc librashader crate

This commit is contained in:
chyyran 2023-01-13 03:19:58 -05:00
parent c948c882ab
commit 589538d8b2
19 changed files with 87 additions and 38 deletions

View file

@ -1,9 +1,12 @@
//! Direct3D11 shader runtime errors.
//!
use librashader_preprocess::PreprocessError; use librashader_preprocess::PreprocessError;
use librashader_presets::ParsePresetError; use librashader_presets::ParsePresetError;
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError}; use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
use librashader_runtime::image::ImageError; use librashader_runtime::image::ImageError;
use thiserror::Error; use thiserror::Error;
/// Cumulative error type for Direct3D11 filter chains.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum FilterChainError { pub enum FilterChainError {
#[error("unable to get direct3d context")] #[error("unable to get direct3d context")]
@ -24,4 +27,5 @@ pub enum FilterChainError {
LutLoadError(#[from] ImageError), LutLoadError(#[from] ImageError),
} }
/// Result type for Direct3D11 filter chains.
pub type Result<T> = std::result::Result<T, FilterChainError>; pub type Result<T> = std::result::Result<T, FilterChainError>;

View file

@ -46,6 +46,7 @@ type ShaderPassMeta = (
>, >,
); );
/// A Direct3D 11 filter chain.
pub struct FilterChain { pub struct FilterChain {
pub(crate) common: FilterCommon, pub(crate) common: FilterCommon,
pub(crate) passes: Vec<FilterPass>, pub(crate) passes: Vec<FilterPass>,

View file

@ -1,3 +1,5 @@
//! Direct3D11 shader runtime options.
/// Options for each Direct3D11 shader frame. /// Options for each Direct3D11 shader frame.
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -13,12 +13,19 @@ use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
use crate::error::Result; use crate::error::Result;
use crate::framebuffer::OwnedFramebuffer; use crate::framebuffer::OwnedFramebuffer;
/// An image view for use as a shader resource.
///
/// Contains an `ID3D11ShaderResourceView`, and a size.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct D3D11InputView { pub struct D3D11InputView {
pub handle: ID3D11ShaderResourceView, pub handle: ID3D11ShaderResourceView,
pub size: Size<u32>, pub size: Size<u32>,
} }
/// An image view for use as a render target.
///
/// Contains an `ID3D11RenderTargetView`, and a size.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct D3D11OutputView { pub struct D3D11OutputView {
pub handle: ID3D11RenderTargetView, pub handle: ID3D11RenderTargetView,

View file

@ -1,3 +1,5 @@
//! OpenGL shader runtime errors.
use gl::types::GLenum; use gl::types::GLenum;
use librashader_preprocess::PreprocessError; use librashader_preprocess::PreprocessError;
use librashader_presets::ParsePresetError; use librashader_presets::ParsePresetError;
@ -5,6 +7,7 @@ use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
use librashader_runtime::image::ImageError; use librashader_runtime::image::ImageError;
use thiserror::Error; use thiserror::Error;
/// Cumulative error type for OpenGL filter chains.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum FilterChainError { pub enum FilterChainError {
#[error("fbo initialization error")] #[error("fbo initialization error")]
@ -27,4 +30,5 @@ pub enum FilterChainError {
GLLinkError, GLLinkError,
} }
/// Result type for OpenGL filter chains.
pub type Result<T> = std::result::Result<T, FilterChainError>; pub type Result<T> = std::result::Result<T, FilterChainError>;

View file

@ -16,11 +16,13 @@ mod parameters;
pub(crate) use filter_impl::FilterCommon; pub(crate) use filter_impl::FilterCommon;
use librashader_common::{Size, Viewport}; use librashader_common::{Size, Viewport};
/// An OpenGL filter chain.
pub struct FilterChain { pub struct FilterChain {
pub(in crate::filter_chain) filter: FilterChainDispatch, pub(in crate::filter_chain) filter: FilterChainDispatch,
} }
impl FilterChain { impl FilterChain {
/// Load a filter chain from a pre-parsed `ShaderPreset`.
pub fn load_from_preset( pub fn load_from_preset(
preset: ShaderPreset, preset: ShaderPreset,
options: Option<&FilterChainOptions>, options: Option<&FilterChainOptions>,

View file

@ -1,10 +1,17 @@
use gl::types::{GLenum, GLuint}; use gl::types::{GLenum, GLuint};
use librashader_common::Size; use librashader_common::Size;
/// A handle to an OpenGL texture with format and size information.
///
/// Generally for use as shader resource inputs.
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
pub struct GLImage { pub struct GLImage {
/// A GLuint to the texture.
pub handle: GLuint, pub handle: GLuint,
/// The format of the texture.
pub format: GLenum, pub format: GLenum,
/// The size of the texture.
pub size: Size<u32>, pub size: Size<u32>,
/// The padded size of the texture. Currently unused and can be ignored.
pub padded_size: Size<u32>, pub padded_size: Size<u32>,
} }

View file

@ -6,6 +6,10 @@ use gl::types::{GLenum, GLuint};
use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode}; use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode};
use librashader_presets::Scale2D; use librashader_presets::Scale2D;
/// A handle to an OpenGL FBO and its backing texture with format and size information.
///
/// Generally for use as render targets.
#[derive(Debug)] #[derive(Debug)]
pub struct Framebuffer { pub struct Framebuffer {
pub(crate) image: GLuint, pub(crate) image: GLuint,
@ -23,6 +27,8 @@ impl Framebuffer {
} }
/// Create a framebuffer from an already initialized texture and framebuffer. /// Create a framebuffer from an already initialized texture and framebuffer.
///
/// The framebuffer will not be deleted when this struct is dropped.
pub fn new_from_raw( pub fn new_from_raw(
texture: GLuint, texture: GLuint,
fbo: GLuint, fbo: GLuint,

View file

@ -1,3 +1,5 @@
//! OpenGL shader runtime options.
/// Options for each OpenGL shader frame. /// Options for each OpenGL shader frame.
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -1,9 +1,11 @@
//! Vulkan shader runtime errors.
use librashader_preprocess::PreprocessError; use librashader_preprocess::PreprocessError;
use librashader_presets::ParsePresetError; use librashader_presets::ParsePresetError;
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError}; use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
use librashader_runtime::image::ImageError; use librashader_runtime::image::ImageError;
use thiserror::Error; use thiserror::Error;
/// Cumulative error type for Vulkan filter chains.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum FilterChainError { pub enum FilterChainError {
#[error("SPIRV reflection error")] #[error("SPIRV reflection error")]
@ -22,4 +24,5 @@ pub enum FilterChainError {
VulkanResult(#[from] ash::vk::Result), VulkanResult(#[from] ash::vk::Result),
} }
/// Result type for Vulkan filter chains.
pub type Result<T> = std::result::Result<T, FilterChainError>; pub type Result<T> = std::result::Result<T, FilterChainError>;

View file

@ -28,7 +28,8 @@ use std::collections::VecDeque;
use std::path::Path; use std::path::Path;
use crate::options::{FilterChainOptions, FrameOptions}; use crate::options::{FilterChainOptions, FrameOptions};
pub struct Vulkan { /// A Vulkan device and metadata that is required by the shader runtime.
pub struct VulkanDevice {
pub(crate) device: ash::Device, pub(crate) device: ash::Device,
pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties, pub(crate) memory_properties: vk::PhysicalDeviceMemoryProperties,
queue: vk::Queue, queue: vk::Queue,
@ -54,7 +55,7 @@ pub struct VulkanInstance {
pub get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr, pub get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
} }
impl TryFrom<VulkanInstance> for Vulkan { impl TryFrom<VulkanInstance> for VulkanDevice {
type Error = FilterChainError; type Error = FilterChainError;
fn try_from(vulkan: VulkanInstance) -> Result<Self, FilterChainError> { fn try_from(vulkan: VulkanInstance) -> Result<Self, FilterChainError> {
@ -75,7 +76,7 @@ impl TryFrom<VulkanInstance> for Vulkan {
let memory_properties = let memory_properties =
instance.get_physical_device_memory_properties(vulkan.physical_device); instance.get_physical_device_memory_properties(vulkan.physical_device);
Ok(Vulkan { Ok(VulkanDevice {
device, device,
queue, queue,
pipeline_cache, pipeline_cache,
@ -86,7 +87,7 @@ impl TryFrom<VulkanInstance> for Vulkan {
} }
} }
impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for Vulkan { impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for VulkanDevice {
type Error = FilterChainError; type Error = FilterChainError;
fn try_from(value: (vk::PhysicalDevice, ash::Instance, ash::Device)) -> error::Result<Self> { fn try_from(value: (vk::PhysicalDevice, ash::Instance, ash::Device)) -> error::Result<Self> {
@ -99,7 +100,7 @@ impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for Vulkan {
let memory_properties = value.1.get_physical_device_memory_properties(value.0); let memory_properties = value.1.get_physical_device_memory_properties(value.0);
Ok(Vulkan { Ok(VulkanDevice {
device, device,
queue, queue,
pipeline_cache, pipeline_cache,
@ -110,10 +111,11 @@ impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for Vulkan {
} }
} }
pub struct FilterChainVulkan { /// A Vulkan filter chain.
pub struct FilterChain {
pub(crate) common: FilterCommon, pub(crate) common: FilterCommon,
passes: Box<[FilterPass]>, passes: Box<[FilterPass]>,
vulkan: Vulkan, vulkan: VulkanDevice,
output_framebuffers: Box<[OwnedImage]>, output_framebuffers: Box<[OwnedImage]>,
feedback_framebuffers: Box<[OwnedImage]>, feedback_framebuffers: Box<[OwnedImage]>,
history_framebuffers: VecDeque<OwnedImage>, history_framebuffers: VecDeque<OwnedImage>,
@ -136,6 +138,11 @@ pub(crate) struct FilterCommon {
pub device: ash::Device, pub device: ash::Device,
} }
/// Contains residual intermediate `VkImageView` and `VkImage` objects created
/// for intermediate shader passes.
///
/// These Vulkan objects must stay alive until the command buffer is submitted
/// to the rendering queue, and the GPU is done with the objects.
#[must_use] #[must_use]
pub struct FrameIntermediates { pub struct FrameIntermediates {
device: ash::Device, device: ash::Device,
@ -173,34 +180,35 @@ impl FrameIntermediates {
} }
} }
impl FilterChainVulkan { impl FilterChain {
/// 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 TryInto<Vulkan, Error = FilterChainError>, vulkan: impl TryInto<VulkanDevice, Error = FilterChainError>,
path: impl AsRef<Path>, path: impl AsRef<Path>,
options: Option<&FilterChainOptions>, options: Option<&FilterChainOptions>,
) -> error::Result<FilterChainVulkan> { ) -> error::Result<FilterChain> {
// load passes from preset // load passes from preset
let preset = ShaderPreset::try_parse(path)?; let preset = ShaderPreset::try_parse(path)?;
Self::load_from_preset(vulkan, preset, options) Self::load_from_preset(vulkan, preset, options)
} }
/// Load a filter chain from a pre-parsed `ShaderPreset`.
pub fn load_from_preset( pub fn load_from_preset(
vulkan: impl TryInto<Vulkan, Error = FilterChainError>, vulkan: impl TryInto<VulkanDevice, Error = FilterChainError>,
preset: ShaderPreset, preset: ShaderPreset,
options: Option<&FilterChainOptions>, options: Option<&FilterChainOptions>,
) -> error::Result<FilterChainVulkan> { ) -> error::Result<FilterChain> {
let (passes, semantics) = FilterChainVulkan::load_preset(preset.shaders, &preset.textures)?; let (passes, semantics) = FilterChain::load_preset(preset.shaders, &preset.textures)?;
let device = vulkan.try_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)?; let luts = FilterChain::load_luts(&device, &preset.textures)?;
let samplers = SamplerSet::new(&device.device)?; let samplers = SamplerSet::new(&device.device)?;
let (history_framebuffers, history_textures) = let (history_framebuffers, history_textures) =
FilterChainVulkan::init_history(&device, &filters)?; FilterChain::init_history(&device, &filters)?;
let mut output_framebuffers = Vec::new(); let mut output_framebuffers = Vec::new();
output_framebuffers.resize_with(filters.len(), || { output_framebuffers.resize_with(filters.len(), || {
@ -222,7 +230,7 @@ impl FilterChainVulkan {
let mut feedback_textures = Vec::new(); let mut feedback_textures = Vec::new();
feedback_textures.resize_with(filters.len(), || None); feedback_textures.resize_with(filters.len(), || None);
Ok(FilterChainVulkan { Ok(FilterChain {
common: FilterCommon { common: FilterCommon {
luts, luts,
samplers, samplers,
@ -303,7 +311,7 @@ impl FilterChainVulkan {
} }
fn init_passes( fn init_passes(
vulkan: &Vulkan, vulkan: &VulkanDevice,
passes: Vec<ShaderPassMeta>, passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics, semantics: &ShaderSemantics,
frames_in_flight: u32, frames_in_flight: u32,
@ -377,7 +385,7 @@ impl FilterChainVulkan {
} }
fn load_luts( fn load_luts(
vulkan: &Vulkan, vulkan: &VulkanDevice,
textures: &[TextureConfig], textures: &[TextureConfig],
) -> error::Result<FxHashMap<usize, LutTexture>> { ) -> error::Result<FxHashMap<usize, LutTexture>> {
let mut luts = FxHashMap::default(); let mut luts = FxHashMap::default();
@ -437,7 +445,7 @@ impl FilterChainVulkan {
} }
fn init_history( fn init_history(
vulkan: &Vulkan, vulkan: &VulkanDevice,
filters: &[FilterPass], filters: &[FilterPass],
) -> error::Result<(VecDeque<OwnedImage>, Box<[Option<InputImage>]>)> { ) -> error::Result<(VecDeque<OwnedImage>, Box<[Option<InputImage>]>)> {
let mut required_images = 0; let mut required_images = 0;

View file

@ -1,4 +1,4 @@
use crate::filter_chain::Vulkan; use crate::filter_chain::VulkanDevice;
use crate::texture::VulkanImage; use crate::texture::VulkanImage;
use crate::{error, util}; use crate::{error, util};
use ash::vk; use ash::vk;
@ -13,7 +13,7 @@ pub(crate) struct OutputImage {
} }
impl OutputImage { impl OutputImage {
pub fn new(vulkan: &Vulkan, image: VulkanImage) -> error::Result<OutputImage> { pub fn new(vulkan: &VulkanDevice, image: VulkanImage) -> error::Result<OutputImage> {
let image_subresource = vk::ImageSubresourceRange::builder() let image_subresource = vk::ImageSubresourceRange::builder()
.base_mip_level(0) .base_mip_level(0)
.base_array_layer(0) .base_array_layer(0)

View file

@ -8,7 +8,7 @@ mod swapchain;
mod syncobjects; mod syncobjects;
pub mod vulkan_base; pub mod vulkan_base;
use crate::filter_chain::{FilterChainVulkan, Vulkan}; use crate::filter_chain::{FilterChain, VulkanDevice};
use crate::hello_triangle::command::VulkanCommandPool; use crate::hello_triangle::command::VulkanCommandPool;
use crate::hello_triangle::framebuffer::VulkanFramebuffer; use crate::hello_triangle::framebuffer::VulkanFramebuffer;
use crate::hello_triangle::pipeline::VulkanPipeline; use crate::hello_triangle::pipeline::VulkanPipeline;
@ -48,7 +48,7 @@ impl VulkanWindow {
event_loop: EventLoop<()>, event_loop: EventLoop<()>,
window: winit::window::Window, window: winit::window::Window,
vulkan: VulkanDraw, vulkan: VulkanDraw,
mut filter_chain: FilterChainVulkan, mut filter_chain: FilterChain,
) { ) {
let mut counter = 0; let mut counter = 0;
event_loop.run(move |event, _, control_flow| match event { event_loop.run(move |event, _, control_flow| match event {
@ -136,7 +136,7 @@ impl VulkanWindow {
vulkan.base.device.cmd_end_render_pass(cmd); vulkan.base.device.cmd_end_render_pass(cmd);
} }
fn draw_frame(frame: usize, vulkan: &VulkanDraw, filter: &mut FilterChainVulkan) { fn draw_frame(frame: usize, vulkan: &VulkanDraw, filter: &mut FilterChain) {
let index = frame % MAX_FRAMES_IN_FLIGHT; let index = frame % MAX_FRAMES_IN_FLIGHT;
let in_flight = [vulkan.sync.in_flight[index]]; let in_flight = [vulkan.sync.in_flight[index]];
let image_available = [vulkan.sync.image_available[index]]; let image_available = [vulkan.sync.image_available[index]];
@ -370,7 +370,7 @@ pub struct VulkanDraw {
pub sync: SyncObjects, pub sync: SyncObjects,
} }
pub fn main(vulkan: VulkanBase, filter_chain: FilterChainVulkan) { pub fn main(vulkan: VulkanBase, filter_chain: FilterChain) {
let event_loop = EventLoopBuilder::new() let event_loop = EventLoopBuilder::new()
.with_any_thread(true) .with_any_thread(true)
.with_dpi_aware(true) .with_dpi_aware(true)

View file

@ -3,7 +3,7 @@ use std::borrow::Cow;
use std::error::Error; use std::error::Error;
use crate::error::FilterChainError; use crate::error::FilterChainError;
use crate::filter_chain::Vulkan; use crate::filter_chain::VulkanDevice;
use crate::hello_triangle::debug::VulkanDebug; use crate::hello_triangle::debug::VulkanDebug;
use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device}; use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device};
use crate::hello_triangle::surface::VulkanSurface; use crate::hello_triangle::surface::VulkanSurface;
@ -158,11 +158,11 @@ impl Drop for VulkanBase {
} }
} }
impl TryFrom<&VulkanBase> for Vulkan { impl TryFrom<&VulkanBase> for VulkanDevice {
type Error = FilterChainError; type Error = FilterChainError;
fn try_from(value: &VulkanBase) -> Result<Self, Self::Error> { fn try_from(value: &VulkanBase) -> Result<Self, Self::Error> {
Vulkan::try_from(( VulkanDevice::try_from((
value.physical_device, value.physical_device,
value.instance.clone(), value.instance.clone(),
value.device.clone(), value.device.clone(),

View file

@ -20,8 +20,8 @@ mod vulkan_primitives;
mod vulkan_state; mod vulkan_state;
pub use filter_chain::FrameIntermediates; pub use filter_chain::FrameIntermediates;
pub use filter_chain::FilterChainVulkan; pub use filter_chain::FilterChain;
pub use filter_chain::Vulkan; pub use filter_chain::VulkanDevice;
pub use filter_chain::VulkanInstance; pub use filter_chain::VulkanInstance;
pub use texture::VulkanImage; pub use texture::VulkanImage;
@ -31,7 +31,7 @@ pub mod options;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::filter_chain::FilterChainVulkan; use crate::filter_chain::FilterChain;
use crate::hello_triangle::vulkan_base::VulkanBase; use crate::hello_triangle::vulkan_base::VulkanBase;
#[test] #[test]
@ -39,7 +39,7 @@ mod tests {
let entry = unsafe { ash::Entry::load().unwrap() }; let entry = unsafe { ash::Entry::load().unwrap() };
let base = VulkanBase::new(entry).unwrap(); let base = VulkanBase::new(entry).unwrap();
dbg!("finished"); dbg!("finished");
let mut filter = FilterChainVulkan::load_from_path( let mut filter = FilterChain::load_from_path(
&base, &base,
// "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
"../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",

View file

@ -1,4 +1,4 @@
use crate::filter_chain::Vulkan; use crate::filter_chain::VulkanDevice;
use crate::texture::{InputImage, VulkanImage}; use crate::texture::{InputImage, VulkanImage};
use crate::vulkan_primitives::{VulkanBuffer, VulkanImageMemory}; use crate::vulkan_primitives::{VulkanBuffer, VulkanImageMemory};
use crate::{error, util}; use crate::{error, util};
@ -15,7 +15,7 @@ pub struct LutTexture {
impl LutTexture { impl LutTexture {
pub fn new( pub fn new(
vulkan: &Vulkan, vulkan: &VulkanDevice,
cmd: vk::CommandBuffer, cmd: vk::CommandBuffer,
image: Image<BGRA8>, image: Image<BGRA8>,
config: &TextureConfig, config: &TextureConfig,

View file

@ -1,3 +1,5 @@
//! Vulkan shader runtime options.
/// Options for each Vulkan shader frame. /// Options for each Vulkan shader frame.
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -1,8 +1,8 @@
use crate::FilterChainVulkan; use crate::FilterChain;
use librashader_runtime::parameters::FilterChainParameters; use librashader_runtime::parameters::FilterChainParameters;
use std::collections::hash_map::Iter; use std::collections::hash_map::Iter;
impl FilterChainParameters for FilterChainVulkan { impl FilterChainParameters for FilterChain {
fn get_enabled_pass_count(&self) -> usize { fn get_enabled_pass_count(&self) -> usize {
self.common.config.passes_enabled self.common.config.passes_enabled
} }

View file

@ -1,4 +1,4 @@
use crate::filter_chain::Vulkan; use crate::filter_chain::VulkanDevice;
use crate::util::find_vulkan_memory_type; use crate::util::find_vulkan_memory_type;
use crate::vulkan_primitives::VulkanImageMemory; use crate::vulkan_primitives::VulkanImageMemory;
use crate::{error, util}; use crate::{error, util};
@ -114,7 +114,7 @@ impl OwnedImage {
} }
pub fn new( pub fn new(
vulkan: &Vulkan, vulkan: &VulkanDevice,
size: Size<u32>, size: Size<u32>,
format: ImageFormat, format: ImageFormat,
max_miplevels: u32, max_miplevels: u32,
@ -505,6 +505,7 @@ impl Drop for OwnedImage {
} }
} }
/// A handle to a `VkImage` with size and format information.
#[derive(Clone)] #[derive(Clone)]
pub struct VulkanImage { pub struct VulkanImage {
pub size: Size<u32>, pub size: Size<u32>,