NVRHI (NVIDIA Rendering Hardware Interface)

repository·main·Indexed 24 days ago

https://github.com/nvidia-rtx/nvrhi

A high-level graphics abstraction library providing a unified interface for Direct3D 11, Direct3D 12, and Vulkan 1.3. NVRHI simplifies modern graphics programming by automating resource lifetime management, state tracking, and barrier placement. It supports Graphics, Compute, Ray Tracing, and Meshlet pipelines, and offers optional integration with NVAPI and RTXMU for advanced acceleration structure management.

Tokens
16.1K
Snippets
9
Records
77
Agent score
83%

What's inside NVRHI

  1. What is NVRHI?

    main
    NVRHI (NVIDIA Rendering Hardware Interface) is a library that provides a common abstraction layer over multiple graphics APIs (GAPIs), specifically Direct3D 11, Direct3D 12, and Vulkan 1.3. It is designed to facilitate the portability of rendering code across different APIs while providing high-performance features like automatic resource state tracking, deferred resource destruction, and parallel command list recording. It supports Graphics, Compute, Ray Tracing, and Meshlet pipelines.
  2. Ray Tracing Support Overview

    main

    NVRHI provides hardware-accelerated ray tracing support for both Vulkan and D3D12. It supports two primary methods:

    1. Ray Tracing Pipelines: Uses KHR_ray_tracing_pipeline (Vulkan) or DXR 1.0 (D3D12).
    2. Ray Queries: Uses KHR_ray_query (Vulkan) or DXR 1.1 TraceRayInline (D3D12).

    Both methods share the same underlying acceleration structure model (TLAS and BLAS).

  3. Understand the NVRHI programming model

    main

    NVRHI uses a programming model that blends DX11, DX12, and Vulkan. Key characteristics include:

    • Resource Lifetime Management: Unlike modern GAPIs where the application manages everything, NVRHI tracks resources, their usage, and GPU completion. It handles automatic resource state tracking and barrier placement.
    • Automatic Data Handling: The library manages GPU data uploads and scratch buffers for ray tracing acceleration structure builds.
    • Coarse-grained API: The API is more structured than DX11, utilizing Pipeline State Objects (PSOs), graphics state structures, and binding layouts/sets. This reduces API call overhead through efficient state caching.
    • Deferred Destruction: Resources are not destroyed immediately when released by the application. Instead, they are destroyed when their reference count reaches zero and the GPU is no longer using them. You must call IDevice::runGarbageCollection() at least once per frame to perform actual destruction.
  4. Implement Bindless Rendering with Descriptor Tables

    main

    For modern techniques like ray tracing that require dynamic indexing, NVRHI provides bindless support.

    Bindless Layouts:

    • Implement IBindingLayout.
    • Created via IDevice::createBindlessLayout.
    • Specify shader visibility, register spaces (DX12), and maximum capacity (Vulkan).

    Descriptor Tables:

    • The runtime counterpart to bindless layouts; they implement IBindingSet.
    • They are untyped arrays of resource bindings with variable size (DX12) that can be modified after creation.
    • Use IDevice::writeDescriptorTable to add a resource, or use ResourceType::None to erase a binding.

    Warning: Descriptor tables do not keep strong references to resources. You must manually manage resource lifetimes and ensure correct synchronization/barriers. It is recommended to use only permanent resources in descriptor tables.

  5. Integrate RTXMU with NVRHI

    main

    NVRHI provides optional integration with the RTXMU library for managing bottom-level ray tracing acceleration structures (BLAS).

    Setup Steps:

    1. Set the CMake variable NVRHI_WITH_RTXMU to ON. This triggers an automatic download of RTXMU via FetchContent.
    2. (Optional) Configure NVRHI_RTXMU_GIT_REPOSITORY, NVRHI_RTXMU_GIT_TAG, or NVRHI_RTXMU_FETCH_DIR to customize the download.

    Usage: When enabled, all BLAS'es are managed by RTXMU. If a BLAS is built with the AllowCompaction flag, it will be automatically compacted when you call the ICommandList::compactBottomLevelAccelStructs method.

  6. How Binding Layouts and Sets work together

    main

    NVRHI uses a symmetrical two-part system for resource binding:

    1. Binding Layouts (IBindingLayout): Declare what resources will be used (type and slot).

      • Items are BindingLayoutItem structures specifying ResourceType (e.g., Texture_SRV, VolatileConstantBuffer) and the binding slot (e.g., t# for SRVs).
      • Layouts define shader visibility, register space (DX12), and array sizes.
      • Layouts are used to create pipelines (graphics, compute, etc.).
    2. Binding Sets (IBindingSet): Provide the actual resources for a layout.

      • Items are BindingSetItem structures specifying the resource, slot, and array index.
      • They handle subresource details (like specific mip levels or slices) automatically.
      • Binding sets are used when issuing commands via ICommandList::setGraphicsState or setComputeState.

    Important Lifecycle Notes:

    • Both are immutable once created.
    • Binding sets keep strong references to their resources. To improve CPU performance for long-lived sets, set BindingSetDesc::trackLiveness = false, but you must then manually ensure the GPU is finished with the resources before releasing the set.
  7. Manage resource lifetime with RefCountPtr and handles

    main

    All NVRHI resources (pipelines, textures, etc.) derive from IResource, which follows the COM reference counting model (AddRef/Release).

    • Automatic Reference Counting: Use the RefCountPtr<T> template to automate lifetime management.
    • Resource Handles: NVRHI defines type-specific handles using RefCountPtr. For example, typedef RefCountPtr<ITexture> TextureHandle.
    • Function Compatibility: Any function accepting a raw resource pointer can be passed a handle, as it will automatically convert the pointer into a strong reference.
    • Fire and Forget: You can create resources or pipelines in a local scope, record commands, and exit the scope; the library will ensure they persist until the GPU work is complete.
  8. Configure resource state tracking and barriers

    main

    NVRHI automates resource state transitions and barrier placement. Because command lists can be recorded in parallel and executed out of order, you must provide hints to the command list about resource states.

    Three ways to manage states:

    1. Explicit Transitions: Use beginTrackingTexture/BufferState to provide the prior state at the start of a command list, and setTexture/BufferState at the end to set the desired exit state.
    2. Initial State Tracking: Create resources with the keepInitialState descriptor set to true. The command list will assume the resource enters in its initialState and transitions it back to that state upon exiting.
    3. Permanent States: Call setPermanentTexture/BufferState for static resources (like material textures). These resources do not require state tracking and are cheaper on the CPU.

    Controlling UAV Barriers:

    • NVRHI automatically places UAV barriers between successive uses of a resource in UnorderedAccess state.
    • Disable automatic UAV barriers: Use setEnableUavBarriersForTexture/Buffer(bool enable) to temporarily stop automatic barriers (useful for overlapping shader accesses).
    • Manual UAV barriers: Use nvrhi::utils::texture/bufferUavBarrier to place barriers manually.
    • Manual Mode: Use setEnableAutomaticBarriers(false) to disable all automatic barrier placement. In this mode, you must manually manage all transitions using setTexture/BufferState and call commitBarriers() to push them to the GAPI.
  9. Work with Framebuffers and FramebufferInfo

    main

    A IFramebuffer is an immutable collection of up to 8 render targets and a depth target.

    While framebuffers were previously required to create pipelines, you now only need a FramebufferInfo structure to create a graphics or meshlet pipeline. This structure defines the render target counts, formats, and multisampling configuration. A pipeline created with a specific FramebufferInfo can be used with any IFramebuffer that shares the same info. You can retrieve this info from a framebuffer using IFramebuffer::getFramebufferInfo.

  10. Initialize an IDevice for your GAPI

    main

    NVRHI does not create the underlying GAPI device. You must provide the existing device to NVRHI using backend-specific creation functions:

    • D3D11: nvrhi::d3d11::createDevice in <nvrhi/d3d11.h>
    • D3D12: nvrhi::d3d12::createDevice in <nvrhi/d3d12.h>
    • Vulkan: nvrhi::vulkan::createDevice in <nvrhi/vulkan.h>
    • Validation: nvrhi::validation::createDevice in <nvrhi/validation.h> (wraps another IDevice to intercept and validate calls).

    Note for DX12 and Vulkan: You must provide up to 3 queues during IDevice creation: graphics (required), compute, and copy (optional).

  11. Enable NVRHI Validation Layers

    main

    You can wrap your existing nvrhi::DeviceHandle with a validation layer to catch errors. This is done using nvrhi::validation::createValidationLayer.

    #include <nvrhi/validation.h>
    if (enableValidation) {
        nvrhi::DeviceHandle nvrhiValidationLayer = nvrhi::validation::createValidationLayer(nvrhiDevice);
        nvrhiDevice = nvrhiValidationLayer; // make the rest of the application go through the validation layer
    }