rt(mtl): drawquad

This commit is contained in:
chyyran 2024-02-10 00:04:16 -05:00 committed by Ronny Chan
parent 12d55e928e
commit f40df9a54a
7 changed files with 159 additions and 42 deletions

View file

@ -39,7 +39,7 @@ features = [
"Win32_Graphics_Direct3D12", "Win32_Graphics_Direct3D12",
] ]
[target.'cfg(macos)'.dependencies.icrate] [target.'cfg(target_vendor="apple")'.dependencies.icrate]
optional = true optional = true
version = "0.1.0" version = "0.1.0"
features = ["Metal"] features = ["Metal", "Metal_all"]

View file

@ -24,7 +24,7 @@ pub mod d3d11;
#[cfg(all(target_os = "windows", feature = "d3d12"))] #[cfg(all(target_os = "windows", feature = "d3d12"))]
pub mod d3d12; pub mod d3d12;
#[cfg(all(target_os = "macos", feature = "metal"))] #[cfg(all(target_vendor = "apple", feature = "metal"))]
pub mod metal; pub mod metal;
mod viewport; mod viewport;

View file

@ -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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [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-presets = { path = "../librashader-presets", version = "0.2.0-beta.7" }
librashader-preprocess = { path = "../librashader-preprocess", 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-reflect = { path = "../librashader-reflect", version = "0.2.0-beta.7" }
librashader-runtime = { path = "../librashader-runtime" , 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] [dev-dependencies]

View file

@ -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<ProtocolObject<dyn MTLBuffer>>,
}
impl DrawQuad {
pub fn new(device: &ProtocolObject<dyn MTLDevice>) -> Result<DrawQuad> {
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<dyn MTLRenderCommandEncoder>, 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);
}
}
}

View file

@ -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<T> = std::result::Result<T, FilterChainError>;

View file

@ -1 +1,3 @@
mod error;
mod samplers; mod samplers;
mod draw_quad;

View file

@ -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::rc::Id;
use objc2::runtime::ProtocolObject; use objc2::runtime::ProtocolObject;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use librashader_common::{FilterMode, WrapMode};
use crate::error::{FilterChainError, Result};
pub struct SamplerSet { pub struct SamplerSet {
// todo: may need to deal with differences in mip filter. // todo: may need to deal with differences in mip filter.
samplers: FxHashMap<(WrapMode, FilterMode, FilterMode), Id<ProtocolObject<dyn MTLSamplerState>>>, samplers:
FxHashMap<(WrapMode, FilterMode, FilterMode), Id<ProtocolObject<dyn MTLSamplerState>>>,
} }
impl SamplerSet { impl SamplerSet {
#[inline(always)] #[inline(always)]
pub fn get(&self, wrap: WrapMode, filter: FilterMode, mipmap: FilterMode) -> Id<ProtocolObject<dyn MTLSamplerState>>> { pub fn get(
&self,
wrap: WrapMode,
filter: FilterMode,
mipmap: FilterMode,
) -> &ProtocolObject<dyn MTLSamplerState> {
// eprintln!("{wrap}, {filter}, {mip}"); // eprintln!("{wrap}, {filter}, {mip}");
// SAFETY: the sampler set is complete for the matrix // SAFETY: the sampler set is complete for the matrix
// wrap x filter x mipmap // wrap x filter x mipmap
unsafe { let id: &Id<ProtocolObject<dyn MTLSamplerState>> = unsafe {
Id::clone( self
&self
.samplers .samplers
.get(&(wrap, filter, mipmap)) .get(&(wrap, filter, mipmap))
.unwrap_unchecked(), .unwrap_unchecked()
) };
}
id.as_ref()
} }
pub fn new(device: &dyn MTLDevice) -> SamplerSet { pub fn new(device: &ProtocolObject<dyn MTLDevice>) -> Result<SamplerSet> {
let mut samplers = FxHashMap::default(); let mut samplers = FxHashMap::default();
let wrap_modes = &[ let wrap_modes = &[
WrapMode::ClampToBorder, WrapMode::ClampToBorder,
@ -37,35 +49,37 @@ impl SamplerSet {
for filter_mode in &[FilterMode::Linear, FilterMode::Nearest] { for filter_mode in &[FilterMode::Linear, FilterMode::Nearest] {
for mipmap_filter in &[FilterMode::Linear, FilterMode::Nearest] { for mipmap_filter in &[FilterMode::Linear, FilterMode::Nearest] {
let descriptor = MTLSamplerDescriptor::new(); let descriptor = MTLSamplerDescriptor::new();
descriptor descriptor.setRAddressMode(MTLSamplerAddressMode::from(*wrap_mode));
.setRAddressMode(MTLSamplerA) descriptor.setSAddressMode(MTLSamplerAddressMode::from(*wrap_mode));
samplers.insert( descriptor.setTAddressMode(MTLSamplerAddressMode::from(*wrap_mode));
(*wrap_mode, *filter_mode, *mipmap_filter),
device.newSamplerStateWithDescriptor(&MTLSamplerDescriptor { descriptor.setMagFilter(MTLSamplerMinMagFilter::from(*filter_mode));
}) descriptor.setMinFilter(MTLSamplerMinMagFilter::from(*filter_mode));
Arc::new(device.create_sampler(&SamplerDescriptor { descriptor.setMipFilter(MTLSamplerMinMagFilter::from(*mipmap_filter));
label: None, descriptor.setLodMinClamp(0.0);
address_mode_u: (*wrap_mode).into(), descriptor.setLodMaxClamp(1000.0);
address_mode_v: (*wrap_mode).into(), descriptor.setCompareFunction(MTLCompareFunctionNever);
address_mode_w: (*wrap_mode).into(), descriptor.setMaxAnisotropy(1);
mag_filter: (*filter_mode).into(), descriptor.setBorderColor(MTLSamplerBorderColorTransparentBlack);
min_filter: (*filter_mode).into(), descriptor.setNormalizedCoordinates(true);
mipmap_filter: (*mipmap_filter).into(),
lod_min_clamp: 0.0, let Some(sampler_state) = device.newSamplerStateWithDescriptor(&descriptor)
lod_max_clamp: 1000.0, else {
compare: None, return Err(FilterChainError::SamplerError(
anisotropy_clamp: 1, *wrap_mode,
border_color: Some(SamplerBorderColor::TransparentBlack), *filter_mode,
})), *mipmap_filter,
); ));
};
samplers.insert((*wrap_mode, *filter_mode, *mipmap_filter), sampler_state);
} }
} }
} }
// assert all samplers were created. // assert all samplers were created.
assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2); assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2);
SamplerSet { samplers } Ok(SamplerSet { samplers })
} }
} }