1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2020-2021 The Khronos Group Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# Author: Spencer Fricke <s.fricke@samsung.com>
18
19import os,re,sys,string,json
20import xml.etree.ElementTree as etree
21from generator import *
22from collections import namedtuple
23from common_codegen import *
24
25# This is a workaround to use a Python 2.7 and 3.x compatible syntax
26from io import open
27
28class SpirvValidationHelperOutputGeneratorOptions(GeneratorOptions):
29    def __init__(self,
30                 conventions = None,
31                 filename = None,
32                 directory = '.',
33                 genpath = None,
34                 apiname = 'vulkan',
35                 profile = None,
36                 versions = '.*',
37                 emitversions = '.*',
38                 defaultExtensions = 'vulkan',
39                 addExtensions = None,
40                 removeExtensions = None,
41                 emitExtensions = None,
42                 emitSpirv = None,
43                 sortProcedure = regSortFeatures,
44                 genFuncPointers = True,
45                 protectFile = True,
46                 protectFeature = False,
47                 apicall = 'VKAPI_ATTR ',
48                 apientry = 'VKAPI_CALL ',
49                 apientryp = 'VKAPI_PTR *',
50                 indentFuncProto = True,
51                 indentFuncPointer = False,
52                 alignFuncParam = 48,
53                 expandEnumerants = False):
54        GeneratorOptions.__init__(self,
55                conventions = conventions,
56                filename = filename,
57                directory = directory,
58                genpath = genpath,
59                apiname = apiname,
60                profile = profile,
61                versions = versions,
62                emitversions = emitversions,
63                defaultExtensions = defaultExtensions,
64                addExtensions = addExtensions,
65                removeExtensions = removeExtensions,
66                emitExtensions = emitExtensions,
67                emitSpirv = emitSpirv,
68                sortProcedure = sortProcedure)
69        self.genFuncPointers = genFuncPointers
70        self.protectFile     = protectFile
71        self.protectFeature  = protectFeature
72        self.apicall         = apicall
73        self.apientry        = apientry
74        self.apientryp       = apientryp
75        self.indentFuncProto = indentFuncProto
76        self.indentFuncPointer = indentFuncPointer
77        self.alignFuncParam  = alignFuncParam
78        self.expandEnumerants = expandEnumerants
79#
80# SpirvValidationHelperOutputGenerator - Generate SPIR-V validation
81# for SPIR-V extensions and capabilities
82class SpirvValidationHelperOutputGenerator(OutputGenerator):
83    def __init__(self,
84                 errFile = sys.stderr,
85                 warnFile = sys.stderr,
86                 diagFile = sys.stdout):
87        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
88        self.extensions = dict()
89        self.capabilities = dict()
90
91        # TODO - Remove these ExludeList array in future when script is been used in a few releases
92        #
93        # Sometimes the Vulkan-Headers XML will mention new SPIR-V capability or extensions
94        # That require an update of the SPIRV-Headers which might not be ready to pull in.
95        # These 2 arrays SHOULD be empty when possible and when the SPIR-V Headers are updated these
96        # should be attempted to be cleared
97        self.extensionExcludeList = []
98        self.capabilityExcludeList = []
99
100        # This is a list that maps the Vulkan struct a feature field is with the internal
101        # state tracker's enabled features value
102        #
103        # If a new SPIR-V Capability is added to uses a new feature struct, it will need to be
104        # added here with the name added in 'DeviceFeatures' struct
105        self.featureMap = [
106          # {'vulkan' : <Vulkan Spec Feature Struct Name>, 'layer' : <Name of variable in CoreChecks DeviceFeatures>},
107            {'vulkan' : 'VkPhysicalDeviceFeatures', 'layer' : 'core'},
108            {'vulkan' : 'VkPhysicalDeviceVulkan11Features', 'layer' : 'core11'},
109            {'vulkan' : 'VkPhysicalDeviceVulkan12Features', 'layer' : 'core12'},
110            {'vulkan' : 'VkPhysicalDeviceTransformFeedbackFeaturesEXT', 'layer' : 'transform_feedback_features'},
111            {'vulkan' : 'VkPhysicalDeviceCooperativeMatrixFeaturesNV', 'layer' : 'cooperative_matrix_features'},
112            {'vulkan' : 'VkPhysicalDeviceComputeShaderDerivativesFeaturesNV', 'layer' : 'compute_shader_derivatives_features'},
113            {'vulkan' : 'VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV', 'layer' : 'fragment_shader_barycentric_features'},
114            {'vulkan' : 'VkPhysicalDeviceShaderImageFootprintFeaturesNV', 'layer' : 'shader_image_footprint_features'},
115            {'vulkan' : 'VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT', 'layer' : 'fragment_shader_interlock_features'},
116            {'vulkan' : 'VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT', 'layer' : 'demote_to_helper_invocation_features'},
117            {'vulkan' : 'VkPhysicalDeviceRayQueryFeaturesKHR', 'layer' : 'ray_query_features'},
118            {'vulkan' : 'VkPhysicalDeviceRayTracingPipelineFeaturesKHR', 'layer' : 'ray_tracing_pipeline_features'},
119            {'vulkan' : 'VkPhysicalDeviceAccelerationStructureFeaturesKHR', 'layer' : 'ray_tracing_acceleration_structure_features'},
120            {'vulkan' : 'VkPhysicalDeviceFragmentDensityMapFeaturesEXT', 'layer' : 'fragment_density_map_features'},
121            {'vulkan' : 'VkPhysicalDeviceBufferDeviceAddressFeaturesEXT', 'layer' : 'buffer_device_address_ext_features'},
122            {'vulkan' : 'VkPhysicalDeviceFragmentShadingRateFeaturesKHR', 'layer' : 'fragment_shading_rate_features'},
123            {'vulkan' : 'VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL', 'layer' : 'shader_integer_functions2_features'},
124            {'vulkan' : 'VkPhysicalDeviceShaderSMBuiltinsFeaturesNV', 'layer' : 'shader_sm_builtins_features'},
125            {'vulkan' : 'VkPhysicalDeviceShadingRateImageFeaturesNV', 'layer' : 'shading_rate_image_features'},
126            {'vulkan' : 'VkPhysicalDeviceShaderAtomicFloatFeaturesEXT', 'layer' : 'shader_atomic_float_features'},
127            {'vulkan' : 'VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT', 'layer' : 'shader_image_atomic_int64_features'},
128            {'vulkan' : 'VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR', 'layer' : 'workgroup_memory_explicit_layout_features'},
129            {'vulkan' : 'VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT', 'layer' : 'shader_atomic_float2_features'},
130            {'vulkan' : 'VkPhysicalDeviceRayTracingMotionBlurFeaturesNV', 'layer' : 'ray_tracing_motion_blur_features'},
131            {'vulkan' : 'VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR', 'layer' : 'shader_integer_dot_product_features'},
132        ]
133
134        # Promoted features structure in state_tracker.cpp are put in the VkPhysicalDeviceVulkan*Features structs
135        # but the XML can still list them. This list all promoted structs to ignore since they are aliased.
136        # Tried to generate these, but no reliable way from vk.xml
137        self.promotedFeatures = [
138            # 1.1
139            "VkPhysicalDevice16BitStorageFeatures",
140            "VkPhysicalDeviceMultiviewFeatures",
141            "VkPhysicalDeviceVariablePointersFeatures",
142            "VkPhysicalDeviceProtectedMemoryFeatures",
143            "VkPhysicalDeviceSamplerYcbcrConversionFeatures",
144            "VkPhysicalDeviceShaderDrawParametersFeatures",
145            # 1.2
146            "VkPhysicalDevice8BitStorageFeatures",
147            "VkPhysicalDeviceShaderFloat16Int8Features",
148            "VkPhysicalDeviceDescriptorIndexingFeatures",
149            "VkPhysicalDeviceScalarBlockLayoutFeatures",
150            "VkPhysicalDeviceImagelessFramebufferFeatures",
151            "VkPhysicalDeviceUniformBufferStandardLayoutFeatures",
152            "VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures",
153            "VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures",
154            "VkPhysicalDeviceTimelineSemaphoreFeatures",
155            "VkPhysicalDeviceBufferDeviceAddressFeatures",
156            "VkPhysicalDeviceShaderAtomicInt64Features",
157            "VkPhysicalDeviceVulkanMemoryModelFeatures",
158        ]
159
160        # Properties are harder to handle genearted without generating a template for every property struct type
161        # The simpler solution is create strings that will be printed out as static comparisons at compile time
162        # The Map is used to map Vulkan property structs with the state tracker variable name
163        self.propertyInfo = dict()
164        self.propertyMap = {
165            'VkPhysicalDeviceVulkan11Properties' : 'phys_dev_props_core11',
166            'VkPhysicalDeviceVulkan12Properties' : 'phys_dev_props_core12',
167        }
168
169    #
170    # Called at beginning of processing as file is opened
171    def beginFile(self, genOpts):
172        OutputGenerator.beginFile(self, genOpts)
173        # File Comment
174        file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
175        file_comment += '// See spirv_validation_generator.py for modifications\n'
176        write(file_comment, file=self.outFile)
177        # Copyright Statement
178        copyright = ''
179        copyright += '\n'
180        copyright += '/***************************************************************************\n'
181        copyright += ' *\n'
182        copyright += ' * Copyright (c) 2020-2021 The Khronos Group Inc.\n'
183        copyright += ' *\n'
184        copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
185        copyright += ' * you may not use this file except in compliance with the License.\n'
186        copyright += ' * You may obtain a copy of the License at\n'
187        copyright += ' *\n'
188        copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
189        copyright += ' *\n'
190        copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
191        copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
192        copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
193        copyright += ' * See the License for the specific language governing permissions and\n'
194        copyright += ' * limitations under the License.\n'
195        copyright += ' *\n'
196        copyright += ' * Author: Spencer Fricke <s.fricke@samsung.com>\n'
197        copyright += ' *\n'
198        copyright += ' * This file is related to anything that is found in the Vulkan XML related\n'
199        copyright += ' * to SPIR-V. Anything related to the SPIR-V grammar belongs in spirv_grammar_helper\n'
200        copyright += ' *\n'
201        copyright += ' ****************************************************************************/\n'
202        write(copyright, file=self.outFile)
203        write('#include <string>', file=self.outFile)
204        write('#include <functional>', file=self.outFile)
205        write('#include <spirv/unified1/spirv.hpp>', file=self.outFile)
206        write('#include "vk_extension_helper.h"', file=self.outFile)
207        write('#include "shader_module.h"', file=self.outFile)
208        write('#include "device_state.h"', file=self.outFile)
209        write('#include "core_validation.h"', file=self.outFile)
210        write(self.featurePointer(), file=self.outFile)
211        write(self.mapStructDeclarations(), file=self.outFile)
212    #
213    # Write generated file content to output file
214    def endFile(self):
215        write(self.capabilityStruct(), file=self.outFile)
216        write(self.extensionStruct(), file=self.outFile)
217        write(self.enumHelper(), file=self.outFile)
218        write(self.validateFunction(), file=self.outFile)
219        # Finish processing in superclass
220        OutputGenerator.endFile(self)
221    #
222    # Processing point at beginning of each extension definition
223    def beginFeature(self, interface, emit):
224        OutputGenerator.beginFeature(self, interface, emit)
225        self.featureExtraProtect = GetFeatureProtect(interface)
226    #
227    # Capture all SPIR-V elements from registry
228    def genSpirv(self, spirvinfo, spirvName, alias):
229        OutputGenerator.genSpirv(self, spirvinfo, spirvName, alias)
230        spirvElem = spirvinfo.elem
231        name = spirvElem.get('name')
232        enables = []
233        for elem in spirvElem:
234            if elem.tag != 'enable':
235                self.logMsg('error', 'should only be <enable> tags in ' + name)
236            # Each <enable> holds only one possible requirment
237            # This internal python dict is to represent the struct generated later
238            enable = {
239                'version' : None,
240                'feature' : None,
241                'extension' : None,
242                'property' : None,
243            }
244            if 'version' in elem.attrib:
245                enable['version'] = elem.attrib['version']
246            elif 'feature' in elem.attrib:
247                if elem.attrib['struct'] in self.promotedFeatures:
248                    continue
249                enable['feature'] = {
250                    'feature' : elem.attrib['feature'],
251                    'struct' : elem.attrib['struct']
252                }
253            elif 'extension' in elem.attrib:
254                enable['extension'] = elem.attrib['extension']
255            elif 'property' in elem.attrib:
256                enable['property'] =  {
257                    'property' : elem.attrib['property'],
258                    'member' : elem.attrib['member'],
259                    'value' : elem.attrib['value']
260                }
261            else:
262                self.logMsg('error', 'No known attributes in <enable> for ' + name)
263            enables.append(enable)
264        if spirvElem.tag == 'spirvcapability':
265            self.capabilities[name] = enables
266        elif spirvElem.tag == 'spirvextension':
267            self.extensions[name] = enables
268    #
269    # Creates the Enum string helpers for better error messages. Same idea of vk_enum_string_helper.h but for SPIR-V
270    def enumHelper(self):
271        # There are some enums that share the same value in the SPIR-V header.
272        # This array remove the duplicate to not print out, usually due to being the older value given
273        excludeList = ['ShaderViewportIndexLayerNV', 'ShadingRateNV']
274        output =  'static inline const char* string_SpvCapability(uint32_t input_value) {\n'
275        output += '    switch ((spv::Capability)input_value) {\n'
276        for name, enables in sorted(self.capabilities.items()):
277            if (name not in excludeList) and (name not in self.capabilityExcludeList):
278                output += '         case spv::Capability' + name + ':\n'
279                output += '            return \"' + name + '\";\n'
280        output += '        default:\n'
281        output += '            return \"Unhandled OpCapability\";\n'
282        output += '    };\n'
283        output += '};'
284        return output
285    #
286    # Creates the FeaturePointer struct to map features with those in the layers state tracker
287    def featurePointer(self):
288        output = '\n'
289        output += 'struct FeaturePointer {\n'
290        output += '    // Callable object to test if this feature is enabled in the given aggregate feature struct\n'
291        output += '    const std::function<VkBool32(const DeviceFeatures &)> IsEnabled;\n'
292        output += '\n'
293        output += '    // Test if feature pointer is populated\n'
294        output += '    explicit operator bool() const { return static_cast<bool>(IsEnabled); }\n'
295        output += '\n'
296        output += '    // Default and nullptr constructor to create an empty FeaturePointer\n'
297        output += '    FeaturePointer() : IsEnabled(nullptr) {}\n'
298        output += '    FeaturePointer(std::nullptr_t ptr) : IsEnabled(nullptr) {}\n'
299        output += '\n'
300        output += '    // Constructors to populate FeaturePointer based on given pointer to member\n'
301        for feature in self.featureMap:
302            output += '    FeaturePointer(VkBool32 ' + feature['vulkan'] + '::*ptr)\n'
303            output += '        : IsEnabled([=](const DeviceFeatures &features) { return features.' + feature['layer'] + '.*ptr; }) {}\n'
304        output += '};\n'
305        return output
306    #
307    # Declare the struct that contains requirement for the spirv info
308    def mapStructDeclarations(self):
309        output = '// Each instance of the struct will only have a singel field non-null\n'
310        output += 'struct RequiredSpirvInfo {\n'
311        output += '    uint32_t version;\n'
312        output += '    FeaturePointer feature;\n'
313        output += '    ExtEnabled DeviceExtensions::*extension;\n'
314        output += '    const char* property; // For human readability and make some capabilities unique\n'
315        output += '};\n'
316        return output
317    #
318    # Creates the value of the struct declared in mapStructDeclarations()
319    def createMapValue(self, name, enable, isExtension):
320        output = ''
321        if enable['version'] != None:
322            # Version should be VK_VERSION_x_x as defined in header but need to get as VK_API_VERSION_x_x
323            version = enable['version'].replace('VK_VERSION', 'VK_API_VERSION')
324            output = '{' + version + ', nullptr, nullptr, ""}'
325        elif enable['feature'] != None:
326            output = '{0, &' + enable['feature']['struct'] + '::'  + enable['feature']['feature'] + ', nullptr, ""}'
327        elif enable['extension'] != None:
328            # All fields in DeviceExtensions should just be the extension name lowercase
329            output = '{0, nullptr, &DeviceExtensions::' + enable['extension'].lower() + ', ""}'
330        elif enable['property'] != None:
331            propertyStruct = enable['property']['property']
332            # Need to make sure to return a boolean value to prevent compiler warning for implicit conversions
333            propertyLogic = "(" + propertyStruct + '::' + enable['property']['member'] + ' & ' + enable['property']['value'] + ") != 0"
334            # Property might have multiple items per capability/extension
335            if name not in self.propertyInfo:
336                self.propertyInfo[name] = []
337            # Save info later to be printed out
338            self.propertyInfo[name].append({
339                "logic" : propertyLogic,
340                "struct" : propertyStruct,
341                "isExtension" : isExtension
342            })
343            # For properties, this string is just for human readableness
344            output = '{0, nullptr, nullptr, "' + propertyLogic + '"}'
345        else:
346            output = '{0, nullptr, nullptr, ""}'
347        return output
348    #
349    # Build the struct with all the requirments for the spirv capabilities
350    def capabilityStruct(self):
351        output = '// clang-format off\n'
352        output += 'static const std::unordered_multimap<uint32_t, RequiredSpirvInfo> spirvCapabilities = {\n'
353
354        # Sort so the order is the same on Windows and Unix
355        for name, enables in sorted(self.capabilities.items()):
356            for enable in enables:
357                # Prepend with comment and comment out line if in exclude list as explained in declaration
358                if name in self.capabilityExcludeList:
359                    output += '    // Not found in current SPIR-V Headers\n    //'
360                output += '    {spv::Capability' + name + ', ' + self.createMapValue(name, enable, False) + '},\n'
361        output += '};\n'
362        output += '// clang-format on\n'
363        return output
364    #
365    # Build the struct with all the requirments for the spirv extensions
366    def extensionStruct(self):
367        output = '// clang-format off\n'
368        output += 'static const std::unordered_multimap<std::string, RequiredSpirvInfo> spirvExtensions = {\n'
369
370        # Sort so the order is the same on Windows and Unix
371        for name, enables in sorted(self.extensions.items()):
372            for enable in enables:
373                # Prepend with comment and comment out line if in exclude list as explained in declaration
374                if name in self.extensionExcludeList:
375                    output += '    // Not found in current SPIR-V Headers\n    //'
376                output += '    {\"' + name + '\", ' + self.createMapValue(name, enable, True) + '},\n'
377        output += '};\n'
378        output += '// clang-format on\n'
379        return output
380    #
381    # The main function to validate all the extensions and capabilities
382    def validateFunction(self):
383        output = '''
384bool CoreChecks::ValidateShaderCapabilitiesAndExtensions(SHADER_MODULE_STATE const *src, spirv_inst_iter& insn) const {
385    bool skip = false;
386
387    if (insn.opcode() == spv::OpCapability) {
388        // All capabilities are generated so if it is not in the list it is not supported by Vulkan
389        if (spirvCapabilities.count(insn.word(1)) == 0) {
390            skip |= LogError(device, "VUID-VkShaderModuleCreateInfo-pCode-01090",
391                "vkCreateShaderModule(): A SPIR-V Capability (%s) was declared that is not supported by Vulkan.", string_SpvCapability(insn.word(1)));
392            return skip; // no known capability to validate
393        }
394
395        // Each capability has one or more requirements to check
396        // Only one item has to be satisfied and an error only occurs
397        // when all are not satisfied
398        auto caps = spirvCapabilities.equal_range(insn.word(1));
399        bool has_support = false;
400        for (auto it = caps.first; (it != caps.second) && (has_support == false); ++it) {
401            if (it->second.version) {
402                if (api_version >= it->second.version) {
403                    has_support = true;
404                }
405            } else if (it->second.feature) {
406                if (it->second.feature.IsEnabled(enabled_features)) {
407                    has_support = true;
408                }
409            } else if (it->second.extension) {
410                // kEnabledByApiLevel is not valid as some extension are promoted with feature bits to be used.
411                // If the new Api Level gives support, it will be caught in the "it->second.version" check instead.
412                if (IsExtEnabledByCreateinfo(device_extensions.*(it->second.extension))) {
413                    has_support = true;
414                }
415            } else if (it->second.property) {
416                // support is or'ed as only one has to be supported (if applicable)
417                switch (insn.word(1)) {'''
418
419        for name, infos in sorted(self.propertyInfo.items()):
420            # Only capabilities here (all items in array are the same)
421            if infos[0]['isExtension'] == True:
422                continue
423
424            # use triple-tick syntax to keep tab alignment for generated code
425            output += '''
426                    case spv::Capability{}:'''.format(name)
427            for info in infos:
428                # Need to string replace property string to create valid C++ logic
429                logic = info['logic'].replace('::', '.')
430                logic = logic.replace(info['struct'], self.propertyMap[info['struct']])
431                output += '''
432                        has_support |= ({});'''.format(logic)
433            output += '''
434                        break;'''
435
436        output += '''
437                    default:
438                        break;
439                }
440            }
441        }
442
443        if (has_support == false) {
444            skip |= LogError(device, "VUID-VkShaderModuleCreateInfo-pCode-01091",
445                "vkCreateShaderModule(): The SPIR-V Capability (%s) was declared, but none of the requirements were met to use it.", string_SpvCapability(insn.word(1)));
446        }
447
448        // Portability checks
449        if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) {
450            if ((VK_FALSE == enabled_features.portability_subset_features.shaderSampleRateInterpolationFunctions) &&
451                (spv::CapabilityInterpolationFunction == insn.word(1))) {
452                skip |= LogError(device, "VUID-RuntimeSpirv-shaderSampleRateInterpolationFunctions-06325",
453                                    "Invalid shader capability (portability error): interpolation functions are not supported "
454                                    "by this platform");
455            }
456        }
457    } else if (insn.opcode() == spv::OpExtension) {
458        static const std::string spv_prefix = "SPV_";
459        std::string extension_name = (char const *)&insn.word(1);
460
461        if (0 == extension_name.compare(0, spv_prefix.size(), spv_prefix)) {
462            if (spirvExtensions.count(extension_name) == 0) {
463                skip |= LogError(device, "VUID-VkShaderModuleCreateInfo-pCode-04146",
464                    "vkCreateShaderModule(): A SPIR-V Extension (%s) was declared that is not supported by Vulkan.", extension_name.c_str());
465                return skip; // no known extension to validate
466            }
467        } else {
468            skip |= LogError(device, kVUID_Core_Shader_InvalidExtension,
469                "vkCreateShaderModule(): The SPIR-V code uses the '%s' extension which is not a SPIR-V extension. Please use a SPIR-V"
470                " extension (https://github.com/KhronosGroup/SPIRV-Registry) for OpExtension instructions. Non-SPIR-V extensions can be"
471                " recorded in SPIR-V using the OpSourceExtension instruction.", extension_name.c_str());
472            return skip; // no known extension to validate
473        }
474
475        // Each SPIR-V Extension has one or more requirements to check
476        // Only one item has to be satisfied and an error only occurs
477        // when all are not satisfied
478        auto ext = spirvExtensions.equal_range(extension_name);
479        bool has_support = false;
480        for (auto it = ext.first; (it != ext.second) && (has_support == false); ++it) {
481            if (it->second.version) {
482                if (api_version >= it->second.version) {
483                    has_support = true;
484                }
485            } else if (it->second.feature) {
486                if (it->second.feature.IsEnabled(enabled_features)) {
487                    has_support = true;
488                }
489            } else if (it->second.extension) {
490                if (IsExtEnabled(device_extensions.*(it->second.extension))) {
491                    has_support = true;
492                }
493            } else if (it->second.property) {
494                // support is or'ed as only one has to be supported (if applicable)
495                switch (insn.word(1)) {'''
496
497        for name, infos in sorted(self.propertyInfo.items()):
498            # Only extensions here (all items in array are the same)
499            if infos[0]['isExtension'] == False:
500                continue
501
502            # use triple-tick syntax to keep tab alignment for generated code
503            output += '''
504                    case spv::Capability{}:'''.format(name)
505            for info in infos:
506                # Need to string replace property string to create valid C++ logic
507                logic = info['logic'].replace('::', '.')
508                logic = logic.replace(info['struct'], self.propertyMap[info['struct']])
509                output += '''
510                    has_support |= ({});'''.format(logic)
511            output += '''
512                    break;'''
513
514        output += '''
515                    default:
516                        break;
517                }
518            }
519        }
520
521        if (has_support == false) {
522            skip |= LogError(device, "VUID-VkShaderModuleCreateInfo-pCode-04147",
523                "vkCreateShaderModule(): The SPIR-V Extension (%s) was declared, but none of the requirements were met to use it.", extension_name.c_str());
524        }
525    } //spv::OpExtension
526    return skip;
527}'''
528        return output
529