From d7cb051fb787838a20b20decf55fad6864c6f6fd Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Thu, 12 Sep 2024 11:37:08 +1000 Subject: [PATCH] split core renderer stuff into metal subfolder --- Sources/Voxelotl/CMakeLists.txt | 8 + .../Renderer/Metal/BlendFuncExtension.swift | 87 ++++++++++ .../Renderer/Metal/ColorExtension.swift | 7 + .../Renderer/Metal/EnvironmentExtension.swift | 11 ++ .../Renderer/Metal/PipelineOptions.swift | 23 +++ .../Renderer/Metal/RendererMesh.swift | 18 ++ Sources/Voxelotl/Renderer/Metal/Shader.swift | 14 ++ Sources/Voxelotl/Renderer/Renderer.swift | 160 ------------------ Sources/Voxelotl/Renderer/RendererError.swift | 5 + 9 files changed, 173 insertions(+), 160 deletions(-) create mode 100644 Sources/Voxelotl/Renderer/Metal/BlendFuncExtension.swift create mode 100644 Sources/Voxelotl/Renderer/Metal/ColorExtension.swift create mode 100644 Sources/Voxelotl/Renderer/Metal/EnvironmentExtension.swift create mode 100644 Sources/Voxelotl/Renderer/Metal/PipelineOptions.swift create mode 100644 Sources/Voxelotl/Renderer/Metal/RendererMesh.swift create mode 100644 Sources/Voxelotl/Renderer/Metal/Shader.swift create mode 100644 Sources/Voxelotl/Renderer/RendererError.swift diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index 07878a5..8dfaf8f 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -49,6 +49,13 @@ add_executable(Voxelotl MACOSX_BUNDLE Renderer/BlendMode.swift Renderer/BlendFunc.swift Renderer/ChunkRenderer.swift + Renderer/Metal/BlendFuncExtension.swift + Renderer/Metal/ColorExtension.swift + Renderer/Metal/EnvironmentExtension.swift + Renderer/Metal/PipelineOptions.swift + Renderer/Metal/Shader.swift + Renderer/Metal/RendererMesh.swift + Renderer/RendererError.swift Renderer/Renderer.swift # Input wrappers @@ -144,4 +151,5 @@ source_group("Source Files\\Noise" REGULAR_EXPRESSION "Noise/") source_group("Source Files\\Math" REGULAR_EXPRESSION "Math/") source_group("Source Files\\Input" REGULAR_EXPRESSION "Input/") source_group("Source Files\\Renderer" REGULAR_EXPRESSION "Renderer/") +source_group("Source Files\\Renderer\\Metal" REGULAR_EXPRESSION "Renderer/Metal/") source_group("Source Files\\Generator" REGULAR_EXPRESSION "Generator/") diff --git a/Sources/Voxelotl/Renderer/Metal/BlendFuncExtension.swift b/Sources/Voxelotl/Renderer/Metal/BlendFuncExtension.swift new file mode 100644 index 0000000..7a5188a --- /dev/null +++ b/Sources/Voxelotl/Renderer/Metal/BlendFuncExtension.swift @@ -0,0 +1,87 @@ +import Metal + +internal extension BlendFunc { + func setBlend(colorAttachment: inout MTLRenderPipelineColorAttachmentDescriptor) { + switch self { + case .off: + colorAttachment.isBlendingEnabled = false + case .on(let srcFactor, let dstFactor, let equation): + colorAttachment.isBlendingEnabled = true + colorAttachment.rgbBlendOperation = .init(equation) + colorAttachment.alphaBlendOperation = .init(equation) + colorAttachment.sourceRGBBlendFactor = .init(srcFactor) + colorAttachment.sourceAlphaBlendFactor = .init(srcFactor) + colorAttachment.destinationRGBBlendFactor = .init(dstFactor) + colorAttachment.destinationAlphaBlendFactor = .init(dstFactor) + case .separate(let srcColor, let srcAlpha, let dstColor, let dstAlpha, let equColor, let equAlpha): + colorAttachment.isBlendingEnabled = true + colorAttachment.rgbBlendOperation = .init(equColor) + colorAttachment.alphaBlendOperation = .init(equAlpha) + colorAttachment.sourceRGBBlendFactor = .init(srcColor) + colorAttachment.sourceAlphaBlendFactor = .init(srcAlpha) + colorAttachment.destinationRGBBlendFactor = .init(dstColor) + colorAttachment.destinationAlphaBlendFactor = .init(dstAlpha) + } + } +} + +internal extension MTLBlendOperation { + init(_ equation: BlendFuncEquation) { + self = switch equation { + case .add: .add + case .subtract: .subtract + case .reverseSubtract: .reverseSubtract + case .min: .min + case .max: .max + } + } +} + +internal extension MTLBlendFactor { + init(_ source: BlendFuncSourceFactor) { + self = switch source { + case .zero: .zero + case .one: .one + case .srcColor: .sourceColor + case .oneMinusSrcColor: .oneMinusSourceColor + case .srcAlpha: .sourceAlpha + case .oneMinusSrcAlpha: .oneMinusSourceAlpha + case .dstColor: .destinationColor + case .oneMinusDstColor: .oneMinusDestinationColor + case .dstAlpha: .destinationAlpha + case .oneMinusDstAlpha: .oneMinusDestinationAlpha + case .srcAlphaSaturate: .sourceAlphaSaturated + /* + case .constantColor: .blendColor + case .oneMinusConstantColor: .oneMinusBlendColor + case .constantAlpha: .blendAlpha + case .oneMinusConstantAlpha: .oneMinusBlendAlpha + */ + case .src1Color: .source1Color + case .oneMinusSrc1Color: .oneMinusSource1Color + case .src1Alpha: .source1Alpha + case .oneMinusSrc1Alpha: .oneMinusSource1Alpha + } + } + + init(_ destination: BlendFuncDestinationFactor) { + self = switch destination { + case .zero: .zero + case .one: .one + case .srcColor: .sourceColor + case .oneMinusSrcColor: .oneMinusSourceColor + case .srcAlpha: .sourceAlpha + case .oneMinusSrcAlpha: .oneMinusSourceAlpha + case .dstColor: .destinationColor + case .oneMinusDstColor: .oneMinusDestinationColor + case .dstAlpha: .destinationAlpha + case .oneMinusDstAlpha: .oneMinusDestinationAlpha + /* + case .constantColor: .blendColor + case .oneMinusConstantColor: .oneMinusBlendColor + case .constantAlpha: .blendAlpha + case .oneMinusConstantAlpha: .oneMinusBlendAlpha + */ + } + } +} diff --git a/Sources/Voxelotl/Renderer/Metal/ColorExtension.swift b/Sources/Voxelotl/Renderer/Metal/ColorExtension.swift new file mode 100644 index 0000000..a08cac5 --- /dev/null +++ b/Sources/Voxelotl/Renderer/Metal/ColorExtension.swift @@ -0,0 +1,7 @@ +import Metal + +internal extension MTLClearColor { + init(_ color: Color) { + self.init(red: color.r, green: color.g, blue: color.b, alpha: color.a) + } +} diff --git a/Sources/Voxelotl/Renderer/Metal/EnvironmentExtension.swift b/Sources/Voxelotl/Renderer/Metal/EnvironmentExtension.swift new file mode 100644 index 0000000..1a5a877 --- /dev/null +++ b/Sources/Voxelotl/Renderer/Metal/EnvironmentExtension.swift @@ -0,0 +1,11 @@ +import Metal + +internal extension MTLCullMode { + init(_ face: Environment.Face) { + self = switch face { + case .none: .none + case .front: .front + case .back: .back + } + } +} diff --git a/Sources/Voxelotl/Renderer/Metal/PipelineOptions.swift b/Sources/Voxelotl/Renderer/Metal/PipelineOptions.swift new file mode 100644 index 0000000..089200c --- /dev/null +++ b/Sources/Voxelotl/Renderer/Metal/PipelineOptions.swift @@ -0,0 +1,23 @@ +import Metal + +internal struct PipelineOptions: Hashable { + let colorFormat: MTLPixelFormat, depthFormat: MTLPixelFormat + let shader: Shader + let blendFunc: BlendFunc +} + +internal extension PipelineOptions { + func createPipeline(_ device: MTLDevice) throws -> MTLRenderPipelineState { + let pipeDescription = MTLRenderPipelineDescriptor() + pipeDescription.vertexFunction = self.shader.vertexProgram + pipeDescription.fragmentFunction = self.shader.fragmentProgram + pipeDescription.colorAttachments[0].pixelFormat = self.colorFormat + self.blendFunc.setBlend(colorAttachment: &pipeDescription.colorAttachments[0]) + pipeDescription.depthAttachmentPixelFormat = self.depthFormat + do { + return try device.makeRenderPipelineState(descriptor: pipeDescription) + } catch { + throw RendererError.initFailure("Failed to create pipeline state: \(error.localizedDescription)") + } + } +} diff --git a/Sources/Voxelotl/Renderer/Metal/RendererMesh.swift b/Sources/Voxelotl/Renderer/Metal/RendererMesh.swift new file mode 100644 index 0000000..fc71c47 --- /dev/null +++ b/Sources/Voxelotl/Renderer/Metal/RendererMesh.swift @@ -0,0 +1,18 @@ +import Metal + +public struct RendererMesh: Hashable { + internal let _vertBuf: MTLBuffer, _idxBuf: MTLBuffer + public let numIndices: Int + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs._vertBuf.gpuAddress == rhs._vertBuf.gpuAddress && lhs._vertBuf.length == rhs._vertBuf.length && + lhs._vertBuf.gpuAddress == rhs._vertBuf.gpuAddress && lhs._vertBuf.length == rhs._vertBuf.length && + lhs.numIndices == rhs.numIndices + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(self._vertBuf.hash) + hasher.combine(self._idxBuf.hash) + hasher.combine(self.numIndices) + } +} diff --git a/Sources/Voxelotl/Renderer/Metal/Shader.swift b/Sources/Voxelotl/Renderer/Metal/Shader.swift new file mode 100644 index 0000000..d1e1ddf --- /dev/null +++ b/Sources/Voxelotl/Renderer/Metal/Shader.swift @@ -0,0 +1,14 @@ +import Metal + +internal struct Shader: Hashable { + let vertexProgram: (any MTLFunction)?, fragmentProgram: (any MTLFunction)? + + static func == (lhs: Shader, rhs: Shader) -> Bool { + lhs.vertexProgram?.hash == rhs.vertexProgram?.hash && lhs.fragmentProgram?.hash == rhs.fragmentProgram?.hash + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(self.vertexProgram?.hash ?? 0) + hasher.combine(self.fragmentProgram?.hash ?? 0) + } +} diff --git a/Sources/Voxelotl/Renderer/Renderer.swift b/Sources/Voxelotl/Renderer/Renderer.swift index c9f399d..28782a5 100644 --- a/Sources/Voxelotl/Renderer/Renderer.swift +++ b/Sources/Voxelotl/Renderer/Renderer.swift @@ -516,163 +516,3 @@ public class Renderer { instanceCount: numInstances) } } - -public struct RendererMesh: Hashable { - fileprivate let _vertBuf: MTLBuffer, _idxBuf: MTLBuffer - public let numIndices: Int - - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs._vertBuf.gpuAddress == rhs._vertBuf.gpuAddress && lhs._vertBuf.length == rhs._vertBuf.length && - lhs._vertBuf.gpuAddress == rhs._vertBuf.gpuAddress && lhs._vertBuf.length == rhs._vertBuf.length && - lhs.numIndices == rhs.numIndices - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(self._vertBuf.hash) - hasher.combine(self._idxBuf.hash) - hasher.combine(self.numIndices) - } -} - -fileprivate extension MTLClearColor { - init(_ color: Color) { - self.init(red: color.r, green: color.g, blue: color.b, alpha: color.a) - } -} - -fileprivate extension MTLCullMode { - init(_ face: Environment.Face) { - self = switch face { - case .none: .none - case .front: .front - case .back: .back - } - } -} - -fileprivate struct Shader: Hashable { - let vertexProgram: (any MTLFunction)?, fragmentProgram: (any MTLFunction)? - - static func == (lhs: Shader, rhs: Shader) -> Bool { - lhs.vertexProgram?.hash == rhs.vertexProgram?.hash && lhs.fragmentProgram?.hash == rhs.fragmentProgram?.hash - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(self.vertexProgram?.hash ?? 0) - hasher.combine(self.fragmentProgram?.hash ?? 0) - } -} - -fileprivate struct PipelineOptions: Hashable { - let colorFormat: MTLPixelFormat, depthFormat: MTLPixelFormat - let shader: Shader - let blendFunc: BlendFunc -} - -fileprivate extension PipelineOptions { - func createPipeline(_ device: MTLDevice) throws -> MTLRenderPipelineState { - let pipeDescription = MTLRenderPipelineDescriptor() - pipeDescription.vertexFunction = self.shader.vertexProgram - pipeDescription.fragmentFunction = self.shader.fragmentProgram - pipeDescription.colorAttachments[0].pixelFormat = self.colorFormat - self.blendFunc.setBlend(colorAttachment: &pipeDescription.colorAttachments[0]) - pipeDescription.depthAttachmentPixelFormat = self.depthFormat - do { - return try device.makeRenderPipelineState(descriptor: pipeDescription) - } catch { - throw RendererError.initFailure("Failed to create pipeline state: \(error.localizedDescription)") - } - } -} - -fileprivate extension BlendFunc { - func setBlend(colorAttachment: inout MTLRenderPipelineColorAttachmentDescriptor) { - switch self { - case .off: - colorAttachment.isBlendingEnabled = false - case .on(let srcFactor, let dstFactor, let equation): - colorAttachment.isBlendingEnabled = true - colorAttachment.rgbBlendOperation = .init(equation) - colorAttachment.alphaBlendOperation = .init(equation) - colorAttachment.sourceRGBBlendFactor = .init(srcFactor) - colorAttachment.sourceAlphaBlendFactor = .init(srcFactor) - colorAttachment.destinationRGBBlendFactor = .init(dstFactor) - colorAttachment.destinationAlphaBlendFactor = .init(dstFactor) - case .separate(let srcColor, let srcAlpha, let dstColor, let dstAlpha, let equColor, let equAlpha): - colorAttachment.isBlendingEnabled = true - colorAttachment.rgbBlendOperation = .init(equColor) - colorAttachment.alphaBlendOperation = .init(equAlpha) - colorAttachment.sourceRGBBlendFactor = .init(srcColor) - colorAttachment.sourceAlphaBlendFactor = .init(srcAlpha) - colorAttachment.destinationRGBBlendFactor = .init(dstColor) - colorAttachment.destinationAlphaBlendFactor = .init(dstAlpha) - } - } -} - -fileprivate extension MTLBlendOperation { - init(_ equation: BlendFuncEquation) { - self = switch equation { - case .add: .add - case .subtract: .subtract - case .reverseSubtract: .reverseSubtract - case .min: .min - case .max: .max - } - } -} - -fileprivate extension MTLBlendFactor { - init(_ source: BlendFuncSourceFactor) { - self = switch source { - case .zero: .zero - case .one: .one - case .srcColor: .sourceColor - case .oneMinusSrcColor: .oneMinusSourceColor - case .srcAlpha: .sourceAlpha - case .oneMinusSrcAlpha: .oneMinusSourceAlpha - case .dstColor: .destinationColor - case .oneMinusDstColor: .oneMinusDestinationColor - case .dstAlpha: .destinationAlpha - case .oneMinusDstAlpha: .oneMinusDestinationAlpha - case .srcAlphaSaturate: .sourceAlphaSaturated - /* - case .constantColor: .blendColor - case .oneMinusConstantColor: .oneMinusBlendColor - case .constantAlpha: .blendAlpha - case .oneMinusConstantAlpha: .oneMinusBlendAlpha - */ - case .src1Color: .source1Color - case .oneMinusSrc1Color: .oneMinusSource1Color - case .src1Alpha: .source1Alpha - case .oneMinusSrc1Alpha: .oneMinusSource1Alpha - } - } - - init(_ destination: BlendFuncDestinationFactor) { - self = switch destination { - case .zero: .zero - case .one: .one - case .srcColor: .sourceColor - case .oneMinusSrcColor: .oneMinusSourceColor - case .srcAlpha: .sourceAlpha - case .oneMinusSrcAlpha: .oneMinusSourceAlpha - case .dstColor: .destinationColor - case .oneMinusDstColor: .oneMinusDestinationColor - case .dstAlpha: .destinationAlpha - case .oneMinusDstAlpha: .oneMinusDestinationAlpha - /* - case .constantColor: .blendColor - case .oneMinusConstantColor: .oneMinusBlendColor - case .constantAlpha: .blendAlpha - case .oneMinusConstantAlpha: .oneMinusBlendAlpha - */ - } - } -} - -enum RendererError: Error { - case initFailure(_ message: String) - case loadFailure(_ message: String) - case drawFailure(_ message: String) -} diff --git a/Sources/Voxelotl/Renderer/RendererError.swift b/Sources/Voxelotl/Renderer/RendererError.swift new file mode 100644 index 0000000..9f38e6b --- /dev/null +++ b/Sources/Voxelotl/Renderer/RendererError.swift @@ -0,0 +1,5 @@ +enum RendererError: Error { + case initFailure(_ message: String) + case loadFailure(_ message: String) + case drawFailure(_ message: String) +}