openglgeometry-shader

Does OpenGL support multiple geometry shaders in series?


I wish to pipe 2 geometry shaders. The first one takes one point and releases multiple points. The second one takes one of those points and releases a rectangle to fragment shader.

Geometry shader 1:

layout (points) in;
layout (points, max_vertices = 16) out;

in GEO1 {
    uint name_tile_atlas;
    uint name_tile_map;
    uint subtiles;
} geo1[];

out GEO2 {
    vec2 pos_ones_atlas;
    vec2 pos_ones_target;
} geo2;

void main {...}

Geometry shader 2:

layout (points) in;
layout (triangle_strip, max_vertices = 4) out;

in GEO2 {
    vec2 pos_ones_atlas;
    vec2 pos_ones_target;
} geo2[];

out vec2 frag_uv;

void main {...}

I naively attached those geometry shaders to one program, and they go to a single geometry stage which can only have one main function. Is there any way to pipe these shaders?
I can merge them by moving the code in shader 2 into a for-loop, but I don't want to loop 16 times for parallellizable logic, and this gets worse when piping an arbitrary number of geometry shaders...


Solution

  • Daisy chaining multiple Geometry Shaders is not possible, as OpenGL uses a predetermined rendering pipeline with one (optional) Geometry Shader stage. However, using two Geometry Shaders in series is effectively possible using two drawing commands, Transform Feedback and an extra buffer.

    The procedure is like this:

    1. Create a Buffer Object ("GS1 output") to store the output of Geometry Shader 1.
    2. Turn off rasterization (glEnable(GL_RASTERIZER_DISCARD)).
    3. Begin a Primitive Query with the target GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to keep track of the number of primitives written by Geometry Shader 1 (we need this later).
    4. Begin Transform Feedback and issue the first drawing command using the existing vertex shader and Geometry Shader 1 (and no Fragment Shader). Capture its output in "GS1 output".
    5. End Transform Feedback
    6. End the Primitive Query and the retrieve the number of primitives.
    7. Turn on rasterization (glDisable(GL_RASTERIZER_DISCARD)).
    8. Issue a second drawing command with a passthrough Vertex Shader, Geometry Shader 2 and the existing Fragment Shader. Use the contents of "GS1 output" as input vertices (points) and the number of primitives as the number of indices.

    The Primitive Query (and thus a host-device-host data transfer) can be avoided by using Transform Feedback Rendering, which is part of core OpenGL since 4.0, but is often available in OpenGL 3.x implementations as an extension.

    This is all assuming that you need the capability of Geometry Shaders to ouput a dynamic number of primitives. If each invokation outputs the same number of primitives (or if this number is dynamic but easily computed), there is probably a better implementation available, such as using Transform Feedback with only a Vertex Shader, or using a Compute Shader (OpenGL >=4.3).

    Source used for OpenGL all specifications and usage: Khronos OpenGL Wiki