混合大四边形对性能的影响

Posted

技术标签:

【中文标题】混合大四边形对性能的影响【英文标题】:Performance hit from blending large quad 【发布时间】:2011-11-20 23:35:03 【问题描述】:

我有一款在视网膜显示器上运行良好 (55-60fps) 的游戏。 我想添加一个与现有场景融合的全屏覆盖。但是,即使使用较小的纹理,对性能的影响也是巨大的。我可以进行优化以使其可用吗?

如果我使用 80x120 纹理(纹理是动态渲染的,这就是它不是方形的原因),我会得到 25-30FPS。如果我使纹理更小,性能会提高,但质量是不可接受的。不过,一般来说,叠加层的质量并不是很重要(它只是照明)。

渲染器利用率为 99%。

即使我使用文件 (.png) 中的方形纹理,性能也很差。

这就是我创建纹理的方式:

    [EAGLContext setCurrentContext:context];

    // Create default framebuffer object.
    glGenFramebuffers(1, &lightFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, lightFramebuffer);

    // Create color render buffer and allocate backing store.
    glGenRenderbuffers(1, &lightRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, lightRenderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, LIGHT_WIDTH, LIGHT_HEIGHT);

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, lightRenderbuffer);

    glGenTextures(1, &lightImage);
    glBindTexture(GL_TEXTURE_2D, lightImage);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, LIGHT_WIDTH, LIGHT_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, lightImage, 0);

这里是渲染...

/* Draw scene... */

glBlendFunc(GL_ONE, GL_ONE);


//Switch to offscreen texture buffer
glBindFramebuffer(GL_FRAMEBUFFER, lightFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, lightRenderbuffer);
glViewport(0, 0, LIGHT_WIDTH, LIGHT_HEIGHT);

glClearColor(ambientLight, ambientLight, ambientLight, ambientLight);
glClear(GL_COLOR_BUFFER_BIT);

/* Draw lights to texture... */

//Switch back to main frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glViewport(0, 0, framebufferWidth, framebufferHeight);  

glBlendFunc(GL_DST_COLOR, GL_ZERO);

glBindTexture(GL_TEXTURE_2D, glview.lightImage);    

/* Set up drawing... */

glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, 0);

以下是我在尝试缩小问题范围时采用的一些基准。 “无混合”意味着我在绘制四边形之前 glDisable(GL_BLEND)。 “无缓冲区切换”意味着我在绘制之前不会从屏幕外缓冲区来回切换。

(Tests using a static 256x256 .png)
No blend, No buffer switching: 52FPS
Yes blend, No buffer switching: 29FPS //disabled the glClear, which would artificially speed up the rendering
No blend, Yes buffer switching: 29FPS
Yes blend, Yes buffer switching: 27FPS

Yes buffer switching, No drawing: 46FPS

感谢任何帮助。谢谢!

更新

我没有在之后混合整个光照贴图,而是最终编写了一个着色器来动态完成这项工作。每个片段都从光照贴图中进行采样和混合(有点像多重纹理)。起初,性能提升很小,但后来我为光照贴图使用了 lowp sampler2d,然后我得到了大约 45FPS。

这是片段着色器:

lowp vec4 texColor = texture2D(tex, texCoordsVarying);
lowp vec4 lightColor = texture2D(lightMap, worldPosVarying);
lightColor.rgb *= lightColor.a;
lightColor.a = 1.0;

gl_FragColor = texColor * color * lightColor;

【问题讨论】:

我怀疑glView 缓冲开关可能是这里的罪魁祸首。这些方法发生了什么?为什么不使用glBindRenderBuffer 我将内联这些方法以进行澄清。 可以调试性能问题,尝试预渲染叠加层(暂时保持静态),然后每帧将其复制到主缓冲区。这至少会告诉你缓冲区切换是否很慢(即每帧对 glBindFrame、glBindRender、glViewport 进行两次调用)。 如果我只做绘图以外的所有事情(包括 BindRender、Viewport 的东西和实际绘制灯光),它会很快。所以我认为它必须是缓慢的最终绘图/合成。 如果你这样做 glDisable(GL_BLEND) 会加快速度吗? 【参考方案1】:

