December 8, 2024
11 Hello Bindless
All modern graphics APIs support bindless resources. The term “bindless” refers to the ability to skip explicit resource binding, enabling the binding of all resources with a single API call and accessing required resources by index. This binding model is crucial for ray tracing, where rendering demands access to any resource in the scene. It also provides significant benefits for rasterization, particularly in GPU-driven workflows.
However, the similarities between APIs end there, as each takes a distinct approach to achieving this functionality.
Fortunately, Tellusim Core SDK+ provides a simple abstraction for managing bindless buffers and textures across Vulkan, Direct3D12, and Metal APIs. Using the Core SDK, developers can seamlessly implement cross-platform GLTF scene ray tracing. While this example can serve as a starting point for advanced shading experiments, such as ReSTIR, the primary purpose of this tutorial is to demonstrate bindless resources. For simplicity, we use the Phong shading model with two rays per pixel.
In Tellusim Engine, bindless resources are standard Texture and Buffer objects stored within TextureTable or BufferTable containers. These table containers can manage thousands of simultaneously bound resources and provide straightforward access within shaders. For example, the following snippet iterates over a loaded GLTF scene to collect textures and geometry ranges:
Array<Texture> textures; Array<Geometry> geometries; Tracing model_tracing = device.createTracing(); for(uint32_t i = 0; i < model.getNumGeometries(); i++) { MeshGeometry mesh_geometry = mesh.getGeometry(i); for(uint32_t j = 0; j < model.getNumMaterials(i); j++) { MeshMaterial mesh_material = mesh_geometry.getMaterial(j); // geometry parameters Geometry &geometry = geometries.append(); geometry.base_index = model.getMaterialBaseIndex(i, j); // load normal texture Texture normal_texture = create_texture(device, mesh_material, MeshMaterial::TypeNormal); if(normal_texture) { geometry.normal_index = textures.size(); textures.append(normal_texture); } ... // tracing geometry model_tracing.addVertexBuffer(model.getNumGeometryVertices(i), model_pipeline.getAttributeFormat(0), model.getVertexBufferStride(0), vertex_buffer); model_tracing.addIndexBuffer(model.getNumMaterialIndices(i, j), FormatRu32, index_buffer, sizeof(uint32_t) * model.getMaterialBaseIndex(i, j)); } } if(!model_tracing.create(Tracing::TypeTriangle, Tracing::FlagCompact | Tracing::FlagFastTrace)) return 1;
Next, we create a TextureTable from all textures using a single line:
// create texture table TextureTable texture_table = device.createTextureTable(textures);
A similar process applies to Buffer resources: create a BufferTable from the generated buffers. The tests/platform/bindless SDK example demonstrates this process for both buffers and textures in a rasterization-based scenario.
Once the table is ready, binding all resources becomes straightforward. During rendering, we can sample the required texture using an integer index:
// load diffuse texture [[branch]] if(geometry.diffuse_index != ~0u) { diffuse_color = textureLod(nonuniformEXT(sampler2D(in_textures[geometry.diffuse_index], in_sampler)), texcoord, 0.0f); #if ALPHA_TEST [[branch]] if(diffuse_color.w < 0.5f) { diffuse_color = vec4(1.0f); continue; } #endif }
Note: A nonuniform decorator is necessary not only for textures but also for buffers. Without it, rendering on AMD GPUs may produce artifacts.
This tutorial utilizes Ray Queries and also demonstrates how to trace materials with binary alpha visibility. To achieve this, you need to iterate the rayQueryProceedEXT() shader function multiple times until the ray intersection is correct. Unfortunately, hardware ray tracing efficiency drops significantly on Apple and AMD GPUs under these conditions. As a result, hybrid ray tracing combined with rasterization for primary rays can be more efficient than full ray tracing. By default, this example does not perform alpha-test intersection, but you can enable it by uncommenting the ALPHA_TEST macro.
Stay tuned! Upcoming tutorials will explore GPU-driven rendering with Tellusim Engine.