【问题标题】:Trouble Getting Depth Testing To Work With Apple's Metal Graphics API无法使用 Apple 的 Metal Graphics API 进行深度测试
【发布时间】: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 帧捕获工具始终将我的深度缓冲区显示为纯白色图像。

【问题讨论】:

    标签: macos 3d metal


    【解决方案1】:

    好的,我想通了。我将在这里发布答案以帮助下一个人。写入深度缓冲区没有问题。这就解释了为什么花时间处理深度纹理和深度模板状态设置让我无处可去。

    问题在于 Metal 与 OpenGL 中用于标准化设备坐标的坐标系不同。在 Metal 中,NDC 位于空间 [-1,+1]x[-1,+1]x[0,1] 中。在 OpenGL 中,NDC 是 [-1,+1]x[-1,+1]x[-1,+1]。如果我只是简单地将 glm::perspective 生成​​的投影矩阵推过 Metal,那么结果将不会像预期的那样。为了补偿使用 Metal 渲染时的 NDC 空间差异,该投影矩阵必须左乘以对角线为 (1, 1, 0.5, 1) 的缩放矩阵。

    我发现这些链接很有帮助: 1.http://blog.athenstean.com/post/135771439196/from-opengl-to-metal-the-projection-matrix 2.http://www.songho.ca/opengl/gl_projectionmatrix.html

    编辑:用更完整和准确的解释替换了解释。用更好的解决方案替换解决方案。

    【讨论】:

    猜你喜欢
    • 2017-07-28
    • 2012-02-21
    • 2021-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-01
    • 2018-12-12
    相关资源
    最近更新 更多