xref: /aosp_15_r20/external/vulkan-validation-layers/scripts/thread_safety_generator.py (revision b7893ccf7851cd6a48cc5a1e965257d8a5cdcc70)
1*b7893ccfSSadaf Ebrahimi#!/usr/bin/python3 -i
2*b7893ccfSSadaf Ebrahimi#
3*b7893ccfSSadaf Ebrahimi# Copyright (c) 2015-2019 The Khronos Group Inc.
4*b7893ccfSSadaf Ebrahimi# Copyright (c) 2015-2019 Valve Corporation
5*b7893ccfSSadaf Ebrahimi# Copyright (c) 2015-2019 LunarG, Inc.
6*b7893ccfSSadaf Ebrahimi# Copyright (c) 2015-2019 Google Inc.
7*b7893ccfSSadaf Ebrahimi#
8*b7893ccfSSadaf Ebrahimi# Licensed under the Apache License, Version 2.0 (the "License");
9*b7893ccfSSadaf Ebrahimi# you may not use this file except in compliance with the License.
10*b7893ccfSSadaf Ebrahimi# You may obtain a copy of the License at
11*b7893ccfSSadaf Ebrahimi#
12*b7893ccfSSadaf Ebrahimi#     http://www.apache.org/licenses/LICENSE-2.0
13*b7893ccfSSadaf Ebrahimi#
14*b7893ccfSSadaf Ebrahimi# Unless required by applicable law or agreed to in writing, software
15*b7893ccfSSadaf Ebrahimi# distributed under the License is distributed on an "AS IS" BASIS,
16*b7893ccfSSadaf Ebrahimi# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17*b7893ccfSSadaf Ebrahimi# See the License for the specific language governing permissions and
18*b7893ccfSSadaf Ebrahimi# limitations under the License.
19*b7893ccfSSadaf Ebrahimi#
20*b7893ccfSSadaf Ebrahimi# Author: Mike Stroyan <[email protected]>
21*b7893ccfSSadaf Ebrahimi# Author: Mark Lobodzinski <[email protected]>
22*b7893ccfSSadaf Ebrahimi
23*b7893ccfSSadaf Ebrahimiimport os,re,sys
24*b7893ccfSSadaf Ebrahimifrom generator import *
25*b7893ccfSSadaf Ebrahimifrom common_codegen import *
26*b7893ccfSSadaf Ebrahimi
27*b7893ccfSSadaf Ebrahimi# ThreadGeneratorOptions - subclass of GeneratorOptions.
28*b7893ccfSSadaf Ebrahimi#
29*b7893ccfSSadaf Ebrahimi# Adds options used by ThreadOutputGenerator objects during threading
30*b7893ccfSSadaf Ebrahimi# layer generation.
31*b7893ccfSSadaf Ebrahimi#
32*b7893ccfSSadaf Ebrahimi# Additional members
33*b7893ccfSSadaf Ebrahimi#   prefixText - list of strings to prefix generated header with
34*b7893ccfSSadaf Ebrahimi#     (usually a copyright statement + calling convention macros).
35*b7893ccfSSadaf Ebrahimi#   protectFile - True if multiple inclusion protection should be
36*b7893ccfSSadaf Ebrahimi#     generated (based on the filename) around the entire header.
37*b7893ccfSSadaf Ebrahimi#   protectFeature - True if #ifndef..#endif protection should be
38*b7893ccfSSadaf Ebrahimi#     generated around a feature interface in the header file.
39*b7893ccfSSadaf Ebrahimi#   genFuncPointers - True if function pointer typedefs should be
40*b7893ccfSSadaf Ebrahimi#     generated
41*b7893ccfSSadaf Ebrahimi#   protectProto - If conditional protection should be generated
42*b7893ccfSSadaf Ebrahimi#     around prototype declarations, set to either '#ifdef'
43*b7893ccfSSadaf Ebrahimi#     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
44*b7893ccfSSadaf Ebrahimi#     to require opt-out (#ifndef protectProtoStr). Otherwise
45*b7893ccfSSadaf Ebrahimi#     set to None.
46*b7893ccfSSadaf Ebrahimi#   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
47*b7893ccfSSadaf Ebrahimi#     declarations, if protectProto is set
48*b7893ccfSSadaf Ebrahimi#   apicall - string to use for the function declaration prefix,
49*b7893ccfSSadaf Ebrahimi#     such as APICALL on Windows.
50*b7893ccfSSadaf Ebrahimi#   apientry - string to use for the calling convention macro,
51*b7893ccfSSadaf Ebrahimi#     in typedefs, such as APIENTRY.
52*b7893ccfSSadaf Ebrahimi#   apientryp - string to use for the calling convention macro
53*b7893ccfSSadaf Ebrahimi#     in function pointer typedefs, such as APIENTRYP.
54*b7893ccfSSadaf Ebrahimi#   indentFuncProto - True if prototype declarations should put each
55*b7893ccfSSadaf Ebrahimi#     parameter on a separate line
56*b7893ccfSSadaf Ebrahimi#   indentFuncPointer - True if typedefed function pointers should put each
57*b7893ccfSSadaf Ebrahimi#     parameter on a separate line
58*b7893ccfSSadaf Ebrahimi#   alignFuncParam - if nonzero and parameters are being put on a
59*b7893ccfSSadaf Ebrahimi#     separate line, align parameter names at the specified column
60*b7893ccfSSadaf Ebrahimiclass ThreadGeneratorOptions(GeneratorOptions):
61*b7893ccfSSadaf Ebrahimi    def __init__(self,
62*b7893ccfSSadaf Ebrahimi                 conventions = None,
63*b7893ccfSSadaf Ebrahimi                 filename = None,
64*b7893ccfSSadaf Ebrahimi                 directory = '.',
65*b7893ccfSSadaf Ebrahimi                 apiname = None,
66*b7893ccfSSadaf Ebrahimi                 profile = None,
67*b7893ccfSSadaf Ebrahimi                 versions = '.*',
68*b7893ccfSSadaf Ebrahimi                 emitversions = '.*',
69*b7893ccfSSadaf Ebrahimi                 defaultExtensions = None,
70*b7893ccfSSadaf Ebrahimi                 addExtensions = None,
71*b7893ccfSSadaf Ebrahimi                 removeExtensions = None,
72*b7893ccfSSadaf Ebrahimi                 emitExtensions = None,
73*b7893ccfSSadaf Ebrahimi                 sortProcedure = regSortFeatures,
74*b7893ccfSSadaf Ebrahimi                 prefixText = "",
75*b7893ccfSSadaf Ebrahimi                 genFuncPointers = True,
76*b7893ccfSSadaf Ebrahimi                 protectFile = True,
77*b7893ccfSSadaf Ebrahimi                 protectFeature = True,
78*b7893ccfSSadaf Ebrahimi                 apicall = '',
79*b7893ccfSSadaf Ebrahimi                 apientry = '',
80*b7893ccfSSadaf Ebrahimi                 apientryp = '',
81*b7893ccfSSadaf Ebrahimi                 indentFuncProto = True,
82*b7893ccfSSadaf Ebrahimi                 indentFuncPointer = False,
83*b7893ccfSSadaf Ebrahimi                 alignFuncParam = 0,
84*b7893ccfSSadaf Ebrahimi                 expandEnumerants = True):
85*b7893ccfSSadaf Ebrahimi        GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
86*b7893ccfSSadaf Ebrahimi                                  versions, emitversions, defaultExtensions,
87*b7893ccfSSadaf Ebrahimi                                  addExtensions, removeExtensions, emitExtensions, sortProcedure)
88*b7893ccfSSadaf Ebrahimi        self.prefixText      = prefixText
89*b7893ccfSSadaf Ebrahimi        self.genFuncPointers = genFuncPointers
90*b7893ccfSSadaf Ebrahimi        self.protectFile     = protectFile
91*b7893ccfSSadaf Ebrahimi        self.protectFeature  = protectFeature
92*b7893ccfSSadaf Ebrahimi        self.apicall         = apicall
93*b7893ccfSSadaf Ebrahimi        self.apientry        = apientry
94*b7893ccfSSadaf Ebrahimi        self.apientryp       = apientryp
95*b7893ccfSSadaf Ebrahimi        self.indentFuncProto = indentFuncProto
96*b7893ccfSSadaf Ebrahimi        self.indentFuncPointer = indentFuncPointer
97*b7893ccfSSadaf Ebrahimi        self.alignFuncParam  = alignFuncParam
98*b7893ccfSSadaf Ebrahimi        self.expandEnumerants = expandEnumerants
99*b7893ccfSSadaf Ebrahimi
100*b7893ccfSSadaf Ebrahimi
101*b7893ccfSSadaf Ebrahimi# ThreadOutputGenerator - subclass of OutputGenerator.
102*b7893ccfSSadaf Ebrahimi# Generates Thread checking framework
103*b7893ccfSSadaf Ebrahimi#
104*b7893ccfSSadaf Ebrahimi# ---- methods ----
105*b7893ccfSSadaf Ebrahimi# ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
106*b7893ccfSSadaf Ebrahimi#   OutputGenerator. Defines additional internal state.
107*b7893ccfSSadaf Ebrahimi# ---- methods overriding base class ----
108*b7893ccfSSadaf Ebrahimi# beginFile(genOpts)
109*b7893ccfSSadaf Ebrahimi# endFile()
110*b7893ccfSSadaf Ebrahimi# beginFeature(interface, emit)
111*b7893ccfSSadaf Ebrahimi# endFeature()
112*b7893ccfSSadaf Ebrahimi# genType(typeinfo,name)
113*b7893ccfSSadaf Ebrahimi# genStruct(typeinfo,name)
114*b7893ccfSSadaf Ebrahimi# genGroup(groupinfo,name)
115*b7893ccfSSadaf Ebrahimi# genEnum(enuminfo, name)
116*b7893ccfSSadaf Ebrahimi# genCmd(cmdinfo)
117*b7893ccfSSadaf Ebrahimiclass ThreadOutputGenerator(OutputGenerator):
118*b7893ccfSSadaf Ebrahimi    """Generate specified API interfaces in a specific style, such as a C header"""
119*b7893ccfSSadaf Ebrahimi
120*b7893ccfSSadaf Ebrahimi    inline_copyright_message = """
121*b7893ccfSSadaf Ebrahimi// This file is ***GENERATED***.  Do Not Edit.
122*b7893ccfSSadaf Ebrahimi// See thread_safety_generator.py for modifications.
123*b7893ccfSSadaf Ebrahimi
124*b7893ccfSSadaf Ebrahimi/* Copyright (c) 2015-2019 The Khronos Group Inc.
125*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2019 Valve Corporation
126*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2019 LunarG, Inc.
127*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2019 Google Inc.
128*b7893ccfSSadaf Ebrahimi *
129*b7893ccfSSadaf Ebrahimi * Licensed under the Apache License, Version 2.0 (the "License");
130*b7893ccfSSadaf Ebrahimi * you may not use this file except in compliance with the License.
131*b7893ccfSSadaf Ebrahimi * You may obtain a copy of the License at
132*b7893ccfSSadaf Ebrahimi *
133*b7893ccfSSadaf Ebrahimi *     http://www.apache.org/licenses/LICENSE-2.0
134*b7893ccfSSadaf Ebrahimi *
135*b7893ccfSSadaf Ebrahimi * Unless required by applicable law or agreed to in writing, software
136*b7893ccfSSadaf Ebrahimi * distributed under the License is distributed on an "AS IS" BASIS,
137*b7893ccfSSadaf Ebrahimi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138*b7893ccfSSadaf Ebrahimi * See the License for the specific language governing permissions and
139*b7893ccfSSadaf Ebrahimi * limitations under the License.
140*b7893ccfSSadaf Ebrahimi *
141*b7893ccfSSadaf Ebrahimi * Author: Mark Lobodzinski <[email protected]>
142*b7893ccfSSadaf Ebrahimi */"""
143*b7893ccfSSadaf Ebrahimi
144*b7893ccfSSadaf Ebrahimi # Note that the inline_custom_header_preamble template below contains three embedded template expansion identifiers.
145*b7893ccfSSadaf Ebrahimi # These get replaced with generated code sections, and are labeled:
146*b7893ccfSSadaf Ebrahimi #  o COUNTER_CLASS_DEFINITIONS_TEMPLATE
147*b7893ccfSSadaf Ebrahimi #  o COUNTER_CLASS_INSTANCES_TEMPLATE
148*b7893ccfSSadaf Ebrahimi #  o COUNTER_CLASS_BODIES_TEMPLATE
149*b7893ccfSSadaf Ebrahimi    inline_custom_header_preamble = """
150*b7893ccfSSadaf Ebrahimi#pragma once
151*b7893ccfSSadaf Ebrahimi
152*b7893ccfSSadaf Ebrahimi#include <chrono>
153*b7893ccfSSadaf Ebrahimi#include <thread>
154*b7893ccfSSadaf Ebrahimi#include <mutex>
155*b7893ccfSSadaf Ebrahimi#include <vector>
156*b7893ccfSSadaf Ebrahimi#include <unordered_set>
157*b7893ccfSSadaf Ebrahimi#include <string>
158*b7893ccfSSadaf Ebrahimi
159*b7893ccfSSadaf EbrahimiVK_DEFINE_NON_DISPATCHABLE_HANDLE(DISTINCT_NONDISPATCHABLE_PHONY_HANDLE)
160*b7893ccfSSadaf Ebrahimi// The following line must match the vulkan_core.h condition guarding VK_DEFINE_NON_DISPATCHABLE_HANDLE
161*b7893ccfSSadaf Ebrahimi#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \
162*b7893ccfSSadaf Ebrahimi    defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
163*b7893ccfSSadaf Ebrahimi// If pointers are 64-bit, then there can be separate counters for each
164*b7893ccfSSadaf Ebrahimi// NONDISPATCHABLE_HANDLE type.  Otherwise they are all typedef uint64_t.
165*b7893ccfSSadaf Ebrahimi#define DISTINCT_NONDISPATCHABLE_HANDLES
166*b7893ccfSSadaf Ebrahimi// Make sure we catch any disagreement between us and the vulkan definition
167*b7893ccfSSadaf Ebrahimistatic_assert(std::is_pointer<DISTINCT_NONDISPATCHABLE_PHONY_HANDLE>::value,
168*b7893ccfSSadaf Ebrahimi              "Mismatched non-dispatchable handle handle, expected pointer type.");
169*b7893ccfSSadaf Ebrahimi#else
170*b7893ccfSSadaf Ebrahimi// Make sure we catch any disagreement between us and the vulkan definition
171*b7893ccfSSadaf Ebrahimistatic_assert(std::is_same<uint64_t, DISTINCT_NONDISPATCHABLE_PHONY_HANDLE>::value,
172*b7893ccfSSadaf Ebrahimi              "Mismatched non-dispatchable handle handle, expected uint64_t.");
173*b7893ccfSSadaf Ebrahimi#endif
174*b7893ccfSSadaf Ebrahimi
175*b7893ccfSSadaf Ebrahimi// Suppress unused warning on Linux
176*b7893ccfSSadaf Ebrahimi#if defined(__GNUC__)
177*b7893ccfSSadaf Ebrahimi#define DECORATE_UNUSED __attribute__((unused))
178*b7893ccfSSadaf Ebrahimi#else
179*b7893ccfSSadaf Ebrahimi#define DECORATE_UNUSED
180*b7893ccfSSadaf Ebrahimi#endif
181*b7893ccfSSadaf Ebrahimi
182*b7893ccfSSadaf Ebrahimi// clang-format off
183*b7893ccfSSadaf Ebrahimistatic const char DECORATE_UNUSED *kVUID_Threading_Info = "UNASSIGNED-Threading-Info";
184*b7893ccfSSadaf Ebrahimistatic const char DECORATE_UNUSED *kVUID_Threading_MultipleThreads = "UNASSIGNED-Threading-MultipleThreads";
185*b7893ccfSSadaf Ebrahimistatic const char DECORATE_UNUSED *kVUID_Threading_SingleThreadReuse = "UNASSIGNED-Threading-SingleThreadReuse";
186*b7893ccfSSadaf Ebrahimi// clang-format on
187*b7893ccfSSadaf Ebrahimi
188*b7893ccfSSadaf Ebrahimi#undef DECORATE_UNUSED
189*b7893ccfSSadaf Ebrahimi
190*b7893ccfSSadaf Ebrahimistruct object_use_data {
191*b7893ccfSSadaf Ebrahimi    loader_platform_thread_id thread;
192*b7893ccfSSadaf Ebrahimi    int reader_count;
193*b7893ccfSSadaf Ebrahimi    int writer_count;
194*b7893ccfSSadaf Ebrahimi};
195*b7893ccfSSadaf Ebrahimi
196*b7893ccfSSadaf Ebrahimi// This is a wrapper around unordered_map that optimizes for the common case
197*b7893ccfSSadaf Ebrahimi// of only containing a single element. The "first" element's use is stored
198*b7893ccfSSadaf Ebrahimi// inline in the class and doesn't require hashing or memory (de)allocation.
199*b7893ccfSSadaf Ebrahimi// TODO: Consider generalizing this from one element to N elements (where N
200*b7893ccfSSadaf Ebrahimi// is a template parameter).
201*b7893ccfSSadaf Ebrahimitemplate <typename Key, typename T>
202*b7893ccfSSadaf Ebrahimiclass small_unordered_map {
203*b7893ccfSSadaf Ebrahimi
204*b7893ccfSSadaf Ebrahimi    bool first_data_allocated;
205*b7893ccfSSadaf Ebrahimi    Key first_data_key;
206*b7893ccfSSadaf Ebrahimi    T first_data;
207*b7893ccfSSadaf Ebrahimi
208*b7893ccfSSadaf Ebrahimi    std::unordered_map<Key, T> uses;
209*b7893ccfSSadaf Ebrahimi
210*b7893ccfSSadaf Ebrahimipublic:
211*b7893ccfSSadaf Ebrahimi    small_unordered_map() : first_data_allocated(false) {}
212*b7893ccfSSadaf Ebrahimi
213*b7893ccfSSadaf Ebrahimi    bool contains(const Key& object) const {
214*b7893ccfSSadaf Ebrahimi        if (first_data_allocated && object == first_data_key) {
215*b7893ccfSSadaf Ebrahimi            return true;
216*b7893ccfSSadaf Ebrahimi        // check size() first to avoid hashing object unnecessarily.
217*b7893ccfSSadaf Ebrahimi        } else if (uses.size() == 0) {
218*b7893ccfSSadaf Ebrahimi            return false;
219*b7893ccfSSadaf Ebrahimi        } else {
220*b7893ccfSSadaf Ebrahimi            return uses.find(object) != uses.end();
221*b7893ccfSSadaf Ebrahimi        }
222*b7893ccfSSadaf Ebrahimi    }
223*b7893ccfSSadaf Ebrahimi
224*b7893ccfSSadaf Ebrahimi    T& operator[](const Key& object) {
225*b7893ccfSSadaf Ebrahimi        if (first_data_allocated && first_data_key == object) {
226*b7893ccfSSadaf Ebrahimi            return first_data;
227*b7893ccfSSadaf Ebrahimi        } else if (!first_data_allocated && uses.size() == 0) {
228*b7893ccfSSadaf Ebrahimi            first_data_allocated = true;
229*b7893ccfSSadaf Ebrahimi            first_data_key = object;
230*b7893ccfSSadaf Ebrahimi            return first_data;
231*b7893ccfSSadaf Ebrahimi        } else {
232*b7893ccfSSadaf Ebrahimi            return uses[object];
233*b7893ccfSSadaf Ebrahimi        }
234*b7893ccfSSadaf Ebrahimi    }
235*b7893ccfSSadaf Ebrahimi
236*b7893ccfSSadaf Ebrahimi    typename std::unordered_map<Key, T>::size_type erase(const Key& object) {
237*b7893ccfSSadaf Ebrahimi        if (first_data_allocated && first_data_key == object) {
238*b7893ccfSSadaf Ebrahimi            first_data_allocated = false;
239*b7893ccfSSadaf Ebrahimi            return 1;
240*b7893ccfSSadaf Ebrahimi        } else {
241*b7893ccfSSadaf Ebrahimi            return uses.erase(object);
242*b7893ccfSSadaf Ebrahimi        }
243*b7893ccfSSadaf Ebrahimi    }
244*b7893ccfSSadaf Ebrahimi};
245*b7893ccfSSadaf Ebrahimi
246*b7893ccfSSadaf Ebrahimi#define THREAD_SAFETY_BUCKETS_LOG2 6
247*b7893ccfSSadaf Ebrahimi#define THREAD_SAFETY_BUCKETS (1 << THREAD_SAFETY_BUCKETS_LOG2)
248*b7893ccfSSadaf Ebrahimi
249*b7893ccfSSadaf Ebrahimitemplate <typename T> inline uint32_t ThreadSafetyHashObject(T object)
250*b7893ccfSSadaf Ebrahimi{
251*b7893ccfSSadaf Ebrahimi    uint64_t u64 = (uint64_t)(uintptr_t)object;
252*b7893ccfSSadaf Ebrahimi    uint32_t hash = (uint32_t)(u64 >> 32) + (uint32_t)u64;
253*b7893ccfSSadaf Ebrahimi    hash ^= (hash >> THREAD_SAFETY_BUCKETS_LOG2) ^ (hash >> (2*THREAD_SAFETY_BUCKETS_LOG2));
254*b7893ccfSSadaf Ebrahimi    hash &= (THREAD_SAFETY_BUCKETS-1);
255*b7893ccfSSadaf Ebrahimi    return hash;
256*b7893ccfSSadaf Ebrahimi}
257*b7893ccfSSadaf Ebrahimi
258*b7893ccfSSadaf Ebrahimitemplate <typename T>
259*b7893ccfSSadaf Ebrahimiclass counter {
260*b7893ccfSSadaf Ebrahimipublic:
261*b7893ccfSSadaf Ebrahimi    const char *typeName;
262*b7893ccfSSadaf Ebrahimi    VkDebugReportObjectTypeEXT objectType;
263*b7893ccfSSadaf Ebrahimi    debug_report_data **report_data;
264*b7893ccfSSadaf Ebrahimi
265*b7893ccfSSadaf Ebrahimi    // Per-bucket locking, to reduce contention.
266*b7893ccfSSadaf Ebrahimi    struct CounterBucket {
267*b7893ccfSSadaf Ebrahimi        small_unordered_map<T, object_use_data> uses;
268*b7893ccfSSadaf Ebrahimi        std::mutex counter_lock;
269*b7893ccfSSadaf Ebrahimi    };
270*b7893ccfSSadaf Ebrahimi
271*b7893ccfSSadaf Ebrahimi    CounterBucket buckets[THREAD_SAFETY_BUCKETS];
272*b7893ccfSSadaf Ebrahimi    CounterBucket &GetBucket(T object)
273*b7893ccfSSadaf Ebrahimi    {
274*b7893ccfSSadaf Ebrahimi        return buckets[ThreadSafetyHashObject(object)];
275*b7893ccfSSadaf Ebrahimi    }
276*b7893ccfSSadaf Ebrahimi
277*b7893ccfSSadaf Ebrahimi    void StartWrite(T object) {
278*b7893ccfSSadaf Ebrahimi        if (object == VK_NULL_HANDLE) {
279*b7893ccfSSadaf Ebrahimi            return;
280*b7893ccfSSadaf Ebrahimi        }
281*b7893ccfSSadaf Ebrahimi        auto &bucket = GetBucket(object);
282*b7893ccfSSadaf Ebrahimi        bool skip = false;
283*b7893ccfSSadaf Ebrahimi        loader_platform_thread_id tid = loader_platform_get_thread_id();
284*b7893ccfSSadaf Ebrahimi        std::unique_lock<std::mutex> lock(bucket.counter_lock);
285*b7893ccfSSadaf Ebrahimi        if (!bucket.uses.contains(object)) {
286*b7893ccfSSadaf Ebrahimi            // There is no current use of the object.  Record writer thread.
287*b7893ccfSSadaf Ebrahimi            struct object_use_data *use_data = &bucket.uses[object];
288*b7893ccfSSadaf Ebrahimi            use_data->reader_count = 0;
289*b7893ccfSSadaf Ebrahimi            use_data->writer_count = 1;
290*b7893ccfSSadaf Ebrahimi            use_data->thread = tid;
291*b7893ccfSSadaf Ebrahimi        } else {
292*b7893ccfSSadaf Ebrahimi            struct object_use_data *use_data = &bucket.uses[object];
293*b7893ccfSSadaf Ebrahimi            if (use_data->reader_count == 0) {
294*b7893ccfSSadaf Ebrahimi                // There are no readers.  Two writers just collided.
295*b7893ccfSSadaf Ebrahimi                if (use_data->thread != tid) {
296*b7893ccfSSadaf Ebrahimi                    skip |= log_msg(*report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
297*b7893ccfSSadaf Ebrahimi                        kVUID_Threading_MultipleThreads,
298*b7893ccfSSadaf Ebrahimi                        "THREADING ERROR : object of type %s is simultaneously used in "
299*b7893ccfSSadaf Ebrahimi                        "thread 0x%" PRIx64 " and thread 0x%" PRIx64,
300*b7893ccfSSadaf Ebrahimi                        typeName, (uint64_t)use_data->thread, (uint64_t)tid);
301*b7893ccfSSadaf Ebrahimi                    if (skip) {
302*b7893ccfSSadaf Ebrahimi                        WaitForObjectIdle(bucket, object, lock);
303*b7893ccfSSadaf Ebrahimi                        // There is now no current use of the object.  Record writer thread.
304*b7893ccfSSadaf Ebrahimi                        struct object_use_data *new_use_data = &bucket.uses[object];
305*b7893ccfSSadaf Ebrahimi                        new_use_data->thread = tid;
306*b7893ccfSSadaf Ebrahimi                        new_use_data->reader_count = 0;
307*b7893ccfSSadaf Ebrahimi                        new_use_data->writer_count = 1;
308*b7893ccfSSadaf Ebrahimi                    } else {
309*b7893ccfSSadaf Ebrahimi                        // Continue with an unsafe use of the object.
310*b7893ccfSSadaf Ebrahimi                        use_data->thread = tid;
311*b7893ccfSSadaf Ebrahimi                        use_data->writer_count += 1;
312*b7893ccfSSadaf Ebrahimi                    }
313*b7893ccfSSadaf Ebrahimi                } else {
314*b7893ccfSSadaf Ebrahimi                    // This is either safe multiple use in one call, or recursive use.
315*b7893ccfSSadaf Ebrahimi                    // There is no way to make recursion safe.  Just forge ahead.
316*b7893ccfSSadaf Ebrahimi                    use_data->writer_count += 1;
317*b7893ccfSSadaf Ebrahimi                }
318*b7893ccfSSadaf Ebrahimi            } else {
319*b7893ccfSSadaf Ebrahimi                // There are readers.  This writer collided with them.
320*b7893ccfSSadaf Ebrahimi                if (use_data->thread != tid) {
321*b7893ccfSSadaf Ebrahimi                    skip |= log_msg(*report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
322*b7893ccfSSadaf Ebrahimi                        kVUID_Threading_MultipleThreads,
323*b7893ccfSSadaf Ebrahimi                        "THREADING ERROR : object of type %s is simultaneously used in "
324*b7893ccfSSadaf Ebrahimi                        "thread 0x%" PRIx64 " and thread 0x%" PRIx64,
325*b7893ccfSSadaf Ebrahimi                        typeName, (uint64_t)use_data->thread, (uint64_t)tid);
326*b7893ccfSSadaf Ebrahimi                    if (skip) {
327*b7893ccfSSadaf Ebrahimi                        WaitForObjectIdle(bucket, object, lock);
328*b7893ccfSSadaf Ebrahimi                        // There is now no current use of the object.  Record writer thread.
329*b7893ccfSSadaf Ebrahimi                        struct object_use_data *new_use_data = &bucket.uses[object];
330*b7893ccfSSadaf Ebrahimi                        new_use_data->thread = tid;
331*b7893ccfSSadaf Ebrahimi                        new_use_data->reader_count = 0;
332*b7893ccfSSadaf Ebrahimi                        new_use_data->writer_count = 1;
333*b7893ccfSSadaf Ebrahimi                    } else {
334*b7893ccfSSadaf Ebrahimi                        // Continue with an unsafe use of the object.
335*b7893ccfSSadaf Ebrahimi                        use_data->thread = tid;
336*b7893ccfSSadaf Ebrahimi                        use_data->writer_count += 1;
337*b7893ccfSSadaf Ebrahimi                    }
338*b7893ccfSSadaf Ebrahimi                } else {
339*b7893ccfSSadaf Ebrahimi                    // This is either safe multiple use in one call, or recursive use.
340*b7893ccfSSadaf Ebrahimi                    // There is no way to make recursion safe.  Just forge ahead.
341*b7893ccfSSadaf Ebrahimi                    use_data->writer_count += 1;
342*b7893ccfSSadaf Ebrahimi                }
343*b7893ccfSSadaf Ebrahimi            }
344*b7893ccfSSadaf Ebrahimi        }
345*b7893ccfSSadaf Ebrahimi    }
346*b7893ccfSSadaf Ebrahimi
347*b7893ccfSSadaf Ebrahimi    void FinishWrite(T object) {
348*b7893ccfSSadaf Ebrahimi        if (object == VK_NULL_HANDLE) {
349*b7893ccfSSadaf Ebrahimi            return;
350*b7893ccfSSadaf Ebrahimi        }
351*b7893ccfSSadaf Ebrahimi        auto &bucket = GetBucket(object);
352*b7893ccfSSadaf Ebrahimi        // Object is no longer in use
353*b7893ccfSSadaf Ebrahimi        std::unique_lock<std::mutex> lock(bucket.counter_lock);
354*b7893ccfSSadaf Ebrahimi        struct object_use_data *use_data = &bucket.uses[object];
355*b7893ccfSSadaf Ebrahimi        use_data->writer_count -= 1;
356*b7893ccfSSadaf Ebrahimi        if ((use_data->reader_count == 0) && (use_data->writer_count == 0)) {
357*b7893ccfSSadaf Ebrahimi            bucket.uses.erase(object);
358*b7893ccfSSadaf Ebrahimi        }
359*b7893ccfSSadaf Ebrahimi    }
360*b7893ccfSSadaf Ebrahimi
361*b7893ccfSSadaf Ebrahimi    void StartRead(T object) {
362*b7893ccfSSadaf Ebrahimi        if (object == VK_NULL_HANDLE) {
363*b7893ccfSSadaf Ebrahimi            return;
364*b7893ccfSSadaf Ebrahimi        }
365*b7893ccfSSadaf Ebrahimi        auto &bucket = GetBucket(object);
366*b7893ccfSSadaf Ebrahimi        bool skip = false;
367*b7893ccfSSadaf Ebrahimi        loader_platform_thread_id tid = loader_platform_get_thread_id();
368*b7893ccfSSadaf Ebrahimi        std::unique_lock<std::mutex> lock(bucket.counter_lock);
369*b7893ccfSSadaf Ebrahimi        if (!bucket.uses.contains(object)) {
370*b7893ccfSSadaf Ebrahimi            // There is no current use of the object.  Record reader count
371*b7893ccfSSadaf Ebrahimi            struct object_use_data *use_data = &bucket.uses[object];
372*b7893ccfSSadaf Ebrahimi            use_data->reader_count = 1;
373*b7893ccfSSadaf Ebrahimi            use_data->writer_count = 0;
374*b7893ccfSSadaf Ebrahimi            use_data->thread = tid;
375*b7893ccfSSadaf Ebrahimi        } else if (bucket.uses[object].writer_count > 0 && bucket.uses[object].thread != tid) {
376*b7893ccfSSadaf Ebrahimi            // There is a writer of the object.
377*b7893ccfSSadaf Ebrahimi            skip |= log_msg(*report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
378*b7893ccfSSadaf Ebrahimi                kVUID_Threading_MultipleThreads,
379*b7893ccfSSadaf Ebrahimi                "THREADING ERROR : object of type %s is simultaneously used in "
380*b7893ccfSSadaf Ebrahimi                "thread 0x%" PRIx64 " and thread 0x%" PRIx64,
381*b7893ccfSSadaf Ebrahimi                typeName, (uint64_t)bucket.uses[object].thread, (uint64_t)tid);
382*b7893ccfSSadaf Ebrahimi            if (skip) {
383*b7893ccfSSadaf Ebrahimi                WaitForObjectIdle(bucket, object, lock);
384*b7893ccfSSadaf Ebrahimi                // There is no current use of the object.  Record reader count
385*b7893ccfSSadaf Ebrahimi                struct object_use_data *use_data = &bucket.uses[object];
386*b7893ccfSSadaf Ebrahimi                use_data->reader_count = 1;
387*b7893ccfSSadaf Ebrahimi                use_data->writer_count = 0;
388*b7893ccfSSadaf Ebrahimi                use_data->thread = tid;
389*b7893ccfSSadaf Ebrahimi            } else {
390*b7893ccfSSadaf Ebrahimi                bucket.uses[object].reader_count += 1;
391*b7893ccfSSadaf Ebrahimi            }
392*b7893ccfSSadaf Ebrahimi        } else {
393*b7893ccfSSadaf Ebrahimi            // There are other readers of the object.  Increase reader count
394*b7893ccfSSadaf Ebrahimi            bucket.uses[object].reader_count += 1;
395*b7893ccfSSadaf Ebrahimi        }
396*b7893ccfSSadaf Ebrahimi    }
397*b7893ccfSSadaf Ebrahimi    void FinishRead(T object) {
398*b7893ccfSSadaf Ebrahimi        if (object == VK_NULL_HANDLE) {
399*b7893ccfSSadaf Ebrahimi            return;
400*b7893ccfSSadaf Ebrahimi        }
401*b7893ccfSSadaf Ebrahimi        auto &bucket = GetBucket(object);
402*b7893ccfSSadaf Ebrahimi        std::unique_lock<std::mutex> lock(bucket.counter_lock);
403*b7893ccfSSadaf Ebrahimi        struct object_use_data *use_data = &bucket.uses[object];
404*b7893ccfSSadaf Ebrahimi        use_data->reader_count -= 1;
405*b7893ccfSSadaf Ebrahimi        if ((use_data->reader_count == 0) && (use_data->writer_count == 0)) {
406*b7893ccfSSadaf Ebrahimi            bucket.uses.erase(object);
407*b7893ccfSSadaf Ebrahimi        }
408*b7893ccfSSadaf Ebrahimi    }
409*b7893ccfSSadaf Ebrahimi    counter(const char *name = "", VkDebugReportObjectTypeEXT type = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, debug_report_data **rep_data = nullptr) {
410*b7893ccfSSadaf Ebrahimi        typeName = name;
411*b7893ccfSSadaf Ebrahimi        objectType = type;
412*b7893ccfSSadaf Ebrahimi        report_data = rep_data;
413*b7893ccfSSadaf Ebrahimi    }
414*b7893ccfSSadaf Ebrahimi
415*b7893ccfSSadaf Ebrahimiprivate:
416*b7893ccfSSadaf Ebrahimi    void WaitForObjectIdle(CounterBucket &bucket, T object, std::unique_lock<std::mutex> &lock) {
417*b7893ccfSSadaf Ebrahimi        // Wait for thread-safe access to object instead of skipping call.
418*b7893ccfSSadaf Ebrahimi        // Don't use condition_variable to wait because it should be extremely
419*b7893ccfSSadaf Ebrahimi        // rare to have collisions, but signaling would be very frequent.
420*b7893ccfSSadaf Ebrahimi        while (bucket.uses.contains(object)) {
421*b7893ccfSSadaf Ebrahimi            lock.unlock();
422*b7893ccfSSadaf Ebrahimi            std::this_thread::sleep_for(std::chrono::microseconds(1));
423*b7893ccfSSadaf Ebrahimi            lock.lock();
424*b7893ccfSSadaf Ebrahimi        }
425*b7893ccfSSadaf Ebrahimi    }
426*b7893ccfSSadaf Ebrahimi};
427*b7893ccfSSadaf Ebrahimi
428*b7893ccfSSadaf Ebrahimi
429*b7893ccfSSadaf Ebrahimi
430*b7893ccfSSadaf Ebrahimiclass ThreadSafety : public ValidationObject {
431*b7893ccfSSadaf Ebrahimipublic:
432*b7893ccfSSadaf Ebrahimi
433*b7893ccfSSadaf Ebrahimi    // Override chassis read/write locks for this validation object
434*b7893ccfSSadaf Ebrahimi    // This override takes a deferred lock. i.e. it is not acquired.
435*b7893ccfSSadaf Ebrahimi    std::unique_lock<std::mutex> write_lock() {
436*b7893ccfSSadaf Ebrahimi        return std::unique_lock<std::mutex>(validation_object_mutex, std::defer_lock);
437*b7893ccfSSadaf Ebrahimi    }
438*b7893ccfSSadaf Ebrahimi
439*b7893ccfSSadaf Ebrahimi    // Per-bucket locking, to reduce contention.
440*b7893ccfSSadaf Ebrahimi    struct CommandBufferBucket {
441*b7893ccfSSadaf Ebrahimi        std::mutex command_pool_lock;
442*b7893ccfSSadaf Ebrahimi        small_unordered_map<VkCommandBuffer, VkCommandPool> command_pool_map;
443*b7893ccfSSadaf Ebrahimi    };
444*b7893ccfSSadaf Ebrahimi
445*b7893ccfSSadaf Ebrahimi    CommandBufferBucket buckets[THREAD_SAFETY_BUCKETS];
446*b7893ccfSSadaf Ebrahimi    CommandBufferBucket &GetBucket(VkCommandBuffer object)
447*b7893ccfSSadaf Ebrahimi    {
448*b7893ccfSSadaf Ebrahimi        return buckets[ThreadSafetyHashObject(object)];
449*b7893ccfSSadaf Ebrahimi    }
450*b7893ccfSSadaf Ebrahimi
451*b7893ccfSSadaf Ebrahimi    counter<VkCommandBuffer> c_VkCommandBuffer;
452*b7893ccfSSadaf Ebrahimi    counter<VkDevice> c_VkDevice;
453*b7893ccfSSadaf Ebrahimi    counter<VkInstance> c_VkInstance;
454*b7893ccfSSadaf Ebrahimi    counter<VkQueue> c_VkQueue;
455*b7893ccfSSadaf Ebrahimi#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
456*b7893ccfSSadaf Ebrahimi
457*b7893ccfSSadaf Ebrahimi    // Special entry to allow tracking of command pool Reset and Destroy
458*b7893ccfSSadaf Ebrahimi    counter<VkCommandPool> c_VkCommandPoolContents;
459*b7893ccfSSadaf EbrahimiCOUNTER_CLASS_DEFINITIONS_TEMPLATE
460*b7893ccfSSadaf Ebrahimi
461*b7893ccfSSadaf Ebrahimi#else   // DISTINCT_NONDISPATCHABLE_HANDLES
462*b7893ccfSSadaf Ebrahimi    // Special entry to allow tracking of command pool Reset and Destroy
463*b7893ccfSSadaf Ebrahimi    counter<uint64_t> c_VkCommandPoolContents;
464*b7893ccfSSadaf Ebrahimi
465*b7893ccfSSadaf Ebrahimi    counter<uint64_t> c_uint64_t;
466*b7893ccfSSadaf Ebrahimi#endif  // DISTINCT_NONDISPATCHABLE_HANDLES
467*b7893ccfSSadaf Ebrahimi
468*b7893ccfSSadaf Ebrahimi    ThreadSafety()
469*b7893ccfSSadaf Ebrahimi        : c_VkCommandBuffer("VkCommandBuffer", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, &report_data),
470*b7893ccfSSadaf Ebrahimi          c_VkDevice("VkDevice", VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, &report_data),
471*b7893ccfSSadaf Ebrahimi          c_VkInstance("VkInstance", VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, &report_data),
472*b7893ccfSSadaf Ebrahimi          c_VkQueue("VkQueue", VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, &report_data),
473*b7893ccfSSadaf Ebrahimi          c_VkCommandPoolContents("VkCommandPool", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, &report_data),
474*b7893ccfSSadaf Ebrahimi
475*b7893ccfSSadaf Ebrahimi#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
476*b7893ccfSSadaf EbrahimiCOUNTER_CLASS_INSTANCES_TEMPLATE
477*b7893ccfSSadaf Ebrahimi
478*b7893ccfSSadaf Ebrahimi
479*b7893ccfSSadaf Ebrahimi#else   // DISTINCT_NONDISPATCHABLE_HANDLES
480*b7893ccfSSadaf Ebrahimi          c_uint64_t("NON_DISPATCHABLE_HANDLE", VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, &report_data)
481*b7893ccfSSadaf Ebrahimi#endif  // DISTINCT_NONDISPATCHABLE_HANDLES
482*b7893ccfSSadaf Ebrahimi              {};
483*b7893ccfSSadaf Ebrahimi
484*b7893ccfSSadaf Ebrahimi#define WRAPPER(type)                                                \
485*b7893ccfSSadaf Ebrahimi    void StartWriteObject(type object) {                             \
486*b7893ccfSSadaf Ebrahimi        c_##type.StartWrite(object);                                 \
487*b7893ccfSSadaf Ebrahimi    }                                                                \
488*b7893ccfSSadaf Ebrahimi    void FinishWriteObject(type object) {                            \
489*b7893ccfSSadaf Ebrahimi        c_##type.FinishWrite(object);                                \
490*b7893ccfSSadaf Ebrahimi    }                                                                \
491*b7893ccfSSadaf Ebrahimi    void StartReadObject(type object) {                              \
492*b7893ccfSSadaf Ebrahimi        c_##type.StartRead(object);                                  \
493*b7893ccfSSadaf Ebrahimi    }                                                                \
494*b7893ccfSSadaf Ebrahimi    void FinishReadObject(type object) {                             \
495*b7893ccfSSadaf Ebrahimi        c_##type.FinishRead(object);                                 \
496*b7893ccfSSadaf Ebrahimi    }
497*b7893ccfSSadaf Ebrahimi
498*b7893ccfSSadaf EbrahimiWRAPPER(VkDevice)
499*b7893ccfSSadaf EbrahimiWRAPPER(VkInstance)
500*b7893ccfSSadaf EbrahimiWRAPPER(VkQueue)
501*b7893ccfSSadaf Ebrahimi#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
502*b7893ccfSSadaf EbrahimiCOUNTER_CLASS_BODIES_TEMPLATE
503*b7893ccfSSadaf Ebrahimi
504*b7893ccfSSadaf Ebrahimi#else   // DISTINCT_NONDISPATCHABLE_HANDLES
505*b7893ccfSSadaf EbrahimiWRAPPER(uint64_t)
506*b7893ccfSSadaf Ebrahimi#endif  // DISTINCT_NONDISPATCHABLE_HANDLES
507*b7893ccfSSadaf Ebrahimi
508*b7893ccfSSadaf Ebrahimi    // VkCommandBuffer needs check for implicit use of command pool
509*b7893ccfSSadaf Ebrahimi    void StartWriteObject(VkCommandBuffer object, bool lockPool = true) {
510*b7893ccfSSadaf Ebrahimi        if (lockPool) {
511*b7893ccfSSadaf Ebrahimi            auto &bucket = GetBucket(object);
512*b7893ccfSSadaf Ebrahimi            std::unique_lock<std::mutex> lock(bucket.command_pool_lock);
513*b7893ccfSSadaf Ebrahimi            VkCommandPool pool = bucket.command_pool_map[object];
514*b7893ccfSSadaf Ebrahimi            lock.unlock();
515*b7893ccfSSadaf Ebrahimi            StartWriteObject(pool);
516*b7893ccfSSadaf Ebrahimi        }
517*b7893ccfSSadaf Ebrahimi        c_VkCommandBuffer.StartWrite(object);
518*b7893ccfSSadaf Ebrahimi    }
519*b7893ccfSSadaf Ebrahimi    void FinishWriteObject(VkCommandBuffer object, bool lockPool = true) {
520*b7893ccfSSadaf Ebrahimi        c_VkCommandBuffer.FinishWrite(object);
521*b7893ccfSSadaf Ebrahimi        if (lockPool) {
522*b7893ccfSSadaf Ebrahimi            auto &bucket = GetBucket(object);
523*b7893ccfSSadaf Ebrahimi            std::unique_lock<std::mutex> lock(bucket.command_pool_lock);
524*b7893ccfSSadaf Ebrahimi            VkCommandPool pool = bucket.command_pool_map[object];
525*b7893ccfSSadaf Ebrahimi            lock.unlock();
526*b7893ccfSSadaf Ebrahimi            FinishWriteObject(pool);
527*b7893ccfSSadaf Ebrahimi        }
528*b7893ccfSSadaf Ebrahimi    }
529*b7893ccfSSadaf Ebrahimi    void StartReadObject(VkCommandBuffer object) {
530*b7893ccfSSadaf Ebrahimi        auto &bucket = GetBucket(object);
531*b7893ccfSSadaf Ebrahimi        std::unique_lock<std::mutex> lock(bucket.command_pool_lock);
532*b7893ccfSSadaf Ebrahimi        VkCommandPool pool = bucket.command_pool_map[object];
533*b7893ccfSSadaf Ebrahimi        lock.unlock();
534*b7893ccfSSadaf Ebrahimi        // We set up a read guard against the "Contents" counter to catch conflict vs. vkResetCommandPool and vkDestroyCommandPool
535*b7893ccfSSadaf Ebrahimi        // while *not* establishing a read guard against the command pool counter itself to avoid false postives for
536*b7893ccfSSadaf Ebrahimi        // non-externally sync'd command buffers
537*b7893ccfSSadaf Ebrahimi        c_VkCommandPoolContents.StartRead(pool);
538*b7893ccfSSadaf Ebrahimi        c_VkCommandBuffer.StartRead(object);
539*b7893ccfSSadaf Ebrahimi    }
540*b7893ccfSSadaf Ebrahimi    void FinishReadObject(VkCommandBuffer object) {
541*b7893ccfSSadaf Ebrahimi        auto &bucket = GetBucket(object);
542*b7893ccfSSadaf Ebrahimi        c_VkCommandBuffer.FinishRead(object);
543*b7893ccfSSadaf Ebrahimi        std::unique_lock<std::mutex> lock(bucket.command_pool_lock);
544*b7893ccfSSadaf Ebrahimi        VkCommandPool pool = bucket.command_pool_map[object];
545*b7893ccfSSadaf Ebrahimi        lock.unlock();
546*b7893ccfSSadaf Ebrahimi        c_VkCommandPoolContents.FinishRead(pool);
547*b7893ccfSSadaf Ebrahimi    } """
548*b7893ccfSSadaf Ebrahimi
549*b7893ccfSSadaf Ebrahimi
550*b7893ccfSSadaf Ebrahimi    inline_custom_source_preamble = """
551*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PreCallRecordAllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo,
552*b7893ccfSSadaf Ebrahimi                                                       VkCommandBuffer *pCommandBuffers) {
553*b7893ccfSSadaf Ebrahimi    StartReadObject(device);
554*b7893ccfSSadaf Ebrahimi    StartWriteObject(pAllocateInfo->commandPool);
555*b7893ccfSSadaf Ebrahimi}
556*b7893ccfSSadaf Ebrahimi
557*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PostCallRecordAllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo,
558*b7893ccfSSadaf Ebrahimi                                                        VkCommandBuffer *pCommandBuffers, VkResult result) {
559*b7893ccfSSadaf Ebrahimi    FinishReadObject(device);
560*b7893ccfSSadaf Ebrahimi    FinishWriteObject(pAllocateInfo->commandPool);
561*b7893ccfSSadaf Ebrahimi
562*b7893ccfSSadaf Ebrahimi    // Record mapping from command buffer to command pool
563*b7893ccfSSadaf Ebrahimi    if(pCommandBuffers) {
564*b7893ccfSSadaf Ebrahimi        for (uint32_t index = 0; index < pAllocateInfo->commandBufferCount; index++) {
565*b7893ccfSSadaf Ebrahimi            auto &bucket = GetBucket(pCommandBuffers[index]);
566*b7893ccfSSadaf Ebrahimi            std::lock_guard<std::mutex> lock(bucket.command_pool_lock);
567*b7893ccfSSadaf Ebrahimi            bucket.command_pool_map[pCommandBuffers[index]] = pAllocateInfo->commandPool;
568*b7893ccfSSadaf Ebrahimi        }
569*b7893ccfSSadaf Ebrahimi    }
570*b7893ccfSSadaf Ebrahimi}
571*b7893ccfSSadaf Ebrahimi
572*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PreCallRecordAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo *pAllocateInfo,
573*b7893ccfSSadaf Ebrahimi                                                       VkDescriptorSet *pDescriptorSets) {
574*b7893ccfSSadaf Ebrahimi    StartReadObject(device);
575*b7893ccfSSadaf Ebrahimi    StartWriteObject(pAllocateInfo->descriptorPool);
576*b7893ccfSSadaf Ebrahimi    // Host access to pAllocateInfo::descriptorPool must be externally synchronized
577*b7893ccfSSadaf Ebrahimi}
578*b7893ccfSSadaf Ebrahimi
579*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PostCallRecordAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo *pAllocateInfo,
580*b7893ccfSSadaf Ebrahimi                                                        VkDescriptorSet *pDescriptorSets, VkResult result) {
581*b7893ccfSSadaf Ebrahimi    FinishReadObject(device);
582*b7893ccfSSadaf Ebrahimi    FinishWriteObject(pAllocateInfo->descriptorPool);
583*b7893ccfSSadaf Ebrahimi    // Host access to pAllocateInfo::descriptorPool must be externally synchronized
584*b7893ccfSSadaf Ebrahimi}
585*b7893ccfSSadaf Ebrahimi
586*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PreCallRecordFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount,
587*b7893ccfSSadaf Ebrahimi                                                   const VkCommandBuffer *pCommandBuffers) {
588*b7893ccfSSadaf Ebrahimi    const bool lockCommandPool = false;  // pool is already directly locked
589*b7893ccfSSadaf Ebrahimi    StartReadObject(device);
590*b7893ccfSSadaf Ebrahimi    StartWriteObject(commandPool);
591*b7893ccfSSadaf Ebrahimi    if(pCommandBuffers) {
592*b7893ccfSSadaf Ebrahimi        // Even though we're immediately "finishing" below, we still are testing for concurrency with any call in process
593*b7893ccfSSadaf Ebrahimi        // so this isn't a no-op
594*b7893ccfSSadaf Ebrahimi        for (uint32_t index = 0; index < commandBufferCount; index++) {
595*b7893ccfSSadaf Ebrahimi            StartWriteObject(pCommandBuffers[index], lockCommandPool);
596*b7893ccfSSadaf Ebrahimi        }
597*b7893ccfSSadaf Ebrahimi        // The driver may immediately reuse command buffers in another thread.
598*b7893ccfSSadaf Ebrahimi        // These updates need to be done before calling down to the driver.
599*b7893ccfSSadaf Ebrahimi        for (uint32_t index = 0; index < commandBufferCount; index++) {
600*b7893ccfSSadaf Ebrahimi            FinishWriteObject(pCommandBuffers[index], lockCommandPool);
601*b7893ccfSSadaf Ebrahimi        }
602*b7893ccfSSadaf Ebrahimi        // Holding the lock for the shortest time while we update the map
603*b7893ccfSSadaf Ebrahimi        for (uint32_t index = 0; index < commandBufferCount; index++) {
604*b7893ccfSSadaf Ebrahimi            auto &bucket = GetBucket(pCommandBuffers[index]);
605*b7893ccfSSadaf Ebrahimi            std::lock_guard<std::mutex> lock(bucket.command_pool_lock);
606*b7893ccfSSadaf Ebrahimi            bucket.command_pool_map.erase(pCommandBuffers[index]);
607*b7893ccfSSadaf Ebrahimi        }
608*b7893ccfSSadaf Ebrahimi    }
609*b7893ccfSSadaf Ebrahimi}
610*b7893ccfSSadaf Ebrahimi
611*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PostCallRecordFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount,
612*b7893ccfSSadaf Ebrahimi                                                    const VkCommandBuffer *pCommandBuffers) {
613*b7893ccfSSadaf Ebrahimi    FinishReadObject(device);
614*b7893ccfSSadaf Ebrahimi    FinishWriteObject(commandPool);
615*b7893ccfSSadaf Ebrahimi}
616*b7893ccfSSadaf Ebrahimi
617*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PreCallRecordResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags) {
618*b7893ccfSSadaf Ebrahimi    StartReadObject(device);
619*b7893ccfSSadaf Ebrahimi    StartWriteObject(commandPool);
620*b7893ccfSSadaf Ebrahimi    // Check for any uses of non-externally sync'd command buffers (for example from vkCmdExecuteCommands)
621*b7893ccfSSadaf Ebrahimi    c_VkCommandPoolContents.StartWrite(commandPool);
622*b7893ccfSSadaf Ebrahimi    // Host access to commandPool must be externally synchronized
623*b7893ccfSSadaf Ebrahimi}
624*b7893ccfSSadaf Ebrahimi
625*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PostCallRecordResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags, VkResult result) {
626*b7893ccfSSadaf Ebrahimi    FinishReadObject(device);
627*b7893ccfSSadaf Ebrahimi    FinishWriteObject(commandPool);
628*b7893ccfSSadaf Ebrahimi    c_VkCommandPoolContents.FinishWrite(commandPool);
629*b7893ccfSSadaf Ebrahimi    // Host access to commandPool must be externally synchronized
630*b7893ccfSSadaf Ebrahimi}
631*b7893ccfSSadaf Ebrahimi
632*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PreCallRecordDestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks *pAllocator) {
633*b7893ccfSSadaf Ebrahimi    StartReadObject(device);
634*b7893ccfSSadaf Ebrahimi    StartWriteObject(commandPool);
635*b7893ccfSSadaf Ebrahimi    // Check for any uses of non-externally sync'd command buffers (for example from vkCmdExecuteCommands)
636*b7893ccfSSadaf Ebrahimi    c_VkCommandPoolContents.StartWrite(commandPool);
637*b7893ccfSSadaf Ebrahimi    // Host access to commandPool must be externally synchronized
638*b7893ccfSSadaf Ebrahimi}
639*b7893ccfSSadaf Ebrahimi
640*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PostCallRecordDestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks *pAllocator) {
641*b7893ccfSSadaf Ebrahimi    FinishReadObject(device);
642*b7893ccfSSadaf Ebrahimi    FinishWriteObject(commandPool);
643*b7893ccfSSadaf Ebrahimi    c_VkCommandPoolContents.FinishWrite(commandPool);
644*b7893ccfSSadaf Ebrahimi}
645*b7893ccfSSadaf Ebrahimi
646*b7893ccfSSadaf Ebrahimi// GetSwapchainImages can return a non-zero count with a NULL pSwapchainImages pointer.  Let's avoid crashes by ignoring
647*b7893ccfSSadaf Ebrahimi// pSwapchainImages.
648*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PreCallRecordGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount,
649*b7893ccfSSadaf Ebrahimi                                                      VkImage *pSwapchainImages) {
650*b7893ccfSSadaf Ebrahimi    StartReadObject(device);
651*b7893ccfSSadaf Ebrahimi    StartReadObject(swapchain);
652*b7893ccfSSadaf Ebrahimi}
653*b7893ccfSSadaf Ebrahimi
654*b7893ccfSSadaf Ebrahimivoid ThreadSafety::PostCallRecordGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount,
655*b7893ccfSSadaf Ebrahimi                                                       VkImage *pSwapchainImages, VkResult result) {
656*b7893ccfSSadaf Ebrahimi    FinishReadObject(device);
657*b7893ccfSSadaf Ebrahimi    FinishReadObject(swapchain);
658*b7893ccfSSadaf Ebrahimi}
659*b7893ccfSSadaf Ebrahimi
660*b7893ccfSSadaf Ebrahimi"""
661*b7893ccfSSadaf Ebrahimi
662*b7893ccfSSadaf Ebrahimi
663*b7893ccfSSadaf Ebrahimi    # This is an ordered list of sections in the header file.
664*b7893ccfSSadaf Ebrahimi    ALL_SECTIONS = ['command']
665*b7893ccfSSadaf Ebrahimi    def __init__(self,
666*b7893ccfSSadaf Ebrahimi                 errFile = sys.stderr,
667*b7893ccfSSadaf Ebrahimi                 warnFile = sys.stderr,
668*b7893ccfSSadaf Ebrahimi                 diagFile = sys.stdout):
669*b7893ccfSSadaf Ebrahimi        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
670*b7893ccfSSadaf Ebrahimi        # Internal state - accumulators for different inner block text
671*b7893ccfSSadaf Ebrahimi        self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
672*b7893ccfSSadaf Ebrahimi        self.non_dispatchable_types = set()
673*b7893ccfSSadaf Ebrahimi        self.object_to_debug_report_type = {
674*b7893ccfSSadaf Ebrahimi            'VkInstance' : 'VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT',
675*b7893ccfSSadaf Ebrahimi            'VkPhysicalDevice' : 'VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT',
676*b7893ccfSSadaf Ebrahimi            'VkDevice' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT',
677*b7893ccfSSadaf Ebrahimi            'VkQueue' : 'VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT',
678*b7893ccfSSadaf Ebrahimi            'VkSemaphore' : 'VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT',
679*b7893ccfSSadaf Ebrahimi            'VkCommandBuffer' : 'VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT',
680*b7893ccfSSadaf Ebrahimi            'VkFence' : 'VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT',
681*b7893ccfSSadaf Ebrahimi            'VkDeviceMemory' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT',
682*b7893ccfSSadaf Ebrahimi            'VkBuffer' : 'VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT',
683*b7893ccfSSadaf Ebrahimi            'VkImage' : 'VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT',
684*b7893ccfSSadaf Ebrahimi            'VkEvent' : 'VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT',
685*b7893ccfSSadaf Ebrahimi            'VkQueryPool' : 'VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT',
686*b7893ccfSSadaf Ebrahimi            'VkBufferView' : 'VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT',
687*b7893ccfSSadaf Ebrahimi            'VkImageView' : 'VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT',
688*b7893ccfSSadaf Ebrahimi            'VkShaderModule' : 'VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT',
689*b7893ccfSSadaf Ebrahimi            'VkPipelineCache' : 'VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT',
690*b7893ccfSSadaf Ebrahimi            'VkPipelineLayout' : 'VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT',
691*b7893ccfSSadaf Ebrahimi            'VkRenderPass' : 'VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT',
692*b7893ccfSSadaf Ebrahimi            'VkPipeline' : 'VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT',
693*b7893ccfSSadaf Ebrahimi            'VkDescriptorSetLayout' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT',
694*b7893ccfSSadaf Ebrahimi            'VkSampler' : 'VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT',
695*b7893ccfSSadaf Ebrahimi            'VkDescriptorPool' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT',
696*b7893ccfSSadaf Ebrahimi            'VkDescriptorSet' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT',
697*b7893ccfSSadaf Ebrahimi            'VkFramebuffer' : 'VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT',
698*b7893ccfSSadaf Ebrahimi            'VkCommandPool' : 'VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT',
699*b7893ccfSSadaf Ebrahimi            'VkSurfaceKHR' : 'VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT',
700*b7893ccfSSadaf Ebrahimi            'VkSwapchainKHR' : 'VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT',
701*b7893ccfSSadaf Ebrahimi            'VkDisplayKHR' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT',
702*b7893ccfSSadaf Ebrahimi            'VkDisplayModeKHR' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT',
703*b7893ccfSSadaf Ebrahimi            'VkObjectTableNVX' : 'VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT',
704*b7893ccfSSadaf Ebrahimi            'VkIndirectCommandsLayoutNVX' : 'VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT',
705*b7893ccfSSadaf Ebrahimi            'VkSamplerYcbcrConversion' : 'VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT',
706*b7893ccfSSadaf Ebrahimi            'VkDescriptorUpdateTemplate' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT',
707*b7893ccfSSadaf Ebrahimi            'VkAccelerationStructureNV' : 'VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT',
708*b7893ccfSSadaf Ebrahimi            'VkDebugReportCallbackEXT' : 'VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT',
709*b7893ccfSSadaf Ebrahimi            'VkValidationCacheEXT' : 'VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT' }
710*b7893ccfSSadaf Ebrahimi
711*b7893ccfSSadaf Ebrahimi    # Check if the parameter passed in is a pointer to an array
712*b7893ccfSSadaf Ebrahimi    def paramIsArray(self, param):
713*b7893ccfSSadaf Ebrahimi        return param.attrib.get('len') is not None
714*b7893ccfSSadaf Ebrahimi
715*b7893ccfSSadaf Ebrahimi    # Check if the parameter passed in is a pointer
716*b7893ccfSSadaf Ebrahimi    def paramIsPointer(self, param):
717*b7893ccfSSadaf Ebrahimi        ispointer = False
718*b7893ccfSSadaf Ebrahimi        for elem in param:
719*b7893ccfSSadaf Ebrahimi            if elem.tag == 'type' and elem.tail is not None and '*' in elem.tail:
720*b7893ccfSSadaf Ebrahimi                ispointer = True
721*b7893ccfSSadaf Ebrahimi        return ispointer
722*b7893ccfSSadaf Ebrahimi
723*b7893ccfSSadaf Ebrahimi    def makeThreadUseBlock(self, cmd, functionprefix):
724*b7893ccfSSadaf Ebrahimi        """Generate C function pointer typedef for <command> Element"""
725*b7893ccfSSadaf Ebrahimi        paramdecl = ''
726*b7893ccfSSadaf Ebrahimi        # Find and add any parameters that are thread unsafe
727*b7893ccfSSadaf Ebrahimi        params = cmd.findall('param')
728*b7893ccfSSadaf Ebrahimi        for param in params:
729*b7893ccfSSadaf Ebrahimi            paramname = param.find('name')
730*b7893ccfSSadaf Ebrahimi            if False: # self.paramIsPointer(param):
731*b7893ccfSSadaf Ebrahimi                paramdecl += '    // not watching use of pointer ' + paramname.text + '\n'
732*b7893ccfSSadaf Ebrahimi            else:
733*b7893ccfSSadaf Ebrahimi                externsync = param.attrib.get('externsync')
734*b7893ccfSSadaf Ebrahimi                if externsync == 'true':
735*b7893ccfSSadaf Ebrahimi                    if self.paramIsArray(param):
736*b7893ccfSSadaf Ebrahimi                        paramdecl += 'if (' + paramname.text + ') {\n'
737*b7893ccfSSadaf Ebrahimi                        paramdecl += '    for (uint32_t index=0; index < ' + param.attrib.get('len') + '; index++) {\n'
738*b7893ccfSSadaf Ebrahimi                        paramdecl += '        ' + functionprefix + 'WriteObject(' + paramname.text + '[index]);\n'
739*b7893ccfSSadaf Ebrahimi                        paramdecl += '    }\n'
740*b7893ccfSSadaf Ebrahimi                        paramdecl += '}\n'
741*b7893ccfSSadaf Ebrahimi                    else:
742*b7893ccfSSadaf Ebrahimi                        paramdecl += functionprefix + 'WriteObject(' + paramname.text + ');\n'
743*b7893ccfSSadaf Ebrahimi                elif (param.attrib.get('externsync')):
744*b7893ccfSSadaf Ebrahimi                    if self.paramIsArray(param):
745*b7893ccfSSadaf Ebrahimi                        # Externsync can list pointers to arrays of members to synchronize
746*b7893ccfSSadaf Ebrahimi                        paramdecl += 'if (' + paramname.text + ') {\n'
747*b7893ccfSSadaf Ebrahimi                        paramdecl += '    for (uint32_t index=0; index < ' + param.attrib.get('len') + '; index++) {\n'
748*b7893ccfSSadaf Ebrahimi                        second_indent = '    '
749*b7893ccfSSadaf Ebrahimi                        for member in externsync.split(","):
750*b7893ccfSSadaf Ebrahimi                            # Replace first empty [] in member name with index
751*b7893ccfSSadaf Ebrahimi                            element = member.replace('[]','[index]',1)
752*b7893ccfSSadaf Ebrahimi                            if '[]' in element:
753*b7893ccfSSadaf Ebrahimi                                # TODO: These null checks can be removed if threading ends up behind parameter
754*b7893ccfSSadaf Ebrahimi                                #       validation in layer order
755*b7893ccfSSadaf Ebrahimi                                element_ptr = element.split('[]')[0]
756*b7893ccfSSadaf Ebrahimi                                paramdecl += '        if (' + element_ptr + ') {\n'
757*b7893ccfSSadaf Ebrahimi                                # Replace any second empty [] in element name with inner array index based on mapping array
758*b7893ccfSSadaf Ebrahimi                                # names like "pSomeThings[]" to "someThingCount" array size. This could be more robust by
759*b7893ccfSSadaf Ebrahimi                                # mapping a param member name to a struct type and "len" attribute.
760*b7893ccfSSadaf Ebrahimi                                limit = element[0:element.find('s[]')] + 'Count'
761*b7893ccfSSadaf Ebrahimi                                dotp = limit.rfind('.p')
762*b7893ccfSSadaf Ebrahimi                                limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
763*b7893ccfSSadaf Ebrahimi                                paramdecl += '            for (uint32_t index2=0; index2 < '+limit+'; index2++) {\n'
764*b7893ccfSSadaf Ebrahimi                                element = element.replace('[]','[index2]')
765*b7893ccfSSadaf Ebrahimi                                second_indent = '        '
766*b7893ccfSSadaf Ebrahimi                                paramdecl += '        ' + second_indent + functionprefix + 'WriteObject(' + element + ');\n'
767*b7893ccfSSadaf Ebrahimi                                paramdecl += '            }\n'
768*b7893ccfSSadaf Ebrahimi                                paramdecl += '        }\n'
769*b7893ccfSSadaf Ebrahimi                            else:
770*b7893ccfSSadaf Ebrahimi                                paramdecl += '    ' + second_indent + functionprefix + 'WriteObject(' + element + ');\n'
771*b7893ccfSSadaf Ebrahimi                        paramdecl += '    }\n'
772*b7893ccfSSadaf Ebrahimi                        paramdecl += '}\n'
773*b7893ccfSSadaf Ebrahimi                    else:
774*b7893ccfSSadaf Ebrahimi                        # externsync can list members to synchronize
775*b7893ccfSSadaf Ebrahimi                        for member in externsync.split(","):
776*b7893ccfSSadaf Ebrahimi                            member = str(member).replace("::", "->")
777*b7893ccfSSadaf Ebrahimi                            member = str(member).replace(".", "->")
778*b7893ccfSSadaf Ebrahimi                            paramdecl += '    ' + functionprefix + 'WriteObject(' + member + ');\n'
779*b7893ccfSSadaf Ebrahimi                else:
780*b7893ccfSSadaf Ebrahimi                    paramtype = param.find('type')
781*b7893ccfSSadaf Ebrahimi                    if paramtype is not None:
782*b7893ccfSSadaf Ebrahimi                        paramtype = paramtype.text
783*b7893ccfSSadaf Ebrahimi                    else:
784*b7893ccfSSadaf Ebrahimi                        paramtype = 'None'
785*b7893ccfSSadaf Ebrahimi                    if paramtype in self.handle_types and paramtype != 'VkPhysicalDevice':
786*b7893ccfSSadaf Ebrahimi                        if self.paramIsArray(param) and ('pPipelines' != paramname.text):
787*b7893ccfSSadaf Ebrahimi                            # Add pointer dereference for array counts that are pointer values
788*b7893ccfSSadaf Ebrahimi                            dereference = ''
789*b7893ccfSSadaf Ebrahimi                            for candidate in params:
790*b7893ccfSSadaf Ebrahimi                                if param.attrib.get('len') == candidate.find('name').text:
791*b7893ccfSSadaf Ebrahimi                                    if self.paramIsPointer(candidate):
792*b7893ccfSSadaf Ebrahimi                                        dereference = '*'
793*b7893ccfSSadaf Ebrahimi                            param_len = str(param.attrib.get('len')).replace("::", "->")
794*b7893ccfSSadaf Ebrahimi                            paramdecl += 'if (' + paramname.text + ') {\n'
795*b7893ccfSSadaf Ebrahimi                            paramdecl += '    for (uint32_t index = 0; index < ' + dereference + param_len + '; index++) {\n'
796*b7893ccfSSadaf Ebrahimi                            paramdecl += '        ' + functionprefix + 'ReadObject(' + paramname.text + '[index]);\n'
797*b7893ccfSSadaf Ebrahimi                            paramdecl += '    }\n'
798*b7893ccfSSadaf Ebrahimi                            paramdecl += '}\n'
799*b7893ccfSSadaf Ebrahimi                        elif not self.paramIsPointer(param):
800*b7893ccfSSadaf Ebrahimi                            # Pointer params are often being created.
801*b7893ccfSSadaf Ebrahimi                            # They are not being read from.
802*b7893ccfSSadaf Ebrahimi                            paramdecl += functionprefix + 'ReadObject(' + paramname.text + ');\n'
803*b7893ccfSSadaf Ebrahimi        explicitexternsyncparams = cmd.findall("param[@externsync]")
804*b7893ccfSSadaf Ebrahimi        if (explicitexternsyncparams is not None):
805*b7893ccfSSadaf Ebrahimi            for param in explicitexternsyncparams:
806*b7893ccfSSadaf Ebrahimi                externsyncattrib = param.attrib.get('externsync')
807*b7893ccfSSadaf Ebrahimi                paramname = param.find('name')
808*b7893ccfSSadaf Ebrahimi                paramdecl += '// Host access to '
809*b7893ccfSSadaf Ebrahimi                if externsyncattrib == 'true':
810*b7893ccfSSadaf Ebrahimi                    if self.paramIsArray(param):
811*b7893ccfSSadaf Ebrahimi                        paramdecl += 'each member of ' + paramname.text
812*b7893ccfSSadaf Ebrahimi                    elif self.paramIsPointer(param):
813*b7893ccfSSadaf Ebrahimi                        paramdecl += 'the object referenced by ' + paramname.text
814*b7893ccfSSadaf Ebrahimi                    else:
815*b7893ccfSSadaf Ebrahimi                        paramdecl += paramname.text
816*b7893ccfSSadaf Ebrahimi                else:
817*b7893ccfSSadaf Ebrahimi                    paramdecl += externsyncattrib
818*b7893ccfSSadaf Ebrahimi                paramdecl += ' must be externally synchronized\n'
819*b7893ccfSSadaf Ebrahimi
820*b7893ccfSSadaf Ebrahimi        # Find and add any "implicit" parameters that are thread unsafe
821*b7893ccfSSadaf Ebrahimi        implicitexternsyncparams = cmd.find('implicitexternsyncparams')
822*b7893ccfSSadaf Ebrahimi        if (implicitexternsyncparams is not None):
823*b7893ccfSSadaf Ebrahimi            for elem in implicitexternsyncparams:
824*b7893ccfSSadaf Ebrahimi                paramdecl += '// '
825*b7893ccfSSadaf Ebrahimi                paramdecl += elem.text
826*b7893ccfSSadaf Ebrahimi                paramdecl += ' must be externally synchronized between host accesses\n'
827*b7893ccfSSadaf Ebrahimi
828*b7893ccfSSadaf Ebrahimi        if (paramdecl == ''):
829*b7893ccfSSadaf Ebrahimi            return None
830*b7893ccfSSadaf Ebrahimi        else:
831*b7893ccfSSadaf Ebrahimi            return paramdecl
832*b7893ccfSSadaf Ebrahimi    def beginFile(self, genOpts):
833*b7893ccfSSadaf Ebrahimi        OutputGenerator.beginFile(self, genOpts)
834*b7893ccfSSadaf Ebrahimi
835*b7893ccfSSadaf Ebrahimi        # Initialize members that require the tree
836*b7893ccfSSadaf Ebrahimi        self.handle_types = GetHandleTypes(self.registry.tree)
837*b7893ccfSSadaf Ebrahimi
838*b7893ccfSSadaf Ebrahimi        # TODO: LUGMAL -- remove this and add our copyright
839*b7893ccfSSadaf Ebrahimi        # User-supplied prefix text, if any (list of strings)
840*b7893ccfSSadaf Ebrahimi        write(self.inline_copyright_message, file=self.outFile)
841*b7893ccfSSadaf Ebrahimi
842*b7893ccfSSadaf Ebrahimi        self.header_file = (genOpts.filename == 'thread_safety.h')
843*b7893ccfSSadaf Ebrahimi        self.source_file = (genOpts.filename == 'thread_safety.cpp')
844*b7893ccfSSadaf Ebrahimi
845*b7893ccfSSadaf Ebrahimi        if not self.header_file and not self.source_file:
846*b7893ccfSSadaf Ebrahimi            print("Error: Output Filenames have changed, update generator source.\n")
847*b7893ccfSSadaf Ebrahimi            sys.exit(1)
848*b7893ccfSSadaf Ebrahimi
849*b7893ccfSSadaf Ebrahimi        if self.source_file:
850*b7893ccfSSadaf Ebrahimi            write('#include "chassis.h"', file=self.outFile)
851*b7893ccfSSadaf Ebrahimi            write('#include "thread_safety.h"', file=self.outFile)
852*b7893ccfSSadaf Ebrahimi            self.newline()
853*b7893ccfSSadaf Ebrahimi            write(self.inline_custom_source_preamble, file=self.outFile)
854*b7893ccfSSadaf Ebrahimi
855*b7893ccfSSadaf Ebrahimi
856*b7893ccfSSadaf Ebrahimi    def endFile(self):
857*b7893ccfSSadaf Ebrahimi
858*b7893ccfSSadaf Ebrahimi        # Create class definitions
859*b7893ccfSSadaf Ebrahimi        counter_class_defs = ''
860*b7893ccfSSadaf Ebrahimi        counter_class_instances = ''
861*b7893ccfSSadaf Ebrahimi        counter_class_bodies = ''
862*b7893ccfSSadaf Ebrahimi
863*b7893ccfSSadaf Ebrahimi        for obj in sorted(self.non_dispatchable_types):
864*b7893ccfSSadaf Ebrahimi            counter_class_defs += '    counter<%s> c_%s;\n' % (obj, obj)
865*b7893ccfSSadaf Ebrahimi            if obj in self.object_to_debug_report_type:
866*b7893ccfSSadaf Ebrahimi                obj_type = self.object_to_debug_report_type[obj]
867*b7893ccfSSadaf Ebrahimi            else:
868*b7893ccfSSadaf Ebrahimi                obj_type = 'VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT'
869*b7893ccfSSadaf Ebrahimi            counter_class_instances += '          c_%s("%s", %s, &report_data),\n' % (obj, obj, obj_type)
870*b7893ccfSSadaf Ebrahimi            counter_class_bodies += 'WRAPPER(%s)\n' % obj
871*b7893ccfSSadaf Ebrahimi        if self.header_file:
872*b7893ccfSSadaf Ebrahimi            class_def = self.inline_custom_header_preamble.replace('COUNTER_CLASS_DEFINITIONS_TEMPLATE', counter_class_defs)
873*b7893ccfSSadaf Ebrahimi            class_def = class_def.replace('COUNTER_CLASS_INSTANCES_TEMPLATE', counter_class_instances[:-2]) # Kill last comma
874*b7893ccfSSadaf Ebrahimi            class_def = class_def.replace('COUNTER_CLASS_BODIES_TEMPLATE', counter_class_bodies)
875*b7893ccfSSadaf Ebrahimi            write(class_def, file=self.outFile)
876*b7893ccfSSadaf Ebrahimi        write('\n'.join(self.sections['command']), file=self.outFile)
877*b7893ccfSSadaf Ebrahimi        if self.header_file:
878*b7893ccfSSadaf Ebrahimi            write('};', file=self.outFile)
879*b7893ccfSSadaf Ebrahimi
880*b7893ccfSSadaf Ebrahimi        # Finish processing in superclass
881*b7893ccfSSadaf Ebrahimi        OutputGenerator.endFile(self)
882*b7893ccfSSadaf Ebrahimi
883*b7893ccfSSadaf Ebrahimi    def beginFeature(self, interface, emit):
884*b7893ccfSSadaf Ebrahimi        #write('// starting beginFeature', file=self.outFile)
885*b7893ccfSSadaf Ebrahimi        # Start processing in superclass
886*b7893ccfSSadaf Ebrahimi        OutputGenerator.beginFeature(self, interface, emit)
887*b7893ccfSSadaf Ebrahimi        # C-specific
888*b7893ccfSSadaf Ebrahimi        # Accumulate includes, defines, types, enums, function pointer typedefs,
889*b7893ccfSSadaf Ebrahimi        # end function prototypes separately for this feature. They're only
890*b7893ccfSSadaf Ebrahimi        # printed in endFeature().
891*b7893ccfSSadaf Ebrahimi        self.featureExtraProtect = GetFeatureProtect(interface)
892*b7893ccfSSadaf Ebrahimi        if (self.featureExtraProtect is not None):
893*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '\n#ifdef %s' % self.featureExtraProtect)
894*b7893ccfSSadaf Ebrahimi
895*b7893ccfSSadaf Ebrahimi        #write('// ending beginFeature', file=self.outFile)
896*b7893ccfSSadaf Ebrahimi    def endFeature(self):
897*b7893ccfSSadaf Ebrahimi        # C-specific
898*b7893ccfSSadaf Ebrahimi        if (self.emit):
899*b7893ccfSSadaf Ebrahimi            if (self.featureExtraProtect is not None):
900*b7893ccfSSadaf Ebrahimi                self.appendSection('command', '#endif // %s' % self.featureExtraProtect)
901*b7893ccfSSadaf Ebrahimi        # Finish processing in superclass
902*b7893ccfSSadaf Ebrahimi        OutputGenerator.endFeature(self)
903*b7893ccfSSadaf Ebrahimi    #
904*b7893ccfSSadaf Ebrahimi    # Append a definition to the specified section
905*b7893ccfSSadaf Ebrahimi    def appendSection(self, section, text):
906*b7893ccfSSadaf Ebrahimi        self.sections[section].append(text)
907*b7893ccfSSadaf Ebrahimi    #
908*b7893ccfSSadaf Ebrahimi    # Type generation
909*b7893ccfSSadaf Ebrahimi    def genType(self, typeinfo, name, alias):
910*b7893ccfSSadaf Ebrahimi        OutputGenerator.genType(self, typeinfo, name, alias)
911*b7893ccfSSadaf Ebrahimi        if self.handle_types.IsNonDispatchable(name):
912*b7893ccfSSadaf Ebrahimi            self.non_dispatchable_types.add(name)
913*b7893ccfSSadaf Ebrahimi    #
914*b7893ccfSSadaf Ebrahimi    # Struct (e.g. C "struct" type) generation.
915*b7893ccfSSadaf Ebrahimi    # This is a special case of the <type> tag where the contents are
916*b7893ccfSSadaf Ebrahimi    # interpreted as a set of <member> tags instead of freeform C
917*b7893ccfSSadaf Ebrahimi    # C type declarations. The <member> tags are just like <param>
918*b7893ccfSSadaf Ebrahimi    # tags - they are a declaration of a struct or union member.
919*b7893ccfSSadaf Ebrahimi    # Only simple member declarations are supported (no nested
920*b7893ccfSSadaf Ebrahimi    # structs etc.)
921*b7893ccfSSadaf Ebrahimi    def genStruct(self, typeinfo, typeName, alias):
922*b7893ccfSSadaf Ebrahimi        OutputGenerator.genStruct(self, typeinfo, typeName, alias)
923*b7893ccfSSadaf Ebrahimi        body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
924*b7893ccfSSadaf Ebrahimi        # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
925*b7893ccfSSadaf Ebrahimi        for member in typeinfo.elem.findall('.//member'):
926*b7893ccfSSadaf Ebrahimi            body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
927*b7893ccfSSadaf Ebrahimi            body += ';\n'
928*b7893ccfSSadaf Ebrahimi        body += '} ' + typeName + ';\n'
929*b7893ccfSSadaf Ebrahimi        self.appendSection('struct', body)
930*b7893ccfSSadaf Ebrahimi    #
931*b7893ccfSSadaf Ebrahimi    # Group (e.g. C "enum" type) generation.
932*b7893ccfSSadaf Ebrahimi    # These are concatenated together with other types.
933*b7893ccfSSadaf Ebrahimi    def genGroup(self, groupinfo, groupName, alias):
934*b7893ccfSSadaf Ebrahimi        pass
935*b7893ccfSSadaf Ebrahimi    # Enumerant generation
936*b7893ccfSSadaf Ebrahimi    # <enum> tags may specify their values in several ways, but are usually
937*b7893ccfSSadaf Ebrahimi    # just integers.
938*b7893ccfSSadaf Ebrahimi    def genEnum(self, enuminfo, name, alias):
939*b7893ccfSSadaf Ebrahimi        pass
940*b7893ccfSSadaf Ebrahimi    #
941*b7893ccfSSadaf Ebrahimi    # Command generation
942*b7893ccfSSadaf Ebrahimi    def genCmd(self, cmdinfo, name, alias):
943*b7893ccfSSadaf Ebrahimi        # Commands shadowed by interface functions and are not implemented
944*b7893ccfSSadaf Ebrahimi        special_functions = [
945*b7893ccfSSadaf Ebrahimi            'vkCreateDevice',
946*b7893ccfSSadaf Ebrahimi            'vkCreateInstance',
947*b7893ccfSSadaf Ebrahimi            'vkAllocateCommandBuffers',
948*b7893ccfSSadaf Ebrahimi            'vkFreeCommandBuffers',
949*b7893ccfSSadaf Ebrahimi            'vkResetCommandPool',
950*b7893ccfSSadaf Ebrahimi            'vkDestroyCommandPool',
951*b7893ccfSSadaf Ebrahimi            'vkAllocateDescriptorSets',
952*b7893ccfSSadaf Ebrahimi            'vkQueuePresentKHR',
953*b7893ccfSSadaf Ebrahimi            'vkGetSwapchainImagesKHR',
954*b7893ccfSSadaf Ebrahimi        ]
955*b7893ccfSSadaf Ebrahimi        if name == 'vkQueuePresentKHR' or (name in special_functions and self.source_file):
956*b7893ccfSSadaf Ebrahimi            return
957*b7893ccfSSadaf Ebrahimi
958*b7893ccfSSadaf Ebrahimi        if (("DebugMarker" in name or "DebugUtilsObject" in name) and "EXT" in name):
959*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
960*b7893ccfSSadaf Ebrahimi            return
961*b7893ccfSSadaf Ebrahimi
962*b7893ccfSSadaf Ebrahimi        # Determine first if this function needs to be intercepted
963*b7893ccfSSadaf Ebrahimi        startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'Start')
964*b7893ccfSSadaf Ebrahimi        if startthreadsafety is None:
965*b7893ccfSSadaf Ebrahimi            return
966*b7893ccfSSadaf Ebrahimi        finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'Finish')
967*b7893ccfSSadaf Ebrahimi
968*b7893ccfSSadaf Ebrahimi        OutputGenerator.genCmd(self, cmdinfo, name, alias)
969*b7893ccfSSadaf Ebrahimi
970*b7893ccfSSadaf Ebrahimi        # setup common to call wrappers
971*b7893ccfSSadaf Ebrahimi        # first parameter is always dispatchable
972*b7893ccfSSadaf Ebrahimi        dispatchable_type = cmdinfo.elem.find('param/type').text
973*b7893ccfSSadaf Ebrahimi        dispatchable_name = cmdinfo.elem.find('param/name').text
974*b7893ccfSSadaf Ebrahimi
975*b7893ccfSSadaf Ebrahimi        decls = self.makeCDecls(cmdinfo.elem)
976*b7893ccfSSadaf Ebrahimi
977*b7893ccfSSadaf Ebrahimi        result_type = cmdinfo.elem.find('proto/type')
978*b7893ccfSSadaf Ebrahimi
979*b7893ccfSSadaf Ebrahimi        if self.source_file:
980*b7893ccfSSadaf Ebrahimi            pre_decl = decls[0][:-1]
981*b7893ccfSSadaf Ebrahimi            pre_decl = pre_decl.split("VKAPI_CALL ")[1]
982*b7893ccfSSadaf Ebrahimi            pre_decl = 'void ThreadSafety::PreCallRecord' + pre_decl + ' {'
983*b7893ccfSSadaf Ebrahimi
984*b7893ccfSSadaf Ebrahimi            # PreCallRecord
985*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '')
986*b7893ccfSSadaf Ebrahimi            self.appendSection('command', pre_decl)
987*b7893ccfSSadaf Ebrahimi            self.appendSection('command', "    " + "\n    ".join(str(startthreadsafety).rstrip().split("\n")))
988*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '}')
989*b7893ccfSSadaf Ebrahimi
990*b7893ccfSSadaf Ebrahimi            # PostCallRecord
991*b7893ccfSSadaf Ebrahimi            post_decl = pre_decl.replace('PreCallRecord', 'PostCallRecord')
992*b7893ccfSSadaf Ebrahimi            if result_type.text == 'VkResult':
993*b7893ccfSSadaf Ebrahimi                post_decl = post_decl.replace(')', ',\n    VkResult                                    result)')
994*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '')
995*b7893ccfSSadaf Ebrahimi            self.appendSection('command', post_decl)
996*b7893ccfSSadaf Ebrahimi            self.appendSection('command', "    " + "\n    ".join(str(finishthreadsafety).rstrip().split("\n")))
997*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '}')
998*b7893ccfSSadaf Ebrahimi
999*b7893ccfSSadaf Ebrahimi        if self.header_file:
1000*b7893ccfSSadaf Ebrahimi            pre_decl = decls[0][:-1]
1001*b7893ccfSSadaf Ebrahimi            pre_decl = pre_decl.split("VKAPI_CALL ")[1]
1002*b7893ccfSSadaf Ebrahimi            pre_decl = 'void PreCallRecord' + pre_decl + ';'
1003*b7893ccfSSadaf Ebrahimi
1004*b7893ccfSSadaf Ebrahimi            # PreCallRecord
1005*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '')
1006*b7893ccfSSadaf Ebrahimi            self.appendSection('command', pre_decl)
1007*b7893ccfSSadaf Ebrahimi
1008*b7893ccfSSadaf Ebrahimi            # PostCallRecord
1009*b7893ccfSSadaf Ebrahimi            post_decl = pre_decl.replace('PreCallRecord', 'PostCallRecord')
1010*b7893ccfSSadaf Ebrahimi            if result_type.text == 'VkResult':
1011*b7893ccfSSadaf Ebrahimi                post_decl = post_decl.replace(')', ',\n    VkResult                                    result)')
1012*b7893ccfSSadaf Ebrahimi            self.appendSection('command', '')
1013*b7893ccfSSadaf Ebrahimi            self.appendSection('command', post_decl)
1014*b7893ccfSSadaf Ebrahimi
1015*b7893ccfSSadaf Ebrahimi    #
1016*b7893ccfSSadaf Ebrahimi    # override makeProtoName to drop the "vk" prefix
1017*b7893ccfSSadaf Ebrahimi    def makeProtoName(self, name, tail):
1018*b7893ccfSSadaf Ebrahimi        return self.genOpts.apientry + name[2:] + tail
1019