rt(mtl): common + shaderset for metal

This commit is contained in:
chyyran 2024-02-09 18:22:14 -05:00 committed by Ronny Chan
parent d5ef5904f3
commit 12d55e928e
8 changed files with 292 additions and 1 deletions

View file

@ -13,7 +13,7 @@ members = [
"librashader-cache",
"librashader-capi",
"librashader-build-script"
, "librashader-runtime-wgpu"]
, "librashader-runtime-wgpu", "librashader-runtime-metal"]
resolver = "2"
[workspace.metadata.release]

View file

@ -19,6 +19,7 @@ d3d12 = ["windows", "dxgi"]
dxgi = ["windows"]
vulkan = ["ash"]
wgpu = ["wgpu-types"]
metal = ["icrate"]
[dependencies]
gl = { version = "0.14.0", optional = true }
@ -37,3 +38,8 @@ features = [
"Win32_Graphics_Direct3D11",
"Win32_Graphics_Direct3D12",
]
[target.'cfg(macos)'.dependencies.icrate]
optional = true
version = "0.1.0"
features = ["Metal"]

View file

@ -24,8 +24,12 @@ pub mod d3d11;
#[cfg(all(target_os = "windows", feature = "d3d12"))]
pub mod d3d12;
#[cfg(all(target_os = "macos", feature = "metal"))]
pub mod metal;
mod viewport;
pub use viewport::Viewport;
use num_traits::AsPrimitive;

View file

@ -0,0 +1,95 @@
use icrate::Metal;
use crate::{Size, ImageFormat, FilterMode, WrapMode};
impl From<ImageFormat> for Metal::MTLPixelFormat {
fn from(format: ImageFormat) -> Self {
match format {
ImageFormat::Unknown => 0 as Metal::MTLPixelFormat,
ImageFormat::R8Unorm => Metal::MTLPixelFormatR8Unorm,
ImageFormat::R8Uint => Metal::MTLPixelFormatR8Uint,
ImageFormat::R8Sint => Metal::MTLPixelFormatR8Sint,
ImageFormat::R8G8Unorm => Metal::MTLPixelFormatRG8Unorm,
ImageFormat::R8G8Uint => Metal::MTLPixelFormatRG8Uint,
ImageFormat::R8G8Sint => Metal::MTLPixelFormatRG8Sint,
ImageFormat::R8G8B8A8Unorm => Metal::MTLPixelFormatRGBA8Unorm,
ImageFormat::R8G8B8A8Uint => Metal::MTLPixelFormatRGBA8Uint,
ImageFormat::R8G8B8A8Sint => Metal::MTLPixelFormatRGBA8Sint,
ImageFormat::R8G8B8A8Srgb => Metal::MTLPixelFormatRGBA8Unorm_sRGB,
ImageFormat::A2B10G10R10UnormPack32 => Metal::MTLPixelFormatRGB10A2Unorm,
ImageFormat::A2B10G10R10UintPack32 => Metal::MTLPixelFormatRGB10A2Uint,
ImageFormat::R16Uint => Metal::MTLPixelFormatR16Uint,
ImageFormat::R16Sint => Metal::MTLPixelFormatR16Sint,
ImageFormat::R16Sfloat => Metal::MTLPixelFormatR16Float,
ImageFormat::R16G16Uint => Metal::MTLPixelFormatRG16Uint,
ImageFormat::R16G16Sint => Metal::MTLPixelFormatRG16Sint,
ImageFormat::R16G16Sfloat => Metal::MTLPixelFormatRG16Float,
ImageFormat::R16G16B16A16Uint => Metal::MTLPixelFormatRGBA16Uint,
ImageFormat::R16G16B16A16Sint => Metal::MTLPixelFormatRGBA16Sint,
ImageFormat::R16G16B16A16Sfloat => Metal::MTLPixelFormatRGBA16Float,
ImageFormat::R32Uint => Metal::MTLPixelFormatR32Uint,
ImageFormat::R32Sint => Metal::MTLPixelFormatR32Sint,
ImageFormat::R32Sfloat => Metal::MTLPixelFormatR32Float,
ImageFormat::R32G32Uint => Metal::MTLPixelFormatRG32Uint,
ImageFormat::R32G32Sint => Metal::MTLPixelFormatRG32Sint,
ImageFormat::R32G32Sfloat => Metal::MTLPixelFormatRG32Float,
ImageFormat::R32G32B32A32Uint => Metal::MTLPixelFormatRGBA32Uint,
ImageFormat::R32G32B32A32Sint => Metal::MTLPixelFormatRGBA32Sint,
ImageFormat::R32G32B32A32Sfloat => Metal::MTLPixelFormatRGBA32Float,
}
}
}
impl From<Metal::MTLViewport> for Size<u32> {
fn from(value: Metal::MTLViewport) -> Self {
Size {
width: value.width as u32,
height: value.height as u32,
}
}
}
impl From<Size<u32>> for Metal::MTLViewport {
fn from(value: Size<u32>) -> Self {
Metal::MTLViewport {
originX: 0.0,
originY: 0.0,
width: value.width as f64,
height: value.height as f64,
znear: -1.0,
zfar: 1.0,
}
}
}
impl From<WrapMode> for Metal::MTLSamplerAddressMode {
fn from(value: WrapMode) -> Self {
match value {
WrapMode::ClampToBorder => Metal::MTLSamplerAddressModeClampToBorderColor,
WrapMode::ClampToEdge => Metal::MTLSamplerAddressModeClampToEdge,
WrapMode::Repeat => Metal::MTLSamplerAddressModeRepeat,
WrapMode::MirroredRepeat => Metal::MTLSamplerAddressModeMirrorRepeat,
}
}
}
impl From<FilterMode> for Metal::MTLSamplerMinMagFilter {
fn from(value: FilterMode) -> Self {
match value {
FilterMode::Linear => Metal::MTLSamplerMinMagFilterLinear,
_ => Metal::MTLSamplerMipFilterNearest,
}
}
}
impl FilterMode {
/// Get the mipmap filtering mode for the given combination.
pub fn mtl_mip(&self, mip: FilterMode) -> Metal::MTLSamplerMipFilter {
match self {
FilterMode::Linear => Metal::MTLSamplerMipFilterLinear,
FilterMode::Nearest => Metal::MTLSamplerMipFilterNearest,
}
}
}

