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.
This commit is contained in:
L'électron rare
2026-06-26 14:50:07 +02:00
parent b1e53da2bb
commit 48d019cc77
4 changed files with 27 additions and 19 deletions
@@ -22,7 +22,8 @@ enum SceneUniformBuilder {
if let body = skeletons.values.first {
func j(_ i: Int) -> SIMD3<Float>? {
(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
@@ -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) {}
}
@@ -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
@@ -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"))
}
}