1
Runt
2025-06-27 bf9e4680eb466bcb7c9cb1bef567252bb1f2bb7d
LiveProject/tool/MetalRenderer.swift
@@ -4,75 +4,35 @@
//  渲染工具
//  Created by 倪路朋 on 6/26/25.
//
import CoreVideo
import Metal
import MetalKit
class Renderer: NSObject, MTKViewDelegate {
    static var shared: Renderer?
class MetalRenderer: NSObject, MTKViewDelegate {
    var device: MTLDevice!
    var commandQueue: MTLCommandQueue!
    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 setup(view: MTKView) {
        self.device = view.device
        self.commandQueue = device.makeCommandQueue()
        // 初始化 texture、pipeline 等
    }
    func updateFrame(data: [UInt8], width: Int, height: Int) {
        currentData = data
        textureWidth = width
        textureHeight = height
    // ✅ 必须实现的方法 1:窗口大小改变时调用
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        // 可以留空或更新视图缩放、渲染区域等
    }
    // ✅ 必须实现的方法 2:每一帧绘制时调用
    func draw(in view: MTKView) {
        guard let drawable = view.currentDrawable,
              let descriptor = view.currentRenderPassDescriptor,
              let data = currentData else { return }
              let descriptor = view.currentRenderPassDescriptor 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 commandBuffer = commandQueue.makeCommandBuffer()!
        let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)!
        // 渲染逻辑,如绘制纹理
        encoder.endEncoding()
        commandBuffer.present(drawable)
        commandBuffer.commit()
        }
        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) {}
}