简体   繁体   English

使用 Metal 间接命令缓冲区时出错:“片段着色器不能与间接命令缓冲区一起使用”

[英]Error when using Metal Indirect Command Buffer: "Fragment shader cannot be used with indirect command buffers"

I'm working on a Metal, MTKView based app that takes advantage of the A11 TBDR architecture to do deferred shading in a single render pass.我正在MTKView基于MTKView的 Metal 应用程序,该应用程序利用 A11 TBDR 架构在单个渲染通道中执行延迟着色。 I used Apple's Deferred Lighting sample code as reference, and it works great.我使用 Apple 的Deferred Lighting 示例代码作为参考,效果很好。

I'd like to try changing the geometry buffer pass to be GPU-driven, using the Indirect Command Buffer feature of Metal 2 on A11 hardware.我想尝试在 A11 硬件上使用 Metal 2 的间接命令缓冲区功能将几何缓冲区传递更改为 GPU 驱动。

I've been using Apple's Encoding Indirect Command Buffers on the GPU sample code as my main point of reference for this.我一直在 GPU 示例代码上使用 Apple 的编码间接命令缓冲区作为我的主要参考点。 I'm able to run this sample on my iPhone XR (although, probably off-topic, the scrolling is not smooth, it judders).我可以在我的 iPhone XR 上运行这个示例(尽管可能偏离主题,滚动不流畅,它会颤抖)。

I'm running into difficulties however with my own code, when I try to move my geometry buffer pass into an indirect command buffer.然而,当我尝试将几何缓冲区传递到间接命令缓冲区时,我遇到了困难,但是使用我自己的代码。 When I set supportIndirectCommandBuffers to true on the MTLRenderPipelineDescriptor of the Geometry Buffer pipeline, device.makeRenderPipelineState fails with the error当我设置supportIndirectCommandBufferstrueMTLRenderPipelineDescriptor几何缓存管线, device.makeRenderPipelineState失败,出现错误

AGXMetalA12 Code=3 "Fragment shader cannot be used with indirect command buffers" AGXMetalA12 Code=3“片段着色器不能与间接命令缓冲区一起使用”

I've not been able to find any information in the documentation on this error.我无法在文档中找到有关此错误的任何信息。 I'm wondering, are there certain kinds of fragment operation that are not allowed in indirect pipelines, or some kind of limit to GPU-driven drawing that I've overlooked (the number of color attachments perhaps)?我想知道,是否有某些类型的片段操作在间接管道中是不允许的,或者我忽略了对 GPU 驱动绘图的某种限制(可能是颜色附件的数量)?

SharedTypes.h共享类型.h

Header shared by Metal and Swift Metal 和 Swift 共享的头文件

#ifndef SharedTypes_h
#define SharedTypes_h

#ifdef __METAL_VERSION__

#define NS_CLOSED_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#define NSInteger metal::int32_t

#else

#import <Foundation/Foundation.h>

#endif

#include <simd/simd.h>

typedef struct {
    uint32_t meshId;
    matrix_float3x3 normalViewMatrix;
    matrix_float4x4 modelMatrix;
    matrix_float4x4 shadowMVPTransformMatrix;
} InstanceData;

typedef struct {
    vector_float3 cameraPosition;
    float voxelScale;
    float blockScale;
    vector_float3 lightDirection;
    matrix_float4x4 viewMatrix;
    matrix_float4x4 projectionMatrix;
    matrix_float4x4 projectionMatrixInverse;
    matrix_float4x4 shadowViewProjectionMatrix;
} VoxelUniforms;

typedef NS_CLOSED_ENUM(NSInteger, BufferIndex)
{
    BufferIndexInstances  = 0,
    BufferIndexVertices = 1,
    BufferIndexIndices = 2,
    BufferIndexVoxelUniforms = 3,
};

typedef NS_CLOSED_ENUM(NSInteger, RenderTarget)
{
    RenderTargetLighting = 0,
    RenderTargetNormal_shadow = 1,
    RenderTargetVoxelIndex = 2,
    RenderTargetDepth = 3,
};

#endif /* SharedTypes_h */

GBuffer shader GBuffer 着色器

#include <metal_stdlib>
using namespace metal;
#include "../SharedTypes.h"

struct VertexIn {
    packed_half3 position;
    packed_half3 texCoord3D;
    half ambientOcclusion;
    uchar normalIndex;
};

struct VertexInOut {
    float4 position [[ position ]];
    half3 worldPos;
    half3 eyeNormal;
    half3 localPosition;
    half3 localNormal;
    float eyeDepth;
    float3 shadowCoord;
    half3 texCoord3D;
};

