Class NVXDeviceGeneratedCommands



  • public class NVXDeviceGeneratedCommands
    extends java.lang.Object
    This extension allows the device to generate a number of critical commands for command buffers.

    When rendering a large number of objects, the device can be leveraged to implement a number of critical functions, like updating matrices, or implementing occlusion culling, frustum culling, front to back sorting... Implementing those on the device does not require any special extension, since an application is free to define its own data structure, and just process them using shaders.

    However, if the application desires to quickly kick off the rendering of the final stream of objects, then unextended Vulkan forces the application to read back the processed stream and issue graphics command from the host. For very large scenes, the synchronization overhead, and cost to generate the command buffer can become the bottleneck. This extension allows an application to generate a device side stream of state changes and commands, and convert it efficiently into a command buffer without having to read it back on the host.

    Furthermore, it allows incremental changes to such command buffers, by manipulating only partial sections of a command stream, for example pipeline bindings. Unextended Vulkan requires re-creation of entire command buffers in such scenario, or updates synchronized on the host.

    The intended usage for this extension is for the application to:

    • create its objects as in unextended Vulkan
    • create a VkObjectTableNVX, and register the various Vulkan objects that are needed to evaluate the input parameters.
    • create a VkIndirectCommandsLayoutNVX, which lists the VkIndirectCommandsTokenTypeNVX it wants to dynamically change as atomic command sequence. This step likely involves some internal device code compilation, since the intent is for the GPU to generate the command buffer in the pipeline.
    • fill the input buffers with the data for each of the inputs it needs. Each input is an array that will be filled with an index in the object table, instead of using CPU pointers.
    • set up a target secondary command buffer
    • reserve command buffer space via CmdReserveSpaceForCommandsNVX in a target command buffer at the position you want the generated commands to be executed.
    • call CmdProcessCommandsNVX to create the actual device commands for all sequences based on the array contents into a provided target command buffer.
    • execute the target command buffer like a regular secondary command buffer

    For each draw/dispatch, the following can be specified:

    • a different pipeline state object
    • a number of descriptor sets, with dynamic offsets
    • a number of vertex buffer bindings, with an optional dynamic offset
    • a different index buffer, with an optional dynamic offset

    Applications should register a small number of objects, and use dynamic offsets whenever possible.

    While the GPU can be faster than a CPU to generate the commands, it may not happen asynchronously, therefore the primary use-case is generating "less" total work (occlusion culling, classification to use specialized shaders...).

    Example Code

    Open-Source samples illustrating the usage of the extension can be found at the following locations:

    https://github.com/nvpro-samples/gl_vk_threaded_cadscene/blob/master/doc/vulkan_nvxdevicegenerated.md

    https://github.com/NVIDIAGameWorks/GraphicsSamples/tree/master/samples/vk10-kepler/BasicDeviceGeneratedCommandsVk

       // setup secondary command buffer
         vkBeginCommandBuffer(generatedCmdBuffer, &beginInfo);
         ... setup its state as usual
     
       // insert the reservation (there can only be one per command buffer)
       // where the generated calls should be filled into
         VkCmdReserveSpaceForCommandsInfoNVX reserveInfo = { VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX };
         reserveInfo.objectTable = objectTable;
         reserveInfo.indirectCommandsLayout = deviceGeneratedLayout;
         reserveInfo.maxSequencesCount = myCount;
         vkCmdReserveSpaceForCommandsNVX(generatedCmdBuffer, &reserveInfo);
     
         vkEndCommandBuffer(generatedCmdBuffer);
     
       // trigger the generation at some point in another primary command buffer
         VkCmdProcessCommandsInfoNVX processInfo = { VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX };
         processInfo.objectTable = objectTable;
         processInfo.indirectCommandsLayout = deviceGeneratedLayout;
         processInfo.maxSequencesCount = myCount;
         // set the target of the generation (if null we would directly execute with mainCmd)
         processInfo.targetCommandBuffer = generatedCmdBuffer;
         // provide input data
         processInfo.indirectCommandsTokenCount = 3;
         processInfo.pIndirectCommandsTokens = myTokens;
     
       // If you modify the input buffer data referenced by VkCmdProcessCommandsInfoNVX,
       // ensure you have added the appropriate barriers prior generation process.
       // When regenerating the content of the same reserved space, ensure prior operations have completed
     
         VkMemoryBarrier memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
         memoryBarrier.srcAccessMask = ...;
         memoryBarrier.dstAccessMask = VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX;
     
         vkCmdPipelineBarrier(mainCmd,
                              // srcStageMaskVK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
                              // dstStageMaskVK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX,
                              // dependencyFlags0,
                              // memoryBarrierCount1,
                              // pMemoryBarriers&memoryBarrier,
                              ...);
     
         vkCmdProcessCommandsNVX(mainCmd, &processInfo);
         ...
       // execute the secondary command buffer and ensure the processing that modifies command-buffer content
       // has completed
     
         memoryBarrier.srcAccessMask = VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX;
         memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
     
         vkCmdPipelineBarrier(mainCmd,
                              // srcStageMaskVK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX,
                              // dstStageMaskVK_PIPELINE_STAGE_DRAW_INDIRECT_BIT,
                              // dependencyFlags0,
                              // memoryBarrierCount1,
                              // pMemoryBarriers&memoryBarrier,
                              ...)
         vkCmdExecuteCommands(mainCmd, 1, &generatedCmdBuffer);
    Name String
    VK_NVX_device_generated_commands
    Extension Type
    Device extension
    Registered Extension Number
    87
    Revision
    3
    Extension and Version Dependencies
    • Requires Vulkan 1.0
    Contact
    • Christoph Kubisch @pixeljetstream
    Last Modified Date
    2017-07-25
    Contributors
    • Pierre Boudier, NVIDIA
    • Christoph Kubisch, NVIDIA
    • Mathias Schott, NVIDIA
    • Jeff Bolz, NVIDIA
    • Eric Werness, NVIDIA
    • Detlef Roettger, NVIDIA
    • Daniel Koch, NVIDIA
    • Chris Hebert, NVIDIA