diff options
Diffstat (limited to 'layers/object_lifetime_validation.h')
-rw-r--r-- | layers/object_lifetime_validation.h | 112 |
1 files changed, 68 insertions, 44 deletions
diff --git a/layers/object_lifetime_validation.h b/layers/object_lifetime_validation.h index bc12f8c85..bd89dc889 100644 --- a/layers/object_lifetime_validation.h +++ b/layers/object_lifetime_validation.h @@ -20,6 +20,18 @@ * Author: Tobin Ehlis <tobine@google.com> */ +// shared_mutex support added in MSVC 2015 update 2 +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && NTDDI_VERSION > NTDDI_WIN10_RS2 +#include <shared_mutex> +typedef std::shared_mutex object_lifetime_mutex_t; +typedef std::shared_lock<object_lifetime_mutex_t> read_object_lifetime_mutex_t; +typedef std::unique_lock<object_lifetime_mutex_t> write_object_lifetime_mutex_t; +#else +typedef std::mutex object_lifetime_mutex_t; +typedef std::unique_lock<object_lifetime_mutex_t> read_object_lifetime_mutex_t; +typedef std::unique_lock<object_lifetime_mutex_t> write_object_lifetime_mutex_t; +#endif + // Suppress unused warning on Linux #if defined(__GNUC__) #define DECORATE_UNUSED __attribute__((unused)) @@ -42,14 +54,8 @@ extern uint64_t object_track_index; typedef VkFlags ObjectStatusFlags; enum ObjectStatusFlagBits { OBJSTATUS_NONE = 0x00000000, // No status is set - OBJSTATUS_FENCE_IS_SUBMITTED = 0x00000001, // Fence has been submitted - OBJSTATUS_VIEWPORT_BOUND = 0x00000002, // Viewport state object has been bound - OBJSTATUS_RASTER_BOUND = 0x00000004, // Viewport state object has been bound - OBJSTATUS_COLOR_BLEND_BOUND = 0x00000008, // Viewport state object has been bound - OBJSTATUS_DEPTH_STENCIL_BOUND = 0x00000010, // Viewport state object has been bound - OBJSTATUS_GPU_MEM_MAPPED = 0x00000020, // Memory object is currently mapped - OBJSTATUS_COMMAND_BUFFER_SECONDARY = 0x00000040, // Command Buffer is of type SECONDARY - OBJSTATUS_CUSTOM_ALLOCATOR = 0x00000080, // Allocated with custom allocator + OBJSTATUS_COMMAND_BUFFER_SECONDARY = 0x00000001, // Command Buffer is of type SECONDARY + OBJSTATUS_CUSTOM_ALLOCATOR = 0x00000002, // Allocated with custom allocator }; // Object and state information structure @@ -61,35 +67,49 @@ struct ObjTrackState { std::unique_ptr<std::unordered_set<uint64_t> > child_objects; // Child objects (used for VkDescriptorPool only) }; -// Track Queue information -struct ObjTrackQueueInfo { - uint32_t queue_node_index; - VkQueue queue; -}; - -typedef std::unordered_map<uint64_t, ObjTrackState *> object_map_type; +typedef vl_concurrent_unordered_map<uint64_t, std::shared_ptr<ObjTrackState>, 6> object_map_type; class ObjectLifetimes : public ValidationObject { public: - uint64_t num_objects[kVulkanObjectTypeMax + 1]; - uint64_t num_total_objects; + // Override chassis read/write locks for this validation object + // This override takes a deferred lock. i.e. it is not acquired. + // This class does its own locking with a shared mutex. + virtual std::unique_lock<std::mutex> write_lock() { + return std::unique_lock<std::mutex>(validation_object_mutex, std::defer_lock); + } + + object_lifetime_mutex_t object_lifetime_mutex; + write_object_lifetime_mutex_t write_shared_lock() { return write_object_lifetime_mutex_t(object_lifetime_mutex); } + read_object_lifetime_mutex_t read_shared_lock() { return read_object_lifetime_mutex_t(object_lifetime_mutex); } + + std::atomic<uint64_t> num_objects[kVulkanObjectTypeMax + 1]; + std::atomic<uint64_t> num_total_objects; // Vector of unordered_maps per object type to hold ObjTrackState info - std::vector<object_map_type> object_map; + object_map_type object_map[kVulkanObjectTypeMax + 1]; // Special-case map for swapchain images - std::unordered_map<uint64_t, ObjTrackState *> swapchainImageMap; - // Map of queue information structures, one per queue - std::unordered_map<VkQueue, ObjTrackQueueInfo *> queue_info_map; - - std::vector<VkQueueFamilyProperties> queue_family_properties; + object_map_type swapchainImageMap; // Constructor for object lifetime tracking - ObjectLifetimes() : num_objects{}, num_total_objects(0), object_map{} { object_map.resize(kVulkanObjectTypeMax + 1); } + ObjectLifetimes() : num_objects{}, num_total_objects(0) {} + + void InsertObject(object_map_type &map, uint64_t object_handle, VulkanObjectType object_type, + std::shared_ptr<ObjTrackState> pNode) { + bool inserted = map.insert(object_handle, pNode); + if (!inserted) { + // The object should not already exist. If we couldn't add it to the map, there was probably + // a race condition in the app. Report an error and move on. + VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; + log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, kVUID_ObjectTracker_Info, + "Couldn't insert %s Object 0x%" PRIxLEAST64 + ", already existed. This should not happen and may indicate a " + "race condition in the application.", + object_string[object_type], object_handle); + } + } bool DeviceReportUndestroyedObjects(VkDevice device, VulkanObjectType object_type, const std::string &error_code); void DeviceDestroyUndestroyedObjects(VkDevice device, VulkanObjectType object_type); void CreateQueue(VkDevice device, VkQueue vkObj); - void AddQueueInfo(VkDevice device, uint32_t queue_node_index, VkQueue queue); - void ValidateQueueFlags(VkQueue queue, const char *function); void AllocateCommandBuffer(VkDevice device, const VkCommandPool command_pool, const VkCommandBuffer command_buffer, VkCommandBufferLevel level); void AllocateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set); @@ -129,7 +149,7 @@ class ObjectLifetimes : public ValidationObject { VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; // Look for object in object map - if (object_map[object_type].find(object_handle) == object_map[object_type].end()) { + if (!object_map[object_type].contains(object_handle)) { // If object is an image, also look for it in the swapchain image map if ((object_type != kVulkanObjectTypeImage) || (swapchainImageMap.find(object_handle) == swapchainImageMap.end())) { // Object not found, look for it in other device object maps @@ -170,13 +190,13 @@ class ObjectLifetimes : public ValidationObject { void CreateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator) { uint64_t object_handle = HandleToUint64(object); bool custom_allocator = (pAllocator != nullptr); - if (!object_map[object_type].count(object_handle)) { - ObjTrackState *pNewObjNode = new ObjTrackState; + if (!object_map[object_type].contains(object_handle)) { + auto pNewObjNode = std::make_shared<ObjTrackState>(); pNewObjNode->object_type = object_type; pNewObjNode->status = custom_allocator ? OBJSTATUS_CUSTOM_ALLOCATOR : OBJSTATUS_NONE; pNewObjNode->handle = object_handle; - object_map[object_type][object_handle] = pNewObjNode; + InsertObject(object_map[object_type], object_handle, object_type, pNewObjNode); num_objects[object_type]++; num_total_objects++; @@ -191,27 +211,31 @@ class ObjectLifetimes : public ValidationObject { auto object_handle = HandleToUint64(object); assert(object_handle != VK_NULL_HANDLE); - auto item = object_map[object_type].find(object_handle); - assert(item != object_map[object_type].end()); - - ObjTrackState *pNode = item->second; + auto item = object_map[object_type].pop(object_handle); + if (item == object_map[object_type].end()) { + // We've already checked that the object exists. If we couldn't find and atomically remove it + // from the map, there must have been a race condition in the app. Report an error and move on. + VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; + log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, kVUID_ObjectTracker_Info, + "Couldn't destroy %s Object 0x%" PRIxLEAST64 + ", not found. This should not happen and may indicate a " + "race condition in the application.", + object_string[object_type], object_handle); + return; + } assert(num_total_objects > 0); num_total_objects--; - assert(num_objects[pNode->object_type] > 0); + assert(num_objects[item->second->object_type] > 0); - num_objects[pNode->object_type]--; - - delete pNode; - object_map[object_type].erase(item); + num_objects[item->second->object_type]--; } template <typename T1, typename T2> void RecordDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type) { auto object_handle = HandleToUint64(object); if (object_handle != VK_NULL_HANDLE) { - auto item = object_map[object_type].find(object_handle); - if (item != object_map[object_type].end()) { + if (object_map[object_type].contains(object_handle)) { DestroyObjectSilently(object, object_type); } } @@ -226,11 +250,11 @@ class ObjectLifetimes : public ValidationObject { VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; bool skip = false; - if (object_handle != VK_NULL_HANDLE) { + if ((expected_custom_allocator_code != kVUIDUndefined || expected_default_allocator_code != kVUIDUndefined) && + object_handle != VK_NULL_HANDLE) { auto item = object_map[object_type].find(object_handle); if (item != object_map[object_type].end()) { - ObjTrackState *pNode = item->second; - auto allocated_with_custom = (pNode->status & OBJSTATUS_CUSTOM_ALLOCATOR) ? true : false; + auto allocated_with_custom = (item->second->status & OBJSTATUS_CUSTOM_ALLOCATOR) ? true : false; if (allocated_with_custom && !custom_allocator && expected_custom_allocator_code != kVUIDUndefined) { // This check only verifies that custom allocation callbacks were provided to both Create and Destroy calls, // it cannot verify that these allocation callbacks are compatible with each other. |