7
\$\begingroup\$

Currently, I'm profiling my iOS. All the calls to glDrawArrays are expensive. For example, here is a screenshot of the rendering part of a particle system:

Time Profiler

As you can see, the glDrawArrays call takes from that method 39% of the CPU time. While other calls like glUniform1i and glBindVertexArrayOES are very quick. Why isn't glDrawArrays as quick as the others are? Aren't all these methods starting with gl*** short functions that will add a command to the driver queue? So, why does it take longer for a glDrawArrays call to be added to the queue, in comparation with all the other gl calls?

Is this normal, or does this mean that I'm doing something wrong?


In response to doug: Nope, that didn't work. The glFinish() call actually took a while, but the glDrawArrays call is still taking much time in comparation with the state changes (like glBindVertexArrayOES).

With glFinish

\$\endgroup\$
3
  • \$\begingroup\$ glUniform1i and glBindVertexArrayOES really don't do anything, they more or less just update the state machine. Only actual draw calls take action on state. \$\endgroup\$
    – doug65536
    Commented Jan 5, 2013 at 10:32
  • \$\begingroup\$ How many times is glDrawArray being called in 1 frame? \$\endgroup\$
    – 5ound
    Commented Feb 5, 2013 at 16:59
  • \$\begingroup\$ I have counted the glDrawArray calls using an Xcode OpenGL ES Frame Capture. In that frame, I had 47 glDrawArray calls. \$\endgroup\$ Commented Feb 6, 2013 at 11:36

3 Answers 3

8
\$\begingroup\$

The way most drivers operate is by using a "lazy state changes" model. What this means is that the vast majority of your gl* calls will actually do nothing much more than recording a state, storing off some parameters, then return immediately. This works perfectly fine up until a gl* call is made that actually needs to do something with all that state (or that depends on the result of some other previously made call); at that point in time all the previously buffered-up state needs to be gathered together and flushed before the call can be made.

So what you're seeing is perfectly normal, if slightly misleading, behaviour: the time recorded for glDrawArrays is not actually the time taken by glDrawArrays alone, but also includes time for actually committing a whole bunch of previous GL calls.

There's a discussion of this for D3D available here but the same basic principle will apply to any modern implementation of either API. The key point to take away from it is: "in general, trying to profile your GPU by timing the CPU is going to be confusing and misleading".

\$\endgroup\$
1
\$\begingroup\$

You may be seeing the effects of queueing. Most likely the command queue blocks at that call because, for example, the queue is too full or you are getting ahead of vsync. It is probably doing a bunch of work from before the glDrawArrays call.

You could try putting a glFinish right before glDrawArrays, just to see if it is the commands in the queue taking the time or actually the glDrawArrays call. If the queueing effects are your actual performance bottleneck, it will blame glFinish instead of the innocent glDrawArrays call.

On the other hand, glDrawArrays may indeed be using a lot of CPU. I would try the above to be sure you're looking at the right thing.

EDIT: glFinish, NOT glFlush

I AM NOT SAYING LEAVE THE glFlush CALL THERE. Come on guys, do you really think I am saying leave the glFlush call there? Its a performance investigation.

\$\endgroup\$
3
  • \$\begingroup\$ Added a response in my question body. \$\endgroup\$ Commented Jan 5, 2013 at 8:47
  • \$\begingroup\$ Good, it does offload a lot of the time it appeared to be taking in glDrawArrays. On second look, you're doing something very bad: you're uploading data and then IMMEDIATELY using it. Never do that, what is probably happening is it is blocking waiting for the buffersubdata to finish. Do that way ahead of time, like in the previous frame and use double buffers and alternate. The blocking may very well be an actual polling spinloop, because the wait time would be really short. \$\endgroup\$
    – doug65536
    Commented Jan 5, 2013 at 10:09
  • \$\begingroup\$ Remember, just because glBufferSubData returned, doesn't mean it is in video memory. It may just be copied to some DMA-capable memory range and uploaded asynchronously, allowing the glBufferSubData call to return early. AFAIK drivers do do that. \$\endgroup\$
    – doug65536
    Commented Jan 5, 2013 at 10:23
0
\$\begingroup\$

Naive implementation of just writing to a command buffer on every gl call makes little sense, when the driver can possibly optimize out state changes. Thus, it makes sense to postpone processing as late as possible, especially on a tiled/binned rendering architecture, which most OpenGL ES implementations are.

On some architectures it actually makes sense to do some shader processing as late as on a glDrawArrays call. (And yes, even your ES1.x hardware likely has some form of shaders under the hood).

Calling glFinish/glFlush like doug65536 suggests won't help on tiled/binned architectures, as it causes a total pipeline flush which has a very, very, VERY heavy overhead on such - causing the rendering of everything queued so far to every tile and forcing a unresolve on the next render cycle.

\$\endgroup\$
10
  • \$\begingroup\$ It looks like you know what you are talking about. But I'm missing an answer to my question. Is this behavior normal? Are GPU and CPU working simultaneously on an iPod Touch 4th gen / iPhone 4S? If they are working simultaneously (what I suspect by seeing the results of adding a glFinish() call), then what takes it so long for the glDrawArrays call? \$\endgroup\$ Commented Jan 5, 2013 at 9:05
  • \$\begingroup\$ I'm not intimately familiar with the internals of the powervr driver, nor do I have benchmark data from various devices to back it, but I'd say it's normal. What you're seeing on the glFinish() call is lots of more work than what you'd expect if this was a direct renderer. \$\endgroup\$ Commented Jan 5, 2013 at 9:10
  • \$\begingroup\$ @MartijnCourteaux Please note that I did NOT say to leave the glFinish call there. I asked him to redo the profile with the glFinish call there to see if it was prior enqueued work that was really taking up the CPU. Notice how in his new profile, glDrawArrays is taking a lot less time. That's mainly because the glFinish call is now doing all the enqueued work. \$\endgroup\$
    – doug65536
    Commented Jan 5, 2013 at 10:05
  • \$\begingroup\$ @JariKomppa I remember ATI drivers spinning in some scenarios because the wait time would be very short and the latency of an IRQ and DPC and all that would be too long. It is possible that the driver is spinning on some "ready" bit in some register deep inside the glFinish implementation, even with full hardware accelerated OpenGL. \$\endgroup\$
    – doug65536
    Commented Jan 5, 2013 at 10:19
  • \$\begingroup\$ @doug65536 remember: this is a "scene capture" architecture. glFinish will cause rendering of all captured geometry, along with tons of other stuff. This is much heavier than just waiting for a timestamp. \$\endgroup\$ Commented Jan 5, 2013 at 10:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .