aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--Android.mk6
-rw-r--r--docs/amber_script.md165
-rw-r--r--include/amber/amber_vulkan.h12
-rw-r--r--include/amber/recipe.h6
-rw-r--r--include/amber/shader_info.h7
-rw-r--r--samples/config_helper_vulkan.cc135
-rw-r--r--samples/config_helper_vulkan.h7
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/acceleration_structure.cc51
-rw-r--r--src/acceleration_structure.h257
-rw-r--r--src/amber.cc2
-rw-r--r--src/amberscript/parser.cc653
-rw-r--r--src/amberscript/parser.h13
-rw-r--r--src/amberscript/parser_device_property_test.cc120
-rw-r--r--src/amberscript/parser_raytracing_test.cc1297
-rw-r--r--src/command.cc15
-rw-r--r--src/command.h65
-rw-r--r--src/engine.h5
-rw-r--r--src/executor.cc3
-rw-r--r--src/executor_test.cc17
-rw-r--r--src/pipeline.cc40
-rw-r--r--src/pipeline.h97
-rw-r--r--src/recipe.cc4
-rw-r--r--src/script.cc28
-rw-r--r--src/script.h71
-rw-r--r--src/shader_compiler.cc12
-rw-r--r--src/vulkan/CMakeLists.txt6
-rw-r--r--src/vulkan/blas.cc268
-rw-r--r--src/vulkan/blas.h58
-rw-r--r--src/vulkan/command_buffer.cc24
-rw-r--r--src/vulkan/command_buffer.h3
-rw-r--r--src/vulkan/descriptor.cc3
-rw-r--r--src/vulkan/descriptor.h6
-rw-r--r--src/vulkan/device.cc161
-rw-r--r--src/vulkan/device.h6
-rw-r--r--src/vulkan/engine_vulkan.cc268
-rw-r--r--src/vulkan/engine_vulkan.h24
-rw-r--r--src/vulkan/graphics_pipeline.cc4
-rw-r--r--src/vulkan/pipeline.cc63
-rw-r--r--src/vulkan/pipeline.h9
-rw-r--r--src/vulkan/raytracing_pipeline.cc240
-rw-r--r--src/vulkan/raytracing_pipeline.h76
-rw-r--r--src/vulkan/resource.cc13
-rw-r--r--src/vulkan/resource.h27
-rw-r--r--src/vulkan/sbt.cc73
-rw-r--r--src/vulkan/sbt.h45
-rw-r--r--src/vulkan/tlas.cc245
-rw-r--r--src/vulkan/tlas.h53
-rw-r--r--src/vulkan/tlas_descriptor.cc86
-rw-r--r--src/vulkan/tlas_descriptor.h59
-rw-r--r--src/vulkan/transfer_buffer.cc18
-rw-r--r--src/vulkan/transfer_buffer.h2
-rw-r--r--src/vulkan/vk-funcs-1-0.inc1
-rw-r--r--src/vulkan/vk-funcs-1-1.inc9
55 files changed, 4816 insertions, 127 deletions
diff --git a/AUTHORS b/AUTHORS
index 8097dd7..a1724f8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -5,3 +5,4 @@
# of contributors, see the revision history in source control.
Google LLC
+Advanced Micro Devices, Inc.
diff --git a/Android.mk b/Android.mk
index 60c320c..fd1fe55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,6 +22,7 @@ LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti \
-DAMBER_ENABLE_SHADERC=1 \
-DAMBER_ENGINE_VULKAN=1
LOCAL_SRC_FILES:= \
+ src/acceleration_structure.cc \
src/amber.cc \
src/amberscript/parser.cc \
src/buffer.cc \
@@ -53,6 +54,7 @@ LOCAL_SRC_FILES:= \
src/vkscript/section_parser.cc \
src/vulkan/buffer_descriptor.cc \
src/vulkan/buffer_backed_descriptor.cc \
+ src/vulkan/blas.cc \
src/vulkan/command_buffer.cc \
src/vulkan/command_pool.cc \
src/vulkan/compute_pipeline.cc \
@@ -65,9 +67,13 @@ LOCAL_SRC_FILES:= \
src/vulkan/index_buffer.cc \
src/vulkan/pipeline.cc \
src/vulkan/push_constant.cc \
+ src/vulkan/raytracing_pipeline.cc \
src/vulkan/resource.cc \
src/vulkan/sampler.cc \
src/vulkan/sampler_descriptor.cc \
+ src/vulkan/sbt.cc \
+ src/vulkan/tlas.cc \
+ src/vulkan/tlas_descriptor.cc \
src/vulkan/transfer_buffer.cc \
src/vulkan/transfer_image.cc \
src/vulkan/vertex_buffer.cc \
diff --git a/docs/amber_script.md b/docs/amber_script.md
index 9bd58d7..7918764 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -149,11 +149,18 @@ SHADER {shader_type} {shader_name} {shader_format} [ TARGET_ENV {target_env} ] V
* `tessellation_evaluation`
* `tessellation_control`
* `compute`
+ * `ray_generation`
+ * `any_hit`
+ * `closest_hit`
+ * `miss`
+ * `intersection`
+ * `callable`
* `multi`
The compute pipeline can only contain compute shaders. The graphics pipeline
can not contain compute shaders, and must contain a vertex shader and a fragment
-shader.
+shader. Ray tracing pipeline can contain only shaders of ray tracing types:
+ray generation, any hit, closest hit, miss, intersection, and callable shaders.
The provided `multi` shader can only be used with `SPIRV-ASM` and `SPIRV-HEX`
and allows for providing multiple shaders in a single module (so the `vertex`
@@ -389,15 +396,107 @@ Note: currently the border color is always transparent black.
Note: the addressing mode is used for all coordinates currently. Arrayed images
should use `clamp_to_edge` for the array index.
+### Acceleration Structures
+
+Acceleration structures are used to enumerate geometries to describe a scene.
+There are two kinds of acceleration structures:
+ * Bottom level
+ * Top level
+
+#### Bottom Level
+
+Bottom level acceleration structures consists of a set of geometries.
+Each bottom level acceleration structure can consists either of triangle or
+axis aligned bounding box (AABB) geometries. It is prohibited to mix triangle
+geometries and AABBs inside same bottom level acceleration structures.
+
+A bottom level acceleration structure consisting of triangle geometries is defined as:
+
+```groovy
+ # Bottom level acceleration structure consisting of triangles
+ ACCELERATION_STRUCTURE BOTTOM_LEVEL {name_of_bottom_level_acceleration_structure}
+ {GEOMETRY TRIANGLES
+ {x0 y0 z0
+ x1 y1 z1
+ x2 y2 z2}+
+ END}+
+ END
+```
+
+A bottom level acceleration structure consisting of axis aligned bounding boxes is defined as:
+
+```groovy
+ # Bottom level acceleration structure consisting of AABBs
+ ACCELERATION_STRUCTURE BOTTOM_LEVEL {name_of_bottom_level_acceleration_structure}
+ {GEOMETRY AABBS
+ {x0 y0 z0 x1 y1 z1}+
+ END}+
+ END
+```
+
+Each coordinate |x{n}|, |y{n}|, and |z{n}| should be floating point values.
+
+#### Top Level
+
+Top level acceleration structures consists of a set of instances of bottom
+level acceleration structures.
+
+```groovy
+ # Acceleration structure with instance defined in one line
+ ACCELERATION_STRUCTURE TOP_LEVEL {name_of_top_level_acceleration_structure}
+ {BLAS_INSTANCE USE {name_of_bottom_level_acceleration_structure}}+
+ END
+
+ # Acceleration structure with instance defined in multiple lines
+ ACCELERATION_STRUCTURE TOP_LEVEL {name_of_top_level_acceleration_structure}
+ {BOTTOM_LEVEL_INSTANCE {name_of_bottom_level_acceleration_structure}
+ [INDEX {index}]
+ [OFFSET {offset}]
+ [FLAGS {flags}]
+ [MASK {mask}]
+ [TRANSFORM \
+ {transform} \
+ END]
+ END}+
+ END
+```
+
+The value of |index| should be an integer in range of [0..16,777,215] is a 24-bit user-specified
+index value accessible to ray shaders in the InstanceCustomIndexKHR built-in.
+
+The value of |offset| should be an integer in range of [0..16,777,215] is a 24-bit offset used
+in calculating the hit shader binding table index.
+
+The value of |mask| should be an integer in range of [0..255] (may be specified as 0xNN) is an
+8-bit visibility mask for the geometry.
+
+The value of |flags| is space-separated or EOL-separated list of following:
+ * `TRIANGLE_FACING_CULL_DISABLE`
+ * `TRIANGLE_FLIP_FACING`
+ * `FORCE_OPAQUE`
+ * `FORCE_NO_OPAQUE`
+ * `FORCE_OPACITY_MICROMAP_2_STATE`
+ * `DISABLE_OPACITY_MICROMAPS`
+ * <any integer number>
+
+If |flags| is a EOL-separated list it should be ended with END statement.
+If |flags| is a space-separated list it should not be ended with END statement.
+
+The |transform| is 12 space-separated values describing a 3x4 row-major affine transformation matrix applied to
+the acceleration structure.
+
+
### Pipelines
#### Pipeline type
* `compute`
* `graphics`
-
+ * `ray_tracing`
+
```groovy
-# The PIPELINE command creates a pipeline. This can be either compute or
-# graphics. Shaders are attached to the pipeline at pipeline creation time.
+# The PIPELINE command creates a pipeline. This can be either compute,
+# graphics, or ray_tracing. Shaders are attached to the pipeline
+# at pipeline creation time.
PIPELINE {pipeline_type} {pipeline_name}
...
END
@@ -458,6 +557,38 @@ The following commands are all specified within the `PIPELINE` command.
PATCH_CONTROL_POINTS {control_points}
```
+Ray tracing pipelines do not attach shaders directly like compute or graphics pipelines.
+Ray tracing pipelines organize shaders into shader groups in one of four ways
+depending on shader types used:
+
+```groovy
+ # Four possible shader group definitions
+ SHADER_GROUP {group_name_1} {ray_generation_shader_name}
+ SHADER_GROUP {group_name_2} {miss_shader_name}
+ SHADER_GROUP {group_name_3} {call_shader_name}
+ SHADER_GROUP {group_name_4} [closest_hit_shader_name] [any_hit_shader_name] [intersection_shader_name]
+```
+
+Shader group cannot be empty.
+Each group name must be unique within a pipeline. The same shader can be used within one or more
+shader groups. The shader group order is important, further commands as shader code might refer
+them directly. With the shader groups defined, they are then added into shader binding tables:
+
+```groovy
+ # Create shader binding tables and set shader groups into it
+ SHADER_BINDING_TABLE {sbt_name}
+ {group_name_1}
+ [ | {group_name_n}]
+ END
+```
+
+Generally a program needs three shader binding tables:
+ * ray generation shader binding table with one ray generation shader group
+ * miss shader binding table containing one or more miss shader groups
+ * hit shader binding table containing one or more hit shader groups
+
+Shader binding tables for call shaders are optional.
+
#### Compare operations
* `never`
* `less`
@@ -746,6 +877,13 @@ ranges can be used also with dynamic buffers.
INDEX_DATA {buffer_name}
```
+Ray tracing pipelines allow bind top level acceleration structures.
+
+```groovy
+ # Bind the top level acceleration structure at the given descriptor set and binding.
+ BIND ACCELERATION_STRUCTURE {tlas_name} DESCRIPTOR_SET _set_id_ BINDING _id_
+```
+
#### OpenCL Plain-Old-Data Arguments
OpenCL kernels can have plain-old-data (pod or pod_ubo in the desriptor map)
arguments set their data via this command. Amber will generate the appropriate
@@ -845,6 +983,25 @@ RUN {pipeline_name} DRAW_ARRAY AS {topology} INDEXED \
[ INSTANCE_COUNT _inst_count_value_ (default 1) ]
```
+```groovy
+# Run the |pipeline_name| which must be a `ray tracing` pipeline.
+# Next four shader binding table names should be specified:
+# * RAYGEN |ray_gen_sbt_name| - shader binding table containing ray generation shader group
+# * MISS |miss_sbt_name| - shader binding table containing one or more miss shader groups
+# * HIT |hit_sbt_name| - shader binding table containing one or more hit shader groups
+# * CALL |call_sbt_name| - shader binding table containing one or more call shader groups
+# RAYGEN is required, other shader binding tables (MISS, HIT and CALL) are optional.
+#
+# The pipeline will be run with the given ray tracing dimensions |x|, |y|, |z|.
+# Each of the x, y and z values must be a uint32.
+RUN {pipeline_name} \
+ RAYGEN {ray_gen_sbt_name} \
+ [MISS {miss_sbt_name}] \
+ [HIT {hit_sbt_name}] \
+ [CALL {call_sbt_name}] \
+ _x_ _y_ _z_
+```
+
### Repeating commands
```groovy
diff --git a/include/amber/amber_vulkan.h b/include/amber/amber_vulkan.h
index d031579..307a58a 100644
--- a/include/amber/amber_vulkan.h
+++ b/include/amber/amber_vulkan.h
@@ -49,6 +49,18 @@ struct VulkanEngineConfig : public EngineConfig {
/// the extension is not enabled, |available_features| will be used.
VkPhysicalDeviceFeatures2KHR available_features2;
+ /// Physical device properties available for |physical_device|. The
+ /// |available_properties| will be ignored if
+ /// VK_KHR_get_physical_device_properties2 is enabled, |available_properties2|
+ /// will be used in that case.
+ VkPhysicalDeviceProperties available_properties;
+
+ /// Physical device properties for |physical_device|.The
+ /// |available_properties2| will only be used if
+ /// VK_KHR_get_physical_device_properties2 is enabled. If the extension is not
+ /// enabled, |available_properties| will be used.
+ VkPhysicalDeviceProperties2KHR available_properties2;
+
/// Instance extensions available.
std::vector<std::string> available_instance_extensions;
diff --git a/include/amber/recipe.h b/include/amber/recipe.h
index 0fd2445..4b8d877 100644
--- a/include/amber/recipe.h
+++ b/include/amber/recipe.h
@@ -35,6 +35,9 @@ class RecipeImpl {
/// Returns required features in the given recipe.
virtual std::vector<std::string> GetRequiredFeatures() const = 0;
+ /// Returns required features in the given recipe.
+ virtual std::vector<std::string> GetRequiredProperties() const = 0;
+
/// Returns required device extensions in the given recipe.
virtual std::vector<std::string> GetRequiredDeviceExtensions() const = 0;
@@ -67,6 +70,9 @@ class Recipe {
/// Returns required features in the given recipe.
std::vector<std::string> GetRequiredFeatures() const;
+ /// Returns required properties in the given recipe.
+ std::vector<std::string> GetRequiredProperties() const;
+
/// Returns required device extensions in the given recipe.
std::vector<std::string> GetRequiredDeviceExtensions() const;
diff --git a/include/amber/shader_info.h b/include/amber/shader_info.h
index b5ce751..84ee153 100644
--- a/include/amber/shader_info.h
+++ b/include/amber/shader_info.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,6 +39,12 @@ enum ShaderType {
kShaderTypeVertex,
kShaderTypeTessellationControl,
kShaderTypeTessellationEvaluation,
+ kShaderTypeRayGeneration,
+ kShaderTypeAnyHit,
+ kShaderTypeClosestHit,
+ kShaderTypeMiss,
+ kShaderTypeIntersection,
+ kShaderTypeCall,
kShaderTypeMulti,
};
diff --git a/samples/config_helper_vulkan.cc b/samples/config_helper_vulkan.cc
index 104e1dc..95900e3 100644
--- a/samples/config_helper_vulkan.cc
+++ b/samples/config_helper_vulkan.cc
@@ -72,6 +72,13 @@ const char kComputeFullSubgroups[] = "SubgroupSizeControl.computeFullSubgroups";
const char kShaderSubgroupExtendedTypes[] =
"ShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes";
+const char kAccelerationStructure[] =
+ "AccelerationStructureFeaturesKHR.accelerationStructure";
+const char kBufferDeviceAddress[] =
+ "BufferDeviceAddressFeatures.bufferDeviceAddress";
+const char kRayTracingPipeline[] =
+ "RayTracingPipelineFeaturesKHR.rayTracingPipeline";
+
const char kExtensionForValidationLayer[] = "VK_EXT_debug_report";
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flag,
@@ -649,7 +656,13 @@ ConfigHelperVulkan::ConfigHelperVulkan()
storage_8bit_feature_(VkPhysicalDevice8BitStorageFeaturesKHR()),
storage_16bit_feature_(VkPhysicalDevice16BitStorageFeaturesKHR()),
subgroup_size_control_feature_(
- VkPhysicalDeviceSubgroupSizeControlFeaturesEXT()) {}
+ VkPhysicalDeviceSubgroupSizeControlFeaturesEXT()),
+ acceleration_structure_feature_(
+ VkPhysicalDeviceAccelerationStructureFeaturesKHR()),
+ buffer_device_address_feature_(
+ VkPhysicalDeviceBufferDeviceAddressFeatures()),
+ ray_tracing_pipeline_feature_(
+ VkPhysicalDeviceRayTracingPipelineFeaturesKHR()) {}
ConfigHelperVulkan::~ConfigHelperVulkan() {
if (vulkan_device_)
@@ -795,6 +808,12 @@ amber::Result ConfigHelperVulkan::CheckVulkanPhysicalDeviceRequirements(
supports_shader_subgroup_extended_types_ = true;
else if (ext == "VK_KHR_variable_pointers")
supports_variable_pointers_ = true;
+ else if (ext == "VK_KHR_acceleration_structure")
+ supports_acceleration_structure_ = true;
+ else if (ext == "VK_KHR_buffer_device_address")
+ supports_buffer_device_address_ = true;
+ else if (ext == "VK_KHR_ray_tracing_pipeline")
+ supports_ray_tracing_pipeline_ = true;
}
VkPhysicalDeviceFeatures required_vulkan_features =
@@ -809,39 +828,71 @@ amber::Result ConfigHelperVulkan::CheckVulkanPhysicalDeviceRequirements(
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features = {};
VkPhysicalDevice8BitStorageFeaturesKHR storage_8bit_features = {};
VkPhysicalDevice16BitStorageFeaturesKHR storage_16bit_features = {};
+ VkPhysicalDeviceAccelerationStructureFeaturesKHR
+ acceleration_structure_features = {};
+ VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_address_features =
+ {};
+ VkPhysicalDeviceRayTracingPipelineFeaturesKHR
+ ray_tracing_pipeline_features = {};
+ void* next_ptr = nullptr;
- subgroup_size_control_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
- subgroup_size_control_features.pNext = nullptr;
+ if (supports_subgroup_size_control_) {
+ subgroup_size_control_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
+ subgroup_size_control_features.pNext = next_ptr;
+ next_ptr = &subgroup_size_control_features;
+ }
- // Add subgroup size control struct into the chain only if
- // VK_EXT_subgroup_size_control is supported.
variable_pointers_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
- variable_pointers_features.pNext = supports_subgroup_size_control_
- ? &subgroup_size_control_features
- : nullptr;
+ variable_pointers_features.pNext = next_ptr;
+ next_ptr = &variable_pointers_features;
shader_subgroup_extended_types_features.sType =
// NOLINTNEXTLINE(whitespace/line_length)
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES;
- shader_subgroup_extended_types_features.pNext = &variable_pointers_features;
+ shader_subgroup_extended_types_features.pNext = next_ptr;
+ next_ptr = &shader_subgroup_extended_types_features;
float16_int8_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
- float16_int8_features.pNext = &shader_subgroup_extended_types_features;
+ float16_int8_features.pNext = next_ptr;
+ next_ptr = &float16_int8_features;
storage_8bit_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR;
- storage_8bit_features.pNext = &float16_int8_features;
+ storage_8bit_features.pNext = next_ptr;
+ next_ptr = &storage_8bit_features;
storage_16bit_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR;
- storage_16bit_features.pNext = &storage_8bit_features;
+ storage_16bit_features.pNext = next_ptr;
+ next_ptr = &storage_16bit_features;
+
+ if (supports_acceleration_structure_) {
+ acceleration_structure_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
+ acceleration_structure_features.pNext = next_ptr;
+ next_ptr = &acceleration_structure_features;
+ }
+
+ if (supports_buffer_device_address_) {
+ buffer_device_address_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
+ buffer_device_address_features.pNext = next_ptr;
+ next_ptr = &buffer_device_address_features;
+ }
+
+ if (supports_ray_tracing_pipeline_) {
+ ray_tracing_pipeline_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR;
+ ray_tracing_pipeline_features.pNext = next_ptr;
+ next_ptr = &ray_tracing_pipeline_features;
+ }
VkPhysicalDeviceFeatures2KHR features2 = VkPhysicalDeviceFeatures2KHR();
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
- features2.pNext = &storage_16bit_features;
+ features2.pNext = next_ptr;
auto vkGetPhysicalDeviceFeatures2KHR =
reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
@@ -889,7 +940,13 @@ amber::Result ConfigHelperVulkan::CheckVulkanPhysicalDeviceRequirements(
VK_FALSE) ||
(feature == kShaderSubgroupExtendedTypes &&
shader_subgroup_extended_types_features
- .shaderSubgroupExtendedTypes == VK_FALSE)) {
+ .shaderSubgroupExtendedTypes == VK_FALSE) ||
+ (feature == kAccelerationStructure &&
+ acceleration_structure_features.accelerationStructure == VK_FALSE) ||
+ (feature == kBufferDeviceAddress &&
+ buffer_device_address_features.bufferDeviceAddress == VK_FALSE) ||
+ (feature == kRayTracingPipeline &&
+ ray_tracing_pipeline_features.rayTracingPipeline == VK_FALSE)) {
return amber::Result("Device does not support all required features");
}
}
@@ -1047,6 +1104,18 @@ amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures2(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES;
shader_subgroup_extended_types_feature_.pNext = nullptr;
+ acceleration_structure_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
+ acceleration_structure_feature_.pNext = nullptr;
+
+ buffer_device_address_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
+ buffer_device_address_feature_.pNext = nullptr;
+
+ ray_tracing_pipeline_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR;
+ ray_tracing_pipeline_feature_.pNext = nullptr;
+
std::vector<std::string> exts = required_extensions;
void* pnext = nullptr;
@@ -1114,6 +1183,36 @@ amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures2(
next_ptr = &shader_subgroup_extended_types_feature_.pNext;
}
+ if (supports_acceleration_structure_) {
+ if (pnext == nullptr) {
+ pnext = &acceleration_structure_feature_;
+ }
+ if (next_ptr != nullptr) {
+ *next_ptr = &acceleration_structure_feature_;
+ }
+ next_ptr = &acceleration_structure_feature_.pNext;
+ }
+
+ if (supports_buffer_device_address_) {
+ if (pnext == nullptr) {
+ pnext = &buffer_device_address_feature_;
+ }
+ if (next_ptr != nullptr) {
+ *next_ptr = &buffer_device_address_feature_;
+ }
+ next_ptr = &buffer_device_address_feature_.pNext;
+ }
+
+ if (supports_ray_tracing_pipeline_) {
+ if (pnext == nullptr) {
+ pnext = &ray_tracing_pipeline_feature_;
+ }
+ if (next_ptr != nullptr) {
+ *next_ptr = &ray_tracing_pipeline_feature_;
+ }
+ next_ptr = &ray_tracing_pipeline_feature_.pNext;
+ }
+
available_features2_.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
available_features2_.pNext = pnext;
@@ -1163,6 +1262,12 @@ amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures2(
else if (feature == kShaderSubgroupExtendedTypes)
shader_subgroup_extended_types_feature_.shaderSubgroupExtendedTypes =
VK_TRUE;
+ else if (feature == kAccelerationStructure)
+ acceleration_structure_feature_.accelerationStructure = VK_TRUE;
+ else if (feature == kBufferDeviceAddress)
+ buffer_device_address_feature_.bufferDeviceAddress = VK_TRUE;
+ else if (feature == kRayTracingPipeline)
+ ray_tracing_pipeline_feature_.rayTracingPipeline = VK_TRUE;
}
VkPhysicalDeviceFeatures required_vulkan_features =
diff --git a/samples/config_helper_vulkan.h b/samples/config_helper_vulkan.h
index 6fe3283..4cb90ff 100644
--- a/samples/config_helper_vulkan.h
+++ b/samples/config_helper_vulkan.h
@@ -119,6 +119,9 @@ class ConfigHelperVulkan : public ConfigHelperImpl {
bool supports_shader_16bit_storage_ = false;
bool supports_subgroup_size_control_ = false;
bool supports_shader_subgroup_extended_types_ = false;
+ bool supports_acceleration_structure_ = false;
+ bool supports_buffer_device_address_ = false;
+ bool supports_ray_tracing_pipeline_ = false;
VkPhysicalDeviceFeatures available_features_;
VkPhysicalDeviceFeatures2KHR available_features2_;
VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers_feature_;
@@ -128,6 +131,10 @@ class ConfigHelperVulkan : public ConfigHelperImpl {
VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_size_control_feature_;
VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures
shader_subgroup_extended_types_feature_;
+ VkPhysicalDeviceAccelerationStructureFeaturesKHR
+ acceleration_structure_feature_;
+ VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_address_feature_;
+ VkPhysicalDeviceRayTracingPipelineFeaturesKHR ray_tracing_pipeline_feature_;
};
} // namespace sample
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3b7a4a5..0819840 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2018 The Amber Authors.
+# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +14,7 @@
# limitations under the License.
set(AMBER_SOURCES
+ acceleration_structure.cc
amber.cc
amberscript/parser.cc
buffer.cc
@@ -141,12 +143,14 @@ if (${AMBER_ENABLE_TESTS})
amberscript/parser_copy_test.cc
amberscript/parser_depth_test.cc
amberscript/parser_device_feature_test.cc
+ amberscript/parser_device_property_test.cc
amberscript/parser_expect_test.cc
amberscript/parser_extension_test.cc
amberscript/parser_framebuffer_test.cc
amberscript/parser_image_test.cc
amberscript/parser_pipeline_test.cc
amberscript/parser_pipeline_set_test.cc
+ amberscript/parser_raytracing_test.cc
amberscript/parser_repeat_test.cc
amberscript/parser_run_test.cc
amberscript/parser_sampler_test.cc
diff --git a/src/acceleration_structure.cc b/src/acceleration_structure.cc
new file mode 100644
index 0000000..4507b5e
--- /dev/null
+++ b/src/acceleration_structure.cc
@@ -0,0 +1,51 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/acceleration_structure.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstring>
+
+namespace amber {
+
+Geometry::Geometry() = default;
+Geometry::~Geometry() = default;
+
+BLAS::BLAS() = default;
+BLAS::~BLAS() = default;
+
+BLASInstance::~BLASInstance() = default;
+
+TLAS::TLAS() = default;
+TLAS::~TLAS() = default;
+
+ShaderGroup::ShaderGroup()
+ : name_(),
+ generalShader_(nullptr),
+ closestHitShader_(nullptr),
+ anyHitShader_(nullptr),
+ intersectionShader_(nullptr) {}
+
+ShaderGroup::~ShaderGroup() = default;
+
+SBTRecord::SBTRecord() = default;
+SBTRecord::~SBTRecord() = default;
+
+SBT::SBT() = default;
+SBT::~SBT() = default;
+
+} // namespace amber
diff --git a/src/acceleration_structure.h b/src/acceleration_structure.h
new file mode 100644
index 0000000..a5a3218
--- /dev/null
+++ b/src/acceleration_structure.h
@@ -0,0 +1,257 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_ACCELERATION_STRUCTURE_H_
+#define SRC_ACCELERATION_STRUCTURE_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "amber/amber.h"
+#include "amber/result.h"
+#include "amber/value.h"
+#include "src/format.h"
+#include "src/image.h"
+
+namespace amber {
+
+enum class GeometryType : int8_t {
+ kUnknown = 0,
+ kTriangle,
+ kAABB,
+};
+
+class Shader;
+
+class Geometry {
+ public:
+ Geometry();
+ ~Geometry();
+
+ void SetType(GeometryType type) { type_ = type; }
+ GeometryType GetType() { return type_; }
+
+ void SetData(std::vector<float>& data) { data_.swap(data); }
+ std::vector<float>& GetData() { return data_; }
+
+ size_t getVertexCount() const {
+ return data_.size() / 3; // Three floats to define vertex
+ }
+
+ size_t getPrimitiveCount() const {
+ return IsTriangle() ? (getVertexCount() / 3) // 3 vertices per triangle
+ : IsAABB() ? (getVertexCount() / 2) // 2 vertices per AABB
+ : 0;
+ }
+
+ bool IsTriangle() const { return type_ == GeometryType::kTriangle; }
+ bool IsAABB() const { return type_ == GeometryType::kAABB; }
+
+ private:
+ GeometryType type_ = GeometryType::kUnknown;
+ std::vector<float> data_;
+};
+
+class BLAS {
+ public:
+ BLAS();
+ ~BLAS();
+
+ void SetName(const std::string& name) { name_ = name; }
+ std::string GetName() const { return name_; }
+
+ void AddGeometry(std::unique_ptr<Geometry>* geometry) {
+ geometry_.push_back(std::move(*geometry));
+ }
+ size_t GetGeometrySize() { return geometry_.size(); }
+ std::vector<std::unique_ptr<Geometry>>& GetGeometries() { return geometry_; }
+
+ private:
+ std::string name_;
+ std::vector<std::unique_ptr<Geometry>> geometry_;
+};
+
+class BLASInstance {
+ public:
+ BLASInstance()
+ : used_blas_name_(),
+ used_blas_(nullptr),
+ transform_(0),
+ instance_custom_index_(0),
+ mask_(0xFF),
+ instanceShaderBindingTableRecordOffset_(0),
+ flags_(0) {}
+ ~BLASInstance();
+
+ void SetUsedBLAS(const std::string& name, BLAS* blas) {
+ used_blas_name_ = name;
+ used_blas_ = blas;
+ }
+ std::string GetUsedBLASName() const { return used_blas_name_; }
+ BLAS* GetUsedBLAS() const { return used_blas_; }
+
+ void SetTransform(const std::vector<float>& transform) {
+ transform_ = transform;
+ }
+ const float* GetTransform() const { return transform_.data(); }
+
+ void SetInstanceIndex(uint32_t instance_custom_index) {
+ instance_custom_index_ = instance_custom_index;
+ // Make sure argument was not cut off
+ assert(instance_custom_index_ == instance_custom_index);
+ }
+ uint32_t GetInstanceIndex() const { return instance_custom_index_; }
+
+ void SetMask(uint32_t mask) {
+ mask_ = mask;
+ // Make sure argument was not cut off
+ assert(mask_ == mask);
+ }
+ uint32_t GetMask() const { return mask_; }
+
+ void SetOffset(uint32_t offset) {
+ instanceShaderBindingTableRecordOffset_ = offset;
+ // Make sure argument was not cut off
+ assert(instanceShaderBindingTableRecordOffset_ == offset);
+ }
+ uint32_t GetOffset() const { return instanceShaderBindingTableRecordOffset_; }
+
+ void SetFlags(uint32_t flags) {
+ flags_ = flags;
+ // Make sure argument was not cut off
+ assert(flags_ == flags);
+ }
+ uint32_t GetFlags() const { return flags_; }
+
+ private:
+ std::string used_blas_name_;
+ BLAS* used_blas_;
+ std::vector<float> transform_;
+ uint32_t instance_custom_index_ : 24;
+ uint32_t mask_ : 8;
+ uint32_t instanceShaderBindingTableRecordOffset_ : 24;
+ uint32_t flags_ : 8;
+};
+
+class TLAS {
+ public:
+ TLAS();
+ ~TLAS();
+
+ void SetName(const std::string& name) { name_ = name; }
+ std::string GetName() const { return name_; }
+
+ void AddInstance(std::unique_ptr<BLASInstance> instance) {
+ blas_instances_.push_back(
+ std::unique_ptr<BLASInstance>(instance.release()));
+ }
+ size_t GetInstanceSize() { return blas_instances_.size(); }
+ std::vector<std::unique_ptr<BLASInstance>>& GetInstances() {
+ return blas_instances_;
+ }
+
+ private:
+ std::string name_;
+ std::vector<std::unique_ptr<BLASInstance>> blas_instances_;
+};
+
+class ShaderGroup {
+ public:
+ ShaderGroup();
+ ~ShaderGroup();
+
+ void SetName(const std::string& name) { name_ = name; }
+ std::string GetName() const { return name_; }
+
+ void SetGeneralShader(Shader* shader) { generalShader_ = shader; }
+ Shader* GetGeneralShader() const { return generalShader_; }
+
+ void SetClosestHitShader(Shader* shader) { closestHitShader_ = shader; }
+ Shader* GetClosestHitShader() const { return closestHitShader_; }
+
+ void SetAnyHitShader(Shader* shader) { anyHitShader_ = shader; }
+ Shader* GetAnyHitShader() const { return anyHitShader_; }
+
+ void SetIntersectionShader(Shader* shader) { intersectionShader_ = shader; }
+ Shader* GetIntersectionShader() const { return intersectionShader_; }
+
+ bool IsGeneralGroup() const { return generalShader_ != nullptr; }
+ bool IsHitGroup() const {
+ return closestHitShader_ != nullptr || anyHitShader_ != nullptr ||
+ intersectionShader_ != nullptr;
+ }
+
+ private:
+ std::string name_;
+ Shader* generalShader_;
+ Shader* closestHitShader_;
+ Shader* anyHitShader_;
+ Shader* intersectionShader_;
+};
+
+class SBTRecord {
+ public:
+ SBTRecord();
+ ~SBTRecord();
+
+ void SetUsedShaderGroupName(const std::string& shader_group_name,
+ uint32_t pipeline_index) {
+ used_shader_group_name_ = shader_group_name;
+ pipeline_index_ = pipeline_index;
+ }
+ std::string GetUsedShaderGroupName() const { return used_shader_group_name_; }
+ uint32_t GetUsedShaderGroupPipelineIndex() const { return pipeline_index_; }
+
+ void SetCount(const uint32_t count) { count_ = count; }
+ uint32_t GetCount() const { return count_; }
+
+ private:
+ std::string used_shader_group_name_;
+ uint32_t count_ = 1;
+ uint32_t pipeline_index_ = static_cast<uint32_t>(-1);
+};
+
+class SBT {
+ public:
+ SBT();
+ ~SBT();
+
+ void SetName(const std::string& name) { name_ = name; }
+ std::string GetName() const { return name_; }
+
+ void AddSBTRecord(std::unique_ptr<SBTRecord> record) {
+ records_.push_back(std::move(record));
+ }
+ size_t GetSBTRecordCount() { return records_.size(); }
+ std::vector<std::unique_ptr<SBTRecord>>& GetSBTRecords() { return records_; }
+ uint32_t GetSBTSize() {
+ uint32_t size = 0;
+ for (auto& x : records_)
+ size += x->GetCount();
+
+ return size;
+ }
+
+ private:
+ std::string name_;
+ std::vector<std::unique_ptr<SBTRecord>> records_;
+};
+
+} // namespace amber
+
+#endif // SRC_ACCELERATION_STRUCTURE_H_
diff --git a/src/amber.cc b/src/amber.cc
index 9bf806e..ef5658f 100644
--- a/src/amber.cc
+++ b/src/amber.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -133,6 +134,7 @@ Result CreateEngineAndCheckRequirements(const Recipe* recipe,
// much else. Refactor this if they end up doing to much here.
Result r =
engine->Initialize(opts->config, delegate, script->GetRequiredFeatures(),
+ script->GetRequiredProperties(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
if (!r.IsSuccess())
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index a2e837f..14bd386 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,6 +23,7 @@
#include <utility>
#include <vector>
+#include "amber/vulkan_header.h"
#include "src/image.h"
#include "src/make_unique.h"
#include "src/sampler.h"
@@ -304,6 +306,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseDeviceFeature();
} else if (tok == "DEVICE_EXTENSION") {
r = ParseDeviceExtension();
+ } else if (tok == "DEVICE_PROPERTY") {
+ r = ParseDeviceProperty();
} else if (tok == "IMAGE") {
r = ParseImage();
} else if (tok == "INSTANCE_EXTENSION") {
@@ -322,6 +326,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseSampler();
} else if (tok == "VIRTUAL_FILE") {
r = ParseVirtualFile();
+ } else if (tok == "ACCELERATION_STRUCTURE") {
+ r = ParseAS();
} else {
r = Result("unknown token: " + tok);
}
@@ -404,6 +410,18 @@ Result Parser::ToShaderType(const std::string& str, ShaderType* type) {
*type = kShaderTypeTessellationControl;
else if (str == "compute")
*type = kShaderTypeCompute;
+ else if (str == "ray_generation")
+ *type = kShaderTypeRayGeneration;
+ else if (str == "any_hit")
+ *type = kShaderTypeAnyHit;
+ else if (str == "closest_hit")
+ *type = kShaderTypeClosestHit;
+ else if (str == "miss")
+ *type = kShaderTypeMiss;
+ else if (str == "intersection")
+ *type = kShaderTypeIntersection;
+ else if (str == "callable")
+ *type = kShaderTypeCall;
else if (str == "multi")
*type = kShaderTypeMulti;
else
@@ -436,6 +454,8 @@ Result Parser::ToPipelineType(const std::string& str, PipelineType* type) {
*type = PipelineType::kCompute;
else if (str == "graphics")
*type = PipelineType::kGraphics;
+ else if (str == "raytracing")
+ *type = PipelineType::kRayTracing;
else
return Result("unknown pipeline type: " + str);
return {};
@@ -548,6 +568,9 @@ Result Parser::ParseShaderBlock() {
if (!token->IsIdentifier() || token->AsString() != "END")
return Result("SHADER missing END command");
+ if (shader->GetTargetEnv().empty() && IsRayTracingShader(type))
+ shader->SetTargetEnv("spv1.4");
+
r = script_->AddShader(std::move(shader));
if (!r.IsSuccess())
return r;
@@ -624,6 +647,10 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
r = ParsePipelinePatchControlPoints(pipeline.get());
} else if (tok == "BLEND") {
r = ParsePipelineBlend(pipeline.get());
+ } else if (tok == "SHADER_GROUP") {
+ r = ParsePipelineShaderGroup(pipeline.get());
+ } else if (tok == "SHADER_BINDING_TABLE") {
+ r = ParseSBT(pipeline.get());
} else {
r = Result("unknown token in pipeline block: " + tok);
}
@@ -1075,8 +1102,8 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
if (!token->IsIdentifier()) {
return Result(
- "missing BUFFER, BUFFER_ARRAY, SAMPLER, or SAMPLER_ARRAY in BIND "
- "command");
+ "missing BUFFER, BUFFER_ARRAY, SAMPLER, SAMPLER_ARRAY, or "
+ "ACCELERATION_STRUCTURE in BIND command");
}
auto object_type = token->AsString();
@@ -1416,6 +1443,38 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
} else {
return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
}
+ } else if (object_type == "ACCELERATION_STRUCTURE") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result(
+ "missing top level acceleration structure name in BIND command");
+
+ TLAS* tlas = script_->GetTLAS(token->AsString());
+ if (!tlas)
+ return Result("unknown top level acceleration structure: " +
+ token->AsString());
+
+ token = tokenizer_->NextToken();
+ if (token->AsString() == "DESCRIPTOR_SET") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for DESCRIPTOR_SET in BIND command");
+ uint32_t descriptor_set = token->AsUint32();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "BINDING")
+ return Result("missing BINDING for BIND command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for BINDING in BIND command");
+
+ uint32_t binding = token->AsUint32();
+
+ pipeline->AddTLAS(tlas, descriptor_set, binding);
+ } else {
+ return Result("missing DESCRIPTOR_SET or BINDING in BIND command");
+ }
} else {
return Result("missing BUFFER or SAMPLER in BIND command");
}
@@ -1955,6 +2014,81 @@ Result Parser::ParsePipelineBlend(Pipeline* pipeline) {
return ValidateEndOfStatement("BLEND command");
}
+Result Parser::ParsePipelineShaderGroup(Pipeline* pipeline) {
+ std::unique_ptr<Token> token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("Group name expected");
+
+ auto tok = token->AsString();
+ if (pipeline->GetShaderGroup(tok))
+ return Result("Group name already exists");
+ std::unique_ptr<ShaderGroup> group = MakeUnique<ShaderGroup>();
+ group->SetName(tok);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ break;
+ if (!token->IsIdentifier())
+ return Result("Shader name expected");
+
+ tok = token->AsString();
+ Shader* shader = script_->GetShader(tok);
+ if (shader == nullptr)
+ return Result("Shader not found: " + tok);
+
+ Result r = pipeline->AddShader(shader, shader->GetType());
+ if (!r.IsSuccess())
+ return r;
+
+ switch (shader->GetType()) {
+ case kShaderTypeRayGeneration:
+ case kShaderTypeMiss:
+ case kShaderTypeCall: {
+ if (group->IsHitGroup())
+ return Result("Hit group cannot contain general shaders");
+ if (group->GetGeneralShader() != nullptr)
+ return Result("Two general shaders cannot be in one group");
+ group->SetGeneralShader(shader);
+ break;
+ }
+ case kShaderTypeAnyHit: {
+ if (group->IsGeneralGroup())
+ return Result("General group cannot contain any hit shaders");
+ if (group->GetAnyHitShader() != nullptr)
+ return Result("Two any hit shaders cannot be in one group");
+ group->SetAnyHitShader(shader);
+ break;
+ }
+ case kShaderTypeClosestHit: {
+ if (group->IsGeneralGroup())
+ return Result("General group cannot contain closest hit shaders");
+ if (group->GetClosestHitShader() != nullptr)
+ return Result("Two closest hit shaders cannot be in one group");
+ group->SetClosestHitShader(shader);
+ break;
+ }
+ case kShaderTypeIntersection: {
+ if (group->IsGeneralGroup())
+ return Result("General group cannot contain intersection shaders");
+ if (group->GetIntersectionShader() != nullptr)
+ return Result("Two intersection shaders cannot be in one group");
+ group->SetIntersectionShader(shader);
+ break;
+ }
+ default:
+ return Result("Shader must be of raytracing type");
+ }
+ }
+
+ if (!group->IsGeneralGroup() && !group->IsHitGroup())
+ return Result("No shaders in shader group defined");
+
+ pipeline->AddShaderGroup(std::move(group));
+
+ return {};
+}
+
Result Parser::ParseStruct() {
auto token = tokenizer_->NextToken();
if (!token->IsIdentifier())
@@ -2572,6 +2706,71 @@ Result Parser::ParseRun() {
if (!pipeline)
return Result("unknown pipeline for RUN command: " + token->AsString());
+ if (pipeline->IsRayTracing()) {
+ auto cmd = MakeUnique<RayTracingCommand>(pipeline);
+ cmd->SetLine(line);
+
+ while (true) {
+ if (tokenizer_->PeekNextToken()->IsInteger())
+ break;
+
+ token = tokenizer_->NextToken();
+
+ if (token->IsEOL() || token->IsEOS())
+ return Result("Incomplete RUN command");
+
+ if (!token->IsIdentifier())
+ return Result("Shader binding table type is expected");
+
+ std::string tok = token->AsString();
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("Shader binding table name expected");
+
+ std::string sbtname = token->AsString();
+ if (pipeline->GetSBT(sbtname) == nullptr)
+ return Result("Shader binding table with this name was not defined");
+
+ if (tok == "RAYGEN") {
+ if (!cmd->GetRayGenSBTName().empty())
+ return Result("RAYGEN shader binding table can specified only once");
+ cmd->SetRayGenSBTName(sbtname);
+ } else if (tok == "MISS") {
+ if (!cmd->GetMissSBTName().empty())
+ return Result("MISS shader binding table can specified only once");
+ cmd->SetMissSBTName(sbtname);
+ } else if (tok == "HIT") {
+ if (!cmd->GetHitsSBTName().empty())
+ return Result("HIT shader binding table can specified only once");
+ cmd->SetHitsSBTName(sbtname);
+ } else if (tok == "CALL") {
+ if (!cmd->GetCallSBTName().empty())
+ return Result("CALL shader binding table can specified only once");
+ cmd->SetCallSBTName(sbtname);
+ } else {
+ return Result("Unknown shader binding table type");
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("invalid parameter for RUN command: " +
+ token->ToOriginalString());
+ if (i == 0)
+ cmd->SetX(token->AsUint32());
+ else if (i == 1)
+ cmd->SetY(token->AsUint32());
+ else
+ cmd->SetZ(token->AsUint32());
+ }
+
+ command_list_.push_back(std::move(cmd));
+ return ValidateEndOfStatement("RUN command");
+ }
+
token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("RUN command requires parameters");
@@ -3421,6 +3620,20 @@ Result Parser::ParseDeviceFeature() {
return ValidateEndOfStatement("DEVICE_FEATURE command");
}
+Result Parser::ParseDeviceProperty() {
+ auto token = tokenizer_->NextToken();
+ if (token->IsEOS() || token->IsEOL())
+ return Result("missing property name for DEVICE_PROPERTY command");
+ if (!token->IsIdentifier())
+ return Result("invalid property name for DEVICE_PROPERTY command");
+ if (!script_->IsKnownProperty(token->AsString()))
+ return Result("unknown property name for DEVICE_PROPERTY command");
+
+ script_->AddRequiredProperty(token->AsString());
+
+ return ValidateEndOfStatement("DEVICE_PROPERTY command");
+}
+
Result Parser::ParseRepeat() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOL())
@@ -3712,6 +3925,442 @@ Result Parser::ParseSampler() {
return script_->AddSampler(std::move(sampler));
}
+bool Parser::IsRayTracingShader(ShaderType type) {
+ return type == kShaderTypeRayGeneration || type == kShaderTypeAnyHit ||
+ type == kShaderTypeClosestHit || type == kShaderTypeMiss ||
+ type == kShaderTypeIntersection || type == kShaderTypeCall;
+}
+
+Result Parser::ParseAS() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("Acceleration structure requires TOP_LEVEL or BOTTOM_LEVEL");
+
+ Result r;
+ auto type = token->AsString();
+ if (type == "BOTTOM_LEVEL")
+ r = ParseBLAS();
+ else if (type == "TOP_LEVEL")
+ r = ParseTLAS();
+ else
+ return Result("Unexpected acceleration structure type");
+
+ return r;
+}
+
+Result Parser::ParseBLAS() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("Bottom level acceleration structure requires a name");
+
+ auto name = token->AsString();
+ if (script_->GetBLAS(name) != nullptr)
+ return Result(
+ "Bottom level acceleration structure with this name already defined");
+
+ std::unique_ptr<BLAS> blas = MakeUnique<BLAS>();
+ blas->SetName(name);
+
+ token = tokenizer_->NextToken();
+ if (!token->IsEOL())
+ return Result("New line expected");
+
+ Result r;
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL()) {
+ continue;
+ }
+ if (token->IsEOS()) {
+ return Result("END command missing");
+ }
+ if (!token->IsIdentifier()) {
+ return Result("Identifier expected");
+ }
+
+ auto geom = token->AsString();
+ if (geom == "END") {
+ break;
+ } else if (geom == "GEOMETRY") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier()) {
+ return Result("Identifier expected");
+ }
+
+ auto type = token->AsString();
+ if (type == "TRIANGLES") {
+ r = ParseBLASTriangle(blas.get());
+ } else if (type == "AABBS") {
+ r = ParseBLASAABB(blas.get());
+ } else {
+ return Result("Unexpected geometry type");
+ }
+ } else {
+ return Result("Unexpected identifier");
+ }
+
+ if (!r.IsSuccess()) {
+ return r;
+ }
+ }
+
+ if (blas->GetGeometrySize() > 0) {
+ auto type = blas->GetGeometries()[0]->GetType();
+ auto& geometries = blas->GetGeometries();
+ for (auto& g : geometries)
+ if (g->GetType() != type)
+ return Result("Only one type of geometry is allowed within a BLAS");
+ }
+
+ return script_->AddBLAS(std::move(blas));
+}
+
+Result Parser::ParseBLASTriangle(BLAS* blas) {
+ std::unique_ptr<Geometry> geometry = MakeUnique<Geometry>();
+ std::vector<float> g;
+ geometry->SetType(GeometryType::kTriangle);
+
+ while (true) {
+ auto token = tokenizer_->NextToken();
+
+ if (token->IsEOS())
+ return Result("END expected");
+ if (token->IsEOL())
+ continue;
+
+ if (token->IsIdentifier()) {
+ std::string tok = token->AsString();
+ if (tok == "END") {
+ break;
+ } else {
+ return Result("END or float value is expected");
+ }
+ } else if (token->IsInteger() || token->IsDouble()) {
+ g.push_back(token->AsFloat());
+ } else {
+ return Result("Unexpected data type");
+ }
+ }
+
+ if (g.empty())
+ return Result("No triangles have been specified.");
+
+ if (g.size() % 3 != 0)
+ return Result("Each vertex consists of three float coordinates.");
+
+ if ((g.size() / 3) % 3 != 0)
+ return Result("Each triangle should include three vertices.");
+
+ geometry->SetData(g);
+
+ blas->AddGeometry(&geometry);
+
+ return {};
+}
+
+Result Parser::ParseBLASAABB(BLAS* blas) {
+ std::unique_ptr<Geometry> geometry = MakeUnique<Geometry>();
+ std::vector<float> g;
+ geometry->SetType(GeometryType::kAABB);
+
+ while (true) {
+ auto token = tokenizer_->NextToken();
+
+ if (token->IsEOS())
+ return Result("END expected");
+ if (token->IsEOL())
+ continue;
+
+ if (token->IsIdentifier()) {
+ std::string tok = token->AsString();
+ if (tok == "END") {
+ break;
+ } else {
+ return Result("END or float value is expected");
+ }
+ } else if (token->IsInteger() || token->IsDouble()) {
+ g.push_back(token->AsFloat());
+ } else {
+ return Result("Unexpected data type");
+ }
+ }
+
+ if (g.empty())
+ return Result("No AABBs have been specified.");
+
+ if ((g.size() % 6) != 0)
+ return Result(
+ "Each vertex consists of three float coordinates. Each AABB should "
+ "include two vertices.");
+
+ geometry->SetData(g);
+
+ blas->AddGeometry(&geometry);
+
+ return {};
+}
+
+Result Parser::ParseTLAS() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("invalid TLAS name provided");
+
+ auto name = token->AsString();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsEOL())
+ return Result("New line expected");
+
+ std::unique_ptr<TLAS> tlas = MakeUnique<TLAS>();
+
+ tlas->SetName(name);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("END command missing");
+ if (!token->IsIdentifier())
+ return Result("expected identifier");
+
+ Result r;
+ std::string tok = token->AsString();
+ if (tok == "END")
+ break;
+ if (tok == "BOTTOM_LEVEL_INSTANCE")
+ r = ParseBLASInstance(tlas.get());
+ else
+ r = Result("unknown token: " + tok);
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ Result r = script_->AddTLAS(std::move(tlas));
+ if (!r.IsSuccess())
+ return r;
+
+ return {};
+}
+
+// BOTTOM_LEVEL_INSTANCE <blas_name> [MASK 0-255] [OFFSET 0-16777215] [INDEX
+// 0-16777215] [FLAGS {flags}] [TRANSFORM {float x 12} END]
+Result Parser::ParseBLASInstance(TLAS* tlas) {
+ std::unique_ptr<Token> token;
+ std::unique_ptr<BLASInstance> instance = MakeUnique<BLASInstance>();
+
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("Bottom level acceleration structure name expected");
+
+ std::string name = token->AsString();
+ auto ptr = script_->GetBLAS(name);
+
+ if (!ptr)
+ return Result(
+ "Bottom level acceleration structure with given name not found");
+
+ instance->SetUsedBLAS(name, ptr);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOS())
+ return Result("Unexpected end");
+ if (token->IsEOL())
+ continue;
+
+ if (!token->IsIdentifier())
+ return Result("expected identifier");
+
+ Result r;
+ std::string tok = token->AsString();
+ if (tok == "END") {
+ break;
+ } else if (tok == "TRANSFORM") {
+ r = ParseBLASInstanceTransform(instance.get());
+ } else if (tok == "FLAGS") {
+ r = ParseBLASInstanceFlags(instance.get());
+ } else if (tok == "MASK") {
+ token = tokenizer_->NextToken();
+ uint64_t v;
+
+ if (token->IsInteger())
+ v = token->AsUint64();
+ else if (token->IsHex())
+ v = token->AsHex();
+ else
+ return Result("Integer or hex value expected");
+
+ instance->SetMask(uint32_t(v));
+ } else if (tok == "OFFSET") {
+ token = tokenizer_->NextToken();
+ uint64_t v;
+
+ if (token->IsInteger())
+ v = token->AsUint64();
+ else if (token->IsHex())
+ v = token->AsHex();
+ else
+ return Result("Integer or hex value expected");
+
+ instance->SetOffset(uint32_t(v));
+ } else if (tok == "INDEX") {
+ token = tokenizer_->NextToken();
+ uint64_t v;
+
+ if (token->IsInteger())
+ v = token->AsUint64();
+ else if (token->IsHex())
+ v = token->AsHex();
+ else
+ return Result("Integer or hex value expected");
+
+ instance->SetInstanceIndex(uint32_t(v));
+ } else {
+ r = Result("Unknown token in BOTTOM_LEVEL_INSTANCE block: " + tok);
+ }
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ tlas->AddInstance(std::move(instance));
+
+ return {};
+}
+
+Result Parser::ParseBLASInstanceTransform(BLASInstance* instance) {
+ std::unique_ptr<Token> token;
+ std::vector<float> transform;
+
+ transform.reserve(12);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("END command missing");
+
+ if (token->IsIdentifier() && token->AsString() == "END")
+ break;
+ else if (token->IsDouble() || token->IsInteger())
+ transform.push_back(token->AsFloat());
+ else
+ return Result("Unknown token: " + token->AsString());
+ }
+
+ if (transform.size() != 12)
+ return Result("Transform matrix expected to have 12 numbers");
+
+ instance->SetTransform(transform);
+
+ return {};
+}
+
+Result Parser::ParseBLASInstanceFlags(BLASInstance* instance) {
+ std::unique_ptr<Token> token;
+ uint32_t flags = 0;
+ bool first_eol = true;
+ bool singleline = true;
+ Result r;
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL()) {
+ if (first_eol) {
+ first_eol = false;
+ singleline = (flags != 0);
+ }
+ if (singleline)
+ break;
+ else
+ continue;
+ }
+ if (token->IsEOS())
+ return Result("END command missing");
+
+ if (token->IsInteger()) {
+ flags |= token->AsUint32();
+ } else if (token->IsHex()) {
+ flags |= uint32_t(token->AsHex());
+ } else if (token->IsIdentifier()) {
+ if (token->AsString() == "END")
+ break;
+ else if (token->AsString() == "TRIANGLE_FACING_CULL_DISABLE")
+ flags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
+ else if (token->AsString() == "TRIANGLE_FLIP_FACING")
+ flags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR;
+ else if (token->AsString() == "FORCE_OPAQUE")
+ flags |= VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR;
+ else if (token->AsString() == "FORCE_NO_OPAQUE")
+ flags |= VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR;
+ else if (token->AsString() == "FORCE_OPACITY_MICROMAP_2_STATE")
+ flags |= VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_EXT;
+ else if (token->AsString() == "DISABLE_OPACITY_MICROMAPS")
+ flags |= VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_EXT;
+ else
+ return Result("Unknown flag: " + token->AsString());
+ } else {
+ r = Result("Identifier expected");
+ }
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (r.IsSuccess())
+ instance->SetFlags(flags);
+
+ return {};
+}
+
+Result Parser::ParseSBT(Pipeline* pipeline) {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("SHADER_BINDINGS_TABLE requires a name");
+
+ auto name = token->AsString();
+ if (pipeline->GetSBT(name) != nullptr)
+ return Result("SHADER_BINDINGS_TABLE with this name already defined");
+
+ std::unique_ptr<SBT> sbt = MakeUnique<SBT>();
+ sbt->SetName(name);
+
+ token = tokenizer_->NextToken();
+ if (!token->IsEOL())
+ return Result("New line expected");
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL()) {
+ continue;
+ }
+ if (token->IsEOS()) {
+ return Result("END command missing");
+ }
+ if (!token->IsIdentifier()) {
+ return Result("Identifier expected");
+ }
+
+ auto tok = token->AsString();
+ if (tok == "END") {
+ break;
+ }
+
+ std::unique_ptr<SBTRecord> sbtrecord = MakeUnique<SBTRecord>();
+
+ sbtrecord->SetUsedShaderGroupName(tok, pipeline->GetShaderGroupIndex(tok));
+ sbtrecord->SetCount(1);
+
+ sbt->AddSBTRecord(std::move(sbtrecord));
+ }
+
+ return pipeline->AddSBT(std::move(sbt));
+}
+
Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
auto token = tokenizer_->PeekNextToken();
while (!token->IsEOL() && !token->IsEOS()) {
diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h
index fb81c82..e9cd19c 100644
--- a/src/amberscript/parser.h
+++ b/src/amberscript/parser.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -75,6 +76,7 @@ class Parser : public amber::Parser {
Result ParsePipelineDepth(Pipeline* pipeline);
Result ParsePipelineStencil(Pipeline* pipeline);
Result ParsePipelineBlend(Pipeline* pipeline);
+ Result ParsePipelineShaderGroup(Pipeline* pipeline);
Result ParseRun();
Result ParseClear();
Result ParseClearColor();
@@ -84,6 +86,7 @@ class Parser : public amber::Parser {
Result ParseCopy();
Result ParseDeviceFeature();
Result ParseDeviceExtension();
+ Result ParseDeviceProperty();
Result ParseInstanceExtension();
Result ParseRepeat();
Result ParseSet();
@@ -94,6 +97,16 @@ class Parser : public amber::Parser {
std::unique_ptr<Pipeline> pipeline);
Result ParseShaderSpecialization(Pipeline* pipeline);
Result ParseSampler();
+ bool IsRayTracingShader(ShaderType type);
+ Result ParseAS();
+ Result ParseBLAS();
+ Result ParseBLASTriangle(BLAS* blas);
+ Result ParseBLASAABB(BLAS* blas);
+ Result ParseTLAS();
+ Result ParseBLASInstance(TLAS* tlas);
+ Result ParseBLASInstanceTransform(BLASInstance* instance);
+ Result ParseBLASInstanceFlags(BLASInstance* instance);
+ Result ParseSBT(Pipeline* pipeline);
Result ParseTolerances(std::vector<Probe::Tolerance>* tolerances);
/// Parses a set of values out of the token stream. |name| is the name of the
diff --git a/src/amberscript/parser_device_property_test.cc b/src/amberscript/parser_device_property_test.cc
new file mode 100644
index 0000000..f42fafe
--- /dev/null
+++ b/src/amberscript/parser_device_property_test.cc
@@ -0,0 +1,120 @@
+// Copyright 2024 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, DeviceProperty) {
+ std::string in = R"(
+DEVICE_PROPERTY FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16
+DEVICE_PROPERTY FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32
+DEVICE_PROPERTY FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64
+DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat16
+DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat32
+DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat64
+DEVICE_PROPERTY FloatControlsProperties.shaderDenormFlushToZeroFloat16
+DEVICE_PROPERTY FloatControlsProperties.shaderDenormFlushToZeroFloat32
+DEVICE_PROPERTY FloatControlsProperties.shaderDenormFlushToZeroFloat64
+DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTEFloat16
+DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTEFloat32
+DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTEFloat64
+DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTZFloat16
+DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTZFloat32
+DEVICE_PROPERTY FloatControlsProperties.shaderRoundingModeRTZFloat64)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& properties = script->GetRequiredProperties();
+ ASSERT_EQ(15U, properties.size());
+ EXPECT_EQ("FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16",
+ properties[0]);
+ EXPECT_EQ("FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32",
+ properties[1]);
+ EXPECT_EQ("FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64",
+ properties[2]);
+ EXPECT_EQ("FloatControlsProperties.shaderDenormPreserveFloat16",
+ properties[3]);
+ EXPECT_EQ("FloatControlsProperties.shaderDenormPreserveFloat32",
+ properties[4]);
+ EXPECT_EQ("FloatControlsProperties.shaderDenormPreserveFloat64",
+ properties[5]);
+ EXPECT_EQ("FloatControlsProperties.shaderDenormFlushToZeroFloat16",
+ properties[6]);
+ EXPECT_EQ("FloatControlsProperties.shaderDenormFlushToZeroFloat32",
+ properties[7]);
+ EXPECT_EQ("FloatControlsProperties.shaderDenormFlushToZeroFloat64",
+ properties[8]);
+ EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTEFloat16",
+ properties[9]);
+ EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTEFloat32",
+ properties[10]);
+ EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTEFloat64",
+ properties[11]);
+ EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTZFloat16",
+ properties[12]);
+ EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTZFloat32",
+ properties[13]);
+ EXPECT_EQ("FloatControlsProperties.shaderRoundingModeRTZFloat64",
+ properties[14]);
+}
+
+TEST_F(AmberScriptParserTest, DevicePropertyMissingProperty) {
+ std::string in = "DEVICE_PROPERTY";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: missing property name for DEVICE_PROPERTY command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DevicePropertyUnknown) {
+ std::string in = "DEVICE_PROPERTY unknown";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: unknown property name for DEVICE_PROPERTY command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DevicePropertyInvalid) {
+ std::string in = "DEVICE_PROPERTY 12345";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid property name for DEVICE_PROPERTY command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DevicePropertyExtraParams) {
+ std::string in =
+ "DEVICE_PROPERTY FloatControlsProperties.shaderDenormPreserveFloat16 "
+ "EXTRA";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: extra parameters after DEVICE_PROPERTY command: EXTRA",
+ r.Error());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_raytracing_test.cc b/src/amberscript/parser_raytracing_test.cc
new file mode 100644
index 0000000..ca6b33b
--- /dev/null
+++ b/src/amberscript/parser_raytracing_test.cc
@@ -0,0 +1,1297 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, RayTracingBlasName) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Bottom level acceleration structure requires a name",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasNameDup) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+END
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "4: Bottom level acceleration structure with this name already defined",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasNameNoEOL) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: New line expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasNoEND) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: END command missing", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasNoId) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Identifier expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasUnexpId) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ UNEXPECTED)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Unexpected identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasUnexpGeomId) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY 1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Identifier expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasUnexpGeom) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY UNEXPECTED)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Unexpected geometry type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasGeomSingleType) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY TRIANGLES
+ 0 0 0 0 1 0 1 0 0
+ END
+ GEOMETRY AABBS
+ 0 0 0 1 1 1
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Only one type of geometry is allowed within a BLAS", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasTriangleEmpty) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY TRIANGLES
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: No triangles have been specified.", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasTriangleThreeVertices) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY TRIANGLES
+ 0.0 0.0 0.0 0.0 0.0 0.0
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: Each triangle should include three vertices.", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasTriangleThreeFloats) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY TRIANGLES
+ 0.0 0.0 0.0 0.0 0.0 0.0 0.0
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: Each vertex consists of three float coordinates.", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasTriangleNoEND) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY TRIANGLES
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: END expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasTriangleUnexpDataType) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY TRIANGLES "unexpected_string"
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Unexpected data type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasAABBEmpty) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: No AABBs have been specified.", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasAABBInvalidData) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 0.0 0.0 0.0 0.0
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "5: Each vertex consists of three float coordinates. Each AABB should "
+ "include two vertices.",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasAABBNoEND) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: END expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingBlasAABBUnexpDataType) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS "unexpected_string"
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Unexpected data type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasName) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: invalid TLAS name provided", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasNameDup) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas_name
+END
+ACCELERATION_STRUCTURE TOP_LEVEL tlas_name
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: duplicate TLAS name provided", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasNameNoEOL) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas_name END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: New line expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasNoEND) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas_name
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: END command missing", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasNoId) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas_name
+1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: expected identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasUnexpId) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas_name
+ UNEXPECTED)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: unknown token: UNEXPECTED", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstNoName) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Bottom level acceleration structure name expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstNoBlas) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Bottom level acceleration structure with given name not found",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstUnexpEnd) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Unexpected end", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstExpId) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name 1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: expected identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstInvalidToken) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name TOKEN)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Unknown token in BOTTOM_LEVEL_INSTANCE block: TOKEN",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstMask) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name MASK no_mask)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Integer or hex value expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstOffset) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name OFFSET no_offset)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Integer or hex value expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstIndex) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name INDEX no_index)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Integer or hex value expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstFlagsEmpty) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name FLAGS)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: END command missing", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstFlagsUnkFlag) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name FLAGS 16 0x0F NO_SUCH_FLAG)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Unknown flag: NO_SUCH_FLAG", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstFlagsIdExp) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name FLAGS "no_id")";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Identifier expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstTransformNoEnd) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ TRANSFORM
+ 1 0 0 0 0 1 0 0 0 0 1 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: END command missing", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstTransformUnknownToken) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name TRANSFORM
+ INVALID_TOKEN
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: Unknown token: INVALID_TOKEN", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingTlasBlasInstTransformIncomplete) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name TRANSFORM
+ 1 2
+ END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: Transform matrix expected to have 12 numbers", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBind) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND 0 tlas1 DESCRIPTOR_SET 0 BINDING 0
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "14: missing BUFFER, BUFFER_ARRAY, SAMPLER, SAMPLER_ARRAY, or "
+ "ACCELERATION_STRUCTURE in BIND command",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindNothing) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND ACCELERATION_STRUCTURE 0
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: missing top level acceleration structure name in BIND command",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindNoTlas) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND ACCELERATION_STRUCTURE no_tlas
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: unknown top level acceleration structure: no_tlas", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindNoSetOrBinding) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND ACCELERATION_STRUCTURE tlas1 NO_TOKEN
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: missing DESCRIPTOR_SET or BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindBadSet) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND ACCELERATION_STRUCTURE tlas1 DESCRIPTOR_SET 0.0
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindBadBindingKeyword) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND ACCELERATION_STRUCTURE tlas1 DESCRIPTOR_SET 0 NOT_BINDING
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindBadBindingValue) {
+ std::string in = R"(
+ACCELERATION_STRUCTURE BOTTOM_LEVEL blas_name
+ GEOMETRY AABBS
+ 0.0 0.0 0.0 1.0 1.0 1.0
+ END
+END
+
+ACCELERATION_STRUCTURE TOP_LEVEL tlas1
+ BOTTOM_LEVEL_INSTANCE blas_name
+ END
+END
+
+PIPELINE raytracing my_rtpipeline
+ BIND ACCELERATION_STRUCTURE tlas1 DESCRIPTOR_SET 0 BINDING 0.0
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupNoName) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP 1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Group name expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupNoNameDup) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP group raygen1
+ SHADER_GROUP group raygen1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: Group name already exists", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupEmpty) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP group
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: No shaders in shader group defined", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupNoShaderName) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP group 1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Shader name expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupNoShader) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP group no_shader
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: Shader not found: no_shader", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupInvalidShader) {
+ std::string in = R"(
+SHADER vertex vertex1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP group vertex1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("8: Shader must be of raytracing type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupTwoGeneral) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER ray_generation raygen2 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP group raygen1 raygen2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: Two general shaders cannot be in one group", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupAddGenToHit) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER intersection intersection1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP hit_group intersection1 raygen1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: Hit group cannot contain general shaders", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupAddAHitToGen) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER any_hit ahit1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group raygen1 ahit1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: General group cannot contain any hit shaders", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupAddCHitToGen) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER closest_hit chit1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group raygen1 chit1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: General group cannot contain closest hit shaders", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupAddSectToGen) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER intersection sect1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group raygen1 sect1
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: General group cannot contain intersection shaders", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupAHitDouble) {
+ std::string in = R"(
+SHADER any_hit ahit1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER any_hit ahit2 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group ahit1 ahit2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: Two any hit shaders cannot be in one group", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupCHitDouble) {
+ std::string in = R"(
+SHADER closest_hit chit1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER closest_hit chit2 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group chit1 chit2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: Two closest hit shaders cannot be in one group", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineBindShaderGroupSectDouble) {
+ std::string in = R"(
+SHADER intersection sect1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+SHADER intersection sect2 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group sect1 sect2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: Two intersection shaders cannot be in one group", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineSBTNoName) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_BINDING_TABLE
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: SHADER_BINDINGS_TABLE requires a name", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineSBTDup) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP gen_group raygen1
+ SHADER_BINDING_TABLE sbt1
+ END
+ SHADER_BINDING_TABLE sbt1
+ END
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: SHADER_BINDINGS_TABLE with this name already defined",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineSBTExtraToken) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_BINDING_TABLE sbt1 extra_token
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: New line expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineSBTNoEnd) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_BINDING_TABLE sbt1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: END command missing", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingPipelineSBTNoId) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+ SHADER_BINDING_TABLE sbt1
+ 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: Identifier expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRun) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 raygen1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline RAYGEN sbt1 1 1 z
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: invalid parameter for RUN command: z", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunIncomplete) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 raygen1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("15: Incomplete RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunExpectsSBTType) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 raygen1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline 0.0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: Shader binding table type is expected", r.Error());
+}
+
+
+TEST_F(AmberScriptParserTest, RayTracingRunExpectsSBTName) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 raygen1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline RAYGEN 0.0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: Shader binding table name expected", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunExpectsSBTUndefined) {
+ std::string in = R"(
+PIPELINE raytracing my_rtpipeline
+END
+RUN my_rtpipeline RAYGEN sbt3
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: Shader binding table with this name was not defined",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunExpectsSBTUnknownType) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 raygen1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline RAYGEN2 sbt1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: Unknown shader binding table type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunSBTRGenDup) {
+ std::string in = R"(
+SHADER ray_generation raygen1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 raygen1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline RAYGEN sbt1 RAYGEN sbt1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: RAYGEN shader binding table can specified only once",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunSBTMissDup) {
+ std::string in = R"(
+SHADER miss miss1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 miss1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline MISS sbt1 MISS sbt1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: MISS shader binding table can specified only once",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunSBTHitDup) {
+ std::string in = R"(
+SHADER any_hit ahit1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 ahit1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline HIT sbt1 HIT sbt1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: HIT shader binding table can specified only once", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RayTracingRunSBTCallDup) {
+ std::string in = R"(
+SHADER callable call1 GLSL
+ #version 460 core
+ void main() {}
+END
+
+PIPELINE raytracing my_rtpipeline
+ SHADER_GROUP g1 call1
+ SHADER_BINDING_TABLE sbt1
+ g1
+ END
+END
+
+RUN my_rtpipeline CALL sbt1 CALL sbt1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: CALL shader binding table can specified only once", r.Error());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/command.cc b/src/command.cc
index ea242b7..75e5916 100644
--- a/src/command.cc
+++ b/src/command.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -46,6 +47,10 @@ ComputeCommand* Command::AsCompute() {
return static_cast<ComputeCommand*>(this);
}
+RayTracingCommand* Command::AsRayTracing() {
+ return static_cast<RayTracingCommand*>(this);
+}
+
CopyCommand* Command::AsCopy() {
return static_cast<CopyCommand*>(this);
}
@@ -184,4 +189,14 @@ RepeatCommand::RepeatCommand(uint32_t count)
RepeatCommand::~RepeatCommand() = default;
+TLASCommand::TLASCommand(Pipeline* pipeline)
+ : BindableResourceCommand(Type::kTLAS, pipeline) {}
+
+TLASCommand::~TLASCommand() = default;
+
+RayTracingCommand::RayTracingCommand(Pipeline* pipeline)
+ : PipelineCommand(Type::kRayTracing, pipeline) {}
+
+RayTracingCommand::~RayTracingCommand() = default;
+
} // namespace amber
diff --git a/src/command.h b/src/command.h
index 213d7b8..a0bdc76 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,6 +24,7 @@
#include "amber/shader_info.h"
#include "amber/value.h"
+#include "src/acceleration_structure.h"
#include "src/buffer.h"
#include "src/command_data.h"
#include "src/pipeline_data.h"
@@ -46,7 +48,9 @@ class PatchParameterVerticesCommand;
class Pipeline;
class ProbeCommand;
class ProbeSSBOCommand;
+class RayTracingCommand;
class RepeatCommand;
+class TLASCommand;
/// Base class for all commands.
class Command {
@@ -69,7 +73,9 @@ class Command {
kProbeSSBO,
kBuffer,
kRepeat,
- kSampler
+ kSampler,
+ kTLAS,
+ kRayTracing
};
virtual ~Command();
@@ -81,6 +87,8 @@ class Command {
bool IsDrawArrays() const { return command_type_ == Type::kDrawArrays; }
bool IsCompareBuffer() const { return command_type_ == Type::kCompareBuffer; }
bool IsCompute() const { return command_type_ == Type::kCompute; }
+ bool IsRayTracing() const { return command_type_ == Type::kRayTracing; }
+ bool IsTLAS() const { return command_type_ == Type::kTLAS; }
bool IsCopy() const { return command_type_ == Type::kCopy; }
bool IsProbe() const { return command_type_ == Type::kProbe; }
bool IsProbeSSBO() const { return command_type_ == Type::kProbeSSBO; }
@@ -101,6 +109,7 @@ class Command {
ClearStencilCommand* AsClearStencil();
CompareBufferCommand* AsCompareBuffer();
ComputeCommand* AsCompute();
+ RayTracingCommand* AsRayTracing();
CopyCommand* AsCopy();
DrawArraysCommand* AsDrawArrays();
DrawRectCommand* AsDrawRect();
@@ -711,6 +720,60 @@ class RepeatCommand : public Command {
std::vector<std::unique_ptr<Command>> commands_;
};
+/// Command for setting TLAS parameters and binding.
+class TLASCommand : public BindableResourceCommand {
+ public:
+ explicit TLASCommand(Pipeline* pipeline);
+ ~TLASCommand() override;
+
+ void SetTLAS(TLAS* tlas) { tlas_ = tlas; }
+ TLAS* GetTLAS() const { return tlas_; }
+
+ std::string ToString() const override { return "TLASCommand"; }
+
+ private:
+ TLAS* tlas_ = nullptr;
+};
+
+/// Command to execute a ray tracing command.
+class RayTracingCommand : public PipelineCommand {
+ public:
+ explicit RayTracingCommand(Pipeline* pipeline);
+ ~RayTracingCommand() override;
+
+ void SetX(uint32_t x) { x_ = x; }
+ uint32_t GetX() const { return x_; }
+
+ void SetY(uint32_t y) { y_ = y; }
+ uint32_t GetY() const { return y_; }
+
+ void SetZ(uint32_t z) { z_ = z; }
+ uint32_t GetZ() const { return z_; }
+
+ void SetRayGenSBTName(const std::string& name) { rgen_sbt_name_ = name; }
+ std::string GetRayGenSBTName() const { return rgen_sbt_name_; }
+
+ void SetMissSBTName(const std::string& name) { miss_sbt_name_ = name; }
+ std::string GetMissSBTName() const { return miss_sbt_name_; }
+
+ void SetHitsSBTName(const std::string& name) { hits_sbt_name_ = name; }
+ std::string GetHitsSBTName() const { return hits_sbt_name_; }
+
+ void SetCallSBTName(const std::string& name) { call_sbt_name_ = name; }
+ std::string GetCallSBTName() const { return call_sbt_name_; }
+
+ std::string ToString() const override { return "RayTracingCommand"; }
+
+ private:
+ uint32_t x_ = 0;
+ uint32_t y_ = 0;
+ uint32_t z_ = 0;
+ std::string rgen_sbt_name_;
+ std::string miss_sbt_name_;
+ std::string hits_sbt_name_;
+ std::string call_sbt_name_;
+};
+
} // namespace amber
#endif // SRC_COMMAND_H_
diff --git a/src/engine.h b/src/engine.h
index d444258..71c14c8 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -71,6 +72,7 @@ class Engine {
EngineConfig* config,
Delegate* delegate,
const std::vector<std::string>& features,
+ const std::vector<std::string>& properties,
const std::vector<std::string>& instance_extensions,
const std::vector<std::string>& device_extensions) = 0;
@@ -101,6 +103,9 @@ class Engine {
/// Execute the compute command
virtual Result DoCompute(const ComputeCommand* cmd) = 0;
+ /// Execute the trace rays command
+ virtual Result DoTraceRays(const RayTracingCommand* cmd) = 0;
+
/// Execute the entry point command
virtual Result DoEntryPoint(const EntryPointCommand* cmd) = 0;
diff --git a/src/executor.cc b/src/executor.cc
index 53e3e55..40cb353 100644
--- a/src/executor.cc
+++ b/src/executor.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -159,6 +160,8 @@ Result Executor::ExecuteCommand(Engine* engine, Command* cmd) {
return engine->DoDrawArrays(cmd->AsDrawArrays());
if (cmd->IsCompute())
return engine->DoCompute(cmd->AsCompute());
+ if (cmd->IsRayTracing())
+ return engine->DoTraceRays(cmd->AsRayTracing());
if (cmd->IsEntryPoint())
return engine->DoEntryPoint(cmd->AsEntryPoint());
if (cmd->IsPatchParameterVertices())
diff --git a/src/executor_test.cc b/src/executor_test.cc
index fa57c74..2f9429e 100644
--- a/src/executor_test.cc
+++ b/src/executor_test.cc
@@ -37,9 +37,11 @@ class EngineStub : public Engine {
Result Initialize(EngineConfig*,
Delegate*,
const std::vector<std::string>& features,
+ const std::vector<std::string>& properties,
const std::vector<std::string>& instance_exts,
const std::vector<std::string>& device_exts) override {
features_ = features;
+ properties_ = properties;
instance_extensions_ = instance_exts;
device_extensions_ = device_exts;
return {};
@@ -167,6 +169,10 @@ class EngineStub : public Engine {
return {};
}
+ Result DoTraceRays(const RayTracingCommand*) override {
+ return Result("traceray stub not implemented");
+ }
+
private:
bool fail_clear_command_ = false;
bool fail_clear_color_command_ = false;
@@ -193,6 +199,7 @@ class EngineStub : public Engine {
bool did_buffer_command_ = false;
std::vector<std::string> features_;
+ std::vector<std::string> properties_;
std::vector<std::string> instance_extensions_;
std::vector<std::string> device_extensions_;
@@ -207,11 +214,12 @@ class VkScriptExecutorTest : public testing::Test {
std::unique_ptr<Engine> MakeEngine() { return MakeUnique<EngineStub>(); }
std::unique_ptr<Engine> MakeAndInitializeEngine(
const std::vector<std::string>& features,
+ const std::vector<std::string>& properties,
const std::vector<std::string>& instance_extensions,
const std::vector<std::string>& device_extensions) {
std::unique_ptr<Engine> engine = MakeUnique<EngineStub>();
- engine->Initialize(nullptr, nullptr, features, instance_extensions,
- device_extensions);
+ engine->Initialize(nullptr, nullptr, features, properties,
+ instance_extensions, device_extensions);
return engine;
}
EngineStub* ToStub(Engine* engine) {
@@ -233,6 +241,7 @@ logicOp)";
auto script = parser.GetScript();
auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(),
+ script->GetRequiredProperties(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
@@ -263,6 +272,7 @@ VK_KHR_variable_pointers)";
auto script = parser.GetScript();
auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(),
+ script->GetRequiredProperties(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
@@ -293,6 +303,7 @@ depthstencil D24_UNORM_S8_UINT)";
auto script = parser.GetScript();
auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(),
+ script->GetRequiredProperties(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
@@ -320,6 +331,7 @@ fence_timeout 12345)";
auto script = parser.GetScript();
auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(),
+ script->GetRequiredProperties(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
@@ -355,6 +367,7 @@ fence_timeout 12345)";
auto script = parser.GetScript();
auto engine = MakeAndInitializeEngine(script->GetRequiredFeatures(),
+ script->GetRequiredProperties(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
diff --git a/src/pipeline.cc b/src/pipeline.cc
index a9b66ca..4be1de2 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -61,7 +62,8 @@ Pipeline::ShaderInfo::ShaderInfo(const ShaderInfo&) = default;
Pipeline::ShaderInfo::~ShaderInfo() = default;
-Pipeline::Pipeline(PipelineType type) : pipeline_type_(type) {}
+Pipeline::Pipeline(PipelineType type) : pipeline_type_(type) {
+}
Pipeline::~Pipeline() = default;
@@ -99,13 +101,15 @@ Result Pipeline::AddShader(Shader* shader, ShaderType shader_type) {
return Result("can not add a compute shader to a graphics pipeline");
}
- for (auto& info : shaders_) {
- const auto* is = info.GetShader();
- if (is == shader)
- return Result("can not add duplicate shader to pipeline");
- if (is->GetType() == shader_type) {
- info.SetShader(shader);
- return {};
+ if (pipeline_type_ != PipelineType::kRayTracing) {
+ for (auto& info : shaders_) {
+ const auto* is = info.GetShader();
+ if (is == shader)
+ return Result("can not add duplicate shader to pipeline");
+ if (is->GetType() == shader_type) {
+ info.SetShader(shader);
+ return {};
+ }
}
}
@@ -292,12 +296,21 @@ Result Pipeline::Validate() const {
}
}
- if (pipeline_type_ == PipelineType::kGraphics)
+ if (pipeline_type_ == PipelineType::kRayTracing)
+ return ValidateRayTracing();
+ else if (pipeline_type_ == PipelineType::kGraphics)
return ValidateGraphics();
return ValidateCompute();
}
+Result Pipeline::ValidateRayTracing() const {
+ if (shader_groups_.empty() && shaders_.empty() && tlases_.empty())
+ return Result("Shader groups are missing");
+
+ return {};
+}
+
Result Pipeline::ValidateGraphics() const {
if (color_attachments_.empty())
return Result("PIPELINE missing color attachment");
@@ -655,6 +668,15 @@ void Pipeline::AddSampler(uint32_t mask,
info.binding = binding;
}
+void Pipeline::AddTLAS(TLAS* tlas, uint32_t descriptor_set, uint32_t binding) {
+ tlases_.push_back(TLASInfo(tlas));
+
+ auto& info = tlases_.back();
+
+ info.descriptor_set = descriptor_set;
+ info.binding = binding;
+}
+
void Pipeline::ClearSamplers(uint32_t descriptor_set, uint32_t binding) {
samplers_.erase(
std::remove_if(samplers_.begin(), samplers_.end(),
diff --git a/src/pipeline.h b/src/pipeline.h
index 12792f0..24b29a1 100644
--- a/src/pipeline.h
+++ b/src/pipeline.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,6 +24,7 @@
#include <vector>
#include "amber/result.h"
+#include "src/acceleration_structure.h"
#include "src/buffer.h"
#include "src/command_data.h"
#include "src/pipeline_data.h"
@@ -31,7 +33,7 @@
namespace amber {
-enum class PipelineType { kCompute = 0, kGraphics };
+enum class PipelineType { kCompute = 0, kGraphics, kRayTracing };
/// Stores all information related to a pipeline.
class Pipeline {
@@ -216,6 +218,15 @@ class Pipeline {
uint32_t mask = 0;
};
+ /// Information on a top level acceleration structure at the pipeline.
+ struct TLASInfo {
+ TLASInfo() = default;
+ explicit TLASInfo(TLAS* as) : tlas(as) {}
+
+ TLAS* tlas = nullptr;
+ uint32_t descriptor_set = 0;
+ uint32_t binding = 0;
+ };
static const char* kGeneratedColorBuffer;
static const char* kGeneratedDepthBuffer;
static const char* kGeneratedPushConstantBuffer;
@@ -227,6 +238,9 @@ class Pipeline {
bool IsGraphics() const { return pipeline_type_ == PipelineType::kGraphics; }
bool IsCompute() const { return pipeline_type_ == PipelineType::kCompute; }
+ bool IsRayTracing() const {
+ return pipeline_type_ == PipelineType::kRayTracing;
+ }
PipelineType GetType() const { return pipeline_type_; }
@@ -261,6 +275,18 @@ class Pipeline {
return nullptr;
}
+ /// Returns a success result if |shader| found and the shader index is
+ /// returned in |out|. Returns failure otherwise.
+ Result GetShaderIndex(Shader* shader, uint32_t* out) const {
+ for (size_t index = 0; index < shaders_.size(); index++) {
+ if (shaders_[index].GetShader() == shader) {
+ *out = static_cast<uint32_t>(index);
+ return {};
+ }
+ }
+ return Result("Referred shader not found in group");
+ }
+
/// Sets the |type| of |shader| in the pipeline.
Result SetShaderType(const Shader* shader, ShaderType type);
/// Sets the entry point |name| for |shader| in this pipeline.
@@ -377,6 +403,68 @@ class Pipeline {
/// Returns information on all samplers in this pipeline.
const std::vector<SamplerInfo>& GetSamplers() const { return samplers_; }
+ /// Adds |tlas| to the pipeline at the given |descriptor_set| and
+ /// |binding|.
+ void AddTLAS(TLAS* tlas, uint32_t descriptor_set, uint32_t binding);
+
+ /// Returns information on all bound TLAS in the pipeline.
+ std::vector<TLASInfo>& GetTLASes() { return tlases_; }
+
+ /// Adds |sbt| to the list of known shader binding tables.
+ /// The |sbt| must have a unique name within pipeline.
+ Result AddSBT(std::unique_ptr<SBT> sbt) {
+ if (name_to_sbt_.count(sbt->GetName()) > 0)
+ return Result("duplicate SBT name provided");
+
+ sbts_.push_back(std::move(sbt));
+ name_to_sbt_[sbts_.back()->GetName()] = sbts_.back().get();
+
+ return {};
+ }
+
+ /// Retrieves the SBT with |name|, |nullptr| if not found.
+ SBT* GetSBT(const std::string& name) const {
+ auto it = name_to_sbt_.find(name);
+ return it == name_to_sbt_.end() ? nullptr : it->second;
+ }
+
+ /// Retrieves a list of all SBTs.
+ const std::vector<std::unique_ptr<SBT>>& GetSBTs() const { return sbts_; }
+
+ /// Adds |group| to the list of known shader groups.
+ /// The |group| must have a unique name within pipeline.
+ Result AddShaderGroup(std::unique_ptr<ShaderGroup> group) {
+ if (name_to_shader_group_.count(group->GetName()) > 0)
+ return Result("shader group name already exists");
+
+ shader_groups_.push_back(std::move(group));
+ name_to_shader_group_[shader_groups_.back()->GetName()] =
+ shader_groups_.back().get();
+
+ return {};
+ }
+
+ /// Retrieves the Shader Group with |name|, |nullptr| if not found.
+ ShaderGroup* GetShaderGroup(const std::string& name) const {
+ auto it = name_to_shader_group_.find(name);
+ return it == name_to_shader_group_.end() ? nullptr : it->second;
+ }
+ uint32_t GetShaderGroupIndex(const std::string& name) const {
+ ShaderGroup* shader_group = GetShaderGroup(name);
+
+ for (size_t i = 0; i < shader_groups_.size(); i++) {
+ if (shader_groups_[i].get() == shader_group) {
+ return static_cast<uint32_t>(i);
+ }
+ }
+ return static_cast<uint32_t>(-1);
+ }
+
+ /// Retrieves a list of all Shader Groups.
+ const std::vector<std::unique_ptr<ShaderGroup>>& GetShaderGroups() const {
+ return shader_groups_;
+ }
+
/// Updates the descriptor set and binding info for the OpenCL-C kernel bound
/// to the pipeline. No effect for other shader formats.
Result UpdateOpenCLBufferBindings();
@@ -435,10 +523,12 @@ class Pipeline {
Result ValidateGraphics() const;
Result ValidateCompute() const;
+ Result ValidateRayTracing() const;
PipelineType pipeline_type_ = PipelineType::kCompute;
std::string name_;
std::vector<ShaderInfo> shaders_;
+ std::vector<TLASInfo> tlases_;
std::vector<BufferInfo> color_attachments_;
std::vector<BufferInfo> resolve_targets_;
std::vector<BufferInfo> vertex_buffers_;
@@ -459,6 +549,11 @@ class Pipeline {
std::map<std::pair<uint32_t, uint32_t>, Buffer*> opencl_pod_buffer_map_;
std::vector<std::unique_ptr<Sampler>> opencl_literal_samplers_;
std::unique_ptr<Buffer> opencl_push_constants_;
+
+ std::map<std::string, ShaderGroup*> name_to_shader_group_;
+ std::vector<std::unique_ptr<ShaderGroup>> shader_groups_;
+ std::map<std::string, SBT*> name_to_sbt_;
+ std::vector<std::unique_ptr<SBT>> sbts_;
};
} // namespace amber
diff --git a/src/recipe.cc b/src/recipe.cc
index 7e22bd4..7d46f05 100644
--- a/src/recipe.cc
+++ b/src/recipe.cc
@@ -35,6 +35,10 @@ std::vector<std::string> Recipe::GetRequiredFeatures() const {
return impl_ ? impl_->GetRequiredFeatures() : std::vector<std::string>();
}
+std::vector<std::string> Recipe::GetRequiredProperties() const {
+ return impl_ ? impl_->GetRequiredProperties() : std::vector<std::string>();
+}
+
std::vector<std::string> Recipe::GetRequiredDeviceExtensions() const {
return impl_ ? impl_->GetRequiredDeviceExtensions()
: std::vector<std::string>();
diff --git a/src/script.cc b/src/script.cc
index 091949e..a5d8bed 100644
--- a/src/script.cc
+++ b/src/script.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -131,7 +132,32 @@ bool Script::IsKnownFeature(const std::string& name) const {
name == "SubgroupSupportedStages.compute" ||
name == "IndexTypeUint8Features.indexTypeUint8" ||
name ==
- "ShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes";
+ "ShaderSubgroupExtendedTypesFeatures"
+ ".shaderSubgroupExtendedTypes" ||
+ name == "RayTracingPipelineFeaturesKHR.rayTracingPipeline" ||
+ name == "AccelerationStructureFeaturesKHR.accelerationStructure" ||
+ name == "BufferDeviceAddressFeatures.bufferDeviceAddress";
+}
+
+bool Script::IsKnownProperty(const std::string& name) const {
+ return name ==
+ "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16" ||
+ name ==
+ "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32" ||
+ name ==
+ "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64" ||
+ name == "FloatControlsProperties.shaderDenormPreserveFloat16" ||
+ name == "FloatControlsProperties.shaderDenormPreserveFloat32" ||
+ name == "FloatControlsProperties.shaderDenormPreserveFloat64" ||
+ name == "FloatControlsProperties.shaderDenormFlushToZeroFloat16" ||
+ name == "FloatControlsProperties.shaderDenormFlushToZeroFloat32" ||
+ name == "FloatControlsProperties.shaderDenormFlushToZeroFloat64" ||
+ name == "FloatControlsProperties.shaderRoundingModeRTEFloat16" ||
+ name == "FloatControlsProperties.shaderRoundingModeRTEFloat32" ||
+ name == "FloatControlsProperties.shaderRoundingModeRTEFloat64" ||
+ name == "FloatControlsProperties.shaderRoundingModeRTZFloat16" ||
+ name == "FloatControlsProperties.shaderRoundingModeRTZFloat32" ||
+ name == "FloatControlsProperties.shaderRoundingModeRTZFloat64";
}
type::Type* Script::ParseType(const std::string& str) {
diff --git a/src/script.h b/src/script.h
index b4c6e1a..edd8f94 100644
--- a/src/script.h
+++ b/src/script.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -25,6 +26,7 @@
#include "amber/recipe.h"
#include "amber/result.h"
+#include "src/acceleration_structure.h"
#include "src/buffer.h"
#include "src/command.h"
#include "src/engine.h"
@@ -43,6 +45,7 @@ class Script : public RecipeImpl {
~Script() override;
bool IsKnownFeature(const std::string& name) const;
+ bool IsKnownProperty(const std::string& name) const;
/// Retrieves information on the shaders in the given script.
std::vector<ShaderInfo> GetShaderInfo() const override;
@@ -52,6 +55,10 @@ class Script : public RecipeImpl {
return engine_info_.required_features;
}
+ std::vector<std::string> GetRequiredProperties() const override {
+ return engine_info_.required_properties;
+ }
+
/// Returns required device extensions in the given recipe.
std::vector<std::string> GetRequiredDeviceExtensions() const override {
return engine_info_.required_device_extensions;
@@ -155,6 +162,52 @@ class Script : public RecipeImpl {
return it == name_to_sampler_.end() ? nullptr : it->second;
}
+ /// Adds |blas| to the list of known bottom level acceleration structures.
+ /// The |blas| must have a unique name over all BLASes in the script.
+ Result AddBLAS(std::unique_ptr<BLAS> blas) {
+ if (name_to_blas_.count(blas->GetName()) > 0)
+ return Result("duplicate BLAS name provided");
+
+ blases_.push_back(std::move(blas));
+ name_to_blas_[blases_.back()->GetName()] = blases_.back().get();
+
+ return {};
+ }
+
+ /// Retrieves the BLAS with |name|, |nullptr| if not found.
+ BLAS* GetBLAS(const std::string& name) const {
+ auto it = name_to_blas_.find(name);
+ return it == name_to_blas_.end() ? nullptr : it->second;
+ }
+
+ /// Retrieves a list of all BLASes.
+ const std::vector<std::unique_ptr<BLAS>>& GetBLASes() const {
+ return blases_;
+ }
+
+ /// Adds |tlas| to the list of known top level acceleration structures.
+ /// The |tlas| must have a unique name over all TLASes in the script.
+ Result AddTLAS(std::unique_ptr<TLAS> tlas) {
+ if (name_to_tlas_.count(tlas->GetName()) > 0)
+ return Result("duplicate TLAS name provided");
+
+ tlases_.push_back(std::move(tlas));
+ name_to_tlas_[tlases_.back()->GetName()] = tlases_.back().get();
+
+ return {};
+ }
+
+ /// Retrieves the TLAS with |name|, |nullptr| if not found.
+ TLAS* GetTLAS(const std::string& name) const {
+ auto it = name_to_tlas_.find(name);
+ return it == name_to_tlas_.end() ? nullptr : it->second;
+ }
+
+ /// Retrieves a list of all TLASes.
+ const std::vector<std::unique_ptr<TLAS>>& GetTLASes() const {
+ return tlases_;
+ }
+
/// Retrieves a list of all samplers.
const std::vector<std::unique_ptr<Sampler>>& GetSamplers() const {
return samplers_;
@@ -166,6 +219,12 @@ class Script : public RecipeImpl {
engine_info_.required_features.push_back(feature);
}
+ /// Adds |prop| to the list of properties that must be supported by the
+ /// engine.
+ void AddRequiredProperty(const std::string& prop) {
+ engine_info_.required_properties.push_back(prop);
+ }
+
/// Checks if |feature| is in required features
bool IsRequiredFeature(const std::string& feature) const {
return std::find(engine_info_.required_features.begin(),
@@ -173,6 +232,13 @@ class Script : public RecipeImpl {
feature) != engine_info_.required_features.end();
}
+ /// Checks if |prop| is in required features
+ bool IsRequiredProperty(const std::string& prop) const {
+ return std::find(engine_info_.required_properties.begin(),
+ engine_info_.required_properties.end(),
+ prop) != engine_info_.required_properties.end();
+ }
+
/// Adds |ext| to the list of device extensions that must be supported.
void AddRequiredDeviceExtension(const std::string& ext) {
engine_info_.required_device_extensions.push_back(ext);
@@ -257,6 +323,7 @@ class Script : public RecipeImpl {
private:
struct {
std::vector<std::string> required_features;
+ std::vector<std::string> required_properties;
std::vector<std::string> required_device_extensions;
std::vector<std::string> required_instance_extensions;
} engine_info_;
@@ -267,12 +334,16 @@ class Script : public RecipeImpl {
std::map<std::string, Buffer*> name_to_buffer_;
std::map<std::string, Sampler*> name_to_sampler_;
std::map<std::string, Pipeline*> name_to_pipeline_;
+ std::map<std::string, BLAS*> name_to_blas_;
+ std::map<std::string, TLAS*> name_to_tlas_;
std::map<std::string, std::unique_ptr<type::Type>> name_to_type_;
std::vector<std::unique_ptr<Shader>> shaders_;
std::vector<std::unique_ptr<Command>> commands_;
std::vector<std::unique_ptr<Buffer>> buffers_;
std::vector<std::unique_ptr<Sampler>> samplers_;
std::vector<std::unique_ptr<Pipeline>> pipelines_;
+ std::vector<std::unique_ptr<BLAS>> blases_;
+ std::vector<std::unique_ptr<TLAS>> tlases_;
std::vector<std::unique_ptr<type::Type>> types_;
std::vector<std::unique_ptr<Format>> formats_;
std::unique_ptr<VirtualFileStore> virtual_files_;
diff --git a/src/shader_compiler.cc b/src/shader_compiler.cc
index 285dd97..bec44e4 100644
--- a/src/shader_compiler.cc
+++ b/src/shader_compiler.cc
@@ -241,6 +241,18 @@ Result ShaderCompiler::CompileGlsl(const Shader* shader,
kind = shaderc_tess_control_shader;
else if (shader->GetType() == kShaderTypeTessellationEvaluation)
kind = shaderc_tess_evaluation_shader;
+ else if (shader->GetType() == kShaderTypeRayGeneration)
+ kind = shaderc_raygen_shader;
+ else if (shader->GetType() == kShaderTypeAnyHit)
+ kind = shaderc_anyhit_shader;
+ else if (shader->GetType() == kShaderTypeClosestHit)
+ kind = shaderc_closesthit_shader;
+ else if (shader->GetType() == kShaderTypeMiss)
+ kind = shaderc_miss_shader;
+ else if (shader->GetType() == kShaderTypeIntersection)
+ kind = shaderc_intersection_shader;
+ else if (shader->GetType() == kShaderTypeCall)
+ kind = shaderc_callable_shader;
else
return Result("Unknown shader type");
diff --git a/src/vulkan/CMakeLists.txt b/src/vulkan/CMakeLists.txt
index 71efeff..f816b3a 100644
--- a/src/vulkan/CMakeLists.txt
+++ b/src/vulkan/CMakeLists.txt
@@ -1,4 +1,5 @@
# Copyright 2018 The Amber Authors.
+# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +14,7 @@
# limitations under the License.
set(VULKAN_ENGINE_SOURCES
+ blas.cc
buffer_descriptor.cc
buffer_backed_descriptor.cc
command_buffer.cc
@@ -27,9 +29,13 @@ set(VULKAN_ENGINE_SOURCES
index_buffer.cc
pipeline.cc
push_constant.cc
+ raytracing_pipeline.cc
resource.cc
sampler.cc
sampler_descriptor.cc
+ sbt.cc
+ tlas.cc
+ tlas_descriptor.cc
transfer_buffer.cc
transfer_image.cc
vertex_buffer.cc
diff --git a/src/vulkan/blas.cc b/src/vulkan/blas.cc
new file mode 100644
index 0000000..f80ec51
--- /dev/null
+++ b/src/vulkan/blas.cc
@@ -0,0 +1,268 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/vulkan/blas.h"
+
+#include <cstring>
+
+#include "src/vulkan/command_buffer.h"
+
+namespace amber {
+namespace vulkan {
+
+inline VkDeviceSize align(VkDeviceSize v, VkDeviceSize a) {
+ return (v + a - 1) & ~(a - 1);
+}
+
+BLAS::BLAS(Device* device) : device_(device) {}
+
+BLAS::~BLAS() {
+ if (blas_ != VK_NULL_HANDLE) {
+ device_->GetPtrs()->vkDestroyAccelerationStructureKHR(
+ device_->GetVkDevice(), blas_, nullptr);
+ }
+}
+
+Result BLAS::CreateBLAS(amber::BLAS* blas) {
+ if (blas_ != VK_NULL_HANDLE)
+ return Result("Cannot recreate acceleration structure");
+
+ std::vector<std::unique_ptr<Geometry>>& geometries = blas->GetGeometries();
+ std::vector<VkDeviceSize> vertexBufferOffsets;
+ VkDeviceSize vertexBufferSize = 0;
+
+ VkDeviceOrHostAddressConstKHR const_null_placeholder = {};
+ VkDeviceOrHostAddressKHR null_placeholder = {};
+
+ accelerationStructureGeometriesKHR_.resize(geometries.size());
+ accelerationStructureBuildRangeInfoKHR_.resize(geometries.size());
+ maxPrimitiveCounts_.resize(geometries.size());
+ vertexBufferOffsets.resize(geometries.size());
+
+ for (size_t geometryNdx = 0; geometryNdx < geometries.size(); ++geometryNdx) {
+ const std::unique_ptr<Geometry>& geometryData = geometries[geometryNdx];
+ VkDeviceOrHostAddressConstKHR vertexData = {};
+ VkAccelerationStructureGeometryDataKHR geometry;
+ VkGeometryTypeKHR geometryType = VK_GEOMETRY_TYPE_MAX_ENUM_KHR;
+
+ if (geometryData->IsTriangle()) {
+ VkAccelerationStructureGeometryTrianglesDataKHR
+ accelerationStructureGeometryTrianglesDataKHR = {
+ // NOLINTNEXTLINE(whitespace/line_length)
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR,
+ nullptr,
+ VK_FORMAT_R32G32B32_SFLOAT,
+ vertexData,
+ 3 * sizeof(float),
+ static_cast<uint32_t>(geometryData->getVertexCount()),
+ VK_INDEX_TYPE_NONE_KHR,
+ const_null_placeholder,
+ const_null_placeholder,
+ };
+
+ geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
+ geometry.triangles = accelerationStructureGeometryTrianglesDataKHR;
+ } else if (geometryData->IsAABB()) {
+ const VkAccelerationStructureGeometryAabbsDataKHR
+ accelerationStructureGeometryAabbsDataKHR = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR,
+ nullptr, vertexData, sizeof(VkAabbPositionsKHR)};
+
+ geometryType = VK_GEOMETRY_TYPE_AABBS_KHR;
+ geometry.aabbs = accelerationStructureGeometryAabbsDataKHR;
+ } else {
+ assert(false && "unknown geometry type");
+ }
+
+ const VkAccelerationStructureGeometryKHR accelerationStructureGeometry = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR,
+ nullptr,
+ geometryType,
+ geometry,
+ 0u,
+ };
+ const VkAccelerationStructureBuildRangeInfoKHR
+ accelerationStructureBuildRangeInfosKHR = {
+ static_cast<uint32_t>(geometryData->getPrimitiveCount()), 0, 0, 0};
+
+ accelerationStructureGeometriesKHR_[geometryNdx] =
+ accelerationStructureGeometry;
+ accelerationStructureBuildRangeInfoKHR_[geometryNdx] =
+ accelerationStructureBuildRangeInfosKHR;
+ maxPrimitiveCounts_[geometryNdx] =
+ accelerationStructureBuildRangeInfosKHR.primitiveCount;
+ vertexBufferOffsets[geometryNdx] = vertexBufferSize;
+ size_t s1 = sizeof(geometryData->GetData()[0]);
+ vertexBufferSize += align(geometryData->GetData().size() * s1, 8);
+ }
+
+ const VkAccelerationStructureGeometryKHR*
+ accelerationStructureGeometriesKHRPointer =
+ accelerationStructureGeometriesKHR_.data();
+ accelerationStructureBuildGeometryInfoKHR_ = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR,
+ nullptr,
+ VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,
+ 0u,
+ VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR,
+ VK_NULL_HANDLE,
+ VK_NULL_HANDLE,
+ static_cast<uint32_t>(accelerationStructureGeometriesKHR_.size()),
+ accelerationStructureGeometriesKHRPointer,
+ nullptr,
+ null_placeholder,
+ };
+ VkAccelerationStructureBuildSizesInfoKHR sizeInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR, nullptr, 0,
+ 0, 0};
+
+ device_->GetPtrs()->vkGetAccelerationStructureBuildSizesKHR(
+ device_->GetVkDevice(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
+ &accelerationStructureBuildGeometryInfoKHR_, maxPrimitiveCounts_.data(),
+ &sizeInfo);
+
+ const uint32_t accelerationStructureSize =
+ static_cast<uint32_t>(sizeInfo.accelerationStructureSize);
+
+ buffer_ =
+ MakeUnique<TransferBuffer>(device_, accelerationStructureSize, nullptr);
+ buffer_->AddUsageFlags(
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ buffer_->Initialize();
+
+ const VkAccelerationStructureCreateInfoKHR accelerationStructureCreateInfoKHR{
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR,
+ nullptr,
+ 0,
+ buffer_->GetVkBuffer(),
+ 0,
+ accelerationStructureSize,
+ VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR,
+ 0};
+
+ if (device_->GetPtrs()->vkCreateAccelerationStructureKHR(
+ device_->GetVkDevice(), &accelerationStructureCreateInfoKHR, nullptr,
+ &blas_) != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateAccelerationStructureKHR failed");
+
+ accelerationStructureBuildGeometryInfoKHR_.dstAccelerationStructure = blas_;
+
+ if (sizeInfo.buildScratchSize > 0) {
+ scratch_buffer_ = MakeUnique<TransferBuffer>(
+ device_, static_cast<uint32_t>(sizeInfo.buildScratchSize), nullptr);
+ scratch_buffer_->AddUsageFlags(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ scratch_buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ scratch_buffer_->Initialize();
+
+ accelerationStructureBuildGeometryInfoKHR_.scratchData.deviceAddress =
+ scratch_buffer_->getBufferDeviceAddress();
+ }
+
+ if (vertexBufferSize > 0) {
+ vertex_buffer_ = MakeUnique<TransferBuffer>(
+ device_, static_cast<uint32_t>(vertexBufferSize), nullptr);
+ vertex_buffer_->AddUsageFlags(
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ vertex_buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ vertex_buffer_->SetMemoryPropertiesFlags(
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
+ VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
+ vertex_buffer_->Initialize();
+
+ void* memory_ptr = vertex_buffer_->HostAccessibleMemoryPtr();
+
+ for (size_t geometryNdx = 0; geometryNdx < geometries.size();
+ ++geometryNdx) {
+ VkDeviceOrHostAddressConstKHR p;
+
+ p.deviceAddress = vertex_buffer_.get()->getBufferDeviceAddress() +
+ vertexBufferOffsets[geometryNdx];
+
+ const auto& data = geometries[geometryNdx]->GetData();
+ std::memcpy(reinterpret_cast<char*>(memory_ptr) +
+ vertexBufferOffsets[geometryNdx],
+ data.data(), data.size() * sizeof(*data.data()));
+
+ if (geometries[geometryNdx]->IsTriangle()) {
+ accelerationStructureGeometriesKHR_[geometryNdx]
+ .geometry.triangles.vertexData = p;
+ } else if (geometries[geometryNdx]->IsAABB()) {
+ accelerationStructureGeometriesKHR_[geometryNdx].geometry.aabbs.data =
+ p;
+ } else {
+ assert(false && "unknown geometry type");
+ }
+ }
+ }
+
+ return {};
+}
+
+Result BLAS::BuildBLAS(CommandBuffer* command_buffer) {
+ if (blas_ == VK_NULL_HANDLE)
+ return Result("Acceleration structure should be created first");
+ if (built_)
+ return {};
+
+ VkCommandBuffer cmdBuffer = command_buffer->GetVkCommandBuffer();
+
+ vertex_buffer_->CopyToDevice(command_buffer);
+
+ VkAccelerationStructureBuildRangeInfoKHR*
+ accelerationStructureBuildRangeInfoKHRPtr =
+ accelerationStructureBuildRangeInfoKHR_.data();
+
+ device_->GetPtrs()->vkCmdBuildAccelerationStructuresKHR(
+ cmdBuffer, 1, &accelerationStructureBuildGeometryInfoKHR_,
+ &accelerationStructureBuildRangeInfoKHRPtr);
+
+ const VkAccessFlags accessMasks =
+ VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR |
+ VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
+ const VkMemoryBarrier memBarrier{
+ VK_STRUCTURE_TYPE_MEMORY_BARRIER,
+ nullptr,
+ accessMasks,
+ accessMasks,
+ };
+
+ device_->GetPtrs()->vkCmdPipelineBarrier(
+ cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memBarrier, 0, nullptr, 0,
+ nullptr);
+
+ built_ = true;
+
+ return {};
+}
+
+VkDeviceAddress BLAS::getVkBLASDeviceAddress() {
+ VkAccelerationStructureDeviceAddressInfoKHR info = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR, nullptr,
+ blas_};
+
+ assert(blas_ != VK_NULL_HANDLE);
+
+ return device_->GetPtrs()->vkGetAccelerationStructureDeviceAddressKHR(
+ device_->GetVkDevice(), &info);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/blas.h b/src/vulkan/blas.h
new file mode 100644
index 0000000..7a22097
--- /dev/null
+++ b/src/vulkan/blas.h
@@ -0,0 +1,58 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_VULKAN_BLAS_H_
+#define SRC_VULKAN_BLAS_H_
+
+#include <vector>
+#include <memory>
+
+#include "src/acceleration_structure.h"
+#include "src/vulkan/device.h"
+#include "src/vulkan/transfer_buffer.h"
+
+namespace amber {
+namespace vulkan {
+
+class BLAS {
+ public:
+ explicit BLAS(Device* device);
+ ~BLAS();
+
+ Result CreateBLAS(amber::BLAS* blas);
+ Result BuildBLAS(CommandBuffer* command_buffer);
+ VkAccelerationStructureKHR GetVkBLAS() { return blas_; }
+ VkDeviceAddress getVkBLASDeviceAddress();
+
+ private:
+ Device* device_ = nullptr;
+ VkAccelerationStructureKHR blas_ = VK_NULL_HANDLE;
+ bool built_ = false;
+ std::unique_ptr<TransferBuffer> buffer_;
+ std::unique_ptr<TransferBuffer> scratch_buffer_;
+ std::unique_ptr<TransferBuffer> vertex_buffer_;
+ VkAccelerationStructureBuildGeometryInfoKHR
+ accelerationStructureBuildGeometryInfoKHR_;
+ std::vector<VkAccelerationStructureGeometryKHR>
+ accelerationStructureGeometriesKHR_;
+ std::vector<VkAccelerationStructureBuildRangeInfoKHR>
+ accelerationStructureBuildRangeInfoKHR_;
+ std::vector<uint32_t> maxPrimitiveCounts_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_BLAS_H_
diff --git a/src/vulkan/command_buffer.cc b/src/vulkan/command_buffer.cc
index b4d28a6..d5546a8 100644
--- a/src/vulkan/command_buffer.cc
+++ b/src/vulkan/command_buffer.cc
@@ -74,7 +74,7 @@ Result CommandBuffer::BeginRecording() {
}
Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms,
- bool pipeline_runtime_layer_enabled) {
+ bool pipeline_runtime_layer_enabled) {
if (device_->GetPtrs()->vkEndCommandBuffer(command_) != VK_SUCCESS)
return Result("Vulkan::Calling vkEndCommandBuffer Fail");
@@ -87,6 +87,7 @@ Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms,
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_;
+
if (device_->GetPtrs()->vkQueueSubmit(device_->GetVkQueue(), 1, &submit_info,
fence_) != VK_SUCCESS) {
return Result("Vulkan::Calling vkQueueSubmit Fail");
@@ -94,9 +95,12 @@ Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms,
guarded_ = false;
+ const uint64_t timeout_ns =
+ timeout_ms == static_cast<uint32_t>(~0u) // honor 32bit infinity
+ ? ~0ull
+ : static_cast<uint64_t>(timeout_ms) * 1000ULL * 1000ULL;
VkResult r = device_->GetPtrs()->vkWaitForFences(
- device_->GetVkDevice(), 1, &fence_, VK_TRUE,
- static_cast<uint64_t>(timeout_ms) * 1000ULL * 1000ULL /* nanosecond */);
+ device_->GetVkDevice(), 1, &fence_, VK_TRUE, timeout_ns);
if (r == VK_TIMEOUT)
return Result("Vulkan::Calling vkWaitForFences Timeout");
if (r != VK_SUCCESS) {
@@ -118,12 +122,12 @@ Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms,
return Result("Vulkan::Calling vkWaitForFences Fail (" + result_str + ")");
}
- /*
- google/vulkan-performance-layers requires a call to vkDeviceWaitIdle or
- vkQueueWaitIdle in order to report the information. Since we want to be
- able to use that layer in conjunction with Amber we need to somehow
- communicate that the Amber script has completed.
- */
+ /*
+google/vulkan-performance-layers requires a call to vkDeviceWaitIdle or
+vkQueueWaitIdle in order to report the information. Since we want to be
+able to use that layer in conjunction with Amber we need to somehow
+communicate that the Amber script has completed.
+*/
if (pipeline_runtime_layer_enabled)
device_->GetPtrs()->vkQueueWaitIdle(device_->GetVkQueue());
@@ -152,7 +156,7 @@ CommandBufferGuard::~CommandBufferGuard() {
}
Result CommandBufferGuard::Submit(uint32_t timeout_ms,
- bool pipeline_runtime_layer_enabled) {
+ bool pipeline_runtime_layer_enabled) {
assert(buffer_->guarded_);
return buffer_->SubmitAndReset(timeout_ms, pipeline_runtime_layer_enabled);
}
diff --git a/src/vulkan/command_buffer.h b/src/vulkan/command_buffer.h
index 349cce4..67bfa11 100644
--- a/src/vulkan/command_buffer.h
+++ b/src/vulkan/command_buffer.h
@@ -84,8 +84,7 @@ class CommandBufferGuard {
Result GetResult() { return result_; }
/// Submits and resets the internal command buffer.
- Result Submit(uint32_t timeout_ms,
- bool pipeline_runtime_layer_enabled);
+ Result Submit(uint32_t timeout_ms, bool pipeline_runtime_layer_enabled);
private:
Result result_;
diff --git a/src/vulkan/descriptor.cc b/src/vulkan/descriptor.cc
index 169c71c..fd605be 100644
--- a/src/vulkan/descriptor.cc
+++ b/src/vulkan/descriptor.cc
@@ -1,4 +1,5 @@
// Copyright 2019 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -54,6 +55,8 @@ VkDescriptorType Descriptor::GetVkDescriptorType() const {
return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
case DescriptorType::kStorageTexelBuffer:
return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+ case DescriptorType::kTLAS:
+ return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
default:
assert(type_ == DescriptorType::kSampledImage);
return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
diff --git a/src/vulkan/descriptor.h b/src/vulkan/descriptor.h
index 88f6813..cc3c7c1 100644
--- a/src/vulkan/descriptor.h
+++ b/src/vulkan/descriptor.h
@@ -1,4 +1,5 @@
// Copyright 2019 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -34,6 +35,7 @@ class BufferDescriptor;
class ImageDescriptor;
class BufferBackedDescriptor;
class SamplerDescriptor;
+class TLASDescriptor;
enum class DescriptorType : uint8_t {
kStorageBuffer = 0,
@@ -45,7 +47,8 @@ enum class DescriptorType : uint8_t {
kCombinedImageSampler,
kUniformTexelBuffer,
kStorageTexelBuffer,
- kSampler
+ kSampler,
+ kTLAS
};
class Descriptor {
@@ -66,6 +69,7 @@ class Descriptor {
virtual ImageDescriptor* AsImageDescriptor() { return nullptr; }
virtual BufferBackedDescriptor* AsBufferBackedDescriptor() { return nullptr; }
virtual SamplerDescriptor* AsSamplerDescriptor() { return nullptr; }
+ virtual TLASDescriptor* AsTLASDescriptor() { return nullptr; }
uint32_t GetDescriptorSet() const { return descriptor_set_; }
uint32_t GetBinding() const { return binding_; }
VkDescriptorType GetVkDescriptorType() const;
diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc
index 43a1d8b..0aebd7c 100644
--- a/src/vulkan/device.cc
+++ b/src/vulkan/device.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -86,8 +87,14 @@ const char kSubgroupSupportedStagesCompute[] =
const char kShaderSubgroupExtendedTypes[] =
"ShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes";
-const char kIndexTypeUint8[] =
- "IndexTypeUint8Features.indexTypeUint8";
+const char kIndexTypeUint8[] = "IndexTypeUint8Features.indexTypeUint8";
+
+const char kAccelerationStructure[] =
+ "AccelerationStructureFeaturesKHR.accelerationStructure";
+const char kBufferDeviceAddress[] =
+ "BufferDeviceAddressFeatures.bufferDeviceAddress";
+const char kRayTracingPipeline[] =
+ "RayTracingPipelineFeaturesKHR.rayTracingPipeline";
struct BaseOutStructure {
VkStructureType sType;
@@ -447,9 +454,11 @@ Result Device::Initialize(
PFN_vkGetInstanceProcAddr getInstanceProcAddr,
Delegate* delegate,
const std::vector<std::string>& required_features,
+ const std::vector<std::string>& required_properties,
const std::vector<std::string>& required_device_extensions,
const VkPhysicalDeviceFeatures& available_features,
const VkPhysicalDeviceFeatures2KHR& available_features2,
+ const VkPhysicalDeviceProperties2KHR& available_properties2,
const std::vector<std::string>& available_extensions) {
Result r = LoadVulkanPointers(getInstanceProcAddr, delegate);
if (!r.IsSuccess())
@@ -479,6 +488,12 @@ Result Device::Initialize(
VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*
shader_subgroup_extended_types_ptrs = nullptr;
VkPhysicalDeviceIndexTypeUint8FeaturesEXT* index_type_uint8_ptrs = nullptr;
+ VkPhysicalDeviceAccelerationStructureFeaturesKHR*
+ acceleration_structure_ptrs = nullptr;
+ VkPhysicalDeviceBufferDeviceAddressFeatures* bda_ptrs = nullptr;
+ VkPhysicalDeviceRayTracingPipelineFeaturesKHR* ray_tracing_pipeline_ptrs =
+ nullptr;
+
void* ptr = available_features2.pNext;
while (ptr != nullptr) {
BaseOutStructure* s = static_cast<BaseOutStructure*>(ptr);
@@ -513,6 +528,19 @@ Result Device::Initialize(
index_type_uint8_ptrs =
static_cast<VkPhysicalDeviceIndexTypeUint8FeaturesEXT*>(ptr);
break;
+ // NOLINTNEXTLINE(whitespace/line_length)
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR:
+ acceleration_structure_ptrs =
+ static_cast<VkPhysicalDeviceAccelerationStructureFeaturesKHR*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES:
+ bda_ptrs =
+ static_cast<VkPhysicalDeviceBufferDeviceAddressFeatures*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR:
+ ray_tracing_pipeline_ptrs =
+ static_cast<VkPhysicalDeviceRayTracingPipelineFeaturesKHR*>(ptr);
+ break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES:
vulkan11_ptrs = static_cast<VkPhysicalDeviceVulkan11Features*>(ptr);
break;
@@ -520,8 +548,8 @@ Result Device::Initialize(
vulkan12_ptrs = static_cast<VkPhysicalDeviceVulkan12Features*>(ptr);
break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES:
- vulkan13_ptrs = static_cast<VkPhysicalDeviceVulkan13Features*>(ptr);
- break;
+ vulkan13_ptrs = static_cast<VkPhysicalDeviceVulkan13Features*>(ptr);
+ break;
default:
break;
}
@@ -581,7 +609,21 @@ Result Device::Initialize(
return amber::Result(
"Index type uint8_t requested but feature not returned");
}
-
+ if (feature == kAccelerationStructure &&
+ acceleration_structure_ptrs == nullptr) {
+ return amber::Result(
+ "Acceleration structure requested but feature not returned");
+ }
+ if (feature == kBufferDeviceAddress && bda_ptrs == nullptr &&
+ vulkan12_ptrs == nullptr) {
+ return amber::Result(
+ "Buffer device address requested but feature not returned");
+ }
+ if (feature == kRayTracingPipeline &&
+ ray_tracing_pipeline_ptrs == nullptr) {
+ return amber::Result(
+ "Ray tracing pipeline requested but feature not returned");
+ }
// Next check the fields of the feature structures.
@@ -668,6 +710,10 @@ Result Device::Initialize(
vulkan12_ptrs->shaderSubgroupExtendedTypes != VK_TRUE) {
return amber::Result("Missing subgroup extended types");
}
+ if (feature == kBufferDeviceAddress &&
+ vulkan12_ptrs->bufferDeviceAddress != VK_TRUE) {
+ return amber::Result("Missing buffer device address");
+ }
} else {
// Vulkan 1.2 structure was not found. Use separate structures per each
// feature.
@@ -695,18 +741,22 @@ Result Device::Initialize(
VK_TRUE) {
return amber::Result("Missing subgroup extended types");
}
+ if (feature == kBufferDeviceAddress &&
+ bda_ptrs->bufferDeviceAddress != VK_TRUE) {
+ return amber::Result("Missing buffer device address");
+ }
}
// If Vulkan 1.3 structure exists the features are set there.
if (vulkan13_ptrs) {
- if (feature == kSubgroupSizeControl &&
- vulkan13_ptrs->subgroupSizeControl != VK_TRUE) {
- return amber::Result("Missing subgroup size control feature");
- }
- if (feature == kComputeFullSubgroups &&
- vulkan13_ptrs->computeFullSubgroups != VK_TRUE) {
- return amber::Result("Missing compute full subgroups feature");
- }
+ if (feature == kSubgroupSizeControl &&
+ vulkan13_ptrs->subgroupSizeControl != VK_TRUE) {
+ return amber::Result("Missing subgroup size control feature");
+ }
+ if (feature == kComputeFullSubgroups &&
+ vulkan13_ptrs->computeFullSubgroups != VK_TRUE) {
+ return amber::Result("Missing compute full subgroups feature");
+ }
} else {
if (feature == kSubgroupSizeControl &&
subgroup_size_control_features->subgroupSizeControl != VK_TRUE) {
@@ -726,6 +776,87 @@ Result Device::Initialize(
"required extensions");
}
+ const bool needs_shader_group_handle_size =
+ std::find(required_features.begin(), required_features.end(),
+ kAccelerationStructure) != required_features.end();
+
+ if (needs_shader_group_handle_size) {
+ VkPhysicalDeviceRayTracingPipelinePropertiesKHR rt_pipeline_properties = {};
+ rt_pipeline_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR;
+
+ VkPhysicalDeviceProperties2KHR properties2 = {};
+ properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+ properties2.pNext = &rt_pipeline_properties;
+
+ ptrs_.vkGetPhysicalDeviceProperties2(physical_device_, &properties2);
+
+ shader_group_handle_size_ = rt_pipeline_properties.shaderGroupHandleSize;
+ }
+
+ VkPhysicalDeviceVulkan12Properties* pv12 = nullptr;
+ VkPhysicalDeviceFloatControlsProperties* pfc = nullptr;
+
+ ptr = available_properties2.pNext;
+ while (ptr != nullptr) {
+ BaseOutStructure* s = static_cast<BaseOutStructure*>(ptr);
+ switch (s->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES:
+ pv12 = static_cast<VkPhysicalDeviceVulkan12Properties*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR:
+ pfc = static_cast<VkPhysicalDeviceFloatControlsPropertiesKHR*>(ptr);
+ break;
+ default:
+ break;
+ }
+ ptr = s->pNext;
+ }
+
+#define CHK_P(R, P, NAME, S1, S2) \
+ do { \
+ if (R == -1 && P == #NAME) \
+ R = ((S1 && S1->NAME) || (S2 && S2->NAME)) ? 1 : 0; \
+ } while (false)
+
+ for (const std::string& prop : required_properties) {
+ const size_t dot_pos = prop.find('.');
+ const size_t dot_found = dot_pos != std::string::npos;
+ const std::string prefix = dot_found ? prop.substr(0, dot_pos) : "";
+ const std::string name = dot_found ? prop.substr(dot_pos + 1) : prop;
+ int supported = -1;
+
+ if (supported == -1 && prefix == "FloatControlsProperties") {
+ if (pfc == nullptr && pv12 == nullptr)
+ return Result(
+ "Vulkan: Device::Initialize given physical device does not support "
+ "required float control properties");
+
+ CHK_P(supported, name, shaderSignedZeroInfNanPreserveFloat16, pfc, pv12);
+ CHK_P(supported, name, shaderSignedZeroInfNanPreserveFloat32, pfc, pv12);
+ CHK_P(supported, name, shaderSignedZeroInfNanPreserveFloat64, pfc, pv12);
+ CHK_P(supported, name, shaderDenormPreserveFloat16, pfc, pv12);
+ CHK_P(supported, name, shaderDenormPreserveFloat32, pfc, pv12);
+ CHK_P(supported, name, shaderDenormPreserveFloat64, pfc, pv12);
+ CHK_P(supported, name, shaderDenormFlushToZeroFloat16, pfc, pv12);
+ CHK_P(supported, name, shaderDenormFlushToZeroFloat32, pfc, pv12);
+ CHK_P(supported, name, shaderDenormFlushToZeroFloat64, pfc, pv12);
+ CHK_P(supported, name, shaderRoundingModeRTEFloat16, pfc, pv12);
+ CHK_P(supported, name, shaderRoundingModeRTEFloat32, pfc, pv12);
+ CHK_P(supported, name, shaderRoundingModeRTEFloat64, pfc, pv12);
+ CHK_P(supported, name, shaderRoundingModeRTZFloat16, pfc, pv12);
+ CHK_P(supported, name, shaderRoundingModeRTZFloat32, pfc, pv12);
+ CHK_P(supported, name, shaderRoundingModeRTZFloat64, pfc, pv12);
+ }
+
+ if (supported == 0)
+ return Result("Vulkan: Device::Initialize missing " + prop + " property");
+
+ if (supported == -1)
+ return Result(
+ "Vulkan: Device::Initialize property not handled " + prop);
+ }
+
ptrs_.vkGetPhysicalDeviceMemoryProperties(physical_device_,
&physical_memory_properties_);
@@ -1396,5 +1527,9 @@ uint32_t Device::GetMaxSubgroupSize() const {
return subgroup_size_control_properties_.maxSubgroupSize;
}
+uint32_t Device::GetRayTracingShaderGroupHandleSize() const {
+ return shader_group_handle_size_;
+}
+
} // namespace vulkan
} // namespace amber
diff --git a/src/vulkan/device.h b/src/vulkan/device.h
index ff76c0f..8cda4b7 100644
--- a/src/vulkan/device.h
+++ b/src/vulkan/device.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -47,9 +48,11 @@ class Device {
Result Initialize(PFN_vkGetInstanceProcAddr getInstanceProcAddr,
Delegate* delegate,
const std::vector<std::string>& required_features,
+ const std::vector<std::string>& required_properties,
const std::vector<std::string>& required_device_extensions,
const VkPhysicalDeviceFeatures& available_features,
const VkPhysicalDeviceFeatures2KHR& available_features2,
+ const VkPhysicalDeviceProperties2KHR& available_properties2,
const std::vector<std::string>& available_extensions);
/// Returns true if |format| and the |buffer|s buffer type combination is
@@ -88,6 +91,8 @@ class Device {
/// Returns the maximum required subgroup size or 0 if subgroup size control
/// is not supported.
uint32_t GetMaxSubgroupSize() const;
+ /// Returns ray tracing shader group handle size.
+ uint32_t GetRayTracingShaderGroupHandleSize() const;
private:
Result LoadVulkanPointers(PFN_vkGetInstanceProcAddr, Delegate* delegate);
@@ -102,6 +107,7 @@ class Device {
VkDevice device_ = VK_NULL_HANDLE;
VkQueue queue_ = VK_NULL_HANDLE;
uint32_t queue_family_index_ = 0;
+ uint32_t shader_group_handle_size_ = 0;
VulkanPtrs ptrs_;
};
diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc
index b0842ec..a61d84e 100644
--- a/src/vulkan/engine_vulkan.cc
+++ b/src/vulkan/engine_vulkan.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,6 +24,7 @@
#include "src/type_parser.h"
#include "src/vulkan/compute_pipeline.h"
#include "src/vulkan/graphics_pipeline.h"
+#include "src/vulkan/raytracing_pipeline.h"
namespace amber {
namespace vulkan {
@@ -48,6 +50,24 @@ Result ToVkShaderStage(ShaderType type, VkShaderStageFlagBits* ret) {
case kShaderTypeTessellationEvaluation:
*ret = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
break;
+ case kShaderTypeRayGeneration:
+ *ret = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
+ break;
+ case kShaderTypeAnyHit:
+ *ret = VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
+ break;
+ case kShaderTypeClosestHit:
+ *ret = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
+ break;
+ case kShaderTypeMiss:
+ *ret = VK_SHADER_STAGE_MISS_BIT_KHR;
+ break;
+ case kShaderTypeIntersection:
+ *ret = VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
+ break;
+ case kShaderTypeCall:
+ *ret = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
+ break;
case kShaderTypeCompute:
*ret = VK_SHADER_STAGE_COMPUTE_BIT;
break;
@@ -85,6 +105,9 @@ EngineVulkan::~EngineVulkan() {
device_->GetPtrs()->vkDestroyShaderModule(vk_device, shader.second,
nullptr);
}
+ pipeline_map_.clear();
+ tlases_.clear();
+ blases_.clear();
}
}
@@ -92,6 +115,7 @@ Result EngineVulkan::Initialize(
EngineConfig* config,
Delegate* delegate,
const std::vector<std::string>& features,
+ const std::vector<std::string>& properties,
const std::vector<std::string>& instance_extensions,
const std::vector<std::string>& device_extensions) {
if (device_)
@@ -118,8 +142,9 @@ Result EngineVulkan::Initialize(
vk_config->queue);
Result r = device_->Initialize(
- vk_config->vkGetInstanceProcAddr, delegate, features, device_extensions,
- vk_config->available_features, vk_config->available_features2,
+ vk_config->vkGetInstanceProcAddr, delegate, features, properties,
+ device_extensions, vk_config->available_features,
+ vk_config->available_features2, vk_config->available_properties2,
vk_config->available_device_extensions);
if (!r.IsSuccess())
return r;
@@ -139,8 +164,8 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
pipeline_map_[pipeline] = PipelineInfo();
auto& info = pipeline_map_[pipeline];
- for (const auto& shader_info : pipeline->GetShaders()) {
- Result r = SetShader(pipeline, shader_info);
+ for (size_t i = 0; i < pipeline->GetShaders().size(); i++) {
+ Result r = SetShader(pipeline, pipeline->GetShaders()[i], i);
if (!r.IsSuccess())
return r;
}
@@ -168,13 +193,23 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
const auto& engine_data = GetEngineData();
std::unique_ptr<Pipeline> vk_pipeline;
- if (pipeline->GetType() == PipelineType::kCompute) {
+ if (pipeline->GetType() == PipelineType::kRayTracing) {
+ std::vector<VkRayTracingShaderGroupCreateInfoKHR> shader_group_create_info;
+
+ r = GetVkShaderGroupInfo(pipeline, &shader_group_create_info);
+ if (!r.IsSuccess())
+ return r;
+
+ vk_pipeline = MakeUnique<RayTracingPipeline>(
+ device_.get(), &blases_, &tlases_, engine_data.fence_timeout_ms,
+ engine_data.pipeline_runtime_layer_enabled, stage_create_info);
+ r = vk_pipeline->AsRayTracingPipeline()->Initialize(
+ pool_.get(), shader_group_create_info);
+ } else if (pipeline->GetType() == PipelineType::kCompute) {
vk_pipeline = MakeUnique<ComputePipeline>(
device_.get(), engine_data.fence_timeout_ms,
engine_data.pipeline_runtime_layer_enabled, stage_create_info);
r = vk_pipeline->AsCompute()->Initialize(pool_.get());
- if (!r.IsSuccess())
- return r;
} else {
vk_pipeline = MakeUnique<GraphicsPipeline>(
device_.get(), pipeline->GetColorAttachments(),
@@ -188,10 +223,11 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
r = vk_pipeline->AsGraphics()->Initialize(pipeline->GetFramebufferWidth(),
pipeline->GetFramebufferHeight(),
pool_.get());
- if (!r.IsSuccess())
- return r;
}
+ if (!r.IsSuccess())
+ return r;
+
info.vk_pipeline = std::move(vk_pipeline);
// Set the entry point names for the pipeline.
@@ -286,21 +322,38 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
return r;
}
+ if (info.vk_pipeline->IsRayTracing()) {
+ for (const auto& tlas_info : pipeline->GetTLASes()) {
+ auto cmd = MakeUnique<TLASCommand>(pipeline);
+ cmd->SetDescriptorSet(tlas_info.descriptor_set);
+ cmd->SetBinding(tlas_info.binding);
+ cmd->SetTLAS(tlas_info.tlas);
+
+ r = info.vk_pipeline->AddTLASDescriptor(cmd.get());
+ if (!r.IsSuccess())
+ return r;
+ }
+ }
+
return {};
}
Result EngineVulkan::SetShader(amber::Pipeline* pipeline,
- const amber::Pipeline::ShaderInfo& shader) {
+ const amber::Pipeline::ShaderInfo& shader,
+ size_t index) {
+ const bool rt = pipeline->IsRayTracing();
const auto type = shader.GetShaderType();
const auto& data = shader.GetData();
const auto shader_name = shader.GetShader()->GetName();
auto& info = pipeline_map_[pipeline];
- auto it = info.shader_info.find(type);
- if (it != info.shader_info.end())
- return Result("Vulkan::Setting Duplicated Shader Types Fail");
+ if (!rt) {
+ auto it = info.shader_info.find(type);
+ if (it != info.shader_info.end())
+ return Result("Vulkan::Setting Duplicated Shader Types Fail");
+ }
- VkShaderModule shader_module;
+ VkShaderModule shader_module = VK_NULL_HANDLE;
if (shaders_.find(shader_name) != shaders_.end()) {
shader_module = shaders_[shader_name];
} else {
@@ -318,7 +371,18 @@ Result EngineVulkan::SetShader(amber::Pipeline* pipeline,
shaders_[shader_name] = shader_module;
}
- info.shader_info[type].shader = shader_module;
+ if (!rt) {
+ info.shader_info[type].shader = shader_module;
+ } else {
+ assert(index <= info.shader_info_rt.size());
+ if (info.shader_info_rt.size() == index) {
+ info.shader_info_rt.push_back(PipelineInfo::ShaderInfo());
+ }
+ info.shader_info_rt[index].shader = shader_module;
+ info.shader_info_rt[index].type = type;
+
+ return {};
+ }
for (auto& shader_info : pipeline->GetShaders()) {
if (shader_info.GetShaderType() != type)
@@ -348,6 +412,7 @@ Result EngineVulkan::SetShader(amber::Pipeline* pipeline,
"device.");
}
}
+
info.shader_info[type].required_subgroup_size = required_subgroup_size_uint;
info.shader_info[type].create_flags = 0;
@@ -388,48 +453,138 @@ Result EngineVulkan::SetShader(amber::Pipeline* pipeline,
}
Result EngineVulkan::GetVkShaderStageInfo(
+ ShaderType shader_type,
+ const PipelineInfo::ShaderInfo& shader_info,
+ VkPipelineShaderStageCreateInfo* stage_info) {
+ VkShaderStageFlagBits stage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
+ Result r = ToVkShaderStage(shader_type, &stage);
+ if (!r.IsSuccess())
+ return r;
+
+ *stage_info = VkPipelineShaderStageCreateInfo();
+ stage_info->sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stage_info->flags = shader_info.create_flags;
+ stage_info->stage = stage;
+ stage_info->module = shader_info.shader;
+
+ stage_info->pName = nullptr;
+ if (shader_info.specialization_entries &&
+ !shader_info.specialization_entries->empty()) {
+ stage_info->pSpecializationInfo = shader_info.specialization_info.get();
+ }
+
+ return {};
+}
+
+Result EngineVulkan::GetVkShaderStageInfo(
amber::Pipeline* pipeline,
std::vector<VkPipelineShaderStageCreateInfo>* out) {
auto& info = pipeline_map_[pipeline];
- std::vector<VkPipelineShaderStageCreateInfo> stage_info(
- info.shader_info.size());
- uint32_t stage_count = 0;
- for (auto& it : info.shader_info) {
- VkShaderStageFlagBits stage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
- Result r = ToVkShaderStage(it.first, &stage);
- if (!r.IsSuccess())
- return r;
-
- stage_info[stage_count] = VkPipelineShaderStageCreateInfo();
- stage_info[stage_count].sType =
- VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
- stage_info[stage_count].flags = it.second.create_flags;
- stage_info[stage_count].stage = stage;
- stage_info[stage_count].module = it.second.shader;
- stage_info[stage_count].pName = nullptr;
- if (it.second.specialization_entries &&
- !it.second.specialization_entries->empty()) {
- stage_info[stage_count].pSpecializationInfo =
- it.second.specialization_info.get();
+ const size_t size = pipeline->IsRayTracing() ? info.shader_info_rt.size()
+ : info.shader_info.size();
+ std::vector<VkPipelineShaderStageCreateInfo> stage_info(size);
+ if (pipeline->IsRayTracing()) {
+ for (size_t i = 0; i < info.shader_info_rt.size(); i++) {
+ Result r = GetVkShaderStageInfo(info.shader_info_rt[i].type,
+ info.shader_info_rt[i], &stage_info[i]);
+ if (!r.IsSuccess())
+ return r;
}
-
- if (stage == VK_SHADER_STAGE_COMPUTE_BIT &&
- it.second.required_subgroup_size > 0) {
- VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT* pSubgroupSize =
- new VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT();
- pSubgroupSize->sType =
- VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT; // NOLINT(whitespace/line_length)
- pSubgroupSize->pNext = nullptr;
- pSubgroupSize->requiredSubgroupSize = it.second.required_subgroup_size;
- stage_info[stage_count].pNext = pSubgroupSize;
+ } else {
+ uint32_t stage_count = 0;
+ for (auto& it : info.shader_info) {
+ Result r =
+ GetVkShaderStageInfo(it.first, it.second, &stage_info[stage_count]);
+ if (!r.IsSuccess())
+ return r;
+
+ if (it.first == kShaderTypeCompute &&
+ it.second.required_subgroup_size > 0) {
+ VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT* pSubgroupSize =
+ new VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT();
+ pSubgroupSize->sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT; // NOLINT(whitespace/line_length)
+ pSubgroupSize->pNext = nullptr;
+ pSubgroupSize->requiredSubgroupSize = it.second.required_subgroup_size;
+ stage_info[stage_count].pNext = pSubgroupSize;
+ }
+ ++stage_count;
}
- ++stage_count;
}
*out = stage_info;
return {};
}
+Result EngineVulkan::GetVkShaderGroupInfo(
+ amber::Pipeline* pipeline,
+ std::vector<VkRayTracingShaderGroupCreateInfoKHR>* out) {
+ auto shaders = pipeline->GetShaders();
+
+ out->clear();
+ out->reserve(pipeline->GetShaderGroups().size());
+
+ for (auto& g : pipeline->GetShaderGroups()) {
+ Result r;
+ ShaderGroup* sg = g.get();
+
+ if (sg == nullptr)
+ return Result("Invalid shader group");
+
+ VkRayTracingShaderGroupCreateInfoKHR group_info = {
+ VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
+ nullptr,
+ VK_RAY_TRACING_SHADER_GROUP_TYPE_MAX_ENUM_KHR,
+ VK_SHADER_UNUSED_KHR,
+ VK_SHADER_UNUSED_KHR,
+ VK_SHADER_UNUSED_KHR,
+ VK_SHADER_UNUSED_KHR,
+ nullptr
+ };
+
+ if (sg->IsGeneralGroup()) {
+ group_info.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
+ r = pipeline->GetShaderIndex(sg->GetGeneralShader(),
+ &group_info.generalShader);
+ if (!r.IsSuccess())
+ return r;
+ } else if (sg->IsHitGroup()) {
+ group_info.type =
+ sg->GetIntersectionShader() == nullptr
+ ? VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR
+ : VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR;
+
+ if (sg->GetClosestHitShader()) {
+ r = pipeline->GetShaderIndex(sg->GetClosestHitShader(),
+ &group_info.closestHitShader);
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (sg->GetAnyHitShader()) {
+ r = pipeline->GetShaderIndex(sg->GetAnyHitShader(),
+ &group_info.anyHitShader);
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (sg->GetIntersectionShader()) {
+ r = pipeline->GetShaderIndex(sg->GetIntersectionShader(),
+ &group_info.intersectionShader);
+ if (!r.IsSuccess())
+ return r;
+ }
+ } else {
+ return Result("Uninitialized shader group");
+ }
+
+ out->push_back(group_info);
+ }
+
+ return {};
+}
+
Result EngineVulkan::DoClearColor(const ClearColorCommand* command) {
auto& info = pipeline_map_[command->GetPipeline()];
if (!info.vk_pipeline->IsGraphics())
@@ -626,13 +781,30 @@ Result EngineVulkan::DoDrawArrays(const DrawArraysCommand* command) {
Result EngineVulkan::DoCompute(const ComputeCommand* command) {
auto& info = pipeline_map_[command->GetPipeline()];
- if (info.vk_pipeline->IsGraphics())
- return Result("Vulkan: Compute called for graphics pipeline.");
+ if (!info.vk_pipeline->IsCompute())
+ return Result("Vulkan: Compute called for non-compute pipeline.");
return info.vk_pipeline->AsCompute()->Compute(
command->GetX(), command->GetY(), command->GetZ());
}
+Result EngineVulkan::DoTraceRays(const RayTracingCommand* command) {
+ auto& info = pipeline_map_[command->GetPipeline()];
+ if (!info.vk_pipeline->IsRayTracing())
+ return Result("Vulkan: RayTracing called for non-RayTracing pipeline.");
+
+ amber::Pipeline* pipeline = command->GetPipeline();
+
+ amber::SBT* rSBT = pipeline->GetSBT(command->GetRayGenSBTName());
+ amber::SBT* mSBT = pipeline->GetSBT(command->GetMissSBTName());
+ amber::SBT* hSBT = pipeline->GetSBT(command->GetHitsSBTName());
+ amber::SBT* cSBT = pipeline->GetSBT(command->GetCallSBTName());
+
+ return info.vk_pipeline->AsRayTracingPipeline()->TraceRays(
+ rSBT, mSBT, hSBT, cSBT, command->GetX(), command->GetY(),
+ command->GetZ());
+}
+
Result EngineVulkan::DoEntryPoint(const EntryPointCommand* command) {
auto& info = pipeline_map_[command->GetPipeline()];
if (!info.vk_pipeline)
diff --git a/src/vulkan/engine_vulkan.h b/src/vulkan/engine_vulkan.h
index 76668fb..6ac6e6e 100644
--- a/src/vulkan/engine_vulkan.h
+++ b/src/vulkan/engine_vulkan.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,13 +24,17 @@
#include <vector>
#include "amber/vulkan_header.h"
+#include "src/acceleration_structure.h"
#include "src/cast_hash.h"
#include "src/engine.h"
#include "src/pipeline.h"
+#include "src/vulkan/blas.h"
#include "src/vulkan/buffer_descriptor.h"
#include "src/vulkan/command_pool.h"
#include "src/vulkan/device.h"
#include "src/vulkan/pipeline.h"
+#include "src/vulkan/tlas.h"
+#include "src/vulkan/tlas_descriptor.h"
#include "src/vulkan/vertex_buffer.h"
namespace amber {
@@ -45,6 +50,7 @@ class EngineVulkan : public Engine {
Result Initialize(EngineConfig* config,
Delegate* delegate,
const std::vector<std::string>& features,
+ const std::vector<std::string>& properties,
const std::vector<std::string>& instance_extensions,
const std::vector<std::string>& device_extensions) override;
Result CreatePipeline(amber::Pipeline* type) override;
@@ -57,6 +63,7 @@ class EngineVulkan : public Engine {
Result DoDrawGrid(const DrawGridCommand* cmd) override;
Result DoDrawArrays(const DrawArraysCommand* cmd) override;
Result DoCompute(const ComputeCommand* cmd) override;
+ Result DoTraceRays(const RayTracingCommand* cmd) override;
Result DoEntryPoint(const EntryPointCommand* cmd) override;
Result DoPatchParameterVertices(
const PatchParameterVerticesCommand* cmd) override;
@@ -67,6 +74,7 @@ class EngineVulkan : public Engine {
std::unique_ptr<Pipeline> vk_pipeline;
std::unique_ptr<VertexBuffer> vertex_buffer;
struct ShaderInfo {
+ ShaderType type;
VkShaderModule shader;
std::unique_ptr<std::vector<VkSpecializationMapEntry>>
specialization_entries;
@@ -77,14 +85,24 @@ class EngineVulkan : public Engine {
};
std::unordered_map<ShaderType, ShaderInfo, CastHash<ShaderType>>
shader_info;
+ std::vector<PipelineInfo::ShaderInfo> shader_info_rt;
};
+ Result GetVkShaderStageInfo(ShaderType shader_type,
+ const PipelineInfo::ShaderInfo& shader_info,
+ VkPipelineShaderStageCreateInfo* stage_ci);
+
Result GetVkShaderStageInfo(
amber::Pipeline* pipeline,
std::vector<VkPipelineShaderStageCreateInfo>* out);
Result SetShader(amber::Pipeline* pipeline,
- const amber::Pipeline::ShaderInfo& shader);
+ const amber::Pipeline::ShaderInfo& shader,
+ size_t index);
+
+ Result GetVkShaderGroupInfo(
+ amber::Pipeline* pipeline,
+ std::vector<VkRayTracingShaderGroupCreateInfoKHR>* out);
std::unique_ptr<Device> device_;
std::unique_ptr<CommandPool> pool_;
@@ -92,6 +110,10 @@ class EngineVulkan : public Engine {
std::map<amber::Pipeline*, PipelineInfo> pipeline_map_;
std::map<std::string, VkShaderModule> shaders_;
+
+ BlasesMap blases_;
+
+ TlasesMap tlases_;
};
} // namespace vulkan
diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc
index 485ebb6..9db5eb7 100644
--- a/src/vulkan/graphics_pipeline.cc
+++ b/src/vulkan/graphics_pipeline.cc
@@ -871,8 +871,8 @@ Result GraphicsPipeline::Clear() {
frame_->TransferImagesToHost(command_.get());
- Result r = cmd_buf_guard.Submit(GetFenceTimeout(),
- GetPipelineRuntimeLayerEnabled());
+ Result r =
+ cmd_buf_guard.Submit(GetFenceTimeout(), GetPipelineRuntimeLayerEnabled());
if (!r.IsSuccess())
return r;
diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc
index d03b2de..6fbb254 100644
--- a/src/vulkan/pipeline.cc
+++ b/src/vulkan/pipeline.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -26,7 +27,9 @@
#include "src/vulkan/device.h"
#include "src/vulkan/graphics_pipeline.h"
#include "src/vulkan/image_descriptor.h"
+#include "src/vulkan/raytracing_pipeline.h"
#include "src/vulkan/sampler_descriptor.h"
+#include "src/vulkan/tlas_descriptor.h"
namespace amber {
namespace vulkan {
@@ -40,7 +43,7 @@ Pipeline::Pipeline(
PipelineType type,
Device* device,
uint32_t fence_timeout_ms,
- bool pipeline_runtime_layer_enabled,
+ bool pipeline_runtime_layer_enabled,
const std::vector<VkPipelineShaderStageCreateInfo>& shader_stage_info)
: device_(device),
pipeline_type_(type),
@@ -77,6 +80,10 @@ ComputePipeline* Pipeline::AsCompute() {
return static_cast<ComputePipeline*>(this);
}
+RayTracingPipeline* Pipeline::AsRayTracingPipeline() {
+ return static_cast<RayTracingPipeline*>(this);
+}
+
Result Pipeline::Initialize(CommandPool* pool) {
push_constant_ = MakeUnique<PushConstant>(device_);
@@ -277,9 +284,9 @@ Result Pipeline::GetDescriptorSlot(uint32_t desc_set,
Result Pipeline::AddDescriptorBuffer(Buffer* amber_buffer) {
// Don't add the buffer if it's already added.
- const auto& buffer = std::find_if(
- descriptor_buffers_.begin(), descriptor_buffers_.end(),
- [&](const Buffer* buf) { return buf == amber_buffer; });
+ const auto& buffer =
+ std::find_if(descriptor_buffers_.begin(), descriptor_buffers_.end(),
+ [&](const Buffer* buf) { return buf == amber_buffer; });
if (buffer != descriptor_buffers_.end()) {
return {};
}
@@ -411,6 +418,35 @@ Result Pipeline::AddSamplerDescriptor(const SamplerCommand* cmd) {
return {};
}
+Result Pipeline::AddTLASDescriptor(const TLASCommand* cmd) {
+ if (cmd == nullptr)
+ return Result("Pipeline::AddTLASDescriptor TLASCommand is nullptr");
+
+ Descriptor* desc;
+ Result r =
+ GetDescriptorSlot(cmd->GetDescriptorSet(), cmd->GetBinding(), &desc);
+ if (!r.IsSuccess())
+ return r;
+
+ auto& descriptors = descriptor_set_info_[cmd->GetDescriptorSet()].descriptors;
+
+ if (desc == nullptr) {
+ auto tlas_desc = MakeUnique<TLASDescriptor>(
+ cmd->GetTLAS(), DescriptorType::kTLAS, device_, GetBlases(),
+ GetTlases(), cmd->GetDescriptorSet(), cmd->GetBinding());
+ descriptors.push_back(std::move(tlas_desc));
+ } else {
+ if (desc->GetDescriptorType() != DescriptorType::kTLAS) {
+ return Result(
+ "Descriptors bound to the same binding needs to have matching "
+ "descriptor types");
+ }
+ desc->AsTLASDescriptor()->AddAmberTLAS(cmd->GetTLAS());
+ }
+
+ return {};
+}
+
Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
{
CommandBufferGuard guard(GetCommandBuffer());
@@ -434,7 +470,7 @@ Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
}
Result r = descriptor_transfer_resources_[buffer]->Initialize();
if (!r.IsSuccess())
- return r;
+ return r;
}
// Note that if a buffer for a descriptor is host accessible and
@@ -443,8 +479,8 @@ Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
// done after resizing backed buffer i.e., copying data to the new
// buffer from the old one. Thus, we must submit commands here to
// guarantee this.
- Result r = guard.Submit(GetFenceTimeout(),
- GetPipelineRuntimeLayerEnabled());
+ Result r =
+ guard.Submit(GetFenceTimeout(), GetPipelineRuntimeLayerEnabled());
if (!r.IsSuccess())
return r;
}
@@ -508,8 +544,10 @@ void Pipeline::BindVkDescriptorSets(const VkPipelineLayout& pipeline_layout) {
device_->GetPtrs()->vkCmdBindDescriptorSets(
command_->GetVkCommandBuffer(),
- IsGraphics() ? VK_PIPELINE_BIND_POINT_GRAPHICS
- : VK_PIPELINE_BIND_POINT_COMPUTE,
+ IsGraphics() ? VK_PIPELINE_BIND_POINT_GRAPHICS
+ : IsCompute() ? VK_PIPELINE_BIND_POINT_COMPUTE
+ : IsRayTracing() ? VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR
+ : VK_PIPELINE_BIND_POINT_MAX_ENUM,
pipeline_layout, static_cast<uint32_t>(i), 1,
&descriptor_set_info_[i].vk_desc_set,
static_cast<uint32_t>(dynamic_offsets.size()), dynamic_offsets.data());
@@ -517,6 +555,9 @@ void Pipeline::BindVkDescriptorSets(const VkPipelineLayout& pipeline_layout) {
}
Result Pipeline::ReadbackDescriptorsToHostDataQueue() {
+ if (descriptor_buffers_.empty())
+ return Result{};
+
// Record required commands to copy the data to a host visible buffer.
{
CommandBufferGuard guard(GetCommandBuffer());
@@ -551,8 +592,8 @@ Result Pipeline::ReadbackDescriptorsToHostDataQueue() {
}
}
- Result r = guard.Submit(GetFenceTimeout(),
- GetPipelineRuntimeLayerEnabled());
+ Result r =
+ guard.Submit(GetFenceTimeout(), GetPipelineRuntimeLayerEnabled());
if (!r.IsSuccess())
return r;
}
diff --git a/src/vulkan/pipeline.h b/src/vulkan/pipeline.h
index 58cefcb..99bac36 100644
--- a/src/vulkan/pipeline.h
+++ b/src/vulkan/pipeline.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,6 +39,7 @@ namespace vulkan {
class ComputePipeline;
class Device;
class GraphicsPipeline;
+class RayTracingPipeline;
/// Base class for a pipeline in Vulkan.
class Pipeline {
@@ -46,12 +48,17 @@ class Pipeline {
bool IsGraphics() const { return pipeline_type_ == PipelineType::kGraphics; }
bool IsCompute() const { return pipeline_type_ == PipelineType::kCompute; }
+ bool IsRayTracing() const {
+ return pipeline_type_ == PipelineType::kRayTracing;
+ }
GraphicsPipeline* AsGraphics();
ComputePipeline* AsCompute();
+ RayTracingPipeline* AsRayTracingPipeline();
Result AddBufferDescriptor(const BufferCommand*);
Result AddSamplerDescriptor(const SamplerCommand*);
+ Result AddTLASDescriptor(const TLASCommand*);
/// Add |buffer| data to the push constants at |offset|.
Result AddPushConstantBuffer(const Buffer* buf, uint32_t offset);
@@ -72,6 +79,8 @@ class Pipeline {
CommandBuffer* GetCommandBuffer() const { return command_.get(); }
Device* GetDevice() const { return device_; }
+ virtual BlasesMap* GetBlases() { return nullptr; }
+ virtual TlasesMap* GetTlases() { return nullptr; }
protected:
Pipeline(
diff --git a/src/vulkan/raytracing_pipeline.cc b/src/vulkan/raytracing_pipeline.cc
new file mode 100644
index 0000000..7e9b6a2
--- /dev/null
+++ b/src/vulkan/raytracing_pipeline.cc
@@ -0,0 +1,240 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <utility>
+
+#include "src/vulkan/raytracing_pipeline.h"
+
+#include "src/vulkan/blas.h"
+#include "src/vulkan/command_pool.h"
+#include "src/vulkan/device.h"
+#include "src/vulkan/sbt.h"
+#include "src/vulkan/tlas.h"
+
+namespace amber {
+namespace vulkan {
+
+inline VkStridedDeviceAddressRegionKHR makeStridedDeviceAddressRegionKHR(
+ VkDeviceAddress deviceAddress,
+ VkDeviceSize stride,
+ VkDeviceSize size) {
+ VkStridedDeviceAddressRegionKHR res;
+ res.deviceAddress = deviceAddress;
+ res.stride = stride;
+ res.size = size;
+ return res;
+}
+
+inline VkDeviceAddress getBufferDeviceAddress(Device* device, VkBuffer buffer) {
+ const VkBufferDeviceAddressInfo bufferDeviceAddressInfo = {
+ VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR,
+ nullptr,
+ buffer,
+ };
+
+ return device->GetPtrs()->vkGetBufferDeviceAddress(device->GetVkDevice(),
+ &bufferDeviceAddressInfo);
+}
+
+RayTracingPipeline::RayTracingPipeline(
+ Device* device,
+ BlasesMap* blases,
+ TlasesMap* tlases,
+ uint32_t fence_timeout_ms,
+ bool pipeline_runtime_layer_enabled,
+ const std::vector<VkPipelineShaderStageCreateInfo>& shader_stage_info)
+ : Pipeline(PipelineType::kRayTracing,
+ device,
+ fence_timeout_ms,
+ pipeline_runtime_layer_enabled,
+ shader_stage_info),
+ shader_group_create_info_(),
+ blases_(blases),
+ tlases_(tlases) {}
+
+RayTracingPipeline::~RayTracingPipeline() = default;
+
+Result RayTracingPipeline::Initialize(
+ CommandPool* pool,
+ std::vector<VkRayTracingShaderGroupCreateInfoKHR>&
+ shader_group_create_info) {
+ shader_group_create_info_.swap(shader_group_create_info);
+
+ return Pipeline::Initialize(pool);
+}
+
+Result RayTracingPipeline::CreateVkRayTracingPipeline(
+ const VkPipelineLayout& pipeline_layout,
+ VkPipeline* pipeline) {
+ std::vector<VkPipelineShaderStageCreateInfo> shader_stage_info =
+ GetVkShaderStageInfo();
+
+ for (auto& info : shader_stage_info)
+ info.pName = GetEntryPointName(info.stage);
+
+ const uint32_t maxRecursionDepth =
+ 1u; // make it a parameter in the AmberScript
+
+ VkRayTracingPipelineCreateInfoKHR pipelineCreateInfo{
+ VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
+ nullptr,
+ static_cast<VkPipelineCreateFlags>(0),
+ static_cast<uint32_t>(shader_stage_info.size()),
+ shader_stage_info.data(),
+ static_cast<uint32_t>(shader_group_create_info_.size()),
+ shader_group_create_info_.data(),
+ maxRecursionDepth,
+ nullptr,
+ nullptr,
+ nullptr,
+ pipeline_layout,
+ VK_NULL_HANDLE,
+ 0,
+ };
+
+ VkResult r = device_->GetPtrs()->vkCreateRayTracingPipelinesKHR(
+ device_->GetVkDevice(), VK_NULL_HANDLE, VK_NULL_HANDLE, 1u,
+ &pipelineCreateInfo, nullptr, pipeline);
+ if (r != VK_SUCCESS)
+ return Result("Vulkan::Calling vkCreateRayTracingPipelinesKHR Fail");
+
+ return {};
+}
+
+Result RayTracingPipeline::getVulkanSBTRegion(
+ VkPipeline pipeline,
+ amber::SBT* aSBT,
+ VkStridedDeviceAddressRegionKHR* region) {
+ const uint32_t handle_size = device_->GetRayTracingShaderGroupHandleSize();
+ if (aSBT != nullptr) {
+ SBT* vSBT = nullptr;
+ auto x = sbtses_.find(aSBT);
+
+ if (x == sbtses_.end()) {
+ auto p = MakeUnique<amber::vulkan::SBT>(device_);
+ sbts_.push_back(std::move(p));
+ auto sbt_vulkan = sbtses_.emplace(aSBT, sbts_.back().get());
+
+ vSBT = sbt_vulkan.first->second;
+
+ Result r = vSBT->Create(aSBT, pipeline);
+ if (!r.IsSuccess())
+ return r;
+ } else {
+ vSBT = x->second;
+ }
+
+ *region = makeStridedDeviceAddressRegionKHR(
+ getBufferDeviceAddress(device_, vSBT->getBuffer()->GetVkBuffer()),
+ handle_size, handle_size * aSBT->GetSBTSize());
+ } else {
+ *region = makeStridedDeviceAddressRegionKHR(0, 0, 0);
+ }
+
+ return {};
+}
+
+Result RayTracingPipeline::TraceRays(amber::SBT* rSBT,
+ amber::SBT* mSBT,
+ amber::SBT* hSBT,
+ amber::SBT* cSBT,
+ uint32_t x,
+ uint32_t y,
+ uint32_t z) {
+ Result r = SendDescriptorDataToDeviceIfNeeded();
+ if (!r.IsSuccess())
+ return r;
+
+ VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
+ r = CreateVkPipelineLayout(&pipeline_layout);
+ if (!r.IsSuccess())
+ return r;
+
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ r = CreateVkRayTracingPipeline(pipeline_layout, &pipeline);
+ if (!r.IsSuccess())
+ return r;
+
+ // Note that a command updating a descriptor set and a command using
+ // it must be submitted separately, because using a descriptor set
+ // while updating it is not safe.
+ UpdateDescriptorSetsIfNeeded();
+
+ {
+ CommandBufferGuard guard(GetCommandBuffer());
+ if (!guard.IsRecording())
+ return guard.GetResult();
+
+ for (auto& i : *blases_) {
+ i.second->BuildBLAS(GetCommandBuffer());
+ }
+ for (auto& i : *tlases_) {
+ i.second->BuildTLAS(GetCommandBuffer()->GetVkCommandBuffer());
+ }
+
+ BindVkDescriptorSets(pipeline_layout);
+
+ r = RecordPushConstant(pipeline_layout);
+ if (!r.IsSuccess())
+ return r;
+
+ device_->GetPtrs()->vkCmdBindPipeline(
+ command_->GetVkCommandBuffer(), VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR,
+ pipeline);
+
+ VkStridedDeviceAddressRegionKHR rSBTRegion = {};
+ VkStridedDeviceAddressRegionKHR mSBTRegion = {};
+ VkStridedDeviceAddressRegionKHR hSBTRegion = {};
+ VkStridedDeviceAddressRegionKHR cSBTRegion = {};
+
+ r = getVulkanSBTRegion(pipeline, rSBT, &rSBTRegion);
+ if (!r.IsSuccess())
+ return r;
+
+ r = getVulkanSBTRegion(pipeline, mSBT, &mSBTRegion);
+ if (!r.IsSuccess())
+ return r;
+
+ r = getVulkanSBTRegion(pipeline, hSBT, &hSBTRegion);
+ if (!r.IsSuccess())
+ return r;
+
+ r = getVulkanSBTRegion(pipeline, cSBT, &cSBTRegion);
+ if (!r.IsSuccess())
+ return r;
+
+ device_->GetPtrs()->vkCmdTraceRaysKHR(command_->GetVkCommandBuffer(),
+ &rSBTRegion, &mSBTRegion, &hSBTRegion,
+ &cSBTRegion, x, y, z);
+
+ r = guard.Submit(GetFenceTimeout(), GetPipelineRuntimeLayerEnabled());
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ r = ReadbackDescriptorsToHostDataQueue();
+ if (!r.IsSuccess())
+ return r;
+
+ device_->GetPtrs()->vkDestroyPipeline(device_->GetVkDevice(), pipeline,
+ nullptr);
+ device_->GetPtrs()->vkDestroyPipelineLayout(device_->GetVkDevice(),
+ pipeline_layout, nullptr);
+
+ return {};
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/raytracing_pipeline.h b/src/vulkan/raytracing_pipeline.h
new file mode 100644
index 0000000..66f4a4c
--- /dev/null
+++ b/src/vulkan/raytracing_pipeline.h
@@ -0,0 +1,76 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_VULKAN_RAYTRACING_PIPELINE_H_
+#define SRC_VULKAN_RAYTRACING_PIPELINE_H_
+
+#include <memory>
+#include <vector>
+
+#include "amber/result.h"
+#include "amber/vulkan_header.h"
+#include "src/vulkan/pipeline.h"
+
+namespace amber {
+namespace vulkan {
+
+/// Pipepline to handle compute commands.
+class RayTracingPipeline : public Pipeline {
+ public:
+ RayTracingPipeline(
+ Device* device,
+ BlasesMap* blases,
+ TlasesMap* tlases,
+ uint32_t fence_timeout_ms,
+ bool pipeline_runtime_layer_enabled,
+ const std::vector<VkPipelineShaderStageCreateInfo>& shader_stage_info);
+ ~RayTracingPipeline() override;
+
+ Result AddTLASDescriptor(const TLASCommand* cmd);
+
+ Result Initialize(CommandPool* pool,
+ std::vector<VkRayTracingShaderGroupCreateInfoKHR>&
+ shader_group_create_info);
+
+ Result getVulkanSBTRegion(VkPipeline pipeline,
+ amber::SBT* aSBT,
+ VkStridedDeviceAddressRegionKHR* region);
+
+ Result TraceRays(amber::SBT* rSBT,
+ amber::SBT* mSBT,
+ amber::SBT* hSBT,
+ amber::SBT* cSBT,
+ uint32_t x,
+ uint32_t y,
+ uint32_t z);
+
+ BlasesMap* GetBlases() override { return blases_; }
+ TlasesMap* GetTlases() override { return tlases_; }
+
+ private:
+ Result CreateVkRayTracingPipeline(const VkPipelineLayout& pipeline_layout,
+ VkPipeline* pipeline);
+
+ std::vector<VkRayTracingShaderGroupCreateInfoKHR> shader_group_create_info_;
+ BlasesMap* blases_;
+ TlasesMap* tlases_;
+ SbtsMap sbtses_;
+ std::vector<std::unique_ptr<amber::vulkan::SBT>> sbts_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_RAYTRACING_PIPELINE_H_
diff --git a/src/vulkan/resource.cc b/src/vulkan/resource.cc
index a52df3d..15537a1 100644
--- a/src/vulkan/resource.cc
+++ b/src/vulkan/resource.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -141,9 +142,21 @@ Result Resource::AllocateMemory(VkDeviceMemory* memory,
VkDeviceSize size,
uint32_t memory_type_index) {
VkMemoryAllocateInfo alloc_info = VkMemoryAllocateInfo();
+ VkMemoryAllocateFlagsInfo allocFlagsInfo = VkMemoryAllocateFlagsInfo();
+
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = size;
alloc_info.memoryTypeIndex = memory_type_index;
+
+ if (memory_allocate_flags_ != 0) {
+ allocFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
+ allocFlagsInfo.pNext = nullptr;
+ allocFlagsInfo.flags = memory_allocate_flags_;
+ allocFlagsInfo.deviceMask = 0u;
+
+ alloc_info.pNext = &allocFlagsInfo;
+ }
+
if (device_->GetPtrs()->vkAllocateMemory(device_->GetVkDevice(), &alloc_info,
nullptr, memory) != VK_SUCCESS) {
return Result("Vulkan::Calling vkAllocateMemory Fail");
diff --git a/src/vulkan/resource.h b/src/vulkan/resource.h
index d3cc0de..8ae4fec 100644
--- a/src/vulkan/resource.h
+++ b/src/vulkan/resource.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,6 +16,7 @@
#ifndef SRC_VULKAN_RESOURCE_H_
#define SRC_VULKAN_RESOURCE_H_
+#include <map>
#include <memory>
#include <vector>
@@ -23,12 +25,24 @@
#include "amber/vulkan_header.h"
namespace amber {
+
+class BLAS;
+class TLAS;
+class SBT;
+
namespace vulkan {
class CommandBuffer;
class Device;
class TransferBuffer;
class TransferImage;
+class BLAS;
+class TLAS;
+class SBT;
+
+typedef std::map<amber::BLAS*, std::unique_ptr<amber::vulkan::BLAS>> BlasesMap;
+typedef std::map<amber::TLAS*, std::unique_ptr<amber::vulkan::TLAS>> TlasesMap;
+typedef std::map<amber::SBT*, amber::vulkan::SBT*> SbtsMap;
// Class for Vulkan resources. Its children are Vulkan Buffer and Vulkan Image.
class Resource {
@@ -52,6 +66,15 @@ class Resource {
virtual Result Initialize() = 0;
virtual TransferBuffer* AsTransferBuffer() { return nullptr; }
virtual TransferImage* AsTransferImage() { return nullptr; }
+ virtual void AddAllocateFlags(VkMemoryAllocateFlags memory_allocate_flags) {
+ memory_allocate_flags_ |= memory_allocate_flags;
+ }
+ VkMemoryPropertyFlags GetMemoryPropertiesFlags() {
+ return memory_properties_flags_;
+ }
+ void SetMemoryPropertiesFlags(VkMemoryPropertyFlags flags) {
+ memory_properties_flags_ = flags;
+ }
protected:
Resource(Device* device, uint32_t size);
@@ -90,6 +113,10 @@ class Resource {
uint32_t size_in_bytes_ = 0;
void* memory_ptr_ = nullptr;
bool is_read_only_ = false;
+ VkMemoryAllocateFlags memory_allocate_flags_ = 0u;
+ VkMemoryPropertyFlags memory_properties_flags_ =
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
};
} // namespace vulkan
diff --git a/src/vulkan/sbt.cc b/src/vulkan/sbt.cc
new file mode 100644
index 0000000..caead26
--- /dev/null
+++ b/src/vulkan/sbt.cc
@@ -0,0 +1,73 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstring>
+
+#include "src/vulkan/sbt.h"
+#include "src/vulkan/pipeline.h"
+
+namespace amber {
+namespace vulkan {
+
+SBT::SBT(Device* device) : device_(device) {}
+
+Result SBT::Create(amber::SBT* sbt, VkPipeline pipeline) {
+ uint32_t handles_count = 0;
+ for (auto& x : sbt->GetSBTRecords())
+ handles_count += x->GetCount();
+
+ if (handles_count == 0)
+ return Result("SBT must contain at least one record");
+
+ const uint32_t handle_size = device_->GetRayTracingShaderGroupHandleSize();
+ const uint32_t buffer_size = handle_size * handles_count;
+ std::vector<uint8_t> handles(buffer_size);
+
+ buffer_ = MakeUnique<TransferBuffer>(device_, buffer_size, nullptr);
+ buffer_->AddUsageFlags(VK_BUFFER_USAGE_TRANSFER_DST_BIT |
+ VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ Result r = buffer_->Initialize();
+ if (!r.IsSuccess())
+ return r;
+
+ size_t start = 0;
+ for (auto& x : sbt->GetSBTRecords()) {
+ const uint32_t index = x->GetUsedShaderGroupPipelineIndex();
+ const uint32_t count = x->GetCount();
+ if (index != static_cast<uint32_t>(-1)) {
+ VkResult vr = device_->GetPtrs()->vkGetRayTracingShaderGroupHandlesKHR(
+ device_->GetVkDevice(), pipeline, index, count, count * handle_size,
+ &handles[start * handle_size]);
+
+ if (vr != VK_SUCCESS)
+ return Result("vkGetRayTracingShaderGroupHandlesKHR has failed");
+ }
+
+ start += x->GetCount();
+ }
+
+ memcpy(buffer_->HostAccessibleMemoryPtr(), handles.data(), handles.size());
+
+ // Skip flush as memory allocated for buffer is coherent
+
+ return r;
+}
+
+SBT::~SBT() = default;
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/sbt.h b/src/vulkan/sbt.h
new file mode 100644
index 0000000..7ff1242
--- /dev/null
+++ b/src/vulkan/sbt.h
@@ -0,0 +1,45 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_VULKAN_SBT_H_
+#define SRC_VULKAN_SBT_H_
+
+#include <memory>
+#include <vector>
+
+#include "src/acceleration_structure.h"
+#include "src/vulkan/device.h"
+#include "src/vulkan/transfer_buffer.h"
+
+namespace amber {
+namespace vulkan {
+
+class SBT {
+ public:
+ explicit SBT(Device* device);
+ ~SBT();
+
+ Result Create(amber::SBT* sbt, VkPipeline pipeline);
+ TransferBuffer* getBuffer() { return buffer_.get(); }
+
+ private:
+ Device* device_ = nullptr;
+ std::unique_ptr<TransferBuffer> buffer_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_SBT_H_
diff --git a/src/vulkan/tlas.cc b/src/vulkan/tlas.cc
new file mode 100644
index 0000000..c0fe422
--- /dev/null
+++ b/src/vulkan/tlas.cc
@@ -0,0 +1,245 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/vulkan/tlas.h"
+#include "src/vulkan/blas.h"
+
+namespace amber {
+namespace vulkan {
+
+static VkTransformMatrixKHR makeVkMatrix(const float* m) {
+ const VkTransformMatrixKHR identityMatrix3x4 = {{{1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 1.0f, 0.0f}}};
+ VkTransformMatrixKHR v;
+
+ if (m == nullptr)
+ return identityMatrix3x4;
+
+ for (size_t i = 0; i < 12; i++) {
+ const size_t r = i / 4;
+ const size_t c = i % 4;
+ v.matrix[r][c] = m[i];
+ }
+
+ return v;
+}
+
+TLAS::TLAS(Device* device) : device_(device) {}
+
+Result TLAS::CreateTLAS(amber::TLAS* tlas,
+ BlasesMap* blases) {
+ if (tlas_ != VK_NULL_HANDLE)
+ return {};
+
+ assert(tlas != nullptr);
+
+ VkDeviceOrHostAddressConstKHR const_default_ptr;
+ VkDeviceOrHostAddressKHR default_ptr;
+
+ const_default_ptr.hostAddress = nullptr;
+ default_ptr.hostAddress = nullptr;
+
+ instances_count_ = static_cast<uint32_t>(tlas->GetInstances().size());
+
+ const uint32_t ib_size =
+ uint32_t(instances_count_ * sizeof(VkAccelerationStructureInstanceKHR));
+
+ instance_buffer_ = MakeUnique<TransferBuffer>(device_, ib_size, nullptr);
+ instance_buffer_->AddUsageFlags(
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ instance_buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ instance_buffer_->Initialize();
+
+ VkAccelerationStructureInstanceKHR* instances_ptr =
+ reinterpret_cast<VkAccelerationStructureInstanceKHR*>
+ (instance_buffer_->HostAccessibleMemoryPtr());
+
+ for (auto& instance : tlas->GetInstances()) {
+ auto blas = instance->GetUsedBLAS();
+
+ assert(blas != nullptr);
+
+ auto blas_vulkan_it = blases->find(blas);
+ amber::vulkan::BLAS* blas_vulkan_ptr = nullptr;
+
+ if (blas_vulkan_it == blases->end()) {
+ auto blas_vulkan =
+ blases->emplace(blas, new amber::vulkan::BLAS(device_));
+ blas_vulkan_ptr = blas_vulkan.first->second.get();
+
+ Result r = blas_vulkan_ptr->CreateBLAS(blas);
+
+ if (!r.IsSuccess())
+ return r;
+ } else {
+ blas_vulkan_ptr = blas_vulkan_it->second.get();
+ }
+
+ VkDeviceAddress accelerationStructureAddress =
+ blas_vulkan_ptr->getVkBLASDeviceAddress();
+
+ *instances_ptr = VkAccelerationStructureInstanceKHR{
+ makeVkMatrix(instance->GetTransform()),
+ instance->GetInstanceIndex(),
+ instance->GetMask(),
+ instance->GetOffset(),
+ instance->GetFlags(),
+ static_cast<uint64_t>(accelerationStructureAddress)};
+
+ instances_ptr++;
+ }
+
+ VkAccelerationStructureGeometryInstancesDataKHR
+ accelerationStructureGeometryInstancesDataKHR = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR,
+ nullptr,
+ VK_FALSE,
+ const_default_ptr,
+ };
+ VkAccelerationStructureGeometryDataKHR geometry = {};
+ geometry.instances = accelerationStructureGeometryInstancesDataKHR;
+
+ accelerationStructureGeometryKHR_ = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR,
+ nullptr,
+ VK_GEOMETRY_TYPE_INSTANCES_KHR,
+ geometry,
+ 0,
+ };
+
+ accelerationStructureBuildGeometryInfoKHR_ = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR,
+ nullptr,
+ VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR,
+ 0,
+ VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR,
+ VK_NULL_HANDLE,
+ VK_NULL_HANDLE,
+ 1,
+ &accelerationStructureGeometryKHR_,
+ nullptr,
+ default_ptr,
+ };
+
+ VkAccelerationStructureBuildSizesInfoKHR sizeInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ };
+
+ device_->GetPtrs()->vkGetAccelerationStructureBuildSizesKHR(
+ device_->GetVkDevice(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
+ &accelerationStructureBuildGeometryInfoKHR_, &instances_count_,
+ &sizeInfo);
+
+ const uint32_t as_size =
+ static_cast<uint32_t>(sizeInfo.accelerationStructureSize);
+
+ buffer_ = MakeUnique<TransferBuffer>(device_, as_size, nullptr);
+ buffer_->AddUsageFlags(
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ buffer_->Initialize();
+
+ const VkAccelerationStructureCreateInfoKHR
+ accelerationStructureCreateInfoKHR = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR,
+ nullptr,
+ 0,
+ buffer_->GetVkBuffer(),
+ 0,
+ as_size,
+ VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR,
+ 0,
+ };
+
+ if (device_->GetPtrs()->vkCreateAccelerationStructureKHR(
+ device_->GetVkDevice(), &accelerationStructureCreateInfoKHR, nullptr,
+ &tlas_) != VK_SUCCESS) {
+ return Result(
+ "Vulkan::Calling vkCreateAccelerationStructureKHR "
+ "failed");
+ }
+
+ accelerationStructureBuildGeometryInfoKHR_.dstAccelerationStructure = tlas_;
+
+ if (sizeInfo.buildScratchSize > 0) {
+ scratch_buffer_ = MakeUnique<TransferBuffer>(
+ device_, static_cast<uint32_t>(sizeInfo.buildScratchSize), nullptr);
+ scratch_buffer_->AddUsageFlags(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+ scratch_buffer_->AddAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
+ scratch_buffer_->Initialize();
+
+ accelerationStructureBuildGeometryInfoKHR_.scratchData.deviceAddress =
+ scratch_buffer_->getBufferDeviceAddress();
+ }
+
+ accelerationStructureGeometryKHR_.geometry.instances.data.deviceAddress =
+ instance_buffer_->getBufferDeviceAddress();
+
+ return {};
+}
+
+Result TLAS::BuildTLAS(VkCommandBuffer cmdBuffer) {
+ if (tlas_ == VK_NULL_HANDLE)
+ return Result("Acceleration structure should be created first");
+ if (built_)
+ return {};
+
+ VkAccelerationStructureBuildRangeInfoKHR
+ accelerationStructureBuildRangeInfoKHR = {instances_count_, 0, 0, 0};
+ VkAccelerationStructureBuildRangeInfoKHR*
+ accelerationStructureBuildRangeInfoKHRPtr =
+ &accelerationStructureBuildRangeInfoKHR;
+
+ device_->GetPtrs()->vkCmdBuildAccelerationStructuresKHR(
+ cmdBuffer, 1, &accelerationStructureBuildGeometryInfoKHR_,
+ &accelerationStructureBuildRangeInfoKHRPtr);
+
+ const VkAccessFlags accessMasks =
+ VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR |
+ VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
+ const VkMemoryBarrier memBarrier{
+ VK_STRUCTURE_TYPE_MEMORY_BARRIER,
+ nullptr,
+ accessMasks,
+ accessMasks,
+ };
+
+ device_->GetPtrs()->vkCmdPipelineBarrier(
+ cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memBarrier, 0, nullptr, 0,
+ nullptr);
+
+ built_ = true;
+
+ return {};
+}
+
+TLAS::~TLAS() {
+ if (tlas_ != VK_NULL_HANDLE) {
+ device_->GetPtrs()->vkDestroyAccelerationStructureKHR(
+ device_->GetVkDevice(), tlas_, nullptr);
+ }
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/tlas.h b/src/vulkan/tlas.h
new file mode 100644
index 0000000..9046d65
--- /dev/null
+++ b/src/vulkan/tlas.h
@@ -0,0 +1,53 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_VULKAN_TLAS_H_
+#define SRC_VULKAN_TLAS_H_
+
+#include <memory>
+
+#include "src/acceleration_structure.h"
+#include "src/vulkan/device.h"
+#include "src/vulkan/transfer_buffer.h"
+
+namespace amber {
+namespace vulkan {
+
+class TLAS {
+ public:
+ explicit TLAS(Device* device);
+ ~TLAS();
+
+ Result CreateTLAS(amber::TLAS* tlas, BlasesMap* blases);
+ Result BuildTLAS(VkCommandBuffer cmdBuffer);
+ VkAccelerationStructureKHR GetVkTLAS() { return tlas_; }
+
+ private:
+ Device* device_ = nullptr;
+ VkAccelerationStructureKHR tlas_ = VK_NULL_HANDLE;
+ bool built_ = false;
+ std::unique_ptr<TransferBuffer> buffer_;
+ std::unique_ptr<TransferBuffer> scratch_buffer_;
+ std::unique_ptr<TransferBuffer> instance_buffer_;
+ uint32_t instances_count_ = 0;
+ VkAccelerationStructureGeometryKHR accelerationStructureGeometryKHR_;
+ VkAccelerationStructureBuildGeometryInfoKHR
+ accelerationStructureBuildGeometryInfoKHR_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_TLAS_H_
diff --git a/src/vulkan/tlas_descriptor.cc b/src/vulkan/tlas_descriptor.cc
new file mode 100644
index 0000000..a1c7adf
--- /dev/null
+++ b/src/vulkan/tlas_descriptor.cc
@@ -0,0 +1,86 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/vulkan/tlas_descriptor.h"
+
+#include "src/vulkan/device.h"
+#include "src/vulkan/resource.h"
+
+namespace amber {
+namespace vulkan {
+
+TLASDescriptor::TLASDescriptor(amber::TLAS* tlas,
+ DescriptorType type,
+ Device* device,
+ BlasesMap* blases,
+ TlasesMap* tlases,
+ uint32_t desc_set,
+ uint32_t binding)
+ : Descriptor(type, device, desc_set, binding),
+ blases_(blases),
+ tlases_(tlases) {
+ assert(blases != nullptr);
+ assert(tlases != nullptr);
+ AddAmberTLAS(tlas);
+}
+
+TLASDescriptor::~TLASDescriptor() = default;
+
+Result TLASDescriptor::CreateResourceIfNeeded() {
+ for (amber::TLAS* amber_tlas : amber_tlases_) {
+ if (tlases_->find(amber_tlas) == tlases_->end()) {
+ auto& vulkan_tlas = ((*tlases_)[amber_tlas] = MakeUnique<TLAS>(device_));
+ Result r = vulkan_tlas->CreateTLAS(amber_tlas, blases_);
+ if (!r.IsSuccess())
+ return r;
+ }
+ }
+
+ return {};
+}
+
+void TLASDescriptor::UpdateDescriptorSetIfNeeded(
+ VkDescriptorSet descriptor_set) {
+ std::vector<VkAccelerationStructureKHR> as;
+
+ for (auto& amber_tlas : amber_tlases_) {
+ auto vulkan_tlas = tlases_->find(amber_tlas);
+ assert(vulkan_tlas != tlases_->end());
+ as.push_back(vulkan_tlas->second->GetVkTLAS());
+ }
+
+ VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorTlas;
+ writeDescriptorTlas.sType =
+ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
+ writeDescriptorTlas.pNext = nullptr;
+ writeDescriptorTlas.accelerationStructureCount =
+ static_cast<uint32_t>(as.size());
+ writeDescriptorTlas.pAccelerationStructures = as.data();
+
+ VkWriteDescriptorSet write = VkWriteDescriptorSet();
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.pNext = &writeDescriptorTlas;
+ write.dstSet = descriptor_set;
+ write.dstBinding = binding_;
+ write.dstArrayElement = 0;
+ write.descriptorCount = static_cast<uint32_t>(as.size());
+ write.descriptorType = GetVkDescriptorType();
+
+ device_->GetPtrs()->vkUpdateDescriptorSets(device_->GetVkDevice(), 1, &write,
+ 0, nullptr);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/tlas_descriptor.h b/src/vulkan/tlas_descriptor.h
new file mode 100644
index 0000000..d043ce2
--- /dev/null
+++ b/src/vulkan/tlas_descriptor.h
@@ -0,0 +1,59 @@
+// Copyright 2024 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_VULKAN_TLAS_DESCRIPTOR_H_
+#define SRC_VULKAN_TLAS_DESCRIPTOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "src/vulkan/descriptor.h"
+#include "src/vulkan/tlas.h"
+#include "src/vulkan/transfer_image.h"
+
+namespace amber {
+namespace vulkan {
+
+class TLASDescriptor : public Descriptor {
+ public:
+ TLASDescriptor(amber::TLAS* tlas,
+ DescriptorType type,
+ Device* device,
+ BlasesMap* blases,
+ TlasesMap* tlases,
+ uint32_t desc_set,
+ uint32_t binding);
+ ~TLASDescriptor() override;
+
+ void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) override;
+
+ Result CreateResourceIfNeeded() override;
+
+ void AddAmberTLAS(amber::TLAS* tlas) { amber_tlases_.push_back(tlas); }
+ uint32_t GetDescriptorCount() override {
+ return static_cast<uint32_t>(amber_tlases_.size());
+ }
+ TLASDescriptor* AsTLASDescriptor() override { return this; }
+
+ private:
+ std::vector<amber::TLAS*> amber_tlases_;
+ BlasesMap* blases_;
+ TlasesMap* tlases_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_TLAS_DESCRIPTOR_H_
diff --git a/src/vulkan/transfer_buffer.cc b/src/vulkan/transfer_buffer.cc
index 512fb7b..174c42b 100644
--- a/src/vulkan/transfer_buffer.cc
+++ b/src/vulkan/transfer_buffer.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -56,10 +57,8 @@ Result TransferBuffer::Initialize() {
return r;
uint32_t memory_type_index = 0;
- r = AllocateAndBindMemoryToVkBuffer(buffer_, &memory_,
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
- VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
- true, &memory_type_index);
+ r = AllocateAndBindMemoryToVkBuffer(
+ buffer_, &memory_, GetMemoryPropertiesFlags(), true, &memory_type_index);
if (!r.IsSuccess())
return r;
@@ -90,6 +89,17 @@ Result TransferBuffer::Initialize() {
return MapMemory(memory_);
}
+VkDeviceAddress TransferBuffer::getBufferDeviceAddress() {
+ const VkBufferDeviceAddressInfo bufferDeviceAddressInfo = {
+ VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR,
+ nullptr,
+ GetVkBuffer(),
+ };
+
+ return device_->GetPtrs()->vkGetBufferDeviceAddress(device_->GetVkDevice(),
+ &bufferDeviceAddressInfo);
+}
+
void TransferBuffer::CopyToDevice(CommandBuffer* command_buffer) {
// This is redundant because this buffer is always host visible
// and coherent and vkQueueSubmit will make writes from host
diff --git a/src/vulkan/transfer_buffer.h b/src/vulkan/transfer_buffer.h
index 7d96bec..8734363 100644
--- a/src/vulkan/transfer_buffer.h
+++ b/src/vulkan/transfer_buffer.h
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -48,6 +49,7 @@ class TransferBuffer : public Resource {
const VkBufferView* GetVkBufferView() const { return &view_; }
VkBuffer GetVkBuffer() const { return buffer_; }
+ VkDeviceAddress getBufferDeviceAddress();
/// Records a command on |command_buffer| to copy the buffer contents from the
/// host to the device.
diff --git a/src/vulkan/vk-funcs-1-0.inc b/src/vulkan/vk-funcs-1-0.inc
index b5a7ac0..50a821b 100644
--- a/src/vulkan/vk-funcs-1-0.inc
+++ b/src/vulkan/vk-funcs-1-0.inc
@@ -21,6 +21,7 @@ AMBER_VK_FUNC(vkCmdPipelineBarrier)
AMBER_VK_FUNC(vkCmdPushConstants)
AMBER_VK_FUNC(vkCreateBuffer)
AMBER_VK_FUNC(vkCreateBufferView)
+AMBER_VK_FUNC(vkGetBufferDeviceAddress)
AMBER_VK_FUNC(vkCreateCommandPool)
AMBER_VK_FUNC(vkCreateComputePipelines)
AMBER_VK_FUNC(vkCreateDescriptorPool)
diff --git a/src/vulkan/vk-funcs-1-1.inc b/src/vulkan/vk-funcs-1-1.inc
index 7fca3c5..db3b2c5 100644
--- a/src/vulkan/vk-funcs-1-1.inc
+++ b/src/vulkan/vk-funcs-1-1.inc
@@ -1 +1,10 @@
AMBER_VK_FUNC(vkGetPhysicalDeviceProperties2)
+AMBER_VK_FUNC(vkCreateRayTracingPipelinesKHR)
+AMBER_VK_FUNC(vkCreateAccelerationStructureKHR)
+AMBER_VK_FUNC(vkDestroyAccelerationStructureKHR)
+AMBER_VK_FUNC(vkGetAccelerationStructureBuildSizesKHR)
+AMBER_VK_FUNC(vkBuildAccelerationStructuresKHR)
+AMBER_VK_FUNC(vkCmdBuildAccelerationStructuresKHR)
+AMBER_VK_FUNC(vkGetAccelerationStructureDeviceAddressKHR)
+AMBER_VK_FUNC(vkCmdTraceRaysKHR)
+AMBER_VK_FUNC(vkGetRayTracingShaderGroupHandlesKHR) \ No newline at end of file