rt(mtl): drawquad
This commit is contained in:
parent
12d55e928e
commit
f40df9a54a
|
@ -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"]
|
|
@ -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;
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
68
librashader-runtime-metal/src/draw_quad.rs
Normal file
68
librashader-runtime-metal/src/draw_quad.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
librashader-runtime-metal/src/error.rs
Normal file
29
librashader-runtime-metal/src/error.rs
Normal 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>;
|
|
@ -1 +1,3 @@
|
||||||
|
mod error;
|
||||||
mod samplers;
|
mod samplers;
|
||||||
|
mod draw_quad;
|
||||||
|
|
|
@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue