Delve into the world of graphics and game development. Discuss creating stunning visuals, optimizing game mechanics, and share resources for game developers.

All subtopics
Posts under Graphics & Games topic

Post

Replies

Boosts

Views

Activity

SpriteKit scene used as SCNView.overlaySKScene crashes due to SKShapeNode
I recently published my first game on the App Store. It uses SceneKit with a SpriteKit overlay. All crashes Xcode downloaded for it so far are related to some SpriteKit/SceneKit internals. The most common crash is caused by SKCShapeNode::_NEW_copyRenderPathData. What could cause such a crash? crash.crash While developing this game (and the BoardGameKit framework that appears in the crash log) over the years I experienced many crashes presumably caused by the SpriteKit overlay (I opened a post SceneKit app randomly crashes with EXC_BAD_ACCESS in jet_context::set_fragment_texture about such a crash in September 2024), and other people on the internet also mention that they experience crashes when using SpriteKit as a SceneKit overlay. Should I use a separate SKView and lay it on top of SCNView rather than setting SCNView.overlaySKScene? That seemed to solve the crashes for a guy on stackoverflow, but is it also encouraged by Apple? I know SceneKit is deprecated, but according to Apple critical bugs would still be fixed. Could this be considered a critical bug?
9
0
1.8k
1d
MPS bf16 softmax produces NaN on M5 Max (regression from M4) — breaks all on-device diffusion inference
Metal Performance Shaders produces from bf16/fp16 softmax for large attention tensors on , forcing the entire local generative-AI ecosystem to fall back to fp32. This is a . Environment Minimal reproduction Decomposed softmax on MPS — diffs = x - maxes produces NaN even though every element is identical (result should be all zeros): Real-world impact Running ComfyUI (the dominant local generative-AI UI) on M5 Max: Root cause (per PyTorch MPS maintainers) PyTorch maintainers (@drisspg, @albanD) have traced this to in the MPS/MPSGraph fused kernels. The NaN originates in the x - maxes subtraction inside softmax, then propagates through the attention block and the entire network. PyTorch cannot fix this — it is in the Metal/MPS kernel layer. Not isolated The M4-era fix ("fixed on macOS 15.1") did not survive onto M5, indicating the MPS fused-kernel precision fix was either reverted or not ported to the M5 GPU architecture. Request
0
0
401
1d
Behaviour of a 0-value `accelerationStructureID`
When constructing a MTLIndirectAccelerationStructureInstanceDescriptor, one specifies an accelerationStructureID. In the equivalents in both Vulkan and DirectX12, one can set it to zero to be an "inactive" instance. However, on Metal, this field does not appear to have any documentation, and thus it is difficult to figure out if there is similar behavior (and no other Metal documentation seems to mention this). Does setting this field to 0 (i.e. null) disable the instance? If not, is there any other way to have an equivalent effect?
0
0
412
1d
Best Way to Use MetalFX in Unreal Engine 5.7 for macOS Port?
Hi everyone, We’re currently porting a high-fidelity AA+ PC title built on Unreal Engine 5.7 to macOS (Apple Silicon), and we’re looking for guidance from anyone with experience in this area. At the moment, the game is already runnable on Mac, but not yet at a playable level — we’re seeing performance around 10–15 FPS on an M4 device. We’re actively analyzing and defining the work needed to reach production-quality performance on macOS. One of the key areas we’re exploring is leveraging MetalFX to improve frame rate. However, it seems there’s no official MetalFX plugin or direct integration available for Unreal Engine. Has anyone here successfully integrated MetalFX into a UE5 rendering pipeline, or found a recommended approach to do so? Any insights on best practices, workflows, or references (docs, samples, etc.) would be greatly appreciated. Thanks in advance!
4
0
1.7k
2d
view protocol update ?
Hello ! Thank you guys for all the hard work you are doing on RealityKit For now in my studying Im trying to understand, in general, what will cause my Main app view to redraw everything? Or any view? If i have a struct that use the view protocol, and that struct is being called inside RealityView on the main app, or outside a reality view within a ZStack on the main app, if this view updates, does this will cause everything in my main app view to redraw as well? Or is it only true to @State variables of the main app? or the @State variables of the external view i try to implement? and what about @Observable ? Is there an accurate table that tells the developer what will cause a redraw of things? (not only the things the logic asks for, but all things sitting idle within the view) I try to understand how to separate UI updates, to avoid full redraw of things that havent been changed. to minimize UI compute in my games Before i play around with instruments i need to understand the general architecture.. if i work in a way that is counter designed to the way reality kit should work then the instrument results will not make sense to me. so i feel like i should ask you guys first. Depending on your answer i will know how to arrange my data in my game. And how to design and instantiate all my views Thanks
0
0
232
3d
MTL4FXFrameInterpolator no-op on MTL4CommandBuffer
I'm trying to use the new MTL4FX::FrameInterpolator(the Metal 4 variant that encodes to MTL4::CommandBuffer). It creates fine, accepts all texture bindings, and encodes without any error or assertion. The GPU signals completion via shared event. But the output texture is completely untouched — zero bytes changed from before the encode. It's a silent no-op. I'm trying to call metal-cpp from python by making dynamic library with cpp. Environment: macOS 26.6 (build 25G72) Apple M3, arm64 Xcode 26 SDK MetalFX framework (MTL4FX API, macOS 26+) I'm on M3 Summary: MTL4FX::FrameInterpolator::encodeToCommandBuffer(MTL4::CommandBuffer*) records, commits, and the GPU signals completion, but writes zero bytes to the output texture. The encode is a silent no-op — identical to documented issues 146436460 and 146436741 for MTL4FXTemporalScaler/DenoisedScaler. Diagnostic trace: Output texture zeroed before encode: 0/32768 non-zero bytes Output texture after encode (delta=0.5): 0/32768 non-zero bytes Changed bytes: 0/32768 ← definitive no-op Input textures verified: correct (R=1, G=0 vs R=0, G=1) I dont really know how to explain this, the result its just blank.
1
0
548
4d
Game Center matchmaking fails for all non-default users on Apple TV — no IDS registration for secondary users (FB24156316)
Game Center matchmaking fails for all non-default users on Apple TV — no IDS registration for secondary users (FB24156316) On a multi-user Apple TV (tvOS 26.5, Apple TV 4K 3rd gen), Game Center real-time matchmaking fails for every user except the default user, in every app I've tested — including Apple Arcade titles. Filed as FB24156316 with full logs and sysdiagnose; posting here for visibility and in case anyone has shipped multi-user GC multiplayer on tvOS successfully. My game adopts com.apple.developer.user-management (runs-as-current-user-with-user-independent-keychain). The entitlement itself works: on a secondary user's profile the app runs under that user's persona and GKLocalPlayer authenticates as them — the welcome banner shows the right account. But any GKMatchmakerViewController quickmatch hard-fails within ~9 seconds ("Failed to find players"), and accepting an invite fails with GKError 35 ("not signed in to iCloud") even though Settings shows that user's iCloud as signed in. Unified logs show the root cause. When matchmaking starts, gamed can't provision the player's pseudonym because the current user has no identity-services registration: gamed No URI found on any account -- returning nil gamed Failed to fetch pseudonym for local player. Error: GameDaemonCore.PseudonymManagerError.failedToProvision( internalError: Error Domain=com.apple.ids.IDSPseudonymErrorDomain Code=400 "Invalid URI") For the default user, the identical flow succeeds (identityservicesd … resultCode: 0). Across a full day of log capture — profile adds, a remove/re-add, multiple user switches — identityservicesd never once references the secondary users' accounts: registration for them is never attempted, not attempted-and-failed. Meanwhile gamed advertises the nearby-matchmaking Bonjour service with the default user's identity while the foreground app runs as the secondary user. Reproduction matrix: two apps (my shipping game Extreme Violence and Apple Arcade's Crossy Road Castle, which also runs under the correct persona), both sandbox and production Game Center, two unrelated secondary accounts (both healthy elsewhere). Persists across reboot and profile remove/re-add. Default user unaffected. The documentation says the entitlement is all that's needed ("each person who uses your app will have access to… their own Game Center… you don't have to make any code changes" — WWDC20 session 10645). As far as I can tell that promise is currently unfulfillable for online play: there is no API or Settings path that creates the missing IDS registration. Has anyone seen non-default-user matchmaking work on tvOS, on any version? Is there anything an app can do here, or is this purely an OS-side fix? (Related: thread 782163 — a different tvOS matchmaking failure that DTS confirmed as a bug.)
2
0
344
4d
What would case a compute kernel to run 100x slower only if it's run after a previous kernel.
I have two compute kernels. The first kernel pre_initization_0 initializes some buffers using a parallel random number generator initialize some buffers. The second kernel pre_sum_weights_0 effectively sums weighted values buffer of a much smaller dimension. #include <metal_stdlib> #include <metal_simdgroup> using namespace metal; struct mt_state { array<uint32_t, 624> array; uint16_t index; }; float random(device mt_state &state) { uint16_t k = state.index; uint16_t j = (k + 1) % 624; uint32_t x = (state.array[k] & 0x80000000U) | (state.array[j] & 0x7fffffffU); uint32_t xA = x >> 1; if (x & 0x00000001U) { xA ^= 0x9908b0dfU; } j = (k + 397) % 624; x = state.array[j]^xA; state.array[k] = x; state.index = (k + 1) % 624; uint32_t y = x^(x >> 11); y = y^((y << 7) & 0x9d2c5680U); y = y^((y << 15) & 0xefc60000U); return static_cast<float> (y^(y >> 18)); } kernel void pre_initization_0( device float *vc30c09b98 [[buffer(0)]], // x used 0 device float *vc30c09c38 [[buffer(1)]], // v_{||} used 0 device float *vc30c09cd8 [[buffer(2)]], // v_{\perp} used 0 device mt_state *sc30c50c18 [[buffer(3)]], constant uint32_t &offset [[buffer(4)]], uint index [[thread_position_in_grid]]) { if (offset + index < 3000000) { device mt_state &rc30c50c18 = sc30c50c18[index]; // used 4 const float rc30c06218 = 2.17689351e-08; // used 1 const float rc30c06318 = -46.7484322; // used 1 const float rc30c0a458 = fma(rc30c06218, random(rc30c50c18), rc30c06318); // used 1 const float rc30c07718 = 5.18059896e-05; // used 1 const float rc30c06798 = -1; // used 1 const float rc30c06618 = 2.32830644e-10; // used 2 const float rc30c06718 = 1.17549435e-38; // used 2 const float rc30c0a4f8 = fma(rc30c06618, random(rc30c50c18), rc30c06718); // used 1 const float r1034f02f8 = log(rc30c0a4f8); // used 1 const float rc30c06818 = rc30c06798*r1034f02f8; // used 1 const float r1034eedf8 = sqrt(rc30c06818); // used 1 const float rc30c06418 = 1.46291812e-09; // used 1 const float rc30c06498 = rc30c06418*random(rc30c50c18); // used 1 const float r1034eee68 = sin(rc30c06498); // used 1 const float rc30c06a18 = r1034eedf8*r1034eee68; // used 1 const float rc30c07698 = rc30c07718*rc30c06a18; // used 1 const float rc30c07598 = 3.3356411e-09; // used 1 const float rc30c07318 = -241213328; // used 1 const float rc30c0a598 = fma(rc30c06618, random(rc30c50c18), rc30c06718); // used 1 const float r1034ef238 = log(rc30c0a598); // used 1 const float rc30c06c98 = rc30c07318*r1034ef238; // used 1 const float rc310b0018 = sqrt(rc30c06c98); // used 1 const float rc30c07618 = rc30c07598*rc310b0018; // used 1 vc30c09b98[offset + index] = rc30c0a458; vc30c09c38[offset + index] = rc30c07698; vc30c09cd8[offset + index] = rc30c07618; } } kernel void pre_sum_weights_0( constant float *vc30c09b98 [[buffer(0)]], // x used 7 device atomic_float *vc30c0a098 [[buffer(1)]], const texture1d<float, access::read> ac310a9000 [[texture(0)]], const texture1d<float, access::read> ac310d8000 [[texture(1)]], const texture1d<float, access::read> ac310d9000 [[texture(2)]], const texture1d<float, access::read> ac310da000 [[texture(3)]], uint index [[thread_position_in_grid]]) { if (index < 3000000) { const float rc30c09b98 = vc30c09b98[index]; // x used 7 const float rc30c07798 = 0.0935904533; // used 2 const float rc30d08318 = rc30c09b98 - rc30c07798; // used 1 const ushort ic30d08398 = (ushort)min(max((rc30d08318 - -46.7484322)/0.0935904533,(float)0),(float)999); // used 1 const float rc30c07c98 = -5.34242535; // used 1 const ushort ic30d08018 = (ushort)min(max((rc30c09b98 - -46.7484322)/0.0935904533,(float)0),(float)999); // used 5 const float rc30c51018 = ac310da000.read(ic30d08018).r; // used 1 const float rc30c0a6d8 = fma(rc30c07c98, rc30c09b98, rc30c51018); // used 1 const float rc30c07a18 = -10.6848507; // used 1 const float rc30c50e98 = ac310d8000.read(ic30d08018).r; // used 1 const float rc30c0a778 = fma(rc30c07a18, rc30c09b98, rc30c50e98); // used 1 const float rc30c07b98 = rc30c0a6d8*rc30c0a778; // used 1 atomic_fetch_add_explicit(&vc30c0a098[ic30d08398], rc30c07b98, memory_order_relaxed); // used 1 const float rc30c07c18 = 0.75; // used 1 const float rc30c07e18 = 114.166031; // used 1 const float rc30c50d18 = ac310a9000.read(ic30d08018).r; // used 1 const float rc30d08098 = rc30c50d18 - rc30c09b98; // used 1 const float rc30c07d98 = rc30d08098*rc30d08098; // used 1 const float rc30c07e98 = rc30c07e18*rc30c07d98; // used 1 const float rc30d08218 = rc30c07c18 - rc30c07e98; // used 1 atomic_fetch_add_explicit(&vc30c0a098[ic30d08018], rc30d08218, memory_order_relaxed); // used 1 const float rc30d08498 = rc30c07798 + rc30c09b98; // used 1 const ushort ic30d08418 = (ushort)min(max((rc30d08498 - -46.7484322)/0.0935904533,(float)0),(float)999); // used 1 const float rc30c07b18 = 0.5; // used 1 const float rc30c07918 = 1.5; // used 1 const float rc30c07818 = 10.6848507; // used 1 const float rc30c50f98 = ac310d9000.read(ic30d08018).r; // used 1 const float rc30d08298 = rc30c50f98 - rc30c09b98; // used 1 const float rc30c07a98 = rc30c07818*rc30d08298; // used 1 const float rc30d08118 = rc30c07918 - rc30c07a98; // used 1 const float rc30d34018 = rc30d08118*rc30d08118; // used 1 const float rc30d34098 = rc30c07b18*rc30d34018; // used 1 atomic_fetch_add_explicit(&vc30c0a098[ic30d08418], rc30d34098, memory_order_relaxed); // used 1 } } Running some timing experiments, If I initialize [[buffer(0)]] of the pre_sum_weights_0 kernel on the CPU, and run the kernel, it will run in 0.0489688 s measured using GPUEndTime - GPUStartTime in a completedHander for the command buffer. If I run the initialization kernel pre_initization_0 before this, the kernel execution time of pre_sum_weights_0 slows to 7.20396 s. Note I am launching these kernels from different command buffers.
1
0
533
5d
GroundingShadowComponent — working on iOS/iPadOS by anyone?
Following up on thread 733918 (visionOS grounding shadows, solved by applying GroundingShadowComponent(castsShadow: true) to every ModelEntity in a hierarchy, not just the root). I've implemented that exact fix in an ARKit + RealityKit app on iOS/iPadOS (LiDAR devices, iOS 18+) — recursively applying the component to every entity with a ModelComponent, confirmed via logging that it's actually set. Real-world grounding shadows still never appear, under every configuration tried: Object anchored via a .plane-type AnchoringComponent.Target ARView.debugOptions.showAnchorGeometry confirms real collision geometry exactly where the object rests Ruled out the debug scene-understanding overlay visually masking the shadow (disabled it, no change) Inter-object shadows (virtual-on-virtual, via DirectionalLightComponent.Shadow) work fine — lighting itself isn't broken, just shadows onto the real-world mesh The pre-iOS 18 path (ARView.environment.sceneUnderstanding.options.insert(.receivesLighting)) also no longer produces a real-floor shadow — tried DirectionalLightComponent(isRealWorldProxy: true) and intensity from 800 up to 4000, no visual change either way. Two questions: Has anyone actually confirmed GroundingShadowComponent rendering a real-world shadow on iOS/iPadOS, or is it visionOS-only in practice despite being available on the iOS 18+ SDK? visionOS's documented fix for the same "lights don't affect passthrough" limitation is the ShadowReceivingOcclusionSurface Shader Graph node — is there an iOS/iPadOS equivalent, or is that visionOS-exclusive? Trying to determine if this is an unreported regression worth filing, or if there's an undocumented step I'm missing.
0
0
316
6d
Pinch gesture not recognized on MTKView when attaching it to a RealityView using a ViewAttachmentComponent in a immersive space
Hello! We are seeing a problem with a SwiftUI view that wraps an MTKView and that MTKView uses gesture recognizers from UIKit. One of those gestures we are using is UIPinchGestureRecognizer. And that gesture isn’t recognized at all when the SwiftUI view is attached to a RealityView using the ViewAttachmentComponent AND the RealityView is being shown in an ImmersiveSpace. If the SwiftUI view is attached to the RealityView using the init that has an attachment closure then pinching works fine there. So this definitely seems like a bug. Here is some code to help you reproduce the problem. Run this on a Vision Pro device. A simple red square will be rendered and if a single tap or pinch gesture is recognized on the red square, it will print to the console. App Code: import SwiftUI @main struct VisionPinchProblemsApp: App { var body: some Scene { WindowGroup { MenuView() } ImmersiveSpace(id: "RedSquare") { RedSquareView() } } } View code: import MetalKit import RealityKit import SwiftUI import UIKit struct MenuView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show red square", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "RedSquare") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct RedSquareView: View { let metalViewAttachmentID = "metalID" var body: some View { // Adds SwiftUI view using attachments closure. // Pinching and single taps are recognized here! // RealityView { content, attachments in // if let metalViewEntity = attachments.entity(for: metalViewAttachmentID) { // metalViewEntity.position = [0, 1, -1.25] // content.add(metalViewEntity) // } // } placeholder: { // ProgressView() // } attachments: { // Attachment(id: metalViewAttachmentID) { // MetalView() // } // } // Add SwiftUI view using ViewAttachmentComponent. // Pinching is not recognized here! // Single tapping is recognized ! // Why doesn't the red square show up in the Vision Pro simulator? RealityView { content in let metalViewEntity = Entity() let metalView = MetalView() .frame(width: 500, height: 500) let component = ViewAttachmentComponent(rootView: metalView) metalViewEntity.components.set(component) metalViewEntity.position = [0, 1, -1.25] content.add(metalViewEntity) } placeholder: { ProgressView() } } } struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator let pinchGesture = UIPinchGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handlePinch(_:))) mtkView.addGestureRecognizer(pinchGesture) let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handleTap(_:))) mtkView.addGestureRecognizer(tapGesture) return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var parent: MetalView init(_ parent: MetalView) { self.parent = parent } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = parent.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } @objc func handlePinch(_ sender: UIPinchGestureRecognizer) { print("Pinch detected") } @objc func handleTap(_ sender: UITapGestureRecognizer) { print("Tap detected") } } }
1
0
403
6d
ManipulationComponent causes makeUIView(context:) to get called twice
Here I have some demo code that is rendering a cylinder "platter" using RealityKit and there is a red circle rendered on top of it which uses Metal and SwiftUI. When the platter appears you will see in the console that makeUIView(context:) is called twice while it is documented that it will only be called once when the view appears for the first time. So this seems like a bug. If you remove ManipulationComponent from the platter's components you will see that this problem goes away so it seems like that is the cause of the problem. Any insight here would be appreciated! Thank you. Here is what is printed in the console: Entity returned from EntityWrapper.makeEntity(context:) was already parented to another entity. This is not supported and may lead to unexpected behavior. SwiftUI adds entities to internally-managed entity hierarchies. Make UI View! This should be called once. Make UI View! This should be called once. Here is the app code: import SwiftUI @main struct SomeApp: App { var body: some Scene { WindowGroup { ContentView() } ImmersiveSpace(id: "TableTop") { TableTopPlatterView() } } } Here is the view code: import MetalKit import RealityKit import SwiftUI struct ContentView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show table top", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "TableTop") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct TableTopPlatterView: View { private var attachmentID: String { "RedCircle" } var body: some View { RealityView { content, attachments in if let redCircleEntity = attachments.entity(for: attachmentID) { // Lays the red circle in the platter. let rotation = Rotation3D(redCircleEntity.orientation) .rotated(by: .init(angle: .degrees(-90), axis: .x)) redCircleEntity.setOrientation(.init(rotation), relativeTo: nil) redCircleEntity.position.y = 0.026 platterEntity.addChild(redCircleEntity) content.add(platterEntity) } } placeholder: { ProgressView() } attachments: { Attachment(id: attachmentID) { MetalView() .clipShape(.circle) } } } /// The platter entity that the red circle lays on top of. private let platterEntity: ModelEntity = { let anchor = AnchorEntity( .plane( .horizontal, classification: .table, minimumBounds: [0.01, 0.01] ) ) let material = SimpleMaterial( color: .lightGray, roughness: 0.5, isMetallic: false ) let platter = ModelEntity( mesh: .generateCylinder(height: 0.05, radius: 0.475), materials: [material] ) platter.generateCollisionShapes(recursive: false) let components: [any Component] = [ InputTargetComponent(), GroundingShadowComponent(castsShadow: true), ManipulationComponent() // MARK: This is causing makeUIView to get called twice! ] platter.components.set(components) // Placed closer to the user when booted up. platter.position = [0, 1, -1.25] anchor.addChild(platter) return platter }() } // Metal view that renders a red square. struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { print("Make UI View! This should be called once.") let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var metalView: MetalView init(_ metalView: MetalView) { self.metalView = metalView } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = metalView.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } } }
3
0
1.2k
6d
TextComponent Renders Inverted Text on Mac Catalyst
On Mac Catalyst, RealityKit TextComponent renders vertically inverted text. The issue occurs with both ARView and RealityView. The same text renders correctly on iOS and in a native macOS RealityView application (not Catalyst). Tested on macOS 26 and macOS 27 beta, with Xcode 26 and Xcode 27 Beta. Here is a screenshot of a text entity with a text component rendering the string "Text Component": Workaround On Mac Catalyst: Detect when the model generated by TextComponent becomes available on the text entity. Find the generated UnlitMaterial in the ModelComponent of the text entity. Disable face culling on the material. Invert the Y scale of the text entity. Here is the result: Reproduce Below is the full code that reproduces the problem, with both ARView and RealityView, as well as the workaround implementation. import SwiftUI import RealityKit import Combine // MARK: Text func createTextEntity() -> Entity { let textEntity = Entity() let attributes: [NSAttributedString.Key: Any] = [ .font: MeshResource.Font.systemFont(ofSize: 48, weight: .bold), .foregroundColor: Material.Color.white ] let attributedText = AttributedString( NSAttributedString( string: "Text Component", attributes: attributes ) ) var textComponent = TextComponent() textComponent.text = attributedText textComponent.size = CGSize(width: 400, height: 200) textComponent.backgroundColor = Material.Color.darkGray.cgColor textEntity.components.set(textComponent) return textEntity } // MARK: Camera func createCameraEntity() -> Entity { let cameraEntity = Entity() cameraEntity.components.set(PerspectiveCameraComponent()) cameraEntity.look(at: .zero, from: [0, 0, 0.3], relativeTo: nil) return cameraEntity } // MARK: ARView struct CatalystTextBugARView: UIViewRepresentable { let applyWorkaround: Bool = true func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) arView.cameraMode = .nonAR arView.automaticallyConfigureSession = false arView.environment.background = .color(.black) let rootEntity = AnchorEntity() let textEntity = createTextEntity() #if targetEnvironment(macCatalyst) if applyWorkaround { /// React when the text entity's ModelComponent becomes available or changes context.coordinator.modelDidAddSubscription = arView.scene.subscribe( to: ComponentEvents.DidAdd.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } context.coordinator.modelDidChangeSubscription = arView.scene.subscribe( to: ComponentEvents.DidChange.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } } #endif rootEntity.addChild(textEntity) rootEntity.addChild(createCameraEntity()) arView.scene.addAnchor(rootEntity) return arView } func updateUIView(_ uiView: ARView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator { /// Retain the scene subscriptions for the lifetime of the ARView. var modelDidAddSubscription: (any Cancellable)? var modelDidChangeSubscription: (any Cancellable)? } } // MARK: RealityView struct CatalystTextBugRealityView: View { let applyWorkaround: Bool = true /// Retain the RealityView subscriptions for the lifetime of the view. @State private var modelDidAddSubscription: EventSubscription? @State private var modelDidChangeSubscription: EventSubscription? var body: some View { RealityView { content in let textEntity = createTextEntity() #if targetEnvironment(macCatalyst) if applyWorkaround { /// React when the text entity's ModelComponent becomes available or changes. modelDidAddSubscription = content.subscribe( to: ComponentEvents.DidAdd.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } modelDidChangeSubscription = content.subscribe( to: ComponentEvents.DidChange.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } } #endif content.add(textEntity) content.add(createCameraEntity()) } .background(.black) } } // MARK: Workaround func applyTextBugWorkaround(to textEntity: Entity) { #if targetEnvironment(macCatalyst) guard var modelComponent = textEntity.components[ModelComponent.self] else { return } var foundTextMaterial = false var changedMaterial = false for materialIndex in modelComponent.materials.indices { guard var textMaterial = modelComponent.materials[materialIndex] as? UnlitMaterial else { continue } foundTextMaterial = true /// If face culling is already disabled, skip if case .none = textMaterial.faceCulling { continue } textMaterial.faceCulling = .none modelComponent.materials[materialIndex] = textMaterial changedMaterial = true } guard foundTextMaterial else { return } if changedMaterial { textEntity.components.set(modelComponent) } /// Counteract vertically inverted text. textEntity.scale.y = -abs(textEntity.scale.y) #endif } Usage Run either CatalystTextBugRealityView() or CatalystTextBugARView() on a Mac Catalyst target. Toggle applyWorkaround on either view to try with and without the workaround.
1
0
445
1w
Metal rendering application is not releasing resources
I am developing a metal based ray tracing rendering application (running heavy GPU kernels). I am sometimes "forcefully quitting" my application and I can see the application is not in the activity monitor. But I can see the windowserver is using %97 the GPU. The mac gets hotter and hotter. I kill the windowserver, re-login it is still the case. The only way to fix is to restart the mac. I have checked if there are any zombie processes, there are none. I am 3-4 month into Mac development (I used many rendering APIs e.g. before under Windows and Linux, they release the resources automatically unless the driver is very broken), but I believe when you force quit or exit gracefully, regarding application should release resources. I may be missing some knowledge. Does anybody have an idea? I had added every corner a graceful exit code but once the kernel has some infinite loop the clean up cannot happen. In Windows there are some driver reload mechanisms to recover when GPU is stuck, is there a similar system ?
1
0
1.4k
1w
macOS 27 beta: ProMotion refresh cadence is unstable, causing constant scroll judder
FB24091347 On macOS 27.0 beta (26A5388g), MacBook Pro M4 Pro, the built-in ProMotion display never settles on a stable refresh cadence. Scrolling in SwiftUI judders constantly. The same app binary was smooth on macOS 26, and is smooth on a 120 Hz ProMotion iPad. I captured two 60-second Instruments traces — same app, same scene, same scrolling, no external display — changing only the display's refresh-rate setting. On ProMotion the vsync interval standard deviation is 4.093 ms across six different cadences, mostly flip-flopping between 120 Hz and 60 Hz. Forced to a fixed 60 Hz it drops to 0.391 ms with a single cadence. The app presented an identical 59 fps median in both runs — frame production is perfectly steady, the display just holds each frame for an unpredictable length of time. That's what makes this nasty: it's invisible to every frame-rate metric, so it looks like the app got slow when nothing about the app changed. I spent most of a day profiling my own code before realising the app was never the problem. Workaround: force the built-in display to 60 Hz. Worth noting, because it complicates the picture: attaching a 60 Hz Studio Display makes the built-in smooth, but the Studio itself then judders — despite its own vsync cadence measuring perfectly stable. So refresh rate alone isn't the whole story, and there may be a second mechanism. The clean, reproducible, single-variable result is the ProMotion vs forced-60 Hz comparison on the built-in panel. If you can reproduce this on an M-series MacBook Pro on 27 beta, please file a duplicate referencing FB24091347.
0
0
343
1w
Using CARenderer for off-screen rendering of WKWebView results in a blank screen for the web page content on iOS 16 system version.
Using the CRenderer off-screen rendering method for WKWebView results in a blank screen for the web page content on the iOS 16 system version, but it can successfully obtain the web page content screen on the iOS 18 system version. I need a solution to achieve the display of web page content on the 16 system version, with a frame rate of more than 60 frames per second.
1
0
1.1k
1w
Draw WKWebView into OpenGL Texture
I'm trying to figure out how to display a Web Browser inside my iOS VR app (Obj-c, SceneKit and raw OpenGL), and the part i'm not fully understanding is how to get the WKWebView to draw it's content into a Pixel Buffer of some sort, so I can use the speed of CVOpenGLESTextureCacheCreateTextureFromImage to convert the pixel data into a OpenGl Texture quickly/efficently and display it on a floating surface.I'm already doing something simular with the video portion of my app, but it has a AVPlayerItemVideoOutput, which produced the pixel buffer, but I can't figure out how to massage the CALayer into a Buffer so I can convert it into a texture to then draw in opengl.I know it has something to do with drawLayer:(Layer) ,(Context), but searching online hasn't been very fruitful.And i'm not using SceneKit like you would assume, the app was built before GVR for Scenekit was a thing, so every part of VR is handled manually (scenekit to textures, textures to opengl for Left/Right eye distortion mesh).
3
0
2.4k
1w
Core Image kernel sampling broken in iOS27 DB4
I've noticed that my camera app is returning blank images on developer beta 4. After some investigation there is an issue with core image custom kernels where the texture sampler is returning NaN / 0 floats. I have a reproducible demo here: https://github.com/alexfoxy/ci-metal-shader-bug Feedback ticket here: https://feedbackassistant.apple.com/feedback/23895753
2
3
2.1k
2w
Residency set memory not freed if process performs no GPU operation
Feedback report: FB23959296 If a process creates a residency set, calls requestResidency, endResidency, and then releases the residency set without ever having done any GPU operations, the memory from the residency set is not freed. Workaround: if the application runs any GPU operation (even an operation not involving the residency set) at any point in its lifecycle (before/while/after creating/releasing the residency set), the memory is freed properly. This was observed in the context of an application that makes an AI model resident in GPU-accessible memory. If the user unloads the model without running any prompts, the memory is not freed. The model occupies ~16GB of RAM so a lot of memory is being leaked. Reproduction: Store the repro.m and workaround.m files from below Run the following commands (repro.m demonstrates the bug; workaround.m demonstrates the workaround): $ clang -framework Foundation -framework Metal -o repro repro.m $ ./repro Footprint at start: 0.00 GB Footprint after buffer allocation: 4.30 GB Footprint 5s after teardown: 4.30 GB $ clang -framework Foundation -framework Metal -o workaround workaround.m $ ./workaround Footprint at start: 0.00 GB Footprint after buffer allocation: 4.37 GB Footprint 5s after teardown: 0.01 GB Expected behavior: Footprint 5s after teardown should be ~0 GB, i.e., the memory is freed. Observed behavior: Footprint 5s after teardown is 4.30 GB, i.e., the memory is not freed. Versions: XCode: 26.6 (17F113) Clang: 21.0.0 (clang-2100.1.1.101, arm64-apple-darwin25.5.0) macOS: 26.5.2 (25F84) Files: repro.m: // Build: clang -framework Foundation -framework Metal -o repro repro.m #import <Metal/Metal.h> #include <mach/mach.h> // Returns the physical memory footprint of the process. static double footprint_gb(void) { task_vm_info_data_t info; mach_msg_type_number_t n = TASK_VM_INFO_COUNT; task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&info, &n); return (double)info.phys_footprint / 1e9; } int main(int argc, char ** argv) { printf("Footprint at start: %5.2f GB\n", footprint_gb()); @autoreleasepool { id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); // Allocate ~4GB of memory. const size_t size = 4ULL << 30; id<MTLBuffer> buf = [dev newBufferWithLength:size options:MTLResourceStorageModeShared]; memset(buf.contents, 0xab, size); // fault the pages in printf("Footprint after buffer allocation: %5.2f GB\n", footprint_gb()); MTLResidencySetDescriptor * desc = [[MTLResidencySetDescriptor alloc] init]; id<MTLResidencySet> rset = [dev newResidencySetWithDescriptor:desc error:nil]; [desc release]; [rset addAllocation:buf]; [rset commit]; [rset requestResidency]; [rset endResidency]; [rset removeAllAllocations]; [rset commit]; [rset release]; [buf release]; [dev release]; } sleep(5); printf("Footprint 5s after teardown: %5.2f GB\n", footprint_gb()); return 0; } workaround.m: // Build: clang -framework Foundation -framework Metal -o workaround workaround.m #import <Metal/Metal.h> #include <mach/mach.h> // Returns the physical memory footprint of the process. static double footprint_gb(void) { task_vm_info_data_t info; mach_msg_type_number_t n = TASK_VM_INFO_COUNT; task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&info, &n); return (double)info.phys_footprint / 1e9; } // Performing any work on the GPU ensures the memory from the residency set will be released. static void do_dummy_work(id<MTLDevice> dev, id<MTLCommandQueue> queue) { @autoreleasepool { id<MTLBuffer> tmp = [dev newBufferWithLength:1 options:MTLResourceStorageModeShared]; id<MTLCommandBuffer> cb = [queue commandBuffer]; id<MTLBlitCommandEncoder> enc = [cb blitCommandEncoder]; [enc fillBuffer:tmp range:NSMakeRange(0, 1) value:0]; [enc endEncoding]; [cb commit]; [tmp release]; } } int main(int argc, char ** argv) { printf("Footprint at start: %5.2f GB\n", footprint_gb()); @autoreleasepool { id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); id<MTLCommandQueue> queue = [dev newCommandQueue]; // Workaround that ensures the memory will be released. // It also works if we call this after the residency set release or at any point in between. do_dummy_work(dev, queue); // Allocate ~4GB of memory. const size_t size = 4ULL << 30; id<MTLBuffer> buf = [dev newBufferWithLength:size options:MTLResourceStorageModeShared]; memset(buf.contents, 0xab, size); // fault the pages in printf("Footprint after buffer allocation: %5.2f GB\n", footprint_gb()); MTLResidencySetDescriptor * desc = [[MTLResidencySetDescriptor alloc] init]; id<MTLResidencySet> rset = [dev newResidencySetWithDescriptor:desc error:nil]; [desc release]; [rset addAllocation:buf]; [rset commit]; [rset requestResidency]; [rset endResidency]; [rset removeAllAllocations]; [rset commit]; [rset release]; [buf release]; [queue release]; [dev release]; } sleep(5); printf("Footprint 5s after teardown: %5.2f GB\n", footprint_gb()); return 0; }
0
0
757
2w
WatchBlocks v1.5 is now available: a sandbox game built for Apple Watch
Hi everyone! After about 4-5 months of development, I released WatchBlocks v1.5 today. The project started as an experiment to answer a simple question: Could you build a real sandbox survival game that runs well on Apple Watch? It eventually grew into a cross-platform game supporting Apple Watch, iPhone, iPad, and Mac. Some of the technical challenges I enjoyed solving included: Optimizing rendering and gameplay for watchOS. Designing controls around the Digital Crown and the watch display. Building multiplayer across Apple platforms. Creating a content system that lets players make and share their own blocks, items, mobs, and biomes. The new update also transitions the game to a free-to-start model, making it easier for people to try it before deciding whether to unlock the full experience. I’d love to hear from other developers building for watchOS. It’s a platform that doesn’t get much attention for games, but I think there’s a lot of untapped potential. If anyone has questions about developing games for Apple Watch, I’m happy to answer them. App Store: https://apps.apple.com/us/app/watchblocks-craft-build/id6760209351
0
0
624
2w
Refresh Rate Drops from 144Hz to 98Hz After Monitor Power Cycle on macOS Golden Gate 27
Hello, I use a Gigabyte M32U monitor with my Mac mini. After updating to macOS Golden Gate 27 Release 3, I started experiencing an issue with my external display. Whenever I turn my monitor off and then turn it back on, the refresh rate automatically changes from 144Hz to 98Hz, and the 144Hz option disappears from the display settings. However, if I unplug the display cable and plug it back in, the monitor is detected again and the 144Hz option returns, allowing me to select it normally. This issue only started after updating to macOS Golden Gate 27 Release 3. Before the update, the monitor consistently worked at 144Hz without any problems. Could you please investigate this issue? Thank you.
1
0
652
2w
SpriteKit scene used as SCNView.overlaySKScene crashes due to SKShapeNode
I recently published my first game on the App Store. It uses SceneKit with a SpriteKit overlay. All crashes Xcode downloaded for it so far are related to some SpriteKit/SceneKit internals. The most common crash is caused by SKCShapeNode::_NEW_copyRenderPathData. What could cause such a crash? crash.crash While developing this game (and the BoardGameKit framework that appears in the crash log) over the years I experienced many crashes presumably caused by the SpriteKit overlay (I opened a post SceneKit app randomly crashes with EXC_BAD_ACCESS in jet_context::set_fragment_texture about such a crash in September 2024), and other people on the internet also mention that they experience crashes when using SpriteKit as a SceneKit overlay. Should I use a separate SKView and lay it on top of SCNView rather than setting SCNView.overlaySKScene? That seemed to solve the crashes for a guy on stackoverflow, but is it also encouraged by Apple? I know SceneKit is deprecated, but according to Apple critical bugs would still be fixed. Could this be considered a critical bug?
Replies
9
Boosts
0
Views
1.8k
Activity
1d
MPS bf16 softmax produces NaN on M5 Max (regression from M4) — breaks all on-device diffusion inference
Metal Performance Shaders produces from bf16/fp16 softmax for large attention tensors on , forcing the entire local generative-AI ecosystem to fall back to fp32. This is a . Environment Minimal reproduction Decomposed softmax on MPS — diffs = x - maxes produces NaN even though every element is identical (result should be all zeros): Real-world impact Running ComfyUI (the dominant local generative-AI UI) on M5 Max: Root cause (per PyTorch MPS maintainers) PyTorch maintainers (@drisspg, @albanD) have traced this to in the MPS/MPSGraph fused kernels. The NaN originates in the x - maxes subtraction inside softmax, then propagates through the attention block and the entire network. PyTorch cannot fix this — it is in the Metal/MPS kernel layer. Not isolated The M4-era fix ("fixed on macOS 15.1") did not survive onto M5, indicating the MPS fused-kernel precision fix was either reverted or not ported to the M5 GPU architecture. Request
Replies
0
Boosts
0
Views
401
Activity
1d
Behaviour of a 0-value `accelerationStructureID`
When constructing a MTLIndirectAccelerationStructureInstanceDescriptor, one specifies an accelerationStructureID. In the equivalents in both Vulkan and DirectX12, one can set it to zero to be an "inactive" instance. However, on Metal, this field does not appear to have any documentation, and thus it is difficult to figure out if there is similar behavior (and no other Metal documentation seems to mention this). Does setting this field to 0 (i.e. null) disable the instance? If not, is there any other way to have an equivalent effect?
Replies
0
Boosts
0
Views
412
Activity
1d
Best Way to Use MetalFX in Unreal Engine 5.7 for macOS Port?
Hi everyone, We’re currently porting a high-fidelity AA+ PC title built on Unreal Engine 5.7 to macOS (Apple Silicon), and we’re looking for guidance from anyone with experience in this area. At the moment, the game is already runnable on Mac, but not yet at a playable level — we’re seeing performance around 10–15 FPS on an M4 device. We’re actively analyzing and defining the work needed to reach production-quality performance on macOS. One of the key areas we’re exploring is leveraging MetalFX to improve frame rate. However, it seems there’s no official MetalFX plugin or direct integration available for Unreal Engine. Has anyone here successfully integrated MetalFX into a UE5 rendering pipeline, or found a recommended approach to do so? Any insights on best practices, workflows, or references (docs, samples, etc.) would be greatly appreciated. Thanks in advance!
Replies
4
Boosts
0
Views
1.7k
Activity
2d
view protocol update ?
Hello ! Thank you guys for all the hard work you are doing on RealityKit For now in my studying Im trying to understand, in general, what will cause my Main app view to redraw everything? Or any view? If i have a struct that use the view protocol, and that struct is being called inside RealityView on the main app, or outside a reality view within a ZStack on the main app, if this view updates, does this will cause everything in my main app view to redraw as well? Or is it only true to @State variables of the main app? or the @State variables of the external view i try to implement? and what about @Observable ? Is there an accurate table that tells the developer what will cause a redraw of things? (not only the things the logic asks for, but all things sitting idle within the view) I try to understand how to separate UI updates, to avoid full redraw of things that havent been changed. to minimize UI compute in my games Before i play around with instruments i need to understand the general architecture.. if i work in a way that is counter designed to the way reality kit should work then the instrument results will not make sense to me. so i feel like i should ask you guys first. Depending on your answer i will know how to arrange my data in my game. And how to design and instantiate all my views Thanks
Replies
0
Boosts
0
Views
232
Activity
3d
MTL4FXFrameInterpolator no-op on MTL4CommandBuffer
I'm trying to use the new MTL4FX::FrameInterpolator(the Metal 4 variant that encodes to MTL4::CommandBuffer). It creates fine, accepts all texture bindings, and encodes without any error or assertion. The GPU signals completion via shared event. But the output texture is completely untouched — zero bytes changed from before the encode. It's a silent no-op. I'm trying to call metal-cpp from python by making dynamic library with cpp. Environment: macOS 26.6 (build 25G72) Apple M3, arm64 Xcode 26 SDK MetalFX framework (MTL4FX API, macOS 26+) I'm on M3 Summary: MTL4FX::FrameInterpolator::encodeToCommandBuffer(MTL4::CommandBuffer*) records, commits, and the GPU signals completion, but writes zero bytes to the output texture. The encode is a silent no-op — identical to documented issues 146436460 and 146436741 for MTL4FXTemporalScaler/DenoisedScaler. Diagnostic trace: Output texture zeroed before encode: 0/32768 non-zero bytes Output texture after encode (delta=0.5): 0/32768 non-zero bytes Changed bytes: 0/32768 ← definitive no-op Input textures verified: correct (R=1, G=0 vs R=0, G=1) I dont really know how to explain this, the result its just blank.
Replies
1
Boosts
0
Views
548
Activity
4d
Game Center matchmaking fails for all non-default users on Apple TV — no IDS registration for secondary users (FB24156316)
Game Center matchmaking fails for all non-default users on Apple TV — no IDS registration for secondary users (FB24156316) On a multi-user Apple TV (tvOS 26.5, Apple TV 4K 3rd gen), Game Center real-time matchmaking fails for every user except the default user, in every app I've tested — including Apple Arcade titles. Filed as FB24156316 with full logs and sysdiagnose; posting here for visibility and in case anyone has shipped multi-user GC multiplayer on tvOS successfully. My game adopts com.apple.developer.user-management (runs-as-current-user-with-user-independent-keychain). The entitlement itself works: on a secondary user's profile the app runs under that user's persona and GKLocalPlayer authenticates as them — the welcome banner shows the right account. But any GKMatchmakerViewController quickmatch hard-fails within ~9 seconds ("Failed to find players"), and accepting an invite fails with GKError 35 ("not signed in to iCloud") even though Settings shows that user's iCloud as signed in. Unified logs show the root cause. When matchmaking starts, gamed can't provision the player's pseudonym because the current user has no identity-services registration: gamed No URI found on any account -- returning nil gamed Failed to fetch pseudonym for local player. Error: GameDaemonCore.PseudonymManagerError.failedToProvision( internalError: Error Domain=com.apple.ids.IDSPseudonymErrorDomain Code=400 "Invalid URI") For the default user, the identical flow succeeds (identityservicesd … resultCode: 0). Across a full day of log capture — profile adds, a remove/re-add, multiple user switches — identityservicesd never once references the secondary users' accounts: registration for them is never attempted, not attempted-and-failed. Meanwhile gamed advertises the nearby-matchmaking Bonjour service with the default user's identity while the foreground app runs as the secondary user. Reproduction matrix: two apps (my shipping game Extreme Violence and Apple Arcade's Crossy Road Castle, which also runs under the correct persona), both sandbox and production Game Center, two unrelated secondary accounts (both healthy elsewhere). Persists across reboot and profile remove/re-add. Default user unaffected. The documentation says the entitlement is all that's needed ("each person who uses your app will have access to… their own Game Center… you don't have to make any code changes" — WWDC20 session 10645). As far as I can tell that promise is currently unfulfillable for online play: there is no API or Settings path that creates the missing IDS registration. Has anyone seen non-default-user matchmaking work on tvOS, on any version? Is there anything an app can do here, or is this purely an OS-side fix? (Related: thread 782163 — a different tvOS matchmaking failure that DTS confirmed as a bug.)
Replies
2
Boosts
0
Views
344
Activity
4d
What would case a compute kernel to run 100x slower only if it's run after a previous kernel.
I have two compute kernels. The first kernel pre_initization_0 initializes some buffers using a parallel random number generator initialize some buffers. The second kernel pre_sum_weights_0 effectively sums weighted values buffer of a much smaller dimension. #include <metal_stdlib> #include <metal_simdgroup> using namespace metal; struct mt_state { array<uint32_t, 624> array; uint16_t index; }; float random(device mt_state &state) { uint16_t k = state.index; uint16_t j = (k + 1) % 624; uint32_t x = (state.array[k] & 0x80000000U) | (state.array[j] & 0x7fffffffU); uint32_t xA = x >> 1; if (x & 0x00000001U) { xA ^= 0x9908b0dfU; } j = (k + 397) % 624; x = state.array[j]^xA; state.array[k] = x; state.index = (k + 1) % 624; uint32_t y = x^(x >> 11); y = y^((y << 7) & 0x9d2c5680U); y = y^((y << 15) & 0xefc60000U); return static_cast<float> (y^(y >> 18)); } kernel void pre_initization_0( device float *vc30c09b98 [[buffer(0)]], // x used 0 device float *vc30c09c38 [[buffer(1)]], // v_{||} used 0 device float *vc30c09cd8 [[buffer(2)]], // v_{\perp} used 0 device mt_state *sc30c50c18 [[buffer(3)]], constant uint32_t &offset [[buffer(4)]], uint index [[thread_position_in_grid]]) { if (offset + index < 3000000) { device mt_state &rc30c50c18 = sc30c50c18[index]; // used 4 const float rc30c06218 = 2.17689351e-08; // used 1 const float rc30c06318 = -46.7484322; // used 1 const float rc30c0a458 = fma(rc30c06218, random(rc30c50c18), rc30c06318); // used 1 const float rc30c07718 = 5.18059896e-05; // used 1 const float rc30c06798 = -1; // used 1 const float rc30c06618 = 2.32830644e-10; // used 2 const float rc30c06718 = 1.17549435e-38; // used 2 const float rc30c0a4f8 = fma(rc30c06618, random(rc30c50c18), rc30c06718); // used 1 const float r1034f02f8 = log(rc30c0a4f8); // used 1 const float rc30c06818 = rc30c06798*r1034f02f8; // used 1 const float r1034eedf8 = sqrt(rc30c06818); // used 1 const float rc30c06418 = 1.46291812e-09; // used 1 const float rc30c06498 = rc30c06418*random(rc30c50c18); // used 1 const float r1034eee68 = sin(rc30c06498); // used 1 const float rc30c06a18 = r1034eedf8*r1034eee68; // used 1 const float rc30c07698 = rc30c07718*rc30c06a18; // used 1 const float rc30c07598 = 3.3356411e-09; // used 1 const float rc30c07318 = -241213328; // used 1 const float rc30c0a598 = fma(rc30c06618, random(rc30c50c18), rc30c06718); // used 1 const float r1034ef238 = log(rc30c0a598); // used 1 const float rc30c06c98 = rc30c07318*r1034ef238; // used 1 const float rc310b0018 = sqrt(rc30c06c98); // used 1 const float rc30c07618 = rc30c07598*rc310b0018; // used 1 vc30c09b98[offset + index] = rc30c0a458; vc30c09c38[offset + index] = rc30c07698; vc30c09cd8[offset + index] = rc30c07618; } } kernel void pre_sum_weights_0( constant float *vc30c09b98 [[buffer(0)]], // x used 7 device atomic_float *vc30c0a098 [[buffer(1)]], const texture1d<float, access::read> ac310a9000 [[texture(0)]], const texture1d<float, access::read> ac310d8000 [[texture(1)]], const texture1d<float, access::read> ac310d9000 [[texture(2)]], const texture1d<float, access::read> ac310da000 [[texture(3)]], uint index [[thread_position_in_grid]]) { if (index < 3000000) { const float rc30c09b98 = vc30c09b98[index]; // x used 7 const float rc30c07798 = 0.0935904533; // used 2 const float rc30d08318 = rc30c09b98 - rc30c07798; // used 1 const ushort ic30d08398 = (ushort)min(max((rc30d08318 - -46.7484322)/0.0935904533,(float)0),(float)999); // used 1 const float rc30c07c98 = -5.34242535; // used 1 const ushort ic30d08018 = (ushort)min(max((rc30c09b98 - -46.7484322)/0.0935904533,(float)0),(float)999); // used 5 const float rc30c51018 = ac310da000.read(ic30d08018).r; // used 1 const float rc30c0a6d8 = fma(rc30c07c98, rc30c09b98, rc30c51018); // used 1 const float rc30c07a18 = -10.6848507; // used 1 const float rc30c50e98 = ac310d8000.read(ic30d08018).r; // used 1 const float rc30c0a778 = fma(rc30c07a18, rc30c09b98, rc30c50e98); // used 1 const float rc30c07b98 = rc30c0a6d8*rc30c0a778; // used 1 atomic_fetch_add_explicit(&vc30c0a098[ic30d08398], rc30c07b98, memory_order_relaxed); // used 1 const float rc30c07c18 = 0.75; // used 1 const float rc30c07e18 = 114.166031; // used 1 const float rc30c50d18 = ac310a9000.read(ic30d08018).r; // used 1 const float rc30d08098 = rc30c50d18 - rc30c09b98; // used 1 const float rc30c07d98 = rc30d08098*rc30d08098; // used 1 const float rc30c07e98 = rc30c07e18*rc30c07d98; // used 1 const float rc30d08218 = rc30c07c18 - rc30c07e98; // used 1 atomic_fetch_add_explicit(&vc30c0a098[ic30d08018], rc30d08218, memory_order_relaxed); // used 1 const float rc30d08498 = rc30c07798 + rc30c09b98; // used 1 const ushort ic30d08418 = (ushort)min(max((rc30d08498 - -46.7484322)/0.0935904533,(float)0),(float)999); // used 1 const float rc30c07b18 = 0.5; // used 1 const float rc30c07918 = 1.5; // used 1 const float rc30c07818 = 10.6848507; // used 1 const float rc30c50f98 = ac310d9000.read(ic30d08018).r; // used 1 const float rc30d08298 = rc30c50f98 - rc30c09b98; // used 1 const float rc30c07a98 = rc30c07818*rc30d08298; // used 1 const float rc30d08118 = rc30c07918 - rc30c07a98; // used 1 const float rc30d34018 = rc30d08118*rc30d08118; // used 1 const float rc30d34098 = rc30c07b18*rc30d34018; // used 1 atomic_fetch_add_explicit(&vc30c0a098[ic30d08418], rc30d34098, memory_order_relaxed); // used 1 } } Running some timing experiments, If I initialize [[buffer(0)]] of the pre_sum_weights_0 kernel on the CPU, and run the kernel, it will run in 0.0489688 s measured using GPUEndTime - GPUStartTime in a completedHander for the command buffer. If I run the initialization kernel pre_initization_0 before this, the kernel execution time of pre_sum_weights_0 slows to 7.20396 s. Note I am launching these kernels from different command buffers.
Replies
1
Boosts
0
Views
533
Activity
5d
GroundingShadowComponent — working on iOS/iPadOS by anyone?
Following up on thread 733918 (visionOS grounding shadows, solved by applying GroundingShadowComponent(castsShadow: true) to every ModelEntity in a hierarchy, not just the root). I've implemented that exact fix in an ARKit + RealityKit app on iOS/iPadOS (LiDAR devices, iOS 18+) — recursively applying the component to every entity with a ModelComponent, confirmed via logging that it's actually set. Real-world grounding shadows still never appear, under every configuration tried: Object anchored via a .plane-type AnchoringComponent.Target ARView.debugOptions.showAnchorGeometry confirms real collision geometry exactly where the object rests Ruled out the debug scene-understanding overlay visually masking the shadow (disabled it, no change) Inter-object shadows (virtual-on-virtual, via DirectionalLightComponent.Shadow) work fine — lighting itself isn't broken, just shadows onto the real-world mesh The pre-iOS 18 path (ARView.environment.sceneUnderstanding.options.insert(.receivesLighting)) also no longer produces a real-floor shadow — tried DirectionalLightComponent(isRealWorldProxy: true) and intensity from 800 up to 4000, no visual change either way. Two questions: Has anyone actually confirmed GroundingShadowComponent rendering a real-world shadow on iOS/iPadOS, or is it visionOS-only in practice despite being available on the iOS 18+ SDK? visionOS's documented fix for the same "lights don't affect passthrough" limitation is the ShadowReceivingOcclusionSurface Shader Graph node — is there an iOS/iPadOS equivalent, or is that visionOS-exclusive? Trying to determine if this is an unreported regression worth filing, or if there's an undocumented step I'm missing.
Replies
0
Boosts
0
Views
316
Activity
6d
Pinch gesture not recognized on MTKView when attaching it to a RealityView using a ViewAttachmentComponent in a immersive space
Hello! We are seeing a problem with a SwiftUI view that wraps an MTKView and that MTKView uses gesture recognizers from UIKit. One of those gestures we are using is UIPinchGestureRecognizer. And that gesture isn’t recognized at all when the SwiftUI view is attached to a RealityView using the ViewAttachmentComponent AND the RealityView is being shown in an ImmersiveSpace. If the SwiftUI view is attached to the RealityView using the init that has an attachment closure then pinching works fine there. So this definitely seems like a bug. Here is some code to help you reproduce the problem. Run this on a Vision Pro device. A simple red square will be rendered and if a single tap or pinch gesture is recognized on the red square, it will print to the console. App Code: import SwiftUI @main struct VisionPinchProblemsApp: App { var body: some Scene { WindowGroup { MenuView() } ImmersiveSpace(id: "RedSquare") { RedSquareView() } } } View code: import MetalKit import RealityKit import SwiftUI import UIKit struct MenuView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show red square", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "RedSquare") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct RedSquareView: View { let metalViewAttachmentID = "metalID" var body: some View { // Adds SwiftUI view using attachments closure. // Pinching and single taps are recognized here! // RealityView { content, attachments in // if let metalViewEntity = attachments.entity(for: metalViewAttachmentID) { // metalViewEntity.position = [0, 1, -1.25] // content.add(metalViewEntity) // } // } placeholder: { // ProgressView() // } attachments: { // Attachment(id: metalViewAttachmentID) { // MetalView() // } // } // Add SwiftUI view using ViewAttachmentComponent. // Pinching is not recognized here! // Single tapping is recognized ! // Why doesn't the red square show up in the Vision Pro simulator? RealityView { content in let metalViewEntity = Entity() let metalView = MetalView() .frame(width: 500, height: 500) let component = ViewAttachmentComponent(rootView: metalView) metalViewEntity.components.set(component) metalViewEntity.position = [0, 1, -1.25] content.add(metalViewEntity) } placeholder: { ProgressView() } } } struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator let pinchGesture = UIPinchGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handlePinch(_:))) mtkView.addGestureRecognizer(pinchGesture) let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handleTap(_:))) mtkView.addGestureRecognizer(tapGesture) return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var parent: MetalView init(_ parent: MetalView) { self.parent = parent } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = parent.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } @objc func handlePinch(_ sender: UIPinchGestureRecognizer) { print("Pinch detected") } @objc func handleTap(_ sender: UITapGestureRecognizer) { print("Tap detected") } } }
Replies
1
Boosts
0
Views
403
Activity
6d
ManipulationComponent causes makeUIView(context:) to get called twice
Here I have some demo code that is rendering a cylinder "platter" using RealityKit and there is a red circle rendered on top of it which uses Metal and SwiftUI. When the platter appears you will see in the console that makeUIView(context:) is called twice while it is documented that it will only be called once when the view appears for the first time. So this seems like a bug. If you remove ManipulationComponent from the platter's components you will see that this problem goes away so it seems like that is the cause of the problem. Any insight here would be appreciated! Thank you. Here is what is printed in the console: Entity returned from EntityWrapper.makeEntity(context:) was already parented to another entity. This is not supported and may lead to unexpected behavior. SwiftUI adds entities to internally-managed entity hierarchies. Make UI View! This should be called once. Make UI View! This should be called once. Here is the app code: import SwiftUI @main struct SomeApp: App { var body: some Scene { WindowGroup { ContentView() } ImmersiveSpace(id: "TableTop") { TableTopPlatterView() } } } Here is the view code: import MetalKit import RealityKit import SwiftUI struct ContentView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show table top", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "TableTop") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct TableTopPlatterView: View { private var attachmentID: String { "RedCircle" } var body: some View { RealityView { content, attachments in if let redCircleEntity = attachments.entity(for: attachmentID) { // Lays the red circle in the platter. let rotation = Rotation3D(redCircleEntity.orientation) .rotated(by: .init(angle: .degrees(-90), axis: .x)) redCircleEntity.setOrientation(.init(rotation), relativeTo: nil) redCircleEntity.position.y = 0.026 platterEntity.addChild(redCircleEntity) content.add(platterEntity) } } placeholder: { ProgressView() } attachments: { Attachment(id: attachmentID) { MetalView() .clipShape(.circle) } } } /// The platter entity that the red circle lays on top of. private let platterEntity: ModelEntity = { let anchor = AnchorEntity( .plane( .horizontal, classification: .table, minimumBounds: [0.01, 0.01] ) ) let material = SimpleMaterial( color: .lightGray, roughness: 0.5, isMetallic: false ) let platter = ModelEntity( mesh: .generateCylinder(height: 0.05, radius: 0.475), materials: [material] ) platter.generateCollisionShapes(recursive: false) let components: [any Component] = [ InputTargetComponent(), GroundingShadowComponent(castsShadow: true), ManipulationComponent() // MARK: This is causing makeUIView to get called twice! ] platter.components.set(components) // Placed closer to the user when booted up. platter.position = [0, 1, -1.25] anchor.addChild(platter) return platter }() } // Metal view that renders a red square. struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { print("Make UI View! This should be called once.") let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var metalView: MetalView init(_ metalView: MetalView) { self.metalView = metalView } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = metalView.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } } }
Replies
3
Boosts
0
Views
1.2k
Activity
6d
TextComponent Renders Inverted Text on Mac Catalyst
On Mac Catalyst, RealityKit TextComponent renders vertically inverted text. The issue occurs with both ARView and RealityView. The same text renders correctly on iOS and in a native macOS RealityView application (not Catalyst). Tested on macOS 26 and macOS 27 beta, with Xcode 26 and Xcode 27 Beta. Here is a screenshot of a text entity with a text component rendering the string "Text Component": Workaround On Mac Catalyst: Detect when the model generated by TextComponent becomes available on the text entity. Find the generated UnlitMaterial in the ModelComponent of the text entity. Disable face culling on the material. Invert the Y scale of the text entity. Here is the result: Reproduce Below is the full code that reproduces the problem, with both ARView and RealityView, as well as the workaround implementation. import SwiftUI import RealityKit import Combine // MARK: Text func createTextEntity() -> Entity { let textEntity = Entity() let attributes: [NSAttributedString.Key: Any] = [ .font: MeshResource.Font.systemFont(ofSize: 48, weight: .bold), .foregroundColor: Material.Color.white ] let attributedText = AttributedString( NSAttributedString( string: "Text Component", attributes: attributes ) ) var textComponent = TextComponent() textComponent.text = attributedText textComponent.size = CGSize(width: 400, height: 200) textComponent.backgroundColor = Material.Color.darkGray.cgColor textEntity.components.set(textComponent) return textEntity } // MARK: Camera func createCameraEntity() -> Entity { let cameraEntity = Entity() cameraEntity.components.set(PerspectiveCameraComponent()) cameraEntity.look(at: .zero, from: [0, 0, 0.3], relativeTo: nil) return cameraEntity } // MARK: ARView struct CatalystTextBugARView: UIViewRepresentable { let applyWorkaround: Bool = true func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) arView.cameraMode = .nonAR arView.automaticallyConfigureSession = false arView.environment.background = .color(.black) let rootEntity = AnchorEntity() let textEntity = createTextEntity() #if targetEnvironment(macCatalyst) if applyWorkaround { /// React when the text entity's ModelComponent becomes available or changes context.coordinator.modelDidAddSubscription = arView.scene.subscribe( to: ComponentEvents.DidAdd.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } context.coordinator.modelDidChangeSubscription = arView.scene.subscribe( to: ComponentEvents.DidChange.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } } #endif rootEntity.addChild(textEntity) rootEntity.addChild(createCameraEntity()) arView.scene.addAnchor(rootEntity) return arView } func updateUIView(_ uiView: ARView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator { /// Retain the scene subscriptions for the lifetime of the ARView. var modelDidAddSubscription: (any Cancellable)? var modelDidChangeSubscription: (any Cancellable)? } } // MARK: RealityView struct CatalystTextBugRealityView: View { let applyWorkaround: Bool = true /// Retain the RealityView subscriptions for the lifetime of the view. @State private var modelDidAddSubscription: EventSubscription? @State private var modelDidChangeSubscription: EventSubscription? var body: some View { RealityView { content in let textEntity = createTextEntity() #if targetEnvironment(macCatalyst) if applyWorkaround { /// React when the text entity's ModelComponent becomes available or changes. modelDidAddSubscription = content.subscribe( to: ComponentEvents.DidAdd.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } modelDidChangeSubscription = content.subscribe( to: ComponentEvents.DidChange.self, on: textEntity, componentType: ModelComponent.self ) { _ in applyTextBugWorkaround(to: textEntity) } } #endif content.add(textEntity) content.add(createCameraEntity()) } .background(.black) } } // MARK: Workaround func applyTextBugWorkaround(to textEntity: Entity) { #if targetEnvironment(macCatalyst) guard var modelComponent = textEntity.components[ModelComponent.self] else { return } var foundTextMaterial = false var changedMaterial = false for materialIndex in modelComponent.materials.indices { guard var textMaterial = modelComponent.materials[materialIndex] as? UnlitMaterial else { continue } foundTextMaterial = true /// If face culling is already disabled, skip if case .none = textMaterial.faceCulling { continue } textMaterial.faceCulling = .none modelComponent.materials[materialIndex] = textMaterial changedMaterial = true } guard foundTextMaterial else { return } if changedMaterial { textEntity.components.set(modelComponent) } /// Counteract vertically inverted text. textEntity.scale.y = -abs(textEntity.scale.y) #endif } Usage Run either CatalystTextBugRealityView() or CatalystTextBugARView() on a Mac Catalyst target. Toggle applyWorkaround on either view to try with and without the workaround.
Replies
1
Boosts
0
Views
445
Activity
1w
Metal rendering application is not releasing resources
I am developing a metal based ray tracing rendering application (running heavy GPU kernels). I am sometimes "forcefully quitting" my application and I can see the application is not in the activity monitor. But I can see the windowserver is using %97 the GPU. The mac gets hotter and hotter. I kill the windowserver, re-login it is still the case. The only way to fix is to restart the mac. I have checked if there are any zombie processes, there are none. I am 3-4 month into Mac development (I used many rendering APIs e.g. before under Windows and Linux, they release the resources automatically unless the driver is very broken), but I believe when you force quit or exit gracefully, regarding application should release resources. I may be missing some knowledge. Does anybody have an idea? I had added every corner a graceful exit code but once the kernel has some infinite loop the clean up cannot happen. In Windows there are some driver reload mechanisms to recover when GPU is stuck, is there a similar system ?
Replies
1
Boosts
0
Views
1.4k
Activity
1w
macOS 27 beta: ProMotion refresh cadence is unstable, causing constant scroll judder
FB24091347 On macOS 27.0 beta (26A5388g), MacBook Pro M4 Pro, the built-in ProMotion display never settles on a stable refresh cadence. Scrolling in SwiftUI judders constantly. The same app binary was smooth on macOS 26, and is smooth on a 120 Hz ProMotion iPad. I captured two 60-second Instruments traces — same app, same scene, same scrolling, no external display — changing only the display's refresh-rate setting. On ProMotion the vsync interval standard deviation is 4.093 ms across six different cadences, mostly flip-flopping between 120 Hz and 60 Hz. Forced to a fixed 60 Hz it drops to 0.391 ms with a single cadence. The app presented an identical 59 fps median in both runs — frame production is perfectly steady, the display just holds each frame for an unpredictable length of time. That's what makes this nasty: it's invisible to every frame-rate metric, so it looks like the app got slow when nothing about the app changed. I spent most of a day profiling my own code before realising the app was never the problem. Workaround: force the built-in display to 60 Hz. Worth noting, because it complicates the picture: attaching a 60 Hz Studio Display makes the built-in smooth, but the Studio itself then judders — despite its own vsync cadence measuring perfectly stable. So refresh rate alone isn't the whole story, and there may be a second mechanism. The clean, reproducible, single-variable result is the ProMotion vs forced-60 Hz comparison on the built-in panel. If you can reproduce this on an M-series MacBook Pro on 27 beta, please file a duplicate referencing FB24091347.
Replies
0
Boosts
0
Views
343
Activity
1w
Using CARenderer for off-screen rendering of WKWebView results in a blank screen for the web page content on iOS 16 system version.
Using the CRenderer off-screen rendering method for WKWebView results in a blank screen for the web page content on the iOS 16 system version, but it can successfully obtain the web page content screen on the iOS 18 system version. I need a solution to achieve the display of web page content on the 16 system version, with a frame rate of more than 60 frames per second.
Replies
1
Boosts
0
Views
1.1k
Activity
1w
Draw WKWebView into OpenGL Texture
I'm trying to figure out how to display a Web Browser inside my iOS VR app (Obj-c, SceneKit and raw OpenGL), and the part i'm not fully understanding is how to get the WKWebView to draw it's content into a Pixel Buffer of some sort, so I can use the speed of CVOpenGLESTextureCacheCreateTextureFromImage to convert the pixel data into a OpenGl Texture quickly/efficently and display it on a floating surface.I'm already doing something simular with the video portion of my app, but it has a AVPlayerItemVideoOutput, which produced the pixel buffer, but I can't figure out how to massage the CALayer into a Buffer so I can convert it into a texture to then draw in opengl.I know it has something to do with drawLayer:(Layer) ,(Context), but searching online hasn't been very fruitful.And i'm not using SceneKit like you would assume, the app was built before GVR for Scenekit was a thing, so every part of VR is handled manually (scenekit to textures, textures to opengl for Left/Right eye distortion mesh).
Replies
3
Boosts
0
Views
2.4k
Activity
1w
Core Image kernel sampling broken in iOS27 DB4
I've noticed that my camera app is returning blank images on developer beta 4. After some investigation there is an issue with core image custom kernels where the texture sampler is returning NaN / 0 floats. I have a reproducible demo here: https://github.com/alexfoxy/ci-metal-shader-bug Feedback ticket here: https://feedbackassistant.apple.com/feedback/23895753
Replies
2
Boosts
3
Views
2.1k
Activity
2w
Residency set memory not freed if process performs no GPU operation
Feedback report: FB23959296 If a process creates a residency set, calls requestResidency, endResidency, and then releases the residency set without ever having done any GPU operations, the memory from the residency set is not freed. Workaround: if the application runs any GPU operation (even an operation not involving the residency set) at any point in its lifecycle (before/while/after creating/releasing the residency set), the memory is freed properly. This was observed in the context of an application that makes an AI model resident in GPU-accessible memory. If the user unloads the model without running any prompts, the memory is not freed. The model occupies ~16GB of RAM so a lot of memory is being leaked. Reproduction: Store the repro.m and workaround.m files from below Run the following commands (repro.m demonstrates the bug; workaround.m demonstrates the workaround): $ clang -framework Foundation -framework Metal -o repro repro.m $ ./repro Footprint at start: 0.00 GB Footprint after buffer allocation: 4.30 GB Footprint 5s after teardown: 4.30 GB $ clang -framework Foundation -framework Metal -o workaround workaround.m $ ./workaround Footprint at start: 0.00 GB Footprint after buffer allocation: 4.37 GB Footprint 5s after teardown: 0.01 GB Expected behavior: Footprint 5s after teardown should be ~0 GB, i.e., the memory is freed. Observed behavior: Footprint 5s after teardown is 4.30 GB, i.e., the memory is not freed. Versions: XCode: 26.6 (17F113) Clang: 21.0.0 (clang-2100.1.1.101, arm64-apple-darwin25.5.0) macOS: 26.5.2 (25F84) Files: repro.m: // Build: clang -framework Foundation -framework Metal -o repro repro.m #import <Metal/Metal.h> #include <mach/mach.h> // Returns the physical memory footprint of the process. static double footprint_gb(void) { task_vm_info_data_t info; mach_msg_type_number_t n = TASK_VM_INFO_COUNT; task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&info, &n); return (double)info.phys_footprint / 1e9; } int main(int argc, char ** argv) { printf("Footprint at start: %5.2f GB\n", footprint_gb()); @autoreleasepool { id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); // Allocate ~4GB of memory. const size_t size = 4ULL << 30; id<MTLBuffer> buf = [dev newBufferWithLength:size options:MTLResourceStorageModeShared]; memset(buf.contents, 0xab, size); // fault the pages in printf("Footprint after buffer allocation: %5.2f GB\n", footprint_gb()); MTLResidencySetDescriptor * desc = [[MTLResidencySetDescriptor alloc] init]; id<MTLResidencySet> rset = [dev newResidencySetWithDescriptor:desc error:nil]; [desc release]; [rset addAllocation:buf]; [rset commit]; [rset requestResidency]; [rset endResidency]; [rset removeAllAllocations]; [rset commit]; [rset release]; [buf release]; [dev release]; } sleep(5); printf("Footprint 5s after teardown: %5.2f GB\n", footprint_gb()); return 0; } workaround.m: // Build: clang -framework Foundation -framework Metal -o workaround workaround.m #import <Metal/Metal.h> #include <mach/mach.h> // Returns the physical memory footprint of the process. static double footprint_gb(void) { task_vm_info_data_t info; mach_msg_type_number_t n = TASK_VM_INFO_COUNT; task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&info, &n); return (double)info.phys_footprint / 1e9; } // Performing any work on the GPU ensures the memory from the residency set will be released. static void do_dummy_work(id<MTLDevice> dev, id<MTLCommandQueue> queue) { @autoreleasepool { id<MTLBuffer> tmp = [dev newBufferWithLength:1 options:MTLResourceStorageModeShared]; id<MTLCommandBuffer> cb = [queue commandBuffer]; id<MTLBlitCommandEncoder> enc = [cb blitCommandEncoder]; [enc fillBuffer:tmp range:NSMakeRange(0, 1) value:0]; [enc endEncoding]; [cb commit]; [tmp release]; } } int main(int argc, char ** argv) { printf("Footprint at start: %5.2f GB\n", footprint_gb()); @autoreleasepool { id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); id<MTLCommandQueue> queue = [dev newCommandQueue]; // Workaround that ensures the memory will be released. // It also works if we call this after the residency set release or at any point in between. do_dummy_work(dev, queue); // Allocate ~4GB of memory. const size_t size = 4ULL << 30; id<MTLBuffer> buf = [dev newBufferWithLength:size options:MTLResourceStorageModeShared]; memset(buf.contents, 0xab, size); // fault the pages in printf("Footprint after buffer allocation: %5.2f GB\n", footprint_gb()); MTLResidencySetDescriptor * desc = [[MTLResidencySetDescriptor alloc] init]; id<MTLResidencySet> rset = [dev newResidencySetWithDescriptor:desc error:nil]; [desc release]; [rset addAllocation:buf]; [rset commit]; [rset requestResidency]; [rset endResidency]; [rset removeAllAllocations]; [rset commit]; [rset release]; [buf release]; [queue release]; [dev release]; } sleep(5); printf("Footprint 5s after teardown: %5.2f GB\n", footprint_gb()); return 0; }
Replies
0
Boosts
0
Views
757
Activity
2w
WatchBlocks v1.5 is now available: a sandbox game built for Apple Watch
Hi everyone! After about 4-5 months of development, I released WatchBlocks v1.5 today. The project started as an experiment to answer a simple question: Could you build a real sandbox survival game that runs well on Apple Watch? It eventually grew into a cross-platform game supporting Apple Watch, iPhone, iPad, and Mac. Some of the technical challenges I enjoyed solving included: Optimizing rendering and gameplay for watchOS. Designing controls around the Digital Crown and the watch display. Building multiplayer across Apple platforms. Creating a content system that lets players make and share their own blocks, items, mobs, and biomes. The new update also transitions the game to a free-to-start model, making it easier for people to try it before deciding whether to unlock the full experience. I’d love to hear from other developers building for watchOS. It’s a platform that doesn’t get much attention for games, but I think there’s a lot of untapped potential. If anyone has questions about developing games for Apple Watch, I’m happy to answer them. App Store: https://apps.apple.com/us/app/watchblocks-craft-build/id6760209351
Replies
0
Boosts
0
Views
624
Activity
2w
Refresh Rate Drops from 144Hz to 98Hz After Monitor Power Cycle on macOS Golden Gate 27
Hello, I use a Gigabyte M32U monitor with my Mac mini. After updating to macOS Golden Gate 27 Release 3, I started experiencing an issue with my external display. Whenever I turn my monitor off and then turn it back on, the refresh rate automatically changes from 144Hz to 98Hz, and the 144Hz option disappears from the display settings. However, if I unplug the display cable and plug it back in, the monitor is detected again and the 144Hz option returns, allowing me to select it normally. This issue only started after updating to macOS Golden Gate 27 Release 3. Before the update, the monitor consistently worked at 144Hz without any problems. Could you please investigate this issue? Thank you.
Replies
1
Boosts
0
Views
652
Activity
2w