From 48d019cc77bc8cc1fc6c2ee2293791d91cc4cc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:50:07 +0200 Subject: [PATCH] fix(avlivebody): apply phase 2 review fixes The phase 2 code review surfaced four follow-ups left after merge. The joint accessor indexed body.valid without checking its length, so a short valid array could crash on out-of-range access; it now guards i < body.valid.count. The viz mode name list was rebuilt on every modeName() call, so it is hoisted to a static constant. A Metal shader compile test is added to catch scene.metal regressions in CI. The dead SceneView placeholder, superseded by LayeredSceneView, is removed. --- .../AVLiveBody/SceneUniformBuilder.swift | 3 ++- .../Sources/AVLiveBody/SceneView.swift | 15 ------------- .../Sources/AVLiveBody/SettingsPanel.swift | 6 ++--- .../AVLiveBodyTests/ShaderCompileTests.swift | 22 +++++++++++++++++++ 4 files changed, 27 insertions(+), 19 deletions(-) delete mode 100644 avlivebody-mac/Sources/AVLiveBody/SceneView.swift create mode 100644 avlivebody-mac/Tests/AVLiveBodyTests/ShaderCompileTests.swift diff --git a/avlivebody-mac/Sources/AVLiveBody/SceneUniformBuilder.swift b/avlivebody-mac/Sources/AVLiveBody/SceneUniformBuilder.swift index 40a4b60..a293321 100644 --- a/avlivebody-mac/Sources/AVLiveBody/SceneUniformBuilder.swift +++ b/avlivebody-mac/Sources/AVLiveBody/SceneUniformBuilder.swift @@ -22,7 +22,8 @@ enum SceneUniformBuilder { if let body = skeletons.values.first { func j(_ i: Int) -> SIMD3? { - (i < body.joints.count && body.valid[i]) ? body.joints[i] : nil + (i < body.joints.count && i < body.valid.count + && body.valid[i]) ? body.joints[i] : nil } if let hips = j(hipsIdx) { u.body_x = hips.x; u.body_y = hips.y; u.body_z = hips.z diff --git a/avlivebody-mac/Sources/AVLiveBody/SceneView.swift b/avlivebody-mac/Sources/AVLiveBody/SceneView.swift deleted file mode 100644 index 66aaf5c..0000000 --- a/avlivebody-mac/Sources/AVLiveBody/SceneView.swift +++ /dev/null @@ -1,15 +0,0 @@ -import RealityKit -import SwiftUI - -/// SwiftUI bridge that hands the SceneController's ARView to the -/// window and runs `setUp()` once. -struct SceneView: NSViewRepresentable { - let controller: SceneController - - func makeNSView(context: Context) -> ARView { - controller.setUp() - return controller.arView - } - - func updateNSView(_ view: ARView, context: Context) {} -} diff --git a/avlivebody-mac/Sources/AVLiveBody/SettingsPanel.swift b/avlivebody-mac/Sources/AVLiveBody/SettingsPanel.swift index 73f1fdb..5ad80f8 100644 --- a/avlivebody-mac/Sources/AVLiveBody/SettingsPanel.swift +++ b/avlivebody-mac/Sources/AVLiveBody/SettingsPanel.swift @@ -54,10 +54,10 @@ struct SettingsPanel: View { .background(.ultraThinMaterial) } + private static let modeNames = ["storm","tunnel","plasma","kaleido", + "voronoi","metaballs","starfield","bars","hands3d","openpos"] static func modeName(_ i: Int) -> String { - let n = ["storm","tunnel","plasma","kaleido","voronoi", - "metaballs","starfield","bars","hands3d","openpos"] - return n.indices.contains(i) ? "\(i) \(n[i])" : "\(i)" + modeNames.indices.contains(i) ? "\(i) \(modeNames[i])" : "\(i)" } @ViewBuilder diff --git a/avlivebody-mac/Tests/AVLiveBodyTests/ShaderCompileTests.swift b/avlivebody-mac/Tests/AVLiveBodyTests/ShaderCompileTests.swift new file mode 100644 index 0000000..fe96a43 --- /dev/null +++ b/avlivebody-mac/Tests/AVLiveBodyTests/ShaderCompileTests.swift @@ -0,0 +1,22 @@ +// avlivebody-mac/Tests/AVLiveBodyTests/ShaderCompileTests.swift +import XCTest +import Metal +@testable import AVLiveBody + +final class ShaderCompileTests: XCTestCase { + func testSceneMetalCompiles() throws { + let here = URL(fileURLWithPath: #filePath) // .../Tests/AVLiveBodyTests/ShaderCompileTests.swift + let shader = here + .deletingLastPathComponent() // AVLiveBodyTests + .deletingLastPathComponent() // Tests + .deletingLastPathComponent() // avlivebody-mac + .appendingPathComponent("Sources/AVLiveBody/Resources/scene.metal") + let src = try String(contentsOf: shader, encoding: .utf8) + guard let dev = MTLCreateSystemDefaultDevice() else { + throw XCTSkip("no Metal device") + } + let lib = try dev.makeLibrary(source: src, options: MTLCompileOptions()) + XCTAssertNotNil(lib.makeFunction(name: "bg_vertex")) + XCTAssertNotNil(lib.makeFunction(name: "bg_fragment")) + } +}