Skip to content

Commit 5e2b0bf

Browse files
MarijnS95claude
andcommitted
Add per-geometry Transform on TriangleGeometryDesc
Adds an optional 3x4 row-major affine transform on triangle BLAS geometries; vertices are baked through it at AS-build time, so Object* shader queries report the transformed positions. Plumbed via DX's Transform3x4 GPU VA, VK's transformData device address, and Metal's transformationMatrixBuffer + MatrixLayoutRowMajor (matches the DX/VK row-major byte layout, so the same upload buffer is reused). The covering test bakes a translate-x-by-5 into a single-triangle BLAS and verifies the same world-space rays hit/miss accordingly with an identity TLAS instance. Part of the inline-RT test coverage epic (#1258). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c822105 commit 5e2b0bf

8 files changed

Lines changed: 150 additions & 0 deletions

File tree

include/API/AccelerationStructure.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
#include "llvm/ADT/SmallVector.h"
1818
#include "llvm/Support/Error.h"
1919

20+
#include <array>
2021
#include <cstdint>
22+
#include <optional>
2123
#include <variant>
2224

2325
namespace offloadtest {
@@ -39,6 +41,10 @@ struct TriangleGeometryDesc {
3941
uint32_t IndexCount = 0;
4042
IndexFormat IdxFormat = IndexFormat::Uint32;
4143
bool Opaque = true;
44+
// Optional BLAS-side bake transform, 3x4 row-major. Vertices are
45+
// multiplied by this before AS build, so the resulting BLAS reports
46+
// transformed positions via Object* shader queries.
47+
std::optional<std::array<float, 12>> Transform;
4248
};
4349

4450
struct AABBGeometryDesc {

include/Support/Pipeline.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/Support/Error.h"
2121
#include "llvm/Support/MemoryBuffer.h"
2222
#include "llvm/Support/YAMLTraits.h"
23+
#include <array>
2324
#include <limits>
2425
#include <memory>
2526
#include <optional>
@@ -493,6 +494,7 @@ struct TriangleGeometry {
493494
IndexFormat IdxFormat = IndexFormat::Uint32;
494495
uint32_t IndexCount = 0;
495496
bool Opaque = true;
497+
std::optional<std::array<float, 12>> Transform;
496498
};
497499

498500
struct AABBGeometry {

lib/API/DX/Device.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,13 @@ class DXDevice : public offloadtest::Device {
16561656
Tri.IndexFormat = getDXGIIndexFormat(T.IdxFormat);
16571657
}
16581658

1659+
// Scratch sizing depends on whether Transform3x4 will be present at
1660+
// build time; signal that with any non-NULL sentinel here — the DXR
1661+
// spec lets the value be NULL or non-NULL for the prebuild query and
1662+
// does not dereference it.
1663+
if (T.Transform)
1664+
Tri.Transform3x4 = 1;
1665+
16591666
GeomDescs.push_back(GD);
16601667
}
16611668

@@ -2957,6 +2964,19 @@ llvm::Error DXComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
29572964
GD.Triangles.IndexCount = T.IndexCount;
29582965
GD.Triangles.IndexFormat = getDXGIIndexFormat(T.IdxFormat);
29592966
}
2967+
if (T.Transform) {
2968+
const BufferCreateDesc XformDesc{MemoryLocation::CpuToGpu,
2969+
BufferUsage::Storage};
2970+
auto XformOrErr = offloadtest::createBufferWithData(
2971+
*Dev, "AS-Geom-Transform", XformDesc, T.Transform->data(),
2972+
T.Transform->size() * sizeof(float), nullptr, nullptr);
2973+
if (!XformOrErr)
2974+
return XformOrErr.takeError();
2975+
auto *XformBuf = llvm::cast<DXBuffer>(XformOrErr->get());
2976+
GD.Triangles.Transform3x4 =
2977+
XformBuf->Buffer->GetGPUVirtualAddress();
2978+
CB.KeepAliveOwned.push_back(std::move(*XformOrErr));
2979+
}
29602980
GeomDescs.push_back(GD);
29612981
}
29622982
} else {

lib/API/Device.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ llvm::Error offloadtest::buildPipelineAccelerationStructures(
132132
TGD.VertexStride = T.VertexStride;
133133
TGD.VertexFormat = T.VertexFormat;
134134
TGD.Opaque = T.Opaque;
135+
TGD.Transform = T.Transform;
135136

136137
OutInputBuffers.push_back(std::move(*VBOrErr));
137138

lib/API/MTL/MTLDevice.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,6 +2457,18 @@ llvm::Error MTLComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
24572457
TD->setIndexType(getMetalIndexType(T.IdxFormat));
24582458
}
24592459
TD->setOpaque(T.Opaque);
2460+
if (T.Transform) {
2461+
MTL::Buffer *XformBuf = MTLDev->newBuffer(
2462+
T.Transform->data(), T.Transform->size() * sizeof(float),
2463+
MTL::ResourceStorageModeShared);
2464+
if (!XformBuf)
2465+
return llvm::createStringError(
2466+
std::errc::not_enough_memory,
2467+
"Failed to allocate BLAS transform buffer.");
2468+
TD->setTransformationMatrixBuffer(XformBuf);
2469+
TD->setTransformationMatrixLayout(MTL::MatrixLayoutRowMajor);
2470+
CB->KeepAliveMTLBuffers.push_back(XformBuf);
2471+
}
24602472
Geoms.push_back(TD);
24612473
}
24622474
} else {

lib/API/VK/Device.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,6 +4392,18 @@ llvm::Error VKComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
43924392
} else {
43934393
Tri.indexType = VK_INDEX_TYPE_NONE_KHR;
43944394
}
4395+
if (T.Transform) {
4396+
const BufferCreateDesc XformDesc{MemoryLocation::CpuToGpu,
4397+
BufferUsage::Storage};
4398+
auto XformOrErr = offloadtest::createBufferWithData(
4399+
*Dev, "AS-Geom-Transform", XformDesc, T.Transform->data(),
4400+
T.Transform->size() * sizeof(float), nullptr, nullptr);
4401+
if (!XformOrErr)
4402+
return XformOrErr.takeError();
4403+
auto *XformBuf = llvm::cast<VulkanBuffer>(XformOrErr->get());
4404+
Tri.transformData.deviceAddress = XformBuf->getDeviceAddress();
4405+
CB.KeepAliveOwned.push_back(std::move(*XformOrErr));
4406+
}
43954407
Geoms[I].push_back(G);
43964408

43974409
VkAccelerationStructureBuildRangeInfoKHR R = {};

lib/Support/Pipeline.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,19 @@ void MappingTraits<offloadtest::TriangleGeometry>::mapping(
634634
I.mapOptional("IndexFormat", G.IdxFormat, IndexFormat::Uint32);
635635
I.mapOptional("IndexCount", G.IndexCount, 0u);
636636
I.mapOptional("Opaque", G.Opaque, true);
637+
llvm::SmallVector<float> Transform;
638+
I.mapOptional("Transform", Transform);
639+
if (!Transform.empty()) {
640+
if (Transform.size() != 12) {
641+
I.setError(llvm::Twine("TriangleGeometry.Transform must have exactly 12 "
642+
"floats (3x4 row-major), got ") +
643+
llvm::Twine(Transform.size()));
644+
return;
645+
}
646+
std::array<float, 12> T;
647+
std::copy(Transform.begin(), Transform.end(), T.begin());
648+
G.Transform = T;
649+
}
637650
}
638651

639652
void MappingTraits<offloadtest::AABBGeometry>::mapping(
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#--- source.hlsl
2+
3+
[[vk::binding(0, 0)]] RaytracingAccelerationStructure Scene : register(t0);
4+
[[vk::binding(1, 0)]] RWStructuredBuffer<uint> Output : register(u0);
5+
6+
[numthreads(2,1,1)]
7+
void main(uint3 tid : SV_DispatchThreadID) {
8+
// The triangle vertices are centered around x=0, but a per-geometry
9+
// BLAS-bake transform translates them to x=5 at AS-build time. With an
10+
// identity-transform TLAS instance, only the ray at x=5 hits.
11+
RayDesc Ray;
12+
Ray.Origin = float3(tid.x == 0 ? 5.0 : 0.0, 0, 1);
13+
Ray.Direction = float3(0, 0, -1);
14+
Ray.TMin = 0.0;
15+
Ray.TMax = 100.0;
16+
RayQuery<RAY_FLAG_NONE> Q;
17+
Q.TraceRayInline(Scene, RAY_FLAG_NONE, 0xFF, Ray);
18+
Q.Proceed();
19+
Output[tid.x] = (uint)Q.CommittedStatus();
20+
}
21+
//--- pipeline.yaml
22+
---
23+
Shaders:
24+
- Stage: Compute
25+
Entry: main
26+
Buffers:
27+
- Name: Vertices
28+
Format: Float32
29+
Stride: 12
30+
Data: [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]
31+
- Name: Output
32+
Format: UInt32
33+
Stride: 4
34+
FillSize: 8
35+
- Name: Expected
36+
Format: UInt32
37+
Stride: 4
38+
# Lane 0: ray hits the baked-translated triangle → COMMITTED_TRIANGLE_HIT (1)
39+
# Lane 1: ray misses (triangle no longer at origin) → COMMITTED_NOTHING (0)
40+
Data: [ 1, 0 ]
41+
AccelerationStructures:
42+
BLAS:
43+
- Name: TriangleBLAS
44+
Triangles:
45+
- VertexBuffer: Vertices
46+
VertexFormat: RGB32Float
47+
VertexStride: 12
48+
VertexCount: 3
49+
# 3x4 row-major affine — translate x by +5.
50+
Transform: [1, 0, 0, 5, 0, 1, 0, 0, 0, 0, 1, 0]
51+
TLAS:
52+
- Name: Scene
53+
Instances:
54+
- BLAS: TriangleBLAS
55+
DescriptorSets:
56+
- Resources:
57+
- Name: Scene
58+
Kind: AccelerationStructure
59+
DirectXBinding:
60+
Register: 0
61+
Space: 0
62+
VulkanBinding:
63+
Binding: 0
64+
- Name: Output
65+
Kind: RWStructuredBuffer
66+
DirectXBinding:
67+
Register: 0
68+
Space: 0
69+
VulkanBinding:
70+
Binding: 1
71+
Results:
72+
- Result: GeometryTransform
73+
Rule: BufferExact
74+
Actual: Output
75+
Expected: Expected
76+
...
77+
#--- end
78+
79+
# REQUIRES: acceleration-structure
80+
# XFAIL: Clang
81+
82+
# RUN: split-file %s %t
83+
# RUN: %dxc_target -T cs_6_5 -Fo %t.o %t/source.hlsl
84+
# RUN: %offloader %t/pipeline.yaml %t.o

0 commit comments

Comments
 (0)