//
|
// Renderer.swift
|
// LiveProject
|
// 渲染工具
|
// Created by 倪路朋 on 6/26/25.
|
//
|
import Foundation
|
import Metal
|
import MetalKit
|
import AVFoundation
|
|
class MetalRenderer: NSObject, MTKViewDelegate {
|
private let device: MTLDevice
|
private let commandQueue: MTLCommandQueue
|
private let ciContext: CIContext
|
|
private var currentPixelBuffer: CVPixelBuffer?
|
private let textureCache: CVMetalTextureCache
|
|
init(mtkView: MTKView) {
|
guard let device = MTLCreateSystemDefaultDevice(),
|
let commandQueue = device.makeCommandQueue() else {
|
fatalError("Unable to create Metal device or command queue")
|
}
|
|
self.device = device
|
self.commandQueue = commandQueue
|
self.ciContext = CIContext(mtlDevice: device)
|
|
var tmpCache: CVMetalTextureCache?
|
CVMetalTextureCacheCreate(nil, nil, device, nil, &tmpCache)
|
guard let textureCache = tmpCache else {
|
fatalError("Unable to create texture cache")
|
}
|
self.textureCache = textureCache
|
|
super.init()
|
|
// ✅ 设置驱动渲染的关键代码
|
mtkView.device = device
|
mtkView.framebufferOnly = false
|
mtkView.isPaused = false
|
mtkView.enableSetNeedsDisplay = false
|
mtkView.delegate = self
|
print("MetalRenderer init")
|
}
|
|
func draw(in view: MTKView) {
|
let size = view.drawableSize
|
//print("🧾 drawableSize = \(size)")
|
|
if !size.width.isFinite || !size.height.isFinite || size.width <= 0 || size.height <= 0 {
|
print("❌ 非法尺寸,跳过渲染 \(size)")
|
return
|
}
|
guard let drawable = view.currentDrawable,
|
let commandBuffer = commandQueue.makeCommandBuffer(),
|
let pixelBuffer = currentPixelBuffer else {
|
return
|
}
|
|
let drawableSize = view.drawableSize
|
guard drawableSize.width > 0, drawableSize.height > 0 else { return }
|
|
// 加方向修正:顺时针旋转90度
|
var ciImage = CIImage(cvPixelBuffer: pixelBuffer).oriented(.right)
|
|
// 等比缩放后居中
|
let sourceExtent = ciImage.extent
|
let scaleX = drawableSize.width / sourceExtent.width
|
let scaleY = drawableSize.height / sourceExtent.height
|
let scale = min(scaleX, scaleY)
|
|
let scaledImage = ciImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
|
|
let xOffset = (drawableSize.width - scaledImage.extent.width) / 2
|
let yOffset = (drawableSize.height - scaledImage.extent.height) / 2
|
let translatedImage = scaledImage.transformed(by: CGAffineTransform(translationX: xOffset, y: yOffset))
|
|
// 渲染
|
ciContext.render(translatedImage,
|
to: drawable.texture,
|
commandBuffer: commandBuffer,
|
bounds: CGRect(origin: .zero, size: drawableSize),
|
colorSpace: CGColorSpaceCreateDeviceRGB())
|
|
commandBuffer.present(drawable)
|
commandBuffer.commit()
|
//print("绘制画面")
|
}
|
|
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
|
// No-op: Handle size change if needed
|
}
|
|
func display(pixelBuffer: CVPixelBuffer) {
|
self.currentPixelBuffer = pixelBuffer
|
//print("display")
|
//刷新
|
|
}
|
}
|