blob: 9c095f5f48a1a2a8c37f0e6057a4a2bd591c9240 [file]
/*
* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Copyright (c) 2015-2026 Google, Inc.
* Modifications Copyright (C) 2022 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 <gtest/gtest.h>
#include <vulkan/vulkan_core.h>
#include "../framework/layer_validation_tests.h"
#include "containers/range.h"
class PositiveDeviceAddress : public VkLayerTest {};
TEST_F(PositiveDeviceAddress, DestroyOneOutOfMultipleBuffers) {
AddRequiredExtensions(VK_KHR_COPY_MEMORY_INDIRECT_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::indirectMemoryCopy);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(Init());
const size_t copy_size = sizeof(VkCopyMemoryIndirectCommandKHR);
VkBufferCreateInfo buffer_ci = vku::InitStructHelper();
buffer_ci.size = copy_size;
buffer_ci.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
vkt::Buffer indirect_buffer1(*m_device, buffer_ci, vkt::no_mem);
vkt::Buffer indirect_buffer2(*m_device, buffer_ci, vkt::no_mem);
VkMemoryRequirements memory_requirements;
vk::GetBufferMemoryRequirements(*m_device, indirect_buffer1, &memory_requirements);
VkMemoryAllocateFlagsInfo memory_allocate_flags = vku::InitStructHelper();
memory_allocate_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo memory_ai = vku::InitStructHelper(&memory_allocate_flags);
memory_ai.allocationSize = memory_requirements.size;
bool pass =
m_device->Physical().SetMemoryType(memory_requirements.memoryTypeBits, &memory_ai, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
ASSERT_TRUE(pass);
vkt::DeviceMemory memory(*m_device, memory_ai);
vk::BindBufferMemory(*m_device, indirect_buffer1, memory, 0);
vk::BindBufferMemory(*m_device, indirect_buffer2, memory, 0);
VkDeviceAddress address1 = indirect_buffer1.Address();
VkDeviceAddress address2 = indirect_buffer2.Address();
if (address1 != address2) {
GTEST_SKIP() << "Device addresses are not equal.";
}
VkStridedDeviceAddressRangeKHR copy_address_range = {};
copy_address_range.address = indirect_buffer1.Address();
copy_address_range.stride = copy_size;
copy_address_range.size = copy_size;
VkCopyMemoryIndirectInfoKHR indirect_info = vku::InitStructHelper();
indirect_info.copyCount = 1u;
indirect_info.copyAddressRange = copy_address_range;
m_command_buffer.Begin();
vk::CmdCopyMemoryIndirectKHR(m_command_buffer, &indirect_info);
m_command_buffer.End();
indirect_buffer1.Destroy();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveDeviceAddress, DestroyOneBufferBeforeCopy) {
AddRequiredExtensions(VK_KHR_COPY_MEMORY_INDIRECT_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::indirectMemoryCopy);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(Init());
const size_t copy_size = sizeof(VkCopyMemoryIndirectCommandKHR);
VkBufferCreateInfo buffer_ci = vku::InitStructHelper();
buffer_ci.size = copy_size;
buffer_ci.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
vkt::Buffer indirect_buffer1(*m_device, buffer_ci, vkt::no_mem);
vkt::Buffer indirect_buffer2(*m_device, buffer_ci, vkt::no_mem);
VkMemoryRequirements memory_requirements;
vk::GetBufferMemoryRequirements(*m_device, indirect_buffer1, &memory_requirements);
VkMemoryAllocateFlagsInfo memory_allocate_flags = vku::InitStructHelper();
memory_allocate_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo memory_ai = vku::InitStructHelper(&memory_allocate_flags);
memory_ai.allocationSize = memory_requirements.size;
bool pass =
m_device->Physical().SetMemoryType(memory_requirements.memoryTypeBits, &memory_ai, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
ASSERT_TRUE(pass);
vkt::DeviceMemory memory(*m_device, memory_ai);
vk::BindBufferMemory(*m_device, indirect_buffer1, memory, 0);
vk::BindBufferMemory(*m_device, indirect_buffer2, memory, 0);
VkDeviceAddress address1 = indirect_buffer1.Address();
VkDeviceAddress address2 = indirect_buffer2.Address();
if (address1 != address2) {
GTEST_SKIP() << "Device addresses are not equal.";
}
VkStridedDeviceAddressRangeKHR copy_address_range = {};
copy_address_range.address = indirect_buffer1.Address();
copy_address_range.stride = copy_size;
copy_address_range.size = copy_size;
VkCopyMemoryIndirectInfoKHR indirect_info = vku::InitStructHelper();
indirect_info.copyCount = 1u;
indirect_info.copyAddressRange = copy_address_range;
indirect_buffer1.Destroy();
m_command_buffer.Begin();
vk::CmdCopyMemoryIndirectKHR(m_command_buffer, &indirect_info);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveDeviceAddress, ValidSubRange1) {
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::descriptorHeap);
RETURN_IF_SKIP(Init());
VkPhysicalDeviceDescriptorHeapPropertiesEXT heap_props = vku::InitStructHelper();
VkPhysicalDeviceProperties2 props2 = vku::InitStructHelper(&heap_props);
GetPhysicalDeviceProperties2(props2);
const VkDeviceSize heap_size = heap_props.minSamplerHeapReservedRange + 256;
const VkDeviceSize offset = heap_size / 2;
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_2_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage);
buffer_ci.size = heap_size + offset * 2;
vkt::Buffer heap1(*m_device, buffer_ci, vkt::no_mem);
buffer_ci.size = heap_size;
vkt::Buffer heap2(*m_device, buffer_ci, vkt::no_mem);
VkMemoryRequirements memory_requirements;
vk::GetBufferMemoryRequirements(*m_device, heap1, &memory_requirements);
VkMemoryAllocateFlagsInfo memory_allocate_flags = vku::InitStructHelper();
memory_allocate_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo memory_ai = vku::InitStructHelper(&memory_allocate_flags);
// make large enough if offset is not perfectly in the middle of the allocation
memory_ai.allocationSize = memory_requirements.size * 2;
if ((offset % memory_requirements.alignment) != 0) {
GTEST_SKIP() << "Alignment not met";
}
bool pass =
m_device->Physical().SetMemoryType(memory_requirements.memoryTypeBits, &memory_ai, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
ASSERT_TRUE(pass);
vkt::DeviceMemory memory(*m_device, memory_ai);
vk::BindBufferMemory(*m_device, heap1, memory, 0);
vk::BindBufferMemory(*m_device, heap2, memory, offset);
VkDeviceAddress address1 = heap1.Address();
VkDeviceAddress address2 = heap2.Address();
vvl::range<VkDeviceAddress> range1(address1, address1 + heap_size + offset * 2);
vvl::range<VkDeviceAddress> range2(address2, address2 + heap_size);
if (!range1.includes(range2)) {
GTEST_SKIP() << "Address are not overlapping";
}
(void)address1;
VkBindHeapInfoEXT bind_info = vku::InitStructHelper();
bind_info.heapRange = {address2, heap_size};
bind_info.reservedRangeOffset = 0;
bind_info.reservedRangeSize = heap_props.minSamplerHeapReservedRange;
m_command_buffer.Begin();
vk::CmdBindSamplerHeapEXT(m_command_buffer, &bind_info);
m_command_buffer.End();
heap2.Destroy();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveDeviceAddress, ValidSubRange2) {
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::descriptorHeap);
RETURN_IF_SKIP(Init());
VkPhysicalDeviceDescriptorHeapPropertiesEXT heap_props = vku::InitStructHelper();
VkPhysicalDeviceProperties2 props2 = vku::InitStructHelper(&heap_props);
GetPhysicalDeviceProperties2(props2);
const VkDeviceSize heap_size = heap_props.minSamplerHeapReservedRange + 256;
const VkDeviceSize offset = heap_size / 2;
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_2_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage);
buffer_ci.size = heap_size + offset * 2;
vkt::Buffer heap1(*m_device, buffer_ci, vkt::no_mem);
buffer_ci.size = heap_size;
vkt::Buffer heap2(*m_device, buffer_ci, vkt::no_mem);
VkMemoryRequirements memory_requirements;
vk::GetBufferMemoryRequirements(*m_device, heap1, &memory_requirements);
VkMemoryAllocateFlagsInfo memory_allocate_flags = vku::InitStructHelper();
memory_allocate_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo memory_ai = vku::InitStructHelper(&memory_allocate_flags);
// make large enough if offset is not perfectly in the middle of the allocation
memory_ai.allocationSize = memory_requirements.size * 2;
bool pass =
m_device->Physical().SetMemoryType(memory_requirements.memoryTypeBits, &memory_ai, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
ASSERT_TRUE(pass);
vkt::DeviceMemory memory(*m_device, memory_ai);
vk::BindBufferMemory(*m_device, heap1, memory, 0);
vk::BindBufferMemory(*m_device, heap2, memory, offset);
VkDeviceAddress address1 = heap1.Address();
VkDeviceAddress address2 = heap2.Address();
(void)address1;
VkBindHeapInfoEXT bind_info = vku::InitStructHelper();
bind_info.heapRange = {address2, heap_size};
bind_info.reservedRangeOffset = 0;
bind_info.reservedRangeSize = heap_props.minSamplerHeapReservedRange;
m_command_buffer.Begin();
vk::CmdBindSamplerHeapEXT(m_command_buffer, &bind_info);
m_command_buffer.End();
heap1.Destroy();
m_default_queue->SubmitAndWait(m_command_buffer);
}