From f40df9a54ab176a7718c765dc511355316319e87 Mon Sep 17 00:00:00 2001 From: chyyran Date: Sat, 10 Feb 2024 00:04:16 -0500 Subject: [PATCH] rt(mtl): drawquad --- librashader-common/Cargo.toml | 4 +- librashader-common/src/lib.rs | 2 +- librashader-runtime-metal/Cargo.toml | 10 ++- librashader-runtime-metal/src/draw_quad.rs | 68 ++++++++++++++++++ librashader-runtime-metal/src/error.rs | 29 ++++++++ librashader-runtime-metal/src/lib.rs | 4 +- librashader-runtime-metal/src/samplers.rs | 84 +++++++++++++--------- 7 files changed, 159 insertions(+), 42 deletions(-) create mode 100644 librashader-runtime-metal/src/draw_quad.rs create mode 100644 librashader-runtime-metal/src/error.rs diff --git a/librashader-common/Cargo.toml b/librashader-common/Cargo.toml index e90e94d..3b882a7 100644 --- a/librashader-common/Cargo.toml +++ b/librashader-common/Cargo.toml @@ -39,7 +39,7 @@ features = [ "Win32_Graphics_Direct3D12", ] -[target.'cfg(macos)'.dependencies.icrate] +[target.'cfg(target_vendor="apple")'.dependencies.icrate] optional = true version = "0.1.0" -features = ["Metal"] \ No newline at end of file +features = ["Metal", "Metal_all"] \ No newline at end of file diff --git a/librashader-common/src/lib.rs b/librashader-common/src/lib.rs index ffebe60..bf32ff0 100644 --- a/librashader-common/src/lib.rs +++ b/librashader-common/src/lib.rs @@ -24,7 +24,7 @@ pub mod d3d11; #[cfg(all(target_os = "windows", feature = "d3d12"))] pub mod d3d12; -#[cfg(all(target_os = "macos", feature = "metal"))] +#[cfg(all(target_vendor = "apple", feature = "metal"))] pub mod metal; mod viewport; diff --git a/librashader-runtime-metal/Cargo.toml b/librashader-runtime-metal/Cargo.toml index e230e6d..d95a0a8 100644 --- a/librashader-runtime-metal/Cargo.toml +++ b/librashader-runtime-metal/Cargo.toml @@ -14,14 +14,18 @@ description = "RetroArch shaders for all." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -librashader-common = { path = "../librashader-common", features = ["vulkan"], version = "0.2.0-beta.7" } +librashader-common = { path = "../librashader-common", features = ["metal"], version = "0.2.0-beta.7" } librashader-presets = { path = "../librashader-presets", version = "0.2.0-beta.7" } librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.0-beta.7" } librashader-reflect = { path = "../librashader-reflect", version = "0.2.0-beta.7" } librashader-runtime = { path = "../librashader-runtime" , version = "0.2.0-beta.7" } -librashader-cache = { path = "../librashader-cache", version = "0.2.0-beta.7" } -icrate = { version = "0.1.0" , features = [ "Metal", "Metal_all", "MetalKit" ]} +icrate = { version = "0.1.0" , features = [ "Metal", "Metal_all" ]} +objc2 = { version = "0.5.0", features = ["apple"] } +rustc-hash = "1.1.0" +thiserror = "1.0" +array-concat = "0.5.2" +bytemuck = "1.14.3" [dev-dependencies] diff --git a/librashader-runtime-metal/src/draw_quad.rs b/librashader-runtime-metal/src/draw_quad.rs new file mode 100644 index 0000000..6088f9f --- /dev/null +++ b/librashader-runtime-metal/src/draw_quad.rs @@ -0,0 +1,68 @@ +use std::ffi::c_void; +use std::ptr::NonNull; +use array_concat::concat_arrays; +use icrate::Metal::{MTLBuffer, MTLDevice, MTLPrimitiveTypeTriangleStrip, MTLRenderCommandEncoder, MTLResourceStorageModeManaged, MTLResourceStorageModeShared}; +use objc2::rc::Id; +use objc2::runtime::ProtocolObject; +use librashader_runtime::quad::QuadType; + +use crate::error::{FilterChainError, Result}; + +#[rustfmt::skip] +const VBO_OFFSCREEN: [f32; 16] = [ + // Offscreen + -1.0f32, -1.0, 0.0, 0.0, + -1.0, 1.0, 0.0, 1.0, + 1.0, -1.0, 1.0, 0.0, + 1.0, 1.0, 1.0, 1.0, +]; + +#[rustfmt::skip] +const VBO_DEFAULT_FINAL: [f32; 16] = [ + // Final + 0.0f32, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 1.0, + 1.0, 0.0, 1.0, 0.0, + 1.0, 1.0, 1.0, 1.0, +]; + +const VBO_DATA: [f32; 32] = concat_arrays!(VBO_OFFSCREEN, VBO_DEFAULT_FINAL); + +pub struct DrawQuad { + buffer: Id>, +} + +impl DrawQuad { + pub fn new(device: &ProtocolObject) -> Result { + let vbo_data: &'static [u8] = bytemuck::cast_slice(&VBO_DATA); + let Some(buffer) = (unsafe { + device.newBufferWithBytes_length_options( + // SAFETY: this pointer is const. + // https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes + NonNull::new_unchecked(vbo_data.as_ptr() as *mut c_void), + vbo_data.len(), + if cfg!(target_os = "ios") { + MTLResourceStorageModeShared + } else { + MTLResourceStorageModeManaged + } + ) + }) else { + return Err(FilterChainError::BufferError) + }; + + Ok(DrawQuad { buffer }) + } + + pub fn draw_quad(&self, cmd: &ProtocolObject, vbo: QuadType) { + let offset = match vbo { + QuadType::Offscreen => 0, + QuadType::Final => 4, + }; + + unsafe { + cmd.setVertexBuffer_offset_atIndex(Some(self.buffer.as_ref()), 0, 0); + cmd.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveTypeTriangleStrip, offset, 4); + } + } +} diff --git a/librashader-runtime-metal/src/error.rs b/librashader-runtime-metal/src/error.rs new file mode 100644 index 0000000..a7a3bfc --- /dev/null +++ b/librashader-runtime-metal/src/error.rs @@ -0,0 +1,29 @@ +//! Metal shader runtime errors. +use librashader_preprocess::PreprocessError; +use librashader_presets::ParsePresetError; +use librashader_reflect::error::{ShaderCompileError, ShaderReflectError}; +use librashader_runtime::image::ImageError; +use thiserror::Error; +use librashader_common::{FilterMode, WrapMode}; + +/// Cumulative error type for Metal filter chains. +#[derive(Error, Debug)] +pub enum FilterChainError { + #[error("shader preset parse error")] + ShaderPresetError(#[from] ParsePresetError), + #[error("shader preprocess error")] + ShaderPreprocessError(#[from] PreprocessError), + #[error("shader compile error")] + ShaderCompileError(#[from] ShaderCompileError), + #[error("shader reflect error")] + ShaderReflectError(#[from] ShaderReflectError), + #[error("lut loading error")] + LutLoadError(#[from] ImageError), + #[error("sampler create error")] + SamplerError(WrapMode, FilterMode, FilterMode), + #[error("buffer creation error")] + BufferError +} + +/// Result type for Metal filter chains. +pub type Result = std::result::Result; diff --git a/librashader-runtime-metal/src/lib.rs b/librashader-runtime-metal/src/lib.rs index 5dc20bb..4101c7a 100644 --- a/librashader-runtime-metal/src/lib.rs +++ b/librashader-runtime-metal/src/lib.rs @@ -1 +1,3 @@ -mod samplers; \ No newline at end of file +mod error; +mod samplers; +mod draw_quad; diff --git a/librashader-runtime-metal/src/samplers.rs b/librashader-runtime-metal/src/samplers.rs index 390a296..1153ce3 100644 --- a/librashader-runtime-metal/src/samplers.rs +++ b/librashader-runtime-metal/src/samplers.rs @@ -1,31 +1,43 @@ -use icrate::Metal::{MTLDevice, MTLSamplerDescriptor, MTLSamplerState}; +use icrate::Metal::{ + MTLCompareFunctionNever, MTLDevice, MTLSamplerAddressMode, + MTLSamplerBorderColorTransparentBlack, MTLSamplerDescriptor, MTLSamplerMinMagFilter, + MTLSamplerState, +}; +use librashader_common::{FilterMode, WrapMode}; use objc2::rc::Id; use objc2::runtime::ProtocolObject; use rustc_hash::FxHashMap; -use librashader_common::{FilterMode, WrapMode}; + +use crate::error::{FilterChainError, Result}; pub struct SamplerSet { // todo: may need to deal with differences in mip filter. - samplers: FxHashMap<(WrapMode, FilterMode, FilterMode), Id>>, + samplers: + FxHashMap<(WrapMode, FilterMode, FilterMode), Id>>, } impl SamplerSet { #[inline(always)] - pub fn get(&self, wrap: WrapMode, filter: FilterMode, mipmap: FilterMode) -> Id>> { + pub fn get( + &self, + wrap: WrapMode, + filter: FilterMode, + mipmap: FilterMode, + ) -> &ProtocolObject { // eprintln!("{wrap}, {filter}, {mip}"); // SAFETY: the sampler set is complete for the matrix // wrap x filter x mipmap - unsafe { - Id::clone( - &self - .samplers - .get(&(wrap, filter, mipmap)) - .unwrap_unchecked(), - ) - } + let id: &Id> = unsafe { + self + .samplers + .get(&(wrap, filter, mipmap)) + .unwrap_unchecked() + }; + + id.as_ref() } - pub fn new(device: &dyn MTLDevice) -> SamplerSet { + pub fn new(device: &ProtocolObject) -> Result { let mut samplers = FxHashMap::default(); let wrap_modes = &[ WrapMode::ClampToBorder, @@ -37,35 +49,37 @@ impl SamplerSet { for filter_mode in &[FilterMode::Linear, FilterMode::Nearest] { for mipmap_filter in &[FilterMode::Linear, FilterMode::Nearest] { let descriptor = MTLSamplerDescriptor::new(); - descriptor - .setRAddressMode(MTLSamplerA) - samplers.insert( - (*wrap_mode, *filter_mode, *mipmap_filter), + descriptor.setRAddressMode(MTLSamplerAddressMode::from(*wrap_mode)); + descriptor.setSAddressMode(MTLSamplerAddressMode::from(*wrap_mode)); + descriptor.setTAddressMode(MTLSamplerAddressMode::from(*wrap_mode)); - device.newSamplerStateWithDescriptor(&MTLSamplerDescriptor { + descriptor.setMagFilter(MTLSamplerMinMagFilter::from(*filter_mode)); - }) - Arc::new(device.create_sampler(&SamplerDescriptor { - label: None, - address_mode_u: (*wrap_mode).into(), - address_mode_v: (*wrap_mode).into(), - address_mode_w: (*wrap_mode).into(), - mag_filter: (*filter_mode).into(), - min_filter: (*filter_mode).into(), - mipmap_filter: (*mipmap_filter).into(), - lod_min_clamp: 0.0, - lod_max_clamp: 1000.0, - compare: None, - anisotropy_clamp: 1, - border_color: Some(SamplerBorderColor::TransparentBlack), - })), - ); + descriptor.setMinFilter(MTLSamplerMinMagFilter::from(*filter_mode)); + descriptor.setMipFilter(MTLSamplerMinMagFilter::from(*mipmap_filter)); + descriptor.setLodMinClamp(0.0); + descriptor.setLodMaxClamp(1000.0); + descriptor.setCompareFunction(MTLCompareFunctionNever); + descriptor.setMaxAnisotropy(1); + descriptor.setBorderColor(MTLSamplerBorderColorTransparentBlack); + descriptor.setNormalizedCoordinates(true); + + let Some(sampler_state) = device.newSamplerStateWithDescriptor(&descriptor) + else { + return Err(FilterChainError::SamplerError( + *wrap_mode, + *filter_mode, + *mipmap_filter, + )); + }; + + samplers.insert((*wrap_mode, *filter_mode, *mipmap_filter), sampler_state); } } } // assert all samplers were created. assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2); - SamplerSet { samplers } + Ok(SamplerSet { samplers }) } }