vertex VertexInOut gBufferVertex(device InstanceData* instances [[ buffer( BufferIndexInstances ) ]],
                                 device VertexIn* vertices [[ buffer( BufferIndexVertices ) ]],
                                 constant VoxelUniforms &uniforms [[ buffer( BufferIndexVoxelUniforms ) ]],
                                 uint vid [[ vertex_id ]],
                                 ushort iid [[ instance_id ]])
{
    InstanceData instance = instances[iid];
    VertexIn vert = vertices[vid];
    VertexInOut out;
    float4 position = float4(float3(vert.position), 1);
    float4 worldPos = instance.modelMatrix * position;
    float4 eyePosition = uniforms.viewMatrix * worldPos;
    out.position = uniforms.projectionMatrix * eyePosition;
    out.worldPos = half3(worldPos.xyz);
    out.eyeDepth = eyePosition.z;

    half3 normal = normals[vert.normalIndex];
    out.eyeNormal = half3(instance.normalViewMatrix * float3(normal));
    out.shadowCoord = (instance.shadowMVPTransformMatrix * position).xyz;

    out.localPosition = half3(vert.position);
    out.localNormal = normal;
    out.texCoord3D = half3(vert.texCoord3D);
    return out;
}

fragment GBufferData gBufferFragment(VertexInOut in [[ stage_in ]],
                                     constant VoxelUniforms &uniforms [[ buffer( BufferIndexVoxelUniforms ) ]],
                                     texture3d<ushort, access::sample> voxelMap [[ texture(0) ]],
                                     depth2d<float> shadowMap [[ texture(1) ]],
                                     texture3d<half, access::sample> fogOfWarMap [[ texture(2) ]]
                                     ) {
    // voxel index
    half3 center = round(in.texCoord3D);
    uchar voxIndex = voxelMap.read(ushort3(center)).r - 1;

    // ambient occlusion
    half3 neighborPos = center + in.localNormal;
    half3 absNormal = abs(in.localNormal);
    half2 texCoord2D = tc2d(in.localPosition / uniforms.voxelScale, absNormal);
    half ao = getAO(voxelMap, neighborPos, absNormal.yzx, absNormal.zxy, texCoord2D);

    // shadow
    constexpr sampler shadowSampler(coord::normalized,
                                    filter::linear,
                                    mip_filter::none,
                                    address::clamp_to_edge,
                                    compare_func::less);

    float shadow_sample = ambientLightingLevel;
    for (short i = 0; i < shadowSampleCount; i++){
        shadow_sample += shadowMap.sample_compare(shadowSampler, in.shadowCoord.xy + poissonDisk[i] * 0.002, in.shadowCoord.z - 0.0018) * shadowContributionPerSample;
    }
    shadow_sample = min(1.0, shadow_sample);

    //fog-of-war
    half fogOfWarSample = fogOfWarMap.sample(fogOfWarSampler, (float3(in.worldPos) / uniforms.blockScale) + float3(0.5, 0.4, 0.5)).r;
    half notVisible = max(fogOfWarSample, 0.5h);

    // output
    GBufferData out;
    out.normal_shadow = half4(in.eyeNormal, ao * half(shadow_sample) * notVisible);
    out.voxelIndex = voxIndex;
    out.depth = in.eyeDepth;
    return out;
};

Pipeline setup管道设置

extension RenderTarget {

    var pixelFormat: MTLPixelFormat {
        switch self {
        case .lighting: return .bgra8Unorm
        case .normal_shadow: return .rgba8Snorm
        case .voxelIndex: return .r8Uint
        case .depth: return .r32Float
        }
    }

    static var allCases: [RenderTarget] = [.lighting, .normal_shadow, .voxelIndex, .depth]
}

public final class GBufferRenderer {
    private let renderPipelineState: MTLRenderPipelineState
    weak var shadowMap: MTLTexture?

    public init(depthPixelFormat: MTLPixelFormat, colorPixelFormat: MTLPixelFormat, sampleCount: Int = 1) throws {
        let library = try LibraryMonad.getLibrary()
        let device = library.device
        let descriptor = MTLRenderPipelineDescriptor()
        descriptor.vertexFunction = library.makeFunction(name: "gBufferVertex")!
        descriptor.fragmentFunction = library.makeFunction(name: "gBufferFragment")!
        descriptor.depthAttachmentPixelFormat = depthPixelFormat
        descriptor.stencilAttachmentPixelFormat = depthPixelFormat
        descriptor.sampleCount = sampleCount
        for target in RenderTarget.allCases {
            descriptor.colorAttachments[target.rawValue].pixelFormat = target.pixelFormat
        }
        // uncomment below to trigger throw
        // descriptor.supportIndirectCommandBuffers = true
        renderPipelineState = try device.makeRenderPipelineState(descriptor: descriptor) // throws "Fragment shader cannot be used with indirect command buffers"
    }

