diff --git a/Terrain2/Renderer.swift b/Terrain2/Renderer.swift index 2c43e84..98c4c3e 100644 --- a/Terrain2/Renderer.swift +++ b/Terrain2/Renderer.swift @@ -30,7 +30,6 @@ class Renderer: NSObject, MTKViewDelegate { public let device: MTLDevice let library: MTLLibrary let commandQueue: MTLCommandQueue - var dynamicUniformBuffer: MTLBuffer var pipelineState: MTLRenderPipelineState var normalPipelineState: MTLRenderPipelineState var depthState: MTLDepthStencilState @@ -38,9 +37,7 @@ class Renderer: NSObject, MTKViewDelegate { let inFlightSemaphore = DispatchSemaphore(value: maxBuffersInFlight) let regenerationSemaphore = DispatchSemaphore(value: 1) - var uniformBufferOffset = 0 - var uniformBufferIndex = 0 - var uniforms: UnsafeMutablePointer + var uniforms: PerFrameObject var lightsBuffer: MTLBuffer var lights: UnsafeMutablePointer @@ -71,14 +68,7 @@ class Renderer: NSObject, MTKViewDelegate { self.device = metalKitView.device! self.commandQueue = self.device.makeCommandQueue()! - let uniformBufferSize = alignedUniformsSize * maxBuffersInFlight - - self.dynamicUniformBuffer = self.device.makeBuffer(length:uniformBufferSize, - options:[MTLResourceOptions.storageModeShared])! - - self.dynamicUniformBuffer.label = "UniformBuffer" - - uniforms = UnsafeMutableRawPointer(dynamicUniformBuffer.contents()).bindMemory(to:Uniforms.self, capacity:1) + uniforms = PerFrameObject(device: device, label: "Uniforms") metalKitView.depthStencilPixelFormat = MTLPixelFormat.depth32Float_stencil8 metalKitView.colorPixelFormat = MTLPixelFormat.bgra8Unorm_srgb @@ -199,16 +189,6 @@ class Renderer: NSObject, MTKViewDelegate { material[0].specularExponent = 10 } - private func updateDynamicBufferState() { - /// Update the state of our uniform buffers before rendering - - uniformBufferIndex = (uniformBufferIndex + 1) % maxBuffersInFlight - - uniformBufferOffset = alignedUniformsSize * uniformBufferIndex - - uniforms = UnsafeMutableRawPointer(dynamicUniformBuffer.contents() + uniformBufferOffset).bindMemory(to:Uniforms.self, capacity:1) - } - private func updateGameState() { /// Update any game state before rendering if iterateTerrainAlgorithm { @@ -217,21 +197,21 @@ class Renderer: NSObject, MTKViewDelegate { } } - uniforms[0].projectionMatrix = projectionMatrix + uniforms.pointer[0].projectionMatrix = projectionMatrix let rotationAxis = float3(0, 1, 0) let modelMatrix = matrix4x4_rotation(radians: rotation, axis: rotationAxis) let viewMatrix = matrix4x4_translation(0.0, -2.0, -8.0) let modelViewMatrix = simd_mul(viewMatrix, modelMatrix) - uniforms[0].modelViewMatrix = modelViewMatrix + uniforms.pointer[0].modelViewMatrix = modelViewMatrix rotation += 0.003 // Remove the fourth row and column from our model-view matrix. Since we're only doing rotations and translations (no scales), this serves as our normal transform matrix. let rotSclModelViewMatrix = float3x3(modelViewMatrix.columns.0.xyz, modelViewMatrix.columns.1.xyz, modelViewMatrix.columns.2.xyz) - uniforms[0].normalMatrix = rotSclModelViewMatrix + uniforms.pointer[0].normalMatrix = rotSclModelViewMatrix - uniforms[0].terrainDimensions = terrain.dimensions - uniforms[0].terrainSegments = terrain.segments + uniforms.pointer[0].terrainDimensions = terrain.dimensions + uniforms.pointer[0].terrainSegments = terrain.segments } func draw(in view: MTKView) { @@ -256,7 +236,7 @@ class Renderer: NSObject, MTKViewDelegate { inFlightSem.signal() } - self.updateDynamicBufferState() + uniforms.updateOffsets() self.updateGameState() @@ -270,7 +250,7 @@ class Renderer: NSObject, MTKViewDelegate { didScheduleAlgorithmIteration = true } - terrain.scheduleGeometryUpdates(inCommandBuffer: commandBuffer, uniforms: dynamicUniformBuffer, uniformsOffset: uniformBufferOffset) + terrain.scheduleGeometryUpdates(inCommandBuffer: commandBuffer, uniforms: uniforms) /// Delay getting the currentRenderPassDescriptor until we absolutely need it to avoid /// holding onto the drawable and blocking the display pipeline any longer than necessary @@ -293,8 +273,8 @@ class Renderer: NSObject, MTKViewDelegate { renderEncoder.setTriangleFillMode(drawLines ? .lines : .fill) renderEncoder.setVertexBuffer(terrain.faceNormalsBuffer, offset: 0, index: BufferIndex.faceNormals.rawValue) - renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue) - renderEncoder.setFragmentBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue) + renderEncoder.setVertexBuffer(uniforms.buffer, offset: uniforms.offset, index: BufferIndex.uniforms.rawValue) + renderEncoder.setFragmentBuffer(uniforms.buffer, offset: uniforms.offset, index: BufferIndex.uniforms.rawValue) renderEncoder.setFragmentBuffer(lightsBuffer, offset: 0, index: BufferIndex.lights.rawValue) renderEncoder.setFragmentBuffer(materialBuffer, offset: 0, index: BufferIndex.materials.rawValue) @@ -366,7 +346,7 @@ class Renderer: NSObject, MTKViewDelegate { encoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: NormalBufferIndex.points.rawValue) encoder.setVertexBuffer(normalBuffer.buffer, offset: normalBuffer.offset, index: NormalBufferIndex.normals.rawValue) - encoder.setVertexBuffer(dynamicUniformBuffer, offset: uniformBufferOffset, index: NormalBufferIndex.uniforms.rawValue) + encoder.setVertexBuffer(uniforms.buffer, offset: uniforms.offset, index: NormalBufferIndex.uniforms.rawValue) encoder.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2, instanceCount: terrain.mesh.vertexCount) encoder.popDebugGroup() @@ -381,9 +361,34 @@ class Renderer: NSObject, MTKViewDelegate { encoder.setVertexBuffer(faceMidpointsBuffer, offset: 0, index: NormalBufferIndex.points.rawValue) encoder.setVertexBuffer(faceNormalsBuffer, offset: 0, index: NormalBufferIndex.normals.rawValue) - encoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: NormalBufferIndex.uniforms.rawValue) + encoder.setVertexBuffer(uniforms.buffer, offset: uniforms.offset, index: NormalBufferIndex.uniforms.rawValue) encoder.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2, instanceCount: instanceCount) encoder.popDebugGroup() } } + +struct PerFrameObject { + static var objectSize: Int { + return (MemoryLayout.size & ~0xFF) + 0x100 + } + + private(set) var offset: Int = 0 + private(set) var index: Int = 0 + private(set) var buffer: MTLBuffer + private(set) var pointer: UnsafeMutablePointer + + init(device: MTLDevice, label: String? = nil) { + let bufferSize = PerFrameObject.objectSize * maxBuffersInFlight + + buffer = device.makeBuffer(length: bufferSize, options: .storageModeShared)! + buffer.label = label + pointer = UnsafeMutableRawPointer(buffer.contents()).bindMemory(to: T.self, capacity: 1) + } + + mutating func updateOffsets() { + index = (index + 1) % maxBuffersInFlight + offset = PerFrameObject.objectSize * index + pointer = UnsafeMutableRawPointer(buffer.contents() + offset).bindMemory(to: T.self, capacity: 1) + } +} diff --git a/Terrain2/Terrain.swift b/Terrain2/Terrain.swift index 311d944..8f193f2 100644 --- a/Terrain2/Terrain.swift +++ b/Terrain2/Terrain.swift @@ -154,7 +154,7 @@ class Terrain: NSObject { return progress } - func scheduleGeometryUpdates(inCommandBuffer commandBuffer: MTLCommandBuffer, uniforms: MTLBuffer, uniformsOffset: Int) { + func scheduleGeometryUpdates(inCommandBuffer commandBuffer: MTLCommandBuffer, uniforms: PerFrameObject) { if let computeEncoder = commandBuffer.makeComputeCommandEncoder() { //print("Scheduling update geometry heights") computeEncoder.label = "Geometry Heights Encoder" @@ -165,7 +165,7 @@ class Terrain: NSObject { computeEncoder.setBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: GeneratorBufferIndex.meshPositions.rawValue) let texCoordBuffer = mesh.vertexBuffers[BufferIndex.meshGenerics.rawValue] computeEncoder.setBuffer(texCoordBuffer.buffer, offset: texCoordBuffer.offset, index: GeneratorBufferIndex.texCoords.rawValue) - computeEncoder.setBuffer(uniforms, offset: uniformsOffset, index: GeneratorBufferIndex.uniforms.rawValue) + computeEncoder.setBuffer(uniforms.buffer, offset: uniforms.offset, index: GeneratorBufferIndex.uniforms.rawValue) computeEncoder.dispatchThreads(MTLSize(width: Int(segments.x + 1), height: Int(segments.y + 1), depth: 1), threadsPerThreadgroup: MTLSize(width: 8, height: 8, depth: 1)) computeEncoder.popDebugGroup() computeEncoder.endEncoding()