1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2015-2020 The Khronos Group Inc.
4# Copyright (c) 2015-2020 Valve Corporation
5# Copyright (c) 2015-2020 LunarG, Inc.
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11#     http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# Author: Mark Lobodzinski <mark@lunarg.com>
20
21import os,re,sys,string,json
22import xml.etree.ElementTree as etree
23from generator import *
24from collections import namedtuple
25from common_codegen import *
26
27# This is a workaround to use a Python 2.7 and 3.x compatible syntax
28from io import open
29
30class BestPracticesOutputGeneratorOptions(GeneratorOptions):
31    def __init__(self,
32                 conventions = None,
33                 filename = None,
34                 directory = '.',
35                 genpath = None,
36                 apiname = None,
37                 profile = None,
38                 versions = '.*',
39                 emitversions = '.*',
40                 defaultExtensions = None,
41                 addExtensions = None,
42                 removeExtensions = None,
43                 emitExtensions = None,
44                 sortProcedure = regSortFeatures,
45                 prefixText = "",
46                 genFuncPointers = True,
47                 protectFile = True,
48                 protectFeature = True,
49                 apicall = '',
50                 apientry = '',
51                 apientryp = '',
52                 indentFuncProto = True,
53                 indentFuncPointer = False,
54                 alignFuncParam = 0,
55                 expandEnumerants = True):
56        GeneratorOptions.__init__(self,
57                conventions = conventions,
58                filename = filename,
59                directory = directory,
60                genpath = genpath,
61                apiname = apiname,
62                profile = profile,
63                versions = versions,
64                emitversions = emitversions,
65                defaultExtensions = defaultExtensions,
66                addExtensions = addExtensions,
67                removeExtensions = removeExtensions,
68                emitExtensions = emitExtensions,
69                sortProcedure = sortProcedure)
70        self.prefixText      = prefixText
71        self.genFuncPointers = genFuncPointers
72        self.protectFile     = protectFile
73        self.protectFeature  = protectFeature
74        self.apicall         = apicall
75        self.apientry        = apientry
76        self.apientryp       = apientryp
77        self.indentFuncProto = indentFuncProto
78        self.indentFuncPointer = indentFuncPointer
79        self.alignFuncParam  = alignFuncParam
80        self.expandEnumerants = expandEnumerants
81#
82# BestPracticesOutputGenerator(errFile, warnFile, diagFile)
83class BestPracticesOutputGenerator(OutputGenerator):
84    def __init__(self,
85                 errFile = sys.stderr,
86                 warnFile = sys.stderr,
87                 diagFile = sys.stdout):
88        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
89        # Commands which are not autogenerated but still intercepted
90        self.no_autogen_list = [
91            'vkEnumerateInstanceVersion',
92            'vkCreateValidationCacheEXT',
93            'vkDestroyValidationCacheEXT',
94            'vkMergeValidationCachesEXT',
95            'vkGetValidationCacheDataEXT',
96            ]
97        # Commands that require an extra parameter for state sharing between validate/record steps
98        self.extra_parameter_list = [
99            "vkCreateShaderModule",
100            "vkCreateGraphicsPipelines",
101            "vkCreateComputePipelines",
102            "vkAllocateDescriptorSets",
103            "vkCreateRayTracingPipelinesNV",
104            "vkCreateRayTracingPipelinesKHR",
105            ]
106        # Commands that have a manually written post-call-record step which needs to be called from the autogen'd fcn
107        self.manual_postcallrecord_list = [
108            'vkAllocateDescriptorSets',
109            'vkAllocateMemory',
110            'vkQueuePresentKHR',
111            'vkQueueBindSparse',
112            'vkCreateGraphicsPipelines',
113            'vkGetPhysicalDeviceSurfaceCapabilitiesKHR',
114            'vkGetPhysicalDeviceSurfaceCapabilities2KHR',
115            'vkGetPhysicalDeviceSurfaceCapabilities2EXT',
116            'vkGetPhysicalDeviceSurfacePresentModesKHR',
117            'vkGetPhysicalDeviceSurfaceFormatsKHR',
118            'vkGetPhysicalDeviceSurfaceFormats2KHR',
119            'vkGetPhysicalDeviceDisplayPlanePropertiesKHR',
120            'vkCreateSwapchainKHR',
121            'vkGetSwapchainImagesKHR',
122            'vkEnumeratePhysicalDevices',
123            'vkCreateDevice',
124            ]
125
126        self.extension_info = dict()
127    #
128    # Separate content for validation source and header files
129    def otwrite(self, dest, formatstring):
130        if 'best_practices.h' in self.genOpts.filename and (dest == 'hdr' or dest == 'both'):
131            write(formatstring, file=self.outFile)
132        elif 'best_practices.cpp' in self.genOpts.filename and (dest == 'cpp' or dest == 'both'):
133            write(formatstring, file=self.outFile)
134    #
135    # Called at beginning of processing as file is opened
136    def beginFile(self, genOpts):
137        OutputGenerator.beginFile(self, genOpts)
138
139        header_file = (genOpts.filename == 'best_practices.h')
140        source_file = (genOpts.filename == 'best_practices.cpp')
141
142        if not header_file and not source_file:
143            print("Error: Output Filenames have changed, update generator source.\n")
144            sys.exit(1)
145
146        # File Comment
147        file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
148        file_comment += '// See best_practices_generator.py for modifications\n'
149        self.otwrite('both', file_comment)
150        # Copyright Statement
151        copyright = ''
152        copyright += '\n'
153        copyright += '/***************************************************************************\n'
154        copyright += ' *\n'
155        copyright += ' * Copyright (c) 2015-2020 The Khronos Group Inc.\n'
156        copyright += ' * Copyright (c) 2015-2020 Valve Corporation\n'
157        copyright += ' * Copyright (c) 2015-2020 LunarG, Inc.\n'
158        copyright += ' *\n'
159        copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
160        copyright += ' * you may not use this file except in compliance with the License.\n'
161        copyright += ' * You may obtain a copy of the License at\n'
162        copyright += ' *\n'
163        copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
164        copyright += ' *\n'
165        copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
166        copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
167        copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
168        copyright += ' * See the License for the specific language governing permissions and\n'
169        copyright += ' * limitations under the License.\n'
170        copyright += ' *\n'
171        copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
172        copyright += ' *\n'
173        copyright += ' ****************************************************************************/\n'
174        self.otwrite('both', copyright)
175        self.newline()
176        self.otwrite('cpp', '#include "chassis.h"')
177        self.otwrite('cpp', '#include "best_practices_validation.h"')
178    #
179    # Now that the data is all collected and complete, generate and output the object validation routines
180    def endFile(self):
181        self.newline()
182        # Actually write the interface to the output file.
183        if (self.emit):
184            self.newline()
185            if self.featureExtraProtect is not None:
186                prot = '#ifdef %s' % self.featureExtraProtect
187                self.otwrite('both', '%s' % prot)
188            if (self.featureExtraProtect is not None):
189                prot = '\n#endif // %s', self.featureExtraProtect
190                self.otwrite('both', prot)
191            else:
192                self.otwrite('both', '\n')
193            ext_deprecation_data = 'const std::unordered_map<std::string, DeprecationData>  deprecated_extensions = {\n'
194            for ext in sorted(self.extension_info):
195                ext_data = self.extension_info[ext]
196                ext_deprecation_data += '    {"%s", {kExt%s, "%s"}},\n' % (ext, ext_data[0], ext_data[1])
197            ext_deprecation_data += '};\n'
198            self.otwrite('hdr', ext_deprecation_data)
199        OutputGenerator.endFile(self)
200    #
201    # Processing point at beginning of each extension definition
202    def beginFeature(self, interface, emit):
203        OutputGenerator.beginFeature(self, interface, emit)
204        self.featureExtraProtect = GetFeatureProtect(interface)
205        ext_name = interface.attrib.get('name')
206        ext_promoted = (interface.attrib.get('promotedto'))
207        ext_obsoleted = interface.attrib.get('obsoletedby')
208        ext_deprecated = interface.attrib.get('deprecatedby')
209        if ext_promoted is not None:
210           reason = 'Promoted'
211           target = ext_promoted
212        elif ext_obsoleted is not None:
213           reason = 'Obsoleted'
214           target = ext_obsoleted
215        elif ext_deprecated is not None:
216           reason = 'Deprecated'
217           target = ext_deprecated
218        else:
219            return
220        self.extension_info[ext_name] = [reason, target]
221    #
222    # Retrieve the type and name for a parameter
223    def getTypeNameTuple(self, param):
224        type = ''
225        name = ''
226        for elem in param:
227            if elem.tag == 'type':
228                type = noneStr(elem.text)
229            elif elem.tag == 'name':
230                name = noneStr(elem.text)
231        return (type, name)
232    #
233    # Capture command parameter info needed to create, destroy, and validate objects
234    def genCmd(self, cmdinfo, cmdname, alias):
235        OutputGenerator.genCmd(self, cmdinfo, cmdname, alias)
236        intercept = ''
237        if cmdname in self.no_autogen_list:
238            intercept += '// Skipping %s for autogen as it has a manually created custom function or ignored.\n' % cmdname
239            self.otwrite('cpp', intercept)
240            return
241        cdecl=self.makeCDecls(cmdinfo.elem)[0]
242        decls = self.makeCDecls(cmdinfo.elem)
243        typedef = decls[1]
244        typedef = typedef.split(')',1)[1]
245        pre_decl = decls[0][:-1]
246        pre_decl = pre_decl.split("VKAPI_CALL ")[1]
247        pre_decl = pre_decl.replace(')', ',\n    VkResult                                    result)')
248        if cmdname in self.extra_parameter_list:
249            pre_decl = pre_decl.replace(')', ',\n    void*                                       state_data)')
250        pre_decl = pre_decl.replace(')', ') {\n')
251        pre_decl = 'void BestPractices::PostCallRecord' + pre_decl[2:]
252        type = cdecl.split(' ')[1]
253        if type == 'VkResult':
254            error_codes = cmdinfo.elem.attrib.get('errorcodes')
255            success_codes = cmdinfo.elem.attrib.get('successcodes')
256            success_codes = success_codes.replace('VK_SUCCESS,','')
257            success_codes = success_codes.replace('VK_SUCCESS','')
258            if error_codes is None and success_codes == '':
259                return
260            if self.featureExtraProtect is not None:
261                self.otwrite('both', '#ifdef %s\n' % self.featureExtraProtect)
262            func_decl = pre_decl.replace(' {',';\n');
263            func_decl = func_decl.replace('BestPractices::', '')
264            self.otwrite('hdr', func_decl)
265            intercept += pre_decl
266            params_text = ''
267            params = cmdinfo.elem.findall('param')
268            for param in params:
269                paramtype,paramname = self.getTypeNameTuple(param)
270                params_text += '%s, ' % paramname
271            params_text = params_text + 'result, '
272            if cmdname in self.extra_parameter_list:
273                params_text += 'state_data, '
274            params_text = params_text[:-2] + ');\n'
275            intercept += '    ValidationStateTracker::PostCallRecord'+cmdname[2:] + '(' + params_text
276            if cmdname in self.manual_postcallrecord_list:
277                intercept += '    ManualPostCallRecord'+cmdname[2:] + '(' + params_text
278            intercept += '    if (result != VK_SUCCESS) {\n'
279            if error_codes is not None:
280                intercept += '        static const std::vector<VkResult> error_codes = {%s};\n' % error_codes
281            else:
282                intercept += '        static const std::vector<VkResult> error_codes = {};\n'
283            if success_codes is not None:
284                intercept += '        static const std::vector<VkResult> success_codes = {%s};\n' % success_codes
285            else:
286                intercept += '        static const std::vector<VkResult> success_codes = {};\n'
287            intercept += '        ValidateReturnCodes("%s", result, error_codes, success_codes);\n' % cmdname
288            intercept += '    }\n'
289            intercept += '}\n'
290            self.otwrite('cpp', intercept)
291            if self.featureExtraProtect is not None:
292                self.otwrite('both', '#endif // %s\n' % self.featureExtraProtect)
293