Turning Off Rasterization
So far, you have seen that transform feedback is a mechanism to save the intermediate results of vertex or geometry shaders while
OpenGL is rendering. But, what if you don’t want to actually draw
anything? What if you only want to use transform feedback on its own
without changing the contents of the screen? This is the kind of thing
you may want to do if you’re using the vertex shader for computation
other than geometry processing (physical simulation, for example). It is
possible to use transform feedback for this purpose by turning off
rasterization. This means that the vertex and geometry shaders will
still run so that transform feedback will work, but the OpenGL pipeline
will be chopped off after that and the fragment shader will not run at
all. This is therefore more efficient than simply making a fragment
shader that discards everything, or turning off color writes with glColorMask, for example. To turn off rasterization, you actually need tell to OpenGL that it should discard all rasterization by calling
glEnable(GL_RASTERIZER_DISCARD);
To turn rasterization back on, simply call
glDisable(GL_RASTERIZER_DISCARD);
When GL_RASTERIZER_DISCARD
is enabled, anything produced by the vertex or geometry shader (if
present) does not create any fragments, and the fragment shader never
runs. If you turn off rasterization and do not use transform feedback
mode, the OpenGL pipeline is essentially turned off.
Counting Vertices Using Primitive Queries
When a vertex shader but no
geometry shader is present, the output from the vertex shader is
recorded, and the number of vertices stored into the transform feedback
is the same as the number of vertices sent to OpenGL unless the
available space in any of the transform feedback buffers is exhausted.
If a geometry shader is present, that shader may create or discard
vertices and so the number of vertices written to the transform feedback
buffer may be different than the number of vertices sent to OpenGL.
OpenGL can keep track of the number of vertices written to the transform
feedback buffers through query objects. The application can then use
this information to draw the resulting data or to know how much to read
back from the transform feedback buffer, should it want to keep the
data.
As before, to generate a query object, call
glGenQueries(1, &one_query);
or to generate a number of query objects, call
glGenQueries(10, ten_queries);
Now that you have created
your query objects, you can ask OpenGL to start counting primitives as
it produces them by beginning a GL_PRIMITIVES_GENERATED or GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query by beginning the query of the appropriate type. To start either query, call
glBeginQuery(GL_PRIMITIVES_GENERATED, one_query);
or
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, one_query);
After a call to glBeginQuery with either GL_PRIMITIVES_GENERATED or GL_TRANSFORM_FEEDBACK_PRIMTIVES_WRITTEN,
OpenGL keeps track of how many primitives were produced by the vertex
or geometry shader, or how many were actually written into the transform
feedback buffers until the query is ended using
glEndQuery(GL_PRIMITIVES_GENERATED);
or
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
The results of the query can be read by calling glGetQueryObjectuiv with the GL_QUERY_RESULT
parameter and the name of the query object. As with other OpenGL
queries, the result might not be available immediately because of the
pipelined nature of OpenGL. To find out if the results are available,
call glGetQueryObjectuiv with the GL_QUERY_RESULT_AVAILABLE parameter.
There are a couple of subtle differences between the GL_PRIMITIVES_GENERATED and GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN queries. The first is that the GL_PRIMITIVES_GENERATED query counts the number of primitives emitted by the geometry shader, but the GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
query only counts primitives that were successfully written into the
transform feedback buffers. The primitive count generated by the
geometry shader may be more or less than the number of primitives sent
to OpenGL, depending on what it does. Normally, the results of these two
queries would be the same, but if not enough space is available in the
transform feedback buffers, GL_PRIMITIVES_GENERATED will keep counting, but GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN will stop.
You can check whether all of the
primitives produced by your application were captured into the
transform feedback buffer by running one of each query simultaneously
and comparing the results. If they are equal, then all the primitives
were successfully written. If they differ, the buffers you used for
transform feedback were probably too small.
The second difference is that GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN is only meaningful when transform feedback is active. That is why it has TRANSFORM_FEEDBACK in its name but GL_PRIMITIVES_GENERATED does not. If you run a GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query when transform feedback is not active, the result will be zero. However, the GL_PRIMITIVES_GENERATED
query can be used at any time and will produce a meaningful count of
the number of primitives produced by OpenGL. You can use this to find
out how many vertices your geometry shader produced or discarded.
Using the Results of a Primitive Query
Now you have the results of
your vertex or geometry shader stored in a buffer. You also know how
much data is in that buffer by using a query object. Now it’s time to
use those results
in further rendering. Remember that the results of the vertex or
geometry shader are placed into a buffer using transform feedback. The
only thing making the buffer a transform feedback buffer is that it’s
bound to one of the GL_TRANSFORM_FEEDBACK_BUFFER binding points. However, buffers in OpenGL are generic chunks of data and can be used for other purposes.
Generally, after running
a rendering pass that produces data into a transform feedback buffer,
you bind the buffer object to the GL_ARRAY_BUFFER
binding point so that it can be used as a vertex buffer. If you are
using a geometry shader that might produce an unknown amount of data,
you need to use a GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query to figure out how many vertices to render on the second pass. Listing 1 shows an example of what such code might look like.
Listing 1. Drawing Data Written to a Transform Feedback Buffer
// We have two buffers, buffer1 and buffer2. First, we'll bind buffer1 as the
// source of data for the draw operation (GL_ARRAY_BUFFER), and buffer2 as
// the destination for transform feedback (GL_TRANSFORM_FEEDBACK_BUFFER).
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFFER, buffer2);
// Now, we need to start a query to count how many vertices get written to
// the transform feedback buffer
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, q);
// Ok, start transform feedback...
glBeginTransformFeedback(GL_POINTS);
// Draw something to get data into the transform feedback buffer
DrawSomePoints();
// Done with transform feedback
glEndTransformFeedback();
// End the query and get the result back
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
glGetQueryObjectuiv(q, GL_QUERY_RESULT, &vertices_to_render);
// Now we bind buffer2 (which has just been used as a transform
// feedback buffer) as a vertex buffer and render some more points
// from it.
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glDrawArrays(GL_POINTS, 0, vertices_to_render);
|