0
\$\begingroup\$

I am building terrain using a lod algorithm and deferred cascaded shadowmaps. The tessellated terrain is about 6,000,000 triangles. I tried this using 2 methods.

1st method: Both the deferred shadow program and the drawing program use tessellation shaders to tessellate the terrain. The tessellation stage for each program is exactly the same.

2nd method: I have 3 programs. The first program tessellates the terrain and stores it on the GPU using transform feedback. The second program uses the transform feedback buffer to render to shadow depth buffer. The third program uses the transform feedback buffer to draws the scene. This way I avoid tessellating the terrain twice.

To my surprise, the 2nd method was actually much slower until I started using the separation of the tessellation phase to tessellate the terrain less frequently. Now the methods are about the same fps, but in the 2nd method the terrain mesh is being updated less frequently.

Shouldnt the 2nd method be faster even if I tessellate every frame like the first method? The only thing I can think of is that the passthrough vertex shaders for the shadow and main program both have to copy 6,000,000*3 vertices even though they are just passthrough shaders. In the first method, the data would be passed right from tessellation shader to the fragment shader. So...

Does a passthrough vertex shader use the transform feedback buffer data directly, or does it copy every single vertice (even though it's the same), and use the copied data?

...
{
    out_position = in_position;
    out_normal = in_normal;
    out_uv = in_uv;
}

Is there any way I can speed this up?

Thanks in advance :)

Edit:

I should note that the example is for the shadow program, the geometry shader applies different mvp matrices, culls, and asigns layers.

In the main program, I use gl_Position=mvp*in_position; to allow gl to cull back facing triangles, but the normals and uvs are still passthrough.

\$\endgroup\$
  • 1
    \$\begingroup\$ "I should note that the example is for the shadow program, the geometry shader applies different mvp matrices, culls, and asigns layers." Then it's not a passthrough shader, now is it? \$\endgroup\$ – Nicol Bolas Feb 20 '18 at 18:54
  • \$\begingroup\$ The main program vertex shader is a passthrough shader for only normals and uvs. The shadow program vertex shader is passthrough for everything. \$\endgroup\$ – Kyy13 Feb 20 '18 at 20:05
  • \$\begingroup\$ I should add that the vertex info for the shadow program does not need clipping or culling. Every primitive is fully inside the frustum and backface culling happens manually in the geometry shader, because it is more efficient to do it there before the triangles are cascaded into layers. \$\endgroup\$ – Kyy13 Feb 20 '18 at 20:16
  • \$\begingroup\$ "backface culling happens manually in the geometry shader, because it is more efficient to do it there before the triangles are cascaded into layers." Never, ever believe that the Geometry Shader doing anything will be faster than actual hardware functionality. Not without an actual benchmark proving that this is the case. Doing frustum culling on triangles in the GS is basically throwing performance away. \$\endgroup\$ – Nicol Bolas Feb 20 '18 at 20:32
  • 1
    \$\begingroup\$ I have tested it both ways, and culling in the geometry shader is slightly faster for my application. \$\endgroup\$ – Kyy13 Feb 20 '18 at 20:36
-1
\$\begingroup\$

Does a passthrough vertex shader use the transform feedback buffer data directly, or does it copy every single vertice (even though it's the same), and use the copied data?

"the same" as what?

You told OpenGL to write 18 million vertices to a buffer object. Then you told OpenGL to render 18 million vertices which are stored in a buffer object. Twice. So that's what OpenGL is going to do.

How is it going to be able to render those 18 million vertices without reading them from memory? That's 36 million reads of vertex data. If your vertex data takes 32 bytes per vertex, that's over 1GB of data reading. Every frame.

Remember: the principle performance gain of tessellation is that you're reading and processing less data. By doing transform feedback reads of tessellated data, you're losing some of those performance benefits. Indeed, since transform feedback cannot generate optimized index lists, this will be lower in performance than if you had hand-tessellated the data and optimized it.

The reason your double-tessellation version is sometimes faster is because the cost of reading the expanded data twice (not to mention the cost of writing it) has exceeded the cost of doing the tessellation twice. And having to use a GS in your shadow render certainly isn't helping performance.

\$\endgroup\$
  • \$\begingroup\$ Transform feedback is stored on the gpu after the vertex shader. I understand that I need to do 36,000,000 reads. The transform feedback buffer is kept on the GPU after the tessellation evaluation phase, so the next logical phase would be a geometry or fragment shader. Thus, the information I need has already been "vertex shader processed", so I just write passthrough vertex shaders for the next 2 programs. Does the passthrough vertex shader copy data from gpu to gpu or just read from the buffer already on the gpu. Can I skip the vertex shading process? \$\endgroup\$ – Kyy13 Feb 20 '18 at 20:02
  • \$\begingroup\$ By skip the vertex shader, I mean only the input/output phase of the vertex shader. Not the entire vertex processing pipeline. \$\endgroup\$ – Kyy13 Feb 20 '18 at 20:12
  • \$\begingroup\$ @Kyy13: "Can I skip the vertex shading process?" Why do you think that matters? The cost is in reading the data. 1GB of data does not magically manifest itself into geometry shader registers. Regardless of who's reading that data, it must be read. And that's where your performance is going. \$\endgroup\$ – Nicol Bolas Feb 20 '18 at 20:29
  • \$\begingroup\$ Because out_position = in_position is one read, and whatever the vertex shader does with out_position is more reads. Skipping the first read would be faster, but it requires the vertex shader to work with in_position directly, which is just a pointer and offset to the transform feedback buffer already on the gpu. \$\endgroup\$ – Kyy13 Feb 20 '18 at 20:41
  • \$\begingroup\$ @Kyy13: "which is just a pointer and offset to the transform feedback buffer already on the gpu." No, it's not. The variable is data fetched from that buffer. By the time the VS gets the in variable, the memory read has already happened. Even on AMD hardware, where VAOs have to be compiled into the VS, by the time your VS code executes, the data has already been fetched. And the same would be true if the VS could be skipped. The cost of reading from the buffer cannot be avoided, whether the VS or the GS were the top of the pipe. \$\endgroup\$ – Nicol Bolas Feb 20 '18 at 20:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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