如何在Metal compute着色器中调试难以理解的“内部错误”? (任意代码更改触发器)

时间:2016-01-19 17:31:06

标签: ios metal

我有一个适用于iOS的Metal计算着色器,它在newComputePipelineStateWithFunction()期间开始生成:"Error Domain=AGXMetal Code=1 "Compiler encountered an internal error"错误。

错误在运行之间是一致的,但似乎是由对不相关代码的几乎任意修改触发的。我的意思是 - 在自然地尝试调试为什么我添加最新的代码块表面上导致了这个问题我发现删除看似任意和不相关的代码或构造行将删除错误。

我想知道我是否可能达到编译器的某些大小或复杂性限制。

我的着色器函数少于200行代码,结构为几个C函数总数,并没有分配太多内存,但它确实有一些循环并放弃了一些缓冲区指针。在某一点上它完全正常工作,最近添加的代码更加相同。

我的问题是:

1)首先 - 计算管道的编译器到底在做什么(生成我的default.metallib时没有完成),是否有希望从中收集更多调试信息?

2)如果这是代码的某种大小或复杂性问题,那么是否有人对如何进行重组以缓解这一问题有任何想法?这有什么意义吗?

为此发布示例代码会很困难,但如果首先没有出现解决方案,我会尝试使用它进行更新。

编辑:

所以我所做的就是精心减少并简化我的代码,直到我有一个相对紧凑的例子来说明问题。这并不像听起来那么简单,因为许多看似微小的变化导致问题消失,但是当复杂性被添加回来时它总会返回。

请记住,这不是下面的代码的内容 - 在运行之前很久就设置了计算管道时发生了故障。如果你看到一些明显错误的东西,请告诉我,但除此之外,代码只是具有代表性。

以下着色器在A9处理器(iPhone 6s或6s plus)上失败,但在A7(iPad Air第一代)上运行。

void myFunc( device int *ibuff0, thread int *ibuff1)
{
    int counter = 0;
    float fbuff0[8];
    for( int i = 0; i<8; i++) {
        if ( ibuff0[0] == 42 ) {
            fbuff0[counter++] = 0.0;
        }
    }

    float val = fbuff0[0];
    if ( distance( float2(0.0f,0.0f), float2(val,val) ) < 42.0f) {
        ibuff1[0] = 0;
    }
}

kernel void myKernelFunc( device int *ibuff0 [[ buffer(0) ]] )
{
    int ibuff1[8];
    myFunc( ibuff0, ibuff1 );
}

有趣的是有多少方法可以解决上述问题。仅举几例:1)内联myFunc(手动或使用inline关键字)。 2)注释掉缓冲区分配。 3)用本地缓冲区替换设备缓冲区。 4)注释掉for循环,离开身体。此外,距离函数调用在这里并不神奇,您可以替换任何使用'val'的非可内联函数。

顺便说一句,这是一个完全虚假的单线版本,在A9和A7处理器上都失败了:

kernel void myKernelFunc() {
    while ( true ) { }
}

更多想法 -

我假设我遇到的是金属编译器试图将这些条件和循环结构映射到可以在GPU上运行的动态统一类型的代码中涉及的一些错误或限制。但是我不知道为什么我在进入这个墙之前就已经在我的代码中做到了这一点,因为上面的内容看起来并不像我成功的那样复杂。

现在我有一个样本,我可能会向Apple提交一个错误(正如一些建议)。但我想在这里分享,以防有人有想法。

更新:

我发现解决此问题的最简单方法是手动内联我的一些功能。

0 个答案:

没有答案