-
Notifications
You must be signed in to change notification settings - Fork 371
Expand file tree
/
Copy pathMetalRendering.swift
More file actions
126 lines (109 loc) · 6 KB
/
MetalRendering.swift
File metadata and controls
126 lines (109 loc) · 6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import Foundation
import Metal
// OpenGL uses a bottom-left origin while Metal uses a top-left origin.
public let standardImageVertices: [Float] = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0]
extension MTLCommandBuffer {
func clear(with color: Color, outputTexture: Texture) {
let renderPass = MTLRenderPassDescriptor()
renderPass.colorAttachments[0].texture = outputTexture.texture
renderPass.colorAttachments[0].clearColor = MTLClearColorMake(
Double(color.redComponent), Double(color.greenComponent), Double(color.blueComponent),
Double(color.alphaComponent))
renderPass.colorAttachments[0].storeAction = .store
renderPass.colorAttachments[0].loadAction = .clear
print("Clear color: \(renderPass.colorAttachments[0].clearColor)")
guard let renderEncoder = self.makeRenderCommandEncoder(descriptor: renderPass) else {
fatalError("Could not create render encoder")
}
// renderEncoder.setRenderPipelineState(sharedMetalRenderingDevice.passthroughRenderState)
// renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 0)
renderEncoder.endEncoding()
}
func copyTexture(from:Texture,fromSize:Size,to:Texture){
guard let blitEncoder = self.makeBlitCommandEncoder() else {
fatalError("Could not create render encoder")
}
blitEncoder.copy(from: from.texture, sourceSlice: 0, sourceLevel: 0, sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0), sourceSize: MTLSize(width: Int(round(fromSize.width)), height: Int(round(fromSize.height)), depth: 1), to: to.texture, destinationSlice: 0, destinationLevel: 0, destinationOrigin: .init(x: 0, y: 0, z: 0))
blitEncoder.endEncoding();
}
func renderQuad(
pipelineState: MTLRenderPipelineState, uniformSettings: ShaderUniformSettings? = nil,
inputTextures: [UInt: Texture], useNormalizedTextureCoordinates: Bool = true,
imageVertices: [Float] = standardImageVertices, outputTexture: Texture,
outputOrientation: ImageOrientation = .portrait
) {
let vertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(
bytes: imageVertices,
length: imageVertices.count * MemoryLayout<Float>.size,
options: [])!
vertexBuffer.label = "Vertices"
let renderPass = MTLRenderPassDescriptor()
renderPass.colorAttachments[0].texture = outputTexture.texture
renderPass.colorAttachments[0].clearColor = MTLClearColorMake(1, 0, 0, 1)
renderPass.colorAttachments[0].storeAction = .store
renderPass.colorAttachments[0].loadAction = .clear
guard let renderEncoder = self.makeRenderCommandEncoder(descriptor: renderPass) else {
fatalError("Could not create render encoder")
}
renderEncoder.setFrontFacing(.counterClockwise)
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
for textureIndex in 0..<inputTextures.count {
let currentTexture = inputTextures[UInt(textureIndex)]!
let inputTextureCoordinates = currentTexture.textureCoordinates(
for: outputOrientation, normalized: useNormalizedTextureCoordinates)
let textureBuffer = sharedMetalRenderingDevice.device.makeBuffer(
bytes: inputTextureCoordinates,
length: inputTextureCoordinates.count * MemoryLayout<Float>.size,
options: [])!
textureBuffer.label = "Texture Coordinates"
renderEncoder.setVertexBuffer(textureBuffer, offset: 0, index: 1 + textureIndex)
renderEncoder.setFragmentTexture(currentTexture.texture, index: textureIndex)
}
uniformSettings?.restoreShaderSettings(renderEncoder: renderEncoder)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
renderEncoder.endEncoding()
}
}
func generateRenderPipelineState(
device: MetalRenderingDevice, vertexFunctionName: String, fragmentFunctionName: String,
operationName: String
) -> (MTLRenderPipelineState, [String: (Int, MTLStructMember)], Int) {
guard let vertexFunction = device.shaderLibrary.makeFunction(name: vertexFunctionName) else {
fatalError("\(operationName): could not compile vertex function \(vertexFunctionName)")
}
guard let fragmentFunction = device.shaderLibrary.makeFunction(name: fragmentFunctionName)
else {
fatalError("\(operationName): could not compile fragment function \(fragmentFunctionName)")
}
let descriptor = MTLRenderPipelineDescriptor()
descriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm
descriptor.rasterSampleCount = 1
descriptor.vertexFunction = vertexFunction
descriptor.fragmentFunction = fragmentFunction
do {
var reflection: MTLAutoreleasedRenderPipelineReflection?
let pipelineState = try device.device.makeRenderPipelineState(
descriptor: descriptor, options: [.bufferTypeInfo, .argumentInfo],
reflection: &reflection)
var uniformLookupTable: [String: (Int, MTLStructMember)] = [:]
var bufferSize: Int = 0
if let fragmentArguments = reflection?.fragmentArguments {
for fragmentArgument in fragmentArguments where fragmentArgument.type == .buffer {
if fragmentArgument.bufferDataType == .struct,
let members = fragmentArgument.bufferStructType?.members.enumerated()
{
bufferSize = fragmentArgument.bufferDataSize
for (index, uniform) in members {
uniformLookupTable[uniform.name] = (index, uniform)
}
}
}
}
return (pipelineState, uniformLookupTable, bufferSize)
} catch {
fatalError(
"Could not create render pipeline state for vertex:\(vertexFunctionName), fragment:\(fragmentFunctionName), error:\(error)"
)
}
}