blob: 186a1674fe705b20246f7a23698eb06eaa8cc273 [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
23#pragma once
24
25#include <mutex>
26#include <cinttypes>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unordered_map>
31#include <unordered_set>
32
33#include "vulkan/vulkan.h"
34#include "vk_object_types.h"
35#include "vk_layer_logging.h"
36#include "object_lifetimes.h"
37
38namespace object_tracker {
39
40#include "precall.h"
41#include "postcall.h"
42
43// Suppress unused warning on Linux
44#if defined(__GNUC__)
45#define DECORATE_UNUSED __attribute__((unused))
46#else
47#define DECORATE_UNUSED
48#endif
49
50// clang-format off
51static const char DECORATE_UNUSED *kVUID_ObjectTracker_Info = "UNASSIGNED-ObjectTracker-Info";
52static const char DECORATE_UNUSED *kVUID_ObjectTracker_InternalError = "UNASSIGNED-ObjectTracker-InternalError";
53static const char DECORATE_UNUSED *kVUID_ObjectTracker_ObjectLeak = "UNASSIGNED-ObjectTracker-ObjectLeak";
54static const char DECORATE_UNUSED *kVUID_ObjectTracker_UnknownObject = "UNASSIGNED-ObjectTracker-UnknownObject";
55// clang-format on
56
57#undef DECORATE_UNUSED
58
59extern uint64_t object_track_index;
60
61bool DeviceReportUndestroyedObjects(VkDevice device, VulkanObjectType object_type, const std::string &error_code);
62void DeviceDestroyUndestroyedObjects(VkDevice device, VulkanObjectType object_type);
63void CreateQueue(VkDevice device, VkQueue vkObj);
64void AddQueueInfo(VkDevice device, uint32_t queue_node_index, VkQueue queue);
65void ValidateQueueFlags(VkQueue queue, const char *function);
66void AllocateCommandBuffer(VkDevice device, const VkCommandPool command_pool, const VkCommandBuffer command_buffer,
67 VkCommandBufferLevel level);
68void AllocateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set);
69void CreateSwapchainImageObject(VkDevice dispatchable_object, VkImage swapchain_image, VkSwapchainKHR swapchain);
70bool ReportUndestroyedObjects(VkDevice device, const std::string &error_code);
71void DestroyUndestroyedObjects(VkDevice device);
72bool ValidateDeviceObject(uint64_t device_handle, const std::string &invalid_handle_code, const std::string &wrong_device_code);
73
74template <typename T1, typename T2>
75bool InstanceValidateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, bool null_allowed,
76 const std::string &invalid_handle_code, const std::string &wrong_device_code) {
77 if (null_allowed && (object == VK_NULL_HANDLE)) {
78 return false;
79 }
80 auto object_handle = HandleToUint64(object);
81
82 if (object_type == kVulkanObjectTypeDevice) {
83 return ValidateDeviceObject(object_handle, invalid_handle_code, wrong_device_code);
84 }
85
86 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type];
87 instance_layer_data *instance_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), instance_layer_data_map);
88 // Look for object in object map
89 if (instance_data->objdata.object_map[object_type].find(object_handle) ==
90 instance_data->objdata.object_map[object_type].end()) {
91 // Object not found, look for it in other instance object maps
92 for (auto other_instance_data : instance_layer_data_map) {
93 if (other_instance_data.second != instance_data) {
94 if (other_instance_data.second->objdata.object_map[object_type].find(object_handle) !=
95 other_instance_data.second->objdata.object_map[object_type].end()) {
96 // Object found on another instance, report an error if object has a instance parent error code
97 if ((wrong_device_code != kVUIDUndefined) && (object_type != kVulkanObjectTypeSurfaceKHR)) {
98 return log_msg(instance_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle,
99 wrong_device_code,
100 "Object 0x%" PRIxLEAST64
101 " was not created, allocated or retrieved from the correct instance.",
102 object_handle);
103 } else {
104 return false;
105 }
106 }
107 }
108 }
109 // Report an error if object was not found anywhere
110 return log_msg(instance_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle,
111 invalid_handle_code, "Invalid %s Object 0x%" PRIxLEAST64 ".", object_string[object_type], object_handle);
112 }
113 return false;
114}
115
116template <typename T1, typename T2>
117bool DeviceValidateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, bool null_allowed,
118 const std::string &invalid_handle_code, const std::string &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 layer_data *device_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map);
131 // Look for object in device object map
132 if (device_data->objdata.object_map[object_type].find(object_handle) == device_data->objdata.object_map[object_type].end()) {
133 // If object is an image, also look for it in the swapchain image map
134 if ((object_type != kVulkanObjectTypeImage) ||
135 (device_data->objdata.swapchainImageMap.find(object_handle) == device_data->objdata.swapchainImageMap.end())) {
136 // Object not found, look for it in other device object maps
137 for (auto other_device_data : layer_data_map) {
138 if (other_device_data.second != device_data) {
139 if (other_device_data.second->objdata.object_map[object_type].find(object_handle) !=
140 other_device_data.second->objdata.object_map[object_type].end() ||
141 (object_type == kVulkanObjectTypeImage &&
142 other_device_data.second->objdata.swapchainImageMap.find(object_handle) !=
143 other_device_data.second->objdata.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(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type,
147 object_handle, 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 }
155 }
156 }
157 // Report an error if object was not found anywhere
158 return log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle,
159 invalid_handle_code, "Invalid %s Object 0x%" PRIxLEAST64 ".", object_string[object_type], object_handle);
160 }
161 }
162 return false;
163}
164
165static void InsertObjectInMap(uint64_t object_handle, VulkanObjectType object_type, object_lifetime *obj_data,
166 debug_report_data *report_data, bool custom_allocator) {
167 if (!obj_data->object_map[object_type].count(object_handle)) {
168 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type];
169 log_msg(report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle, kVUID_ObjectTracker_Info,
170 "OBJ[0x%" PRIxLEAST64 "] : CREATE %s object 0x%" PRIxLEAST64, object_track_index++, object_string[object_type],
171 object_handle);
172
173 ObjTrackState *pNewObjNode = new ObjTrackState;
174 pNewObjNode->object_type = object_type;
175 pNewObjNode->status = custom_allocator ? OBJSTATUS_CUSTOM_ALLOCATOR : OBJSTATUS_NONE;
176 pNewObjNode->handle = object_handle;
177
178 obj_data->object_map[object_type][object_handle] = pNewObjNode;
179 obj_data->num_objects[object_type]++;
180 obj_data->num_total_objects++;
181 }
182}
183
184template <typename T1, typename T2>
185void InstanceCreateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type,
186 const VkAllocationCallbacks *pAllocator) {
187 instance_layer_data *layer_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), instance_layer_data_map);
188 InsertObjectInMap(HandleToUint64(object), object_type, &layer_data->objdata, layer_data->report_data, (pAllocator != nullptr));
189}
190
191template <typename T1, typename T2>
192void DeviceCreateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator) {
193 layer_data *layer_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map);
194 InsertObjectInMap(HandleToUint64(object), object_type, &layer_data->objdata, layer_data->report_data, (pAllocator != nullptr));
195}
196
197template <typename T1>
198void DestroyObjectSilently(object_lifetime *obj_data, T1 object, VulkanObjectType object_type) {
199 auto object_handle = HandleToUint64(object);
200 assert(object_handle != VK_NULL_HANDLE);
201
202 auto item = obj_data->object_map[object_type].find(object_handle);
203 assert(item != obj_data->object_map[object_type].end());
204
205 ObjTrackState *pNode = item->second;
206 assert(obj_data->num_total_objects > 0);
207
208 obj_data->num_total_objects--;
209 assert(obj_data->num_objects[pNode->object_type] > 0);
210
211 obj_data->num_objects[pNode->object_type]--;
212
213 delete pNode;
214 obj_data->object_map[object_type].erase(item);
215}
216
217template <typename T1>
218void DeleteObjectFromMap(T1 object, VulkanObjectType object_type, object_lifetime *obj_data) {
219 auto object_handle = HandleToUint64(object);
220 if (object_handle != VK_NULL_HANDLE) {
221 auto item = obj_data->object_map[object_type].find(object_handle);
222 if (item != obj_data->object_map[object_type].end()) {
223 DestroyObjectSilently(obj_data, object, object_type);
224 }
225 }
226}
227
228template <typename T1, typename T2>
229void InstanceRecordDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type) {
230 instance_layer_data *layer_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), instance_layer_data_map);
231 DeleteObjectFromMap(object, object_type, &layer_data->objdata);
232}
233
234template <typename T1, typename T2>
235void DeviceRecordDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type) {
236 layer_data *layer_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map);
237 DeleteObjectFromMap(object, object_type, &layer_data->objdata);
238}
239
240template <typename T1>
241bool ValidateDestroyObject(T1 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator,
242 const std::string &expected_custom_allocator_code, const std::string &expected_default_allocator_code,
243 object_lifetime *obj_data, debug_report_data *report_data) {
244 auto object_handle = HandleToUint64(object);
245 bool custom_allocator = pAllocator != nullptr;
246 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type];
247 bool skip = false;
248
249 if (object_handle != VK_NULL_HANDLE) {
250 auto item = obj_data->object_map[object_type].find(object_handle);
251 if (item != obj_data->object_map[object_type].end()) {
252 ObjTrackState *pNode = item->second;
253 skip |= log_msg(report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle,
254 kVUID_ObjectTracker_Info,
255 "OBJ_STAT Destroy %s obj 0x%" PRIxLEAST64 " (%" PRIu64 " total objs remain & %" PRIu64 " %s objs).",
256 object_string[object_type], HandleToUint64(object), obj_data->num_total_objects - 1,
257 obj_data->num_objects[pNode->object_type] - 1, object_string[object_type]);
258
259 auto allocated_with_custom = (pNode->status & OBJSTATUS_CUSTOM_ALLOCATOR) ? true : false;
260 if (allocated_with_custom && !custom_allocator && expected_custom_allocator_code != kVUIDUndefined) {
261 // This check only verifies that custom allocation callbacks were provided to both Create and Destroy calls,
262 // it cannot verify that these allocation callbacks are compatible with each other.
263 skip |= log_msg(
264 report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, expected_custom_allocator_code,
265 "Custom allocator not specified while destroying %s obj 0x%" PRIxLEAST64 " but specified at creation.",
266 object_string[object_type], object_handle);
267 } else if (!allocated_with_custom && custom_allocator && expected_default_allocator_code != kVUIDUndefined) {
268 skip |= log_msg(
269 report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, expected_default_allocator_code,
270 "Custom allocator specified while destroying %s obj 0x%" PRIxLEAST64 " but not specified at creation.",
271 object_string[object_type], object_handle);
272 }
273 }
274 }
275 return skip;
276}
277
278template <typename T1, typename T2>
279bool InstanceValidateDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type,
280 const VkAllocationCallbacks *pAllocator, const std::string &expected_custom_allocator_code,
281 const std::string &expected_default_allocator_code) {
282 instance_layer_data *layer_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), instance_layer_data_map);
283 return ValidateDestroyObject(object, object_type, pAllocator, expected_custom_allocator_code, expected_default_allocator_code,
284 &layer_data->objdata, layer_data->report_data);
285}
286
287template <typename T1, typename T2>
288bool DeviceValidateDestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type,
289 const VkAllocationCallbacks *pAllocator, const std::string &expected_custom_allocator_code,
290 const std::string &expected_default_allocator_code) {
291 layer_data *layer_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map);
292 return ValidateDestroyObject(object, object_type, pAllocator, expected_custom_allocator_code, expected_default_allocator_code,
293 &layer_data->objdata, layer_data->report_data);
294}
295
296} // namespace object_tracker