layers: Dehandle the image layout maps

Change the keys in the CMD_BUFFER_STATE and CoreChecks image layout
maps to be IMAGE_STATE* rather than VkImage. Layout validation
requires the IMAGE_STATE object so this saves many lookups in cases
where the entire maps are processed. The CMD_BUFFER_STATE layout
map can be kept up to date by removing entries that are being
destroyed in the NotifyInvalidate() callback.
diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp
index f89a2d3..e918cc2 100644
--- a/layers/buffer_validation.cpp
+++ b/layers/buffer_validation.cpp
@@ -334,7 +334,7 @@
 uint32_t FullMipChainLevels(VkExtent2D extent) { return FullMipChainLevels(extent.height, extent.width); }
 
 bool CoreChecks::FindLayouts(const IMAGE_STATE &image_state, std::vector<VkImageLayout> &layouts) const {
-    const auto *layout_range_map = GetLayoutRangeMap(imageLayoutMap, image_state.image());
+    const auto *layout_range_map = GetLayoutRangeMap(imageLayoutMap, image_state);
     if (!layout_range_map) return false;
     // TODO: FindLayouts function should mutate into a ValidatePresentableLayout with the loop wrapping the LogError
     //       from the caller. You can then use decode to add the subresource of the range::begin to the error message.
@@ -569,7 +569,7 @@
                         // Cast pCB to const because we don't want to create entries that don't exist here (in case the key changes to something
                         // in common with the non-const version.)
                         // The lookup is expensive, so cache it.
-                        subresource_map = const_p_cb->GetImageSubresourceLayoutMap(image);
+                        subresource_map = const_p_cb->GetImageSubresourceLayoutMap(*image_state);
                         has_queried_map = true;
                     }
 
@@ -871,13 +871,13 @@
             if (img_barrier.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
                 // TODO: Set memory invalid which is in mem_tracker currently
             } else if (!QueueFamilyIsExternal(img_barrier.srcQueueFamilyIndex)) {
-                auto &write_subresource_map = layout_updates[img_barrier.image];
+                auto &write_subresource_map = layout_updates[image_state];
                 bool new_write = false;
                 if (!write_subresource_map) {
                     write_subresource_map.emplace(*image_state);
                     new_write = true;
                 }
-                const auto &current_subresource_map = current_map.find(img_barrier.image);
+                const auto &current_subresource_map = current_map.find(image_state);
                 const auto &read_subresource_map = (new_write && current_subresource_map != current_map.end())
                                                        ? (*current_subresource_map).second
                                                        : write_subresource_map;
@@ -1384,10 +1384,9 @@
     if (disabled[image_layout_validation]) return false;
     assert(cb_node);
     assert(image_state);
-    const auto image = image_state->image();
     bool skip = false;
 
-    const auto *subresource_map = cb_node->GetImageSubresourceLayoutMap(image);
+    const auto *subresource_map = cb_node->GetImageSubresourceLayoutMap(*image_state);
     if (subresource_map) {
         bool subres_skip = false;
         LayoutUseCheckAndMessage layout_check(subresource_map, aspect_mask);
@@ -1396,12 +1395,13 @@
         for (auto pos = subresource_map->Find(range); !(pos.AtEnd()) && !subres_skip; pos.IncrementInterval()) {
             if (!layout_check.Check(pos->subresource, explicit_layout, pos->current_layout, pos->initial_layout)) {
                 *error = true;
-                subres_skip |= LogError(cb_node->commandBuffer(), layout_mismatch_msg_code,
-                                        "%s: Cannot use %s (layer=%u mip=%u) with specific layout %s that doesn't match the "
-                                        "%s layout %s.",
-                                        caller, report_data->FormatHandle(image).c_str(), pos->subresource.arrayLayer,
-                                        pos->subresource.mipLevel, string_VkImageLayout(explicit_layout), layout_check.message,
-                                        string_VkImageLayout(layout_check.layout));
+                subres_skip |=
+                    LogError(cb_node->commandBuffer(), layout_mismatch_msg_code,
+                             "%s: Cannot use %s (layer=%u mip=%u) with specific layout %s that doesn't match the "
+                             "%s layout %s.",
+                             caller, report_data->FormatHandle(image_state->Handle()).c_str(), pos->subresource.arrayLayer,
+                             pos->subresource.mipLevel, string_VkImageLayout(explicit_layout), layout_check.message,
+                             string_VkImageLayout(layout_check.layout));
             }
         }
         skip |= subres_skip;
@@ -1414,7 +1414,8 @@
                 // LAYOUT_GENERAL is allowed, but may not be performance optimal, flag as perf warning.
                 skip |= LogPerformanceWarning(cb_node->commandBuffer(), kVUID_Core_DrawState_InvalidImageLayout,
                                               "%s: For optimal performance %s layout should be %s instead of GENERAL.", caller,
-                                              report_data->FormatHandle(image).c_str(), string_VkImageLayout(optimal_layout));
+                                              report_data->FormatHandle(image_state->Handle()).c_str(),
+                                              string_VkImageLayout(optimal_layout));
             }
         } else if (IsExtEnabled(device_extensions.vk_khr_shared_presentable_image)) {
             if (image_state->shared_presentable) {
@@ -1429,7 +1430,7 @@
             *error = true;
             skip |= LogError(cb_node->commandBuffer(), layout_invalid_msg_code,
                              "%s: Layout for %s is %s but can only be %s or VK_IMAGE_LAYOUT_GENERAL.", caller,
-                             report_data->FormatHandle(image).c_str(), string_VkImageLayout(explicit_layout),
+                             report_data->FormatHandle(image_state->Handle()).c_str(), string_VkImageLayout(explicit_layout),
                              string_VkImageLayout(optimal_layout));
         }
     }
