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
7 changes: 5 additions & 2 deletions Sources/DemoGame/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,11 @@
// Input (WASD) for the demo
InputSystem.shared.registerKeyboardEvents()

// bypass Post FX effects
bypassPostProcessing = true

// Disable SSAO
SSAOParams.shared.enabled = true
SSAOParams.shared.enabled = false
// Test Fast quality (8 samples, half-res)
SSAOParams.shared.quality = .high
}
Expand Down Expand Up @@ -219,7 +222,7 @@

// Step 1. Create and configure the window
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
contentRect: NSRect(x: 0, y: 0, width: 1920, height: 1080),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false
Expand Down
100 changes: 6 additions & 94 deletions Sources/UntoldEngine/Renderer/RenderPasses.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1266,99 +1266,6 @@ public enum RenderPasses {
textureResources.shadowMap, index: Int(lightPassShadowTextureIndex.rawValue)
)

// point lights
/*
if let pointLightBuffer = bufferResources.pointLightBuffer {

let headerSize = MemoryLayout<UInt32>.stride * 4
let MAX_POINT_LIGHTS = 1024

// Grab lights and cap to buffer capacity
let src = getPointLights()
let capped = min(src.count, MAX_POINT_LIGHTS)

// Write count at offset 0
pointLightBuffer.contents().storeBytes(of: UInt32(capped), toByteOffset: 0, as: UInt32.self)

// Copy the contiguous bytes of the array after the header
let dst = pointLightBuffer.contents().advanced(by: headerSize)

src.withUnsafeBytes{ raw in
let stride = MemoryLayout<PointLightUniform>.stride
let nBytes = capped * stride
dst.copyMemory(from: raw.baseAddress!, byteCount: nBytes)
}

renderEncoder.setFragmentBuffer(
pointLightBuffer, offset: 0, index: Int(lightPassPointLightsIndex.rawValue)
)
} else {
handleError(.bufferAllocationFailed, bufferResources.pointLightBuffer!.label!)
return
}

// spot light
if let spotLightBuffer = bufferResources.spotLightBuffer {

let headerSize = MemoryLayout<UInt32>.stride * 4
let MAX_POINT_LIGHTS = 1024

// Grab lights and cap to buffer capacity
let src = getSpotLights()
let capped = min(src.count, MAX_POINT_LIGHTS)

// Write count at offset 0
spotLightBuffer.contents().storeBytes(of: UInt32(capped), toByteOffset: 0, as: UInt32.self)

// Copy the contiguous bytes of the array after the header
let dst = spotLightBuffer.contents().advanced(by: headerSize)

src.withUnsafeBytes{ raw in
let stride = MemoryLayout<SpotLightUniform>.stride
let nBytes = capped * stride
dst.copyMemory(from: raw.baseAddress!, byteCount: nBytes)
}

renderEncoder.setFragmentBuffer(
spotLightBuffer, offset: 0, index: Int(lightPassSpotLightsIndex.rawValue)
)

} else {
handleError(.bufferAllocationFailed, bufferResources.spotLightBuffer!.label!)
return
}

// area light
if let areaLightBuffer = bufferResources.areaLightBuffer {

let headerSize = MemoryLayout<UInt32>.stride * 4
let MAX_POINT_LIGHTS = 1024

// Grab lights and cap to buffer capacity
let src = getAreaLights()
let capped = min(src.count, MAX_POINT_LIGHTS)

// Write count at offset 0
areaLightBuffer.contents().storeBytes(of: UInt32(capped), toByteOffset: 0, as: UInt32.self)

// Copy the contiguous bytes of the array after the header
let dst = areaLightBuffer.contents().advanced(by: headerSize)

src.withUnsafeBytes{ raw in
let stride = MemoryLayout<AreaLightUniform>.stride
let nBytes = capped * stride
dst.copyMemory(from: raw.baseAddress!, byteCount: nBytes)
}

renderEncoder.setFragmentBuffer(
areaLightBuffer, offset: 0, index: Int(lightPassAreaLightsIndex.rawValue)
)

} else {
handleError(.bufferAllocationFailed, bufferResources.areaLightBuffer!.label!)
return
}
*/
let MAX_POINT_LIGHTS = 1024
let headerSize = 16
// Point
Expand Down Expand Up @@ -1528,8 +1435,13 @@ public enum RenderPasses {
renderInfo.offscreenRenderPassDescriptor?.depthAttachment.texture, index: Int(prePassDepthTextureIndex.rawValue)
)
} else {
let postProcessColorTexture = renderInfo.postProcessRenderPassDescriptor?.colorAttachments[0].texture
let finalColorTexture = bypassPostProcessing
? (renderInfo.deferredRenderPassDescriptor?.colorAttachments[0].texture ?? postProcessColorTexture)
: postProcessColorTexture

renderEncoder.setFragmentTexture(
renderInfo.postProcessRenderPassDescriptor?.colorAttachments[0].texture,
finalColorTexture,
index: Int(prePassFinalTextureIndex.rawValue)
)

Expand Down
2 changes: 1 addition & 1 deletion Sources/UntoldEngine/Renderer/UntoldEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class UntoldRenderer: NSObject, MTKViewDelegate {
let renderer = UntoldRenderer(configuration: configuration)

guard let device = MTLCreateSystemDefaultDevice() else {
assertionFailure("Metal device is not available.")
Logger.logError(message: "Metal device is not available.")
return nil
}
renderer.metalView.device = device
Expand Down
26 changes: 24 additions & 2 deletions Sources/UntoldEngine/Systems/RenderingSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,32 @@ public func buildGameModeGraph() -> RenderGraphResult {
let gaussianPass = RenderPass(id: "gaussian", dependencies: ["model"], execute: RenderPasses.gaussianExecution)
graph[gaussianPass.id] = gaussianPass

let postProcess = postProcessingEffects(graph: &graph, deferredPassId: "lightPass", geometryPassId: "model")
let postProcessID: String
if bypassPostProcessing {
let bypassPass = RenderPass(
id: "postProcessBypass",
dependencies: ["lightPass"],
execute: { _ in
guard let deferredDescriptor = renderInfo.deferredRenderPassDescriptor else {
return
}
renderInfo.postProcessRenderPassDescriptor?.colorAttachments[0].texture =
deferredDescriptor.colorAttachments[0].texture
}
)
graph[bypassPass.id] = bypassPass
postProcessID = bypassPass.id
} else {
let postProcess = postProcessingEffects(graph: &graph, deferredPassId: "lightPass", geometryPassId: "model")
postProcessID = postProcess.id
}

// PreComposite depends on both post-processing and gaussian
let preCompPass = RenderPass(id: "precomp", dependencies: [postProcess.id, gaussianPass.id], execute: RenderPasses.preCompositeExecution)
let preCompPass = RenderPass(
id: "precomp",
dependencies: [postProcessID, gaussianPass.id],
execute: RenderPasses.preCompositeExecution
)
graph[preCompPass.id] = preCompPass

return (graph, preCompPass.id)
Expand Down
1 change: 1 addition & 0 deletions Sources/UntoldEngine/Utils/Globals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public let commandBufferSemaphore = DispatchSemaphore(value: maxInFlightCommandB

// Engine profiling/benchmarking
public var enableEngineMetrics: Bool = false
public var bypassPostProcessing = false

public class ToneMappingParams: ObservableObject {
static let shared = ToneMappingParams()
Expand Down
24 changes: 24 additions & 0 deletions Tests/UntoldEngineRenderTests/RenderGraphBuilderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,30 @@ final class RenderGraphBuilderTest: BaseRenderSetup {
])
}

func testBuildGameModeGraph_BypassPostProcessing_UsesBypassPass() {
renderInfo.immersionStyle = .none
renderEnvironment = true
bypassPostProcessing = true
defer { bypassPostProcessing = false }

let (graph, finalPassID) = buildGameModeGraph()

XCTAssertEqual(finalPassID, "precomp", "Final pass should be precomp")
XCTAssertNotNil(graph["postProcessBypass"], "Bypass pass should exist when bypassPostProcessing is enabled")
XCTAssertEqual(graph["postProcessBypass"]?.dependencies, ["lightPass"],
"Bypass pass should depend on lightPass")

XCTAssertNil(graph["depthOfField"], "Depth of field pass should not exist when bypassing post-processing")
XCTAssertNil(graph["chromatic"], "Chromatic pass should not exist when bypassing post-processing")
XCTAssertNil(graph["bloomThreshold"], "Bloom threshold pass should not exist when bypassing post-processing")

let precompDeps = graph["precomp"]?.dependencies.sorted() ?? []
XCTAssertTrue(precompDeps.contains("postProcessBypass"),
"Precomp should depend on postProcessBypass when bypassing post-processing")
XCTAssertTrue(precompDeps.contains("gaussian"),
"Precomp should still depend on gaussian pass")
}

// MARK: - Gaussian Pass Integration Tests

func testBuildGameModeGraph_GaussianPassExists() {
Expand Down
Loading