use objc2::{Message, extern_protocol, msg_send, rc::Retained, runtime::ProtocolObject};
use objc2_foundation::{NSArray, NSDictionary, NSObjectProtocol, NSString};
use crate::{
MTLArgument, MTLArgumentEncoder, MTLAttribute, MTLDevice, MTLFunctionConstant, MTLFunctionOptions, MTLFunctionType,
MTLPatchType, MTLVertexAttribute,
};
extern_protocol!(
pub unsafe trait MTLFunction: NSObjectProtocol + Send + Sync {
#[unsafe(method(device))]
#[unsafe(method_family = none)]
fn device(&self) -> Retained<ProtocolObject<dyn MTLDevice>>;
#[unsafe(method(functionType))]
#[unsafe(method_family = none)]
fn function_type(&self) -> MTLFunctionType;
#[unsafe(method(patchType))]
#[unsafe(method_family = none)]
fn patch_type(&self) -> MTLPatchType;
#[unsafe(method(patchControlPointCount))]
#[unsafe(method_family = none)]
fn patch_control_point_count(&self) -> isize;
#[unsafe(method(newArgumentEncoderWithBufferIndex:))]
#[unsafe(method_family = new)]
fn new_argument_encoder_with_buffer_index(
&self,
buffer_index: usize,
) -> Retained<ProtocolObject<dyn MTLArgumentEncoder>>;
#[deprecated(note = "use MTLDevice::new_argument_encoder_with_buffer_binding instead")]
#[unsafe(method(newArgumentEncoderWithBufferIndex:reflection:))]
#[unsafe(method_family = new)]
fn new_argument_encoder_with_buffer_index_reflection(
&self,
buffer_index: usize,
reflection: Option<&mut Option<Retained<MTLArgument>>>,
) -> Retained<ProtocolObject<dyn MTLArgumentEncoder>>;
#[unsafe(method(options))]
#[unsafe(method_family = none)]
fn options(&self) -> MTLFunctionOptions;
}
);
pub trait MTLFunctionExt: MTLFunction + Message {
fn label(&self) -> Option<String>;
fn set_label(
&self,
label: Option<&str>,
);
fn vertex_attributes(&self) -> Option<Box<[Retained<MTLVertexAttribute>]>>;
fn stage_input_attributes(&self) -> Option<Box<[Retained<MTLAttribute>]>>;
fn name(&self) -> String;
fn function_constants_dictionary(&self) -> Box<[(String, Retained<MTLFunctionConstant>)]>;
}
impl<T> MTLFunctionExt for T
where
T: MTLFunction + Message + ?Sized,
{
fn label(&self) -> Option<String> {
let label: Option<Retained<NSString>> = unsafe { msg_send![self, label] };
label.map(|label| label.to_string())
}
fn set_label(
&self,
label: Option<&str>,
) {
unsafe {
let _: () = msg_send![self, setLabel: label.map(NSString::from_str).as_deref()];
}
}
fn vertex_attributes(&self) -> Option<Box<[Retained<MTLVertexAttribute>]>> {
let attributes: Option<Retained<NSArray<MTLVertexAttribute>>> = unsafe { msg_send![self, vertexAttributes] };
attributes.map(|attributes| attributes.to_vec().into_boxed_slice())
}
fn stage_input_attributes(&self) -> Option<Box<[Retained<MTLAttribute>]>> {
let attributes: Option<Retained<NSArray<MTLAttribute>>> = unsafe { msg_send![self, stageInputAttributes] };
attributes.map(|attributes| attributes.to_vec().into_boxed_slice())
}
fn name(&self) -> String {
let name: Retained<NSString> = unsafe { msg_send![self, name] };
name.to_string()
}
fn function_constants_dictionary(&self) -> Box<[(String, Retained<MTLFunctionConstant>)]> {
let constants: Retained<NSDictionary<NSString, MTLFunctionConstant>> =
unsafe { msg_send![self, functionConstantsDictionary] };
let (names, values) = constants.to_vecs();
names
.into_iter()
.zip(values)
.map(|(name, value)| (name.to_string(), value))
.collect::<Vec<_>>()
.into_boxed_slice()
}
}
#[cfg(test)]
mod tests {
use objc2::{rc::Retained, runtime::ProtocolObject};
use super::{MTLFunction, MTLFunctionExt};
use crate::{MTLArgumentEncoder, MTLFunctionConstant, MTLVertexAttribute};
#[test]
fn rust_native_accessors_have_rust_owned_signatures() {
let _: fn(&ProtocolObject<dyn MTLFunction>) -> Option<Box<[Retained<MTLVertexAttribute>]>> =
<ProtocolObject<dyn MTLFunction> as MTLFunctionExt>::vertex_attributes;
let _: fn(&ProtocolObject<dyn MTLFunction>) -> Box<[(String, Retained<MTLFunctionConstant>)]> =
<ProtocolObject<dyn MTLFunction> as MTLFunctionExt>::function_constants_dictionary;
}
#[test]
fn numeric_argument_encoder_method_is_safe_to_call() {
let _: fn(&ProtocolObject<dyn MTLFunction>, usize) -> Retained<ProtocolObject<dyn MTLArgumentEncoder>> =
MTLFunction::new_argument_encoder_with_buffer_index;
}
}