blob: 98dfa37abdb64e7efe2069bce2547548dcbeb6ca [file] [log] [blame]
Mark Lobodzinski63902f02018-09-21 10:36:44 -06001/* Copyright (c) 2015-2018 The Khronos Group Inc.
2 * Copyright (c) 2015-2018 Valve Corporation
3 * Copyright (c) 2015-2018 LunarG, Inc.
4 * Copyright (C) 2015-2018 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
Mark Lobodzinski63902f02018-09-21 10:36:44 -060023// 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
31static const char DECORATE_UNUSED *kVUID_ObjectTracker_Info = "UNASSIGNED-ObjectTracker-Info";
32static const char DECORATE_UNUSED *kVUID_ObjectTracker_InternalError = "UNASSIGNED-ObjectTracker-InternalError";
33static const char DECORATE_UNUSED *kVUID_ObjectTracker_ObjectLeak = "UNASSIGNED-ObjectTracker-ObjectLeak";
34static const char DECORATE_UNUSED *kVUID_ObjectTracker_UnknownObject = "UNASSIGNED-ObjectTracker-UnknownObject";
35// clang-format on
36
37#undef DECORATE_UNUSED
38
39extern uint64_t object_track_index;
40
Mark Lobodzinskiadd93232018-10-09 11:49:42 -060041// Object Status -- used to track state of individual objects
42typedef VkFlags ObjectStatusFlags;
43enum 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
56struct 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};
62
63// Track Queue information
64struct ObjTrackQueueInfo {
65 uint32_t queue_node_index;
66 VkQueue queue;
67};
68
69typedef std::unordered_map<uint64_t, ObjTrackState *> object_map_type;
70
71class ObjectLifetimes : public ValidationObject {
Mark Lobodzinski0c668462018-09-27 10:13:19 -060072 public:
Mark Lobodzinskiadd93232018-10-09 11:49:42 -060073 uint64_t num_objects[kVulkanObjectTypeMax + 1];
74 uint64_t num_total_objects;
75 // Vector of unordered_maps per object type to hold ObjTrackState info
76 std::vector<object_map_type> object_map;
77 // Special-case map for swapchain images
78 std::unordered_map<uint64_t, ObjTrackState *> swapchainImageMap;
79 // Map of queue information structures, one per queue
80 std::unordered_map<VkQueue, ObjTrackQueueInfo *> queue_info_map;
81
82 std::vector<VkQueueFamilyProperties> queue_family_properties;
83
84 // Constructor for object lifetime tracking
85 ObjectLifetimes() : num_objects{}, num_total_objects(0), object_map{} { object_map.resize(kVulkanObjectTypeMax + 1); }
Mark Lobodzinski63902f02018-09-21 10:36:44 -060086
Mark Lobodzinski0c668462018-09-27 10:13:19 -060087 bool DeviceReportUndestroyedObjects(VkDevice device, VulkanObjectType object_type, const std::string &error_code);
88 void DeviceDestroyUndestroyedObjects(VkDevice device, VulkanObjectType object_type);
89 void CreateQueue(VkDevice device, VkQueue vkObj);
90 void AddQueueInfo(VkDevice device, uint32_t queue_node_index, VkQueue queue);
91 void ValidateQueueFlags(VkQueue queue, const char *function);
92 void AllocateCommandBuffer(VkDevice device, const VkCommandPool command_pool, const VkCommandBuffer command_buffer,
93 VkCommandBufferLevel level);
94 void AllocateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set);
95 void CreateSwapchainImageObject(VkDevice dispatchable_object, VkImage swapchain_image, VkSwapchainKHR swapchain);
96 bool ReportUndestroyedObjects(VkDevice device, const std::string &error_code);
97 void DestroyUndestroyedObjects(VkDevice device);
98 bool ValidateDeviceObject(uint64_t device_handle, const std::string &invalid_handle_code, const std::string &wrong_device_code);
99 void DestroyQueueDataStructures(VkDevice device);
100 bool ValidateCommandBuffer(VkDevice device, VkCommandPool command_pool, VkCommandBuffer command_buffer);
101 bool ValidateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set);
102 bool ValidateSamplerObjects(VkDevice device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo);
103 template <typename DispObj>
104 bool ValidateDescriptorWrite(DispObj disp, VkWriteDescriptorSet const *desc, bool isPush);
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600105
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600106 ObjectLifetimes *GetObjectLifetimeData(std::vector<ValidationObject *> &object_dispatch) {
107 for (auto layer_object : object_dispatch) {
108 if (layer_object->container_type == LayerObjectTypeObjectTracker) {
109 return (reinterpret_cast<ObjectLifetimes *>(layer_object));
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600110 }
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600111 }
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600112 return nullptr;
113 };
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600114
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600115 template <typename T1, typename T2>
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600116 bool ValidateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, bool null_allowed,
117 const std::string &invalid_handle_code, const std::string &wrong_device_code) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600118 if (null_allowed && (object == VK_NULL_HANDLE)) {
119 return false;
120 }
121 auto object_handle = HandleToUint64(object);
122
123 if (object_type == kVulkanObjectTypeDevice) {
124 return ValidateDeviceObject(object_handle, invalid_handle_code, wrong_device_code);
125 }
126
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600127 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type];
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600128
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600129 // Look for object in object map
130 if (object_map[object_type].find(object_handle) == object_map[object_type].end()) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600131 // If object is an image, also look for it in the swapchain image map
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600132 if ((object_type != kVulkanObjectTypeImage) || (swapchainImageMap.find(object_handle) == swapchainImageMap.end())) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600133 // Object not found, look for it in other device object maps
134 for (auto other_device_data : layer_data_map) {
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600135 for (auto layer_object_data : other_device_data.second->object_dispatch) {
136 if (layer_object_data->container_type == LayerObjectTypeObjectTracker) {
137 auto object_lifetime_data = reinterpret_cast<ObjectLifetimes *>(layer_object_data);
138 if (object_lifetime_data && (object_lifetime_data != this)) {
139 if (object_lifetime_data->object_map[object_type].find(object_handle) !=
140 object_lifetime_data->object_map[object_type].end() ||
141 (object_type == kVulkanObjectTypeImage &&
142 object_lifetime_data->swapchainImageMap.find(object_handle) !=
143 object_lifetime_data->swapchainImageMap.end())) {
144 // Object found on other device, report an error if object has a device parent error code
145 if ((wrong_device_code != kVUIDUndefined) && (object_type != kVulkanObjectTypeSurfaceKHR)) {
146 return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle,
147 wrong_device_code,
148 "Object 0x%" PRIxLEAST64
149 " was not created, allocated or retrieved from the correct device.",
150 object_handle);
151 } else {
152 return false;
153 }
154 }
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600155 }
156 }
157 }
158 }
159 // Report an error if object was not found anywhere
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600160 return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, invalid_handle_code,
161 "Invalid %s Object 0x%" PRIxLEAST64 ".", object_string[object_type], object_handle);
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600162 }
163 }
164 return false;
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600165 }
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600166
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600167 template <typename T1, typename T2>
168 void CreateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator) {
169 uint64_t object_handle = HandleToUint64(object);
170 bool custom_allocator = (pAllocator != nullptr);
171 if (!object_map[object_type].count(object_handle)) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600172 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type];
173 log_msg(report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle, kVUID_ObjectTracker_Info,
174 "OBJ[0x%" PRIxLEAST64 "] : CREATE %s object 0x%" PRIxLEAST64, object_track_index++, object_string[object_type],
175 object_handle);
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600176
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600177 ObjTrackState *pNewObjNode = new ObjTrackState;
178 pNewObjNode->object_type = object_type;
179 pNewObjNode->status = custom_allocator ? OBJSTATUS_CUSTOM_ALLOCATOR : OBJSTATUS_NONE;
180 pNewObjNode->handle = object_handle;
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600181
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600182 object_map[object_type][object_handle] = pNewObjNode;
183 num_objects[object_type]++;
184 num_total_objects++;
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600185 }
186 }
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600187
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600188 template <typename T1>
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600189 void DestroyObjectSilently(T1 object, VulkanObjectType object_type) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600190 auto object_handle = HandleToUint64(object);
191 assert(object_handle != VK_NULL_HANDLE);
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600192
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600193 auto item = object_map[object_type].find(object_handle);
194 assert(item != object_map[object_type].end());
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600195
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600196 ObjTrackState *pNode = item->second;
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600197 assert(num_total_objects > 0);
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600198
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600199 num_total_objects--;
200 assert(num_objects[pNode->object_type] > 0);
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600201
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600202 num_objects[pNode->object_type]--;
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600203
204 delete pNode;
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600205 object_map[object_type].erase(item);
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600206 }
207
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600208 template <typename T1, typename T2>
209 void RecordDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600210 auto object_handle = HandleToUint64(object);
211 if (object_handle != VK_NULL_HANDLE) {
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600212 auto item = object_map[object_type].find(object_handle);
213 if (item != object_map[object_type].end()) {
214 DestroyObjectSilently(object, object_type);
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600215 }
216 }
217 }
Mark Lobodzinski63902f02018-09-21 10:36:44 -0600218
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600219 template <typename T1, typename T2>
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600220 bool ValidateDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type,
221 const VkAllocationCallbacks *pAllocator, const std::string &expected_custom_allocator_code,
222 const std::string &expected_default_allocator_code) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600223 auto object_handle = HandleToUint64(object);
224 bool custom_allocator = pAllocator != nullptr;
225 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type];
226 bool skip = false;
227
228 if (object_handle != VK_NULL_HANDLE) {
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600229 auto item = object_map[object_type].find(object_handle);
230 if (item != object_map[object_type].end()) {
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600231 ObjTrackState *pNode = item->second;
232 skip |= log_msg(report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle,
233 kVUID_ObjectTracker_Info,
234 "OBJ_STAT Destroy %s obj 0x%" PRIxLEAST64 " (%" PRIu64 " total objs remain & %" PRIu64 " %s objs).",
Mark Lobodzinskiadd93232018-10-09 11:49:42 -0600235 object_string[object_type], HandleToUint64(object), num_total_objects - 1,
236 num_objects[pNode->object_type] - 1, object_string[object_type]);
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600237
238 auto allocated_with_custom = (pNode->status & OBJSTATUS_CUSTOM_ALLOCATOR) ? true : false;
239 if (allocated_with_custom && !custom_allocator && expected_custom_allocator_code != kVUIDUndefined) {
240 // This check only verifies that custom allocation callbacks were provided to both Create and Destroy calls,
241 // it cannot verify that these allocation callbacks are compatible with each other.
242 skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle,
243 expected_custom_allocator_code,
244 "Custom allocator not specified while destroying %s obj 0x%" PRIxLEAST64
245 " but specified at creation.",
246 object_string[object_type], object_handle);
247 } else if (!allocated_with_custom && custom_allocator && expected_default_allocator_code != kVUIDUndefined) {
248 skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle,
249 expected_default_allocator_code,
250 "Custom allocator specified while destroying %s obj 0x%" PRIxLEAST64
251 " but not specified at creation.",
252 object_string[object_type], object_handle);
253 }
254 }
255 }
256 return skip;
257 }
258
Mark Lobodzinski0c668462018-09-27 10:13:19 -0600259#include "object_tracker.h"
260};