如何在 NSScrollView 中显示非常大的基于金属的纹理?
How to display very large Metal-based textures in an NSScrollView?
我有一个程序可以在 NSScrollView
中显示图像。我正在使用 Metal 渲染图像,所以 NSClipView
的 documentView
是 MTKView
。我支持缩放图像 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 工程师发来的 suggestion 的 MTKView
在 NSScrollView
的文档视图中放置一个 MTKView
,即:
NSScrollView
NSClipView
NSView <-- document view
MTKView
这是有道理的,因为 Metal 和 AppKit 并不真正相互交谈。使用此方案,可以操纵文档视图大小以使滚动条正确反映,并手动将 MTKView
设置为不大于剪辑视图中可见的大小。这不是理想的解决方案,因为涉及到很多 NSRect 数学,但肯定可行。 (我还没有时间实施它,但如果出现任何有用的信息,我会更新这个答案。)
论坛上的回复建议查看 synchronizes two scroll views 的代码作为一个很好的起点。
我有一个程序可以在 NSScrollView
中显示图像。我正在使用 Metal 渲染图像,所以 NSClipView
的 documentView
是 MTKView
。我支持缩放图像 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 工程师发来的 suggestion 的 MTKView
在 NSScrollView
的文档视图中放置一个 MTKView
,即:
NSScrollView
NSClipView
NSView <-- document view
MTKView
这是有道理的,因为 Metal 和 AppKit 并不真正相互交谈。使用此方案,可以操纵文档视图大小以使滚动条正确反映,并手动将 MTKView
设置为不大于剪辑视图中可见的大小。这不是理想的解决方案,因为涉及到很多 NSRect 数学,但肯定可行。 (我还没有时间实施它,但如果出现任何有用的信息,我会更新这个答案。)
论坛上的回复建议查看 synchronizes two scroll views 的代码作为一个很好的起点。