1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//
//  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
    private var rotate:Int = 0;
 
    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 orien:CGImagePropertyOrientation = .up;
        switch self.rotate{
        case 0:
            break;
        case 90:
            orien = .right;
            break;
        case 180:
            orien = .down;
            break;
        case 270:
            orien = .left;
            break;
        default:
            orien = .up;
        }
        print(" roate = \(rotate)")
        var ciImage = CIImage(cvPixelBuffer: pixelBuffer).oriented(orien)
 
        // 等比缩放后居中
        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")
        //刷新
        
    }
    
    func updateRotate(angle:Int){
        rotate = angle;
    }
    
}