layers:Add BINDABLE superclass for image/buffer state
Support for sparse memory binding is virually non-existant in current
validation layers. This is a first step down a long path to improve
sparse binding support.
The BINDABLE superclass is sub-classed by IMAGE & BUFFER state objs
and contains memory binding info in both the single binding and
sparse binding cases.
Bindings are initially encapsulated in the new MEM_BINDING struct.
For single binding cases, things are almost exactly the same as they
have always been. For sparse binding, the various bindings will be
stored in the sparse_bindings set where we initally don't have complete
information on the sparse binding as we only store the MEM_BINDING info
but this is a start.
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp
index 4e82a47..ecde405 100644
--- a/layers/core_validation.cpp
+++ b/layers/core_validation.cpp
@@ -403,14 +403,14 @@
auto image_state = getImageState(my_data, VkImage(handle));
*sparse = image_state->createInfo.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
if (image_state)
- return &image_state->mem;
+ return &image_state->binding.mem;
break;
}
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: {
auto buff_node = getBufferNode(my_data, VkBuffer(handle));
*sparse = buff_node->createInfo.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT;
if (buff_node)
- return &buff_node->mem;
+ return &buff_node->binding.mem;
break;
}
default:
@@ -545,22 +545,22 @@
// If mem is special swapchain key, then verify that image_state valid member is true
// Else verify that the image's bound memory range is valid
static bool ValidateImageMemoryIsValid(layer_data *dev_data, IMAGE_STATE *image_state, const char *functionName) {
- if (image_state->mem == MEMTRACKER_SWAP_CHAIN_IMAGE_KEY) {
+ if (image_state->binding.mem == MEMTRACKER_SWAP_CHAIN_IMAGE_KEY) {
if (!image_state->valid) {
return log_msg(dev_data->report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT,
- reinterpret_cast<uint64_t &>(image_state->mem), __LINE__, MEMTRACK_INVALID_MEM_REGION, "MEM",
+ reinterpret_cast<uint64_t &>(image_state->binding.mem), __LINE__, MEMTRACK_INVALID_MEM_REGION, "MEM",
"%s: Cannot read invalid swapchain image 0x%" PRIx64 ", please fill the memory before using.",
functionName, reinterpret_cast<uint64_t &>(image_state->image));
}
} else {
- return ValidateMemoryIsValid(dev_data, image_state->mem, reinterpret_cast<uint64_t &>(image_state->image),
+ return ValidateMemoryIsValid(dev_data, image_state->binding.mem, reinterpret_cast<uint64_t &>(image_state->image),
VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, functionName);
}
return false;
}
// For given buffer_node, verify that the range it's bound to is valid
static bool ValidateBufferMemoryIsValid(layer_data *dev_data, BUFFER_NODE *buffer_node, const char *functionName) {
- return ValidateMemoryIsValid(dev_data, buffer_node->mem, reinterpret_cast<uint64_t &>(buffer_node->buffer),
+ return ValidateMemoryIsValid(dev_data, buffer_node->binding.mem, reinterpret_cast<uint64_t &>(buffer_node->buffer),
VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, functionName);
}
// For the given memory allocation, set the range bound by the given handle object to the valid param value
@@ -574,15 +574,15 @@
// If mem is special swapchain key, then set entire image_state to valid param value
// Else set the image's bound memory range to valid param value
static void SetImageMemoryValid(layer_data *dev_data, IMAGE_STATE *image_state, bool valid) {
- if (image_state->mem == MEMTRACKER_SWAP_CHAIN_IMAGE_KEY) {
+ if (image_state->binding.mem == MEMTRACKER_SWAP_CHAIN_IMAGE_KEY) {
image_state->valid = valid;
} else {
- SetMemoryValid(dev_data, image_state->mem, reinterpret_cast<uint64_t &>(image_state->image), valid);
+ SetMemoryValid(dev_data, image_state->binding.mem, reinterpret_cast<uint64_t &>(image_state->image), valid);
}
}
// For given buffer node set the buffer's bound memory range to valid param value
static void SetBufferMemoryValid(layer_data *dev_data, BUFFER_NODE *buffer_node, bool valid) {
- SetMemoryValid(dev_data, buffer_node->mem, reinterpret_cast<uint64_t &>(buffer_node->buffer), valid);
+ SetMemoryValid(dev_data, buffer_node->binding.mem, reinterpret_cast<uint64_t &>(buffer_node->buffer), valid);
}
// Find CB Info and add mem reference to list container
// Find Mem Obj Info and add CB reference to list container
@@ -617,13 +617,13 @@
// Create binding link between given image node and command buffer node
void AddCommandBufferBindingImage(const layer_data *dev_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *image_state) {
// Skip validation if this image was created through WSI
- if (image_state->mem != MEMTRACKER_SWAP_CHAIN_IMAGE_KEY) {
+ if (image_state->binding.mem != MEMTRACKER_SWAP_CHAIN_IMAGE_KEY) {
// First update CB binding in MemObj mini CB list
- DEVICE_MEM_INFO *pMemInfo = getMemObjInfo(dev_data, image_state->mem);
+ DEVICE_MEM_INFO *pMemInfo = getMemObjInfo(dev_data, image_state->binding.mem);
if (pMemInfo) {
pMemInfo->command_buffer_bindings.insert(cb_node->commandBuffer);
// Now update CBInfo's Mem reference list
- cb_node->memObjs.insert(image_state->mem);
+ cb_node->memObjs.insert(image_state->binding.mem);
}
// Now update cb binding for image
cb_node->object_bindings.insert({reinterpret_cast<uint64_t &>(image_state->image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT});
@@ -647,11 +647,11 @@
// Create binding link between given buffer node and command buffer node
void AddCommandBufferBindingBuffer(const layer_data *dev_data, GLOBAL_CB_NODE *cb_node, BUFFER_NODE *buff_node) {
// First update CB binding in MemObj mini CB list
- DEVICE_MEM_INFO *pMemInfo = getMemObjInfo(dev_data, buff_node->mem);
+ DEVICE_MEM_INFO *pMemInfo = getMemObjInfo(dev_data, buff_node->binding.mem);
if (pMemInfo) {
pMemInfo->command_buffer_bindings.insert(cb_node->commandBuffer);
// Now update CBInfo's Mem reference list
- cb_node->memObjs.insert(buff_node->mem);
+ cb_node->memObjs.insert(buff_node->binding.mem);
cb_node->object_bindings.insert({reinterpret_cast<uint64_t &>(buff_node->buffer), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT});
}
// Now update cb binding for buffer
@@ -725,13 +725,13 @@
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: {
auto image_state = getImageState(dev_data, reinterpret_cast<VkImage &>(obj.handle));
assert(image_state); // Any destroyed images should already be removed from bindings
- image_state->mem = MEMORY_UNBOUND;
+ image_state->binding.mem = MEMORY_UNBOUND;
break;
}
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: {
auto buff_node = getBufferNode(dev_data, reinterpret_cast<VkBuffer &>(obj.handle));
assert(buff_node); // Any destroyed buffers should already be removed from bindings
- buff_node->mem = MEMORY_UNBOUND;
+ buff_node->binding.mem = MEMORY_UNBOUND;
break;
}
default:
@@ -834,7 +834,7 @@
bool ValidateMemoryIsBoundToImage(const layer_data *dev_data, const IMAGE_STATE *image_state, const char *api_name) {
bool result = false;
if (0 == (static_cast<uint32_t>(image_state->createInfo.flags) & VK_IMAGE_CREATE_SPARSE_BINDING_BIT)) {
- result = VerifyBoundMemoryIsValid(dev_data, image_state->mem, reinterpret_cast<const uint64_t &>(image_state->image),
+ result = VerifyBoundMemoryIsValid(dev_data, image_state->binding.mem, reinterpret_cast<const uint64_t &>(image_state->image),
api_name, "Image");
}
return result;
@@ -844,8 +844,8 @@
bool ValidateMemoryIsBoundToBuffer(const layer_data *dev_data, const BUFFER_NODE *buffer_node, const char *api_name) {
bool result = false;
if (0 == (static_cast<uint32_t>(buffer_node->createInfo.flags) & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)) {
- result = VerifyBoundMemoryIsValid(dev_data, buffer_node->mem, reinterpret_cast<const uint64_t &>(buffer_node->buffer),
- api_name, "Buffer");
+ result = VerifyBoundMemoryIsValid(dev_data, buffer_node->binding.mem,
+ reinterpret_cast<const uint64_t &>(buffer_node->buffer), api_name, "Buffer");
}
return result;
}
@@ -935,10 +935,10 @@
*mem = VK_NULL_HANDLE;
switch (type) {
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT:
- *mem = getImageState(dev_data, VkImage(handle))->mem;
+ *mem = getImageState(dev_data, VkImage(handle))->binding.mem;
break;
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT:
- *mem = getBufferNode(dev_data, VkBuffer(handle))->mem;
+ *mem = getBufferNode(dev_data, VkBuffer(handle))->binding.mem;
break;
default:
assert(0);
@@ -5770,7 +5770,7 @@
// Any bound cmd buffers are now invalid
invalidateCommandBuffers(buff_node->cb_bindings,
{reinterpret_cast<uint64_t &>(buff_node->buffer), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT});
- auto mem_info = getMemObjInfo(dev_data, buff_node->mem);
+ auto mem_info = getMemObjInfo(dev_data, buff_node->binding.mem);
if (mem_info) {
RemoveBufferMemoryRange(reinterpret_cast<uint64_t &>(buffer), mem_info);
}
@@ -5834,7 +5834,7 @@
static void PostCallRecordDestroyImage(layer_data *dev_data, VkImage image, IMAGE_STATE *image_state, VK_OBJECT obj_struct) {
invalidateCommandBuffers(image_state->cb_bindings, obj_struct);
// Clean up memory mapping, bindings and range references for image
- auto mem_info = getMemObjInfo(dev_data, image_state->mem);
+ auto mem_info = getMemObjInfo(dev_data, image_state->binding.mem);
if (mem_info) {
RemoveImageMemoryRange(obj_struct.handle, mem_info);
clear_object_binding(dev_data, obj_struct.handle, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT);
@@ -5891,9 +5891,9 @@
if (buffer_node) {
VkMemoryRequirements memRequirements;
dev_data->dispatch_table.GetBufferMemoryRequirements(device, buffer, &memRequirements);
- buffer_node->mem = mem;
- buffer_node->memOffset = memoryOffset;
- buffer_node->memSize = memRequirements.size;
+ buffer_node->binding.mem = mem;
+ buffer_node->binding.offset = memoryOffset;
+ buffer_node->binding.size = memRequirements.size;
// Track and validate bound memory range information
auto mem_info = getMemObjInfo(dev_data, mem);
@@ -9037,7 +9037,7 @@
auto buffer_node = getBufferNode(dev_data, mem_barrier->buffer);
if (buffer_node) {
- auto buffer_size = buffer_node->memSize;
+ auto buffer_size = buffer_node->binding.size;
if (mem_barrier->offset >= buffer_size) {
skip_call |= log_msg(
dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT)0, 0, __LINE__,
@@ -9623,7 +9623,7 @@
continue;
}
MT_FB_ATTACHMENT_INFO fb_info;
- fb_info.mem = getImageState(dev_data, view_state->create_info.image)->mem;
+ fb_info.mem = getImageState(dev_data, view_state->create_info.image)->binding.mem;
fb_info.view_state = view_state;
fb_info.image = view_state->create_info.image;
fb_state->attachments.push_back(fb_info);
@@ -9782,8 +9782,9 @@
if (!image_data_i || !image_data_j) {
continue;
}
- if (image_data_i->mem == image_data_j->mem && isRangeOverlapping(image_data_i->memOffset, image_data_i->memSize,
- image_data_j->memOffset, image_data_j->memSize)) {
+ if (image_data_i->binding.mem == image_data_j->binding.mem &&
+ isRangeOverlapping(image_data_i->binding.offset, image_data_i->binding.size, image_data_j->binding.offset,
+ image_data_j->binding.size)) {
overlapping_attachments[i].push_back(j);
overlapping_attachments[j].push_back(i);
}
@@ -11099,9 +11100,9 @@
if (!skip_call) {
result = dev_data->dispatch_table.BindImageMemory(device, image, mem, memoryOffset);
lock.lock();
- image_state->mem = mem;
- image_state->memOffset = memoryOffset;
- image_state->memSize = memRequirements.size;
+ image_state->binding.mem = mem;
+ image_state->binding.offset = memoryOffset;
+ image_state->binding.size = memRequirements.size;
lock.unlock();
}
} else {
@@ -11413,7 +11414,7 @@
dev_data->imageMap[pSwapchainImages[i]] = unique_ptr<IMAGE_STATE>(new IMAGE_STATE(pSwapchainImages[i], &image_ci));
auto &image_state = dev_data->imageMap[pSwapchainImages[i]];
image_state->valid = false;
- image_state->mem = MEMTRACKER_SWAP_CHAIN_IMAGE_KEY;
+ image_state->binding.mem = MEMTRACKER_SWAP_CHAIN_IMAGE_KEY;
swapchain_node->images.push_back(pSwapchainImages[i]);
ImageSubresourcePair subpair = {pSwapchainImages[i], false, VkImageSubresource()};
dev_data->imageSubresourceMap[pSwapchainImages[i]].push_back(subpair);
diff --git a/layers/core_validation_types.h b/layers/core_validation_types.h
index debc29c..6539d83 100644
--- a/layers/core_validation_types.h
+++ b/layers/core_validation_types.h
@@ -146,15 +146,46 @@
}
};
-class BUFFER_NODE : public BASE_NODE {
+// Generic memory binding struct to track objects bound to objects
+struct MEM_BINDING {
+ VkDeviceMemory mem;
+ VkDeviceSize offset;
+ VkDeviceSize size;
+};
+
+inline bool operator==(MEM_BINDING a, MEM_BINDING b) NOEXCEPT { return a.mem == b.mem && a.offset == b.offset && a.size == b.size; }
+
+namespace std {
+template <> struct hash<MEM_BINDING> {
+ size_t operator()(MEM_BINDING mb) const NOEXCEPT {
+ auto intermediate = hash<uint64_t>()(reinterpret_cast<uint64_t &>(mb.mem)) ^ hash<uint64_t>()(mb.offset);
+ return intermediate ^ hash<uint64_t>()(mb.size);
+ }
+};
+}
+
+// Superclass for bindable object state (currently imagesa and buffers)
+class BINDABLE : public BASE_NODE {
+ public:
+ bool sparse; // Is this object being bound with sparse memory or not?
+ // Non-sparse binding data
+ MEM_BINDING binding;
+ // Sparse binding data, initially just tracking MEM_BINDING per mem object
+ // There's more data for sparse bindings so need better long-term solution
+ // TODO : Need to update solution to track all sparse binding data
+ std::unordered_set<MEM_BINDING> sparse_bindings;
+ BINDABLE() : sparse(false), binding{}, sparse_bindings{}{};
+};
+
+class BUFFER_NODE : public BINDABLE {
public:
VkBuffer buffer;
- VkDeviceMemory mem;
- VkDeviceSize memOffset;
- VkDeviceSize memSize; // Note: may differ from createInfo::size
VkBufferCreateInfo createInfo;
- BUFFER_NODE(VkBuffer buff, const VkBufferCreateInfo *pCreateInfo)
- : buffer(buff), mem(VK_NULL_HANDLE), memOffset(0), memSize(0), createInfo(*pCreateInfo){};
+ BUFFER_NODE(VkBuffer buff, const VkBufferCreateInfo *pCreateInfo) : buffer(buff), createInfo(*pCreateInfo) {
+ if (createInfo.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) {
+ sparse = true;
+ }
+ };
BUFFER_NODE(BUFFER_NODE const &rh_obj) = delete;
};
@@ -174,17 +205,18 @@
SAMPLER_NODE(const VkSampler *ps, const VkSamplerCreateInfo *pci) : sampler(*ps), createInfo(*pci){};
};
-class IMAGE_STATE : public BASE_NODE {
+class IMAGE_STATE : public BINDABLE {
public:
VkImage image;
VkImageCreateInfo createInfo;
- VkDeviceMemory mem;
bool valid; // If this is a swapchain image backing memory track valid here as it doesn't have DEVICE_MEM_INFO
bool acquired; // If this is a swapchain image, has it been acquired by the app.
- VkDeviceSize memOffset;
- VkDeviceSize memSize;
IMAGE_STATE(VkImage img, const VkImageCreateInfo *pCreateInfo)
- : image(img), createInfo(*pCreateInfo), mem(VK_NULL_HANDLE), valid(false), acquired(false), memOffset(0), memSize(0){};
+ : image(img), createInfo(*pCreateInfo), valid(false), acquired(false) {
+ if (createInfo.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) {
+ sparse = true;
+ }
+ };
IMAGE_STATE(IMAGE_STATE const &rh_obj) = delete;
};
diff --git a/layers/descriptor_sets.cpp b/layers/descriptor_sets.cpp
index a5f6b0c..1e22f82 100644
--- a/layers/descriptor_sets.cpp
+++ b/layers/descriptor_sets.cpp
@@ -386,12 +386,12 @@
*error = error_str.str();
return false;
} else {
- auto mem_entry = getMemObjInfo(device_data_, buffer_node->mem);
+ auto mem_entry = getMemObjInfo(device_data_, buffer_node->binding.mem);
if (!mem_entry) {
std::stringstream error_str;
error_str << "Descriptor in binding #" << binding << " at global descriptor index " << i
- << " uses buffer " << buffer << " that references invalid memory " << buffer_node->mem
- << ".";
+ << " uses buffer " << buffer << " that references invalid memory "
+ << buffer_node->binding.mem << ".";
*error = error_str.str();
return false;
}