Compare commits

...

7 commits

Author SHA1 Message Date
Alex Janka e45e5f242d shaders can be either a path or a string 2024-07-07 10:14:08 +10:00
chyyran fff80df5a0 ci: temporarily remove deny-deprecated on ctypes to unblock ares 2024-06-14 18:17:36 -04:00
chyyran 875968d097 dep: update spirv_cross 2024-06-14 18:17:36 -04:00
chyyran fa48b936be rt: cap texture scaling to [1, 16384]
Fixes #79, #78
2024-06-14 18:17:36 -04:00
chyyran 0a9fa16855 rt: update for new TAIT scope rules 2024-06-14 18:17:36 -04:00
chyyran d558c6e50d dep: update image dependency 2024-06-14 18:17:36 -04:00
chyyran 48d91dfe58 dep: update image dependency 2024-06-14 18:17:36 -04:00
18 changed files with 916 additions and 478 deletions

1021
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -181,7 +181,6 @@ pub(crate) use config_struct;
pub(crate) use config_version_set; pub(crate) use config_version_set;
#[doc(hidden)] #[doc(hidden)]
#[deny(deprecated)]
#[deprecated = "Forward declarations for cbindgen, do not use."] #[deprecated = "Forward declarations for cbindgen, do not use."]
mod __cbindgen_opaque_forward_declarations { mod __cbindgen_opaque_forward_declarations {
macro_rules! typedef_struct { macro_rules! typedef_struct {
@ -189,7 +188,6 @@ mod __cbindgen_opaque_forward_declarations {
$($(#[$($attrss)*])* $($(#[$($attrss)*])*
#[allow(unused)] #[allow(unused)]
#[doc(hidden)] #[doc(hidden)]
#[deny(deprecated)]
#[deprecated] #[deprecated]
pub struct $name; pub struct $name;
)* )*

View file

@ -66,7 +66,6 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![feature(try_blocks)] #![feature(try_blocks)]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
#![deny(deprecated)]
extern crate alloc; extern crate alloc;

View file

@ -42,6 +42,12 @@ use num_traits::AsPrimitive;
use std::convert::Infallible; use std::convert::Infallible;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum ShaderStorage {
Path(std::path::PathBuf),
String(String),
}
#[repr(u32)] #[repr(u32)]
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
/// Supported image formats for textures. /// Supported image formats for textures.

View file

@ -17,7 +17,6 @@ use crate::include::read_source;
pub use error::*; pub use error::*;
use librashader_common::map::FastHashMap; use librashader_common::map::FastHashMap;
use librashader_common::ImageFormat; use librashader_common::ImageFormat;
use std::path::Path;
/// The source file for a single shader pass. /// The source file for a single shader pass.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -58,8 +57,8 @@ pub struct ShaderParameter {
impl ShaderSource { impl ShaderSource {
/// Load the source file at the given path, resolving includes relative to the location of the /// Load the source file at the given path, resolving includes relative to the location of the
/// source file. /// source file.
pub fn load(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> { pub fn load(file: &librashader_common::ShaderStorage) -> Result<ShaderSource, PreprocessError> {
load_shader_source(path) load_shader_source(file)
} }
} }
@ -78,8 +77,14 @@ impl SourceOutput for String {
} }
} }
pub(crate) fn load_shader_source(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> { pub(crate) fn load_shader_source(
let source = read_source(path)?; file: &librashader_common::ShaderStorage,
) -> Result<ShaderSource, PreprocessError> {
let source = match file {
librashader_common::ShaderStorage::Path(path) => read_source(path)?,
librashader_common::ShaderStorage::String(s) => s.to_string(),
};
let meta = pragma::parse_pragma_meta(&source)?; let meta = pragma::parse_pragma_meta(&source)?;
let text = stage::process_stages(&source)?; let text = stage::process_stages(&source)?;
let parameters = FastHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p))); let parameters = FastHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p)));

View file

