Skip to content

Commit a7cdb63

Browse files
MarijnS95claude
andcommitted
Add RT acceleration structure abstraction with size queries and resource allocation
Introduce the foundational types for ray tracing acceleration structures: abstract `AccelerationStructure` base class, geometry/instance descriptors, BLAS/TLAS build-request structs with size queries, and AS resource allocation across DX12, Vulkan, and Metal. Recording build commands lands in a follow-up commit on top of the ComputeEncoder abstraction. DX12: `ID3D12DeviceX` typedef bumps from `ID3D12Device2` to `ID3D12Device5`, so the existing `Device` member directly exposes `GetRaytracingAccelerationStructurePrebuildInfo` (and the eventual `CreateStateObject` / `SetPipelineState1` for the PSO RT epic) — no separate `Device5` member or post-create `QueryInterface` dance. `D3D12CreateDevice` is already invoked with `IID_PPV_ARGS(&Device)`, so the bump naturally requires the adapter to support the Device5 interface (Win10 1809+); RT-capable hardware is selected by the `acceleration-structure` lit feature regardless. Vulkan device creation switches to a single `vkGetPhysicalDeviceFeatures2` call covering every extension feature struct we care about (atomic-int64, mesh-shader, acceleration-structure, BDA on 1.1): each struct is chained into `pNext` before the query, and post-query we verify the gating bool and clear the sub-features we don't enable (capture-replay, indirect-build, multiview, etc.). Drive-by: rather than letting `vkCreateDevice` reject the device with a generic `VK_ERROR_FEATURE_NOT_PRESENT`, the code now returns a descriptive `llvm::Error` naming the extension and the bool that came back zero — pinpointing the case where a driver advertises an extension but reports its base feature as `VK_FALSE`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 43764b3 commit a7cdb63

10 files changed

Lines changed: 1017 additions & 54 deletions

File tree

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
//===- AccelerationStructure.h - RT Acceleration Structure Types ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef OFFLOADTEST_API_ACCELERATIONSTRUCTURE_H
10+
#define OFFLOADTEST_API_ACCELERATIONSTRUCTURE_H
11+
12+
#include "API/API.h"
13+
#include "API/Buffer.h"
14+
#include "API/Resources.h"
15+
16+
#include "llvm/ADT/ArrayRef.h"
17+
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/Support/Error.h"
19+
20+
#include <cstdint>
21+
#include <variant>
22+
23+
namespace offloadtest {
24+
25+
struct AccelerationStructureSizes {
26+
uint64_t ResultDataMaxSizeInBytes = 0;
27+
uint64_t ScratchDataSizeInBytes = 0;
28+
uint64_t UpdateScratchDataSizeInBytes = 0;
29+
};
30+
31+
struct TriangleGeometryDesc {
32+
Buffer *VertexBuffer = nullptr;
33+
uint64_t VertexBufferOffset = 0;
34+
uint32_t VertexCount = 0;
35+
uint32_t VertexStride = 0;
36+
Format VertexFormat = Format::RGB32Float;
37+
Buffer *IndexBuffer = nullptr;
38+
uint64_t IndexBufferOffset = 0;
39+
uint32_t IndexCount = 0;
40+
IndexFormat IdxFormat = IndexFormat::Uint32;
41+
bool Opaque = true;
42+
};
43+
44+
struct AABBGeometryDesc {
45+
Buffer *AABBBuffer = nullptr;
46+
uint64_t AABBBufferOffset = 0;
47+
uint32_t AABBCount = 0;
48+
uint32_t AABBStride = 24;
49+
bool Opaque = true;
50+
};
51+
52+
class AccelerationStructure;
53+
54+
struct AccelerationStructureInstance {
55+
float Transform[3][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}};
56+
uint32_t InstanceID = 0;
57+
uint8_t InstanceMask = 0xFF;
58+
AccelerationStructure *BLAS = nullptr;
59+
};
60+
61+
struct BLASBuildRequest {
62+
// DXR / Vulkan / Metal all forbid mixing triangle and AABB geometry in a
63+
// single BLAS, so the geometry list is held as a variant — the invalid
64+
// mixed-geometry state is unrepresentable.
65+
std::variant<llvm::SmallVector<TriangleGeometryDesc>,
66+
llvm::SmallVector<AABBGeometryDesc>>
67+
Geometry;
68+
AccelerationStructureSizes Sizes;
69+
};
70+
71+
struct TLASBuildRequest {
72+
llvm::SmallVector<AccelerationStructureInstance> Instances;
73+
AccelerationStructureSizes Sizes;
74+
};
75+
76+
inline llvm::Error validateGeometryDesc(const TriangleGeometryDesc &D) {
77+
if (!D.VertexBuffer)
78+
return llvm::createStringError(
79+
std::errc::invalid_argument,
80+
"TriangleGeometryDesc: VertexBuffer is null.");
81+
if (!isPositionCompatible(D.VertexFormat))
82+
return llvm::createStringError(
83+
std::errc::invalid_argument,
84+
"TriangleGeometryDesc: VertexFormat '%s' is not position-compatible.",
85+
getFormatName(D.VertexFormat).data());
86+
if (D.VertexStride < getFormatSizeInBytes(D.VertexFormat))
87+
return llvm::createStringError(
88+
std::errc::invalid_argument,
89+
"TriangleGeometryDesc: VertexStride (%u) must be >= format size (%u).",
90+
D.VertexStride, getFormatSizeInBytes(D.VertexFormat));
91+
if (D.VertexCount == 0)
92+
return llvm::createStringError(std::errc::invalid_argument,
93+
"TriangleGeometryDesc: VertexCount is 0.");
94+
if (D.IndexBuffer && D.IndexCount == 0)
95+
return llvm::createStringError(
96+
std::errc::invalid_argument,
97+
"TriangleGeometryDesc: IndexBuffer is set but IndexCount is 0.");
98+
if (!D.IndexBuffer && D.IndexCount != 0)
99+
return llvm::createStringError(
100+
std::errc::invalid_argument,
101+
"TriangleGeometryDesc: IndexCount is set but IndexBuffer is null.");
102+
if (D.IndexBuffer && D.IndexCount % 3 != 0)
103+
return llvm::createStringError(
104+
std::errc::invalid_argument,
105+
"TriangleGeometryDesc: IndexCount (%u) must be a multiple of 3.",
106+
D.IndexCount);
107+
if (!D.IndexBuffer && D.VertexCount % 3 != 0)
108+
return llvm::createStringError(
109+
std::errc::invalid_argument,
110+
"TriangleGeometryDesc: VertexCount (%u) must be a multiple of 3 when "
111+
"no index buffer is provided.",
112+
D.VertexCount);
113+
return llvm::Error::success();
114+
}
115+
116+
inline llvm::Error validateGeometryDesc(const AABBGeometryDesc &D) {
117+
if (!D.AABBBuffer)
118+
return llvm::createStringError(std::errc::invalid_argument,
119+
"AABBGeometryDesc: AABBBuffer is null.");
120+
if (D.AABBCount == 0)
121+
return llvm::createStringError(std::errc::invalid_argument,
122+
"AABBGeometryDesc: AABBCount is 0.");
123+
if (D.AABBStride < 24)
124+
return llvm::createStringError(
125+
std::errc::invalid_argument,
126+
"AABBGeometryDesc: AABBStride (%u) must be >= 24.", D.AABBStride);
127+
return llvm::Error::success();
128+
}
129+
130+
template <typename T>
131+
inline llvm::Error validateBLASGeometry(llvm::ArrayRef<T> Geoms) {
132+
if (Geoms.empty())
133+
return llvm::createStringError(
134+
std::errc::invalid_argument,
135+
"BLASBuildRequest must have at least one geometry descriptor.");
136+
for (const auto &G : Geoms)
137+
if (auto Err = validateGeometryDesc(G))
138+
return Err;
139+
return llvm::Error::success();
140+
}
141+
142+
inline llvm::Error validateTLASBuildRequest(const TLASBuildRequest &Req) {
143+
if (Req.Instances.empty())
144+
return llvm::createStringError(
145+
std::errc::invalid_argument,
146+
"TLASBuildRequest: Must have at least one instance.");
147+
for (size_t I = 0; I < Req.Instances.size(); ++I)
148+
if (!Req.Instances[I].BLAS)
149+
return llvm::createStringError(
150+
std::errc::invalid_argument,
151+
"TLASBuildRequest: Instance %zu has a null BLAS pointer.", I);
152+
return llvm::Error::success();
153+
}
154+
155+
class AccelerationStructure {
156+
GPUAPI API;
157+
158+
public:
159+
virtual ~AccelerationStructure();
160+
AccelerationStructure(const AccelerationStructure &) = delete;
161+
AccelerationStructure &operator=(const AccelerationStructure &) = delete;
162+
163+
GPUAPI getAPI() const { return API; }
164+
165+
protected:
166+
explicit AccelerationStructure(GPUAPI API) : API(API) {}
167+
};
168+
169+
} // namespace offloadtest
170+
171+
#endif // OFFLOADTEST_API_ACCELERATIONSTRUCTURE_H

