In traditional 3D rendering pipelines, each stage of the rendering process is treated as a separate pass, involving multiple frame buffer operations and memory transfers. The primary stages include:
- Velocity Buffer Generation (Pass 1)
- This off-screen render pass generates a velocity texture, capturing the motion of objects in the scene. Both color and depth attachments are used to store motion data for post-processing effects like motion blur.
- Depth Shadow Map Generation (Pass 2)
- This pass creates a depth buffer to calculate dynamic shadows that are read by the main rendering pass for a real-time shadow application.
- Main Scene Rendering (Pass 3)
- The primary render pass that integrates shadow, color, depth, and stencil buffers into the final scene. It reads the shadow map generated in Pass 2 for real-time lighting effects.
- Post-Processing (Pass 4):
- This final pass applies effects such as motion blur using the velocity buffer generated in Pass 1, composites the final image to be displayed.
Inefficiencies in the Traditional Pipeline Code
A traditional pipeline, illustrated in the Figure 1 diagram, suffers from several inefficiencies, primarily due to frequent switching between frame buffers and the associated memory operations. Each pass writes data to external memory and subsequently reads it back, leading to higher memory bandwidth consumption and power usage.
// Begin off-screen shadow map rendering (first part)
glBindFramebuffer(2); // Bind FBO2 (shadow map)
glClear(GL_DEPTH_BUFFER_BIT); // Clear the depth buffer
glDrawElements(...); // Perform initial shadow map draws
...
// Begin off-screen velocity map rendering
glBindFramebuffer(1); // Bind FBO1 (velocity map)
glClear(GL_DEPTH_BUFFER_BIT); // Clear the depth buffer
glDrawElements(...); // Render velocity map data
...
// Return to shadow map rendering (second part)
glBindFramebuffer(2); // Re-bind FBO2 (shadow map)
glDrawElements(...); // Complete shadow map draws
...
// Render the main 3D scene
glBindFramebuffer(3); // Bind FBO3 (main scene)
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Clear both depth and stencil buffers
glDrawElements(...); // Render the entire 3D scene
...
// Final rendering for the window surface with motion blur
glBindFramebuffer(0); // Bind the default framebuffer (screen)
glDrawElements(...); // Render the final frame with motion blur
eglSwapBuffers(); // Present the final frame
Frequent frame buffer switches, combined with unnecessary read and write operations to external memory, cause significant overhead. On resource-constrained embedded platforms, such as those using the Arm Mali-G78AE, this inefficiency leads to increased memory usage and power consumption.