rt(mtl): drawquad
This commit is contained in:
parent
12d55e928e
commit
f40df9a54a
|
@ -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"]
|
||||
features = ["Metal", "Metal_all"]
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
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 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::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<ProtocolObject<dyn MTLSamplerState>>>,
|
||||
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>>> {
|
||||
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
|
||||
unsafe {
|
||||
Id::clone(
|
||||
&self
|
||||
let id: &Id<ProtocolObject<dyn MTLSamplerState>> = unsafe {
|
||||
self
|
||||
.samplers
|
||||
.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 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 })
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue