首页 > 解决方案 > 如何在 NSScrollView 中显示非常大的基于 Metal 的纹理?

问题描述

我有一个程序可以在NSScrollView. 我正在使用 Metal 来渲染图像,所以NSClipView'sdocumentViewMTKView. 我支持放大/缩小图像的能力。我的渲染管道的最后一步将我的金属纹理包装到CIImage我应用CIAffineTransform过滤器进行缩放的地方。我将图像渲染drawRect成这样的drawable:

CIImage *image = ...;
CIRenderDestination *renderDest = [[CIRenderDestination alloc] initWithWidth:image.extent.size.width
                                                                      height:image.extent.size.height
                                                                 pixelFormat:self.colorPixelFormat
                                                               commandBuffer:commandBuffer
                                                          mtlTextureProvider:^id<MTLTexture> _Nonnull{
    return drawable.texture;
}];

[self.ciContext startTaskToRender:image
                    toDestination:renderDest
                            error:&error];

当文档视图大小发生变化时,我会更新视图的drawableSize属性。这很好用,滚动条和滚动条按预期工作。问题是当放大系数变得太大时,drawable的底层纹理超过了GPU内存:

validateTextureDimensions, line 1081: error 'MTLTextureDescriptor has width (18798) greater than the maximum allowed size of 16384.'
validateTextureDimensions:1081: failed assertion `MTLTextureDescriptor has width (18798) greater than the maximum allowed size of 16384.'

所以将裁剪的图像绘制到可绘制对象的一部分中是行不通的(不是我知道如何从 a 中做到这一点CIImage)。即使我没有在视图中绘制任何图像(即仅设置预期的可绘制大小)也会发生这种情况,这表明MTKView' 的可绘制是问题所在。当然,我不需要绘制完整的图像;只是在剪辑视图中可见的部分。我可以限制drawableSize/document 视图,但是我必须手动有效地实现滚动并丢失滚动条。

在仍然显示超大图像的同时,标准滚动视图的最佳路径是什么?或者,是否有使用CATiledLayerMetal 之类的方法?

标签: macoscocoametalcore-imagensscrollview

解决方案


我在开发者论坛上收到了一位 Apple 工程师的建议MTKView,该建议是在 的文档视图中放置一个NSScrollView,即:

NSScrollView
  NSClipView
    NSView <-- document view
      MTKView

这是有道理的,因为 Metal 和 AppKit 并没有真正相互交流。使用这种方案,可以操纵文档视图大小以使滚动条正确反映并手动将其设置MTKView为不大于剪辑视图中可见的大小。不是理想的解决方案,因为涉及到很多 NSRect 数学,但肯定是可行的。(我还没有时间实现它,但如果它出现,我会用任何有用的信息更新这个答案。)

论坛上的回复建议查看同步两个滚动视图的代码作为一个很好的起点。


推荐阅读