From c251bbbf050b44de01a238bac14d3c18a87aacb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Rouleau?= Date: Fri, 12 Jun 2026 22:42:49 -0400 Subject: [PATCH] metal: match pipeline attachment formats to the view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Metal backend unconditionally configured every render pipeline state for two color attachments (`for i in 0..2`) plus depth and stencil at `Depth32Float_Stencil8`, while MTKView by default exposes only color attachment 0 and `depthStencilPixelFormat = Invalid`. Metal validation fires on the first draw: failed assertion `Set Render Pipeline State Validation For color attachment 1, the renderPipelineState pixelFormat must be MTLPixelFormatInvalid, as no texture is set. For depth attachment, the renderPipelineState pixelFormat must be MTLPixelFormatInvalid, as no texture is set. For stencil attachment, the renderPipelineState pixelFormat must be MTLPixelFormatInvalid, as no texture is set. Configure only color attachment 0 (the backend has no MRT path) and mirror the view's `depthStencilPixelFormat` onto the pipeline's depth + stencil formats — but only when the view actually has one. When MTKView reports `Invalid`, skip the `set*AttachmentPixelFormat:` calls so the descriptor keeps its `Invalid` defaults instead of having that sentinel passed in explicitly. Adds `MTLPixelFormat::Invalid = 0` to the binding (Apple's `MTLPixelFormatInvalid`). --- src/graphics/metal.rs | 30 +++++++++++++++++++++--------- src/native/apple/frameworks.rs | 1 + 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/graphics/metal.rs b/src/graphics/metal.rs index 39004579..9f88493b 100644 --- a/src/graphics/metal.rs +++ b/src/graphics/metal.rs @@ -929,8 +929,11 @@ impl RenderingBackend for MetalContext { msg_send_![descriptor, setVertexFunction:shader_internal.vertex_function]; msg_send_![descriptor, setFragmentFunction:shader_internal.fragment_function]; msg_send_![descriptor, setVertexDescriptor: vertex_descriptor]; + // Only attachment 0 — the backend has no MRT path and + // setting attachment 1 to a non-Invalid pixelFormat + // fails Metal validation when no texture is bound. let color_attachments = msg_send_![descriptor, colorAttachments]; - for i in 0..2 { + for i in 0..1usize { let color_attachment = msg_send_![color_attachments, objectAtIndexedSubscript: i]; let view_pixel_format: MTLPixelFormat = msg_send![self.view, colorPixelFormat]; msg_send_![color_attachment, setPixelFormat: view_pixel_format]; @@ -977,14 +980,23 @@ impl RenderingBackend for MetalContext { ]; } } - msg_send_![ - descriptor, - setDepthAttachmentPixelFormat: MTLPixelFormat::Depth32Float_Stencil8 - ]; - msg_send_![ - descriptor, - setStencilAttachmentPixelFormat: MTLPixelFormat::Depth32Float_Stencil8 - ]; + // Pipeline depth/stencil formats must match whatever + // render pass uses this pipeline. MTKView reports + // `Invalid` when no depth/stencil is configured — + // leave the descriptor's defaults (also `Invalid`) + // alone in that case; otherwise mirror the view. + let view_depth_stencil_format: MTLPixelFormat = + msg_send![self.view, depthStencilPixelFormat]; + if view_depth_stencil_format != MTLPixelFormat::Invalid { + msg_send_![ + descriptor, + setDepthAttachmentPixelFormat: view_depth_stencil_format + ]; + msg_send_![ + descriptor, + setStencilAttachmentPixelFormat: view_depth_stencil_format + ]; + } let mut error: ObjcId = nil; let pipeline_state: ObjcId = msg_send![ diff --git a/src/native/apple/frameworks.rs b/src/native/apple/frameworks.rs index 01e8bb42..b4da3141 100644 --- a/src/native/apple/frameworks.rs +++ b/src/native/apple/frameworks.rs @@ -632,6 +632,7 @@ impl MTLClearColor { #[allow(non_camel_case_types)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum MTLPixelFormat { + Invalid = 0, BGRA8Unorm = 80, Depth32Float = 252, Stencil8 = 253,