๐ŸŽ 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);
}

 

๋ฐ˜์‘ํ˜•