1 /* Copyright (c) 2015-2019 The Khronos Group Inc. 2 * Copyright (c) 2015-2019 Valve Corporation 3 * Copyright (c) 2015-2019 LunarG, Inc. 4 * Copyright (C) 2015-2019 Google Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * Author: Mark Lobodzinski <mark@lunarg.com> 19 * Author: Jon Ashburn <jon@lunarg.com> 20 * Author: Tobin Ehlis <tobine@google.com> 21 */ 22 23 // Suppress unused warning on Linux 24 #if defined(__GNUC__) 25 #define DECORATE_UNUSED __attribute__((unused)) 26 #else 27 #define DECORATE_UNUSED 28 #endif 29 30 // clang-format off 31 static const char DECORATE_UNUSED *kVUID_ObjectTracker_Info = "UNASSIGNED-ObjectTracker-Info"; 32 static const char DECORATE_UNUSED *kVUID_ObjectTracker_InternalError = "UNASSIGNED-ObjectTracker-InternalError"; 33 static const char DECORATE_UNUSED *kVUID_ObjectTracker_ObjectLeak = "UNASSIGNED-ObjectTracker-ObjectLeak"; 34 static const char DECORATE_UNUSED *kVUID_ObjectTracker_UnknownObject = "UNASSIGNED-ObjectTracker-UnknownObject"; 35 // clang-format on 36 37 #undef DECORATE_UNUSED 38 39 extern uint64_t object_track_index; 40 41 // Object Status -- used to track state of individual objects 42 typedef VkFlags ObjectStatusFlags; 43 enum ObjectStatusFlagBits { 44 OBJSTATUS_NONE = 0x00000000, // No status is set 45 OBJSTATUS_FENCE_IS_SUBMITTED = 0x00000001, // Fence has been submitted 46 OBJSTATUS_VIEWPORT_BOUND = 0x00000002, // Viewport state object has been bound 47 OBJSTATUS_RASTER_BOUND = 0x00000004, // Viewport state object has been bound 48 OBJSTATUS_COLOR_BLEND_BOUND = 0x00000008, // Viewport state object has been bound 49 OBJSTATUS_DEPTH_STENCIL_BOUND = 0x00000010, // Viewport state object has been bound 50 OBJSTATUS_GPU_MEM_MAPPED = 0x00000020, // Memory object is currently mapped 51 OBJSTATUS_COMMAND_BUFFER_SECONDARY = 0x00000040, // Command Buffer is of type SECONDARY 52 OBJSTATUS_CUSTOM_ALLOCATOR = 0x00000080, // Allocated with custom allocator 53 }; 54 55 // Object and state information structure 56 struct ObjTrackState { 57 uint64_t handle; // Object handle (new) 58 VulkanObjectType object_type; // Object type identifier 59 ObjectStatusFlags status; // Object state 60 uint64_t parent_object; // Parent object 61 std::unique_ptr<std::unordered_set<uint64_t> > child_objects; // Child objects (used for VkDescriptorPool only) 62 }; 63 64 // Track Queue information 65 struct ObjTrackQueueInfo { 66 uint32_t queue_node_index; 67 VkQueue queue; 68 }; 69 70 typedef std::unordered_map<uint64_t, ObjTrackState *> object_map_type; 71 72 class ObjectLifetimes : public ValidationObject { 73 public: 74 uint64_t num_objects[kVulkanObjectTypeMax + 1]; 75 uint64_t num_total_objects; 76 // Vector of unordered_maps per object type to hold ObjTrackState info 77 std::vector<object_map_type> object_map; 78 // Special-case map for swapchain images 79 std::unordered_map<uint64_t, ObjTrackState *> swapchainImageMap; 80 // Map of queue information structures, one per queue 81 std::unordered_map<VkQueue, ObjTrackQueueInfo *> queue_info_map; 82 83 std::vector<VkQueueFamilyProperties> queue_family_properties; 84 85 // Constructor for object lifetime tracking ObjectLifetimes()86 ObjectLifetimes() : num_objects{}, num_total_objects(0), object_map{} { object_map.resize(kVulkanObjectTypeMax + 1); } 87 88 bool DeviceReportUndestroyedObjects(VkDevice device, VulkanObjectType object_type, const std::string &error_code); 89 void DeviceDestroyUndestroyedObjects(VkDevice device, VulkanObjectType object_type); 90 void CreateQueue(VkDevice device, VkQueue vkObj); 91 void AddQueueInfo(VkDevice device, uint32_t queue_node_index, VkQueue queue); 92 void ValidateQueueFlags(VkQueue queue, const char *function); 93 void AllocateCommandBuffer(VkDevice device, const VkCommandPool command_pool, const VkCommandBuffer command_buffer, 94 VkCommandBufferLevel level); 95 void AllocateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set); 96 void CreateSwapchainImageObject(VkDevice dispatchable_object, VkImage swapchain_image, VkSwapchainKHR swapchain); 97 bool ReportUndestroyedObjects(VkDevice device, const std::string &error_code); 98 void DestroyUndestroyedObjects(VkDevice device); 99 bool ValidateDeviceObject(uint64_t device_handle, const char *invalid_handle_code, const char *wrong_device_code); 100 void DestroyQueueDataStructures(VkDevice device); 101 bool ValidateCommandBuffer(VkDevice device, VkCommandPool command_pool, VkCommandBuffer command_buffer); 102 bool ValidateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set); 103 bool ValidateSamplerObjects(VkDevice device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo); 104 template <typename DispObj> 105 bool ValidateDescriptorWrite(DispObj disp, VkWriteDescriptorSet const *desc, bool isPush); 106 GetObjectLifetimeData(std::vector<ValidationObject * > & object_dispatch)107 ObjectLifetimes *GetObjectLifetimeData(std::vector<ValidationObject *> &object_dispatch) { 108 for (auto layer_object : object_dispatch) { 109 if (layer_object->container_type == LayerObjectTypeObjectTracker) { 110 return (reinterpret_cast<ObjectLifetimes *>(layer_object)); 111 } 112 } 113 return nullptr; 114 }; 115 116 template <typename T1, typename T2> ValidateObject(T1 dispatchable_object,T2 object,VulkanObjectType object_type,bool null_allowed,const char * invalid_handle_code,const char * wrong_device_code)117 bool ValidateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, bool null_allowed, 118 const char *invalid_handle_code, const char *wrong_device_code) { 119 if (null_allowed && (object == VK_NULL_HANDLE)) { 120 return false; 121 } 122 auto object_handle = HandleToUint64(object); 123 124 if (object_type == kVulkanObjectTypeDevice) { 125 return ValidateDeviceObject(object_handle, invalid_handle_code, wrong_device_code); 126 } 127 128 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; 129 130 // Look for object in object map 131 if (object_map[object_type].find(object_handle) == object_map[object_type].end()) { 132 // If object is an image, also look for it in the swapchain image map 133 if ((object_type != kVulkanObjectTypeImage) || (swapchainImageMap.find(object_handle) == swapchainImageMap.end())) { 134 // Object not found, look for it in other device object maps 135 for (auto other_device_data : layer_data_map) { 136 for (auto layer_object_data : other_device_data.second->object_dispatch) { 137 if (layer_object_data->container_type == LayerObjectTypeObjectTracker) { 138 auto object_lifetime_data = reinterpret_cast<ObjectLifetimes *>(layer_object_data); 139 if (object_lifetime_data && (object_lifetime_data != this)) { 140 if (object_lifetime_data->object_map[object_type].find(object_handle) != 141 object_lifetime_data->object_map[object_type].end() || 142 (object_type == kVulkanObjectTypeImage && 143 object_lifetime_data->swapchainImageMap.find(object_handle) != 144 object_lifetime_data->swapchainImageMap.end())) { 145 // Object found on other device, report an error if object has a device parent error code 146 if ((wrong_device_code != kVUIDUndefined) && (object_type != kVulkanObjectTypeSurfaceKHR)) { 147 return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, 148 wrong_device_code, 149 "Object 0x%" PRIxLEAST64 150 " was not created, allocated or retrieved from the correct device.", 151 object_handle); 152 } else { 153 return false; 154 } 155 } 156 } 157 } 158 } 159 } 160 // Report an error if object was not found anywhere 161 return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, invalid_handle_code, 162 "Invalid %s Object 0x%" PRIxLEAST64 ".", object_string[object_type], object_handle); 163 } 164 } 165 return false; 166 } 167 168 template <typename T1, typename T2> CreateObject(T1 dispatchable_object,T2 object,VulkanObjectType object_type,const VkAllocationCallbacks * pAllocator)169 void CreateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator) { 170 uint64_t object_handle = HandleToUint64(object); 171 bool custom_allocator = (pAllocator != nullptr); 172 if (!object_map[object_type].count(object_handle)) { 173 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; 174 log_msg(report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle, kVUID_ObjectTracker_Info, 175 "OBJ[0x%" PRIxLEAST64 "] : CREATE %s object 0x%" PRIxLEAST64, object_track_index++, object_string[object_type], 176 object_handle); 177 178 ObjTrackState *pNewObjNode = new ObjTrackState; 179 pNewObjNode->object_type = object_type; 180 pNewObjNode->status = custom_allocator ? OBJSTATUS_CUSTOM_ALLOCATOR : OBJSTATUS_NONE; 181 pNewObjNode->handle = object_handle; 182 183 object_map[object_type][object_handle] = pNewObjNode; 184 num_objects[object_type]++; 185 num_total_objects++; 186 187 if (object_type == kVulkanObjectTypeDescriptorPool) { 188 pNewObjNode->child_objects.reset(new std::unordered_set<uint64_t>); 189 } 190 } 191 } 192 193 template <typename T1> DestroyObjectSilently(T1 object,VulkanObjectType object_type)194 void DestroyObjectSilently(T1 object, VulkanObjectType object_type) { 195 auto object_handle = HandleToUint64(object); 196 assert(object_handle != VK_NULL_HANDLE); 197 198 auto item = object_map[object_type].find(object_handle); 199 assert(item != object_map[object_type].end()); 200 201 ObjTrackState *pNode = item->second; 202 assert(num_total_objects > 0); 203 204 num_total_objects--; 205 assert(num_objects[pNode->object_type] > 0); 206 207 num_objects[pNode->object_type]--; 208 209 delete pNode; 210 object_map[object_type].erase(item); 211 } 212 213 template <typename T1, typename T2> RecordDestroyObject(T1 dispatchable_object,T2 object,VulkanObjectType object_type)214 void RecordDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type) { 215 auto object_handle = HandleToUint64(object); 216 if (object_handle != VK_NULL_HANDLE) { 217 auto item = object_map[object_type].find(object_handle); 218 if (item != object_map[object_type].end()) { 219 DestroyObjectSilently(object, object_type); 220 } 221 } 222 } 223 224 template <typename T1, typename T2> ValidateDestroyObject(T1 dispatchable_object,T2 object,VulkanObjectType object_type,const VkAllocationCallbacks * pAllocator,const char * expected_custom_allocator_code,const char * expected_default_allocator_code)225 bool ValidateDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, 226 const VkAllocationCallbacks *pAllocator, const char *expected_custom_allocator_code, 227 const char *expected_default_allocator_code) { 228 auto object_handle = HandleToUint64(object); 229 bool custom_allocator = pAllocator != nullptr; 230 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; 231 bool skip = false; 232 233 if (object_handle != VK_NULL_HANDLE) { 234 auto item = object_map[object_type].find(object_handle); 235 if (item != object_map[object_type].end()) { 236 ObjTrackState *pNode = item->second; 237 skip |= log_msg(report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle, 238 kVUID_ObjectTracker_Info, 239 "OBJ_STAT Destroy %s obj 0x%" PRIxLEAST64 " (%" PRIu64 " total objs remain & %" PRIu64 " %s objs).", 240 object_string[object_type], HandleToUint64(object), num_total_objects - 1, 241 num_objects[pNode->object_type] - 1, object_string[object_type]); 242 243 auto allocated_with_custom = (pNode->status & OBJSTATUS_CUSTOM_ALLOCATOR) ? true : false; 244 if (allocated_with_custom && !custom_allocator && expected_custom_allocator_code != kVUIDUndefined) { 245 // This check only verifies that custom allocation callbacks were provided to both Create and Destroy calls, 246 // it cannot verify that these allocation callbacks are compatible with each other. 247 skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, 248 expected_custom_allocator_code, 249 "Custom allocator not specified while destroying %s obj 0x%" PRIxLEAST64 250 " but specified at creation.", 251 object_string[object_type], object_handle); 252 } else if (!allocated_with_custom && custom_allocator && expected_default_allocator_code != kVUIDUndefined) { 253 skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, 254 expected_default_allocator_code, 255 "Custom allocator specified while destroying %s obj 0x%" PRIxLEAST64 256 " but not specified at creation.", 257 object_string[object_type], object_handle); 258 } 259 } 260 } 261 return skip; 262 } 263 264 #include "object_tracker.h" 265 }; 266