Graphics Programming Virtual Meetup

Discord

Twitter

Exploration of Bindless Rendering in Vulkan, DirectX 12, and OpenGL

Vulkan Links

Dx12 Links

OpenGL links

Bindless Overview

What it is, isn't, the good and the bad

Bind-full Rendering

  • Each draw call requires a set of resources to be "bound" beforehand
  • Different draw calls need different resources
  • Thus, a pattern of 'bind-draw; bind-draw; bind-draw'
  • For every unique object there will exist a set of resources specific to that object
    • Some resources can be shared, others cant

Typical Bind-full render loop

for each view {
  bind view resources          // camera, environment...
  for each shader {
    bind shader pipeline  
    bind shader resources      // shader control values
    for each material {
      bind material resources  // material parameters and textures
      for each object {
        bind object resources  // object transforms
        draw object
      }
    }
  }
}

Side note - Slot vs Frequency binding

  • Slot based resource binding
    • Limited number of slots per resource per shader stage
    • OpenGL, DirectX11, Metal
    • Rebinding swaps individual slots out
    • Driver figures out how slots map to hardware
  • Frequency based resource binding
    • Resources grouped into set based on update frequency
    • Rebind whole group(s) at a time
    • Driver can bulk update resources more easily
    • Vulkan & DX12

Bindless Rendering

  • Bindless doesn't mean 'no-binding'
  • Instead, bind all resources at the start of a frame into big arrays (textures and buffers)
  • Shaders then access the resources by indexing into the arrays
  • The drawing loop is greatly simplified by not bindings all the time

Bindless render loop

for each object {
  update descriptor arrays
}
bind everything
for each view {
  set global data index       // camera, lighting, shadow maps etc
  for each shader {
    bind shader pipeline      // still have to bind the pipeline
    for each object {
      set material data index // containts the texture indices
      set object data index   // contains object matrix indices   
      draw object
    }
  }
}

Core concept that allows bindless

  • A descriptor is a handle to some memory on the GPU
    • textures, data buffers, samplers, etc
  • Binding puts a descriptor in a location shader can access
    • Registers in DirectX, Slots in OpenGL
  • Shaders can bind many descriptors at once already
  • Descriptors in modern hardware live in "regular memory"
  • Bindless is about making descriptors directly available to shaders
    • Instead of the driver setting up the addresses for applications

Direct Benefits of bindless

  • No costly transfer of descriptor to GPU every frame
    • Shows up as spending a lot of time in
      • CopyDescriptorsSimple (DX12)
      • vkUpdateDescriptorSets (Vulkan)
  • More flexible / dynamic rendering architecture
  • No manual tracking of per-object resource groups
  • Updating matrices and material data can be done in bulk before command recording
  • CPU and GPU refer to resources the same way, by index
  • GPU can store Texture ID's in a buffer for  reference later in the frame - many uses

Bindless makes possible:

  • Easy Vertex Pulling - gets rid of binding vertex buffers
  • Raytracing - requires bindless in fact
  • Write resources indexes from one shader into a buffer that another shader reads & uses
  • G-Buffer can uses material ID instead of values
  • Terrain Splatmap contains material ID's allowing many materials to be used, instead of 4
  • Many GPU Driven Rendering approaches
  • And more...

Bindless Disadvantages

  • Requires hardware support
    • May be too new for widespread use
    • Different 'feature levels' can help ease transition
  • Different Performance Penalties
    • Arrays indexing can cause memory indirections
  • "With great power comes great responsibility"
    • GPU can't verify that valid descriptors are bound
    • Validation is costlier: happens inside shaders
    • Can be difficult to debug
    • Descriptor management is up to the Application

Commonalities

Types of Indexing, Unbounded Arrays, Descriptor Updating Synchronization

Uniform Indexing

  • GPU's run many invocations of a shader at a time
  • Arrays of Descriptors allow multiple descriptors of the same type to be available, like texture arrays
  • The limitations are
    • Every invocation will index into the same element in the array
    • The index is set statically - essentially at shader compile time
  • Similar with binding specific descriptor in a slot

