[iphone] 如何提高自定義OpenGL ES 2.0深度紋理生成的性能?


Answers

在桌面上,許多早期可編程設備就是這樣,雖然它們可以同時處理8或16個或任何片段,但它們實際上只有一個程序計數器用於它們(因為這也意味著只有一個提取/解碼單元和其他一切,只要它們以8或16像素為單位工作)。 因此,對條件的初始禁止以及在此之後的一段時間內,如果對將要一起處理的像素的條件評估返回不同的值,則這些像素將以某種佈置在較小的組中處理。

儘管PowerVR並不明確,但是他們的應用程序開發建議中有一節關於流量控制,並且只有在結果可以合理預測的情況下才能提出很多關於動態分支的建議通常是一個好主意,這讓我覺得它們是相同的那類的東西。 因此,我建議速度差異可能是因為你已經包含了條件。

作為第一個測試,如果您嘗試以下操作會發生什麼?

void main()
{
    float distanceFromCenter = length(impostorSpaceCoordinate);

    // the step function doesn't count as a conditional
    float inCircleMultiplier = step(distanceFromCenter, 1.0);

    float calculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter * inCircleMultiplier);
    mediump float currentDepthValue = normalizedDepth - adjustedSphereRadius * calculatedDepth;

    // Inlined color encoding for the depth values
    float ceiledValue = ceil(currentDepthValue * 765.0) * inCircleMultiplier;

    vec3 intDepthValue = (vec3(ceiledValue) * scaleDownFactor) - (stepValues * inCircleMultiplier);

     // use the result of the step to combine results
    gl_FragColor = vec4(1.0 - inCircleMultiplier) + vec4(intDepthValue, inCircleMultiplier);

}
Question

我有一個開源iOS應用程序,它使用自定義OpenGL ES 2.0著色器來顯示分子結構的三維表示。 它通過使用在矩形上繪製的程序生成的球體和圓柱體冒充者來實現這一點,而不是使用大量頂點構建的這些相同形狀。 這種方法的缺點是這些冒名頂替對象的每個片段的深度值需要在片段著色器中計算,以便在對象重疊時使用。

不幸的是,OpenGL ES 2.0 不允許你寫入gl_FragDepth ,所以我需要將這些值輸出到自定義深度紋理。 我使用幀緩衝對象(FBO)對場景進行傳遞,僅渲染出與深度值對應的顏色,並將結果存儲到紋理中。 然後將此紋理加載到渲染過程的後半部分,在該過程中生成實際的屏幕圖像。 如果該階段的片段處於存儲在屏幕上該點的深度紋理中的深度級別,則顯示該片段。 如果沒有,它就會被拋出。 有關該過程的更多信息,包括圖表,可以在我的帖子中here

這種深度紋理的生成是我渲染過程中的一個瓶頸,我正在尋找一種方法來加快它的速度。 它似乎比它應該慢,但我無法弄清楚為什麼。 為了正確生成此深度紋理,禁用GL_DEPTH_TEST ,使用glBlendFunc(GL_ONE, GL_ONE)啟用glBlendEquation() ,並將glBlendEquation()設置為GL_MIN_EXT 。 我知道以這種方式輸出的場景在基於圖塊的延遲渲染器上並不是最快的,比如iOS設備中的PowerVR系列,但我想不出更好的方法。

我對球體的深度片段著色器(最常見的顯示元素)看起來是這個瓶頸的核心(儀器中的渲染器利用率固定在99%,表明我受片段處理的限制)。 目前看起來如下:

precision mediump float;

varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;

const vec3 stepValues = vec3(2.0, 1.0, 0.0);
const float scaleDownFactor = 1.0 / 255.0;

void main()
{
    float distanceFromCenter = length(impostorSpaceCoordinate);
    if (distanceFromCenter > 1.0)
    {
        gl_FragColor = vec4(1.0);
    }
    else
    {
        float calculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter);
        mediump float currentDepthValue = normalizedDepth - adjustedSphereRadius * calculatedDepth;

        // Inlined color encoding for the depth values
        float ceiledValue = ceil(currentDepthValue * 765.0);

        vec3 intDepthValue = (vec3(ceiledValue) * scaleDownFactor) - stepValues;

        gl_FragColor = vec4(intDepthValue, 1.0);
    }
}

在iPad 1上,這需要35 - 68 ms來渲染DNA空間填充模型的幀,使用直通著色器進行顯示(iPhone 4上為18至35毫秒)。 根據PowerVR PVRUniSCo編譯器( 其SDK的一部分),該著色器最多使用11個GPU週期,最差時使用16個週期。 我知道你被建議不要在著色器中使用分支,但在這種情況下,導致比其他方式更好的性能。

當我簡化它

precision mediump float;

varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;

void main()
{
    gl_FragColor = vec4(adjustedSphereRadius * normalizedDepth * (impostorSpaceCoordinate + 1.0) / 2.0, normalizedDepth, 1.0);
}

在iPad 1上需要18-35毫秒,但在iPhone 4上只需1.7到2.4毫秒。此著色器的估計GPU週期數為8個週期。 基於週期計數的渲染時間的變化似乎不是線性的。

最後,如果我只輸出一個恆定的顏色:

precision mediump float;

void main()
{
    gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);
}

iPad 1上的渲染時間下降到1.1 - 2.3 ms(iPhone 4上為1.3 ms)。

渲染時間的非線性縮放以及第二個著色器的iPad和iPhone 4之間的突然變化讓我覺得我在這裡缺少一些東西。 如果您希望自己嘗試here ,可以從here下載包含這三個著色器變體的完整源項目(查看SphereDepth.fsh文件並註釋掉相應的部分)和測試模型。

如果您已經閱讀過這篇文章,我的問題是:基於此分析信息,如何在iOS設備上提高自定義深度著色器的渲染性能?




我根本不是移動平台專家,但我認為咬你的是:

  • 你的深度著色器非常昂貴
  • 在禁用GL_DEPTH測試時,會在深度傳遞中體驗大量透支

在深度測試之前繪製的額外通行證是否有用?

此過程可以執行GL_DEPTH填充,例如通過繪製表示為四面相機(或多維數據集,可能更容易設置)的每個球體,並包含在關聯的球體中。 可以在沒有顏色掩碼或片段著色器的情況下繪製此過程,只需啟用GL_DEPTH_TESTglDepthMask 。 在桌面平台上,這些類型的傳遞比顏色+深度傳遞更快。

然後在深度計算過程中,您可以啟用GL_DEPTH_TEST並禁用glDepthMask ,這樣您的著色器就不會在由更近的幾何體隱藏的像素上執行。

該解決方案將涉及發出另一組繪製調用,因此這可能沒有益處。




Related