๐ iOS
SwiftUI์์ Metal ์ฌ์ฉํด๋ณด๊ธฐ
JerryiOS
2025. 1. 2. 19:42
Metal ๊ด๋ จ ์ฉ์ด
MTKView
MetalKit์์ ์ ๊ณตํ๋ ๋ทฐ๋ก Metal์ ์ฌ์ฉํ๋ ๊ทธ๋ํฝ ์ปจํ ์ธ ๋ฅผ ๊ทธ๋ฆฌ๋ ๋ฐ ์ฌ์ฉ. GPU์์ ์ํธ์์ฉ ๊ด๋ฆฌ
MTLDevice
GPU๋ฅผ ๋ํ๋ด๋ ๊ฐ์ฒด๋ก, Metal์์ ๊ทธ๋ํฝ ์์ ์ ์ํํ๋ ๋ฐ ํ์ํ ๋ค์ํ ๋ฆฌ์์ค ์์ฑ ๊ฐ๋ฅ
MTLCommandQueue
GPU์ ์ฐ์ฐ์ ์์ฒญํ๊ธฐ ์ํด ๋ช ๋ น์ ํ์ ์ถ๊ฐํ๋ ๊ฐ์ฒด. ๊ทธ๋ํฝ ๊ฒฝ๋ก์์ ์ํํ ์์ ์ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ.
MTLRenderPipelineState
๋ ๋๋ง ํ์ดํ๋ผ์ธ์ ์ํ. ์ ฐ์ด๋ ํ๋ก๊ทธ๋จ๊ณผ ๋ ๋๋ง ํน์ฑ์ ํฌํจํจ. ๊ทธ๋ํฝ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ์ ์.
MTLBuffer
GPU์ CPU ๊ฐ์ ๋ฐ์ดํฐ ์ ์ก์ ์ํ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก.
Shader
๊ทธ๋ํฝ ๋ ๋๋ง์ ํน์ ๋จ๊ณ๋ฅผ ์ฒ๋ฆฌํ๋ ํ๋ก๊ทธ๋จ.
์ ์ ์ ฐ์ด๋์ ํ๋๊ทธ๋จผํธ ์ ฐ์ด๋๊ฐ ์๋ค.
View ๋ง๋ค์ด๋ณด๊ธฐ
MetalView.swift
struct MetalView: UIViewRepresentable {
class Coordinator: NSObject, MTKViewDelegate {
var metalView: MetalView
init(metalView: MetalView) {
self.metalView = metalView
}
var device: MTLDevice!
var commandQueue: MTLCommandQueue!
var renderPipelineState: MTLRenderPipelineState!
var vertexBuffer: MTLBuffer!
let vertexData: [Float] = [
0.0, 1.0, 0.0, // Vertex 1 (X, Y)
-1.0, -1.0, 0.0, // Vertex 2 (X, Y)
1.0, -1.0, 0.0 // Vertex 3 (X, Y)
]
func setUpMetal(view: MTKView) {
device = MTLCreateSystemDefaultDevice()
view.device = device
commandQueue = device.makeCommandQueue()
// ์
ฐ์ด๋ ์ปดํ์ผ
let library = device.makeDefaultLibrary()
let vertexFunction = library?.makeFunction(name: "vertex_main")
let fragmentFunction = library?.makeFunction(name: "fragment_main")
// ํ์ดํ๋ผ์ธ ์ํ ์ค์
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat
renderPipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor)
// ์ ์ ๋ฒํผ ์์ฑ
vertexBuffer = device.makeBuffer(bytes: vertexData,
length: MemoryLayout<Float>.size * vertexData.count,
options: [])
}
func draw(in view: MTKView) {
guard let commandBuffer = commandQueue.makeCommandBuffer(),
let drawable = view.currentDrawable,
let descriptor = view.currentRenderPassDescriptor else { return }
let passEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)!
passEncoder.setRenderPipelineState(renderPipelineState)
passEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
passEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
passEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(metalView: self)
}
func makeUIView(context: UIViewRepresentableContext<MetalView>) -> MTKView {
let view = MTKView()
view.delegate = context.coordinator
context.coordinator.setUpMetal(view: view)
return view
}
func updateUIView(_ uiView: MTKView, context: UIViewRepresentableContext<MetalView>) {}
}
Shaders.metal
#include <metal_stdlib>
using namespace metal;
vertex float4 vertex_main(const device float *vertex_array [[buffer(0)]], uint id [[vertex_id]]) {
return float4(vertex_array[id * 3], vertex_array[id * 3 + 1], vertex_array[id * 3 + 2], 1.0);
}
fragment float4 fragment_main() {
return float4(1.0, 0.0, 0.0, 1.0);
}
๋ฐ์ํ