Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

import Foundation

final class FrameMetricsCollector {
/// All mutable state is guarded by `lock`, so the class is safe to share across threads.
final class FrameMetricsCollector: @unchecked Sendable {
private let capacity: Int
private var samples: [Double]
private var writeIndex: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ final class AssetLoadingGateRenderingTests: BaseRenderSetup {
}
}

/// Mirrors the `if !loading { ... }` gate in `executeXRSystemPass`. Routed through a
/// function (rather than inlining `!loading` on a local literal) so the compiler can't
/// prove either branch unreachable and flag it as dead code.
private func xrSystemPassRenderPrepWouldExecute(loading: Bool) -> Bool {
!loading
}

// MARK: - Test 1: Verify render prep is skipped when AssetLoadingGate.shared.isLoadingAny is true

func testUpdateRenderingSystem_SkipsRenderPrepWhenLoadingGateIsActive() {
Expand Down Expand Up @@ -143,10 +150,7 @@ final class AssetLoadingGateRenderingTests: BaseRenderSetup {
// This verifies the condition that guards render prep execution
// The actual functions (performFrustumCulling, executeGaussianDepth, executeBitonicSort)
// should NOT be called when loading is true
var renderPrepExecuted = false
if !loading {
renderPrepExecuted = true
}
let renderPrepExecuted = xrSystemPassRenderPrepWouldExecute(loading: loading)
XCTAssertFalse(renderPrepExecuted, "Render prep should not execute when loading is true")
}

Expand Down Expand Up @@ -175,7 +179,7 @@ final class AssetLoadingGateRenderingTests: BaseRenderSetup {

// When: We apply the executeXRSystemPass logic:
// `if !loading { visibleEntityIds = tripleVisibleEntities.snapshotForRead(frame: cullFrameIndex) }`
if !loading {
if xrSystemPassRenderPrepWouldExecute(loading: loading) {
visibleEntityIds = tripleVisibleEntities.snapshotForRead(frame: cullFrameIndex)
}

Expand Down Expand Up @@ -302,7 +306,7 @@ final class AssetLoadingGateRenderingTests: BaseRenderSetup {

// During loading: visibleEntityIds should NOT update
var loading = AssetLoadingGate.shared.isLoadingAny
var previousVisibleIds = visibleEntityIds
let previousVisibleIds = visibleEntityIds
if !loading {
visibleEntityIds = tripleVisibleEntities.snapshotForRead(frame: cullFrameIndex)
}
Expand Down
31 changes: 2 additions & 29 deletions Tests/UntoldEngineRenderTests/AsyncMeshLoadingTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,12 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {
// When: Start loading and track progress
let loadingCompleteExpectation = XCTestExpectation(description: "Loading completed")

// Track that loading was started at some point during the async operation
var loadingWasStarted = false

// Poll rapidly to catch loading state (loading may complete very quickly)
let monitorTask = Task {
for _ in 0 ..< 500 { // Check for up to 0.5 seconds
if Task.isCancelled { break }
let isLoading = await AssetLoadingState.shared.isLoading(entityId: entityId)
if isLoading {
loadingWasStarted = true
break
}
try? await Task.sleep(nanoseconds: 1_000_000) // 1ms
Expand All @@ -243,9 +239,6 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {

let progressAfter = await AssetLoadingState.shared.getProgress(for: entityId)
XCTAssertNil(progressAfter, "Progress should be cleared after completion")

// Note: loadingWasStarted may be false if loading completed before monitoring could detect it
// This is expected behavior with very fast loading - the important thing is that completion works
}

func testAsyncLoading_tracksMultipleEntitiesSimultaneously() async {
Expand Down Expand Up @@ -297,7 +290,6 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {
setEntityName(entityId: entityId, name: "PhaseEntity")

var loadingPhaseDetected = false
var registeringPhaseDetected = false

// When: Load and monitor phase transitions with rapid polling
let monitorTask = Task {
Expand All @@ -308,7 +300,7 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {
case .loading:
loadingPhaseDetected = true
case .registering:
registeringPhaseDetected = true
break
}
}
try? await Task.sleep(nanoseconds: 1_000_000) // 1ms
Expand Down Expand Up @@ -400,27 +392,10 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {
let entityId = createEntity()
setEntityName(entityId: entityId, name: "VisibilityEntity")

var wasHiddenDuringLoad = false

// When: Monitor visibility during loading
let monitorTask = Task {
for _ in 0 ..< 50 {
if Task.isCancelled { break }
if let renderComp = scene.get(component: RenderComponent.self, for: entityId) {
if !renderComp.isVisible {
wasHiddenDuringLoad = true
}
}
// Also check children
if let sceneGraph = scene.get(component: ScenegraphComponent.self, for: entityId) {
for childId in sceneGraph.children {
if let childRenderComp = scene.get(component: RenderComponent.self, for: childId) {
if !childRenderComp.isVisible {
wasHiddenDuringLoad = true
}
}
}
}
try? await Task.sleep(nanoseconds: 50_000_000) // 0.05s
}
}
Expand Down Expand Up @@ -582,7 +557,6 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {

// When: Load with asset name filter
let expectation = XCTestExpectation(description: "Filtered load")
var loadSuccess = false

// Note: This requires knowing the internal asset name structure of stadium.usdz
// For this test, we'll use a generic approach
Expand All @@ -591,8 +565,7 @@ final class AsyncMeshLoadingTest: BaseRenderSetup {
filename: "stadium",
withExtension: "untold",
assetName: "stadium" // Using the file name as asset name
) { success in
loadSuccess = success
) { _ in
expectation.fulfill()
}

Expand Down
16 changes: 8 additions & 8 deletions Tests/UntoldEngineRenderTests/DeviceRadixSortTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
else { XCTFail("Buffer allocation failed"); return }

zeroBuffer(histBuf)
var numElems = UInt32(n)
let numElems = UInt32(n)
var passIndex = UInt32(0)

dispatch(pipeline, threadCount: n) { enc in
Expand Down Expand Up @@ -262,7 +262,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
else { XCTFail("Buffer allocation failed"); return }

zeroBuffer(histBuf)
var numElems = UInt32(kRadixBuckets)
let numElems = UInt32(kRadixBuckets)
var passIndex = UInt32(0)

dispatch(pipeline, threadCount: kRadixBuckets) { enc in
Expand Down Expand Up @@ -295,7 +295,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
else { XCTFail("Buffer allocation failed"); return }

zeroBuffer(histBuf)
var numElems = UInt32(1)
let numElems = UInt32(1)
var passIndex = UInt32(0)

dispatch(pipeline, threadCount: 1) { enc in
Expand Down Expand Up @@ -331,7 +331,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
let histBuf = makeBuffer(count: kRadixBuckets, type: UInt32.self)
else { XCTFail("Buffer allocation failed"); return }

var numElems = UInt32(n)
let numElems = UInt32(n)

for pass in 0 ..< 4 {
zeroBuffer(histBuf)
Expand Down Expand Up @@ -366,7 +366,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
let histBuf = makeBuffer(count: kRadixBuckets, type: UInt32.self)
else { XCTFail("Buffer allocation failed"); return }

var numElems = UInt32(n)
let numElems = UInt32(n)

for pass in 0 ..< 4 {
zeroBuffer(histBuf)
Expand Down Expand Up @@ -580,7 +580,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
else { XCTFail("Buffer allocation failed"); return }

zeroBuffer(zeroBuf)
var numElems = UInt32(n)
let numElems = UInt32(n)
var passIndex = UInt32(0)

dispatch(pipeline, threadCount: n) { enc in
Expand Down Expand Up @@ -626,7 +626,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
else { XCTFail("Buffer allocation failed"); return }

zeroBuffer(zeroBuf)
var numElems = UInt32(n)
let numElems = UInt32(n)
var passIndex = UInt32(0)

dispatch(pipeline, threadCount: n) { enc in
Expand Down Expand Up @@ -674,7 +674,7 @@ final class DeviceRadixSortTest: BaseRenderSetup {
else { XCTFail("Buffer allocation failed"); return }

zeroBuffer(zeroBuf)
var numElems = UInt32(n)
let numElems = UInt32(n)
var passIndex = UInt32(0)

dispatch(pipeline, threadCount: n) { enc in
Expand Down
4 changes: 2 additions & 2 deletions Tests/UntoldEngineRenderTests/EmissiveShadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ final class EmissiveShadingTests: MaterialShadingTestCase {
let without = MDLMaterial(name: "wall", scatteringFunction: MDLPhysicallyPlausibleScatteringFunction())

let textureLoader = TextureLoader(device: renderInfo.device)
let sign = Material(mdlMaterial: withTexture, textureLoader: textureLoader, name: "sign")
let wall = Material(mdlMaterial: without, textureLoader: textureLoader, name: "wall")
let sign = Material(mdlMaterial: withTexture, textureLoader: textureLoader)
let wall = Material(mdlMaterial: without, textureLoader: textureLoader)

XCTAssertTrue(sign.hasEmissiveMap)
XCTAssertEqual(sign.emissiveValue, simd_float3(1, 1, 1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ final class EnvironmentAssetRebakeTests: BaseRenderSetup {

private var customDirectory: URL?

override func tearDown() {
override func tearDown() async throws {
if let customDirectory {
try? FileManager.default.removeItem(at: customDirectory)
}
super.tearDown()
try await super.tearDown()
}

func testViewportRebakeKeepsEnvironmentLoadedFromCustomDirectory() throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3495,7 +3495,7 @@ final class RenderGraphBuilderTest: BaseRenderSetup {
func assertTopologicalConstraints(
order: [String],
constraints: [(before: String, after: String)],
file: StaticString = #file,
file: StaticString = #filePath,
line: UInt = #line
) {
for (before, after) in constraints {
Expand Down
1 change: 0 additions & 1 deletion Tests/UntoldEngineRenderTests/SceneSerializerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ final class SceneSerializerTests: BaseRenderSetup {
XCTAssertEqual(getEntityName(entityId: children[2]), "new_child_name")

let originalSceneData = serializeScene()
let serializedAuthoredCount = originalSceneData.entities.count

let tempDir = FileManager.default.temporaryDirectory
let sceneURL = tempDir.appendingPathComponent("base_scene_roundtrip.json")
Expand Down
4 changes: 2 additions & 2 deletions Tests/UntoldEngineRenderTests/StaticBatchingTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ final class StaticBatchingTest: BaseRenderSetup {

func testGenerateBatchesWithNoStaticEntities() {
// Given: No entities marked as static
let entity1 = createEntity()
let entity2 = createEntity()
_ = createEntity()
_ = createEntity()

// When: Generate batches
generateBatches()
Expand Down
30 changes: 15 additions & 15 deletions Tests/UntoldEngineRenderTests/TransparencyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaMode", semantic: .userDefined, string: "blend"))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .blend, "Explicit 'blend' string should decode to .blend")
}
Expand All @@ -41,7 +41,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaMode", semantic: .userDefined, string: "mask"))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .mask, "Explicit 'mask' string should decode to .mask")
}
Expand All @@ -51,7 +51,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaMode", semantic: .userDefined, string: "opaque"))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .opaque, "Explicit 'opaque' string should decode to .opaque")
}
Expand All @@ -61,7 +61,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "transparencyMode", semantic: .userDefined, string: "transparent"))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .blend, "transparencyMode='transparent' should decode to .blend")
}
Expand All @@ -71,7 +71,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaMode", semantic: .userDefined, string: "cutout"))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .mask, "Explicit 'cutout' string should decode to .mask")
}
Expand All @@ -89,9 +89,9 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {

let textureLoader = TextureLoader(device: renderInfo.device)

let matOpaque = Material(mdlMaterial: mdlMaterialOpaque, textureLoader: textureLoader, name: "test")
let matMask = Material(mdlMaterial: mdlMaterialMask, textureLoader: textureLoader, name: "test")
let matBlend = Material(mdlMaterial: mdlMaterialBlend, textureLoader: textureLoader, name: "test")
let matOpaque = Material(mdlMaterial: mdlMaterialOpaque, textureLoader: textureLoader)
let matMask = Material(mdlMaterial: mdlMaterialMask, textureLoader: textureLoader)
let matBlend = Material(mdlMaterial: mdlMaterialBlend, textureLoader: textureLoader)

XCTAssertEqual(matOpaque.alphaMode, .opaque, "Scalar 0.0 should decode to .opaque")
XCTAssertEqual(matMask.alphaMode, .mask, "Scalar 1.0 should decode to .mask")
Expand All @@ -107,7 +107,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "baseColor", semantic: .baseColor, color: color))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .blend,
"Base color with alpha < 1.0 should infer .blend when no explicit alphaMode is set")
Expand All @@ -118,7 +118,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "opacity", semantic: .opacity, float: 0.7))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .blend,
"Opacity < 1.0 should infer .blend when no explicit alphaMode is set")
Expand All @@ -130,7 +130,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
// Base color without explicit alpha (float3 path returns hasExplicitAlpha=false)

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaMode, .opaque,
"Material with no transparency signals should default to .opaque")
Expand All @@ -144,7 +144,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaCutoff", semantic: .userDefined, float: 2.5))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaCutoff, 1.0, accuracy: 0.0001,
"alphaCutoff above 1.0 should be clamped to 1.0")
Expand All @@ -156,7 +156,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaCutoff", semantic: .userDefined, float: -0.5))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaCutoff, 0.0, accuracy: 0.0001,
"alphaCutoff below 0.0 should be clamped to 0.0")
Expand All @@ -168,7 +168,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
mdlMaterial.setProperty(MDLMaterialProperty(name: "alphaCutoff", semantic: .userDefined, float: 0.3))

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaCutoff, 0.3, accuracy: 0.0001,
"alphaCutoff within [0,1] should be preserved as-is")
Expand All @@ -180,7 +180,7 @@ final class MaterialAlphaDecodingTests: BaseRenderSetup {
// No alphaCutoff property

let textureLoader = TextureLoader(device: renderInfo.device)
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader, name: "test")
let material = Material(mdlMaterial: mdlMaterial, textureLoader: textureLoader)

XCTAssertEqual(material.alphaCutoff, 0.5, accuracy: 0.0001,
"alphaCutoff should default to 0.5 when not explicitly provided")
Expand Down
Loading
Loading