rt(mtl): common + shaderset for metal
This commit is contained in:
parent
d5ef5904f3
commit
12d55e928e
|
@ -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]
|
||||
|
|
|
@ -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"]
|
|
@ -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;
|
||||
|
|
95
librashader-common/src/metal.rs
Normal file
95
librashader-common/src/metal.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
30
librashader-runtime-metal/Cargo.toml
Normal file
30
librashader-runtime-metal/Cargo.toml
Normal 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"]
|
1
librashader-runtime-metal/src/lib.rs
Normal file
1
librashader-runtime-metal/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
|||
mod samplers;
|
71
librashader-runtime-metal/src/samplers.rs
Normal file
71
librashader-runtime-metal/src/samplers.rs
Normal 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 }
|
||||
}
|
||||
}
|
84
librashader-runtime-mtl/src/samplers.rs
Normal file
84
librashader-runtime-mtl/src/samplers.rs
Normal 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 })
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue