From 3b7521a47ae731f0bf0a922822e4417493489539 Mon Sep 17 00:00:00 2001 From: Runt <qingingrunt2010@qq.com> Date: Wed, 09 Jul 2025 10:42:02 +0000 Subject: [PATCH] 小窗布局,小窗移动 --- LiveProject/tool/MetalRenderer.swift | 127 ++++++++++++++++++++++++++---------------- 1 files changed, 79 insertions(+), 48 deletions(-) diff --git a/LiveProject/tool/MetalRenderer.swift b/LiveProject/tool/MetalRenderer.swift index a2aa4ba..61bd769 100644 --- a/LiveProject/tool/MetalRenderer.swift +++ b/LiveProject/tool/MetalRenderer.swift @@ -4,68 +4,99 @@ // 渲染工具 // Created by 倪路朋 on 6/26/25. // +import Foundation +import Metal import MetalKit +import AVFoundation class MetalRenderer: NSObject, MTKViewDelegate { - private var device: MTLDevice! - private var commandQueue: MTLCommandQueue! - private var textureCache: CVMetalTextureCache! + private let device: MTLDevice + private let commandQueue: MTLCommandQueue + private let ciContext: CIContext + private var currentPixelBuffer: CVPixelBuffer? + private let textureCache: CVMetalTextureCache - func setup(view: MTKView) { - self.device = view.device - self.commandQueue = device.makeCommandQueue() - CVMetalTextureCacheCreate(nil, nil, device, nil, &textureCache) + 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 updateFrame(pixelBuffer: CVPixelBuffer) { - self.currentPixelBuffer = pixelBuffer - } - - func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {} func draw(in view: MTKView) { - guard let drawable = view.currentDrawable, - let descriptor = view.currentRenderPassDescriptor, - let pixelBuffer = currentPixelBuffer else { return } + let size = view.drawableSize + //print("🧾 drawableSize = \(size)") - var textureRef: CVMetalTexture? - let width = CVPixelBufferGetWidth(pixelBuffer) - let height = CVPixelBufferGetHeight(pixelBuffer) - - let status = CVMetalTextureCacheCreateTextureFromImage( - nil, textureCache, pixelBuffer, nil, - .bgra8Unorm, width, height, 0, &textureRef) - - guard status == kCVReturnSuccess, - let cvTexture = textureRef, - let texture = CVMetalTextureGetTexture(cvTexture) else { return } - - let commandBuffer = commandQueue.makeCommandBuffer()! - let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)! - encoder.setFragmentTexture(texture, index: 0) - encoder.endEncoding() - - // 简单拷贝(不做 shader 处理) - let blitEncoder = commandBuffer.makeBlitCommandEncoder()! - let dstTexture = drawable.texture - if dstTexture.width != texture.width || dstTexture.height != texture.height { - print("❌ 尺寸不一致,无法 blit:src = \(texture.width)x\(texture.height), dst = \(dstTexture.width)x\(dstTexture.height)") + if !size.width.isFinite || !size.height.isFinite || size.width <= 0 || size.height <= 0 { + print("❌ 非法尺寸,跳过渲染 \(size)") return } - blitEncoder.copy(from: texture, - sourceSlice: 0, - sourceLevel: 0, - sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0), - sourceSize: MTLSize(width: width, height: height, depth: 1), + 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, - destinationSlice: 0, - destinationLevel: 0, - destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0)) - blitEncoder.endEncoding() + commandBuffer: commandBuffer, + bounds: CGRect(origin: .zero, size: drawableSize), + colorSpace: CGColorSpaceCreateDeviceRGB()) commandBuffer.present(drawable) commandBuffer.commit() - print("绘制画面") + //print("绘制画面") + } + + func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { + // No-op: Handle size change if needed + } + + func display(pixelBuffer: CVPixelBuffer) { + self.currentPixelBuffer = pixelBuffer + //print("display") + //刷新 + } } -- Gitblit v1.9.1