| /* |
| * Copyright (c) 2015-2026 The Khronos Group Inc. |
| * Copyright (c) 2015-2026 Valve Corporation |
| * Copyright (c) 2015-2026 LunarG, Inc. |
| * Copyright (c) 2015-2025 Google, Inc. |
| * Modifications Copyright (C) 2020 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 |
| */ |
| |
| #include "../framework/layer_validation_tests.h" |
| #include "../framework/descriptor_helper.h" |
| |
| class PositiveRayTracingMicromap : public RayTracingTest {}; |
| |
| TEST_F(PositiveRayTracingMicromap, BasicEXT) { |
| TEST_DESCRIPTION("Test building an opacity micromap then building an acceleration structure with that"); |
| |
| // Mask data for 2 levels of subdivision. Middle triangle is index 1, so drop that one out. |
| // Bit string for middle missing is '1011' (0 on the left). In number form, that's 0xd. |
| // Extending the Sierpinski-esque pattern out one level is 0xdd0d |
| uint32_t test_mask = 0xdd0d; |
| |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); |
| AddRequiredExtensions(VK_EXT_OPACITY_MICROMAP_EXTENSION_NAME); |
| |
| AddRequiredFeature(vkt::Feature::bufferDeviceAddress); |
| AddRequiredFeature(vkt::Feature::accelerationStructure); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredFeature(vkt::Feature::rayTracingPipeline); |
| AddRequiredFeature(vkt::Feature::micromap); |
| AddRequiredFeature(vkt::Feature::micromapHostCommands); |
| |
| RETURN_IF_SKIP(InitFrameworkForRayTracingTest()); |
| RETURN_IF_SKIP(InitState()); |
| |
| if (IsPlatformMockICD()) { |
| GTEST_SKIP() << "Test not supported by MockICD"; |
| } |
| |
| VkMemoryAllocateFlagsInfo allocate_da_flag_info = vku::InitStructHelper(); |
| allocate_da_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; |
| |
| // Create a buffer with the mask and index data |
| vkt::Buffer micromap_data_buffer( |
| *m_device, 2 * 1048576 /*XXX*/, |
| VK_BUFFER_USAGE_MICROMAP_BUILD_INPUT_READ_ONLY_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, vkt::device_address); |
| |
| VkDeviceAddress micromap_address = micromap_data_buffer.Address(); |
| |
| // Fill out VkMicromapUsageEXT with size information |
| VkMicromapUsageEXT mm_usage = {}; |
| mm_usage.count = 1; |
| |
| const int triangle_offset = 0; |
| const int index_offset = 256; |
| const int data_offset = 512; |
| |
| mm_usage.subdivisionLevel = 2; |
| mm_usage.format = VK_OPACITY_MICROMAP_FORMAT_2_STATE_EXT; |
| |
| { |
| uint32_t* data = (uint32_t*)micromap_data_buffer.Memory().Map(); |
| |
| VkMicromapTriangleEXT* tri = (VkMicromapTriangleEXT*)&data[triangle_offset / 4]; |
| tri->dataOffset = 0; |
| tri->subdivisionLevel = uint16_t(mm_usage.subdivisionLevel); |
| tri->format = uint16_t(mm_usage.format); |
| |
| // Micromap data |
| // Just replicate for testing higher subdivision |
| { |
| uint32_t mask_word = test_mask | (test_mask << 16); |
| int words = ((1 << (2 * mm_usage.subdivisionLevel)) + 31) / 32; |
| for (int i = 0; i < words; i++) { |
| data[data_offset / 4 + i] = mask_word; |
| } |
| } |
| |
| // Index information |
| data[index_offset / 4] = 0; |
| } |
| |
| VkMicromapBuildInfoEXT mm_build_info = vku::InitStructHelper(); |
| |
| mm_build_info.type = VK_MICROMAP_TYPE_OPACITY_MICROMAP_EXT; |
| mm_build_info.flags = 0; |
| mm_build_info.mode = VK_BUILD_MICROMAP_MODE_BUILD_EXT; |
| mm_build_info.dstMicromap = VK_NULL_HANDLE; |
| mm_build_info.usageCountsCount = 1; |
| mm_build_info.pUsageCounts = &mm_usage; |
| mm_build_info.data.deviceAddress = 0ull; |
| mm_build_info.triangleArray.deviceAddress = 0ull; |
| mm_build_info.triangleArrayStride = 0; |
| |
| VkMicromapBuildSizesInfoEXT size_info = vku::InitStructHelper(); |
| vk::GetMicromapBuildSizesEXT(device(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &mm_build_info, &size_info); |
| |
| // Create a buffer and micromap on top from the size |
| vkt::Buffer micromap_buffer(*m_device, size_info.micromapSize, VK_BUFFER_USAGE_MICROMAP_STORAGE_BIT_EXT); |
| // Scratch buffer |
| vkt::Buffer ms_buffer(*m_device, size_info.buildScratchSize > 4 ? size_info.buildScratchSize : 4, |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &allocate_da_flag_info); |
| |
| VkMicromapEXT micromap; |
| VkMicromapCreateInfoEXT ma_ci = vku::InitStructHelper(); |
| |
| ma_ci.createFlags = 0; |
| ma_ci.buffer = micromap_buffer; |
| ma_ci.offset = 0; |
| ma_ci.size = size_info.micromapSize; |
| ma_ci.type = VK_MICROMAP_TYPE_OPACITY_MICROMAP_EXT; |
| ma_ci.deviceAddress = 0ull; |
| |
| VkResult result = vk::CreateMicromapEXT(device(), &ma_ci, nullptr, µmap); |
| ASSERT_EQ(VK_SUCCESS, result); |
| |
| // Build the array with vkBuildmicromapsEXT |
| { |
| // Fill in the pointers we didn't have at size query |
| mm_build_info.dstMicromap = micromap; |
| mm_build_info.data.deviceAddress = micromap_address + data_offset; |
| mm_build_info.triangleArray.deviceAddress = micromap_address + triangle_offset; |
| mm_build_info.scratchData.deviceAddress = ms_buffer.Address(); |
| |
| m_command_buffer.Begin(); |
| |
| vk::CmdBuildMicromapsEXT(m_command_buffer, 1, &mm_build_info); |
| |
| { |
| VkMemoryBarrier2 memory_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, |
| NULL, |
| VK_PIPELINE_STAGE_2_MICROMAP_BUILD_BIT_EXT, |
| VK_ACCESS_2_MICROMAP_WRITE_BIT_EXT, |
| VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, |
| VK_ACCESS_2_MICROMAP_READ_BIT_EXT}; |
| m_command_buffer.BarrierKHR(memory_barrier); |
| } |
| m_command_buffer.End(); |
| |
| m_default_queue->Submit(m_command_buffer); |
| m_device->Wait(); |
| } |
| |
| // Create a buffer with the triangle data in it |
| static float const vertex_data[6 * 2] = { |
| 0.25, 0.75, 0.5, 0.25, 0.75, 0.75, |
| }; |
| static uint32_t const index_data[6] = {0, 1, 2}; |
| |
| vkt::Buffer vertex_buffer( |
| *m_device, sizeof(vertex_data) + sizeof(index_data), |
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
| vkt::device_address); |
| |
| VkDeviceAddress vertex_address = vertex_buffer.Address(); |
| |
| // Upload data to the vertex buffer. |
| { |
| char* ptr; |
| |
| vk::MapMemory(device(), vertex_buffer.Memory(), 0, VK_WHOLE_SIZE, 0, (void**)&ptr); |
| |
| memcpy(ptr, &vertex_data[0], sizeof(vertex_data)); |
| memcpy(ptr + sizeof(vertex_data), &index_data[0], sizeof(index_data)); |
| |
| vk::UnmapMemory(device(), vertex_buffer.Memory()); |
| } |
| |
| VkAccelerationStructureBuildSizesInfoKHR bottom_as_build_size_info = vku::InitStructHelper(); |
| VkAccelerationStructureBuildSizesInfoKHR top_as_build_size_info = vku::InitStructHelper(); |
| |
| // Create a bottom-level acceleration structure with one triangle |
| VkAccelerationStructureGeometryKHR bottom_as_geometry = vku::InitStructHelper(); |
| |
| bottom_as_geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; |
| bottom_as_geometry.geometry.triangles = vku::InitStructHelper(); |
| bottom_as_geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32_SFLOAT; |
| bottom_as_geometry.geometry.triangles.vertexData.deviceAddress = vertex_address; |
| bottom_as_geometry.geometry.triangles.vertexStride = 8; |
| bottom_as_geometry.geometry.triangles.maxVertex = 3; |
| bottom_as_geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; |
| bottom_as_geometry.geometry.triangles.indexData.deviceAddress = vertex_address + sizeof(vertex_data); |
| bottom_as_geometry.geometry.triangles.transformData.deviceAddress = 0; |
| bottom_as_geometry.flags = 0; |
| |
| VkAccelerationStructureTrianglesOpacityMicromapEXT opacity_geometry_micromap = vku::InitStructHelper(); |
| opacity_geometry_micromap.indexType = VK_INDEX_TYPE_UINT32; |
| opacity_geometry_micromap.indexBuffer.deviceAddress = micromap_address + index_offset; |
| opacity_geometry_micromap.indexStride = 0; |
| opacity_geometry_micromap.baseTriangle = 0; |
| opacity_geometry_micromap.micromap = micromap; |
| bottom_as_geometry.geometry.triangles.pNext = &opacity_geometry_micromap; |
| |
| VkAccelerationStructureBuildGeometryInfoKHR bottomASInfo = vku::InitStructHelper(); |
| bottomASInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; |
| bottomASInfo.flags = 0; |
| bottomASInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; |
| bottomASInfo.srcAccelerationStructure = VK_NULL_HANDLE; |
| bottomASInfo.dstAccelerationStructure = VK_NULL_HANDLE; |
| bottomASInfo.geometryCount = 1; |
| bottomASInfo.pGeometries = &bottom_as_geometry; |
| bottomASInfo.ppGeometries = NULL; |
| bottomASInfo.scratchData.deviceAddress = 0; |
| |
| uint32_t bottom_max_primitive_counts = 1; |
| vk::GetAccelerationStructureBuildSizesKHR(*m_device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &bottomASInfo, |
| &bottom_max_primitive_counts, &bottom_as_build_size_info); |
| |
| vkt::Buffer bottom_as_buffer(*m_device, bottom_as_build_size_info.accelerationStructureSize, |
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &allocate_da_flag_info); |
| |
| VkAccelerationStructureCreateInfoKHR as_create_info = vku::InitStructHelper(); |
| as_create_info.createFlags = 0; |
| as_create_info.buffer = bottom_as_buffer; |
| as_create_info.offset = 0; |
| as_create_info.size = bottom_as_build_size_info.accelerationStructureSize; |
| as_create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; |
| as_create_info.deviceAddress = 0; |
| |
| VkAccelerationStructureKHR bottomAS, topAS; |
| |
| result = vk::CreateAccelerationStructureKHR(*m_device, &as_create_info, NULL, &bottomAS); |
| ASSERT_EQ(VK_SUCCESS, result); |
| |
| vkt::Buffer instance_buffer( |
| *m_device, 2 * sizeof(VkAccelerationStructureInstanceKHR), |
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
| vkt::device_address); |
| |
| { |
| VkAccelerationStructureInstanceKHR* instance = (VkAccelerationStructureInstanceKHR*)instance_buffer.Memory().Map(); |
| |
| memset(instance, 0, 2 * sizeof(VkAccelerationStructureInstanceKHR)); |
| |
| instance[0].transform.matrix[0][0] = 1; |
| instance[0].transform.matrix[0][1] = 0; |
| instance[0].transform.matrix[0][2] = 0; |
| instance[0].transform.matrix[0][3] = 0; |
| |
| instance[0].transform.matrix[1][0] = 0; |
| instance[0].transform.matrix[1][1] = 1; |
| instance[0].transform.matrix[1][2] = 0; |
| instance[0].transform.matrix[1][3] = 0; |
| |
| instance[0].transform.matrix[2][0] = 0; |
| instance[0].transform.matrix[2][1] = 0; |
| instance[0].transform.matrix[2][2] = 1; |
| instance[0].transform.matrix[2][3] = 0; |
| |
| instance[0].instanceCustomIndex = 0xdeadfe; |
| instance[0].mask = 0xff; |
| instance[0].instanceShaderBindingTableRecordOffset = 0; |
| instance[0].flags = 0; |
| |
| VkAccelerationStructureDeviceAddressInfoKHR as_device_address_info = vku::InitStructHelper(); |
| as_device_address_info.accelerationStructure = bottomAS; |
| instance[0].accelerationStructureReference = |
| vk::GetAccelerationStructureDeviceAddressKHR(device(), &as_device_address_info); |
| } |
| |
| VkAccelerationStructureGeometryKHR top_as_geometry = vku::InitStructHelper(); |
| |
| top_as_geometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; |
| top_as_geometry.geometry.instances = vku::InitStructHelper(); |
| top_as_geometry.geometry.instances.arrayOfPointers = VK_FALSE; |
| top_as_geometry.geometry.instances.data.deviceAddress = instance_buffer.Address(); |
| top_as_geometry.flags = 0; |
| |
| VkAccelerationStructureBuildGeometryInfoKHR topASInfo = vku::InitStructHelper(); |
| topASInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; |
| topASInfo.flags = 0; |
| topASInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; |
| topASInfo.srcAccelerationStructure = VK_NULL_HANDLE; |
| topASInfo.dstAccelerationStructure = VK_NULL_HANDLE; |
| topASInfo.geometryCount = 1; |
| topASInfo.pGeometries = &top_as_geometry; |
| topASInfo.ppGeometries = NULL; |
| topASInfo.scratchData.deviceAddress = 0; |
| |
| uint32_t top_max_primitive_counts = 1; |
| vk::GetAccelerationStructureBuildSizesKHR(device(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &topASInfo, |
| &top_max_primitive_counts, &top_as_build_size_info); |
| |
| vkt::Buffer top_as_buffer(*m_device, top_as_build_size_info.accelerationStructureSize, |
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &allocate_da_flag_info); |
| |
| as_create_info.createFlags = 0; |
| as_create_info.buffer = top_as_buffer; |
| as_create_info.offset = 0; |
| as_create_info.size = top_as_build_size_info.accelerationStructureSize; |
| as_create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; |
| as_create_info.deviceAddress = 0; |
| |
| result = vk::CreateAccelerationStructureKHR(device(), &as_create_info, NULL, &topAS); |
| ASSERT_EQ(VK_SUCCESS, result); |
| |
| vkt::Buffer scratch_buffer(*m_device, |
| std::max(bottom_as_build_size_info.buildScratchSize, top_as_build_size_info.buildScratchSize), |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &allocate_da_flag_info); |
| |
| VkDeviceAddress scratch_address = scratch_buffer.Address(); |
| |
| // Build the bottom-level acceleration structure |
| { |
| bottomASInfo.dstAccelerationStructure = bottomAS; |
| bottomASInfo.scratchData.deviceAddress = scratch_address; |
| |
| VkAccelerationStructureBuildRangeInfoKHR build_range_info = {1, 0, 0, 0}; |
| const VkAccelerationStructureBuildRangeInfoKHR* p_build_range_info = &build_range_info; |
| |
| m_command_buffer.Begin(); |
| vk::CmdBuildAccelerationStructuresKHR(m_command_buffer, 1, &bottomASInfo, &p_build_range_info); |
| VkMemoryBarrier memory_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, NULL, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, |
| VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR}; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, |
| VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &memory_barrier, 0, 0, 0, 0); |
| m_command_buffer.End(); |
| m_default_queue->Submit(m_command_buffer); |
| m_device->Wait(); |
| } |
| |
| // Build the top-level acceleration structure |
| { |
| topASInfo.dstAccelerationStructure = topAS; |
| topASInfo.scratchData.deviceAddress = scratch_address; |
| |
| VkAccelerationStructureBuildRangeInfoKHR build_range_info = {1, 0, 0, 0}; |
| const VkAccelerationStructureBuildRangeInfoKHR* p_build_range_info = &build_range_info; |
| |
| m_command_buffer.Begin(); |
| vk::CmdBuildAccelerationStructuresKHR(m_command_buffer, 1, &topASInfo, &p_build_range_info); |
| VkMemoryBarrier memory_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, NULL, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, |
| VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR}; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, |
| VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, 1, &memory_barrier, 0, 0, 0, 0); |
| m_command_buffer.End(); |
| m_default_queue->Submit(m_command_buffer); |
| m_device->Wait(); |
| } |
| |
| vk::DestroyAccelerationStructureKHR(*m_device, topAS, NULL); |
| vk::DestroyAccelerationStructureKHR(*m_device, bottomAS, NULL); |
| vk::DestroyMicromapEXT(*m_device, micromap, NULL); |
| } |