//
|
// Renderer.swift
|
// LiveProject
|
// 渲染工具
|
// Created by 倪路朋 on 6/26/25.
|
//
|
import Metal
|
import MetalKit
|
|
class Renderer: NSObject, MTKViewDelegate {
|
static var shared: Renderer?
|
|
private var device: MTLDevice!
|
private var commandQueue: MTLCommandQueue!
|
private var pipelineState: MTLRenderPipelineState!
|
private var texture: MTLTexture?
|
private var textureDescriptor: MTLTextureDescriptor!
|
private var currentData: [UInt8]?
|
private var textureWidth = 0
|
private var textureHeight = 0
|
|
weak var mtkView: MTKView? {
|
didSet {
|
guard let view = mtkView else { return }
|
device = view.device
|
commandQueue = device.makeCommandQueue()
|
|
let library = device.makeDefaultLibrary()
|
let pipelineDesc = MTLRenderPipelineDescriptor()
|
pipelineDesc.vertexFunction = library?.makeFunction(name: "vertexShader")
|
pipelineDesc.fragmentFunction = library?.makeFunction(name: "fragmentShader")
|
pipelineDesc.colorAttachments[0].pixelFormat = view.colorPixelFormat
|
|
pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDesc)
|
|
Renderer.shared = self
|
}
|
}
|
|
func updateFrame(data: [UInt8], width: Int, height: Int) {
|
currentData = data
|
textureWidth = width
|
textureHeight = height
|
}
|
|
func draw(in view: MTKView) {
|
guard let drawable = view.currentDrawable,
|
let descriptor = view.currentRenderPassDescriptor,
|
let data = currentData else { return }
|
|
if texture == nil || texture?.width != textureWidth || texture?.height != textureHeight {
|
textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
|
pixelFormat: .rgba8Unorm,
|
width: textureWidth,
|
height: textureHeight,
|
mipmapped: false
|
)
|
textureDescriptor.usage = [.shaderRead, .shaderWrite]
|
texture = device.makeTexture(descriptor: textureDescriptor)
|
}
|
|
let region = MTLRegionMake2D(0, 0, textureWidth, textureHeight)
|
data.withUnsafeBytes { ptr in
|
texture?.replace(region: region, mipmapLevel: 0, withBytes: ptr.baseAddress!, bytesPerRow: textureWidth * 4)
|
}
|
|
let commandBuffer = commandQueue.makeCommandBuffer()
|
let encoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor)
|
encoder?.setRenderPipelineState(pipelineState)
|
encoder?.setFragmentTexture(texture, index: 0)
|
encoder?.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
|
encoder?.endEncoding()
|
commandBuffer?.present(drawable)
|
commandBuffer?.commit()
|
}
|
|
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
|
}
|