|
| 1 | +--- |
| 2 | +title: 0045 - HLSL support for Clustered Geometry in Raytracing |
| 3 | +params: |
| 4 | + authors: |
| 5 | + - jschmid-nvidia: Jan Schmid |
| 6 | + - simoll: Simon Moll |
| 7 | + sponsors: |
| 8 | + - jenatali: Jesse Natalie |
| 9 | + status: Under Review |
| 10 | +--- |
| 11 | + |
| 12 | +## Introduction |
| 13 | + |
| 14 | +Acceleration structure build times can become a bottleneck in raytracing |
| 15 | +applications that require large amounts of dynamic geometry. Example scenarios |
| 16 | +include new geometry being streamed in from disk, high numbers of animated |
| 17 | +objects, level-of-detail systems, or dynamic tessellation. |
| 18 | + |
| 19 | +Clustered Geometry is a proposed future DXR feature that aims to address this |
| 20 | +issue by enabling the application to construct bottom-level acceleration structures |
| 21 | +from previously generated clusters of primitives, resulting in significant speedups |
| 22 | +in acceleration structure build times. The DXR spec details for this are not available |
| 23 | +yet, but as an example for now see [NVIDIA's blog](https://developer.nvidia.com/blog/nvidia-rtx-mega-geometry-now-available-with-new-vulkan-samples/) describing the overall concept of clustered |
| 24 | +geometry (among other topics). |
| 25 | + |
| 26 | +How clustered geometry gets exposed in D3D overall is to be determined but this document is |
| 27 | +certainly a part: a proposal for the HLSL operations needed for clustered geometry. |
| 28 | + |
| 29 | +## Motivation |
| 30 | + |
| 31 | +Clustered Geometry refers to a building block for Bottom Level Acceleration Structures |
| 32 | +(BLASes). Most of the details of what a cluster is are out of scope for this spec. |
| 33 | +What's relevant is that the application can build BLASes out of cluster building |
| 34 | +blocks. When raytracing, hit shaders will want to know the ID of the cluster that is hit |
| 35 | +so the shader can do things like look up cluster-specific user data. Hence the need for a `ClusterID()` intrinsic. |
| 36 | + |
| 37 | +This isn't an index into the clusters in a given BLAS but rather an ID assigned by |
| 38 | +the user for each cluster in a BLAS. Different BLASs could reuse simmilar building blocks, and |
| 39 | +thus share cluster ID. |
| 40 | + |
| 41 | +## HLSL |
| 42 | + |
| 43 | +### Enums |
| 44 | + |
| 45 | +```C |
| 46 | +enum CLUSTER_ID_CONSTANTS : uint |
| 47 | +{ |
| 48 | + CLUSTER_ID_INVALID = 0xffffffff, |
| 49 | +}; |
| 50 | +``` |
| 51 | + |
| 52 | +ClusterID values returned by [ClusterID()](#clusterid) with predefined meaning |
| 53 | + |
| 54 | +Value | Definition |
| 55 | +----- | ---- |
| 56 | +`CLUSTER_ID_INVALID` | Returned if a BLAS was intersected that was not constructed from CLAS |
| 57 | + |
| 58 | +### DXR 1.0 System Value Intrinsics |
| 59 | + |
| 60 | +A new DXR System Value Intrinsic is added to support fetching the `ClusterID` of an intersected CLAS. |
| 61 | + |
| 62 | +#### ClusterID |
| 63 | + |
| 64 | +```C |
| 65 | +uint ClusterID() |
| 66 | +``` |
| 67 | + |
| 68 | +Returns a `uint` containing the user-defined `ClusterID` value a CLAS was |
| 69 | +built with. If a non-clustered BLAS was intersected, `CLUSTER_ID_INVALID` |
| 70 | +is returned. |
| 71 | + |
| 72 | +The following table shows which shaders can access it: |
| 73 | +| **values \\ shaders** | ray generation | intersection | any hit | closest hit | miss | callable | |
| 74 | +|:---------------------------------------------------------:|:--------------:|:------------:|:-------:|:-----------:|:----:|:--------:| |
| 75 | +| *Primitive/object space system values:* | | | | | | | |
| 76 | +| uint [ClusterID()](#clusterid) | | | \* | \* | | | |
| 77 | + |
| 78 | +## Extension to DXR 1.1 RayQuery API |
| 79 | + |
| 80 | +New intrinsics [CandidateClusterID()](#rayquery-candidateclusterid) and |
| 81 | +[CommittedClusterID()](#rayquery-committedclusterid) are added to `RayQuery`. |
| 82 | +Behavior of all other intrinsics is unchanged. |
| 83 | + |
| 84 | +### RayQuery intrinsics |
| 85 | + |
| 86 | +The following table lists intrinsics available when |
| 87 | +[RayQuery::Proceed()](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/Raytracing.md#rayquery-proceed) returned `TRUE`, |
| 88 | +meaning a type of hit candidate that requires shader evaluation has been found. |
| 89 | +Methods named `Committed*()` in this table may actually not be available |
| 90 | +depending on the current [CommittedStatus()](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/Raytracing.md#rayquery-committedstatus) |
| 91 | +(i.e. what type of hit has been committed yet, if any) - this is further |
| 92 | +clarified in another table further below. |
| 93 | + |
| 94 | +| **Intrinsic** \ **CandidateType()** | `HIT_CANDIDATE_NON_OPAQUE_TRIANGLE` | `HIT_CANDIDATE_PROCEDURAL_PRIMITIVE` | |
| 95 | +|:---------------------------------------------------------|:-----------------------------------:|:------------------------------------:| |
| 96 | +| uint [CandidateClusterID()](#rayquery-candidateclusterid)| \* | | |
| 97 | +| uint [CommittedClusterID()](#rayquery-committedclusterid)| \* | | |
| 98 | + |
| 99 | +The following table lists intrinsics available depending on the current |
| 100 | +[COMMITTED_STATUS](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/Raytracing.md#committed_status) (i.e. what type of |
| 101 | +hit has been committed, if any). This applies regardless of whether |
| 102 | +[RayQuery::Proceed()](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/Raytracing.md#rayquery-proceed) has returned |
| 103 | +`TRUE` (shader evaluation needed for traversal), or `FALSE` (traversal |
| 104 | +complete). If `TRUE`, additional methods than those shown below are |
| 105 | +available (see the above table). |
| 106 | + |
| 107 | +| **Intrinsic** \ **CommittedStatus()** | `COMMITTED_TRIANGLE_HIT` | `COMMITTED_PROCEDURAL_PRIMITIVE_HIT` | `COMMITTED_NOTHING` | |
| 108 | +|:---------------------------------------------------------|:------------------------:|:------------------------------------:|:-------------------:| |
| 109 | +| uint [CommittedClusterID()](#rayquery-committedclusterid)| \* | | | |
| 110 | + |
| 111 | +#### RayQuery CandidateClusterID |
| 112 | + |
| 113 | +The user-provided `ClusterID` of the intersected CLAS, if a Cluster BLAS was |
| 114 | +intersected for the current hit candidate. |
| 115 | +Returns `CLUSTER_ID_INVALID` if a non-Cluster BLAS was intersected. |
| 116 | + |
| 117 | +```C++ |
| 118 | +uint RayQuery::CandidateClusterID(); |
| 119 | +``` |
| 120 | + |
| 121 | +[RayQuery intrinsics](#rayquery-intrinsics) illustrates when this is valid to |
| 122 | +call. |
| 123 | +Lowers to [RayQuery_CandidateClusterID DXIL Opcode](#rayquery_candidateclusterid-dxil-opcode). |
| 124 | + |
| 125 | +#### RayQuery CommittedClusterID |
| 126 | + |
| 127 | +The user-provided `ClusterID` of the intersected CLAS, if a Cluster BLAS was |
| 128 | +intersected for the closest hit committed so far. |
| 129 | +Returns `CLUSTER_ID_INVALID` if a non-Cluster BLAS was intersected. |
| 130 | + |
| 131 | +```C++ |
| 132 | +uint RayQuery::CommittedClusterID(); |
| 133 | +``` |
| 134 | + |
| 135 | +[RayQuery intrinsics](#rayquery-intrinsics) illustrates when this is valid to |
| 136 | +call. |
| 137 | +Lowers to [RayQuery_CommittedClusterID DXIL Opcode](#rayquery_committedclusterid-dxil-opcode). |
| 138 | + |
| 139 | +### Extension to the DXR 1.2 HitObject API |
| 140 | + |
| 141 | +Cluster Geometries are also supported with the HitObject feature. |
| 142 | +The following intrinsic is added to `HitObject` type. |
| 143 | + |
| 144 | +#### HitObject::GetClusterID |
| 145 | + |
| 146 | +```C |
| 147 | +uint HitObject::GetClusterID(); |
| 148 | +``` |
| 149 | + |
| 150 | +Returns the user-provided `ClusterID` of the intersected CLAS of a hit. |
| 151 | +Returns `CLUSTER_ID_INVALID` if a non-Cluster BLAS was intersected or if |
| 152 | +the `HitObject` does not encode a hit. |
| 153 | +Lowers to [HitObject_ClusterID DXIL Opcode](#hitobject_clusterid-dxil-opcode). |
| 154 | + |
| 155 | +### Diagnostic Changes |
| 156 | + |
| 157 | +This proposal does not introduce or remove diagnostics or warnings. |
| 158 | + |
| 159 | +## Testing |
| 160 | + |
| 161 | +### Unit Tests |
| 162 | + |
| 163 | +#### CodeGen & AST Tests |
| 164 | + |
| 165 | +* Expected AST for new implicit HLSL ops. |
| 166 | +* Lowering from HLSL to HL. |
| 167 | +* Lowering from HL to DXIL. |
| 168 | +* Lowering from HLSL to DXIL. |
| 169 | + |
| 170 | +#### Diagnostics Tests |
| 171 | + |
| 172 | +* Expected error when ClusterID builtins called from unsupported shader kinds. |
| 173 | + |
| 174 | +### DXIL |
| 175 | + |
| 176 | +| Opcode | Opcode name | Description |
| 177 | +|:--- |:--- |:--- |
| 178 | +XXX | ClusterID | Returns the cluster ID of this hit |
| 179 | +XXX + 1 | RayQuery_CandidateClusterID | Returns the candidate hit cluster ID |
| 180 | +XXX + 2 | RayQuery_CommittedClusterID | Returns the committed hit cluster ID |
| 181 | +XXX + 2 | HitObject_ClusterID | Returns the cluster ID of this committed hit |
| 182 | + |
| 183 | +#### ClusterID DXIL Opcode |
| 184 | + |
| 185 | +```DXIL |
| 186 | +declare i32 @dx.op.clusterID( |
| 187 | + i32) ; Opcode (ClusterID) |
| 188 | + nounwind readnone |
| 189 | +``` |
| 190 | + |
| 191 | +Valid shader kinds defined in [ClusterID HLSL](#clusterid). |
| 192 | + |
| 193 | +#### RayQuery_CandidateClusterID DXIL Opcode |
| 194 | + |
| 195 | +```DXIL |
| 196 | +declare i32 @dx.op.rayQuery_StateScalar.i32( |
| 197 | + i32, ; Opcode (RayQuery_CandidateClusterID) |
| 198 | + i32) ; RayQuery handle |
| 199 | + nounwind readonly |
| 200 | +``` |
| 201 | + |
| 202 | +Validation errors: |
| 203 | +* Validate that the RayQuery handle is not `undef`. |
| 204 | + |
| 205 | +#### RayQuery_CommittedClusterID DXIL Opcode |
| 206 | + |
| 207 | +```DXIL |
| 208 | +declare i32 @dx.op.rayQuery_StateScalar.i32( |
| 209 | + i32, ; Opcode (RayQuery_CommittedClusterID) |
| 210 | + i32) ; RayQuery handle |
| 211 | + nounwind readonly |
| 212 | +``` |
| 213 | + |
| 214 | +Validation errors: |
| 215 | +* Validate that the RayQuery handle is not `undef`. |
| 216 | + |
| 217 | +#### HitObject_ClusterID DXIL Opcode |
| 218 | + |
| 219 | +```DXIL |
| 220 | +declare i32 @dx.op.hitObject_StateScalar.i32( |
| 221 | + i32, ; Opcode (HitObject_ClusterID) |
| 222 | + %dx.types.HitObject) ; HitObject |
| 223 | + nounwind readnone |
| 224 | +``` |
| 225 | + |
| 226 | +Validation errors: |
| 227 | +* Validate that the HitObject is not `undef`. |
| 228 | + |
| 229 | +### Diagnostic Changes |
| 230 | + |
| 231 | +This proposal does not introduce or remove diagnostics or warnings. |
| 232 | + |
| 233 | +### Runtime Additions |
| 234 | + |
| 235 | +TODO: link the DXR spec proposal when available |
| 236 | + |
| 237 | +Note: No unique raytracing tier is required to use this intrinsic. If the device does not support clustered geometry, it must still support returning `CLUSTER_ID_INVALID` from this intrinsic. |
| 238 | + |
| 239 | +## Testing |
| 240 | + |
| 241 | +### Validation Tests |
| 242 | + |
| 243 | +* Valid calls to new DXIL ops pass validation. |
| 244 | +* Invalid calls fail validation (undef `i32` RayQuery handle or `%dx.types.HitObject` HitObject parameter). |
| 245 | +* Calls in invalid shader stages fail validation. |
| 246 | + |
| 247 | +### Execution Tests |
| 248 | + |
| 249 | +#### ClusterID Execution Tests |
| 250 | + |
| 251 | +Test in both `anyhit` and `closesthit` shader kinds. |
| 252 | + |
| 253 | +* Query for CLAS hit returns expected Cluster ID. |
| 254 | +* Query for non-clustered BLAS hit returns `CLUSTER_ID_INVALID`. |
| 255 | + |
| 256 | +#### RayQuery Execution Tests |
| 257 | + |
| 258 | +Test both [RayQuery::CandidateClusterID](#rayquery-candidateclusterid) and [RayQuery::CommittedClusterID](#rayquery-committedclusterid). |
| 259 | +Builtins to test in the these RayQuery object states: |
| 260 | + |
| 261 | +* Queries for CLAS committed hits return expected Cluster ID (`COMMITTED_TRIANGLE_HIT`). |
| 262 | +* Query for committed cluster ID in no-hit-committed state returns `CLUSTER_ID_INVALID`. |
| 263 | +* Queries for candidate cluster ID for CLAS candidate hits return expected Cluster ID (`CANDIDATE_NON_OPAQUE_TRIANGLE` and when first call to `Proceed` returns `FALSE` ). |
| 264 | +* Queries for non-clustered BLAS candidate/committed hits return `CLUSTER_ID_INVALID`. |
| 265 | + |
| 266 | +#### HitObject Execution Tests |
| 267 | + |
| 268 | +Test CLAS and non-CLAS hit return expected values for candidate hit. |
| 269 | +Builtin [HitObject::GetClusterID](#hitobjectgetclusterid) to test in these HitObject setups: |
| 270 | + |
| 271 | +* Add `HitObject::FromRayQuery` + `HitObject::GetClusterID` variants in [RayQuery execution tests](#rayquery-execution-tests). |
| 272 | +* Test expected cluster ID value for HitObject obtained from `HitObject::TraceRay` (CLAS hit, CLAS miss, non-CLAS hit). |
| 273 | +* Test `CLUSTER_ID_INVALID` for HitObject constructed from `HitObject::MakeMiss` and `HitObject::MakeNop`. |
| 274 | + |
0 commit comments