test(mtl): update the metal test
This commit is contained in:
parent
50580cfc3b
commit
fe48fd03a5
70
Cargo.lock
generated
70
Cargo.lock
generated
|
@ -1859,8 +1859,11 @@ dependencies = [
|
||||||
"librashader-reflect",
|
"librashader-reflect",
|
||||||
"librashader-runtime",
|
"librashader-runtime",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
|
"objc2-app-kit",
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
"objc2-metal",
|
"objc2-metal",
|
||||||
|
"objc2-metal-kit",
|
||||||
|
"objc2-quartz-core",
|
||||||
"rayon",
|
"rayon",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -2291,6 +2294,46 @@ dependencies = [
|
||||||
"objc2-encode 4.0.3",
|
"objc2-encode 4.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-app-kit"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"block2 0.5.1",
|
||||||
|
"libc",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-core-data",
|
||||||
|
"objc2-core-image",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-quartz-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-core-data"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"block2 0.5.1",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-core-image"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
|
||||||
|
dependencies = [
|
||||||
|
"block2 0.5.1",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-metal",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-encode"
|
name = "objc2-encode"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
|
@ -2327,6 +2370,33 @@ dependencies = [
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-metal-kit"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e9ec2524854019d1ff69d0a3222ce930a1bdf31add9b58ef8cffb6a37eead6c"
|
||||||
|
dependencies = [
|
||||||
|
"block2 0.5.1",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-app-kit",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-metal",
|
||||||
|
"objc2-quartz-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-quartz-core"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"block2 0.5.1",
|
||||||
|
"objc2 0.5.2",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-metal",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
//! is supported.
|
//! is supported.
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
|
||||||
#![allow(stable_features)]
|
#![allow(stable_features)]
|
||||||
#![feature(c_str_literals)]
|
#![feature(c_str_literals)]
|
||||||
/// Shader codegen backends.
|
/// Shader codegen backends.
|
||||||
|
|
|
@ -39,8 +39,12 @@ objc2-metal = { workspace = true, features = ["all"] }
|
||||||
objc2 = { workspace = true, features = ["apple"] }
|
objc2 = { workspace = true, features = ["apple"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
||||||
# run_test = ["icrate/AppKit", "i "icrate/Foundation_all", "icrate/MetalKit", "icrate/MetalKit_all"]
|
# run_test = ["icrate/AppKit", "i "icrate/Foundation_all", "icrate/MetalKit", "icrate/MetalKit_all"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
objc2-metal-kit = { version = "0.2", features = ["all"]}
|
||||||
|
objc2-foundation = { version = "0.2", features = ["all"] }
|
||||||
|
objc2-app-kit = { version = "0.2", features = ["all"] }
|
||||||
|
objc2-quartz-core = { version = "0.2", features = ["CAMetalLayer", "objc2-metal"]}
|
||||||
#[lib]
|
#[lib]
|
||||||
#crate-type = ["lib", "staticlib"]
|
#crate-type = ["lib", "staticlib"]
|
|
@ -1,32 +1,29 @@
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::{cell::OnceCell, ptr::NonNull};
|
use core::{cell::OnceCell, ptr::NonNull};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use icrate::Metal::{
|
use objc2_app_kit::{
|
||||||
MTLBlitCommandEncoder, MTLClearColor, MTLTexture, MTLTextureDescriptor,
|
NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate, NSBackingStoreType,
|
||||||
MTLTextureUsageRenderTarget,
|
NSWindow, NSWindowStyleMask,
|
||||||
};
|
};
|
||||||
use icrate::{
|
use objc2_metal::{
|
||||||
AppKit::{
|
MTLBlitCommandEncoder, MTLClearColor, MTLTexture, MTLTextureDescriptor, MTLTextureUsage,
|
||||||
NSApplication, NSApplicationActivationPolicyRegular, NSApplicationDelegate,
|
|
||||||
NSBackingStoreBuffered, NSWindow, NSWindowStyleMaskClosable, NSWindowStyleMaskResizable,
|
|
||||||
NSWindowStyleMaskTitled,
|
|
||||||
},
|
|
||||||
Foundation::{
|
|
||||||
ns_string, MainThreadMarker, NSDate, NSNotification, NSObject, NSObjectProtocol, NSPoint,
|
|
||||||
NSRect, NSSize,
|
|
||||||
},
|
|
||||||
Metal::{
|
|
||||||
MTLCommandBuffer, MTLCommandEncoder, MTLCommandQueue, MTLCreateSystemDefaultDevice,
|
|
||||||
MTLDevice, MTLDrawable, MTLLibrary, MTLPrimitiveTypeTriangle, MTLRenderCommandEncoder,
|
|
||||||
MTLRenderPipelineDescriptor, MTLRenderPipelineState,
|
|
||||||
},
|
|
||||||
MetalKit::{MTKView, MTKViewDelegate},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use objc2_foundation::{
|
||||||
|
ns_string, MainThreadMarker, NSDate, NSNotification, NSObject, NSObjectProtocol, NSPoint,
|
||||||
|
NSRect, NSSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use objc2_metal::{
|
||||||
|
MTLCommandBuffer, MTLCommandEncoder, MTLCommandQueue, MTLCreateSystemDefaultDevice, MTLDevice,
|
||||||
|
MTLDrawable, MTLLibrary, MTLPrimitiveType, MTLRenderCommandEncoder,
|
||||||
|
MTLRenderPipelineDescriptor, MTLRenderPipelineState,
|
||||||
|
};
|
||||||
|
use objc2_metal_kit::{MTKView, MTKViewDelegate};
|
||||||
|
|
||||||
use librashader_common::Viewport;
|
use librashader_common::Viewport;
|
||||||
use librashader_presets::ShaderPreset;
|
use librashader_presets::ShaderPreset;
|
||||||
use librashader_runtime_metal::FilterChainMetal;
|
use librashader_runtime_mtl::FilterChainMetal;
|
||||||
use objc2::{
|
use objc2::{
|
||||||
declare_class, msg_send_id, mutability::MainThreadOnly, rc::Id, runtime::ProtocolObject,
|
declare_class, msg_send_id, mutability::MainThreadOnly, rc::Id, runtime::ProtocolObject,
|
||||||
ClassType, DeclaredClass,
|
ClassType, DeclaredClass,
|
||||||
|
@ -161,10 +158,10 @@ declare_class!(
|
||||||
// create the app window
|
// create the app window
|
||||||
let window = {
|
let window = {
|
||||||
let content_rect = NSRect::new(NSPoint::new(0., 0.), NSSize::new(768., 768.));
|
let content_rect = NSRect::new(NSPoint::new(0., 0.), NSSize::new(768., 768.));
|
||||||
let style = NSWindowStyleMaskClosable
|
let style = NSWindowStyleMask::Closable
|
||||||
| NSWindowStyleMaskResizable
|
| NSWindowStyleMask::Resizable
|
||||||
| NSWindowStyleMaskTitled;
|
| NSWindowStyleMask::Titled;
|
||||||
let backing_store_type = NSBackingStoreBuffered;
|
let backing_store_type = NSBackingStoreType::NSBackingStoreBuffered;
|
||||||
let flag = false;
|
let flag = false;
|
||||||
unsafe {
|
unsafe {
|
||||||
NSWindow::initWithContentRect_styleMask_backing_defer(
|
NSWindow::initWithContentRect_styleMask_backing_defer(
|
||||||
|
@ -362,7 +359,7 @@ declare_class!(
|
||||||
// configure the encoder with the pipeline and draw the triangle
|
// configure the encoder with the pipeline and draw the triangle
|
||||||
encoder.setRenderPipelineState(pipeline_state);
|
encoder.setRenderPipelineState(pipeline_state);
|
||||||
unsafe {
|
unsafe {
|
||||||
encoder.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveTypeTriangle, 0, 3)
|
encoder.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveType::Triangle, 0, 3)
|
||||||
};
|
};
|
||||||
encoder.endEncoding();
|
encoder.endEncoding();
|
||||||
|
|
||||||
|
@ -382,7 +379,7 @@ declare_class!(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
tex_desc.setUsage(MTLTextureUsageRenderTarget);
|
tex_desc.setUsage(MTLTextureUsage::RenderTarget);
|
||||||
// let frontbuffer = command_queue
|
// let frontbuffer = command_queue
|
||||||
// .device()
|
// .device()
|
||||||
// .newTextureWithDescriptor(&tex_desc)
|
// .newTextureWithDescriptor(&tex_desc)
|
||||||
|
@ -447,7 +444,7 @@ fn main() {
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
let mtm = MainThreadMarker::new().unwrap();
|
||||||
// configure the app
|
// configure the app
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
let app = NSApplication::sharedApplication(mtm);
|
||||||
app.setActivationPolicy(NSApplicationActivationPolicyRegular);
|
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
|
||||||
|
|
||||||
// configure the application delegate
|
// configure the application delegate
|
||||||
let delegate = Delegate::new(mtm);
|
let delegate = Delegate::new(mtm);
|
||||||
|
|
|
@ -1,488 +1,6 @@
|
||||||
#![cfg(feature = "run_test")]
|
include!("./hello_triangle/main.rs");
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::{cell::OnceCell, ptr::NonNull};
|
#[test]
|
||||||
use std::sync::RwLock;
|
fn test_mtl() {
|
||||||
|
main()
|
||||||
use icrate::Foundation::NSString;
|
|
||||||
use icrate::Metal::{
|
|
||||||
MTLBlitCommandEncoder, MTLClearColor, MTLResource, MTLTexture, MTLTextureDescriptor,
|
|
||||||
MTLTextureUsagePixelFormatView, MTLTextureUsageRenderTarget, MTLTextureUsageShaderRead,
|
|
||||||
};
|
|
||||||
use icrate::{
|
|
||||||
AppKit::{
|
|
||||||
NSApplication, NSApplicationActivationPolicyRegular, NSApplicationDelegate,
|
|
||||||
NSBackingStoreBuffered, NSWindow, NSWindowStyleMaskClosable, NSWindowStyleMaskResizable,
|
|
||||||
NSWindowStyleMaskTitled,
|
|
||||||
},
|
|
||||||
Foundation::{
|
|
||||||
ns_string, MainThreadMarker, NSDate, NSNotification, NSObject, NSObjectProtocol, NSPoint,
|
|
||||||
NSRect, NSSize,
|
|
||||||
},
|
|
||||||
Metal::{
|
|
||||||
MTLCommandBuffer, MTLCommandEncoder, MTLCommandQueue, MTLCreateSystemDefaultDevice,
|
|
||||||
MTLDevice, MTLDrawable, MTLLibrary, MTLPrimitiveTypeTriangle, MTLRenderCommandEncoder,
|
|
||||||
MTLRenderPipelineDescriptor, MTLRenderPipelineState,
|
|
||||||
},
|
|
||||||
MetalKit::{MTKView, MTKViewDelegate},
|
|
||||||
};
|
|
||||||
use librashader_common::Viewport;
|
|
||||||
use librashader_presets::ShaderPreset;
|
|
||||||
use librashader_runtime_mtl::FilterChainMetal;
|
|
||||||
use objc2::{
|
|
||||||
declare_class, msg_send_id, mutability::MainThreadOnly, rc::Id, runtime::ProtocolObject,
|
|
||||||
ClassType, DeclaredClass,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
const SHADERS: &str = r#"
|
|
||||||
#include <metal_stdlib>
|
|
||||||
|
|
||||||
struct SceneProperties {
|
|
||||||
float time;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
metal::packed_float3 position;
|
|
||||||
metal::packed_float3 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
metal::float4 position [[position]];
|
|
||||||
metal::float4 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
vertex VertexOutput vertex_main(
|
|
||||||
device const SceneProperties& properties [[buffer(0)]],
|
|
||||||
device const VertexInput* vertices [[buffer(1)]],
|
|
||||||
uint vertex_idx [[vertex_id]]
|
|
||||||
) {
|
|
||||||
VertexOutput out;
|
|
||||||
VertexInput in = vertices[vertex_idx];
|
|
||||||
out.position =
|
|
||||||
metal::float4(
|
|
||||||
metal::float2x2(
|
|
||||||
metal::cos(properties.time), -metal::sin(properties.time),
|
|
||||||
metal::sin(properties.time), metal::cos(properties.time)
|
|
||||||
) * in.position.xy,
|
|
||||||
in.position.z,
|
|
||||||
1);
|
|
||||||
out.color = metal::float4(in.color, 1);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment metal::float4 fragment_main(VertexOutput in [[stage_in]]) {
|
|
||||||
return in.color;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SceneProperties {
|
|
||||||
pub time: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct VertexInput {
|
|
||||||
pub position: Position,
|
|
||||||
pub color: Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
// NOTE: this has the same ABI as `MTLPackedFloat3`
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Position {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
// NOTE: this has the same ABI as `MTLPackedFloat3`
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Color {
|
|
||||||
pub r: f32,
|
|
||||||
pub g: f32,
|
|
||||||
pub b: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! idcell {
|
|
||||||
($name:ident => $this:expr) => {
|
|
||||||
$this.ivars().$name.set($name).expect(&format!(
|
|
||||||
"ivar should not already be initialized: `{}`",
|
|
||||||
stringify!($name)
|
|
||||||
));
|
|
||||||
};
|
|
||||||
($name:ident <= $this:expr) => {
|
|
||||||
#[rustfmt::skip]
|
|
||||||
let Some($name) = $this.ivars().$name.get() else {
|
|
||||||
unreachable!(
|
|
||||||
"ivar should be initialized: `{}`",
|
|
||||||
stringify!($name)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// declare the desired instance variables
|
|
||||||
struct Ivars {
|
|
||||||
start_date: Id<NSDate>,
|
|
||||||
command_queue: OnceCell<Id<ProtocolObject<dyn MTLCommandQueue>>>,
|
|
||||||
pipeline_state: OnceCell<Id<ProtocolObject<dyn MTLRenderPipelineState>>>,
|
|
||||||
filter_chain: OnceCell<RwLock<FilterChainMetal>>,
|
|
||||||
window: OnceCell<Id<NSWindow>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// declare the Objective-C class machinery
|
|
||||||
declare_class!(
|
|
||||||
struct Delegate;
|
|
||||||
|
|
||||||
// SAFETY:
|
|
||||||
// - The superclass NSObject does not have any subclassing requirements.
|
|
||||||
// - Main thread only mutability is correct, since this is an application delegate.
|
|
||||||
// - `Delegate` does not implement `Drop`.
|
|
||||||
unsafe impl ClassType for Delegate {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = MainThreadOnly;
|
|
||||||
const NAME: &'static str = "Delegate";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeclaredClass for Delegate {
|
|
||||||
type Ivars = Ivars;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl NSObjectProtocol for Delegate {}
|
|
||||||
|
|
||||||
// define the delegate methods for the `NSApplicationDelegate` protocol
|
|
||||||
unsafe impl NSApplicationDelegate for Delegate {
|
|
||||||
#[method(applicationDidFinishLaunching:)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn applicationDidFinishLaunching(&self, _notification: &NSNotification) {
|
|
||||||
let mtm = MainThreadMarker::from(self);
|
|
||||||
// create the app window
|
|
||||||
let window = {
|
|
||||||
let content_rect = NSRect::new(NSPoint::new(0., 0.), NSSize::new(768., 768.));
|
|
||||||
let style = NSWindowStyleMaskClosable
|
|
||||||
| NSWindowStyleMaskResizable
|
|
||||||
| NSWindowStyleMaskTitled;
|
|
||||||
let backing_store_type = NSBackingStoreBuffered;
|
|
||||||
let flag = false;
|
|
||||||
unsafe {
|
|
||||||
NSWindow::initWithContentRect_styleMask_backing_defer(
|
|
||||||
mtm.alloc(),
|
|
||||||
content_rect,
|
|
||||||
style,
|
|
||||||
backing_store_type,
|
|
||||||
flag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// get the default device
|
|
||||||
let device = {
|
|
||||||
let ptr = unsafe { MTLCreateSystemDefaultDevice() };
|
|
||||||
unsafe { Id::retain(ptr) }.expect("Failed to get default system device.")
|
|
||||||
};
|
|
||||||
|
|
||||||
// create the command queue
|
|
||||||
let command_queue = device
|
|
||||||
.newCommandQueue()
|
|
||||||
.expect("Failed to create a command queue.");
|
|
||||||
|
|
||||||
// create the metal view
|
|
||||||
let mtk_view = {
|
|
||||||
let frame_rect = window.frame();
|
|
||||||
unsafe { MTKView::initWithFrame_device(mtm.alloc(), frame_rect, Some(&device)) }
|
|
||||||
};
|
|
||||||
|
|
||||||
// create the pipeline descriptor
|
|
||||||
let pipeline_descriptor = MTLRenderPipelineDescriptor::new();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
pipeline_descriptor
|
|
||||||
.colorAttachments()
|
|
||||||
.objectAtIndexedSubscript(0)
|
|
||||||
.setPixelFormat(mtk_view.colorPixelFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile the shaders
|
|
||||||
let library = device
|
|
||||||
.newLibraryWithSource_options_error(ns_string!(SHADERS), None)
|
|
||||||
.expect("Failed to create a library.");
|
|
||||||
|
|
||||||
// configure the vertex shader
|
|
||||||
let vertex_function = library.newFunctionWithName(ns_string!("vertex_main"));
|
|
||||||
pipeline_descriptor.setVertexFunction(vertex_function.as_deref());
|
|
||||||
|
|
||||||
// configure the fragment shader
|
|
||||||
let fragment_function = library.newFunctionWithName(ns_string!("fragment_main"));
|
|
||||||
pipeline_descriptor.setFragmentFunction(fragment_function.as_deref());
|
|
||||||
|
|
||||||
// create the pipeline state
|
|
||||||
let pipeline_state = device
|
|
||||||
.newRenderPipelineStateWithDescriptor_error(&pipeline_descriptor)
|
|
||||||
.expect("Failed to create a pipeline state.");
|
|
||||||
// let preset = ShaderPreset::try_parse("./test/shaders_slang/crt/crt-lottes.slangp").unwrap();
|
|
||||||
|
|
||||||
let preset = ShaderPreset::try_parse("./test/shaders_slang/crt/crt-royale.slangp").unwrap();
|
|
||||||
// let preset = ShaderPreset::try_parse("./test/basic.slangp").unwrap();
|
|
||||||
|
|
||||||
let filter_chain = FilterChainMetal::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&command_queue,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let filter_chain = RwLock::new(filter_chain);
|
|
||||||
|
|
||||||
// configure the metal view delegate
|
|
||||||
unsafe {
|
|
||||||
let object = ProtocolObject::from_ref(self);
|
|
||||||
mtk_view.setDelegate(Some(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure the window
|
|
||||||
window.setContentView(Some(&mtk_view));
|
|
||||||
window.center();
|
|
||||||
window.setTitle(ns_string!("metal example"));
|
|
||||||
window.makeKeyAndOrderFront(None);
|
|
||||||
|
|
||||||
// initialize the delegate state
|
|
||||||
idcell!(command_queue => self);
|
|
||||||
idcell!(pipeline_state => self);
|
|
||||||
idcell!(filter_chain => self);
|
|
||||||
idcell!(window => self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// define the delegate methods for the `MTKViewDelegate` protocol
|
|
||||||
unsafe impl MTKViewDelegate for Delegate {
|
|
||||||
#[method(drawInMTKView:)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn drawInMTKView(&self, mtk_view: &MTKView) {
|
|
||||||
idcell!(command_queue <= self);
|
|
||||||
idcell!(pipeline_state <= self);
|
|
||||||
idcell!(filter_chain <= self);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
mtk_view.setFramebufferOnly(false);
|
|
||||||
mtk_view.setClearColor(MTLClearColor {
|
|
||||||
red: 0.3,
|
|
||||||
blue: 0.5,
|
|
||||||
green: 0.3,
|
|
||||||
alpha: 0.0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: icrate `MTKView` doesn't have a generated binding for `currentDrawable` yet
|
|
||||||
// (because it needs a definition of `CAMetalDrawable`, which we don't support yet) so
|
|
||||||
// we have to use a raw `msg_send_id` call here instead.
|
|
||||||
let current_drawable: Option<Id<ProtocolObject<dyn MTLDrawable>>> =
|
|
||||||
msg_send_id![mtk_view, currentDrawable];
|
|
||||||
|
|
||||||
// prepare for drawing
|
|
||||||
let Some(current_drawable) = current_drawable else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(command_buffer) = command_queue.commandBuffer() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(pass_descriptor) = (unsafe { mtk_view.currentRenderPassDescriptor() }) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(encoder) = command_buffer.renderCommandEncoderWithDescriptor(&pass_descriptor)
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// compute the scene properties
|
|
||||||
let scene_properties_data = &SceneProperties {
|
|
||||||
// time: unsafe { self.ivars().start_date.timeIntervalSinceNow() } as f32,
|
|
||||||
time: 0.0
|
|
||||||
};
|
|
||||||
// write the scene properties to the vertex shader argument buffer at index 0
|
|
||||||
let scene_properties_bytes = NonNull::from(scene_properties_data);
|
|
||||||
unsafe {
|
|
||||||
encoder.setVertexBytes_length_atIndex(
|
|
||||||
scene_properties_bytes.cast::<core::ffi::c_void>(),
|
|
||||||
core::mem::size_of_val(scene_properties_data),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// compute the triangle geometry
|
|
||||||
let vertex_input_data: &[VertexInput] = &[
|
|
||||||
VertexInput {
|
|
||||||
position: Position {
|
|
||||||
x: -f32::sqrt(3.0) / 4.0,
|
|
||||||
y: -0.25,
|
|
||||||
z: 0.,
|
|
||||||
},
|
|
||||||
color: Color {
|
|
||||||
r: 1.,
|
|
||||||
g: 0.,
|
|
||||||
b: 0.,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VertexInput {
|
|
||||||
position: Position {
|
|
||||||
x: f32::sqrt(3.0) / 4.0,
|
|
||||||
y: -0.25,
|
|
||||||
z: 0.,
|
|
||||||
},
|
|
||||||
color: Color {
|
|
||||||
r: 0.,
|
|
||||||
g: 1.,
|
|
||||||
b: 0.,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VertexInput {
|
|
||||||
position: Position {
|
|
||||||
x: 0.,
|
|
||||||
y: 0.5,
|
|
||||||
z: 0.,
|
|
||||||
},
|
|
||||||
color: Color {
|
|
||||||
r: 0.,
|
|
||||||
g: 0.,
|
|
||||||
b: 1.,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// write the triangle geometry to the vertex shader argument buffer at index 1
|
|
||||||
let vertex_input_bytes = NonNull::from(vertex_input_data);
|
|
||||||
unsafe {
|
|
||||||
encoder.setVertexBytes_length_atIndex(
|
|
||||||
vertex_input_bytes.cast::<core::ffi::c_void>(),
|
|
||||||
core::mem::size_of_val(vertex_input_data),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// configure the encoder with the pipeline and draw the triangle
|
|
||||||
encoder.setRenderPipelineState(pipeline_state);
|
|
||||||
unsafe {
|
|
||||||
encoder.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveTypeTriangle, 0, 3)
|
|
||||||
};
|
|
||||||
encoder.endEncoding();
|
|
||||||
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut filter_chain = filter_chain.write().unwrap();
|
|
||||||
let texture = pass_descriptor
|
|
||||||
.colorAttachments()
|
|
||||||
.objectAtIndexedSubscript(0)
|
|
||||||
.texture()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let tex_desc = MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
|
||||||
texture.pixelFormat(),
|
|
||||||
texture.width(),
|
|
||||||
texture.height(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
tex_desc.setUsage(MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView);
|
|
||||||
// tex_desc.setPixelFormat(MTLPixelFormatRGBA8Unorm);
|
|
||||||
let frontbuffer = command_queue
|
|
||||||
.device()
|
|
||||||
.newTextureWithDescriptor(&tex_desc)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
frontbuffer
|
|
||||||
.setLabel(Some(&*NSString::from_str("librashader frontbuffer")));
|
|
||||||
|
|
||||||
let backbuffer = command_queue
|
|
||||||
.device()
|
|
||||||
.newTextureWithDescriptor(&tex_desc)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
backbuffer
|
|
||||||
.setLabel(Some(&*NSString::from_str("librashader backbuffer")));
|
|
||||||
let blit = command_buffer
|
|
||||||
.blitCommandEncoder()
|
|
||||||
.unwrap();
|
|
||||||
blit.copyFromTexture_toTexture(&texture, &frontbuffer);
|
|
||||||
blit.endEncoding();
|
|
||||||
|
|
||||||
filter_chain.frame(&frontbuffer,
|
|
||||||
&Viewport {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
mvp: None,
|
|
||||||
output: &backbuffer
|
|
||||||
}, &command_buffer, 1, None)
|
|
||||||
.expect("frame");
|
|
||||||
|
|
||||||
// let rpass = MTLRenderPassDescriptor::new();
|
|
||||||
// rpass.colorAttachments()
|
|
||||||
// .objectAtIndexedSubscript(0)
|
|
||||||
// .setTexture(Some(&backbuffer));
|
|
||||||
// rpass.colorAttachments()
|
|
||||||
// .objectAtIndexedSubscript(0)
|
|
||||||
// .setLoadAction(MTLLoadActionClear);
|
|
||||||
// rpass.colorAttachments()
|
|
||||||
// .objectAtIndexedSubscript(0)
|
|
||||||
// .setStoreAction(MTLStoreActionStore);
|
|
||||||
//
|
|
||||||
|
|
||||||
// let clear = command_buffer
|
|
||||||
// .renderCommandEncoderWithDescriptor(&*rpass)
|
|
||||||
// .unwrap();
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// clear.endEncoding();
|
|
||||||
|
|
||||||
let blit = command_buffer
|
|
||||||
.blitCommandEncoder()
|
|
||||||
.unwrap();
|
|
||||||
blit.copyFromTexture_toTexture(&backbuffer, &texture);
|
|
||||||
blit.endEncoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// schedule the command buffer for display and commit
|
|
||||||
command_buffer.presentDrawable(¤t_drawable);
|
|
||||||
command_buffer.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(mtkView:drawableSizeWillChange:)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn mtkView_drawableSizeWillChange(&self, _view: &MTKView, _size: NSSize) {
|
|
||||||
// println!("mtkView_drawableSizeWillChange");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Delegate {
|
|
||||||
pub fn new(mtm: MainThreadMarker) -> Id<Self> {
|
|
||||||
let this = mtm.alloc();
|
|
||||||
let this = this.set_ivars(Ivars {
|
|
||||||
start_date: unsafe { NSDate::now() },
|
|
||||||
command_queue: OnceCell::default(),
|
|
||||||
pipeline_state: OnceCell::default(),
|
|
||||||
filter_chain: OnceCell::default(),
|
|
||||||
window: OnceCell::default(),
|
|
||||||
});
|
|
||||||
unsafe { msg_send_id![super(this), init] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
// configure the app
|
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
|
||||||
app.setActivationPolicy(NSApplicationActivationPolicyRegular);
|
|
||||||
|
|
||||||
// configure the application delegate
|
|
||||||
let delegate = Delegate::new(mtm);
|
|
||||||
let object = ProtocolObject::from_ref(&*delegate);
|
|
||||||
app.setDelegate(Some(object));
|
|
||||||
|
|
||||||
// run the app
|
|
||||||
unsafe { app.run() };
|
|
||||||
}
|
}
|
|
@ -1,469 +0,0 @@
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::{cell::OnceCell, ptr::NonNull};
|
|
||||||
use std::sync::RwLock;
|
|
||||||
|
|
||||||
use icrate::Foundation::NSString;
|
|
||||||
use icrate::Metal::{
|
|
||||||
MTLBlitCommandEncoder, MTLClearColor, MTLPixelFormatRGBA8Unorm, MTLResource, MTLTexture,
|
|
||||||
MTLTextureDescriptor, MTLTextureUsagePixelFormatView, MTLTextureUsageRenderTarget,
|
|
||||||
MTLTextureUsageShaderRead,
|
|
||||||
};
|
|
||||||
use icrate::{
|
|
||||||
AppKit::{
|
|
||||||
NSApplication, NSApplicationActivationPolicyRegular, NSApplicationDelegate,
|
|
||||||
NSBackingStoreBuffered, NSWindow, NSWindowStyleMaskClosable, NSWindowStyleMaskResizable,
|
|
||||||
NSWindowStyleMaskTitled,
|
|
||||||
},
|
|
||||||
Foundation::{
|
|
||||||
ns_string, MainThreadMarker, NSDate, NSNotification, NSObject, NSObjectProtocol, NSPoint,
|
|
||||||
NSRect, NSSize,
|
|
||||||
},
|
|
||||||
Metal::{
|
|
||||||
MTLCommandBuffer, MTLCommandEncoder, MTLCommandQueue, MTLCreateSystemDefaultDevice,
|
|
||||||
MTLDevice, MTLDrawable, MTLLibrary, MTLPrimitiveTypeTriangle, MTLRenderCommandEncoder,
|
|
||||||
MTLRenderPipelineDescriptor, MTLRenderPipelineState,
|
|
||||||
},
|
|
||||||
MetalKit::{MTKView, MTKViewDelegate},
|
|
||||||
};
|
|
||||||
use librashader_common::Viewport;
|
|
||||||
use librashader_presets::ShaderPreset;
|
|
||||||
use librashader_runtime_metal::FilterChainMetal;
|
|
||||||
use objc2::{
|
|
||||||
declare_class, msg_send_id, mutability::MainThreadOnly, rc::Id, runtime::ProtocolObject,
|
|
||||||
ClassType, DeclaredClass,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
const SHADERS: &str = r#"
|
|
||||||
#include <metal_stdlib>
|
|
||||||
|
|
||||||
struct SceneProperties {
|
|
||||||
float time;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
metal::packed_float3 position;
|
|
||||||
metal::packed_float3 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
metal::float4 position [[position]];
|
|
||||||
metal::float4 color;
|
|
||||||
};
|
|
||||||
|
|
||||||
vertex VertexOutput vertex_main(
|
|
||||||
device const SceneProperties& properties [[buffer(0)]],
|
|
||||||
device const VertexInput* vertices [[buffer(1)]],
|
|
||||||
uint vertex_idx [[vertex_id]]
|
|
||||||
) {
|
|
||||||
VertexOutput out;
|
|
||||||
VertexInput in = vertices[vertex_idx];
|
|
||||||
out.position =
|
|
||||||
metal::float4(
|
|
||||||
metal::float2x2(
|
|
||||||
metal::cos(properties.time), -metal::sin(properties.time),
|
|
||||||
metal::sin(properties.time), metal::cos(properties.time)
|
|
||||||
) * in.position.xy,
|
|
||||||
in.position.z,
|
|
||||||
1);
|
|
||||||
out.color = metal::float4(in.color, 1);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment metal::float4 fragment_main(VertexOutput in [[stage_in]]) {
|
|
||||||
return in.color;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SceneProperties {
|
|
||||||
pub time: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct VertexInput {
|
|
||||||
pub position: Position,
|
|
||||||
pub color: Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
// NOTE: this has the same ABI as `MTLPackedFloat3`
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Position {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
// NOTE: this has the same ABI as `MTLPackedFloat3`
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Color {
|
|
||||||
pub r: f32,
|
|
||||||
pub g: f32,
|
|
||||||
pub b: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! idcell {
|
|
||||||
($name:ident => $this:expr) => {
|
|
||||||
$this.ivars().$name.set($name).expect(&format!(
|
|
||||||
"ivar should not already be initialized: `{}`",
|
|
||||||
stringify!($name)
|
|
||||||
));
|
|
||||||
};
|
|
||||||
($name:ident <= $this:expr) => {
|
|
||||||
#[rustfmt::skip]
|
|
||||||
let Some($name) = $this.ivars().$name.get() else {
|
|
||||||
unreachable!(
|
|
||||||
"ivar should be initialized: `{}`",
|
|
||||||
stringify!($name)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// declare the desired instance variables
|
|
||||||
struct Ivars {
|
|
||||||
start_date: Id<NSDate>,
|
|
||||||
command_queue: OnceCell<Id<ProtocolObject<dyn MTLCommandQueue>>>,
|
|
||||||
pipeline_state: OnceCell<Id<ProtocolObject<dyn MTLRenderPipelineState>>>,
|
|
||||||
filter_chain: OnceCell<RwLock<FilterChainMetal>>,
|
|
||||||
window: OnceCell<Id<NSWindow>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// declare the Objective-C class machinery
|
|
||||||
declare_class!(
|
|
||||||
struct Delegate;
|
|
||||||
|
|
||||||
// SAFETY:
|
|
||||||
// - The superclass NSObject does not have any subclassing requirements.
|
|
||||||
// - Main thread only mutability is correct, since this is an application delegate.
|
|
||||||
// - `Delegate` does not implement `Drop`.
|
|
||||||
unsafe impl ClassType for Delegate {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = MainThreadOnly;
|
|
||||||
const NAME: &'static str = "Delegate";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeclaredClass for Delegate {
|
|
||||||
type Ivars = Ivars;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl NSObjectProtocol for Delegate {}
|
|
||||||
|
|
||||||
// define the delegate methods for the `NSApplicationDelegate` protocol
|
|
||||||
unsafe impl NSApplicationDelegate for Delegate {
|
|
||||||
#[method(applicationDidFinishLaunching:)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn applicationDidFinishLaunching(&self, _notification: &NSNotification) {
|
|
||||||
let mtm = MainThreadMarker::from(self);
|
|
||||||
// create the app window
|
|
||||||
let window = {
|
|
||||||
let content_rect = NSRect::new(NSPoint::new(0., 0.), NSSize::new(768., 768.));
|
|
||||||
let style = NSWindowStyleMaskClosable
|
|
||||||
| NSWindowStyleMaskResizable
|
|
||||||
| NSWindowStyleMaskTitled;
|
|
||||||
let backing_store_type = NSBackingStoreBuffered;
|
|
||||||
let flag = false;
|
|
||||||
unsafe {
|
|
||||||
NSWindow::initWithContentRect_styleMask_backing_defer(
|
|
||||||
mtm.alloc(),
|
|
||||||
content_rect,
|
|
||||||
style,
|
|
||||||
backing_store_type,
|
|
||||||
flag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// get the default device
|
|
||||||
let device = {
|
|
||||||
let ptr = unsafe { MTLCreateSystemDefaultDevice() };
|
|
||||||
unsafe { Id::retain(ptr) }.expect("Failed to get default system device.")
|
|
||||||
};
|
|
||||||
|
|
||||||
// create the command queue
|
|
||||||
let command_queue = device
|
|
||||||
.newCommandQueue()
|
|
||||||
.expect("Failed to create a command queue.");
|
|
||||||
|
|
||||||
// create the metal view
|
|
||||||
let mtk_view = {
|
|
||||||
let frame_rect = window.frame();
|
|
||||||
unsafe { MTKView::initWithFrame_device(mtm.alloc(), frame_rect, Some(&device)) }
|
|
||||||
};
|
|
||||||
|
|
||||||
// create the pipeline descriptor
|
|
||||||
let pipeline_descriptor = MTLRenderPipelineDescriptor::new();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
pipeline_descriptor
|
|
||||||
.colorAttachments()
|
|
||||||
.objectAtIndexedSubscript(0)
|
|
||||||
.setPixelFormat(mtk_view.colorPixelFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile the shaders
|
|
||||||
let library = device
|
|
||||||
.newLibraryWithSource_options_error(ns_string!(SHADERS), None)
|
|
||||||
.expect("Failed to create a library.");
|
|
||||||
|
|
||||||
// configure the vertex shader
|
|
||||||
let vertex_function = library.newFunctionWithName(ns_string!("vertex_main"));
|
|
||||||
pipeline_descriptor.setVertexFunction(vertex_function.as_deref());
|
|
||||||
|
|
||||||
// configure the fragment shader
|
|
||||||
let fragment_function = library.newFunctionWithName(ns_string!("fragment_main"));
|
|
||||||
pipeline_descriptor.setFragmentFunction(fragment_function.as_deref());
|
|
||||||
|
|
||||||
// create the pipeline state
|
|
||||||
let pipeline_state = device
|
|
||||||
.newRenderPipelineStateWithDescriptor_error(&pipeline_descriptor)
|
|
||||||
.expect("Failed to create a pipeline state.");
|
|
||||||
// let preset = ShaderPreset::try_parse("./test/shaders_slang/crt/crt-lottes.slangp").unwrap();
|
|
||||||
|
|
||||||
let preset = ShaderPreset::try_parse("./test/shaders_slang/crt/crt-royale.slangp").unwrap();
|
|
||||||
// let preset = ShaderPreset::try_parse("./test/basic.slangp").unwrap();
|
|
||||||
|
|
||||||
let filter_chain = FilterChainMetal::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&command_queue,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let filter_chain = RwLock::new(filter_chain);
|
|
||||||
|
|
||||||
// configure the metal view delegate
|
|
||||||
unsafe {
|
|
||||||
let object = ProtocolObject::from_ref(self);
|
|
||||||
mtk_view.setDelegate(Some(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure the window
|
|
||||||
window.setContentView(Some(&mtk_view));
|
|
||||||
window.center();
|
|
||||||
window.setTitle(ns_string!("metal example"));
|
|
||||||
window.makeKeyAndOrderFront(None);
|
|
||||||
|
|
||||||
// initialize the delegate state
|
|
||||||
idcell!(command_queue => self);
|
|
||||||
idcell!(pipeline_state => self);
|
|
||||||
idcell!(filter_chain => self);
|
|
||||||
idcell!(window => self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// define the delegate methods for the `MTKViewDelegate` protocol
|
|
||||||
unsafe impl MTKViewDelegate for Delegate {
|
|
||||||
#[method(drawInMTKView:)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn drawInMTKView(&self, mtk_view: &MTKView) {
|
|
||||||
idcell!(command_queue <= self);
|
|
||||||
idcell!(pipeline_state <= self);
|
|
||||||
idcell!(filter_chain <= self);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
mtk_view.setFramebufferOnly(false);
|
|
||||||
mtk_view.setClearColor(MTLClearColor {
|
|
||||||
red: 0.3,
|
|
||||||
blue: 0.5,
|
|
||||||
green: 0.3,
|
|
||||||
alpha: 0.0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: icrate `MTKView` doesn't have a generated binding for `currentDrawable` yet
|
|
||||||
// (because it needs a definition of `CAMetalDrawable`, which we don't support yet) so
|
|
||||||
// we have to use a raw `msg_send_id` call here instead.
|
|
||||||
let current_drawable: Option<Id<ProtocolObject<dyn MTLDrawable>>> =
|
|
||||||
msg_send_id![mtk_view, currentDrawable];
|
|
||||||
|
|
||||||
// prepare for drawing
|
|
||||||
let Some(current_drawable) = current_drawable else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(command_buffer) = command_queue.commandBuffer() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(pass_descriptor) = (unsafe { mtk_view.currentRenderPassDescriptor() }) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(encoder) = command_buffer.renderCommandEncoderWithDescriptor(&pass_descriptor)
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// compute the scene properties
|
|
||||||
let scene_properties_data = &SceneProperties {
|
|
||||||
// time: unsafe { self.ivars().start_date.timeIntervalSinceNow() } as f32,
|
|
||||||
time: 0.0
|
|
||||||
};
|
|
||||||
// write the scene properties to the vertex shader argument buffer at index 0
|
|
||||||
let scene_properties_bytes = NonNull::from(scene_properties_data);
|
|
||||||
unsafe {
|
|
||||||
encoder.setVertexBytes_length_atIndex(
|
|
||||||
scene_properties_bytes.cast::<core::ffi::c_void>(),
|
|
||||||
core::mem::size_of_val(scene_properties_data),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// compute the triangle geometry
|
|
||||||
let vertex_input_data: &[VertexInput] = &[
|
|
||||||
VertexInput {
|
|
||||||
position: Position {
|
|
||||||
x: -f32::sqrt(3.0) / 4.0,
|
|
||||||
y: -0.25,
|
|
||||||
z: 0.,
|
|
||||||
},
|
|
||||||
color: Color {
|
|
||||||
r: 1.,
|
|
||||||
g: 0.,
|
|
||||||
b: 0.,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VertexInput {
|
|
||||||
position: Position {
|
|
||||||
x: f32::sqrt(3.0) / 4.0,
|
|
||||||
y: -0.25,
|
|
||||||
z: 0.,
|
|
||||||
},
|
|
||||||
color: Color {
|
|
||||||
r: 0.,
|
|
||||||
g: 1.,
|
|
||||||
b: 0.,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VertexInput {
|
|
||||||
position: Position {
|
|
||||||
x: 0.,
|
|
||||||
y: 0.5,
|
|
||||||
z: 0.,
|
|
||||||
},
|
|
||||||
color: Color {
|
|
||||||
r: 0.,
|
|
||||||
g: 0.,
|
|
||||||
b: 1.,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// write the triangle geometry to the vertex shader argument buffer at index 1
|
|
||||||
let vertex_input_bytes = NonNull::from(vertex_input_data);
|
|
||||||
unsafe {
|
|
||||||
encoder.setVertexBytes_length_atIndex(
|
|
||||||
vertex_input_bytes.cast::<core::ffi::c_void>(),
|
|
||||||
core::mem::size_of_val(vertex_input_data),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// configure the encoder with the pipeline and draw the triangle
|
|
||||||
encoder.setRenderPipelineState(pipeline_state);
|
|
||||||
unsafe {
|
|
||||||
encoder.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveTypeTriangle, 0, 3)
|
|
||||||
};
|
|
||||||
encoder.endEncoding();
|
|
||||||
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut filter_chain = filter_chain.write().unwrap();
|
|
||||||
let texture = pass_descriptor
|
|
||||||
.colorAttachments()
|
|
||||||
.objectAtIndexedSubscript(0)
|
|
||||||
.texture()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let tex_desc = MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
|
||||||
texture.pixelFormat(),
|
|
||||||
texture.width(),
|
|
||||||
texture.height(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
tex_desc.setUsage(MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView);
|
|
||||||
// tex_desc.setPixelFormat(MTLPixelFormatRGBA8Unorm);
|
|
||||||
let frontbuffer = command_queue
|
|
||||||
.device()
|
|
||||||
.newTextureWithDescriptor(&tex_desc)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
frontbuffer
|
|
||||||
.setLabel(Some(&*NSString::from_str("librashader frontbuffer")));
|
|
||||||
|
|
||||||
let backbuffer = command_queue
|
|
||||||
.device()
|
|
||||||
.newTextureWithDescriptor(&tex_desc)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
backbuffer
|
|
||||||
.setLabel(Some(&*NSString::from_str("librashader backbuffer")));
|
|
||||||
let blit = command_buffer
|
|
||||||
.blitCommandEncoder()
|
|
||||||
.unwrap();
|
|
||||||
blit.copyFromTexture_toTexture(&texture, &frontbuffer);
|
|
||||||
blit.endEncoding();
|
|
||||||
|
|
||||||
filter_chain.frame(&frontbuffer,
|
|
||||||
&Viewport {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
mvp: None,
|
|
||||||
output: &backbuffer
|
|
||||||
}, &command_buffer, 1, None)
|
|
||||||
.expect("frame");
|
|
||||||
|
|
||||||
let blit = command_buffer
|
|
||||||
.blitCommandEncoder()
|
|
||||||
.unwrap();
|
|
||||||
blit.copyFromTexture_toTexture(&backbuffer, &texture);
|
|
||||||
blit.endEncoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// schedule the command buffer for display and commit
|
|
||||||
command_buffer.presentDrawable(¤t_drawable);
|
|
||||||
command_buffer.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(mtkView:drawableSizeWillChange:)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe fn mtkView_drawableSizeWillChange(&self, _view: &MTKView, _size: NSSize) {
|
|
||||||
// println!("mtkView_drawableSizeWillChange");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Delegate {
|
|
||||||
pub fn new(mtm: MainThreadMarker) -> Id<Self> {
|
|
||||||
let this = mtm.alloc();
|
|
||||||
let this = this.set_ivars(Ivars {
|
|
||||||
start_date: unsafe { NSDate::now() },
|
|
||||||
command_queue: OnceCell::default(),
|
|
||||||
pipeline_state: OnceCell::default(),
|
|
||||||
filter_chain: OnceCell::default(),
|
|
||||||
window: OnceCell::default(),
|
|
||||||
});
|
|
||||||
unsafe { msg_send_id![super(this), init] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
// configure the app
|
|
||||||
let app = NSApplication::sharedApplication(mtm);
|
|
||||||
app.setActivationPolicy(NSApplicationActivationPolicyRegular);
|
|
||||||
|
|
||||||
// configure the application delegate
|
|
||||||
let delegate = Delegate::new(mtm);
|
|
||||||
let object = ProtocolObject::from_ref(&*delegate);
|
|
||||||
app.setDelegate(Some(object));
|
|
||||||
|
|
||||||
// run the app
|
|
||||||
unsafe { app.run() };
|
|
||||||
}
|
|
Loading…
Reference in a new issue