Non-Uniform Indexing

  • Non-Uniform Indexing allows invocations to diverge
    • Pixel 0 samples texture A, Pixel 1 samples texture B
  • A hard requirment for true-bindless
  • "Dynamically Uniform Indexing" is a step down
    • All invocations use the same index
    • Index is set at runtime, not shader compile time
    • Limited application, but is more widely supported
  • Different descriptor types might have different  indexing capabilities
    • NonUniform may not be supported on all types

Unbounded Descriptor Arrays

  • Was possible to create arrays of descriptors before
    • Limited in size
    • Texture Arrays had to have identical dimensions
      • Though Arrays of Textures don't have to
  • Unbounded Arrays mean the size limit is practically infinite - usually in the millions or more
  • Let applications create many descriptors ahead of time and use them as needed

Descriptor Updating Synchronization

  • Descriptor updates must be manually synchronized
    • (No surprise to those familiar with Dx12 or Vulkan)
  • Must have a strategy to prevent data races
    • Keep a free list of available descriptors for
  • Not-in-use descriptors can be updated without concern
  • Vulkan has additional requirements in this domain

Vulkan

Descriptor Indexing,
Updating Descriptor Sets,

and Quirks

Descriptor Indexing

  • Bindless requires the use of Descriptor Indexing
    • Extension - VK_EXT_descriptor_indexing
    • In Core Version 1.2
      • A 1.2 device doesn't mean the hardware supports it, its an optional feature
  • Available on all desktop hardware but requires drivers from ~2018 onwards
    • Intel was very late to adding support in their driver

What is Descriptor Indexing?

Updating Descriptor Sets

  • Still uses `vkUpdateDescriptorSets` to update them
  • Vulkan 1.0: Descriptors may not be updated if they have been bound in a command buffer (eg before execution)
    • VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT
      • Alleviates that but can't update in-use descriptors
  • VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
    • Unused descriptors don't need to be filled in
  • VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT
    • Means updating while in use is allowed, as long as no currently in-use descriptors are updated
  • Partialy bound and Update Unused While Pending pair well

Quirks

  • Unbounded Arrays must be the last descriptor in a descriptor set
  • Can't have arrays of descriptor types
    • Means there will an array per descriptor type
    • Causes friction in Dx12 on Vulkan emulation
      • VK_VALVE_mutable_descriptor_type helps
  • Validation is difficult, and sometimes not very helpful
    • Also is slow, which limits its usefulness

DX12

Available earlier and probably easier...

Descriptor Management

  • Vulkan Descriptor Sets are 'simpler' because they aren't as manual but also seem to be more flexible
  • At its core is the Root Signature
  • Hold a variety of data
    • Constants
    • Descriptors
    • Descriptor Tables - Pointer + Count to an allocated array of descriptors
    • Unbounded Descriptor Tables - same as before but no defined size

Manual Management = More Fun

  • Descriptors are always exposed to the user
    • Must copy descriptors from the CPU to GPU
    • Allocate memory for them directly
      • Instead of `VkDescriptorPool` hiding it
  • Setting up bindless is thus more natural
    • Vulkan started with a 'frequency model' and it shows. DX12 supports it but not as explicitely
    • Allows multiple types of descriptors in the same tables (with restrictions)

OpenGL

The OG - but NVidia only

NV_bindless_texture

NV_shader_buffer_store

NV_shader_buffer_load

  • Released ~2011?
  • Application queries a 'integer' that is the index of the texture
    • The integer is more like a direct memory address than a Descriptor (handle)
  • Mentally distinct from Vulkan & Dx12

ARB_bindless_texture

  • Cross vendor extension

  • All I had time to research about it

  • Probably has something to do with AZDO techniques

Thanks!

Questions?

Graphics Programming Virtual Meetup

Exploration of Bindless Rendering in Vulkan, DirectX 12, and OpenGL

By Charles Giessen

Exploration of Bindless Rendering in Vulkan, DirectX 12, and OpenGL

This week we will discuss the 'Bindless Rendering' model, examining various presentations, blog posts, and articles that explore the concept in Vulkan, DirectX 12 and OpenGL. By exploring the different approaches used in each graphics API, we can develop an understanding for the technique as it is used in real code.

  • 538