@@ -2079,9 +2080,10 @@
 
 void CoreChecks::PreCallRecordDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator) {
     // Clean up validation specific data
+    auto *image_state = Get<IMAGE_STATE>(image);
     qfo_release_image_barrier_map.erase(image);
 
-    imageLayoutMap.erase(image);
+    imageLayoutMap.erase(image_state);
 
     // Clean up generic image state
     StateTracker::PreCallRecordDestroyImage(device, image, pAllocator);
@@ -2148,7 +2150,7 @@
     }
 
     // Cast to const to prevent creation at validate time.
-    const auto *subresource_map = cb_node->GetImageSubresourceLayoutMap(image_state->image());
+    const auto *subresource_map = cb_node->GetImageSubresourceLayoutMap(*image_state);
     if (subresource_map) {
         bool subres_skip = false;
         LayoutUseCheckAndMessage layout_check(subresource_map);
@@ -4203,15 +4205,15 @@
 
 GlobalImageLayoutRangeMap *GetLayoutRangeMap(GlobalImageLayoutMap &map, const IMAGE_STATE &image_state) {
     // This approach allows for a single hash lookup or/create new
-    auto &layout_map = map[image_state.image()];
+    auto &layout_map = map[&image_state];
     if (!layout_map) {
         layout_map.emplace(image_state.subresource_encoder.SubresourceCount());
     }
     return &layout_map;
 }
 
-const GlobalImageLayoutRangeMap *GetLayoutRangeMap(const GlobalImageLayoutMap &map, VkImage image) {
-    auto it = map.find(image);
+const GlobalImageLayoutRangeMap *GetLayoutRangeMap(const GlobalImageLayoutMap &map, const IMAGE_STATE &image_state) {
+    auto it = map.find(&image_state);
     if (it != map.end()) {
         return &it->second;
     }
@@ -4246,16 +4248,14 @@
     // Iterate over the layout maps for each referenced image
     GlobalImageLayoutRangeMap empty_map(1);
     for (const auto &layout_map_entry : pCB->image_layout_map) {
-        const auto image = layout_map_entry.first;
-        const auto *image_state = GetImageState(image);
-        if (!image_state) continue;  // Can't check layouts of a dead image
+        const auto *image_state = layout_map_entry.first;
         const auto &subres_map = layout_map_entry.second;
         const auto &layout_map = subres_map->GetLayoutMap();
         // Validate the initial_uses for each subresource referenced
         if (layout_map.empty()) continue;
 
         auto *overlay_map = GetLayoutRangeMap(overlayLayoutMap, *image_state);
-        const auto *global_map = GetLayoutRangeMap(globalImageLayoutMap, image);
+        const auto *global_map = GetLayoutRangeMap(globalImageLayoutMap, *image_state);
         if (global_map == nullptr) {
             global_map = &empty_map;
         }
@@ -4297,7 +4297,7 @@
                             "%s command buffer %s expects %s (subresource: aspectMask 0x%X array layer %u, mip level %u) "
                             "to be in layout %s--instead, current layout is %s.",
                             loc.Message().c_str(), report_data->FormatHandle(pCB->commandBuffer()).c_str(),
-                            report_data->FormatHandle(image).c_str(), subresource.aspectMask, subresource.arrayLayer,
+                            report_data->FormatHandle(image_state->Handle()).c_str(), subresource.aspectMask, subresource.arrayLayer,
                             subresource.mipLevel, string_VkImageLayout(initial_layout), string_VkImageLayout(image_layout));
                     }
                 }
@@ -4320,10 +4320,8 @@
 
 void CoreChecks::UpdateCmdBufImageLayouts(CMD_BUFFER_STATE *pCB) {
     for (const auto &layout_map_entry : pCB->image_layout_map) {
-        const auto image = layout_map_entry.first;
+        const auto *image_state = layout_map_entry.first;
         const auto &subres_map = layout_map_entry.second;
-        const auto *image_state = GetImageState(image);
-        if (!image_state) continue;  // Can't set layouts of a dead image
         auto *global_map = GetLayoutRangeMap(imageLayoutMap, *image_state);
         sparse_container::splice(*global_map, subres_map->GetLayoutMap(), GlobalLayoutUpdater());
     }
diff --git a/layers/buffer_validation.h b/layers/buffer_validation.h
index 7f4f3b9..d8284ba 100644
--- a/layers/buffer_validation.h
+++ b/layers/buffer_validation.h
@@ -34,6 +34,6 @@
 uint32_t FullMipChainLevels(VkExtent2D);
 
 GlobalImageLayoutRangeMap *GetLayoutRangeMap(GlobalImageLayoutMap &map, const IMAGE_STATE &image_state);
-const GlobalImageLayoutRangeMap *GetLayoutRangeMap(const GlobalImageLayoutMap &map, VkImage image);
+const GlobalImageLayoutRangeMap *GetLayoutRangeMap(const GlobalImageLayoutMap &map, const IMAGE_STATE &image);
 
 #endif  // CORE_VALIDATION_BUFFER_VALIDATION_H_
diff --git a/layers/cmd_buffer_state.cpp b/layers/cmd_buffer_state.cpp
index 1c6b486..1c77a16 100644
--- a/layers/cmd_buffer_state.cpp
+++ b/layers/cmd_buffer_state.cpp
@@ -466,8 +466,15 @@
     if (unlink) {
         for (auto *obj : invalid_nodes) {
             object_bindings.erase(obj);
-            if (obj->Type() == kVulkanObjectTypeCommandBuffer) {
-                linkedCommandBuffers.erase(static_cast<CMD_BUFFER_STATE *>(obj));
+            switch (obj->Type()) {
+                case kVulkanObjectTypeCommandBuffer:
+                    linkedCommandBuffers.erase(static_cast<CMD_BUFFER_STATE *>(obj));
+                    break;
+                case kVulkanObjectTypeImage:
+                    image_layout_map.erase(static_cast<IMAGE_STATE *>(obj));
+                    break;
+                default:
+                    break;
             }
         }
     }
@@ -477,8 +484,8 @@
 const CommandBufferImageLayoutMap& CMD_BUFFER_STATE::GetImageSubresourceLayoutMap() const { return image_layout_map; }
 
 // The const variant only need the image as it is the key for the map
-const ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(VkImage image) const {
-    auto it = image_layout_map.find(image);
+const ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state) const {
+    auto it = image_layout_map.find(&image_state);
     if (it == image_layout_map.cend()) {
         return nullptr;
     }
@@ -487,7 +494,7 @@
 
 // The non-const variant only needs the image state, as the factory requires it to construct a new entry
 ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state) {
-    auto &layout_map = image_layout_map[image_state.image()];
+    auto &layout_map = image_layout_map[&image_state];
     if (!layout_map) {
         // Was an empty slot... fill it in.
         layout_map.emplace(image_state);
@@ -763,9 +770,7 @@
         // ValidationStateTracker these maps will be empty, so leaving the propagation in the the state tracker should be a no-op
         // for those other classes.
         for (const auto &sub_layout_map_entry : sub_cb_state->image_layout_map) {
-            const auto image = sub_layout_map_entry.first;
-            const IMAGE_STATE *image_state = dev_data->GetImageState(image);
-            if (!image_state) continue;                // Can't set layouts of a dead image
+            const auto *image_state = sub_layout_map_entry.first;
 
             auto *cb_subres_map = GetImageSubresourceLayoutMap(*image_state);
             const auto *sub_cb_subres_map = &sub_layout_map_entry.second;
diff --git a/layers/cmd_buffer_state.h b/layers/cmd_buffer_state.h
index eebb1b1..40bff88 100644
--- a/layers/cmd_buffer_state.h
+++ b/layers/cmd_buffer_state.h
@@ -167,11 +167,7 @@
     std::vector<BufferBinding> vertex_buffer_bindings;
 };
 
-typedef subresource_adapter::BothRangeMap<VkImageLayout, 16> GlobalImageLayoutRangeMap;
-typedef layer_data::unordered_map<VkImage, layer_data::optional<GlobalImageLayoutRangeMap>> GlobalImageLayoutMap;
-
-typedef layer_data::unordered_map<VkImage, layer_data::optional<ImageSubresourceLayoutMap>> CommandBufferImageLayoutMap;
-
+typedef layer_data::unordered_map<const IMAGE_STATE *, layer_data::optional<ImageSubresourceLayoutMap>> CommandBufferImageLayoutMap;
 
 class CMD_BUFFER_STATE : public REFCOUNTED_NODE {
   public:
@@ -344,9 +340,9 @@
 
     void ResetPushConstantDataIfIncompatible(const PIPELINE_LAYOUT_STATE *pipeline_layout_state);
 
+    const ImageSubresourceLayoutMap *GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state) const;
     ImageSubresourceLayoutMap *GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state);
     const CommandBufferImageLayoutMap& GetImageSubresourceLayoutMap() const;
-    const ImageSubresourceLayoutMap *GetImageSubresourceLayoutMap(VkImage image) const;
 
     const QFOTransferBarrierSets<QFOImageTransferBarrier> &GetQFOBarrierSets(const QFOImageTransferBarrier &type_tag) const {
         return qfo_transfer_image_barriers;
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp
index 1fc9874..ab3de7f 100644
--- a/layers/core_validation.cpp
+++ b/layers/core_validation.cpp
@@ -252,7 +252,7 @@
         }
         const DEVICE_MEMORY_STATE *mem_info = ValidationStateTracker::GetDevMemState(mem);
         if (mem_info) {
-            const auto prev_binding = mem_binding.MemState();
+            const auto *prev_binding = mem_binding.MemState();
             if (prev_binding) {
                 if (!prev_binding->Destroyed()) {
                     const char *error_code = nullptr;
@@ -12543,11 +12543,10 @@
         // initial layout usage of secondary command buffers resources must match parent command buffer
         const auto *const_cb_state = static_cast<const CMD_BUFFER_STATE *>(cb_state);
         for (const auto &sub_layout_map_entry : sub_cb_state->image_layout_map) {
-            const auto image = sub_layout_map_entry.first;
-            const auto *image_state = GetImageState(image);
-            if (!image_state) continue;  // Can't set layouts of a dead image
+            const auto *image_state = sub_layout_map_entry.first;
+            const auto image = image_state->image();
 
-            const auto *cb_subres_map = const_cb_state->GetImageSubresourceLayoutMap(image);
+            const auto *cb_subres_map = const_cb_state->GetImageSubresourceLayoutMap(*image_state);
             // Const getter can be null in which case we have nothing to check against for this image...
             if (!cb_subres_map) continue;
 
@@ -14053,7 +14052,7 @@
         if (swapchain_data) {
             for (const auto &swapchain_image : swapchain_data->images) {
                 if (!swapchain_image.image_state) continue;
-                imageLayoutMap.erase(swapchain_image.image_state->image());
+                imageLayoutMap.erase(swapchain_image.image_state);
                 qfo_release_image_barrier_map.erase(swapchain_image.image_state->image());
             }
         }
diff --git a/layers/core_validation.h b/layers/core_validation.h
index dac4979..170b6b2 100644
--- a/layers/core_validation.h
+++ b/layers/core_validation.h
@@ -110,6 +110,9 @@
     const char *base_mip_err, *mip_count_err, *base_layer_err, *layer_count_err;
 };
 
+typedef subresource_adapter::BothRangeMap<VkImageLayout, 16> GlobalImageLayoutRangeMap;
+typedef layer_data::unordered_map<const IMAGE_STATE*, layer_data::optional<GlobalImageLayoutRangeMap>> GlobalImageLayoutMap;
+
 class CoreChecks : public ValidationStateTracker {
   public:
     using StateTracker = ValidationStateTracker;