    public convenience init(mtkView: MTKView) throws {
        try self.init(depthPixelFormat: mtkView.depthStencilPixelFormat, colorPixelFormat: mtkView.colorPixelFormat, sampleCount: mtkView.sampleCount)
    }
}

The above works great when triggering draws from the CPU in the usual way, but when setting supportIndirectCommandBuffers in preparation for GPU drawing it throws the error.当以通常的方式从 CPU 触发绘制时,上述方法效果很好,但是当设置supportIndirectCommandBuffers以准备 GPU 绘制时,它会引发错误。

I've tried stripping down the fragment shader to just return constant values for the GBuffers, and then makeRenderPipelineState succeeds, but when I add texture sampling back in it begins complaining again.我试过剥离片段着色器,只为 GBuffers 返回常量值,然后makeRenderPipelineState成功,但是当我重新添加纹理采样时,它又开始抱怨了。 I can't seem to pin down what exactly it doesn't like about the frag shader.我似乎无法确定它到底不喜欢碎片着色器的什么地方。

Looking through the code and through Metal documentation and Metal Shading Language specification, I think I know why you get this error.查看代码以及 Metal 文档和 Metal Shading Language 规范,我想我知道为什么会出现此错误。

If you look through render_command interface that is present in metal_command_buffer header in Metal, you'll find that to pass parameters to indirect render commands, you only have these functions: set_vertex_buffer and set_fragment_buffer , there is no set_vertex_texture or set_vertex_sampler like you have in MTLRenderCommandEncoder .如果您查看 Metal 中metal_command_buffer标头中存在的render_command接口,您会发现要将参数传递给间接渲染命令,您只有以下功能: set_vertex_bufferset_fragment_buffer ,没有set_vertex_texture set_vertex_sampler那样的set_vertex_textureMTLRenderCommandEncoder

But, since your pipeline uses shader that in turn uses textures as arguments and you indicate by using supportIndirectCommandBuffers that you would like to use this pipeline in indirect commands, Metal has no choice but to fail pipeline creation.但是,由于您的管道使用着色器,而着色器又使用纹理作为参数,并且您通过使用supportIndirectCommandBuffers指示您希望在间接命令中使用此管道,因此 Metal 没有选择,只能使管道创建失败。

Instead if you want to pass textures or samplers to indirect render commands, you should use argument buffers, that you will pass to the shader that issues indirect render commands, which in turn will bind them using set_vertex_buffer and set_fragment_buffer for each render_command .相反,如果你想将纹理或采样器传递给间接渲染命令,你应该使用参数缓冲区,你将传递给发出间接渲染命令的着色器,后者将使用set_vertex_bufferset_fragment_buffer为每个render_command绑定它们。

Specification: Metal Shading Language Specification (Section 5.16)规范: 金属着色语言规范(第 5.16 节)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将数字添加到数组值时,由于执行期间发生错误,命令缓冲区的Swift Metal Shader执行被中止 - Swift Metal Shader Execution of the command buffer was aborted due to an error during execution when adding a number to an array value 尝试将制服传递给金属着色器时,“SceneKit:错误,缺少缓冲区[-1/2]” - “SceneKit: error, missing buffer [-1/2]” when trying to pass uniforms to a metal shader 金属中的片段着色器和顶点着色器 - Fragment shader and Vertex Shader in Metal iOS Metal:命令缓冲区执行顺序 - iOS Metal: command buffer execution order 带有异步/等待的金属命令缓冲区 addCompletedHandler - Metal Command Buffer addCompletedHandler with async/await 金属着色器在缓冲区中插值 - Metal shader interpolate values in buffer 金属片段着色器的uv坐标在读取顶点颜色时发生更改 - Metal fragment shader uv coordinates change when reading in vertex color 如何在Metal中组合使用不同着色器的渲染命令编码器 - How to combine Render Command Encoders that use a different shader in Metal iOS12导致金属命令缓冲区执行错误,渲染是毛病或不会发生 - iOS12 is causing an error on Metal Command Buffer execution, render is glitchy or doesn't occur 如何在Metal中的片段着色器中获取片段坐标? - How to get fragment coordinate in fragment shader in Metal?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM