Skip to content

Commit 0fa4d10

Browse files
committed
Implement dedicated Vulkan Path Tracing benchmark and resolve GUI layout and path resolution bugs
1 parent 9ef658c commit 0fa4d10

10 files changed

Lines changed: 778 additions & 29 deletions

File tree

.github/workflows/release.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,45 @@ jobs:
5454
build/GPUBench-*-win64.exe
5555
env:
5656
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57+
58+
build-linux:
59+
runs-on: ubuntu-latest
60+
steps:
61+
- name: Checkout repository
62+
uses: actions/checkout@v3
63+
64+
- name: Install System Dependencies
65+
run: |
66+
sudo apt-get update
67+
sudo apt-get install -y libvulkan-dev vulkan-tools ocl-icd-opencl-dev opencl-headers pkg-config libx11-dev libwayland-dev libxkbcommon-dev libasound2-dev libfontconfig1-dev
68+
69+
- name: Install Rust
70+
uses: dtolnay/rust-toolchain@stable
71+
72+
- name: Configure CMake
73+
run: |
74+
mkdir build
75+
cd build
76+
cmake .. -DCMAKE_BUILD_TYPE=Release
77+
78+
- name: Build Target
79+
run: |
80+
cd build
81+
cmake --build . --config Release
82+
83+
- name: Package with CPack
84+
run: |
85+
cd build
86+
cpack -G TGZ
87+
cpack -G DEB
88+
cpack -G RPM
89+
90+
- name: Upload to Release
91+
uses: softprops/action-gh-release@v1
92+
with:
93+
files: |
94+
build/GPUBench-*-Linux.tar.gz
95+
build/GPUBench-*-Linux.deb
96+
build/GPUBench-*-Linux.rpm
97+
env:
98+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD 17)
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)
77

88
# Versioning
9-
set(BUILD_NUMBER "0") # Build number for the project
9+
set(BUILD_NUMBER "1") # Build number for the project
1010
set(PROJECT_FULL_VERSION "${PROJECT_VERSION}.${BUILD_NUMBER}")
1111

1212

@@ -190,6 +190,7 @@ set(LIB_SOURCES
190190
cpp_src/benchmarks/RayASBuildBench.cpp
191191
cpp_src/benchmarks/RayProceduralBench.cpp
192192
cpp_src/benchmarks/RayMaterialDivergenceBench.cpp
193+
cpp_src/benchmarks/RayPathTracingBench.cpp
193194
cpp_src/utils/KernelPath.cpp
194195
cpp_src/utils/ShaderCache.cpp
195196
)
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
#include "RayPathTracingBench.h"
2+
#include "core/VulkanContext.h"
3+
#include <algorithm>
4+
#include <chrono>
5+
#include <cstring>
6+
#include <filesystem>
7+
#include <iostream>
8+
9+
bool RayPathTracingBench::IsSupported(const DeviceInfo &info,
10+
IComputeContext *context) const {
11+
return info.rayTracingSupport &&
12+
(context && context->getBackend() == ComputeBackend::Vulkan);
13+
}
14+
15+
void RayPathTracingBench::loadRTProcs(VkDevice device) {
16+
vkGetAccelerationStructureBuildSizesKHR_ptr =
17+
(PFN_vkGetAccelerationStructureBuildSizesKHR)vkGetDeviceProcAddr(
18+
device, "vkGetAccelerationStructureBuildSizesKHR");
19+
vkCreateAccelerationStructureKHR_ptr =
20+
(PFN_vkCreateAccelerationStructureKHR)vkGetDeviceProcAddr(
21+
device, "vkCreateAccelerationStructureKHR");
22+
vkCmdBuildAccelerationStructuresKHR_ptr =
23+
(PFN_vkCmdBuildAccelerationStructuresKHR)vkGetDeviceProcAddr(
24+
device, "vkCmdBuildAccelerationStructuresKHR");
25+
vkGetAccelerationStructureDeviceAddressKHR_ptr =
26+
(PFN_vkGetAccelerationStructureDeviceAddressKHR)vkGetDeviceProcAddr(
27+
device, "vkGetAccelerationStructureDeviceAddressKHR");
28+
vkDestroyAccelerationStructureKHR_ptr =
29+
(PFN_vkDestroyAccelerationStructureKHR)vkGetDeviceProcAddr(
30+
device, "vkDestroyAccelerationStructureKHR");
31+
}
32+
33+
void RayPathTracingBench::Setup(IComputeContext &context,
34+
const std::string &kernel_dir) {
35+
this->context = &context;
36+
VulkanContext *vContext = dynamic_cast<VulkanContext *>(&context);
37+
if (!vContext)
38+
throw std::runtime_error("RayPathTracingBench requires VulkanContext");
39+
40+
loadRTProcs(vContext->getVulkanDevice());
41+
42+
// Setup ray workload (4M paths)
43+
rayCount = 4000000;
44+
resultBuffer = context.createBuffer(sizeof(uint32_t));
45+
uint32_t zero = 0;
46+
context.writeBuffer(resultBuffer, 0, 4, &zero);
47+
48+
// Setup Triangle and Box data (64 layers of 16x16 grids = 16,384 primitives)
49+
uint32_t gridSize = 16;
50+
uint32_t layers = 64;
51+
numPrimitives = gridSize * gridSize * layers;
52+
53+
std::vector<float> vertices;
54+
for (uint32_t z = 0; z < layers; ++z) {
55+
float jitterX = (z % 8) * 0.05f;
56+
float jitterY = (z / 8) * 0.05f;
57+
for (uint32_t y = 0; y < gridSize; ++y) {
58+
for (uint32_t x = 0; x < gridSize; ++x) {
59+
float fx = (float)x - 8.0f + jitterX;
60+
float fy = (float)y - 8.0f + jitterY;
61+
float fz = (float)z * 0.1f;
62+
vertices.push_back(fx + 0.1f);
63+
vertices.push_back(fy + 0.1f);
64+
vertices.push_back(fz);
65+
vertices.push_back(fx + 0.4f);
66+
vertices.push_back(fy + 0.1f);
67+
vertices.push_back(fz);
68+
vertices.push_back(fx + 0.1f);
69+
vertices.push_back(fy + 0.4f);
70+
vertices.push_back(fz);
71+
}
72+
}
73+
}
74+
vertexBuffer =
75+
context.createBuffer(vertices.size() * sizeof(float), vertices.data());
76+
77+
aabbBuffer = nullptr;
78+
79+
buildAS();
80+
81+
std::filesystem::path kdir(kernel_dir);
82+
std::filesystem::path kernel_file = kdir / "vulkan" / "rt_path_tracing.comp";
83+
kernel = context.createKernel(kernel_file.string(), "main", 2);
84+
}
85+
86+
void RayPathTracingBench::buildAS() {
87+
VulkanContext *vContext = static_cast<VulkanContext *>(context);
88+
VkDevice device = vContext->getVulkanDevice();
89+
VkQueue queue = vContext->getComputeQueue();
90+
91+
VkDeviceAddress vAddr = vContext->getBufferDeviceAddress(vertexBuffer);
92+
93+
// 1. Triangle BLAS
94+
VkAccelerationStructureGeometryKHR triGeom{
95+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
96+
triGeom.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
97+
triGeom.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
98+
triGeom.geometry.triangles.sType =
99+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
100+
triGeom.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
101+
triGeom.geometry.triangles.vertexData.deviceAddress = vAddr;
102+
triGeom.geometry.triangles.vertexStride = sizeof(float) * 3;
103+
triGeom.geometry.triangles.maxVertex = numPrimitives * 3;
104+
triGeom.geometry.triangles.indexType = VK_INDEX_TYPE_NONE_KHR;
105+
106+
VkAccelerationStructureBuildGeometryInfoKHR triBuildInfo{
107+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR};
108+
triBuildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
109+
triBuildInfo.flags =
110+
VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
111+
triBuildInfo.geometryCount = 1;
112+
triBuildInfo.pGeometries = &triGeom;
113+
triBuildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
114+
115+
uint32_t triMaxPrimCount = numPrimitives;
116+
VkAccelerationStructureBuildSizesInfoKHR triSizes{
117+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};
118+
vkGetAccelerationStructureBuildSizesKHR_ptr(
119+
device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &triBuildInfo,
120+
&triMaxPrimCount, &triSizes);
121+
122+
triangleBlasBuffer =
123+
context->createBuffer(triSizes.accelerationStructureSize);
124+
VkAccelerationStructureCreateInfoKHR triCreateInfo{
125+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR};
126+
triCreateInfo.buffer = vContext->getVkBuffer(triangleBlasBuffer);
127+
triCreateInfo.size = triSizes.accelerationStructureSize;
128+
triCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
129+
vkCreateAccelerationStructureKHR_ptr(device, &triCreateInfo, nullptr,
130+
&triangleBlas);
131+
132+
// 2. TLAS
133+
VkAccelerationStructureDeviceAddressInfoKHR triAddrInfo{
134+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR};
135+
triAddrInfo.accelerationStructure = triangleBlas;
136+
VkDeviceAddress triASAddr =
137+
vkGetAccelerationStructureDeviceAddressKHR_ptr(device, &triAddrInfo);
138+
139+
// Triangle Instance
140+
VkAccelerationStructureInstanceKHR triInstance = {};
141+
triInstance.transform = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0};
142+
triInstance.instanceCustomIndex = 0;
143+
triInstance.mask = 0xFF;
144+
triInstance.accelerationStructureReference = triASAddr;
145+
triInstance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
146+
147+
instanceBuffer =
148+
context->createBuffer(sizeof(VkAccelerationStructureInstanceKHR));
149+
context->writeBuffer(instanceBuffer, 0, sizeof(triInstance), &triInstance);
150+
151+
VkAccelerationStructureGeometryKHR topGeom{
152+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
153+
topGeom.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
154+
topGeom.geometry.instances.sType =
155+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
156+
topGeom.geometry.instances.data.deviceAddress =
157+
vContext->getBufferDeviceAddress(instanceBuffer);
158+
159+
VkAccelerationStructureBuildGeometryInfoKHR buildInfo{
160+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR};
161+
buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
162+
buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
163+
buildInfo.geometryCount = 1;
164+
buildInfo.pGeometries = &topGeom;
165+
buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
166+
167+
uint32_t maxPrimCount = 1; // One instance
168+
VkAccelerationStructureBuildSizesInfoKHR sizes{
169+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};
170+
vkGetAccelerationStructureBuildSizesKHR_ptr(
171+
device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo,
172+
&maxPrimCount, &sizes);
173+
174+
tlasBuffer = context->createBuffer(sizes.accelerationStructureSize);
175+
VkAccelerationStructureCreateInfoKHR createInfo{
176+
VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR};
177+
createInfo.buffer = vContext->getVkBuffer(tlasBuffer);
178+
createInfo.size = sizes.accelerationStructureSize;
179+
createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
180+
vkCreateAccelerationStructureKHR_ptr(device, &createInfo, nullptr,
181+
&sceneTlas);
182+
183+
size_t scratchSize = std::max(triSizes.buildScratchSize, sizes.buildScratchSize);
184+
scratchBuffer = context->createBuffer(scratchSize);
185+
VkDeviceAddress sAddr = vContext->getBufferDeviceAddress(scratchBuffer);
186+
187+
// Command compilation for structure builds
188+
VkCommandPoolCreateInfo cpInfo{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
189+
cpInfo.queueFamilyIndex = vContext->getComputeQueueFamilyIndex();
190+
VkCommandPool tmpPool;
191+
vkCreateCommandPool(device, &cpInfo, nullptr, &tmpPool);
192+
193+
VkCommandBufferAllocateInfo cbAlloc{
194+
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
195+
cbAlloc.commandPool = tmpPool;
196+
cbAlloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
197+
cbAlloc.commandBufferCount = 1;
198+
VkCommandBuffer cmd;
199+
vkAllocateCommandBuffers(device, &cbAlloc, &cmd);
200+
201+
VkCommandBufferBeginInfo begin{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
202+
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
203+
vkBeginCommandBuffer(cmd, &begin);
204+
205+
auto cmdBuild = [&](VkAccelerationStructureBuildGeometryInfoKHR &info,
206+
VkAccelerationStructureKHR dst, uint32_t primCount) {
207+
info.dstAccelerationStructure = dst;
208+
info.scratchData.deviceAddress = sAddr;
209+
VkAccelerationStructureBuildRangeInfoKHR range{primCount, 0, 0, 0};
210+
const VkAccelerationStructureBuildRangeInfoKHR *pRange = &range;
211+
vkCmdBuildAccelerationStructuresKHR_ptr(cmd, 1, &info, &pRange);
212+
213+
VkMemoryBarrier barrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER};
214+
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR;
215+
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
216+
vkCmdPipelineBarrier(cmd,
217+
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
218+
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
219+
0, 1, &barrier, 0, nullptr, 0, nullptr);
220+
};
221+
222+
cmdBuild(triBuildInfo, triangleBlas, numPrimitives);
223+
224+
buildInfo.dstAccelerationStructure = sceneTlas;
225+
buildInfo.scratchData.deviceAddress = sAddr;
226+
cmdBuild(buildInfo, sceneTlas, 1);
227+
228+
vkEndCommandBuffer(cmd);
229+
230+
VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO};
231+
submit.commandBufferCount = 1;
232+
submit.pCommandBuffers = &cmd;
233+
vkQueueSubmit(queue, 1, &submit, VK_NULL_HANDLE);
234+
vkQueueWaitIdle(queue);
235+
236+
vkDestroyCommandPool(device, tmpPool, nullptr);
237+
}
238+
239+
void RayPathTracingBench::Run(uint32_t config_idx) {
240+
VulkanContext *vContext = static_cast<VulkanContext *>(context);
241+
242+
// Clear results hits buffer for this timed run iteration
243+
uint32_t zero = 0;
244+
context->writeBuffer(resultBuffer, 0, 4, &zero);
245+
246+
vContext->setKernelAS(kernel, 0, (AccelerationStructure)sceneTlas);
247+
vContext->setKernelArg(kernel, 1, resultBuffer);
248+
249+
uint32_t bounces = (config_idx == 0) ? 2 : ((config_idx == 1) ? 4 : 8);
250+
uint32_t seed = rand();
251+
252+
vContext->setKernelArg(kernel, 2, sizeof(uint32_t), &rayCount);
253+
vContext->setKernelArg(kernel, 3, sizeof(uint32_t), &bounces);
254+
vContext->setKernelArg(kernel, 4, sizeof(uint32_t), &seed);
255+
256+
auto start = std::chrono::high_resolution_clock::now();
257+
vContext->dispatch(kernel, (rayCount + 31) / 32, 1, 1, 32, 1, 1);
258+
context->waitIdle();
259+
auto end = std::chrono::high_resolution_clock::now();
260+
261+
std::chrono::duration<double> diff = end - start;
262+
results[config_idx] = diff.count();
263+
}
264+
265+
void RayPathTracingBench::Teardown() {
266+
VulkanContext *vContext = static_cast<VulkanContext *>(context);
267+
VkDevice device = vContext->getVulkanDevice();
268+
269+
if (triangleBlas)
270+
vkDestroyAccelerationStructureKHR_ptr(device, triangleBlas, nullptr);
271+
if (boxBlas)
272+
vkDestroyAccelerationStructureKHR_ptr(device, boxBlas, nullptr);
273+
if (sceneTlas)
274+
vkDestroyAccelerationStructureKHR_ptr(device, sceneTlas, nullptr);
275+
276+
if (kernel)
277+
context->releaseKernel(kernel);
278+
if (resultBuffer)
279+
context->releaseBuffer(resultBuffer);
280+
if (vertexBuffer)
281+
context->releaseBuffer(vertexBuffer);
282+
if (aabbBuffer)
283+
context->releaseBuffer(aabbBuffer);
284+
if (instanceBuffer)
285+
context->releaseBuffer(instanceBuffer);
286+
if (triangleBlasBuffer)
287+
context->releaseBuffer(triangleBlasBuffer);
288+
if (boxBlasBuffer)
289+
context->releaseBuffer(boxBlasBuffer);
290+
if (tlasBuffer)
291+
context->releaseBuffer(tlasBuffer);
292+
if (scratchBuffer)
293+
context->releaseBuffer(scratchBuffer);
294+
}
295+
296+
BenchmarkResult RayPathTracingBench::GetResult(uint32_t config_idx) const {
297+
uint32_t bounces = (config_idx == 0) ? 2 : ((config_idx == 1) ? 4 : 8);
298+
// Return raw rays count. Formatter will divide by 1e6 to output MRays/s.
299+
return {(uint64_t)rayCount * bounces, results[config_idx]};
300+
}
301+
302+
const char *RayPathTracingBench::GetName() const { return "RayPathTracing"; }
303+
304+
const char *RayPathTracingBench::GetComponent(uint32_t config_idx) const {
305+
return "Ray Tracing";
306+
}
307+
308+
const char *RayPathTracingBench::GetMetric(uint32_t config_idx) const {
309+
return "MRays/s";
310+
}
311+
312+
const char *RayPathTracingBench::GetSubCategory(uint32_t config_idx) const {
313+
return "Path Tracing";
314+
}
315+
316+
std::string RayPathTracingBench::GetConfigName(uint32_t config_idx) const {
317+
return config_idx == 0 ? "2 Bounces" : ((config_idx == 1) ? "4 Bounces" : "8 Bounces");
318+
}

0 commit comments

Comments
 (0)