use objc2::{Message, extern_protocol, msg_send, rc::Retained, runtime::ProtocolObject};
use objc2_foundation::NSString;
use crate::{
MTL4BinaryFunction, MTL4PipelineDescriptor, MTL4RenderPipelineBinaryFunctionsDescriptor, MTLAllocation, MTLDevice,
MTLFunction, MTLFunctionHandle, MTLIntersectionFunctionTable, MTLIntersectionFunctionTableDescriptor,
MTLRenderPipelineFunctionsDescriptor, MTLRenderPipelineReflection, MTLRenderStages, MTLResourceID,
MTLShaderValidation, MTLSize, MTLVisibleFunctionTable, MTLVisibleFunctionTableDescriptor, MetalError,
};
extern_protocol!(
#[expect(
clippy::missing_safety_doc,
reason = "extern_protocol does not attach this safety section to its generated unsafe trait"
)]
pub unsafe trait MTLRenderPipelineState: MTLAllocation {
#[unsafe(method(device))]
#[unsafe(method_family = none)]
fn device(&self) -> Retained<ProtocolObject<dyn MTLDevice>>;
#[unsafe(method(reflection))]
#[unsafe(method_family = none)]
fn reflection(&self) -> Option<Retained<MTLRenderPipelineReflection>>;
#[unsafe(method(functionHandleWithBinaryFunction:stage:))]
#[unsafe(method_family = none)]
fn function_handle_with_binary_function_stage(
&self,
function: &ProtocolObject<dyn MTL4BinaryFunction>,
stage: MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLFunctionHandle>>>;
#[unsafe(method(newRenderPipelineDescriptorForSpecialization))]
#[unsafe(method_family = new)]
fn new_render_pipeline_descriptor_for_specialization(&self) -> Retained<MTL4PipelineDescriptor>;
#[unsafe(method(maxTotalThreadsPerThreadgroup))]
#[unsafe(method_family = none)]
fn max_total_threads_per_threadgroup(&self) -> usize;
#[unsafe(method(threadgroupSizeMatchesTileSize))]
#[unsafe(method_family = none)]
fn threadgroup_size_matches_tile_size(&self) -> bool;
#[unsafe(method(imageblockSampleLength))]
#[unsafe(method_family = none)]
fn imageblock_sample_length(&self) -> usize;
#[unsafe(method(imageblockMemoryLengthForDimensions:))]
#[unsafe(method_family = none)]
fn imageblock_memory_length_for_dimensions(
&self,
imageblock_dimensions: MTLSize,
) -> usize;
#[unsafe(method(supportIndirectCommandBuffers))]
#[unsafe(method_family = none)]
fn support_indirect_command_buffers(&self) -> bool;
#[unsafe(method(maxTotalThreadsPerObjectThreadgroup))]
#[unsafe(method_family = none)]
fn max_total_threads_per_object_threadgroup(&self) -> usize;
#[unsafe(method(maxTotalThreadsPerMeshThreadgroup))]
#[unsafe(method_family = none)]
fn max_total_threads_per_mesh_threadgroup(&self) -> usize;
#[unsafe(method(objectThreadExecutionWidth))]
#[unsafe(method_family = none)]
fn object_thread_execution_width(&self) -> usize;
#[unsafe(method(meshThreadExecutionWidth))]
#[unsafe(method_family = none)]
fn mesh_thread_execution_width(&self) -> usize;
#[unsafe(method(maxTotalThreadgroupsPerMeshGrid))]
#[unsafe(method_family = none)]
fn max_total_threadgroups_per_mesh_grid(&self) -> usize;
#[unsafe(method(gpuResourceID))]
#[unsafe(method_family = none)]
fn gpu_resource_id(&self) -> MTLResourceID;
#[unsafe(method(functionHandleWithFunction:stage:))]
#[unsafe(method_family = none)]
fn function_handle_with_function_stage(
&self,
function: &ProtocolObject<dyn MTLFunction>,
stage: MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLFunctionHandle>>>;
#[unsafe(method(newVisibleFunctionTableWithDescriptor:stage:))]
#[unsafe(method_family = new)]
fn new_visible_function_table_with_descriptor_stage(
&self,
descriptor: &MTLVisibleFunctionTableDescriptor,
stage: MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLVisibleFunctionTable>>>;
#[unsafe(method(newIntersectionFunctionTableWithDescriptor:stage:))]
#[unsafe(method_family = new)]
fn new_intersection_function_table_with_descriptor_stage(
&self,
descriptor: &MTLIntersectionFunctionTableDescriptor,
stage: MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLIntersectionFunctionTable>>>;
#[unsafe(method(shaderValidation))]
#[unsafe(method_family = none)]
fn shader_validation(&self) -> MTLShaderValidation;
#[unsafe(method(requiredThreadsPerTileThreadgroup))]
#[unsafe(method_family = none)]
fn required_threads_per_tile_threadgroup(&self) -> MTLSize;
#[unsafe(method(requiredThreadsPerObjectThreadgroup))]
#[unsafe(method_family = none)]
fn required_threads_per_object_threadgroup(&self) -> MTLSize;
#[unsafe(method(requiredThreadsPerMeshThreadgroup))]
#[unsafe(method_family = none)]
fn required_threads_per_mesh_threadgroup(&self) -> MTLSize;
}
);
pub trait MTLRenderPipelineStateExt: MTLRenderPipelineState + Message {
fn label(&self) -> Option<String>;
fn function_handle_with_name_stage(
&self,
name: &str,
stage: MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLFunctionHandle>>>;
fn new_render_pipeline_state_with_binary_functions(
&self,
binary_functions_descriptor: &MTL4RenderPipelineBinaryFunctionsDescriptor,
) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, MetalError>;
fn new_render_pipeline_state_with_additional_binary_functions(
&self,
additional_binary_functions: &MTLRenderPipelineFunctionsDescriptor,
) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, MetalError>;
}
impl MTLRenderPipelineStateExt for ProtocolObject<dyn MTLRenderPipelineState> {
fn label(&self) -> Option<String> {
let label: Option<Retained<NSString>> = unsafe { msg_send![self, label] };
label.map(|label| label.to_string())
}
fn function_handle_with_name_stage(
&self,
name: &str,
stage: MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLFunctionHandle>>> {
let name = NSString::from_str(name);
unsafe { msg_send![self, functionHandleWithName: &*name, stage: stage] }
}
fn new_render_pipeline_state_with_binary_functions(
&self,
binary_functions_descriptor: &MTL4RenderPipelineBinaryFunctionsDescriptor,
) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, MetalError> {
unsafe { msg_send![self, newRenderPipelineStateWithBinaryFunctions: binary_functions_descriptor, error: _] }
.map_err(MetalError::from_nserror)
}
fn new_render_pipeline_state_with_additional_binary_functions(
&self,
additional_binary_functions: &MTLRenderPipelineFunctionsDescriptor,
) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, MetalError> {
unsafe {
msg_send![
self,
newRenderPipelineStateWithAdditionalBinaryFunctions: additional_binary_functions,
error: _
]
}
.map_err(MetalError::from_nserror)
}
}
#[cfg(test)]
mod tests {
use objc2::{rc::Retained, runtime::ProtocolObject};
use super::{MTLRenderPipelineState, MTLRenderPipelineStateExt};
use crate::{MTLFunctionHandle, MTLRenderStages};
#[test]
fn string_methods_have_rust_native_signatures() {
let _: fn(&ProtocolObject<dyn MTLRenderPipelineState>) -> Option<String> =
<ProtocolObject<dyn MTLRenderPipelineState> as MTLRenderPipelineStateExt>::label;
let _: fn(
&ProtocolObject<dyn MTLRenderPipelineState>,
&str,
MTLRenderStages,
) -> Option<Retained<ProtocolObject<dyn MTLFunctionHandle>>> =
<ProtocolObject<dyn MTLRenderPipelineState> as MTLRenderPipelineStateExt>::function_handle_with_name_stage;
}
}