Article / 2026/05/29

MTKView API 完整指南

MTKView 是 Apple 提供的 Metal 框架中的视图类,用于简化 Metal 渲染的集成。它继承自 UIView (iOS) 或 NSView (macOS),为 Metal 渲染提供了便捷的接口。

🚀 MTKView API 完整指南

MTKView 是 Apple 提供的 Metal 框架中的视图类,用于简化 Metal 渲染的集成。它继承自 UIView (iOS) 或 NSView (macOS),为 Metal 渲染提供了便捷的接口。

目录

基本属性

device

@NSManaged public var device: MTLDevice?
  • 用途: 指定用于渲染的 Metal 设备
  • 说明: 必须设置此属性才能进行 Metal 渲染
  • 示例:
mtkView.device = MTLCreateSystemDefaultDevice()

delegate

@NSManaged public weak var delegate: MTKViewDelegate?
  • 用途: 设置视图的代理对象,处理渲染回调
  • 说明: 代理对象需要实现 MTKViewDelegate 协议
  • 示例:
mtkView.delegate = self

preferredFramesPerSecond

@NSManaged public var preferredFramesPerSecond: Int
  • 用途: 设置期望的帧率
  • 默认值: 60 FPS
  • 说明: 实际帧率可能因性能限制而有所不同
  • 示例:
mtkView.preferredFramesPerSecond = 30

enableSetNeedsDisplay

@NSManaged public var enableSetNeedsDisplay: Bool
  • 用途: 控制是否使用按需渲染模式
  • 默认值: false (自动渲染模式)
  • 说明:
    • true: 仅在调用 setNeedsDisplay 时渲染
    • false: 连续渲染模式
  • 示例:
mtkView.enableSetNeedsDisplay = true
mtkView.setNeedsDisplay() // 触发单次渲染

isPaused

@NSManaged public var isPaused: Bool
  • 用途: 暂停或恢复渲染循环
  • 默认值: false
  • 示例:
mtkView.isPaused = true  // 暂停渲染
mtkView.isPaused = false // 恢复渲染

渲染配置

colorPixelFormat

@NSManaged public var colorPixelFormat: MTLPixelFormat
  • 用途: 设置颜色缓冲区的像素格式
  • 默认值: MTLPixelFormat.bgra8Unorm
  • 常用格式:
    • .bgra8Unorm: 标准 8 位 BGRA
    • .rgba16Float: 16 位浮点 RGBA
    • .rgb10a2Unorm: 10 位 RGB + 2 位 Alpha
  • 示例:
mtkView.colorPixelFormat = .rgba16Float

depthStencilPixelFormat

@NSManaged public var depthStencilPixelFormat: MTLPixelFormat
  • 用途: 设置深度/模板缓冲区的像素格式
  • 默认值: MTLPixelFormat.invalid (不使用深度缓冲)
  • 常用格式:
    • .depth32Float: 32 位浮点深度
    • .depth24Unorm_stencil8: 24 位深度 + 8 位模板
  • 示例:
mtkView.depthStencilPixelFormat = .depth32Float

sampleCount

@NSManaged public var sampleCount: Int
  • 用途: 设置多重采样抗锯齿 (MSAA) 的采样数
  • 默认值: 1 (无抗锯齿)
  • 常用值: 1, 2, 4, 8
  • 示例:
mtkView.sampleCount = 4 // 4x MSAA

clearColor

@NSManaged public var clearColor: MTLClearColor
  • 用途: 设置清除颜色
  • 默认值: (0.0, 0.0, 0.0, 1.0) 黑色
  • 示例:
mtkView.clearColor = MTLClearColor(red: 0.5, green: 0.7, blue: 1.0, alpha: 1.0)

clearDepth

@NSManaged public var clearDepth: Double
  • 用途: 设置深度缓冲区的清除值
  • 默认值: 1.0
  • 范围: 0.0 到 1.0
  • 示例:
mtkView.clearDepth = 1.0

clearStencil

@NSManaged public var clearStencil: UInt32
  • 用途: 设置模板缓冲区的清除值
  • 默认值: 0
  • 示例:
mtkView.clearStencil = 0

纹理和帧缓冲

drawableSize

@NSManaged public var drawableSize: CGSize
  • 用途: 获取或设置可绘制区域的尺寸
  • 说明: 通常与视图的像素尺寸相匹配
  • 示例:
let size = mtkView.drawableSize
mtkView.drawableSize = CGSize(width: 1920, height: 1080)

autoResizeDrawable

@NSManaged public var autoResizeDrawable: Bool
  • 用途: 控制是否自动调整 drawable 尺寸以匹配视图尺寸
  • 默认值: true
  • 示例:
mtkView.autoResizeDrawable = false

currentDrawable

@NSManaged public var currentDrawable: CAMetalDrawable? { get }
  • 用途: 获取当前的可绘制对象
  • 说明: 只读属性,在渲染过程中使用
  • 示例:
guard let drawable = mtkView.currentDrawable else { return }

currentRenderPassDescriptor

@NSManaged public var currentRenderPassDescriptor: MTLRenderPassDescriptor? { get }
  • 用途: 获取当前的渲染通道描述符
  • 说明: 只读属性,已配置好清除颜色、深度等设置
  • 示例:
guard let renderPassDescriptor = mtkView.currentRenderPassDescriptor else { return }

depthStencilTexture

@NSManaged public var depthStencilTexture: MTLTexture? { get }
  • 用途: 获取深度/模板纹理
  • 说明: 只读属性,当设置了深度格式时自动创建
  • 示例:
let depthTexture = mtkView.depthStencilTexture

multisampleColorTexture

@NSManaged public var multisampleColorTexture: MTLTexture? { get }
  • 用途: 获取多重采样颜色纹理
  • 说明: 只读属性,当启用 MSAA 时自动创建
  • 示例:
let msaaTexture = mtkView.multisampleColorTexture

framebufferOnly

@NSManaged public var framebufferOnly: Bool
  • 用途: 控制 drawable 纹理是否仅用作帧缓冲区
  • 默认值: true
  • 说明:
    • true: drawable 纹理只能用作渲染目标,性能更优
    • false: drawable 纹理可以被读取、采样或用作其他用途
  • 使用场景:
    • 需要读取渲染结果时设为 false
    • 需要将渲染结果用作纹理输入时设为 false
    • 需要进行后处理效果时设为 false
    • 纯粹渲染显示时保持 true 以获得最佳性能
  • 性能影响: 设为 false 会有一定性能开销
  • 示例:
// 当需要读取渲染结果或用作纹理时
mtkView.framebufferOnly = false

// 纯显示渲染时(默认值,最佳性能)
mtkView.framebufferOnly = true

初始化方法

基本初始化

public init(frame frameRect: CGRect, device: MTLDevice?)
  • 用途: 使用指定框架和设备初始化视图
  • 示例:
let device = MTLCreateSystemDefaultDevice()
let mtkView = MTKView(frame: view.bounds, device: device)

从 Coder 初始化

public required init?(coder: NSCoder)
  • 用途: 从 Interface Builder 或归档中初始化
  • 示例:
// 在 Storyboard 中使用时自动调用

实例方法

setNeedsDisplay()

public func setNeedsDisplay()
  • 用途: 在按需渲染模式下触发重绘
  • 前提: enableSetNeedsDisplay 必须为 true
  • 示例:
mtkView.enableSetNeedsDisplay = true
mtkView.setNeedsDisplay() // 触发一次渲染

presentsWithTransaction (macOS)

@available(macOS 10.13, *)
@NSManaged public var presentsWithTransaction: Bool
  • 用途: 控制是否与 Core Animation 事务同步呈现
  • 平台: 仅 macOS
  • 示例:
#if os(macOS)
mtkView.presentsWithTransaction = true
#endif

代理方法

实现 MTKViewDelegate 协议来处理渲染:

mtkView(_:drawableSizeWillChange:)

func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize)
  • 用途: 当可绘制尺寸即将改变时调用
  • 用法: 更新投影矩阵、视口等
  • 示例:
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    // 更新投影矩阵
    updateProjectionMatrix(size: size)
}

draw(in:)

func draw(in view: MTKView)
  • 用途: 执行实际的渲染操作
  • 调用时机: 每帧或按需渲染时
  • 示例:
func draw(in view: MTKView) {
    guard let drawable = view.currentDrawable,
          let renderPassDescriptor = view.currentRenderPassDescriptor else {
        return
    }
    
    // 执行渲染命令
    let commandBuffer = commandQueue.makeCommandBuffer()
    let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
    
    // 渲染操作...
    
    renderEncoder?.endEncoding()
    commandBuffer?.present(drawable)
    commandBuffer?.commit()
}

使用示例

基本设置

class MetalViewController: UIViewController, MTKViewDelegate {
    var mtkView: MTKView!
    var device: MTLDevice!
    var commandQueue: MTLCommandQueue!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 设置 Metal 设备
        device = MTLCreateSystemDefaultDevice()
        guard device != nil else {
            fatalError("Metal is not supported on this device")
        }
        
        // 创建 MTKView
        mtkView = MTKView(frame: view.bounds, device: device)
        mtkView.delegate = self
        mtkView.backgroundColor = UIColor.clear
        
        // 配置渲染设置
        mtkView.colorPixelFormat = .bgra8Unorm
        mtkView.depthStencilPixelFormat = .depth32Float
        mtkView.sampleCount = 4
        mtkView.clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
        
        // 创建命令队列
        commandQueue = device.makeCommandQueue()
        
        view.addSubview(mtkView)
    }
    
    // MARK: - MTKViewDelegate
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        // 处理尺寸变化
    }
    
    func draw(in view: MTKView) {
        // 执行渲染
    }
}

按需渲染示例

class OnDemandRenderer {
    let mtkView: MTKView
    
    init(mtkView: MTKView) {
        self.mtkView = mtkView
        
        // 启用按需渲染
        mtkView.enableSetNeedsDisplay = true
        mtkView.isPaused = true
    }
    
    func triggerRender() {
        // 触发单次渲染
        mtkView.setNeedsDisplay()
    }
}

高级配置示例

func setupAdvancedMTKView() {
    // HDR 渲染配置
    mtkView.colorPixelFormat = .rgba16Float
    mtkView.wantsExtendedDynamicRangeContent = true // iOS 16+
    
    // 高质量抗锯齿
    mtkView.sampleCount = 8
    
    // 自定义尺寸 (超采样)
    mtkView.autoResizeDrawable = false
    mtkView.drawableSize = CGSize(
        width: view.bounds.width * 2.0,
        height: view.bounds.height * 2.0
    )
    
    // 性能优化
    mtkView.preferredFramesPerSecond = 120 // ProMotion 设备
}

framebufferOnly 使用场景示例

class PostProcessingRenderer {
    let mtkView: MTKView
    
    init(mtkView: MTKView) {
        self.mtkView = mtkView
        
        // 需要读取渲染结果进行后处理
        mtkView.framebufferOnly = false
    }
    
    func renderWithPostProcessing() {
        guard let drawable = mtkView.currentDrawable else { return }
        
        // 第一步:渲染场景到 drawable
        renderScene(to: drawable)
        
        // 第二步:将 drawable 作为纹理输入进行后处理
        applyPostProcessing(inputTexture: drawable.texture)
        
        // 第三步:呈现最终结果
        commandBuffer?.present(drawable)
        commandBuffer?.commit()
    }
}

纹理读取示例

func captureFrameBuffer() {
    // 必须设置为 false 才能读取纹理内容
    mtkView.framebufferOnly = false
    
    guard let drawable = mtkView.currentDrawable else { return }
    let texture = drawable.texture
    
    // 创建缓冲区读取纹理数据
    let bytesPerPixel = 4
    let bytesPerRow = texture.width * bytesPerPixel
    let region = MTLRegionMake2D(0, 0, texture.width, texture.height)
    
    let pixelBuffer = UnsafeMutableRawPointer.allocate(
        byteCount: texture.height * bytesPerRow,
        alignment: 1
    )
    
    texture.getBytes(pixelBuffer,
                    bytesPerRow: bytesPerRow,
                    from: region,
                    mipmapLevel: 0)
    
    // 使用 pixelBuffer 中的数据...
    
    pixelBuffer.deallocate()
}

注意事项

  1. 设备设置: 必须在使用前设置 device 属性
  2. 代理实现: 必须实现 MTKViewDelegatedraw(in:) 方法
  3. 线程安全: Metal 命令应在主线程上创建和提交
  4. 内存管理: 及时释放不需要的纹理和缓冲区
  5. 性能优化: 根据需求选择合适的像素格式和采样数
  6. 平台差异: 某些 API 仅在特定平台上可用

相关类和协议

  • MTKViewDelegate: 处理渲染回调的协议
  • CAMetalDrawable: 可绘制对象接口
  • MTLDevice: Metal 设备接口
  • MTLRenderPassDescriptor: 渲染通道描述符
  • MTLTexture: Metal 纹理接口

这份指南涵盖了 MTKView 的所有主要 API 和使用方法,可以作为 Metal 渲染开发的参考文档。

Giscus 未启用:请在 src/site.config.ts 中配置 repoId 与 categoryId。