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