View file

@ -0,0 +1,30 @@
[package]
name = "librashader-runtime-metal"
edition = "2021"
license = "MPL-2.0 OR GPL-3.0-only"
version = "0.2.0-beta.7"
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
repository = "https://github.com/SnowflakePowered/librashader"
readme = "../README.md"
categories = ["emulators", "compilers", "graphics"]
keywords = ["shader", "retroarch", "SPIR-V"]
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-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" ]}
[dev-dependencies]
[package.metadata.docs.rs]
features = ["librashader-cache/docsrs"]

View file

@ -0,0 +1 @@
mod samplers;

View file

@ -0,0 +1,71 @@
use icrate::Metal::{MTLDevice, MTLSamplerDescriptor, MTLSamplerState};
use objc2::rc::Id;
use objc2::runtime::ProtocolObject;
use rustc_hash::FxHashMap;
use librashader_common::{FilterMode, WrapMode};
pub struct SamplerSet {
// todo: may need to deal with differences in mip filter.
samplers: FxHashMap<(WrapMode, FilterMode, FilterMode), Id<ProtocolObject<dyn MTLSamplerState>>>,
}
impl SamplerSet {
#[inline(always)]
pub fn get(&self, wrap: WrapMode, filter: FilterMode, mipmap: FilterMode) -> Id<ProtocolObject<dyn MTLSamplerState>>> {
// 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(),
)
}
}
pub fn new(device: &dyn MTLDevice) -> SamplerSet {
let mut samplers = FxHashMap::default();
let wrap_modes = &[
WrapMode::ClampToBorder,
WrapMode::ClampToEdge,
WrapMode::Repeat,
WrapMode::MirroredRepeat,
];
for wrap_mode in wrap_modes {
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),
device.newSamplerStateWithDescriptor(&MTLSamplerDescriptor {
})
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),
})),
);
}
}
}
// assert all samplers were created.
assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2);
SamplerSet { samplers }
}
}

View file

@ -0,0 +1,84 @@
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 crate::error::{FilterChainError, Result};
pub struct SamplerSet {
// todo: may need to deal with differences in mip filter.
samplers:
FxHashMap<(WrapMode, FilterMode, FilterMode), Id<ProtocolObject<dyn MTLSamplerState>>>,
}
impl SamplerSet {
#[inline(always)]
pub fn get(
&self,
wrap: WrapMode,
filter: FilterMode,
mipmap: FilterMode,
) -> &ProtocolObject<dyn MTLSamplerState> {
// eprintln!("{wrap}, {filter}, {mip}");
// SAFETY: the sampler set is complete for the matrix
// wrap x filter x mipmap
let id: &Id<ProtocolObject<dyn MTLSamplerState>> = unsafe {
self.samplers
.get(&(wrap, filter, mipmap))
.unwrap_unchecked()
};
id.as_ref()
}
pub fn new(device: &ProtocolObject<dyn MTLDevice>) -> Result<SamplerSet> {
let mut samplers = FxHashMap::default();
let wrap_modes = &[
WrapMode::ClampToBorder,
WrapMode::ClampToEdge,
WrapMode::Repeat,
WrapMode::MirroredRepeat,
];
for wrap_mode in wrap_modes {
for filter_mode in &[FilterMode::Linear, FilterMode::Nearest] {
for mipmap_filter in &[FilterMode::Linear, FilterMode::Nearest] {
let descriptor = MTLSamplerDescriptor::new();
descriptor.setRAddressMode(MTLSamplerAddressMode::from(*wrap_mode));
descriptor.setSAddressMode(MTLSamplerAddressMode::from(*wrap_mode));
descriptor.setTAddressMode(MTLSamplerAddressMode::from(*wrap_mode));
descriptor.setMagFilter(MTLSamplerMinMagFilter::from(*filter_mode));
descriptor.setMinFilter(MTLSamplerMinMagFilter::from(*filter_mode));
descriptor.setMipFilter(filter_mode.mtl_mip(*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);
Ok(SamplerSet { samplers })
}
}