好的,我认为您已经遇到了硬件的限制。在整个场景中混合一个屏幕大小的四边形对于基于图块的硬件来说可能是一个特别糟糕的情况。 PowerVR SGX(在 iPhone 上)针对隐藏表面移除进行了优化,以避免在不需要时绘制东西。它具有低内存带宽,因为它针对低功耗设备进行了优化。

所以屏幕大小的混合四边形正在读取然后写入屏幕上的每个片段。哎哟!

glClear 的加速是相关的 - 因为您告诉 GL 在渲染之前您不关心后台缓冲区的内容,这样可以节省将之前的内容加载到内存中。

这里有一个很好的 ios 硬件概述:http://www.imgtec.com/factsheets/SDK/POWERVR%20SGX.OpenGL%20ES%202.0%20Application%20Development%20Recommendations.1.1f.External.pdf

至于实际的解决方案 - 我会尝试直接在游戏场景中渲染您的叠加层。

例如,您的渲染循环应如下所示:

[EAGLContext setCurrentContext:context];

// Set up game view port and render the game
InitGameViewPort();
GameRender();

// Change camera to 2d/orthographic, turn off depth write and compare
InitOverlayViewPort()

// Render overlay into same buffer 
OverlayRender()

【讨论】:

谢谢。是的,我得出了同样的结论。不幸的是,我不能使用你的解决方案——我正在绘制的叠加层实际上更像是一个光照贴图,所以它会影响实际的游戏环境像素。我最终编写了一个着色器来完成同样的工作,并取得了相当大的成功。我将在我的问题中发布详细信息。感谢您对调查此问题的所有帮助! 太棒了 - 我很想看看你做了什么。 我在游戏的整个场景中使用了屏幕大小的四边形,并且帧速率没有下降(3G 为 30 fps,视网膜为 60 秒)。减速可能是在纹理的创建中?你每一帧都这样做吗?【参考方案2】:

如果您渲染到 PowerVR 芯片上的渲染目标,切换到另一个渲染目标并渲染,然后切换回任何以前的渲染目标,您将遭受重大性能损失。这种访问模式被最新 Instruments 中内置的 OpenGL ES Analyzer 标记为“逻辑缓冲区加载”。

如果您切换渲染顺序以便首先绘制光照贴图渲染目标,然后将场景渲染到主帧缓冲区,然后对光照贴图渲染目标纹理进行全屏混合,您的性能应该会更高。

【讨论】:

【参考方案3】:

我可以确认,在使用 iOS 4.2 的 iPad 1 上,为一个在 18 到 31 fps 之间切换的全屏四边形启用/禁用 GL_BLEND。在两次运行中,渲染器利用率为 90-100%。

【讨论】:

【参考方案4】:

在调整纹理之前,请确保您的着色器已经过优化。当填充 960x640 屏幕(614400 像素)时,片段着色器中的任何操作都会产生巨大影响。

为这种情况创建特定版本的片段着色器是一件好事。应该是这样的:

varying mediump vec2 vertexTexCoord;
uniform sampler2D texture;

void main() 
    gl_FragColor = texture2D(texture, vertexTexCoord);

使用这个片段着色器创建另一个程序并在绘制大四边形之前使用它,然后恢复正常程序。 iPhone 4 能够通过混合渲染大约 7 个全屏、每帧 1:1 纹理四边形,但使用更复杂的着色器很快就会下降到大约 1。

(另外,在您的情况下,尝试先渲染覆盖纹理,然后是普通元素,然后是其余部分的纹理。它应该会显着提高性能。)

【讨论】:

以上是关于混合大四边形对性能的影响的主要内容,如果未能解决你的问题,请参考以下文章

用docker启动nginx影响性能吗

混合模式调试(C++、C#、VB)

OpenGL 在单色上混合颜色

OpenGL GLSL 通过任意形状混合两个纹理

OpenCV:如何混合多个不同颜色的半透明多边形?

透明天空盒+透明高度图的OpenGL混合问题