Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

In "modern" environments, the "NV Occlusion Query" extension provides a method to get the number of fragments which passed the depth test. However, on the iPad / iPhone using OpenGL ES, the extension is not available.

What is the most performant approach to implement a similar behaviour in the fragment shader?

Some of my ideas:

  • Render the object completely in white, then count all the colors together using a two-pass shader where first a vertical line is rendered and for each fragment the shader computes the sum over the whole row. Then, a single vertex is rendered whose fragment sums all the partial sums of the first pass. Doesn't seem to be very efficient.

  • Render the object completely in white over a black background. Downsample recursively, abusing the hardware linear interpolation between textures until being at a reasonably small resolution. This leads to fragments which have a greyscale level depending on the number of white pixels where in their corresponding region. Is this even accurate enough?

  • Use mipmaps and simply read the pixel on the 1x1 level. Again the question of accuracy and if it is even possible using non-power-of-two textures.

The problem wit these approaches is, that the pipeline gets stalled which results in major performance issues. Therefore, I'm looking for a more performant way to accomplish my goal.

Using the EXT_OCCLUSION_QUERY_BOOLEAN extension

Apple introduced EXT_OCCLUSION_QUERY_BOOLEAN in iOS 5.0 for iPad 2.

"4.1.6  Occlusion Queries

Occlusion queries use query objects to track the number of fragments or 
samples that pass the depth test. An occlusion query can be started and 
finished by calling BeginQueryEXT and EndQueryEXT, respectively, with a 
target of ANY_SAMPLES_PASSED_EXT or ANY_SAMPLES_PASSED_CONSERVATIVE_EXT.

When an occlusion query is started with the target 
ANY_SAMPLES_PASSED_EXT, the samples-boolean state maintained by the GL is
set to FALSE. While that occlusion query is active, the samples-boolean 
state is set to TRUE if any fragment or sample passes the depth test. When 
the occlusion query finishes, the samples-boolean state of FALSE or TRUE is
written to the corresponding query object as the query result value, and 
the query result for that object is marked as available. If the target of 
the query is ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, an implementation may 
choose to use a less precise version of the test which can additionally set
the samples-boolean state to TRUE in some other implementation dependent 
cases."

The first sentence hints on a behavior which is exactly what I'm looking for: getting the number of pixels which passed the depth test in an asynchronous manner without much performance loss. However, the rest of the document describes only how to get boolean results.

Is it possible to exploit this extension to get the pixel count? Does the hardware support it so that there may be hidden API to get access to the pixel count?

Other extensions which could be exploitable would be debugging features like the number of times the fragment shader was invoked (PSInvocations in DirectX - not sure if something simila is available in OpenGL ES). However, this would also result in a pipeline stall.

share|improve this question
    
What's the end effect you're trying to achieve? –  Tetrad Nov 24 '11 at 17:36
    
I need the pixel count not for a rendering effect, but for computation. I want to use it to calculate weights of particles in a particle filter. –  Etan Nov 24 '11 at 23:07
    
Does apple support AMD_performance_monitor? If yes, you may be able to find the counter that shows passed pixels. Do note that this is very, very device specific even if you get it to work. –  Jari Komppa Nov 26 '11 at 21:02
    
The problem with AMD_performance_monitor would be that it is not asynchronous, so the performance loss would also be there since I have to wait between frames to know that the invocations from the next frame won't have effect to the performance counters already. –  Etan Nov 27 '11 at 0:24
    
Also, AMD_performance_monitor is not available. –  Etan Nov 28 '11 at 14:41
show 1 more comment

2 Answers

Is it possible to exploit this extension to get the pixel count? Does the hardware support it so that there may be hidden API to get access to the pixel count?

No, and no. Well, I supposed if you draw a series of one-pixel-sized triangle in window-space, you could count how many boolean values you get. But that would require a separate query for each one pixel. Probably not the fastest thing in the world.

If there's a "hidden API", you won't have access to it (since it's hidden), so it doesn't matter. Furthermore, the nature of the extension already suggests that there isn't. After all, if the hardware had the actual fragment count, why not just expose it directly, the way desktop OpenGL does? If the hardware supported it, they could have just taken ARB_occlusion_query and used that.

But they didn't. Which strongly suggests that they couldn't.

share|improve this answer
    
Thanks for the answer! The approach with one-pixel sized triangles can be improved by simply rendering points or small lines, and it could even be improved by a logarithmic scaling where a line which returns "true" is simply split in the middle and then both parts checked again. Not sure about the performance yet as the models won't have more than 100-200 vertices. The reasoning for the non-existence of the "hidden API" is also valid. Are there any other means to get data from the GPU back to the CPU in an asynchronous manner without a pipeline stall (also hacky ways please)? –  Etan Nov 28 '11 at 21:49
add comment

The results are returned in a GLuint, (also can be GLint for a different call) unfortunately, the result is always a 0 or a 1, maybe they will change it to register frags in the future.

also it looks like not a single person on the entire internet has posted about these new extensions... so here they are set up properly...which is not documented anywhere as far as i can tell... you can imagine how they would go in your code from this little sudo code here:

import UIKit/UIKit.h

import GLKit/GLKit.h

import "GLProgram.h"

GLuint testBox,hasBeenTested,theParams;

//...

glGenQueriesEXT(1, &testBox);

glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, testBox);


//... draw an object .......

glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);

glGetQueryObjectuivEXT(testBox, GL_QUERY_RESULT_AVAILABLE_EXT, &hasBeenTested);

if (hasBeenTested) glGetQueryObjectuivEXT(testBox, GL_QUERY_RESULT_EXT, &theParams);

glDeleteQueriesEXT(1, &testBox);

if (!theParams)  object is hidden;  don't draw next time;
share|improve this answer
    
Just because the type says GLint does not mean that it is an integer count of the number of passing fragments. The specification is quite clear that the QUERY_RESULT_EXT state is a boolean value; you just query it as an integer one. It is GL_FALSE if it failed and not GL_FALSE if it passed. –  Nicol Bolas Dec 22 '11 at 0:04
    
I had just implemented the code and found it was just a true or false and changed my answer before i saw your comment... since it is a gluint, maybe they will add states in the future hopefully. –  honjj Dec 22 '11 at 0:32
    
by the way, there appears to be a bug in GKKit, where if you use self.effect2 = [[GLKBaseEffect alloc] init]; type of GLKit type of code to render an object, rather than a normal GLES2.0 pipeline, the queries will fail to give you the correct answer... (never hidden) although i tested a pipeline object hiding a GLKBaseEffect object, so it could be there is a bug just mixing the two, i did not test further yet... –  honjj Dec 22 '11 at 0:43
    
The extensions are explained with examples in the WWDC videos which can be found on Apples site with an active developer account. –  Etan Dec 22 '11 at 8:51
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.