4

I recently upgraded my old Galaxy S2 phone to a brand new Galaxy S7, and was very surprised to find an old game I wrote seemed to be performing worse on the new phone. After cutting everything down to a bare bones project, I have discovered the problem - the GLES20.glFinish() call I was performing at the end of every onDrawFrame. With this in there, with a glClear but no draw calls, the FPS hovered around 40. Without the glFinish, solid 60 FPS. My old S2 had solid 60 FPS regardless.

I then went back to my game, and removed the glFinish method call, and sure enough performance went back to being perfect and there was no obvious downside to its removal.

Why was glFinish slowing down my frame rate on my new phone but not my old phone?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Esaptonor
  • 143
  • 8

1 Answers1

5

I think a speculative answer is as good as it's going to get, so — apologies for almost certainly repeating a lot of what you already know:

Commands sent to OpenGL go through three states, named relative to the GPU side of things:

  1. unsubmitted
  2. submitted but pending
  3. completed

Communicating with the code running the GPU is usually expensive. So most OpenGL implementations accept your calls and just queue the work up inside your memory space for a while. At some point it'll decide that a communication is justified and will pay the cost to transfer all the calls at once, promoting them to the submitted state. Then the GPU will complete each one (potentially out-of-order, subject to not breaking the API).

glFinish:

... does not return until the effects of all previously called GL commands are complete. Such effects include all changes to GL state, all changes to connection state, and all changes to the frame buffer contents.

So for some period when that CPU thread might have been doing something else, it now definitely won't. But if you don't glFinish then your output will probably still appear, it's just unclear when. glFlush is often the correct way forwards — it'll advance everything to submitted but not wait for completed, so everything will definitely appear shortly, you just don't bother waiting for it.

OpenGL bindings to the OS vary a lot; in general though you almost certainly want to flush rather than finish if your environment permits you to do so. If it's valid to neither flush nor finish and the OS isn't pushing things along for you based on any criteria then it's possible you're incurring some extra latency (e.g. the commands you issue one frame may not reach the GPU until you fill up the unsubmitted queue again during the next frame) but if you're doing GL work indefinitely then output will almost certainly still proceed.

Android sits upon EGL. Per the spec, 3.9.3:

... eglSwapBuffers and eglCopyBuffers perform an implicit flush operation on the context ...

I therefore believe that you are not required to perform either a flush or a finish in Android if you're double buffering. A call to swap the buffers will cause a buffer swap as soon as drawing is complete without blocking the CPU.

As to the real question, the S7 has an Adreno 530 GPU. The S2 has a Mali T760MP6 GPU. The Malis are produced by ARM, the Adrenos by Qualcomm, so they're completely different architectures and driver implementations. So the difference that causes the blocking could be almost anything. But it's permitted to be. glFinish isn't required and is a very blunt instrument; it's probably not one of the major optimisation targets.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • 2
    The Android graphics architecture pairs buffers with kernel fence objects that are signaled on completion. This means you can render, call `eglSwapBuffers()`, and have the buffer available in the system compositor before rendering has even begun... this allows the buffering and IPC mechanisms to overlap with rendering. Calling `glFinish()` prevents that from working. I doubt this is solely responsible for the dramatic reduction in frame rate, but a synchronous wait for completion is certainly not helping. https://source.android.com/devices/graphics/architecture.html – fadden Apr 08 '16 at 23:04
  • 1
    Another aspect to keep in mind is that some vendors don't actually wait for everything to finish when you call `glFinish()`. You could argue that it violates the spec, but it's still done that way. – Reto Koradi Apr 09 '16 at 16:29