This sample demonstrates how to render enormous amounts of geometry by generating not only animation data but even command buffers in multiple threads using Vulkan.


APIs Used

  • Vulkan 1.0

Shared User Interface

The Graphics samples all share a common app framework and certain user interface elements, centered around the "Tweakbar" panel on the left side of the screen which lets you interactively control certain variables in each sample.

To show and hide the Tweakbar, simply click or touch the triangular button positioned in the top-left of the view.

Technical Details


This sample implements techniques for utilizing multiple threads, along with instanced meshes, to render a high number of moving objects with minimal draw calls.

The application uses multiple threads to perform tasks such as:
  • Animation
  • Flocking Simulation
  • Generating CommandSets for rendering

This frees up the main thread to simply submit the generated CommandSets and perform the final buffer swap.

Algorithm overview

The application maintains pools of threads, each specialized to a particular task, such as animation or rendering. Each thread is specialized by associating them with the AnimateJobFunction or RenderJobFunction, respectively.

The application is structured such that each School of fish is a mostly self-contained set of data and methods. This allows threads within the application to simply call a method on the School to perform the work for that thread with minimal synchronization.

When a School's Update() method is called, any animation, including the flocking simulation, is performed on the School and its fish. Once all of the new state is calculated, the School's InstanceData buffer is updated in preparation for rendering. The InstanceData buffer contains any per-fish data required by the shader, including the fish's position and heading. Once the Update() call is complete, the School is handed off to the Rendering queue to be serviced by an available Rendering thread.

When a School's Render() method is called, the School creates a CommandBuffer containing the commands required to bind a Uniform Buffer Object, containing school-specific data used by the shader, as well as the commands required to render the model associated with the School. The model contained in the School is actually an "Instanced" model, in that each of its component meshes are rendered using the CommandBuffer's vkCmdDrawIndexed() method along with the InstanceData that was filled in during the School's Update() call. With this method, each mesh is rendered once for each entry in the InstanceData buffer. Since there is one entry per fish in the school, this results in the mesh being rendered once for each fish in the school. If the model contains only a single mesh, then this allows every fish in the School to be drawn with a single Draw call. If the model contains more than one mesh, then the entire school will be drawn in a number of Draw calls equal to the number of meshes in the model.

Once all of the Rendering threads have completed, the resultant CommandBuffers are submitted for rendering and the render buffer is swapped to the front.

UI Options
  • Number of Schools: Changes the number of Schools currently active in the world.
  • Fish per School: Changes the number of fish rendered in each School.
  • Max Roam Distance: Changes the the "soft" limit of the size of the "fishbowl".
  • Draw using GLES3 AEP: Only available on NVIDIA devices, switches live from rendering using Vulkan to rendering using OpenGL ES.
  • Threaded Rendering: In Vulkan mode, changes whether the command buffers for rendering are generated per animation thread, or all on the main thread.
  • Number of worker threads: Changes the number of OS threads used by the app to animate and render.
  • Requested Batch Size: Sets the number of fish rendered per draw call. Decreasing this simulates more individual fish and shows the low overhead of Vulkan better.
  • Actual Batch Size: Displays the number of fish rendered per draw call. This may be smaller than the requested size if the number of fish per school is smaller than the requested batch size.
  • Pause Animation: Pauses the animation and movement of all fish.
  • Use Avoidance: Enables the more expensive, O(N^2) CPU animation mode.
  • Flocking complexity: Trades off between smoother flocking and low CPU overhead in animation.
  • Reset Schools: Selecting a type of reset from this dropdown will set all active schools (and the camera) to the state indicated.
    • Fish Fireworks: Chooses a random location for each school and moves all of its fish to that spot. Also sets the camera to the center of the aquarium.
    • Fishsplosion: Moves all of Fish in all Schools to the center of the aquarium and places the camera at a point looking at that location.
  • Cycle Stats: Cycles through minimal, detailed and no stats in the upper-right corner of the screen.