@ -115,7 +115,7 @@ pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
let shader = ShaderPassConfig { let shader = ShaderPassConfig {
id, id,
name, name: librashader_common::ShaderStorage::Path(name),
alias: shader_values.iter().find_map(|f| match f { alias: shader_values.iter().find_map(|f| match f {
Value::Alias(_, value) => Some(value.to_string()), Value::Alias(_, value) => Some(value.to_string()),
_ => None, _ => None,

View file

@ -10,7 +10,7 @@ pub struct ShaderPassConfig {
/// The index of the shader pass relative to its parent preset. /// The index of the shader pass relative to its parent preset.
pub id: i32, pub id: i32,
/// The fully qualified path to the shader pass source file. /// The fully qualified path to the shader pass source file.
pub name: PathBuf, pub name: librashader_common::ShaderStorage,
/// The alias of the shader pass if available. /// The alias of the shader pass if available.
pub alias: Option<String>, pub alias: Option<String>,
/// The filtering mode that this shader pass should expect. /// The filtering mode that this shader pass should expect.

View file

@ -75,28 +75,34 @@ pub(crate) struct FilterCommon {
pub(crate) draw_quad: DrawQuad, pub(crate) draw_quad: DrawQuad,
} }
type ShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>; use super::*;
fn compile_passes( pub type ShaderPassMeta =
shaders: Vec<ShaderPassConfig>, ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>;
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
HLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics)) pub fn compile_passes(
shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
HLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics))
}
} }
use compile::{compile_passes, ShaderPassMeta};
impl FilterChainD3D11 { impl FilterChainD3D11 {
/// 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 unsafe fn load_from_path( pub unsafe fn load_from_path(

View file

@ -146,49 +146,57 @@ impl Drop for FrameResiduals {
} }
} }
type DxilShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<DXIL, SpirvCompilation, SpirvCross> + Send>; use super::*;
fn compile_passes_dxil( pub type DxilShaderPassMeta =
shaders: Vec<ShaderPassConfig>, ShaderPassArtifact<impl CompileReflectShader<DXIL, SpirvCompilation, SpirvCross> + Send>;
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<DxilShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
DXIL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
DXIL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics)) pub fn compile_passes_dxil(
} shaders: Vec<ShaderPassConfig>,
type HlslShaderPassMeta = textures: &[TextureConfig],
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>; disable_cache: bool,
fn compile_passes_hlsl( ) -> Result<(Vec<DxilShaderPassMeta>, ShaderSemantics), FilterChainError> {
shaders: Vec<ShaderPassConfig>, let (passes, semantics) = if !disable_cache {
textures: &[TextureConfig], DXIL::compile_preset_passes::<
disable_cache: bool, CachedCompilation<SpirvCompilation>,
) -> Result<(Vec<HlslShaderPassMeta>, ShaderSemantics), FilterChainError> { SpirvCross,
let (passes, semantics) = if !disable_cache { FilterChainError,
HLSL::compile_preset_passes::< >(shaders, &textures)?
CachedCompilation<SpirvCompilation>, } else {
SpirvCross, DXIL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
FilterChainError, shaders, &textures,
>(shaders, &textures)? )?
} else { };
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics)) Ok((passes, semantics))
}
pub type HlslShaderPassMeta =
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>;
pub fn compile_passes_hlsl(
shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<HlslShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
HLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics))
}
} }
use compile::{compile_passes_dxil, compile_passes_hlsl, DxilShaderPassMeta, HlslShaderPassMeta};
impl FilterChainD3D12 { impl FilterChainD3D12 {
/// 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 unsafe fn load_from_path( pub unsafe fn load_from_path(

View file

@ -63,29 +63,34 @@ pub struct FilterChainD3D9 {
default_options: FrameOptionsD3D9, default_options: FrameOptionsD3D9,
} }
type ShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>; use super::*;
pub type ShaderPassMeta =
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>;
fn compile_passes( pub fn compile_passes(
shaders: Vec<ShaderPassConfig>, shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig], textures: &[TextureConfig],
disable_cache: bool, disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> { ) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache { let (passes, semantics) = if !disable_cache {
HLSL::compile_preset_passes::< HLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>, CachedCompilation<SpirvCompilation>,
SpirvCross, SpirvCross,
FilterChainError, FilterChainError,
>(shaders, &textures)? >(shaders, &textures)?
} else { } else {
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>( HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures, shaders, &textures,
)? )?
}; };
Ok((passes, semantics)) Ok((passes, semantics))
}
} }
use compile::{compile_passes, ShaderPassMeta};
impl FilterChainD3D9 { impl FilterChainD3D9 {
fn init_passes( fn init_passes(
device: &IDirect3DDevice9, device: &IDirect3DDevice9,

View file

@ -92,28 +92,34 @@ impl<T: GLInterface> FilterChainImpl<T> {
} }
} }
type ShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<GLSL, SpirvCompilation, SpirvCross>>; use super::*;
fn compile_passes( pub type ShaderPassMeta =
shaders: Vec<ShaderPassConfig>, ShaderPassArtifact<impl CompileReflectShader<GLSL, SpirvCompilation, SpirvCross>>;
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
GLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
GLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics)) pub fn compile_passes(
shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
GLSL::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
GLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics))
}
} }
use compile::{compile_passes, ShaderPassMeta};
impl<T: GLInterface> FilterChainImpl<T> { impl<T: GLInterface> FilterChainImpl<T> {
/// Load a filter chain from a pre-parsed `ShaderPreset`. /// Load a filter chain from a pre-parsed `ShaderPreset`.
pub(crate) unsafe fn load_from_preset( pub(crate) unsafe fn load_from_preset(

View file

@ -187,4 +187,4 @@ impl FramebufferInterface for Gl46Framebuffer {
} }
Ok(()) Ok(())
} }
} }

