Processing math: 0%
\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{\,\mathrm{#1}}

Inherited Viewport Scissor Sample

Inherited Viewport Scissor Sample
Contents

(Top)
Introduction
Sample Background
Extension
Extension Integration

by David Zhao Akeley

This sample provides an example of how indirect draw commands and the optional VK_NV_inherited_viewport_scissor extension can be used to maximize secondary command buffer re-use. The indirect draw commands allow a pre-recorded subpass secondary command buffer to deal with varying model counts and positions, while the extension helps deal with changing viewport sizes (due to the user resizing the window, etc.). The extension is not required to run the sample; the sample can still be used in this case as a simple example of secondary command buffer re-use.

Repository Link

   

Introduction

In core Vulkan (at time of writing), secondary command buffers do not inherit any state from the calling primary command buffer except for the active render pass and subpass. This means that secondary command buffers must set the dynamic viewport and scissor state themselves, and typically must be re-recorded with new state when the window is resized, even though all the other commands are unchanged. The extension provides a way to avoid this inefficiency.

   

Sample Background

See inherited.cpp

Without the extension enabled, the sample is able to re-use a secondary subpass command buffer only when the viewport that the command buffer is meant to draw to has not been resized. A command buffer is paired with the VkViewport state it was recorded with in the SubpassSecondary struct. The App::needNewSecondaryCmdBuf function detects when the secondary command buffer must be re-recorded, which is when:

The scissor is deduced from the viewport, so the sample doesn't track stale scissor state. Most applications also don't vary the depth bounds dynamically and can skip the “depth bounds changed” check.

As each of the four viewports on screen needs different viewport/scissor state, one secondary command buffer is needed for each one; they are identical except for different vkCmdSetViewport and vkCmdSetScissor commands. These secondary command buffers are stored in App::m_subpassSecondaryArray.

   

Extension

The extension defines the following extension structure for VkCommandBufferInheritanceInfo:

// Provided by VK_NV_inherited_viewport_scissor
typedef struct VkCommandBufferInheritanceViewportScissorInfoNV {
    VkStructureType      sType;
    const void*          pNext;
    VkBool32             viewportScissor2D;
    uint32_t             viewportDepthCount;
    const VkViewport*    pViewportDepths;
} VkCommandBufferInheritanceViewportScissorInfoNV;

If the extension structure is present with viewportScissor2D true, then the restriction on inheriting viewport and scissor state is relaxed. The spec describes the requirements precisely, but basically, the viewport and scissor state is that set by the most-recently executed viewport/scissor setting command, subject to the restrictions:

Additionally, secondary command buffers that enable viewport/scissor inheritance are specifically forbidden from setting the inherited state themselves.

To be clear, the viewports in pViewportDepths are used only for their minDepth and maxDepth values; x, y, width, and height are ignored. We decided to re-use the VkViewport struct (rather than defining a new “depth only” viewport structure) as that allows more code reuse between the VK_NV_inherited_viewport_scissor code path and the fallback code path.

   

Extension Integration

Start by checking if the extension is supported by checking for the VK_NV_INHERITED_VIEWPORT_SCISSOR_EXTENSION_NAME string and placing a VkPhysicalDeviceInheritedViewportScissorFeaturesNV structure on the pNext chain of VkPhysicalDeviceFeatures2, as usual.

To enable viewport/scissor inheritance for a secondary command buffer, add a VkCommandBufferInheritanceViewportScissorInfoNV structure to the pNext chain of VkCommandBufferInheritanceInfo with viewportScissor2D == VK_TRUE. This is done in App::replaceSecondaryCmdBuf in the sample.

    // The extension struct needed to enable inheriting 2D viewport+scisors.
    VkCommandBufferInheritanceViewportScissorInfoNV inheritViewportInfo {
        VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV,
        nullptr,      // pNext
        VK_TRUE,      // viewportScissor2D
        1,            // viewportDepthCount
        &viewport     // pViewportDepths
    };

When this inheritance is done, we also must not set the viewport and scissor state in the secondary command buffer, as would otherwise usually be done.

The sample records the fact that this inheritance is enabled for the command buffer in SubpassSecondary::inheritViewportScissor; if this is true, there is no longer any need to match the target viewport's 2D size, and we skip the matching-2D-rectangle check in App::needNewSecondaryCmdBuf.

The depth range check must still remain, due to the pViewportDepth-matching requirement discussed earlier.

Finally, in App::doFrame, when viewport/scissor inheritance is enabled, we:

The sample does this in the calling primary command buffer (re-recorded every frame), but, it is also allowed to set the state in an earlier secondary command buffer in the same vkCmdExecuteCommands call.

That's about it. When the extension is supported, you can tap the i key to toggle viewport/scissor inheritance. The sample logs when it recreates the framebuffer or re-records the secondary command buffer. You can see that when inheritance is disabled, both the secondary command buffer and framebuffer are recreated every time the window changes size, but when inheritance is enabled, only the framebuffer is recreated upon window resize:

formatted by Markdeep 1.16