Graphics Programming Virtual Meetup


Discord


Exploration of Bindless Rendering in Vulkan, DirectX 12, and OpenGL
Vulkan Links
- https://community.arm.com/developer/tools-software/graphics/b/blog/posts/vulkan-descriptor-indexing
- https://chunkstories.xyz/blog/a-note-on-descriptor-indexing/
- https://ourmachinery.com/post/moving-the-machinery-to-bindless/
- https://zeux.io/2020/02/27/writing-an-efficient-vulkan-renderer/
- Khronos Descriptor Indexing https://www.youtube.com/watch?v=tXipcoeuNh4
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)
-
- Shows up as spending a lot of time in
- 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?
- Like the name suggests, it allows the indexing of descriptors in shaders
- Adds NonUniformIndexing in shaders
- Adds Unbounded Descriptor Arrays
- Relaxes when descriptor sets can be updated
- Before was restricted to when it wasn't bound
- Specification Description - https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_EXT_descriptor_indexing.html
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_POOL_CREATE_UPDATE_AFTER_BIND_BIT
-
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
- But likely used Descriptor Sets/Tables behind the scenes https://github.com/KhronosGroup/Vulkan-Docs/issues/871
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