如何在 NSScrollView 中显示非常大的基于金属的纹理?

How to display very large Metal-based textures in an NSScrollView?

我有一个程序可以在 NSScrollView 中显示图像。我正在使用 Metal 渲染图像,所以 NSClipViewdocumentViewMTKView。我支持缩放图像 in/out 的功能。渲染管道的最后一步将我的金属纹理包装到 CIImage 中,我在其中应用 CIAffineTransform 过滤器进行缩放。我将 drawRect 中的图像渲染成这样的可绘制对象:

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.'

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

在显示超大图像的同时,标准滚动视图的最佳路径是什么?或者,有没有办法在 Metal 中使用 CATiledLayer 之类的东西?

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

NSScrollView
  NSClipView
    NSView <-- document view
      MTKView

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

论坛上的回复建议查看 synchronizes two scroll views 的代码作为一个很好的起点。