在 Metal 中反射着色器统一名称

Reflecting shader uniform names in Metal

假设我的着色器中有以下统一缓冲区:

typedef struct
{
    matrix_float4x4 modelview_projection_matrix;
    float someValue;
} uniforms_t;

如何在 C++ 或 Objective-C 中获取 someValue 的位置?我想做这样的事情:

void Shader::SetFloat( const char* name, float value )

名称为 'someValue'.

看看Specifying Resources for a Render Command Encoder section of Apple's Metal Programming Guide

作为一个非常基本的解释...

  1. 将您的 uniforms_t 结构(通常是包含特定着色器函数的所有制服的单个结构)声明为 Metal[=36 的参数=] 着色器函数,并将其与特定缓冲区索引(例如 [[ buffer(0) ]])相关联,作为着色器函数声明的一部分。

  2. 从您的应用程序代码中,将 uniforms_t 结构的内容复制到 MTLBuffer 中,以某个偏移量。

  3. 从您的应用程序代码中,调用 MTLRenderCommandEncoder setVertexBuffer:offset:atIndex:setFragmentBuffer:offset:atIndex: 方法来关联 MTLBuffer 的内容(在偏移量处您使用在着色器函数中声明的缓冲区索引复制了 uniforms_t 结构)。这基本上告诉着色器函数在哪个 MTLBuffer 中查找(以及在该缓冲区中的哪个位置)该函数参数的值。

我通过检查 BGFX 的源代码得出了一个解决方案:

    NSError* error = NULL;
    MTLRenderPipelineReflection* reflectionObj;
    MTLPipelineOption option = MTLPipelineOptionBufferTypeInfo | MTLPipelineOptionArgumentInfo;
    id <MTLRenderPipelineState> pso = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor options:option reflection:&reflectionObj error:&error];

    for (MTLArgument *arg in reflectionObj.vertexArguments)
    {
        NSLog(@"Found arg: %@\n", arg.name);

        if (arg.bufferDataType == MTLDataTypeStruct)
        {
            for( MTLStructMember* uniform in arg.bufferStructType.members )
            {
                NSLog(@"uniform: %@ type:%lu, location: %lu", uniform.name, (unsigned long)uniform.dataType, (unsigned long)uniform.offset);         
            }
        }
    }