include/API/Device.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "Config.h"
1616

1717
#include "API/API.h"
18+
#include "API/AccelerationStructure.h"
1819
#include "API/Buffer.h"
1920
#include "API/Capabilities.h"
2021
#include "API/CommandBuffer.h"
@@ -226,6 +227,21 @@ class Device {
226227
virtual llvm::Expected<std::unique_ptr<CommandBuffer>>
227228
createCommandBuffer() = 0;
228229

230+
virtual llvm::Expected<BLASBuildRequest> createTriangleBLASBuildRequest(
231+
llvm::ArrayRef<TriangleGeometryDesc> Triangles) = 0;
232+
233+
virtual llvm::Expected<BLASBuildRequest>
234+
createAABBBLASBuildRequest(llvm::ArrayRef<AABBGeometryDesc> AABBs) = 0;
235+
236+
virtual llvm::Expected<TLASBuildRequest> createTLASBuildRequest(
237+
llvm::ArrayRef<AccelerationStructureInstance> Instances) = 0;
238+
239+
virtual llvm::Expected<std::unique_ptr<AccelerationStructure>>
240+
createAccelerationStructure(const BLASBuildRequest &Request) = 0;
241+
242+
virtual llvm::Expected<std::unique_ptr<AccelerationStructure>>
243+
createAccelerationStructure(const TLASBuildRequest &Request) = 0;
244+
229245
virtual ~Device() = 0;
230246

231247
llvm::StringRef getDescription() const { return Description; }

include/API/Texture.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <variant>
2727

2828
namespace offloadtest {
29+
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
2930

3031
enum TextureUsage : uint32_t {
3132
Sampled = 1 << 0,

lib/API/DX/DXResources.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ inline D3D12_RESOURCE_FLAGS getDXResourceFlags(TextureUsage Usage) {
8484
return Flags;
8585
}
8686

87+
inline DXGI_FORMAT getDXGIIndexFormat(IndexFormat Fmt) {
88+
switch (Fmt) {
89+
case IndexFormat::Uint16:
90+
return DXGI_FORMAT_R16_UINT;
91+
case IndexFormat::Uint32:
92+
return DXGI_FORMAT_R32_UINT;
93+
}
94+
llvm_unreachable("All IndexFormat cases handled");
95+
}
96+
8797
} // namespace offloadtest
8898

8999
#endif // OFFLOADTEST_API_DXRESOURCES_H

0 commit comments

Comments
 (0)