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;
                             }