View file

@ -40,19 +40,25 @@ use std::collections::VecDeque;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::path::Path; use std::path::Path;
type ShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<MSL, SpirvCompilation, SpirvCross> + Send>; use super::*;
fn compile_passes( pub type ShaderPassMeta =
shaders: Vec<ShaderPassConfig>, ShaderPassArtifact<impl CompileReflectShader<MSL, SpirvCompilation, SpirvCross> + Send>;
textures: &[TextureConfig],
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> { pub fn compile_passes(
let (passes, semantics) = shaders: Vec<ShaderPassConfig>,
MSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>( textures: &[TextureConfig],
shaders, &textures, ) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
)?; let (passes, semantics) =
Ok((passes, semantics)) MSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?;
Ok((passes, semantics))
}
} }
use compile::{compile_passes, ShaderPassMeta};
/// A Metal filter chain. /// A Metal filter chain.
pub struct FilterChainMetal { pub struct FilterChainMetal {
pub(crate) common: FilterCommon, pub(crate) common: FilterCommon,

View file

@ -209,28 +209,34 @@ impl Drop for FrameResiduals {
} }
} }
type ShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross> + Send>; use super::*;
fn compile_passes( pub type ShaderPassMeta =
shaders: Vec<ShaderPassConfig>, ShaderPassArtifact<impl CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross> + Send>;
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
SPIRV::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
SPIRV::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics)) pub fn compile_passes(
shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig],
disable_cache: bool,
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
let (passes, semantics) = if !disable_cache {
SPIRV::compile_preset_passes::<
CachedCompilation<SpirvCompilation>,
SpirvCross,
FilterChainError,
>(shaders, &textures)?
} else {
SPIRV::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
shaders, &textures,
)?
};
Ok((passes, semantics))
}
} }
use compile::{compile_passes, ShaderPassMeta};
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 unsafe fn load_from_path<V, E>( pub unsafe fn load_from_path<V, E>(

View file

@ -21,7 +21,7 @@ librashader-reflect = { path = "../librashader-reflect", version = "0.2.7", feat
librashader-runtime = { path = "../librashader-runtime" , version = "0.2.7" } librashader-runtime = { path = "../librashader-runtime" , version = "0.2.7" }
wgpu = { version = "0.20.0", default-features = false, features = ["wgsl"] } wgpu = { version = "0.20.0", default-features = false, features = ["wgsl"] }
image = "0.24.7" image = "0.25.1"
thiserror = "1.0.50" thiserror = "1.0.50"
bytemuck = { version = "1.14.0", features = ["derive"] } bytemuck = { version = "1.14.0", features = ["derive"] }
array-concat = "0.5.2" array-concat = "0.5.2"

View file

@ -38,19 +38,25 @@ use crate::options::{FilterChainOptionsWgpu, FrameOptionsWgpu};
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::texture::{InputImage, OwnedImage}; use crate::texture::{InputImage, OwnedImage};
type ShaderPassMeta = mod compile {
ShaderPassArtifact<impl CompileReflectShader<WGSL, SpirvCompilation, Naga> + Send>; use super::*;
fn compile_passes( pub type ShaderPassMeta =
shaders: Vec<ShaderPassConfig>, ShaderPassArtifact<impl CompileReflectShader<WGSL, SpirvCompilation, Naga> + Send>;
textures: &[TextureConfig],
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> { pub fn compile_passes(
let (passes, semantics) = shaders: Vec<ShaderPassConfig>,
WGSL::compile_preset_passes::<SpirvCompilation, Naga, FilterChainError>( textures: &[TextureConfig],
shaders, &textures, ) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
)?; let (passes, semantics) =
Ok((passes, semantics)) WGSL::compile_preset_passes::<SpirvCompilation, Naga, FilterChainError>(
shaders, &textures,
)?;
Ok((passes, semantics))
}
} }
use compile::{compile_passes, ShaderPassMeta};
/// A wgpu filter chain. /// A wgpu filter chain.
pub struct FilterChainWgpu { pub struct FilterChainWgpu {
pub(crate) common: FilterCommon, pub(crate) common: FilterCommon,

View file

@ -23,7 +23,7 @@ array-concat = "0.5.2"
tinymap = "0.4.0" tinymap = "0.4.0"
[dependencies.image] [dependencies.image]
version = "0.24.5" version = "0.25.1"
features = [ features = [
"gif", "jpeg", "png", "gif", "jpeg", "png",
"tga", "pnm", "tiff", "tga", "pnm", "tiff",

View file

@ -5,24 +5,27 @@ use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
use num_traits::AsPrimitive; use num_traits::AsPrimitive;
use std::ops::Mul; use std::ops::Mul;
pub const MAX_TEXEL_SIZE: f32 = 16384f32;
/// Trait for size scaling relative to the viewport. /// Trait for size scaling relative to the viewport.
pub trait ViewportSize<T> pub trait ViewportSize<T>
where where
T: Mul<ScaleFactor, Output = f32> + Copy + 'static, T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
f32: AsPrimitive<T>, f32: AsPrimitive<T>,
{ {
/// Produce a `Size<T>` scaled with the input scaling options. /// Produce a `Size<T>` scaled with the input scaling options.
/// The size will at minimum be 1x1, and at a maximum 16384x16384.
fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>, original: Size<T>) -> Size<T>; fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>, original: Size<T>) -> Size<T>;
} }
impl<T> ViewportSize<T> for Size<T> impl<T> ViewportSize<T> for Size<T>
where where
T: Mul<ScaleFactor, Output = f32> + Copy + 'static, T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
f32: AsPrimitive<T>, f32: AsPrimitive<T>,
{ {
fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>, original: Size<T>) -> Size<T> fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>, original: Size<T>) -> Size<T>
where where
T: Mul<ScaleFactor, Output = f32> + Copy + 'static, T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
f32: AsPrimitive<T>, f32: AsPrimitive<T>,
{ {
scaling::scale(scaling, self, viewport, original) scaling::scale(scaling, self, viewport, original)
@ -35,6 +38,7 @@ pub trait MipmapSize<T> {
fn calculate_miplevels(self) -> T; fn calculate_miplevels(self) -> T;
/// Scale the size according to the given mipmap level. /// Scale the size according to the given mipmap level.
/// The size will at minimum be 1x1, and at a maximum 16384x16384.
fn scale_mipmap(self, miplevel: T) -> Size<T>; fn scale_mipmap(self, miplevel: T) -> Size<T>;
} }
@ -59,7 +63,7 @@ impl MipmapSize<u32> for Size<u32> {
fn scale<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>, original: Size<T>) -> Size<T> fn scale<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>, original: Size<T>) -> Size<T>
where where
T: Mul<ScaleFactor, Output = f32> + Copy + 'static, T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
f32: AsPrimitive<T>, f32: AsPrimitive<T>,
{ {
let width = match scaling.x { let width = match scaling.x {
@ -101,8 +105,14 @@ where
}; };
Size { Size {
width: width.round().as_(), width: std::cmp::min(
height: height.round().as_(), std::cmp::max(width.round().as_(), 1f32.as_()),
MAX_TEXEL_SIZE.as_(),
),
height: std::cmp::min(
std::cmp::max(height.round().as_(), 1f32.as_()),
MAX_TEXEL_SIZE.as_(),
),
} }
} }