|
@@ -96,35 +96,25 @@ private final class LNHWDMetalCoreRenderer {
|
|
|
|
|
|
|
|
private let device: MTLDevice
|
|
private let device: MTLDevice
|
|
|
private let commandQueue: MTLCommandQueue
|
|
private let commandQueue: MTLCommandQueue
|
|
|
- private let pipelineState: MTLRenderPipelineState
|
|
|
|
|
|
|
+ private var pipelineState: MTLRenderPipelineState?
|
|
|
private var textureCache: CVMetalTextureCache?
|
|
private var textureCache: CVMetalTextureCache?
|
|
|
private var vertexBuffer: MTLBuffer?
|
|
private var vertexBuffer: MTLBuffer?
|
|
|
private var yuvMatrixBuffer: MTLBuffer?
|
|
private var yuvMatrixBuffer: MTLBuffer?
|
|
|
private var currentColorMatrix = matrix601Full
|
|
private var currentColorMatrix = matrix601Full
|
|
|
|
|
+ // 与 ObjC QGHWDMetalRenderer 对齐:标记渲染资源是否已被回收,用于 reconstruct
|
|
|
|
|
+ private var renderingResourcesDisposed = false
|
|
|
|
|
+ private var currentBlendMode: LNTextureBlendMode
|
|
|
|
|
+ private weak var metalLayerRef: CAMetalLayer?
|
|
|
|
|
|
|
|
init?(metalLayer: CAMetalLayer, blendMode: LNTextureBlendMode) {
|
|
init?(metalLayer: CAMetalLayer, blendMode: LNTextureBlendMode) {
|
|
|
guard let device = MTLCreateSystemDefaultDevice(),
|
|
guard let device = MTLCreateSystemDefaultDevice(),
|
|
|
let queue = device.makeCommandQueue() else {
|
|
let queue = device.makeCommandQueue() else {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- let shaderLoader = LNVAPMetalShaderFunctionLoader(device: device)
|
|
|
|
|
- guard let vertexFunc = shaderLoader.loadFunction(withName: Self.kVertexFunctionName),
|
|
|
|
|
- let fragmentFunc = shaderLoader.loadFunction(withName: Self.kFragmentFunctionName) else {
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let desc = MTLRenderPipelineDescriptor()
|
|
|
|
|
- desc.vertexFunction = vertexFunc
|
|
|
|
|
- desc.fragmentFunction = fragmentFunc
|
|
|
|
|
- desc.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
|
|
|
|
|
- guard let pipelineState = try? device.makeRenderPipelineState(descriptor: desc) else {
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
self.device = device
|
|
self.device = device
|
|
|
self.commandQueue = queue
|
|
self.commandQueue = queue
|
|
|
- self.pipelineState = pipelineState
|
|
|
|
|
|
|
+ self.currentBlendMode = blendMode
|
|
|
|
|
+ self.metalLayerRef = metalLayer
|
|
|
|
|
|
|
|
metalLayer.device = device
|
|
metalLayer.device = device
|
|
|
metalLayer.framebufferOnly = true
|
|
metalLayer.framebufferOnly = true
|
|
@@ -132,19 +122,25 @@ private final class LNHWDMetalCoreRenderer {
|
|
|
let cacheStatus = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
|
|
let cacheStatus = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
|
|
|
guard cacheStatus == kCVReturnSuccess else { return nil }
|
|
guard cacheStatus == kCVReturnSuccess else { return nil }
|
|
|
|
|
|
|
|
|
|
+ setupPipelineState(metalLayer: metalLayer)
|
|
|
updateBlendMode(blendMode)
|
|
updateBlendMode(blendMode)
|
|
|
updateYuvMatrixBuffer(matrix: Self.matrix601Full)
|
|
updateYuvMatrixBuffer(matrix: Self.matrix601Full)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func updateBlendMode(_ blendMode: LNTextureBlendMode) {
|
|
func updateBlendMode(_ blendMode: LNTextureBlendMode) {
|
|
|
|
|
+ currentBlendMode = blendMode
|
|
|
let modeIndex = max(0, min(Int(blendMode.rawValue), Self.verticesByBlendMode.count - 1))
|
|
let modeIndex = max(0, min(Int(blendMode.rawValue), Self.verticesByBlendMode.count - 1))
|
|
|
let flat = Self.verticesByBlendMode[modeIndex].flatMap { $0 }
|
|
let flat = Self.verticesByBlendMode[modeIndex].flatMap { $0 }
|
|
|
vertexBuffer = device.makeBuffer(bytes: flat, length: flat.count * MemoryLayout<Float>.size, options: .storageModeShared)
|
|
vertexBuffer = device.makeBuffer(bytes: flat, length: flat.count * MemoryLayout<Float>.size, options: .storageModeShared)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func render(pixelBuffer: CVPixelBuffer, metalLayer: CAMetalLayer) {
|
|
func render(pixelBuffer: CVPixelBuffer, metalLayer: CAMetalLayer) {
|
|
|
|
|
+ // 与 ObjC QGHWDMetalRenderer.reconstructIfNeed 对齐:dispose 后自动重建渲染资源
|
|
|
|
|
+ reconstructIfNeeded(metalLayer: metalLayer)
|
|
|
|
|
+
|
|
|
guard metalLayer.bounds.width > 0,
|
|
guard metalLayer.bounds.width > 0,
|
|
|
metalLayer.bounds.height > 0,
|
|
metalLayer.bounds.height > 0,
|
|
|
|
|
+ let pipelineState,
|
|
|
let drawable = metalLayer.nextDrawable(),
|
|
let drawable = metalLayer.nextDrawable(),
|
|
|
let commandBuffer = commandQueue.makeCommandBuffer(),
|
|
let commandBuffer = commandQueue.makeCommandBuffer(),
|
|
|
let descriptor = currentRenderPassDescriptor(texture: drawable.texture),
|
|
let descriptor = currentRenderPassDescriptor(texture: drawable.texture),
|
|
@@ -157,6 +153,7 @@ private final class LNHWDMetalCoreRenderer {
|
|
|
updateYuvMatrixIfNeeded(pixelBuffer: pixelBuffer)
|
|
updateYuvMatrixIfNeeded(pixelBuffer: pixelBuffer)
|
|
|
guard let yTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 0, format: .r8Unorm),
|
|
guard let yTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 0, format: .r8Unorm),
|
|
|
let uvTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 1, format: .rg8Unorm) else {
|
|
let uvTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 1, format: .rg8Unorm) else {
|
|
|
|
|
+ encoder.endEncoding()
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -179,6 +176,33 @@ private final class LNHWDMetalCoreRenderer {
|
|
|
textureCache = nil
|
|
textureCache = nil
|
|
|
vertexBuffer = nil
|
|
vertexBuffer = nil
|
|
|
yuvMatrixBuffer = nil
|
|
yuvMatrixBuffer = nil
|
|
|
|
|
+ pipelineState = nil
|
|
|
|
|
+ renderingResourcesDisposed = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 与 ObjC reconstructIfNeed: 对齐:dispose 后下次渲染前自动重建资源
|
|
|
|
|
+ private func reconstructIfNeeded(metalLayer: CAMetalLayer) {
|
|
|
|
|
+ guard renderingResourcesDisposed else { return }
|
|
|
|
|
+ if textureCache == nil {
|
|
|
|
|
+ CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
|
|
|
|
|
+ }
|
|
|
|
|
+ setupPipelineState(metalLayer: metalLayer)
|
|
|
|
|
+ updateBlendMode(currentBlendMode)
|
|
|
|
|
+ updateYuvMatrixBuffer(matrix: currentColorMatrix)
|
|
|
|
|
+ renderingResourcesDisposed = false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func setupPipelineState(metalLayer: CAMetalLayer) {
|
|
|
|
|
+ let shaderLoader = LNVAPMetalShaderFunctionLoader(device: device)
|
|
|
|
|
+ guard let vertexFunc = shaderLoader.loadFunction(withName: Self.kVertexFunctionName),
|
|
|
|
|
+ let fragmentFunc = shaderLoader.loadFunction(withName: Self.kFragmentFunctionName) else {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ let desc = MTLRenderPipelineDescriptor()
|
|
|
|
|
+ desc.vertexFunction = vertexFunc
|
|
|
|
|
+ desc.fragmentFunction = fragmentFunc
|
|
|
|
|
+ desc.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
|
|
|
|
|
+ pipelineState = try? device.makeRenderPipelineState(descriptor: desc)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func makeTexture(pixelBuffer: CVPixelBuffer, plane: Int, format: MTLPixelFormat) -> MTLTexture? {
|
|
private func makeTexture(pixelBuffer: CVPixelBuffer, plane: Int, format: MTLPixelFormat) -> MTLTexture? {
|
|
@@ -262,11 +286,11 @@ private final class LNVAPMetalCoreRenderer {
|
|
|
private var maskBlurBuffer: MTLBuffer?
|
|
private var maskBlurBuffer: MTLBuffer?
|
|
|
private var maskTexture: MTLTexture?
|
|
private var maskTexture: MTLTexture?
|
|
|
private var currentColorMatrix = matrix601Full
|
|
private var currentColorMatrix = matrix601Full
|
|
|
|
|
+ // 与 ObjC QGVAPMetalRenderer 对齐:标记渲染资源是否已被回收,用于 reconstruct
|
|
|
|
|
+ private var renderingResourcesDisposed = false
|
|
|
|
|
|
|
|
var commonInfo: LNVAPCommonInfo? {
|
|
var commonInfo: LNVAPCommonInfo? {
|
|
|
- didSet {
|
|
|
|
|
- updateMainVertexBuffer()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ didSet { updateMainVertexBuffer() }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var maskInfo: LNVAPMaskInfo? {
|
|
var maskInfo: LNVAPMaskInfo? {
|
|
@@ -297,6 +321,9 @@ private final class LNVAPMetalCoreRenderer {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func render(pixelBuffer: CVPixelBuffer, metalLayer: CAMetalLayer, mergeInfos: [LNVAPMergedInfo]) {
|
|
func render(pixelBuffer: CVPixelBuffer, metalLayer: CAMetalLayer, mergeInfos: [LNVAPMergedInfo]) {
|
|
|
|
|
+ // 与 ObjC QGVAPMetalRenderer.reconstructIfNeed 对齐:dispose 后自动重建渲染资源
|
|
|
|
|
+ reconstructIfNeeded()
|
|
|
|
|
+
|
|
|
guard metalLayer.bounds.width > 0,
|
|
guard metalLayer.bounds.width > 0,
|
|
|
metalLayer.bounds.height > 0,
|
|
metalLayer.bounds.height > 0,
|
|
|
let drawable = metalLayer.nextDrawable(),
|
|
let drawable = metalLayer.nextDrawable(),
|
|
@@ -309,6 +336,7 @@ private final class LNVAPMetalCoreRenderer {
|
|
|
updateYuvMatrixIfNeeded(pixelBuffer: pixelBuffer)
|
|
updateYuvMatrixIfNeeded(pixelBuffer: pixelBuffer)
|
|
|
guard let yTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 0, format: .r8Unorm),
|
|
guard let yTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 0, format: .r8Unorm),
|
|
|
let uvTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 1, format: .rg8Unorm) else {
|
|
let uvTexture = makeTexture(pixelBuffer: pixelBuffer, plane: 1, format: .rg8Unorm) else {
|
|
|
|
|
+ encoder.endEncoding()
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -333,6 +361,19 @@ private final class LNVAPMetalCoreRenderer {
|
|
|
mainPipelineStateForMask = nil
|
|
mainPipelineStateForMask = nil
|
|
|
mainPipelineStateForMaskBlur = nil
|
|
mainPipelineStateForMaskBlur = nil
|
|
|
attachmentPipelineState = nil
|
|
attachmentPipelineState = nil
|
|
|
|
|
+ renderingResourcesDisposed = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 与 ObjC QGVAPMetalRenderer.reconstructIfNeed: 对齐:dispose 后下次渲染前自动重建资源
|
|
|
|
|
+ private func reconstructIfNeeded() {
|
|
|
|
|
+ guard renderingResourcesDisposed else { return }
|
|
|
|
|
+ if textureCache == nil {
|
|
|
|
|
+ CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
|
|
|
|
|
+ }
|
|
|
|
|
+ updateYuvMatrixBuffer(matrix: currentColorMatrix)
|
|
|
|
|
+ // 重建顶点缓冲(commonInfo/maskInfo 未变,直接重建)
|
|
|
|
|
+ updateMainVertexBuffer()
|
|
|
|
|
+ renderingResourcesDisposed = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func drawBackground(encoder: MTLRenderCommandEncoder, yTexture: MTLTexture, uvTexture: MTLTexture) {
|
|
private func drawBackground(encoder: MTLRenderCommandEncoder, yTexture: MTLTexture, uvTexture: MTLTexture) {
|
|
@@ -648,6 +689,8 @@ public final class LNHWDMetalView: UIView {
|
|
|
private let unavailableBridge = LNMetalUnavailableBridge()
|
|
private let unavailableBridge = LNMetalUnavailableBridge()
|
|
|
private var renderLayer: CALayer = CALayer()
|
|
private var renderLayer: CALayer = CALayer()
|
|
|
private lazy var fallbackRenderer = LNHWDMetalRenderer(metalLayer: renderLayer, blendMode: blendMode)
|
|
private lazy var fallbackRenderer = LNHWDMetalRenderer(metalLayer: renderLayer, blendMode: blendMode)
|
|
|
|
|
+ // 与 ObjC QGHWDMetalView 保持一致:视图尺寸或窗口变化后需要更新 drawableSize
|
|
|
|
|
+ private var drawableSizeShouldUpdate = true
|
|
|
|
|
|
|
|
@objc(initWithFrame:blendMode:)
|
|
@objc(initWithFrame:blendMode:)
|
|
|
public init(frame: CGRect, blendMode: LNTextureBlendMode) {
|
|
public init(frame: CGRect, blendMode: LNTextureBlendMode) {
|
|
@@ -684,10 +727,29 @@ public final class LNHWDMetalView: UIView {
|
|
|
public override func layoutSubviews() {
|
|
public override func layoutSubviews() {
|
|
|
super.layoutSubviews()
|
|
super.layoutSubviews()
|
|
|
renderLayer.frame = bounds
|
|
renderLayer.frame = bounds
|
|
|
|
|
+ drawableSizeShouldUpdate = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public override func didMoveToWindow() {
|
|
|
|
|
+ super.didMoveToWindow()
|
|
|
|
|
+ drawableSizeShouldUpdate = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@objc(display:)
|
|
@objc(display:)
|
|
|
public func display(_ pixelBuffer: CVPixelBuffer?) {
|
|
public func display(_ pixelBuffer: CVPixelBuffer?) {
|
|
|
|
|
+ // 与 ObjC QGHWDMetalView 对齐:每次渲染前检查是否需要更新 drawableSize,
|
|
|
|
|
+ // 避免视图尺寸变化后画面出现模糊或错位。
|
|
|
|
|
+ if drawableSizeShouldUpdate {
|
|
|
|
|
+ if #available(iOS 13.0, *), let metalLayer = renderLayer as? CAMetalLayer {
|
|
|
|
|
+ let nativeScale = UIScreen.main.nativeScale
|
|
|
|
|
+ metalLayer.drawableSize = CGSize(
|
|
|
|
|
+ width: bounds.width * nativeScale,
|
|
|
|
|
+ height: bounds.height * nativeScale
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ drawableSizeShouldUpdate = false
|
|
|
|
|
+ }
|
|
|
|
|
+ fallbackRenderer.blendMode = blendMode
|
|
|
fallbackRenderer.renderPixelBuffer(pixelBuffer, metalLayer: renderLayer)
|
|
fallbackRenderer.renderPixelBuffer(pixelBuffer, metalLayer: renderLayer)
|
|
|
}
|
|
}
|
|
|
|
|
|