blob: 1f6f46de1664efeae50ce0b860e95191e3b4a691 [file] [log] [blame]
/*
* Copyright (c) 2015-2024 The Khronos Group Inc.
* Copyright (c) 2015-2024 Valve Corporation
* Copyright (c) 2015-2024 LunarG, Inc.
* Copyright (c) 2015-2024 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 "utils/vk_layer_utils.h"
#include "../framework/layer_validation_tests.h"
#include "../framework/external_memory_sync.h"
class NegativeSparseImage : public VkLayerTest {};
TEST_F(NegativeSparseImage, BindingImageBufferCreate) {
TEST_DESCRIPTION("Create buffer/image with sparse attributes but without the sparse_binding bit set");
AddRequiredFeature(vkt::Feature::sparseResidencyAliased);
AddRequiredFeature(vkt::Feature::sparseResidencyBuffer);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
VkBufferCreateInfo buf_info = vku::InitStructHelper();
buf_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
buf_info.size = 2048;
if (m_device->Physical().Features().sparseResidencyBuffer) {
buf_info.flags = VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT;
CreateBufferTest(*this, &buf_info, "VUID-VkBufferCreateInfo-flags-00918");
} else {
GTEST_SKIP() << "Test requires unsupported sparseResidencyBuffer feature";
}
if (m_device->Physical().Features().sparseResidencyAliased) {
buf_info.flags = VK_BUFFER_CREATE_SPARSE_ALIASED_BIT;
CreateBufferTest(*this, &buf_info, "VUID-VkBufferCreateInfo-flags-00918");
} else {
GTEST_SKIP() << "Test requires unsupported sparseResidencyAliased feature";
}
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 512;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (m_device->Physical().Features().sparseResidencyImage2D) {
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-flags-00987");
} else {
GTEST_SKIP() << "Test requires unsupported sparseResidencyImage2D feature";
}
if (m_device->Physical().Features().sparseResidencyAliased) {
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-flags-00987");
} else {
GTEST_SKIP() << "Test requires unsupported sparseResidencyAliased feature";
}
}
TEST_F(NegativeSparseImage, ResidencyImageCreateUnsupportedTypes) {
TEST_DESCRIPTION("Create images with sparse residency with unsupported types");
AddRequiredFeature(vkt::Feature::sparseBinding);
RETURN_IF_SKIP(Init());
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.imageType = VK_IMAGE_TYPE_1D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 512;
image_create_info.extent.height = 1;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
// 1D image w/ sparse residency is an error
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00970");
// 2D image w/ sparse residency when feature isn't available
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.extent.height = 64;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00971");
// 3D image w/ sparse residency when feature isn't available
image_create_info.imageType = VK_IMAGE_TYPE_3D;
image_create_info.extent.depth = 8;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00972");
}
TEST_F(NegativeSparseImage, ResidencyImageCreateUnsupportedSamples) {
TEST_DESCRIPTION("Create images with sparse residency with unsupported tiling or sample counts");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 64;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_LINEAR;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
// 2D image w/ sparse residency and linear tiling is an error
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-tiling-04121");
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
// Multi-sample image w/ sparse residency when feature isn't available (4 flavors)
image_create_info.samples = VK_SAMPLE_COUNT_2_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00973");
image_create_info.samples = VK_SAMPLE_COUNT_4_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00974");
image_create_info.samples = VK_SAMPLE_COUNT_8_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00975");
image_create_info.samples = VK_SAMPLE_COUNT_16_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-imageType-00976");
}
TEST_F(NegativeSparseImage, ResidencyFlag) {
TEST_DESCRIPTION("Try to use VkSparseImageMemoryBindInfo without sparse residency flag");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 512;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
VkSparseImageMemoryBind image_memory_bind = {};
image_memory_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_memory_bind.extent = image_create_info.extent;
VkSparseImageMemoryBindInfo image_memory_bind_info = {};
image_memory_bind_info.image = image.handle();
image_memory_bind_info.bindCount = 1;
image_memory_bind_info.pBinds = &image_memory_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.imageBindCount = 1;
bind_info.pImageBinds = &image_memory_bind_info;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBindInfo-image-02901");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSparseImage, ImageUsageBits) {
TEST_DESCRIPTION("Try to use VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT with sparse image");
AddRequiredFeature(vkt::Feature::sparseBinding);
RETURN_IF_SKIP(Init());
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent = {32, 32, 1};
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
CreateImageTest(*this, &image_create_info, "VUID-VkImageCreateInfo-None-01925");
}
TEST_F(NegativeSparseImage, MemoryBindOffset) {
TEST_DESCRIPTION("Try to use VkSparseImageMemoryBind with offset not less than memory size");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyBuffer);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
buffer_create_info.size = 0x10000;
if (m_device->Physical().Features().sparseResidencyBuffer) {
buffer_create_info.flags = VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
} else {
GTEST_SKIP() << "Test requires unsupported sparseResidencyBuffer feature";
}
VkImageCreateInfo image_create_info = vku::InitStructHelper(nullptr);
image_create_info.flags = 0;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 8;
image_create_info.extent.height = 8;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (m_device->Physical().Features().sparseResidencyImage2D) {
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
} else {
GTEST_SKIP() << "Test requires unsupported sparseResidencyImage2D feature";
}
vkt::Buffer buffer(*m_device, buffer_create_info, vkt::no_mem);
VkMemoryRequirements buffer_mem_reqs;
vk::GetBufferMemoryRequirements(device(), buffer, &buffer_mem_reqs);
VkMemoryAllocateInfo buffer_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
buffer_mem_alloc.allocationSize =
(buffer_mem_alloc.allocationSize + buffer_mem_reqs.alignment - 1) & ~(buffer_mem_reqs.alignment - 1);
vkt::DeviceMemory buffer_mem(*m_device, buffer_mem_alloc);
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(device(), image, &image_mem_reqs);
VkMemoryAllocateInfo image_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
image_mem_alloc.allocationSize =
(image_mem_alloc.allocationSize + image_mem_reqs.alignment - 1) & ~(image_mem_reqs.alignment - 1);
vkt::DeviceMemory image_mem(*m_device, image_mem_alloc);
VkSparseMemoryBind buffer_memory_bind = {};
buffer_memory_bind.size = buffer_create_info.size;
buffer_memory_bind.memory = buffer_mem;
buffer_memory_bind.memoryOffset = buffer_mem_alloc.allocationSize;
VkSparseImageMemoryBind image_memory_bind = {};
image_memory_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_memory_bind.memoryOffset = image_mem_alloc.allocationSize;
image_memory_bind.memory = image_mem;
image_memory_bind.extent = image_create_info.extent;
VkSparseBufferMemoryBindInfo buffer_memory_bind_info = {};
buffer_memory_bind_info.buffer = buffer;
buffer_memory_bind_info.bindCount = 1;
buffer_memory_bind_info.pBinds = &buffer_memory_bind;
VkSparseMemoryBind image_opaque_memory_bind = {};
image_opaque_memory_bind.size = 4 * image_create_info.extent.width * image_create_info.extent.height;
image_opaque_memory_bind.memory = image_mem;
image_opaque_memory_bind.memoryOffset = image_mem_alloc.allocationSize;
VkSparseImageOpaqueMemoryBindInfo image_opaque_memory_bind_info = {};
image_opaque_memory_bind_info.image = image;
image_opaque_memory_bind_info.bindCount = 1;
image_opaque_memory_bind_info.pBinds = &image_opaque_memory_bind;
VkSparseImageMemoryBindInfo image_memory_bind_info = {};
image_memory_bind_info.image = image;
image_memory_bind_info.bindCount = 1;
image_memory_bind_info.pBinds = &image_memory_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.bufferBindCount = 1;
bind_info.pBufferBinds = &buffer_memory_bind_info;
bind_info.imageOpaqueBindCount = 1;
bind_info.pImageOpaqueBinds = &image_opaque_memory_bind_info;
bind_info.imageBindCount = 1;
bind_info.pImageBinds = &image_memory_bind_info;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memoryOffset-01101", 3);
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-size-01102", 2);
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSparseImage, QueueBindSparseMemoryType) {
TEST_DESCRIPTION("Test QueueBindSparse with memory of a wrong type");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyBuffer);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
const uint32_t mem_types_mask = (1u << m_device->Physical().memory_properties_.memoryTypeCount) - 1;
/// Create buffer whose memory has an incompatible type
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
buffer_create_info.size = 1024;
vkt::Buffer buffer(*m_device, buffer_create_info, vkt::no_mem);
VkMemoryRequirements buffer_mem_reqs;
vk::GetBufferMemoryRequirements(device(), buffer.handle(), &buffer_mem_reqs);
const bool buffer_supports_all_mem_types = (buffer_mem_reqs.memoryTypeBits & mem_types_mask) == mem_types_mask;
VkMemoryAllocateInfo buffer_mem_alloc = vku::InitStructHelper();
buffer_mem_alloc.allocationSize = buffer_mem_reqs.size;
buffer_mem_alloc.memoryTypeIndex = vvl::kU32Max;
// Try to pick incompatible memory type
for (uint32_t memory_type_i = 0; memory_type_i < m_device->Physical().memory_properties_.memoryTypeCount; ++memory_type_i) {
if (m_device->Physical().memory_properties_.memoryTypes[memory_type_i].propertyFlags &
VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) {
continue;
}
if (m_device->Physical().memory_properties_.memoryTypes[memory_type_i].propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) {
continue;
}
if (!((1u << memory_type_i) & buffer_mem_reqs.memoryTypeBits)) {
buffer_mem_alloc.memoryTypeIndex = memory_type_i;
break;
}
}
if (buffer_mem_alloc.memoryTypeIndex == vvl::kU32Max) {
GTEST_SKIP() << "Could not find suitable memory type for buffer, skipping test";
}
const bool buffer_mem_lazy =
m_device->Physical().memory_properties_.memoryTypes[buffer_mem_alloc.memoryTypeIndex].propertyFlags &
VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
vkt::DeviceMemory buffer_mem(*m_device, buffer_mem_alloc);
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 64;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
/// Create image whose memory has an incompatible type
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(device(), image.handle(), &image_mem_reqs);
const bool image_supports_all_mem_types = (image_mem_reqs.memoryTypeBits & mem_types_mask) == mem_types_mask;
VkMemoryAllocateInfo image_mem_alloc = vku::InitStructHelper();
image_mem_alloc.allocationSize = image_mem_reqs.size;
image_mem_alloc.memoryTypeIndex = vvl::kU32Max;
// Try to pick incompatible memory type
for (uint32_t memory_type_i = 0; memory_type_i < m_device->Physical().memory_properties_.memoryTypeCount; ++memory_type_i) {
if (m_device->Physical().memory_properties_.memoryTypes[memory_type_i].propertyFlags &
VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) {
continue;
}
if (m_device->Physical().memory_properties_.memoryTypes[memory_type_i].propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) {
continue;
}
if (!((1u << memory_type_i) & image_mem_reqs.memoryTypeBits)) {
image_mem_alloc.memoryTypeIndex = memory_type_i;
break;
}
}
if (image_mem_alloc.memoryTypeIndex == vvl::kU32Max) {
GTEST_SKIP() << "Could not find suitable memory type for image, skipping test";
}
const bool image_mem_lazy = m_device->Physical().memory_properties_.memoryTypes[image_mem_alloc.memoryTypeIndex].propertyFlags &
VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
vkt::DeviceMemory image_mem(*m_device, image_mem_alloc);
/// Specify memory bindings
VkSparseMemoryBind buffer_memory_bind = {};
buffer_memory_bind.size = buffer_mem_reqs.size;
buffer_memory_bind.memory = buffer_mem.handle();
VkSparseMemoryBind image_memory_bind = {};
image_memory_bind.size = image_mem_reqs.size;
image_memory_bind.memory = image_mem.handle();
VkSparseBufferMemoryBindInfo buffer_memory_bind_info = {};
buffer_memory_bind_info.buffer = buffer.handle();
buffer_memory_bind_info.bindCount = 1;
buffer_memory_bind_info.pBinds = &buffer_memory_bind;
VkSparseImageOpaqueMemoryBindInfo image_opaque_memory_bind_info = {};
image_opaque_memory_bind_info.image = image.handle();
image_opaque_memory_bind_info.bindCount = 1;
image_opaque_memory_bind_info.pBinds = &image_memory_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.pBufferBinds = &buffer_memory_bind_info;
bind_info.pImageOpaqueBinds = &image_opaque_memory_bind_info;
// Validate only buffer
if (!buffer_supports_all_mem_types) {
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
if (buffer_mem_lazy) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
}
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
} else {
printf("Could not find an invalid memory type for buffer, skipping part of test.\n");
}
// Validate only image
if (!image_supports_all_mem_types) {
bind_info.bufferBindCount = 0;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
if (image_mem_lazy) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
}
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
} else {
printf("Could not find an invalid memory type for image, skipping part of test.\n");
}
// Validate both a buffer and image error occur
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 1;
if (!buffer_supports_all_mem_types) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
}
if (buffer_mem_lazy) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
}
if (!image_supports_all_mem_types) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
}
if (image_mem_lazy) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
}
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSparseImage, QueueBindSparseMemoryType2) {
TEST_DESCRIPTION("Test QueueBindSparse with lazily allocated memory");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyBuffer);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
uint32_t lazily_allocated_index =
m_device->Physical().memory_properties_.memoryTypeCount; // Set to an invalid value just in case
for (uint32_t i = 0; i < m_device->Physical().memory_properties_.memoryTypeCount; ++i) {
if ((m_device->Physical().memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0) {
lazily_allocated_index = i;
break;
}
}
if (lazily_allocated_index == m_device->Physical().memory_properties_.memoryTypeCount) {
GTEST_SKIP() << "Did not find memory with VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT";
}
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
buffer_create_info.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
buffer_create_info.size = 1024;
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 64;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkt::Buffer buffer(*m_device, buffer_create_info, vkt::no_mem);
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
VkMemoryRequirements buffer_mem_reqs;
vk::GetBufferMemoryRequirements(device(), buffer.handle(), &buffer_mem_reqs);
VkMemoryAllocateInfo buffer_mem_alloc = vku::InitStructHelper();
buffer_mem_alloc.allocationSize = buffer_mem_reqs.size;
buffer_mem_alloc.memoryTypeIndex = lazily_allocated_index;
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(device(), image.handle(), &image_mem_reqs);
VkMemoryAllocateInfo image_mem_alloc = vku::InitStructHelper();
image_mem_alloc.allocationSize = image_mem_reqs.size;
image_mem_alloc.memoryTypeIndex = lazily_allocated_index;
vkt::DeviceMemory buffer_mem(*m_device, buffer_mem_alloc);
vkt::DeviceMemory image_mem(*m_device, image_mem_alloc);
VkSparseMemoryBind buffer_memory_bind = {};
buffer_memory_bind.size = buffer_mem_reqs.size;
buffer_memory_bind.memory = buffer_mem.handle();
VkSparseMemoryBind image_memory_bind = {};
image_memory_bind.size = image_mem_reqs.size;
image_memory_bind.memory = image_mem.handle();
VkSparseBufferMemoryBindInfo buffer_memory_bind_info = {};
buffer_memory_bind_info.buffer = buffer.handle();
buffer_memory_bind_info.bindCount = 1;
buffer_memory_bind_info.pBinds = &buffer_memory_bind;
VkSparseImageOpaqueMemoryBindInfo image_opaque_memory_bind_info = {};
image_opaque_memory_bind_info.image = image.handle();
image_opaque_memory_bind_info.bindCount = 1;
image_opaque_memory_bind_info.pBinds = &image_memory_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.pBufferBinds = &buffer_memory_bind_info;
bind_info.pImageOpaqueBinds = &image_opaque_memory_bind_info;
// Validate only buffer
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
if (!((1u << buffer_mem_alloc.memoryTypeIndex) & buffer_mem_reqs.memoryTypeBits)) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
}
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate only image
{
bind_info.bufferBindCount = 0;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
if (!((1u << image_mem_alloc.memoryTypeIndex) & image_mem_reqs.memoryTypeBits)) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
}
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate both a buffer and image error occur
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01097");
if (!((1u << buffer_mem_alloc.memoryTypeIndex) & buffer_mem_reqs.memoryTypeBits)) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
}
if (!((1u << image_mem_alloc.memoryTypeIndex) & image_mem_reqs.memoryTypeBits)) {
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
}
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSparseImage, QueueBindSparseMemoryType3) {
TEST_DESCRIPTION(
"Test QueueBindSparse with memory having export external handle types that do not match those of the resource");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyBuffer);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
/// Allocate buffer and buffer memory with an external handle type
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper(); // Do not set any supported external handle type
buffer_create_info.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
buffer_create_info.size = 1024;
vkt::Buffer buffer(*m_device, buffer_create_info, vkt::no_mem);
const auto buffer_exportable_types =
FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_create_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT);
if (!buffer_exportable_types) {
GTEST_SKIP() << "Unable to find exportable handle type for buffer, skipping test";
}
const auto buffer_exportable_type = LeastSignificantFlag<VkExternalMemoryHandleTypeFlagBits>(buffer_exportable_types);
VkExportMemoryAllocateInfo buffer_export_mem_alloc_info = vku::InitStructHelper();
buffer_export_mem_alloc_info.handleTypes = GetCompatibleHandleTypes(Gpu(), buffer_create_info, buffer_exportable_type);
VkMemoryRequirements buffer_mem_reqs{};
vk::GetBufferMemoryRequirements(device(), buffer.handle(), &buffer_mem_reqs);
const VkMemoryAllocateInfo buffer_mem_alloc = vkt::DeviceMemory::GetResourceAllocInfo(
*m_device, buffer_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &buffer_export_mem_alloc_info);
vkt::DeviceMemory buffer_mem(*m_device, buffer_mem_alloc);
/// Allocate image and image memory with an external handle type
VkImageCreateInfo image_create_info = vku::InitStructHelper(); // Do not set any supported external handle type
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 64;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
const auto image_exportable_types =
FindSupportedExternalMemoryHandleTypes(Gpu(), image_create_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT);
if (!image_exportable_types) {
GTEST_SKIP() << "Unable to find exportable handle type for image, skipping test";
}
const auto image_exportable_type = LeastSignificantFlag<VkExternalMemoryHandleTypeFlagBits>(image_exportable_types);
VkExportMemoryAllocateInfo image_export_mem_alloc_info = vku::InitStructHelper();
image_export_mem_alloc_info.handleTypes = GetCompatibleHandleTypes(Gpu(), image_create_info, image_exportable_type);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(device(), image.handle(), &image_mem_reqs);
const VkMemoryAllocateInfo image_mem_alloc = vkt::DeviceMemory::GetResourceAllocInfo(
*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &image_export_mem_alloc_info);
vkt::DeviceMemory image_mem(*m_device, image_mem_alloc);
// Setup memory bindings
VkSparseMemoryBind buffer_memory_bind = {};
buffer_memory_bind.size = buffer_mem_reqs.size;
buffer_memory_bind.memory = buffer_mem.handle();
VkSparseBufferMemoryBindInfo buffer_memory_bind_info = {};
buffer_memory_bind_info.buffer = buffer.handle();
buffer_memory_bind_info.bindCount = 1;
buffer_memory_bind_info.pBinds = &buffer_memory_bind;
VkSparseMemoryBind image_memory_bind = {};
image_memory_bind.size = image_mem_reqs.size;
image_memory_bind.memory = image_mem.handle();
VkSparseImageOpaqueMemoryBindInfo image_opaque_memory_bind_info = {};
image_opaque_memory_bind_info.image = image.handle();
image_opaque_memory_bind_info.bindCount = 1;
image_opaque_memory_bind_info.pBinds = &image_memory_bind;
VkSparseImageMemoryBind image_memory_bind_2 = {};
image_memory_bind_2.extent = image_create_info.extent;
image_memory_bind_2.memory = image_mem.handle();
image_memory_bind_2.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkSparseImageMemoryBindInfo image_memory_bind_info = {};
image_memory_bind_info.image = image;
image_memory_bind_info.bindCount = 1;
image_memory_bind_info.pBinds = &image_memory_bind_2;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.pBufferBinds = &buffer_memory_bind_info;
bind_info.pImageOpaqueBinds = &image_opaque_memory_bind_info;
bind_info.pImageBinds = &image_memory_bind_info;
// Validate only buffer
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-02730");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate only image opaque bind
{
bind_info.bufferBindCount = 0;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-02730");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate both a buffer and image error occur
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-02730", 2);
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate only image bind
{
bind_info.bufferBindCount = 0;
bind_info.imageOpaqueBindCount = 0;
bind_info.imageBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-memory-02732");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSparseImage, QueueBindSparseMemoryType4) {
TEST_DESCRIPTION(
"Test QueueBindSparse with memory having import external handle types that do not match those of the resource");
SetTargetApiVersion(VK_API_VERSION_1_1);
#ifdef _WIN32
const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME;
const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
#else
const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME;
const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
#endif
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
AddRequiredExtensions(ext_mem_extension_name);
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyBuffer);
AddRequiredFeature(vkt::Feature::sparseResidencyImage2D);
RETURN_IF_SKIP(Init());
if (IsPlatformMockICD()) {
GTEST_SKIP() << "External tests are not supported by MockICD, skipping tests";
}
// Check for import/export capability
VkPhysicalDeviceExternalBufferInfoKHR external_buffer_info = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR, nullptr, VK_BUFFER_CREATE_SPARSE_BINDING_BIT,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, handle_type};
VkExternalBufferProperties external_buffer_props = {VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR, nullptr, {0, 0, 0}};
vk::GetPhysicalDeviceExternalBufferProperties(Gpu(), &external_buffer_info, &external_buffer_props);
if (!(external_buffer_props.externalMemoryProperties.compatibleHandleTypes & handle_type) ||
!(external_buffer_props.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT) ||
!(external_buffer_props.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
GTEST_SKIP() << "External buffer does not support importing and exporting";
}
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
// Check if dedicated allocation is required
const bool dedicated_allocation =
external_buffer_props.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT;
if (dedicated_allocation) {
// VUID-VkMemoryDedicatedAllocateInfo-buffer-01436
GTEST_SKIP() << "Dedicated allocation is required, which cannot be used with VK_BUFFER_CREATE_SPARSE_BINDING_BIT";
}
/// Allocate buffer and buffer memory with no supported external type
VkExternalMemoryBufferCreateInfo external_memory_buffer_info = vku::InitStructHelper();
external_memory_buffer_info.handleTypes = 0;
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper(&external_memory_buffer_info);
buffer_create_info.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
buffer_create_info.size = 1024;
vkt::Buffer buffer1(*m_device, buffer_create_info, vkt::no_mem);
buffer_create_info.size = 65536;
vkt::Buffer buffer2(*m_device, buffer_create_info, vkt::no_mem);
VkMemoryAllocateInfo buffer_mem_alloc1 = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer1.MemoryRequirements(), 0);
VkMemoryAllocateInfo buffer_mem_alloc2 = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer2.MemoryRequirements(), 0);
VkExportMemoryAllocateInfo export_info1 = vku::InitStructHelper();
export_info1.handleTypes = handle_type;
buffer_mem_alloc1.pNext = &export_info1;
VkExportMemoryAllocateInfo export_info2 = vku::InitStructHelper();
export_info2.handleTypes = handle_type;
buffer_mem_alloc2.pNext = &export_info2;
// Export memory
vkt::DeviceMemory buffer_memory_export1(*m_device, buffer_mem_alloc1);
vkt::DeviceMemory buffer_memory_export2(*m_device, buffer_mem_alloc2);
#ifdef _WIN32
// Export memory to handle
VkMemoryGetWin32HandleInfoKHR mghi = vku::InitStructHelper();
mghi.memory = buffer_memory_export1;
mghi.handleType = handle_type;
HANDLE handle1;
ASSERT_EQ(VK_SUCCESS, vk::GetMemoryWin32HandleKHR(device(), &mghi, &handle1));
VkImportMemoryWin32HandleInfoKHR import_info1 = vku::InitStructHelper();
import_info1.handleType = handle_type;
import_info1.handle = handle1;
mghi.memory = buffer_memory_export2;
HANDLE handle2;
ASSERT_EQ(VK_SUCCESS, vk::GetMemoryWin32HandleKHR(device(), &mghi, &handle2));
VkImportMemoryWin32HandleInfoKHR import_info2 = vku::InitStructHelper();
import_info2.handleType = handle_type;
import_info2.handle = handle2;
#else
// Export memory to fd
VkMemoryGetFdInfoKHR mgfi = vku::InitStructHelper();
mgfi.memory = buffer_memory_export1;
mgfi.handleType = handle_type;
int fd1 = 0;
ASSERT_EQ(VK_SUCCESS, vk::GetMemoryFdKHR(device(), &mgfi, &fd1));
VkImportMemoryFdInfoKHR import_info1 = vku::InitStructHelper();
import_info1.handleType = handle_type;
import_info1.fd = fd1;
mgfi.memory = buffer_memory_export2;
int fd2 = 0;
ASSERT_EQ(VK_SUCCESS, vk::GetMemoryFdKHR(device(), &mgfi, &fd2));
VkImportMemoryFdInfoKHR import_info2 = vku::InitStructHelper();
import_info2.handleType = handle_type;
import_info2.fd = fd2;
#endif
buffer_mem_alloc1.pNext = &import_info1;
vkt::DeviceMemory buffer_memory_imported(*m_device, buffer_mem_alloc1);
/// Allocate image and image memory with an external handle type
VkImageCreateInfo image_create_info = vku::InitStructHelper(); // Do not set any supported external handle type
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = 64;
image_create_info.extent.height = 64;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(device(), image.handle(), &image_mem_reqs);
const VkMemoryAllocateInfo image_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &import_info2);
vkt::DeviceMemory image_mem(*m_device, image_mem_alloc);
// Setup memory bindings
VkSparseMemoryBind buffer_memory_bind = {};
buffer_memory_bind.size = buffer1.MemoryRequirements().size;
buffer_memory_bind.memory = buffer_memory_imported.handle();
VkSparseBufferMemoryBindInfo buffer_memory_bind_info = {};
buffer_memory_bind_info.buffer = buffer1.handle();
buffer_memory_bind_info.bindCount = 1;
buffer_memory_bind_info.pBinds = &buffer_memory_bind;
VkSparseMemoryBind image_memory_bind = {};
image_memory_bind.size = image_mem_reqs.size;
image_memory_bind.memory = image_mem.handle();
VkSparseImageOpaqueMemoryBindInfo image_opaque_memory_bind_info = {};
image_opaque_memory_bind_info.image = image.handle();
image_opaque_memory_bind_info.bindCount = 1;
image_opaque_memory_bind_info.pBinds = &image_memory_bind;
VkSparseImageMemoryBind image_memory_bind_2 = {};
image_memory_bind_2.extent = image_create_info.extent;
image_memory_bind_2.memory = image_mem.handle();
image_memory_bind_2.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkSparseImageMemoryBindInfo image_memory_bind_info = {};
image_memory_bind_info.image = image;
image_memory_bind_info.bindCount = 1;
image_memory_bind_info.pBinds = &image_memory_bind_2;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.pBufferBinds = &buffer_memory_bind_info;
bind_info.pImageOpaqueBinds = &image_opaque_memory_bind_info;
bind_info.pImageBinds = &image_memory_bind_info;
// Validate only buffer
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-02731");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate only image opaque bind
{
bind_info.bufferBindCount = 0;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-02731");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate both a buffer and image error occur
{
bind_info.bufferBindCount = 1;
bind_info.imageOpaqueBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-02731", 2);
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
// Validate only image bind
{
bind_info.bufferBindCount = 0;
bind_info.imageOpaqueBindCount = 0;
bind_info.imageBindCount = 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-memory-02733");
vk::QueueBindSparse(m_device->QueuesWithSparseCapability()[0]->handle(), 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSparseImage, ImageMemoryBind) {
TEST_DESCRIPTION("Try to bind sparse resident image with invalid VkSparseImageMemoryBind");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyImage3D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
VkImageCreateInfo create_info = vkt::Image::CreateInfo();
create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
create_info.imageType = VK_IMAGE_TYPE_3D;
create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
create_info.extent.width = 1024;
create_info.extent.height = 1024;
create_info.extent.depth = 1;
create_info.arrayLayers = 1;
vkt::Image image(*m_device, create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(m_device->handle(), image.handle(), &image_mem_reqs);
const auto image_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkt::DeviceMemory image_mem;
image_mem.init(*m_device, image_mem_alloc);
uint32_t requirements_count = 0u;
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, nullptr);
if (requirements_count == 0u) {
GTEST_SKIP() << "No sparse image requirements for image format VK_FORMAT_B8G8R8A8_UNORM";
}
std::vector<VkSparseImageMemoryRequirements> sparse_reqs(requirements_count);
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, sparse_reqs.data());
VkExtent3D granularity = sparse_reqs[0].formatProperties.imageGranularity;
VkSparseImageMemoryBind image_bind{};
image_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_bind.memory = image_mem.handle();
image_bind.extent = granularity;
VkSparseImageMemoryBindInfo image_bind_info{};
image_bind_info.image = image.handle();
image_bind_info.bindCount = 1u;
image_bind_info.pBinds = &image_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.imageBindCount = 1u;
bind_info.pImageBinds = &image_bind_info;
VkQueue sparse_queue = m_device->QueuesWithSparseCapability()[0]->handle();
// Force offset.x to invalid value
image_bind.offset.x = granularity.width - 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-offset-01107");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.offset.x = 0u;
// Force offset.y to invalid value
image_bind.offset.y = granularity.height - 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-offset-01109");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.offset.y = 0u;
// Force offset.y to invalid value
image_bind.offset.z = granularity.depth - 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-offset-01111");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.offset.z = 0u;
// Force extent.width to invalid value
image_bind.extent.width = granularity.width - 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-extent-01108");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.extent.width = granularity.width;
// Force extent.height to invalid value
image_bind.extent.height = granularity.height - 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-extent-01110");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.extent.height = granularity.height;
// Force extent.depth to invalid value
image_bind.extent.depth = granularity.depth - 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-extent-01112");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.extent.depth = granularity.depth;
// Force greater mip level
image_bind.subresource.mipLevel = VK_REMAINING_MIP_LEVELS;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBindInfo-subresource-01722");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.subresource.mipLevel = 0;
// Force greater array layer
image_bind.subresource.arrayLayer = VK_REMAINING_ARRAY_LAYERS;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBindInfo-subresource-01723");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.subresource.arrayLayer = 0;
// Force invalid aspect mask
image_bind.subresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBindInfo-subresource-01106");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
TEST_F(NegativeSparseImage, ImageMemoryBindInvalidMemory) {
TEST_DESCRIPTION("Try to bind sparse resident image with invalid VkSparseImageMemoryBind");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyImage3D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
VkImageCreateInfo create_info = vkt::Image::CreateInfo();
create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
create_info.imageType = VK_IMAGE_TYPE_3D;
create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
create_info.extent.width = 1024;
create_info.extent.height = 1024;
create_info.extent.depth = 1;
create_info.arrayLayers = 1;
vkt::Image image(*m_device, create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(m_device->handle(), image.handle(), &image_mem_reqs);
const auto image_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkt::DeviceMemory image_mem;
image_mem.init(*m_device, image_mem_alloc);
VkImageCreateInfo invalid_create_info = create_info;
vkt::Image invalid_image(*m_device, invalid_create_info, vkt::no_mem);
VkMemoryRequirements invalid_image_mem_reqs;
vk::GetImageMemoryRequirements(m_device->handle(), invalid_image.handle(), &invalid_image_mem_reqs);
// Make sure that the same memory type is not chosen.
invalid_image_mem_reqs.memoryTypeBits = ~image_mem_reqs.memoryTypeBits;
VkMemoryAllocateInfo invalid_image_mem_alloc = vku::InitStructHelper();
invalid_image_mem_alloc.allocationSize = invalid_image_mem_reqs.size;
if (!m_device->Physical().SetMemoryType(invalid_image_mem_reqs.memoryTypeBits, &invalid_image_mem_alloc,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD | VK_MEMORY_PROPERTY_PROTECTED_BIT)) {
GTEST_SKIP() << "Could not find required memory type";
}
vkt::DeviceMemory invalid_image_mem;
invalid_image_mem.init(*m_device, invalid_image_mem_alloc);
uint32_t requirements_count = 0u;
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, nullptr);
if (requirements_count == 0u) {
GTEST_SKIP() << "No sparse image requirements for image format VK_FORMAT_B8G8R8A8_UNORM";
}
std::vector<VkSparseImageMemoryRequirements> sparse_reqs(requirements_count);
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, sparse_reqs.data());
VkExtent3D granularity = sparse_reqs[0].formatProperties.imageGranularity;
VkSparseImageMemoryBind image_bind{};
image_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_bind.memory = image_mem.handle();
image_bind.memoryOffset = 0;
image_bind.extent = granularity;
VkSparseImageMemoryBindInfo image_bind_info{};
image_bind_info.image = image.handle();
image_bind_info.bindCount = 1u;
image_bind_info.pBinds = &image_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.imageBindCount = 1u;
bind_info.pImageBinds = &image_bind_info;
VkQueue sparse_queue = m_device->QueuesWithSparseCapability()[0]->handle();
// Force invalid device memory
image_bind.memory = invalid_image_mem.handle();
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-memory-01105");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSparseImage, ImageMemoryBindInvalidAlignment) {
TEST_DESCRIPTION("Try to bind sparse resident image with invalid VkSparseImageMemoryBind");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyImage3D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
VkImageCreateInfo create_info = vkt::Image::CreateInfo();
create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
create_info.imageType = VK_IMAGE_TYPE_3D;
create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
create_info.extent.width = 1024;
create_info.extent.height = 1024;
create_info.extent.depth = 1;
create_info.arrayLayers = 1;
vkt::Image image(*m_device, create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(m_device->handle(), image.handle(), &image_mem_reqs);
if (image_mem_reqs.alignment == 1) {
GTEST_SKIP() << "Need image memory required alignment to be more than 1";
}
const auto image_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkt::DeviceMemory image_mem;
image_mem.init(*m_device, image_mem_alloc);
VkImageCreateInfo invalid_create_info = create_info;
vkt::Image invalid_image(*m_device, invalid_create_info, vkt::no_mem);
VkMemoryRequirements invalid_image_mem_reqs;
vk::GetImageMemoryRequirements(m_device->handle(), invalid_image.handle(), &invalid_image_mem_reqs);
// Make sure that the same memory type is not chosen.
invalid_image_mem_reqs.memoryTypeBits = ~image_mem_reqs.memoryTypeBits;
VkMemoryAllocateInfo invalid_image_mem_alloc = vku::InitStructHelper();
invalid_image_mem_alloc.allocationSize = invalid_image_mem_reqs.size;
if (!m_device->Physical().SetMemoryType(invalid_image_mem_reqs.memoryTypeBits, &invalid_image_mem_alloc,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD | VK_MEMORY_PROPERTY_PROTECTED_BIT)) {
GTEST_SKIP() << "Could not find required memory type";
}
vkt::DeviceMemory invalid_image_mem;
invalid_image_mem.init(*m_device, invalid_image_mem_alloc);
uint32_t requirements_count = 0u;
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, nullptr);
if (requirements_count == 0u) {
GTEST_SKIP() << "No sparse image requirements for image format VK_FORMAT_B8G8R8A8_UNORM";
}
std::vector<VkSparseImageMemoryRequirements> sparse_reqs(requirements_count);
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, sparse_reqs.data());
VkExtent3D granularity = sparse_reqs[0].formatProperties.imageGranularity;
VkSparseImageMemoryBind image_bind{};
image_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_bind.memory = image_mem.handle();
image_bind.memoryOffset = 0;
image_bind.extent = granularity;
VkSparseImageMemoryBindInfo image_bind_info{};
image_bind_info.image = image.handle();
image_bind_info.bindCount = 1u;
image_bind_info.pBinds = &image_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.imageBindCount = 1u;
bind_info.pImageBinds = &image_bind_info;
VkQueue sparse_queue = m_device->QueuesWithSparseCapability()[0]->handle();
// Force memoryOffset to invalid value
image_bind.memoryOffset = image_mem_reqs.alignment + 1;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-memory-01105");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.memoryOffset = 0;
}
TEST_F(NegativeSparseImage, ImageMemoryBindInvalidExtent) {
TEST_DESCRIPTION("Try to bind sparse resident image with an extent having a null size on one of its dimension");
AddRequiredFeature(vkt::Feature::sparseBinding);
AddRequiredFeature(vkt::Feature::sparseResidencyImage3D);
RETURN_IF_SKIP(Init());
if (m_device->QueuesWithSparseCapability().empty()) {
GTEST_SKIP() << "Required SPARSE_BINDING queue families not present";
}
VkImageCreateInfo create_info = vkt::Image::CreateInfo();
create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
create_info.imageType = VK_IMAGE_TYPE_3D;
create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
create_info.extent.width = 1024;
create_info.extent.height = 1024;
create_info.extent.depth = 1;
create_info.arrayLayers = 1;
vkt::Image image(*m_device, create_info, vkt::no_mem);
VkMemoryRequirements image_mem_reqs;
vk::GetImageMemoryRequirements(m_device->handle(), image.handle(), &image_mem_reqs);
const auto image_mem_alloc =
vkt::DeviceMemory::GetResourceAllocInfo(*m_device, image_mem_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vkt::DeviceMemory image_mem;
image_mem.init(*m_device, image_mem_alloc);
uint32_t requirements_count = 0u;
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, nullptr);
if (requirements_count == 0u) {
GTEST_SKIP() << "No sparse image requirements for image format VK_FORMAT_B8G8R8A8_UNORM";
}
std::vector<VkSparseImageMemoryRequirements> sparse_reqs(requirements_count);
vk::GetImageSparseMemoryRequirements(m_device->handle(), image.handle(), &requirements_count, sparse_reqs.data());
VkExtent3D granularity = sparse_reqs[0].formatProperties.imageGranularity;
VkSparseImageMemoryBind image_bind{};
image_bind.subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_bind.memory = image_mem.handle();
image_bind.extent = granularity;
VkSparseImageMemoryBindInfo image_bind_info{};
image_bind_info.image = image.handle();
image_bind_info.bindCount = 1u;
image_bind_info.pBinds = &image_bind;
VkBindSparseInfo bind_info = vku::InitStructHelper();
bind_info.imageBindCount = 1u;
bind_info.pImageBinds = &image_bind_info;
VkQueue sparse_queue = m_device->QueuesWithSparseCapability()[0]->handle();
image_bind.extent.width = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-extent-09388");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.extent = granularity;
image_bind.extent.height = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-extent-09389");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
image_bind.extent = granularity;
image_bind.extent.depth = 0;
m_errorMonitor->SetDesiredError("VUID-VkSparseImageMemoryBind-extent-09390");
vk::QueueBindSparse(sparse_queue, 1, &bind_info, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSparseImage, UnalignedBindOffsets) {
TEST_DESCRIPTION("VkSparseMemoryBind have unaligned memory offset and resource offset");
AddRequiredFeature(vkt::Feature::sparseBinding);
RETURN_IF_SKIP(Init());
auto index = m_device->graphics_queue_node_index_;
if (!(m_device->Physical().queue_properties_[index].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)) {
GTEST_SKIP() << "Graphics queue does not have sparse binding bit";
}
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
image_create_info.extent.width = 1024;
image_create_info.extent.height = 1024;
image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
vkt::Image image(*m_device, image_create_info, vkt::no_mem);
VkMemoryRequirements memory_reqs;
vk::GetImageMemoryRequirements(device(), image, &memory_reqs);
if (memory_reqs.alignment == 1) {
GTEST_SKIP() << "Need image memory required alignment to be more than 1";
}
// Find an image big enough to allow sparse mapping of 2 memory regions
// Increase the image size until it is at least twice the
// size of the required alignment, to ensure we can bind both
// allocated memory blocks to the image on aligned offsets.
while (memory_reqs.size < (memory_reqs.alignment * 2)) {
image.destroy();
image_create_info.extent.width *= 2;
image_create_info.extent.height *= 2;
image.InitNoMemory(*m_device, image_create_info);
vk::GetImageMemoryRequirements(device(), image, &memory_reqs);
}
// Allocate 2 memory regions of minimum alignment size, bind one at 0, the other
// at the end of the first
VkMemoryAllocateInfo memory_info = vku::InitStructHelper();
memory_info.allocationSize = 2 * memory_reqs.alignment;
bool pass = m_device->Physical().SetMemoryType(memory_reqs.memoryTypeBits, &memory_info, 0);
ASSERT_TRUE(pass);
vkt::DeviceMemory memory_one(*m_device, memory_info);
vkt::DeviceMemory memory_two(*m_device, memory_info);
std::array<VkSparseMemoryBind, 2> binds = {};
binds[0].memory = memory_one;
binds[0].memoryOffset = 1;
binds[0].resourceOffset = memory_info.allocationSize / 2;
binds[0].size = memory_info.allocationSize / 2;
binds[1].memory = memory_two;
binds[1].memoryOffset = 0;
binds[1].resourceOffset = 1;
binds[1].size = memory_info.allocationSize / 2;
VkSparseImageOpaqueMemoryBindInfo opaqueBindInfo;
opaqueBindInfo.image = image;
opaqueBindInfo.bindCount = size32(binds);
opaqueBindInfo.pBinds = binds.data();
VkBindSparseInfo bindSparseInfo = vku::InitStructHelper();
bindSparseInfo.imageOpaqueBindCount = 1;
bindSparseInfo.pImageOpaqueBinds = &opaqueBindInfo;
// Unaligned memory bind offset
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-memory-01096");
// Unaligned memory bind offset, and resource offset
m_errorMonitor->SetDesiredError("VUID-VkSparseMemoryBind-resourceOffset-09492", 2);
vk::QueueBindSparse(m_default_queue->handle(), 1, &bindSparseInfo, VK_NULL_HANDLE);
m_errorMonitor->VerifyFound();
// Wait for operations to finish before destroying anything
m_default_queue->Wait();
}