【发布时间】:2017-03-06 23:43:20
【问题描述】:
我晚上会花一些时间来学习 Apple 的 Metal 图形 API。我遇到了一个令人沮丧的问题,因此肯定遗漏了一些非常基本的东西:只有在禁用深度测试或将深度功能更改为“更大”时,我才能让渲染对象出现在屏幕上。可能出了什么问题?另外,为了调试这个问题,我可以检查哪些类型的东西?
这就是我正在做的事情:
1) 我正在使用 SDL 创建我的窗口。在设置 Metal 时,我手动创建了 CAMetalLayer 并将其插入到层层次结构中。需要明确的是,我没有使用 MTKView,也不想使用 MTKView。尽可能远离 Objective-C 和 Cocoa 似乎是编写这个跨平台应用程序的最佳策略。目的是使用 SDL 和可以在运行时交换的渲染引擎编写与平台无关的 C++ 代码。在这个界面后面是所有 Apple 特定的代码所在的地方。但是,我强烈怀疑部分问题与设置层有关:
SDL_SysWMinfo windowManagerInfo;
SDL_VERSION(&windowManagerInfo.version);
SDL_GetWindowWMInfo(&window, &windowManagerInfo);
// Create a metal layer and add it to the view that SDL created.
NSView *sdlView = windowManagerInfo.info.cocoa.window.contentView;
sdlView.wantsLayer = YES;
CALayer *sdlLayer = sdlView.layer;
CGFloat contentsScale = sdlLayer.contentsScale;
NSSize layerSize = sdlLayer.frame.size;
_metalLayer = [[CAMetalLayer layer] retain];
_metalLayer.contentsScale = contentsScale;
_metalLayer.drawableSize = NSMakeSize(layerSize.width * contentsScale,
layerSize.height * contentsScale);
_metalLayer.device = device;
_metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
_metalLayer.frame = sdlLayer.frame;
_metalLayer.framebufferOnly = true;
[sdlLayer addSublayer:_metalLayer];
2) 我创建了一个深度纹理以用作深度缓冲区。我的理解是,这一步在 Metal 中是必要的。不过,在 OpenGL 中,框架会自动为我创建一个深度缓冲区:
CGSize drawableSize = _metalLayer.drawableSize;
MTLTextureDescriptor *descriptor =
[MTLTextureDescriptorr texture2DDescriptorWithPixelFormat:MTLPixelFormatDepth32Float_Stencil8 width:drawableSize.width height:drawableSize.height mipmapped:NO];
descriptor.storageMode = MTLStorageModePrivate;
descriptor.usage = MTLTextureUsageRenderTarget;
_depthTexture = [_metalLayer.device newTextureWithDescriptor:descriptor];
_depthTexture.label = @"DepthStencil";
3) 我创建了一个将在渲染时设置的深度模板状态对象:
MTLDepthStencilDescriptor *depthDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthDescriptor.depthWriteEnabled = YES;
depthDescriptor.depthCompareFunction = MTLCompareFunctionLess;
_depthState = [device newDepthStencilStateWithDescriptor:depthDescriptor];
4) 创建渲染通道对象时,我明确附加了深度纹理:
_metalRenderPassDesc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
MTLRenderPassColorAttachmentDescriptor *colorAttachment = _metalRenderPassDesc.colorAttachments[0];
colorAttachment.texture = _drawable.texture;
colorAttachment.clearColor = MTLClearColorMake(0.2, 0.4, 0.5, 1.0);
colorAttachment.storeAction = MTLStoreActionStore;
colorAttachment.loadAction = desc.clear ? MTLLoadActionClear : MTLLoadActionLoad;
MTLRenderPassDepthAttachmentDescriptor *depthAttachment = _metalRenderPassDesc.depthAttachment;
depthAttachment.texture = depthTexture;
depthAttachment.clearDepth = 1.0;
depthAttachment.storeAction = MTLStoreActionDontCare;
depthAttachment.loadAction = desc.clear ? MTLLoadActionClear : MTLLoadActionLoad;
MTLRenderPassStencilAttachmentDescriptor *stencilAttachment = _metalRenderPassDesc.stencilAttachment;
stencilAttachment.texture = depthAttachment.texture;
stencilAttachment.storeAction = MTLStoreActionDontCare;
stencilAttachment.loadAction = desc.clear ? MTLLoadActionClear : MTLLoadActionLoad;
5) 最后,在渲染时,我在绘制对象之前设置了 depth-stencil 对象:
[_encoder setDepthStencilState:_depthState];
请注意,如果我进入第 3 步并将 depthCompareFunction 更改为 MTLCompareFunctionAlways 或 MTLCompareFunctionGreater,那么我会在屏幕上看到多边形,但排序(预期)不正确。如果我将 depthCompareFunction 设置为 MTLCompareFunctionLess,那么我只看到背景颜色。如果所有片段始终未能通过深度测试,它就会起作用。
Metal API 验证器没有报告错误,也没有警告...
我尝试了各种设置组合,例如深度模板纹理格式,但没有取得任何进展。老实说,我不确定下一步该尝试什么。
编辑:Xcode 中的 GPU 帧捕获显示我的多边形的绿色轮廓,但实际上没有绘制这些片段。
编辑 2:我了解到 Metal API 验证器具有“扩展”模式。启用此功能后,我会收到以下两个警告:
warning: Texture Usage Should not be Flagged as MTLTextureUsageRenderTarget: This texture is not a render target. Clear the MTLTextureUsageRenderTarget bit flag in the texture usage options. Texture = DepthStencil. Texture is used in the Depth attachment.
warning: Resource Storage Mode Should be MTLStorageModePrivate and it Should be Initialized with a Blit: This resource is rarely accessed by the CPU. Changing the storage mode to MTLStorageModePrivate and initializing it with a blit from a shared buffer may improve performance. Texture = 0x102095000.
当我看到这两个警告时,我得到了这两个错误。 (警告和错误似乎相互矛盾。)]
error 'MTLTextureDescriptor: Depth, Stencil, DepthStencil, and Multisample textures must be allocated with the MTLResourceStorageModePrivate resource option.'
failed assertion `MTLTextureDescriptor: Depth, Stencil, DepthStencil, and Multisample textures must be allocated with the MTLResourceStorageModePrivate resource option.'
编辑 3:当我运行示例 Metal 应用程序并使用 GPU 帧捕获工具时,我会看到深度缓冲区的灰度表示,并且渲染的对象清晰可见。这不会发生在我的应用程序中。在那里,GPU 帧捕获工具始终将我的深度缓冲区显示为纯白色图像。
【问题讨论】: