Skip to content

Commit abd68af

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 2fd3384 commit abd68af

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
@@ -1757,6 +1757,13 @@ class DXDevice : public offloadtest::Device {
17571757
Tri.IndexFormat = getDXGIIndexFormat(T.IdxFormat);
17581758
}
17591759

1760+
// Scratch sizing depends on whether Transform3x4 will be present at
1761+
// build time; signal that with any non-NULL sentinel here — the DXR
1762+
// spec lets the value be NULL or non-NULL for the prebuild query and
1763+
// does not dereference it.
1764+
if (T.Transform)
1765+
Tri.Transform3x4 = 1;
1766+
17601767
GeomDescs.push_back(GD);
17611768
}
17621769
return queryBLASPrebuildSize(GeomDescs);
@@ -3019,6 +3026,19 @@ llvm::Error DXComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
30193026
GD.Triangles.IndexCount = T.IndexCount;
30203027
GD.Triangles.IndexFormat = getDXGIIndexFormat(T.IdxFormat);
30213028
}
3029+
if (T.Transform) {
3030+
const BufferCreateDesc XformDesc{MemoryLocation::CpuToGpu,
3031+
BufferUsage::Storage};
3032+
auto XformOrErr = offloadtest::createBufferWithData(
3033+
*Dev, "AS-Geom-Transform", XformDesc, T.Transform->data(),
3034+
T.Transform->size() * sizeof(float), nullptr, nullptr);
3035+
if (!XformOrErr)
3036+
return XformOrErr.takeError();
3037+
auto *XformBuf = llvm::cast<DXBuffer>(XformOrErr->get());
3038+
GD.Triangles.Transform3x4 =
3039+
XformBuf->Buffer->GetGPUVirtualAddress();
3040+
CB.KeepAliveOwned.push_back(std::move(*XformOrErr));
3041+
}
30223042
GeomDescs.push_back(GD);
30233043
}
30243044
} else {

lib/API/Device.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ llvm::Error offloadtest::buildPipelineAccelerationStructures(
134134
TGD.VertexStride = T.VertexStride;
135135
TGD.VertexFormat = T.VertexFormat;
136136
TGD.Opaque = T.Opaque;
137+
TGD.Transform = T.Transform;
137138

138139
OutInputBuffers.push_back(std::move(*VBOrErr));
139140

lib/API/MTL/MTLDevice.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,6 +2446,18 @@ llvm::Error MTLComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
24462446
TD->setIndexType(getMetalIndexType(T.IdxFormat));
24472447
}
24482448
TD->setOpaque(T.Opaque);
2449+
if (T.Transform) {
2450+
MTL::Buffer *XformBuf = MTLDev->newBuffer(
2451+
T.Transform->data(), T.Transform->size() * sizeof(float),
2452+
MTL::ResourceStorageModeShared);
2453+
if (!XformBuf)
2454+
return llvm::createStringError(
2455+
std::errc::not_enough_memory,
2456+
"Failed to allocate BLAS transform buffer.");
2457+
TD->setTransformationMatrixBuffer(XformBuf);
2458+
TD->setTransformationMatrixLayout(MTL::MatrixLayoutRowMajor);
2459+
CB->KeepAliveMTLBuffers.push_back(XformBuf);
2460+
}
24492461
Geoms.push_back(TD);
24502462
}
24512463
} else {

lib/API/VK/Device.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4346,6 +4346,18 @@ llvm::Error VKComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
43464346
} else {
43474347
Tri.indexType = VK_INDEX_TYPE_NONE_KHR;
43484348
}
4349+
if (T.Transform) {
4350+
const BufferCreateDesc XformDesc{MemoryLocation::CpuToGpu,
4351+
BufferUsage::Storage};
4352+
auto XformOrErr = offloadtest::createBufferWithData(
4353+
*Dev, "AS-Geom-Transform", XformDesc, T.Transform->data(),
4354+
T.Transform->size() * sizeof(float), nullptr, nullptr);
4355+
if (!XformOrErr)
4356+
return XformOrErr.takeError();
4357+
auto *XformBuf = llvm::cast<VulkanBuffer>(XformOrErr->get());
4358+
Tri.transformData.deviceAddress = XformBuf->getDeviceAddress();
4359+
CB.KeepAliveOwned.push_back(std::move(*XformOrErr));
4360+
}
43494361
Geoms[I].push_back(G);
43504362

43514363
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)