Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2015-2016 The Khronos Group Inc. |
| 2 | * Copyright (c) 2015-2016 Valve Corporation |
| 3 | * Copyright (c) 2015-2016 LunarG, Inc. |
| 4 | * Copyright (C) 2015-2016 Google Inc. |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and/or associated documentation files (the "Materials"), to |
| 8 | * deal in the Materials without restriction, including without limitation the |
| 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| 10 | * sell copies of the Materials, and to permit persons to whom the Materials |
| 11 | * are furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice(s) and this permission notice shall be included |
| 14 | * in all copies or substantial portions of the Materials. |
| 15 | * |
| 16 | * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| 19 | * |
| 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| 21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE |
| 23 | * USE OR OTHER DEALINGS IN THE MATERIALS |
| 24 | * |
| 25 | * Author: Courtney Goeltzenleuchter <courtneygo@google.com> |
| 26 | * Author: Tobin Ehlis <tobine@google.com> |
| 27 | * Author: Chris Forbes <chrisf@ijw.co.nz> |
| 28 | * Author: Mark Lobodzinski <mark@lunarg.com> |
| 29 | */ |
| 30 | |
Dustin Graves | acb23f7 | 2016-04-01 15:00:09 -0600 | [diff] [blame] | 31 | // Check for noexcept support |
| 32 | #if defined(__clang__) |
| 33 | #if __has_feature(cxx_noexcept) |
| 34 | #define HAS_NOEXCEPT |
| 35 | #endif |
| 36 | #else |
Jamie Madill | db073a5 | 2016-04-04 14:42:21 -0400 | [diff] [blame] | 37 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 |
Dustin Graves | acb23f7 | 2016-04-01 15:00:09 -0600 | [diff] [blame] | 38 | #define HAS_NOEXCEPT |
Jamie Madill | db073a5 | 2016-04-04 14:42:21 -0400 | [diff] [blame] | 39 | #else |
| 40 | #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 && defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS |
| 41 | #define HAS_NOEXCEPT |
| 42 | #endif |
Dustin Graves | acb23f7 | 2016-04-01 15:00:09 -0600 | [diff] [blame] | 43 | #endif |
| 44 | #endif |
| 45 | |
| 46 | #ifdef HAS_NOEXCEPT |
| 47 | #define NOEXCEPT noexcept |
| 48 | #else |
| 49 | #define NOEXCEPT |
| 50 | #endif |
| 51 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 52 | // Enable mem_tracker merged code |
| 53 | #define MTMERGE 1 |
| 54 | |
| 55 | #pragma once |
Tobin Ehlis | 5f4cef1 | 2016-04-01 13:51:33 -0600 | [diff] [blame] | 56 | #include "vk_safe_struct.h" |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 57 | #include "vulkan/vk_layer.h" |
| 58 | #include <atomic> |
| 59 | #include <vector> |
| 60 | #include <unordered_map> |
Chris Forbes | 2dbf807 | 2016-03-31 17:37:36 +1300 | [diff] [blame] | 61 | #include <unordered_set> |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 62 | #include <memory> |
Mark Lobodzinski | 397fd79 | 2016-03-15 09:19:59 -0600 | [diff] [blame] | 63 | #include <functional> |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 64 | |
| 65 | using std::vector; |
Chris Forbes | 2dbf807 | 2016-03-31 17:37:36 +1300 | [diff] [blame] | 66 | using std::unordered_set; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 67 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 68 | #if MTMERGE |
| 69 | // Mem Tracker ERROR codes |
| 70 | typedef enum _MEM_TRACK_ERROR { |
| 71 | MEMTRACK_NONE, // Used for INFO & other non-error messages |
| 72 | MEMTRACK_INVALID_CB, // Cmd Buffer invalid |
| 73 | MEMTRACK_INVALID_MEM_OBJ, // Invalid Memory Object |
| 74 | MEMTRACK_INVALID_ALIASING, // Invalid Memory Aliasing |
| 75 | MEMTRACK_INVALID_LAYOUT, // Invalid Layout |
| 76 | MEMTRACK_INTERNAL_ERROR, // Bug in Mem Track Layer internal data structures |
| 77 | MEMTRACK_FREED_MEM_REF, // MEM Obj freed while it still has obj and/or CB refs |
| 78 | MEMTRACK_MEM_OBJ_CLEAR_EMPTY_BINDINGS, // Clearing bindings on mem obj that doesn't have any bindings |
| 79 | MEMTRACK_MISSING_MEM_BINDINGS, // Trying to retrieve mem bindings, but none found (may be internal error) |
| 80 | MEMTRACK_INVALID_OBJECT, // Attempting to reference generic VK Object that is invalid |
| 81 | MEMTRACK_MEMORY_BINDING_ERROR, // Error during one of many calls that bind memory to object or CB |
| 82 | MEMTRACK_MEMORY_LEAK, // Failure to call vkFreeMemory on Mem Obj prior to DestroyDevice |
| 83 | MEMTRACK_INVALID_STATE, // Memory not in the correct state |
| 84 | MEMTRACK_RESET_CB_WHILE_IN_FLIGHT, // vkResetCommandBuffer() called on a CB that hasn't completed |
| 85 | MEMTRACK_INVALID_FENCE_STATE, // Invalid Fence State signaled or used |
| 86 | MEMTRACK_REBIND_OBJECT, // Non-sparse object bindings are immutable |
| 87 | MEMTRACK_INVALID_USAGE_FLAG, // Usage flags specified at image/buffer create conflict w/ use of object |
| 88 | MEMTRACK_INVALID_MAP, // Size flag specified at alloc is too small for mapping range |
| 89 | } MEM_TRACK_ERROR; |
| 90 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 91 | struct MemRange { |
| 92 | VkDeviceSize offset; |
| 93 | VkDeviceSize size; |
| 94 | }; |
| 95 | |
| 96 | /* |
Tobin Ehlis | b3593a4 | 2016-03-16 16:00:36 -0600 | [diff] [blame] | 97 | * MTMTODO : Update this comment |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 98 | * Data Structure overview |
| 99 | * There are 4 global STL(' maps |
| 100 | * cbMap -- map of command Buffer (CB) objects to MT_CB_INFO structures |
| 101 | * Each MT_CB_INFO struct has an stl list container with |
| 102 | * memory objects that are referenced by this CB |
| 103 | * memObjMap -- map of Memory Objects to MT_MEM_OBJ_INFO structures |
| 104 | * Each MT_MEM_OBJ_INFO has two stl list containers with: |
| 105 | * -- all CBs referencing this mem obj |
| 106 | * -- all VK Objects that are bound to this memory |
| 107 | * objectMap -- map of objects to MT_OBJ_INFO structures |
| 108 | * |
| 109 | * Algorithm overview |
| 110 | * These are the primary events that should happen related to different objects |
| 111 | * 1. Command buffers |
| 112 | * CREATION - Add object,structure to map |
| 113 | * CMD BIND - If mem associated, add mem reference to list container |
| 114 | * DESTROY - Remove from map, decrement (and report) mem references |
| 115 | * 2. Mem Objects |
| 116 | * CREATION - Add object,structure to map |
| 117 | * OBJ BIND - Add obj structure to list container for that mem node |
| 118 | * CMB BIND - If mem-related add CB structure to list container for that mem node |
| 119 | * DESTROY - Flag as errors any remaining refs and remove from map |
| 120 | * 3. Generic Objects |
| 121 | * MEM BIND - DESTROY any previous binding, Add obj node w/ ref to map, add obj ref to list container for that mem node |
| 122 | * DESTROY - If mem bound, remove reference list container for that memInfo, remove object ref from map |
| 123 | */ |
| 124 | // TODO : Is there a way to track when Cmd Buffer finishes & remove mem references at that point? |
| 125 | // TODO : Could potentially store a list of freed mem allocs to flag when they're incorrectly used |
| 126 | |
| 127 | // Simple struct to hold handle and type of object so they can be uniquely identified and looked up in appropriate map |
| 128 | struct MT_OBJ_HANDLE_TYPE { |
| 129 | uint64_t handle; |
| 130 | VkDebugReportObjectTypeEXT type; |
| 131 | }; |
| 132 | |
Dustin Graves | acb23f7 | 2016-04-01 15:00:09 -0600 | [diff] [blame] | 133 | bool operator==(MT_OBJ_HANDLE_TYPE a, MT_OBJ_HANDLE_TYPE b) NOEXCEPT{ |
Chris Forbes | 2dbf807 | 2016-03-31 17:37:36 +1300 | [diff] [blame] | 134 | return a.handle == b.handle && a.type == b.type; |
| 135 | } |
| 136 | |
| 137 | namespace std { |
| 138 | template<> |
| 139 | struct hash<MT_OBJ_HANDLE_TYPE> { |
Dustin Graves | acb23f7 | 2016-04-01 15:00:09 -0600 | [diff] [blame] | 140 | size_t operator()(MT_OBJ_HANDLE_TYPE obj) const NOEXCEPT{ |
Chris Forbes | 2dbf807 | 2016-03-31 17:37:36 +1300 | [diff] [blame] | 141 | return hash<uint64_t>()(obj.handle) ^ |
| 142 | hash<uint32_t>()(obj.type); |
| 143 | } |
| 144 | }; |
| 145 | } |
| 146 | |
Tobin Ehlis | b3593a4 | 2016-03-16 16:00:36 -0600 | [diff] [blame] | 147 | struct MEMORY_RANGE { |
| 148 | uint64_t handle; |
| 149 | VkDeviceMemory memory; |
| 150 | VkDeviceSize start; |
| 151 | VkDeviceSize end; |
| 152 | }; |
| 153 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 154 | // Data struct for tracking memory object |
Tobin Ehlis | b3593a4 | 2016-03-16 16:00:36 -0600 | [diff] [blame] | 155 | struct DEVICE_MEM_INFO { |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 156 | void *object; // Dispatchable object used to create this memory (device of swapchain) |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 157 | bool valid; // Stores if the memory has valid data or not |
| 158 | VkDeviceMemory mem; |
| 159 | VkMemoryAllocateInfo allocInfo; |
Chris Forbes | 2dbf807 | 2016-03-31 17:37:36 +1300 | [diff] [blame] | 160 | unordered_set<MT_OBJ_HANDLE_TYPE> objBindings; // objects bound to this memory |
Chris Forbes | a11561f | 2016-03-31 17:58:13 +1300 | [diff] [blame] | 161 | unordered_set<VkCommandBuffer> commandBufferBindings; // cmd buffers referencing this memory |
Tobin Ehlis | b3593a4 | 2016-03-16 16:00:36 -0600 | [diff] [blame] | 162 | vector<MEMORY_RANGE> bufferRanges; |
| 163 | vector<MEMORY_RANGE> imageRanges; |
| 164 | VkImage image; // If memory is bound to image, this will have VkImage handle, else VK_NULL_HANDLE |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 165 | MemRange memRange; |
| 166 | void *pData, *pDriverData; |
| 167 | }; |
| 168 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 169 | struct MT_FB_ATTACHMENT_INFO { |
| 170 | VkImage image; |
| 171 | VkDeviceMemory mem; |
| 172 | }; |
| 173 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 174 | struct MT_PASS_ATTACHMENT_INFO { |
| 175 | uint32_t attachment; |
| 176 | VkAttachmentLoadOp load_op; |
| 177 | VkAttachmentStoreOp store_op; |
| 178 | }; |
| 179 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 180 | // Associate fenceId with a fence object |
| 181 | struct MT_FENCE_INFO { |
| 182 | uint64_t fenceId; // Sequence number for fence at last submit |
| 183 | VkQueue queue; // Queue that this fence is submitted against or NULL |
| 184 | VkSwapchainKHR swapchain; // Swapchain that this fence is submitted against or NULL |
Dustin Graves | 8f1eab9 | 2016-04-05 09:41:17 -0600 | [diff] [blame] | 185 | bool firstTimeFlag; // Fence was created in signaled state, avoid warnings for first use |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 186 | VkFenceCreateInfo createInfo; |
| 187 | }; |
| 188 | |
| 189 | // Track Queue information |
| 190 | struct MT_QUEUE_INFO { |
| 191 | uint64_t lastRetiredId; |
| 192 | uint64_t lastSubmittedId; |
| 193 | list<VkCommandBuffer> pQueueCommandBuffers; |
| 194 | list<VkDeviceMemory> pMemRefList; |
| 195 | }; |
| 196 | |
| 197 | struct MT_DESCRIPTOR_SET_INFO { |
| 198 | std::vector<VkImageView> images; |
| 199 | std::vector<VkBuffer> buffers; |
| 200 | }; |
| 201 | |
| 202 | // Track Swapchain Information |
| 203 | struct MT_SWAP_CHAIN_INFO { |
| 204 | VkSwapchainCreateInfoKHR createInfo; |
| 205 | std::vector<VkImage> images; |
| 206 | }; |
| 207 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 208 | #endif |
| 209 | // Draw State ERROR codes |
| 210 | typedef enum _DRAW_STATE_ERROR { |
Michael Lentine | d464881 | 2016-03-24 20:48:59 -0500 | [diff] [blame] | 211 | // TODO: Remove the comments here or expand them. There isn't any additional information in the |
| 212 | // comments than in the name in almost all cases. |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 213 | DRAWSTATE_NONE, // Used for INFO & other non-error messages |
| 214 | DRAWSTATE_INTERNAL_ERROR, // Error with DrawState internal data structures |
| 215 | DRAWSTATE_NO_PIPELINE_BOUND, // Unable to identify a bound pipeline |
| 216 | DRAWSTATE_INVALID_POOL, // Invalid DS pool |
| 217 | DRAWSTATE_INVALID_SET, // Invalid DS |
Michael Lentine | d464881 | 2016-03-24 20:48:59 -0500 | [diff] [blame] | 218 | DRAWSTATE_INVALID_RENDER_AREA, // Invalid renderArea |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 219 | DRAWSTATE_INVALID_LAYOUT, // Invalid DS layout |
| 220 | DRAWSTATE_INVALID_IMAGE_LAYOUT, // Invalid Image layout |
| 221 | DRAWSTATE_INVALID_PIPELINE, // Invalid Pipeline handle referenced |
| 222 | DRAWSTATE_INVALID_PIPELINE_LAYOUT, // Invalid PipelineLayout |
| 223 | DRAWSTATE_INVALID_PIPELINE_CREATE_STATE, // Attempt to create a pipeline |
| 224 | // with invalid state |
| 225 | DRAWSTATE_INVALID_COMMAND_BUFFER, // Invalid CommandBuffer referenced |
| 226 | DRAWSTATE_INVALID_BARRIER, // Invalid Barrier |
| 227 | DRAWSTATE_INVALID_BUFFER, // Invalid Buffer |
| 228 | DRAWSTATE_INVALID_QUERY, // Invalid Query |
| 229 | DRAWSTATE_INVALID_FENCE, // Invalid Fence |
| 230 | DRAWSTATE_INVALID_SEMAPHORE, // Invalid Semaphore |
| 231 | DRAWSTATE_INVALID_EVENT, // Invalid Event |
| 232 | DRAWSTATE_VTX_INDEX_OUT_OF_BOUNDS, // binding in vkCmdBindVertexData() too |
| 233 | // large for PSO's |
| 234 | // pVertexBindingDescriptions array |
| 235 | DRAWSTATE_VTX_INDEX_ALIGNMENT_ERROR, // binding offset in |
| 236 | // vkCmdBindIndexBuffer() out of |
| 237 | // alignment based on indexType |
| 238 | // DRAWSTATE_MISSING_DOT_PROGRAM, // No "dot" program in order |
| 239 | // to generate png image |
| 240 | DRAWSTATE_OUT_OF_MEMORY, // malloc failed |
| 241 | DRAWSTATE_INVALID_DESCRIPTOR_SET, // Descriptor Set handle is unknown |
| 242 | DRAWSTATE_DESCRIPTOR_TYPE_MISMATCH, // Type in layout vs. update are not the |
| 243 | // same |
| 244 | DRAWSTATE_DESCRIPTOR_STAGEFLAGS_MISMATCH, // StageFlags in layout are not |
| 245 | // the same throughout a single |
| 246 | // VkWriteDescriptorSet update |
| 247 | DRAWSTATE_DESCRIPTOR_UPDATE_OUT_OF_BOUNDS, // Descriptors set for update out |
| 248 | // of bounds for corresponding |
| 249 | // layout section |
| 250 | DRAWSTATE_DESCRIPTOR_POOL_EMPTY, // Attempt to allocate descriptor from a |
| 251 | // pool with no more descriptors of that |
| 252 | // type available |
| 253 | DRAWSTATE_CANT_FREE_FROM_NON_FREE_POOL, // Invalid to call |
| 254 | // vkFreeDescriptorSets on Sets |
| 255 | // allocated from a NON_FREE Pool |
| 256 | DRAWSTATE_INVALID_UPDATE_INDEX, // Index of requested update is invalid for |
| 257 | // specified descriptors set |
| 258 | DRAWSTATE_INVALID_UPDATE_STRUCT, // Struct in DS Update tree is of invalid |
| 259 | // type |
| 260 | DRAWSTATE_NUM_SAMPLES_MISMATCH, // Number of samples in bound PSO does not |
| 261 | // match number in FB of current RenderPass |
| 262 | DRAWSTATE_NO_END_COMMAND_BUFFER, // Must call vkEndCommandBuffer() before |
| 263 | // QueueSubmit on that commandBuffer |
| 264 | DRAWSTATE_NO_BEGIN_COMMAND_BUFFER, // Binding cmds or calling End on CB that |
| 265 | // never had vkBeginCommandBuffer() |
| 266 | // called on it |
| 267 | DRAWSTATE_COMMAND_BUFFER_SINGLE_SUBMIT_VIOLATION, // Cmd Buffer created with |
| 268 | // VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT |
| 269 | // flag is submitted |
| 270 | // multiple times |
| 271 | DRAWSTATE_INVALID_SECONDARY_COMMAND_BUFFER, // vkCmdExecuteCommands() called |
| 272 | // with a primary commandBuffer |
| 273 | // in pCommandBuffers array |
| 274 | DRAWSTATE_VIEWPORT_NOT_BOUND, // Draw submitted with no viewport state bound |
| 275 | DRAWSTATE_SCISSOR_NOT_BOUND, // Draw submitted with no scissor state bound |
| 276 | DRAWSTATE_LINE_WIDTH_NOT_BOUND, // Draw submitted with no line width state |
| 277 | // bound |
| 278 | DRAWSTATE_DEPTH_BIAS_NOT_BOUND, // Draw submitted with no depth bias state |
| 279 | // bound |
| 280 | DRAWSTATE_BLEND_NOT_BOUND, // Draw submitted with no blend state bound when |
| 281 | // color write enabled |
| 282 | DRAWSTATE_DEPTH_BOUNDS_NOT_BOUND, // Draw submitted with no depth bounds |
| 283 | // state bound when depth enabled |
| 284 | DRAWSTATE_STENCIL_NOT_BOUND, // Draw submitted with no stencil state bound |
| 285 | // when stencil enabled |
| 286 | DRAWSTATE_INDEX_BUFFER_NOT_BOUND, // Draw submitted with no depth-stencil |
| 287 | // state bound when depth write enabled |
| 288 | DRAWSTATE_PIPELINE_LAYOUTS_INCOMPATIBLE, // Draw submitted PSO Pipeline |
| 289 | // layout that's not compatible |
| 290 | // with layout from |
| 291 | // BindDescriptorSets |
| 292 | DRAWSTATE_RENDERPASS_INCOMPATIBLE, // Incompatible renderpasses between |
| 293 | // secondary cmdBuffer and primary |
| 294 | // cmdBuffer or framebuffer |
| 295 | DRAWSTATE_FRAMEBUFFER_INCOMPATIBLE, // Incompatible framebuffer between |
| 296 | // secondary cmdBuffer and active |
| 297 | // renderPass |
| 298 | DRAWSTATE_INVALID_RENDERPASS, // Use of a NULL or otherwise invalid |
| 299 | // RenderPass object |
| 300 | DRAWSTATE_INVALID_RENDERPASS_CMD, // Invalid cmd submitted while a |
| 301 | // RenderPass is active |
| 302 | DRAWSTATE_NO_ACTIVE_RENDERPASS, // Rendering cmd submitted without an active |
| 303 | // RenderPass |
| 304 | DRAWSTATE_DESCRIPTOR_SET_NOT_UPDATED, // DescriptorSet bound but it was |
| 305 | // never updated. This is a warning |
| 306 | // code. |
| 307 | DRAWSTATE_DESCRIPTOR_SET_NOT_BOUND, // DescriptorSet used by pipeline at |
| 308 | // draw time is not bound, or has been |
| 309 | // disturbed (which would have flagged |
| 310 | // previous warning) |
| 311 | DRAWSTATE_INVALID_DYNAMIC_OFFSET_COUNT, // DescriptorSets bound with |
| 312 | // different number of dynamic |
| 313 | // descriptors that were included in |
| 314 | // dynamicOffsetCount |
| 315 | DRAWSTATE_CLEAR_CMD_BEFORE_DRAW, // Clear cmd issued before any Draw in |
| 316 | // CommandBuffer, should use RenderPass Ops |
| 317 | // instead |
| 318 | DRAWSTATE_BEGIN_CB_INVALID_STATE, // CB state at Begin call is bad. Can be |
| 319 | // Primary/Secondary CB created with |
| 320 | // mismatched FB/RP information or CB in |
| 321 | // RECORDING state |
| 322 | DRAWSTATE_INVALID_CB_SIMULTANEOUS_USE, // CmdBuffer is being used in |
| 323 | // violation of |
| 324 | // VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT |
| 325 | // rules (i.e. simultaneous use w/o |
| 326 | // that bit set) |
| 327 | DRAWSTATE_INVALID_COMMAND_BUFFER_RESET, // Attempting to call Reset (or |
| 328 | // Begin on recorded cmdBuffer) that |
| 329 | // was allocated from Pool w/o |
| 330 | // VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT |
| 331 | // bit set |
| 332 | DRAWSTATE_VIEWPORT_SCISSOR_MISMATCH, // Count for viewports and scissors |
| 333 | // mismatch and/or state doesn't match |
| 334 | // count |
| 335 | DRAWSTATE_INVALID_IMAGE_ASPECT, // Image aspect is invalid for the current |
| 336 | // operation |
| 337 | DRAWSTATE_MISSING_ATTACHMENT_REFERENCE, // Attachment reference must be |
| 338 | // present in active subpass |
| 339 | DRAWSTATE_SAMPLER_DESCRIPTOR_ERROR, // A Descriptor of *_SAMPLER type is |
| 340 | // being updated with an invalid or bad |
| 341 | // Sampler |
| 342 | DRAWSTATE_INCONSISTENT_IMMUTABLE_SAMPLER_UPDATE, // Descriptors of |
| 343 | // *COMBINED_IMAGE_SAMPLER |
| 344 | // type are being updated |
| 345 | // where some, but not all, |
| 346 | // of the updates use |
| 347 | // immutable samplers |
| 348 | DRAWSTATE_IMAGEVIEW_DESCRIPTOR_ERROR, // A Descriptor of *_IMAGE or |
| 349 | // *_ATTACHMENT type is being updated |
| 350 | // with an invalid or bad ImageView |
| 351 | DRAWSTATE_BUFFERVIEW_DESCRIPTOR_ERROR, // A Descriptor of *_TEXEL_BUFFER |
| 352 | // type is being updated with an |
| 353 | // invalid or bad BufferView |
| 354 | DRAWSTATE_BUFFERINFO_DESCRIPTOR_ERROR, // A Descriptor of |
| 355 | // *_[UNIFORM|STORAGE]_BUFFER_[DYNAMIC] |
| 356 | // type is being updated with an |
| 357 | // invalid or bad BufferView |
| 358 | DRAWSTATE_DYNAMIC_OFFSET_OVERFLOW, // At draw time the dynamic offset |
| 359 | // combined with buffer offset and range |
| 360 | // oversteps size of buffer |
| 361 | DRAWSTATE_DOUBLE_DESTROY, // Destroying an object twice |
| 362 | DRAWSTATE_OBJECT_INUSE, // Destroying or modifying an object in use by a |
| 363 | // command buffer |
| 364 | DRAWSTATE_QUEUE_FORWARD_PROGRESS, // Queue cannot guarantee forward progress |
Dustin Graves | a97c394 | 2016-03-31 18:01:37 -0600 | [diff] [blame] | 365 | DRAWSTATE_INVALID_BUFFER_MEMORY_OFFSET, // Dynamic Buffer Offset |
| 366 | // violates memory requirements limit |
| 367 | DRAWSTATE_INVALID_TEXEL_BUFFER_OFFSET, // Dynamic Texel Buffer Offsets |
| 368 | // violate device limit |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 369 | DRAWSTATE_INVALID_UNIFORM_BUFFER_OFFSET, // Dynamic Uniform Buffer Offsets |
| 370 | // violate device limit |
| 371 | DRAWSTATE_INVALID_STORAGE_BUFFER_OFFSET, // Dynamic Storage Buffer Offsets |
| 372 | // violate device limit |
| 373 | DRAWSTATE_INDEPENDENT_BLEND, // If independent blending is not enabled, all |
| 374 | // elements of pAttachmentsMustBeIdentical |
| 375 | DRAWSTATE_DISABLED_LOGIC_OP, // If logic operations is not enabled, logicOpEnable |
| 376 | // must be VK_FALSE |
| 377 | DRAWSTATE_INVALID_LOGIC_OP, // If logicOpEnable is VK_TRUE, logicOp must |
| 378 | // must be a valid VkLogicOp value |
| 379 | DRAWSTATE_INVALID_QUEUE_INDEX, // Specified queue index exceeds number |
| 380 | // of queried queue families |
| 381 | DRAWSTATE_PUSH_CONSTANTS_ERROR, // Push constants exceed maxPushConstantSize |
| 382 | } DRAW_STATE_ERROR; |
| 383 | |
| 384 | typedef enum _SHADER_CHECKER_ERROR { |
| 385 | SHADER_CHECKER_NONE, |
| 386 | SHADER_CHECKER_INTERFACE_TYPE_MISMATCH, // Type mismatch between shader stages or shader and pipeline |
| 387 | SHADER_CHECKER_OUTPUT_NOT_CONSUMED, // Entry appears in output interface, but missing in input |
| 388 | SHADER_CHECKER_INPUT_NOT_PRODUCED, // Entry appears in input interface, but missing in output |
| 389 | SHADER_CHECKER_NON_SPIRV_SHADER, // Shader image is not SPIR-V |
| 390 | SHADER_CHECKER_INCONSISTENT_SPIRV, // General inconsistency within a SPIR-V module |
| 391 | SHADER_CHECKER_UNKNOWN_STAGE, // Stage is not supported by analysis |
| 392 | SHADER_CHECKER_INCONSISTENT_VI, // VI state contains conflicting binding or attrib descriptions |
| 393 | SHADER_CHECKER_MISSING_DESCRIPTOR, // Shader attempts to use a descriptor binding not declared in the layout |
| 394 | SHADER_CHECKER_BAD_SPECIALIZATION, // Specialization map entry points outside specialization data block |
| 395 | SHADER_CHECKER_MISSING_ENTRYPOINT, // Shader module does not contain the requested entrypoint |
| 396 | SHADER_CHECKER_PUSH_CONSTANT_OUT_OF_RANGE, // Push constant variable is not in a push constant range |
| 397 | SHADER_CHECKER_PUSH_CONSTANT_NOT_ACCESSIBLE_FROM_STAGE, // Push constant range exists, but not accessible from stage |
| 398 | SHADER_CHECKER_DESCRIPTOR_TYPE_MISMATCH, // Descriptor type does not match shader resource type |
| 399 | SHADER_CHECKER_DESCRIPTOR_NOT_ACCESSIBLE_FROM_STAGE, // Descriptor used by shader, but not accessible from stage |
Chris Forbes | c3a1dbc | 2016-03-15 10:12:48 +1300 | [diff] [blame] | 400 | SHADER_CHECKER_FEATURE_NOT_ENABLED, // Shader uses capability requiring a feature not enabled on device |
| 401 | SHADER_CHECKER_BAD_CAPABILITY, // Shader uses capability not supported by Vulkan (OpenCL features) |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 402 | } SHADER_CHECKER_ERROR; |
| 403 | |
| 404 | typedef enum _DRAW_TYPE { |
| 405 | DRAW = 0, |
| 406 | DRAW_INDEXED = 1, |
| 407 | DRAW_INDIRECT = 2, |
| 408 | DRAW_INDEXED_INDIRECT = 3, |
| 409 | DRAW_BEGIN_RANGE = DRAW, |
| 410 | DRAW_END_RANGE = DRAW_INDEXED_INDIRECT, |
| 411 | NUM_DRAW_TYPES = (DRAW_END_RANGE - DRAW_BEGIN_RANGE + 1), |
| 412 | } DRAW_TYPE; |
| 413 | |
| 414 | typedef struct _SHADER_DS_MAPPING { |
| 415 | uint32_t slotCount; |
| 416 | VkDescriptorSetLayoutCreateInfo *pShaderMappingSlot; |
| 417 | } SHADER_DS_MAPPING; |
| 418 | |
| 419 | typedef struct _GENERIC_HEADER { |
| 420 | VkStructureType sType; |
| 421 | const void *pNext; |
| 422 | } GENERIC_HEADER; |
| 423 | |
Tobin Ehlis | 5f4cef1 | 2016-04-01 13:51:33 -0600 | [diff] [blame] | 424 | class PIPELINE_NODE { |
| 425 | public: |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 426 | VkPipeline pipeline; |
Tobin Ehlis | 5f4cef1 | 2016-04-01 13:51:33 -0600 | [diff] [blame] | 427 | safe_VkGraphicsPipelineCreateInfo graphicsPipelineCI; |
| 428 | safe_VkComputePipelineCreateInfo computePipelineCI; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 429 | // Flag of which shader stages are active for this pipeline |
| 430 | uint32_t active_shaders; |
Chris Forbes | 3001c77 | 2016-04-06 11:21:28 +1200 | [diff] [blame] | 431 | uint32_t duplicate_shaders; |
Tobin Ehlis | 81e8ca4 | 2016-03-24 09:17:25 -0600 | [diff] [blame] | 432 | // Capture which slots (set#->bindings) are actually used by the shaders of this pipeline |
| 433 | unordered_map<uint32_t, unordered_set<uint32_t>> active_slots; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 434 | // Vtx input info (if any) |
Chris Forbes | ec1ed13 | 2016-03-18 11:07:59 +1300 | [diff] [blame] | 435 | std::vector<VkVertexInputBindingDescription> vertexBindingDescriptions; |
| 436 | std::vector<VkVertexInputAttributeDescription> vertexAttributeDescriptions; |
| 437 | std::vector<VkPipelineColorBlendAttachmentState> attachments; |
Tobin Ehlis | 7a1d235 | 2016-03-28 11:18:19 -0600 | [diff] [blame] | 438 | bool blendConstantsEnabled; // Blend constants enabled for any attachments |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 439 | // Default constructor |
Tobin Ehlis | 5f4cef1 | 2016-04-01 13:51:33 -0600 | [diff] [blame] | 440 | PIPELINE_NODE() |
Chris Forbes | 3001c77 | 2016-04-06 11:21:28 +1200 | [diff] [blame] | 441 | : pipeline{}, graphicsPipelineCI{}, computePipelineCI{}, active_shaders(0), duplicate_shaders(0), active_slots(), vertexBindingDescriptions(), |
Tobin Ehlis | 5f4cef1 | 2016-04-01 13:51:33 -0600 | [diff] [blame] | 442 | vertexAttributeDescriptions(), attachments(), blendConstantsEnabled(false) {} |
| 443 | |
| 444 | void initGraphicsPipeline(const VkGraphicsPipelineCreateInfo *pCreateInfo) { |
| 445 | graphicsPipelineCI.initialize(pCreateInfo); |
| 446 | // Make sure compute pipeline is null |
| 447 | VkComputePipelineCreateInfo emptyComputeCI = {}; |
| 448 | computePipelineCI.initialize(&emptyComputeCI); |
| 449 | for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) { |
| 450 | const VkPipelineShaderStageCreateInfo *pPSSCI = &pCreateInfo->pStages[i]; |
Chris Forbes | 3001c77 | 2016-04-06 11:21:28 +1200 | [diff] [blame] | 451 | this->duplicate_shaders |= this->active_shaders & pPSSCI->stage; |
| 452 | this->active_shaders |= pPSSCI->stage; |
Tobin Ehlis | 5f4cef1 | 2016-04-01 13:51:33 -0600 | [diff] [blame] | 453 | } |
| 454 | if (pCreateInfo->pVertexInputState) { |
| 455 | const VkPipelineVertexInputStateCreateInfo *pVICI = pCreateInfo->pVertexInputState; |
| 456 | if (pVICI->vertexBindingDescriptionCount) { |
| 457 | this->vertexBindingDescriptions = std::vector<VkVertexInputBindingDescription>( |
| 458 | pVICI->pVertexBindingDescriptions, pVICI->pVertexBindingDescriptions + pVICI->vertexBindingDescriptionCount); |
| 459 | } |
| 460 | if (pVICI->vertexAttributeDescriptionCount) { |
| 461 | this->vertexAttributeDescriptions = std::vector<VkVertexInputAttributeDescription>( |
| 462 | pVICI->pVertexAttributeDescriptions, |
| 463 | pVICI->pVertexAttributeDescriptions + pVICI->vertexAttributeDescriptionCount); |
| 464 | } |
| 465 | } |
| 466 | if (pCreateInfo->pColorBlendState) { |
| 467 | const VkPipelineColorBlendStateCreateInfo *pCBCI = pCreateInfo->pColorBlendState; |
| 468 | if (pCBCI->attachmentCount) { |
| 469 | this->attachments = std::vector<VkPipelineColorBlendAttachmentState>(pCBCI->pAttachments, |
| 470 | pCBCI->pAttachments + pCBCI->attachmentCount); |
| 471 | } |
| 472 | } |
| 473 | } |
| 474 | void initComputePipeline(const VkComputePipelineCreateInfo *pCreateInfo) { |
| 475 | computePipelineCI.initialize(pCreateInfo); |
| 476 | // Make sure gfx pipeline is null |
| 477 | VkGraphicsPipelineCreateInfo emptyGraphicsCI = {}; |
| 478 | graphicsPipelineCI.initialize(&emptyGraphicsCI); |
| 479 | switch (computePipelineCI.stage.stage) { |
| 480 | case VK_SHADER_STAGE_COMPUTE_BIT: |
| 481 | this->active_shaders |= VK_SHADER_STAGE_COMPUTE_BIT; |
| 482 | break; |
| 483 | default: |
| 484 | // TODO : Flag error |
| 485 | break; |
| 486 | } |
| 487 | } |
| 488 | }; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 489 | |
| 490 | class BASE_NODE { |
| 491 | public: |
| 492 | std::atomic_int in_use; |
| 493 | }; |
| 494 | |
| 495 | typedef struct _SAMPLER_NODE { |
| 496 | VkSampler sampler; |
| 497 | VkSamplerCreateInfo createInfo; |
| 498 | |
| 499 | _SAMPLER_NODE(const VkSampler *ps, const VkSamplerCreateInfo *pci) : sampler(*ps), createInfo(*pci){}; |
| 500 | } SAMPLER_NODE; |
| 501 | |
| 502 | class IMAGE_NODE : public BASE_NODE { |
| 503 | public: |
| 504 | VkImageCreateInfo createInfo; |
| 505 | VkDeviceMemory mem; |
Tobin Ehlis | f263ba4 | 2016-04-05 13:33:00 -0600 | [diff] [blame] | 506 | bool valid; // If this is a swapchain image backing memory track valid here as it doesn't have DEVICE_MEM_INFO |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 507 | VkDeviceSize memOffset; |
| 508 | VkDeviceSize memSize; |
| 509 | }; |
| 510 | |
| 511 | typedef struct _IMAGE_LAYOUT_NODE { |
| 512 | VkImageLayout layout; |
| 513 | VkFormat format; |
| 514 | } IMAGE_LAYOUT_NODE; |
| 515 | |
Michael Lentine | 60063c2 | 2016-03-24 15:36:27 -0500 | [diff] [blame] | 516 | class IMAGE_CMD_BUF_LAYOUT_NODE { |
| 517 | public: |
| 518 | IMAGE_CMD_BUF_LAYOUT_NODE() {} |
| 519 | IMAGE_CMD_BUF_LAYOUT_NODE(VkImageLayout initialLayoutInput, VkImageLayout layoutInput) |
| 520 | : initialLayout(initialLayoutInput), layout(layoutInput) {} |
| 521 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 522 | VkImageLayout initialLayout; |
| 523 | VkImageLayout layout; |
Michael Lentine | 60063c2 | 2016-03-24 15:36:27 -0500 | [diff] [blame] | 524 | }; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 525 | |
| 526 | class BUFFER_NODE : public BASE_NODE { |
| 527 | public: |
| 528 | using BASE_NODE::in_use; |
Tobin Ehlis | f263ba4 | 2016-04-05 13:33:00 -0600 | [diff] [blame] | 529 | VkDeviceMemory mem; |
| 530 | VkBufferCreateInfo createInfo; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 531 | }; |
| 532 | |
| 533 | // Store the DAG. |
| 534 | struct DAGNode { |
| 535 | uint32_t pass; |
| 536 | std::vector<uint32_t> prev; |
| 537 | std::vector<uint32_t> next; |
| 538 | }; |
| 539 | |
| 540 | struct RENDER_PASS_NODE { |
| 541 | VkRenderPassCreateInfo const *pCreateInfo; |
Tobin Ehlis | 5494852 | 2016-03-22 13:50:21 -0600 | [diff] [blame] | 542 | VkFramebuffer fb; |
| 543 | vector<bool> hasSelfDependency; |
| 544 | vector<DAGNode> subpassToNode; |
| 545 | vector<vector<VkFormat>> subpassColorFormats; |
| 546 | vector<MT_PASS_ATTACHMENT_INFO> attachments; |
| 547 | unordered_map<uint32_t, bool> attachment_first_read; |
| 548 | unordered_map<uint32_t, VkImageLayout> attachment_first_layout; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 549 | |
Tobin Ehlis | 5494852 | 2016-03-22 13:50:21 -0600 | [diff] [blame] | 550 | RENDER_PASS_NODE(VkRenderPassCreateInfo const *pCreateInfo) : pCreateInfo(pCreateInfo), fb(VK_NULL_HANDLE) { |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 551 | uint32_t i; |
| 552 | |
| 553 | subpassColorFormats.reserve(pCreateInfo->subpassCount); |
| 554 | for (i = 0; i < pCreateInfo->subpassCount; i++) { |
| 555 | const VkSubpassDescription *subpass = &pCreateInfo->pSubpasses[i]; |
| 556 | vector<VkFormat> color_formats; |
| 557 | uint32_t j; |
| 558 | |
| 559 | color_formats.reserve(subpass->colorAttachmentCount); |
| 560 | for (j = 0; j < subpass->colorAttachmentCount; j++) { |
| 561 | const uint32_t att = subpass->pColorAttachments[j].attachment; |
| 562 | const VkFormat format = pCreateInfo->pAttachments[att].format; |
| 563 | |
| 564 | color_formats.push_back(format); |
| 565 | } |
| 566 | |
| 567 | subpassColorFormats.push_back(color_formats); |
| 568 | } |
| 569 | } |
| 570 | }; |
| 571 | |
| 572 | class PHYS_DEV_PROPERTIES_NODE { |
| 573 | public: |
| 574 | VkPhysicalDeviceProperties properties; |
| 575 | VkPhysicalDeviceFeatures features; |
| 576 | vector<VkQueueFamilyProperties> queue_family_properties; |
| 577 | }; |
| 578 | |
| 579 | class FENCE_NODE : public BASE_NODE { |
| 580 | public: |
| 581 | using BASE_NODE::in_use; |
| 582 | #if MTMERGE |
| 583 | uint64_t fenceId; // Sequence number for fence at last submit |
| 584 | VkSwapchainKHR swapchain; // Swapchain that this fence is submitted against or NULL |
Dustin Graves | 8f1eab9 | 2016-04-05 09:41:17 -0600 | [diff] [blame] | 585 | bool firstTimeFlag; // Fence was created in signaled state, avoid warnings for first use |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 586 | VkFenceCreateInfo createInfo; |
| 587 | #endif |
| 588 | VkQueue queue; |
| 589 | vector<VkCommandBuffer> cmdBuffers; |
| 590 | bool needsSignaled; |
Michael Lentine | d4f382b | 2016-03-17 16:34:32 -0500 | [diff] [blame] | 591 | vector<VkFence> priorFences; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 592 | |
| 593 | // Default constructor |
Michael Lentine | d4f382b | 2016-03-17 16:34:32 -0500 | [diff] [blame] | 594 | FENCE_NODE() : queue(NULL), needsSignaled(VK_FALSE){}; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 595 | }; |
| 596 | |
| 597 | class SEMAPHORE_NODE : public BASE_NODE { |
| 598 | public: |
| 599 | using BASE_NODE::in_use; |
Tobin Ehlis | 9984f1e | 2016-04-12 10:49:41 -0600 | [diff] [blame] | 600 | bool signaled; |
Michael Lentine | d4f382b | 2016-03-17 16:34:32 -0500 | [diff] [blame] | 601 | VkQueue queue; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 602 | }; |
| 603 | |
| 604 | class EVENT_NODE : public BASE_NODE { |
| 605 | public: |
| 606 | using BASE_NODE::in_use; |
| 607 | bool needsSignaled; |
| 608 | VkPipelineStageFlags stageMask; |
| 609 | }; |
| 610 | |
| 611 | class QUEUE_NODE { |
| 612 | public: |
| 613 | VkDevice device; |
Michael Lentine | d4f382b | 2016-03-17 16:34:32 -0500 | [diff] [blame] | 614 | vector<VkFence> lastFences; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 615 | #if MTMERGE |
| 616 | uint64_t lastRetiredId; |
| 617 | uint64_t lastSubmittedId; |
| 618 | // MTMTODO : merge cmd_buffer data structs here |
| 619 | list<VkCommandBuffer> pQueueCommandBuffers; |
| 620 | list<VkDeviceMemory> pMemRefList; |
| 621 | #endif |
| 622 | vector<VkCommandBuffer> untrackedCmdBuffers; |
| 623 | unordered_set<VkCommandBuffer> inFlightCmdBuffers; |
Michael Lentine | b653eb2 | 2016-03-18 14:11:44 -0500 | [diff] [blame] | 624 | unordered_map<VkEvent, VkPipelineStageFlags> eventToStageMap; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 625 | }; |
| 626 | |
| 627 | class QUERY_POOL_NODE : public BASE_NODE { |
| 628 | public: |
| 629 | VkQueryPoolCreateInfo createInfo; |
| 630 | }; |
| 631 | |
| 632 | class FRAMEBUFFER_NODE { |
| 633 | public: |
| 634 | VkFramebufferCreateInfo createInfo; |
| 635 | unordered_set<VkCommandBuffer> referencingCmdBuffers; |
Tobin Ehlis | 4953c82 | 2016-03-22 13:38:08 -0600 | [diff] [blame] | 636 | vector<MT_FB_ATTACHMENT_INFO> attachments; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 637 | }; |
| 638 | |
| 639 | // Descriptor Data structures |
| 640 | // Layout Node has the core layout data |
| 641 | typedef struct _LAYOUT_NODE { |
| 642 | VkDescriptorSetLayout layout; |
| 643 | VkDescriptorSetLayoutCreateInfo createInfo; |
| 644 | uint32_t startIndex; // 1st index of this layout |
| 645 | uint32_t endIndex; // last index of this layout |
| 646 | uint32_t dynamicDescriptorCount; // Total count of dynamic descriptors used |
| 647 | // by this layout |
| 648 | vector<VkDescriptorType> descriptorTypes; // Type per descriptor in this |
| 649 | // layout to verify correct |
| 650 | // updates |
| 651 | vector<VkShaderStageFlags> stageFlags; // stageFlags per descriptor in this |
| 652 | // layout to verify correct updates |
| 653 | unordered_map<uint32_t, uint32_t> bindingToIndexMap; // map set binding # to |
Tobin Ehlis | 81e8ca4 | 2016-03-24 09:17:25 -0600 | [diff] [blame] | 654 | // createInfo.pBindings index |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 655 | // Default constructor |
| 656 | _LAYOUT_NODE() : layout{}, createInfo{}, startIndex(0), endIndex(0), dynamicDescriptorCount(0){}; |
| 657 | } LAYOUT_NODE; |
| 658 | |
| 659 | // Store layouts and pushconstants for PipelineLayout |
| 660 | struct PIPELINE_LAYOUT_NODE { |
| 661 | vector<VkDescriptorSetLayout> descriptorSetLayouts; |
| 662 | vector<VkPushConstantRange> pushConstantRanges; |
| 663 | }; |
| 664 | |
| 665 | class SET_NODE : public BASE_NODE { |
| 666 | public: |
| 667 | using BASE_NODE::in_use; |
| 668 | VkDescriptorSet set; |
| 669 | VkDescriptorPool pool; |
| 670 | // Head of LL of all Update structs for this set |
| 671 | GENERIC_HEADER *pUpdateStructs; |
| 672 | // Total num of descriptors in this set (count of its layout plus all prior layouts) |
| 673 | uint32_t descriptorCount; |
Tobin Ehlis | 38f5bbb | 2016-03-24 10:16:09 -0600 | [diff] [blame] | 674 | vector<GENERIC_HEADER*> pDescriptorUpdates; // Vector where each index points to update node for its slot |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 675 | LAYOUT_NODE *pLayout; // Layout for this set |
| 676 | SET_NODE *pNext; |
| 677 | unordered_set<VkCommandBuffer> boundCmdBuffers; // Cmd buffers that this set has been bound to |
Tobin Ehlis | 38f5bbb | 2016-03-24 10:16:09 -0600 | [diff] [blame] | 678 | SET_NODE() : set(VK_NULL_HANDLE), pool(VK_NULL_HANDLE), pUpdateStructs(nullptr), pLayout(nullptr), pNext(nullptr){}; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 679 | }; |
| 680 | |
| 681 | typedef struct _DESCRIPTOR_POOL_NODE { |
| 682 | VkDescriptorPool pool; |
Mark Lobodzinski | a3efb0c | 2016-03-21 16:32:53 -0600 | [diff] [blame] | 683 | uint32_t maxSets; // Max descriptor sets allowed in this pool |
Tobin Ehlis | 1cee141 | 2016-03-30 12:20:53 -0600 | [diff] [blame] | 684 | uint32_t availableSets; // Available descriptor sets in this pool |
Mark Lobodzinski | a3efb0c | 2016-03-21 16:32:53 -0600 | [diff] [blame] | 685 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 686 | VkDescriptorPoolCreateInfo createInfo; |
| 687 | SET_NODE *pSets; // Head of LL of sets for this Pool |
Mark Lobodzinski | a3efb0c | 2016-03-21 16:32:53 -0600 | [diff] [blame] | 688 | vector<uint32_t> maxDescriptorTypeCount; // Max # of descriptors of each type in this pool |
| 689 | vector<uint32_t> availableDescriptorTypeCount; // Available # of descriptors of each type in this pool |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 690 | |
| 691 | _DESCRIPTOR_POOL_NODE(const VkDescriptorPool pool, const VkDescriptorPoolCreateInfo *pCreateInfo) |
Mark Lobodzinski | a3efb0c | 2016-03-21 16:32:53 -0600 | [diff] [blame] | 692 | : pool(pool), maxSets(pCreateInfo->maxSets), availableSets(pCreateInfo->maxSets), createInfo(*pCreateInfo), pSets(NULL), |
Norbert Nopper | 83e6c69 | 2016-04-13 01:08:36 +0200 | [diff] [blame] | 693 | maxDescriptorTypeCount(VK_DESCRIPTOR_TYPE_RANGE_SIZE, 0), availableDescriptorTypeCount(VK_DESCRIPTOR_TYPE_RANGE_SIZE, 0) { |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 694 | if (createInfo.poolSizeCount) { // Shadow type struct from ptr into local struct |
| 695 | size_t poolSizeCountSize = createInfo.poolSizeCount * sizeof(VkDescriptorPoolSize); |
| 696 | createInfo.pPoolSizes = new VkDescriptorPoolSize[poolSizeCountSize]; |
| 697 | memcpy((void *)createInfo.pPoolSizes, pCreateInfo->pPoolSizes, poolSizeCountSize); |
| 698 | // Now set max counts for each descriptor type based on count of that type times maxSets |
| 699 | uint32_t i = 0; |
| 700 | for (i = 0; i < createInfo.poolSizeCount; ++i) { |
| 701 | uint32_t typeIndex = static_cast<uint32_t>(createInfo.pPoolSizes[i].type); |
Tobin Ehlis | bb888a2 | 2016-04-13 15:15:09 -0600 | [diff] [blame] | 702 | // Same descriptor types can appear several times |
Norbert Nopper | 83e6c69 | 2016-04-13 01:08:36 +0200 | [diff] [blame] | 703 | maxDescriptorTypeCount[typeIndex] += createInfo.pPoolSizes[i].descriptorCount; |
Mark Lobodzinski | a3efb0c | 2016-03-21 16:32:53 -0600 | [diff] [blame] | 704 | availableDescriptorTypeCount[typeIndex] = maxDescriptorTypeCount[typeIndex]; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 705 | } |
| 706 | } else { |
| 707 | createInfo.pPoolSizes = NULL; // Make sure this is NULL so we don't try to clean it up |
| 708 | } |
| 709 | } |
| 710 | ~_DESCRIPTOR_POOL_NODE() { |
| 711 | delete[] createInfo.pPoolSizes; |
| 712 | // TODO : pSets are currently freed in deletePools function which uses freeShadowUpdateTree function |
| 713 | // need to migrate that struct to smart ptrs for auto-cleanup |
| 714 | } |
| 715 | } DESCRIPTOR_POOL_NODE; |
| 716 | |
| 717 | // Cmd Buffer Tracking |
| 718 | typedef enum _CMD_TYPE { |
| 719 | CMD_BINDPIPELINE, |
| 720 | CMD_BINDPIPELINEDELTA, |
| 721 | CMD_SETVIEWPORTSTATE, |
| 722 | CMD_SETSCISSORSTATE, |
| 723 | CMD_SETLINEWIDTHSTATE, |
| 724 | CMD_SETDEPTHBIASSTATE, |
| 725 | CMD_SETBLENDSTATE, |
| 726 | CMD_SETDEPTHBOUNDSSTATE, |
| 727 | CMD_SETSTENCILREADMASKSTATE, |
| 728 | CMD_SETSTENCILWRITEMASKSTATE, |
| 729 | CMD_SETSTENCILREFERENCESTATE, |
| 730 | CMD_BINDDESCRIPTORSETS, |
| 731 | CMD_BINDINDEXBUFFER, |
| 732 | CMD_BINDVERTEXBUFFER, |
| 733 | CMD_DRAW, |
| 734 | CMD_DRAWINDEXED, |
| 735 | CMD_DRAWINDIRECT, |
| 736 | CMD_DRAWINDEXEDINDIRECT, |
| 737 | CMD_DISPATCH, |
| 738 | CMD_DISPATCHINDIRECT, |
| 739 | CMD_COPYBUFFER, |
| 740 | CMD_COPYIMAGE, |
| 741 | CMD_BLITIMAGE, |
| 742 | CMD_COPYBUFFERTOIMAGE, |
| 743 | CMD_COPYIMAGETOBUFFER, |
| 744 | CMD_CLONEIMAGEDATA, |
| 745 | CMD_UPDATEBUFFER, |
| 746 | CMD_FILLBUFFER, |
| 747 | CMD_CLEARCOLORIMAGE, |
| 748 | CMD_CLEARATTACHMENTS, |
| 749 | CMD_CLEARDEPTHSTENCILIMAGE, |
| 750 | CMD_RESOLVEIMAGE, |
| 751 | CMD_SETEVENT, |
| 752 | CMD_RESETEVENT, |
| 753 | CMD_WAITEVENTS, |
| 754 | CMD_PIPELINEBARRIER, |
| 755 | CMD_BEGINQUERY, |
| 756 | CMD_ENDQUERY, |
| 757 | CMD_RESETQUERYPOOL, |
| 758 | CMD_COPYQUERYPOOLRESULTS, |
| 759 | CMD_WRITETIMESTAMP, |
| 760 | CMD_PUSHCONSTANTS, |
| 761 | CMD_INITATOMICCOUNTERS, |
| 762 | CMD_LOADATOMICCOUNTERS, |
| 763 | CMD_SAVEATOMICCOUNTERS, |
| 764 | CMD_BEGINRENDERPASS, |
| 765 | CMD_NEXTSUBPASS, |
| 766 | CMD_ENDRENDERPASS, |
| 767 | CMD_EXECUTECOMMANDS, |
| 768 | } CMD_TYPE; |
| 769 | // Data structure for holding sequence of cmds in cmd buffer |
| 770 | typedef struct _CMD_NODE { |
| 771 | CMD_TYPE type; |
| 772 | uint64_t cmdNumber; |
| 773 | } CMD_NODE; |
| 774 | |
| 775 | typedef enum _CB_STATE { |
| 776 | CB_NEW, // Newly created CB w/o any cmds |
| 777 | CB_RECORDING, // BeginCB has been called on this CB |
| 778 | CB_RECORDED, // EndCB has been called on this CB |
| 779 | CB_INVALID // CB had a bound descriptor set destroyed or updated |
| 780 | } CB_STATE; |
| 781 | // CB Status -- used to track status of various bindings on cmd buffer objects |
| 782 | typedef VkFlags CBStatusFlags; |
| 783 | typedef enum _CBStatusFlagBits { |
Tobin Ehlis | 7a1d235 | 2016-03-28 11:18:19 -0600 | [diff] [blame] | 784 | // clang-format off |
| 785 | CBSTATUS_NONE = 0x00000000, // No status is set |
| 786 | CBSTATUS_VIEWPORT_SET = 0x00000001, // Viewport has been set |
| 787 | CBSTATUS_LINE_WIDTH_SET = 0x00000002, // Line width has been set |
| 788 | CBSTATUS_DEPTH_BIAS_SET = 0x00000004, // Depth bias has been set |
| 789 | CBSTATUS_BLEND_CONSTANTS_SET = 0x00000008, // Blend constants state has been set |
| 790 | CBSTATUS_DEPTH_BOUNDS_SET = 0x00000010, // Depth bounds state object has been set |
| 791 | CBSTATUS_STENCIL_READ_MASK_SET = 0x00000020, // Stencil read mask has been set |
| 792 | CBSTATUS_STENCIL_WRITE_MASK_SET = 0x00000040, // Stencil write mask has been set |
| 793 | CBSTATUS_STENCIL_REFERENCE_SET = 0x00000080, // Stencil reference has been set |
| 794 | CBSTATUS_INDEX_BUFFER_BOUND = 0x00000100, // Index buffer has been set |
| 795 | CBSTATUS_SCISSOR_SET = 0x00000200, // Scissor has been set |
| 796 | CBSTATUS_ALL = 0x000003FF, // All dynamic state set |
| 797 | // clang-format on |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 798 | } CBStatusFlagBits; |
| 799 | |
| 800 | typedef struct stencil_data { |
| 801 | uint32_t compareMask; |
| 802 | uint32_t writeMask; |
| 803 | uint32_t reference; |
| 804 | } CBStencilData; |
| 805 | |
| 806 | typedef struct _DRAW_DATA { vector<VkBuffer> buffers; } DRAW_DATA; |
| 807 | |
| 808 | struct ImageSubresourcePair { |
| 809 | VkImage image; |
| 810 | bool hasSubresource; |
| 811 | VkImageSubresource subresource; |
| 812 | }; |
| 813 | |
| 814 | bool operator==(const ImageSubresourcePair &img1, const ImageSubresourcePair &img2) { |
| 815 | if (img1.image != img2.image || img1.hasSubresource != img2.hasSubresource) |
| 816 | return false; |
| 817 | return !img1.hasSubresource || |
| 818 | (img1.subresource.aspectMask == img2.subresource.aspectMask && img1.subresource.mipLevel == img2.subresource.mipLevel && |
| 819 | img1.subresource.arrayLayer == img2.subresource.arrayLayer); |
| 820 | } |
| 821 | |
| 822 | namespace std { |
| 823 | template <> struct hash<ImageSubresourcePair> { |
| 824 | size_t operator()(ImageSubresourcePair img) const throw() { |
| 825 | size_t hashVal = hash<uint64_t>()(reinterpret_cast<uint64_t &>(img.image)); |
| 826 | hashVal ^= hash<bool>()(img.hasSubresource); |
| 827 | if (img.hasSubresource) { |
| 828 | hashVal ^= hash<uint32_t>()(reinterpret_cast<uint32_t &>(img.subresource.aspectMask)); |
| 829 | hashVal ^= hash<uint32_t>()(img.subresource.mipLevel); |
| 830 | hashVal ^= hash<uint32_t>()(img.subresource.arrayLayer); |
| 831 | } |
| 832 | return hashVal; |
| 833 | } |
| 834 | }; |
| 835 | } |
| 836 | |
| 837 | struct QueryObject { |
| 838 | VkQueryPool pool; |
| 839 | uint32_t index; |
| 840 | }; |
| 841 | |
| 842 | bool operator==(const QueryObject &query1, const QueryObject &query2) { |
| 843 | return (query1.pool == query2.pool && query1.index == query2.index); |
| 844 | } |
| 845 | |
| 846 | namespace std { |
| 847 | template <> struct hash<QueryObject> { |
| 848 | size_t operator()(QueryObject query) const throw() { |
| 849 | return hash<uint64_t>()((uint64_t)(query.pool)) ^ hash<uint32_t>()(query.index); |
| 850 | } |
| 851 | }; |
| 852 | } |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 853 | // Track last states that are bound per pipeline bind point (Gfx & Compute) |
| 854 | struct LAST_BOUND_STATE { |
| 855 | VkPipeline pipeline; |
| 856 | VkPipelineLayout pipelineLayout; |
| 857 | // Track each set that has been bound |
| 858 | // TODO : can unique be global per CB? (do we care about Gfx vs. Compute?) |
| 859 | unordered_set<VkDescriptorSet> uniqueBoundSets; |
| 860 | // Ordered bound set tracking where index is set# that given set is bound to |
| 861 | vector<VkDescriptorSet> boundDescriptorSets; |
| 862 | // one dynamic offset per dynamic descriptor bound to this CB |
| 863 | vector<uint32_t> dynamicOffsets; |
| 864 | void reset() { |
| 865 | pipeline = VK_NULL_HANDLE; |
| 866 | pipelineLayout = VK_NULL_HANDLE; |
| 867 | uniqueBoundSets.clear(); |
| 868 | boundDescriptorSets.clear(); |
| 869 | dynamicOffsets.clear(); |
| 870 | } |
| 871 | }; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 872 | // Cmd Buffer Wrapper Struct |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 873 | struct GLOBAL_CB_NODE { |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 874 | VkCommandBuffer commandBuffer; |
| 875 | VkCommandBufferAllocateInfo createInfo; |
| 876 | VkCommandBufferBeginInfo beginInfo; |
| 877 | VkCommandBufferInheritanceInfo inheritanceInfo; |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 878 | // VkFence fence; // fence tracking this cmd buffer |
| 879 | VkDevice device; // device this CB belongs to |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 880 | uint64_t numCmds; // number of cmds in this CB |
| 881 | uint64_t drawCount[NUM_DRAW_TYPES]; // Count of each type of draw in this CB |
| 882 | CB_STATE state; // Track cmd buffer update state |
| 883 | uint64_t submitCount; // Number of times CB has been submitted |
| 884 | CBStatusFlags status; // Track status of various bindings on cmd buffer |
| 885 | vector<CMD_NODE> cmds; // vector of commands bound to this command buffer |
| 886 | // Currently storing "lastBound" objects on per-CB basis |
| 887 | // long-term may want to create caches of "lastBound" states and could have |
| 888 | // each individual CMD_NODE referencing its own "lastBound" state |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 889 | // VkPipeline lastBoundPipeline; |
| 890 | // VkPipelineLayout lastBoundPipelineLayout; |
| 891 | // // Capture unique std::set of descriptorSets that are bound to this CB. |
| 892 | // std::set<VkDescriptorSet> uniqueBoundSets; |
| 893 | // vector<VkDescriptorSet> boundDescriptorSets; // Index is set# that given set is bound to |
| 894 | // Store last bound state for Gfx & Compute pipeline bind points |
| 895 | LAST_BOUND_STATE lastBound[VK_PIPELINE_BIND_POINT_RANGE_SIZE]; |
| 896 | |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 897 | vector<VkViewport> viewports; |
| 898 | vector<VkRect2D> scissors; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 899 | VkRenderPassBeginInfo activeRenderPassBeginInfo; |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 900 | uint64_t fenceId; |
| 901 | VkFence lastSubmittedFence; |
| 902 | VkQueue lastSubmittedQueue; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 903 | VkRenderPass activeRenderPass; |
| 904 | VkSubpassContents activeSubpassContents; |
| 905 | uint32_t activeSubpass; |
Mark Lobodzinski | 188b230 | 2016-04-12 10:41:59 -0600 | [diff] [blame^] | 906 | std::unordered_set<VkFramebuffer> framebuffers; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 907 | // Track descriptor sets that are destroyed or updated while bound to CB |
| 908 | // TODO : These data structures relate to tracking resources that invalidate |
| 909 | // a cmd buffer that references them. Need to unify how we handle these |
| 910 | // cases so we don't have different tracking data for each type. |
Chris Forbes | d50f389 | 2016-04-14 10:34:17 +1200 | [diff] [blame] | 911 | unordered_set<VkDescriptorSet> destroyedSets; |
| 912 | unordered_set<VkDescriptorSet> updatedSets; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 913 | unordered_set<VkFramebuffer> destroyedFramebuffers; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 914 | vector<VkEvent> waitedEvents; |
| 915 | vector<VkSemaphore> semaphores; |
| 916 | vector<VkEvent> events; |
| 917 | unordered_map<QueryObject, vector<VkEvent>> waitedEventsBeforeQueryReset; |
| 918 | unordered_map<QueryObject, bool> queryToStateMap; // 0 is unavailable, 1 is available |
| 919 | unordered_set<QueryObject> activeQueries; |
| 920 | unordered_set<QueryObject> startedQueries; |
| 921 | unordered_map<ImageSubresourcePair, IMAGE_CMD_BUF_LAYOUT_NODE> imageLayoutMap; |
| 922 | unordered_map<VkImage, vector<ImageSubresourcePair>> imageSubresourceMap; |
| 923 | unordered_map<VkEvent, VkPipelineStageFlags> eventToStageMap; |
| 924 | vector<DRAW_DATA> drawData; |
| 925 | DRAW_DATA currentDrawData; |
| 926 | VkCommandBuffer primaryCommandBuffer; |
Tobin Ehlis | 2e319d4 | 2016-03-25 11:49:51 -0600 | [diff] [blame] | 927 | // Track images and buffers that are updated by this CB at the point of a draw |
| 928 | unordered_set<VkImageView> updateImages; |
| 929 | unordered_set<VkBuffer> updateBuffers; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 930 | // If cmd buffer is primary, track secondary command buffers pending |
| 931 | // execution |
| 932 | std::unordered_set<VkCommandBuffer> secondaryCommandBuffers; |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 933 | // MTMTODO : Scrub these data fields and merge active sets w/ lastBound as appropriate |
Dustin Graves | 8f1eab9 | 2016-04-05 09:41:17 -0600 | [diff] [blame] | 934 | vector<std::function<bool()>> validate_functions; |
Chris Forbes | e6625b1 | 2016-03-31 18:11:28 +1300 | [diff] [blame] | 935 | std::unordered_set<VkDeviceMemory> memObjs; |
Michael Lentine | b653eb2 | 2016-03-18 14:11:44 -0500 | [diff] [blame] | 936 | vector<std::function<bool(VkQueue)>> eventUpdates; |
Tobin Ehlis | 223b01e | 2016-03-21 14:14:44 -0600 | [diff] [blame] | 937 | }; |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 938 | |
Tobin Ehlis | 43d7c52 | 2016-03-16 13:52:20 -0600 | [diff] [blame] | 939 | class SWAPCHAIN_NODE { |
| 940 | public: |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 941 | VkSwapchainCreateInfoKHR createInfo; |
| 942 | uint32_t *pQueueFamilyIndices; |
| 943 | std::vector<VkImage> images; |
Tobin Ehlis | 43d7c52 | 2016-03-16 13:52:20 -0600 | [diff] [blame] | 944 | SWAPCHAIN_NODE(const VkSwapchainCreateInfoKHR *pCreateInfo) : createInfo(*pCreateInfo), pQueueFamilyIndices(NULL) { |
Tobin Ehlis | c96f806 | 2016-03-09 16:12:48 -0700 | [diff] [blame] | 945 | if (pCreateInfo->queueFamilyIndexCount && pCreateInfo->imageSharingMode == VK_SHARING_MODE_CONCURRENT) { |
| 946 | pQueueFamilyIndices = new uint32_t[pCreateInfo->queueFamilyIndexCount]; |
| 947 | memcpy(pQueueFamilyIndices, pCreateInfo->pQueueFamilyIndices, pCreateInfo->queueFamilyIndexCount * sizeof(uint32_t)); |
| 948 | createInfo.pQueueFamilyIndices = pQueueFamilyIndices; |
| 949 | } |
| 950 | } |
Tobin Ehlis | 43d7c52 | 2016-03-16 13:52:20 -0600 | [diff] [blame] | 951 | ~SWAPCHAIN_NODE() { delete[] pQueueFamilyIndices; } |
| 952 | }; |