• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2015-2019 The Khronos Group Inc.
4# Copyright (c) 2015-2019 Valve Corporation
5# Copyright (c) 2015-2019 LunarG, Inc.
6# Copyright (c) 2015-2019 Google Inc.
7#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12#     http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19#
20# Author: Mark Lobodzinski <mark@lunarg.com>
21
22import os,re,sys
23import xml.etree.ElementTree as etree
24from generator import *
25from collections import namedtuple
26from common_codegen import *
27
28#
29# DispatchTableHelperOutputGeneratorOptions - subclass of GeneratorOptions.
30class DispatchTableHelperOutputGeneratorOptions(GeneratorOptions):
31    def __init__(self,
32                 conventions = None,
33                 filename = None,
34                 directory = '.',
35                 apiname = None,
36                 profile = None,
37                 versions = '.*',
38                 emitversions = '.*',
39                 defaultExtensions = None,
40                 addExtensions = None,
41                 removeExtensions = None,
42                 emitExtensions = None,
43                 sortProcedure = regSortFeatures,
44                 prefixText = "",
45                 genFuncPointers = True,
46                 apicall = '',
47                 apientry = '',
48                 apientryp = '',
49                 alignFuncParam = 0,
50                 expandEnumerants = True):
51        GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
52                                  versions, emitversions, defaultExtensions,
53                                  addExtensions, removeExtensions, emitExtensions, sortProcedure)
54        self.prefixText      = prefixText
55        self.genFuncPointers = genFuncPointers
56        self.prefixText      = None
57        self.apicall         = apicall
58        self.apientry        = apientry
59        self.apientryp       = apientryp
60        self.alignFuncParam  = alignFuncParam
61#
62# DispatchTableHelperOutputGenerator - subclass of OutputGenerator.
63# Generates dispatch table helper header files for LVL
64class DispatchTableHelperOutputGenerator(OutputGenerator):
65    """Generate dispatch table helper header based on XML element attributes"""
66    def __init__(self,
67                 errFile = sys.stderr,
68                 warnFile = sys.stderr,
69                 diagFile = sys.stdout):
70        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
71        # Internal state - accumulators for different inner block text
72        self.instance_dispatch_list = []      # List of entries for instance dispatch list
73        self.device_dispatch_list = []        # List of entries for device dispatch list
74        self.dev_ext_stub_list = []           # List of stub functions for device extension functions
75        self.device_extension_list = []       # List of device extension functions
76        self.device_stub_list = []            # List of device functions with stubs (promoted or extensions)
77        self.extension_type = ''
78    #
79    # Called once at the beginning of each run
80    def beginFile(self, genOpts):
81        OutputGenerator.beginFile(self, genOpts)
82
83        # Initialize members that require the tree
84        self.handle_types = GetHandleTypes(self.registry.tree)
85
86        write("#pragma once", file=self.outFile)
87        # User-supplied prefix text, if any (list of strings)
88        if (genOpts.prefixText):
89            for s in genOpts.prefixText:
90                write(s, file=self.outFile)
91        # File Comment
92        file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
93        file_comment += '// See dispatch_helper_generator.py for modifications\n'
94        write(file_comment, file=self.outFile)
95        # Copyright Notice
96        copyright =  '/*\n'
97        copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n'
98        copyright += ' * Copyright (c) 2015-2019 Valve Corporation\n'
99        copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n'
100        copyright += ' *\n'
101        copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
102        copyright += ' * you may not use this file except in compliance with the License.\n'
103        copyright += ' * You may obtain a copy of the License at\n'
104        copyright += ' *\n'
105        copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
106        copyright += ' *\n'
107        copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
108        copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
109        copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
110        copyright += ' * See the License for the specific language governing permissions and\n'
111        copyright += ' * limitations under the License.\n'
112        copyright += ' *\n'
113        copyright += ' * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>\n'
114        copyright += ' * Author: Jon Ashburn <jon@lunarg.com>\n'
115        copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
116        copyright += ' */\n'
117
118        preamble = ''
119        preamble += '#include <vulkan/vulkan.h>\n'
120        preamble += '#include <vulkan/vk_layer.h>\n'
121        preamble += '#include <cstring>\n'
122        preamble += '#include <string>\n'
123        preamble += '#include <unordered_set>\n'
124        preamble += '#include <unordered_map>\n'
125        preamble += '#include "vk_layer_dispatch_table.h"\n'
126        preamble += '#include "vk_extension_helper.h"\n'
127
128        write(copyright, file=self.outFile)
129        write(preamble, file=self.outFile)
130    #
131    # Write generate and write dispatch tables to output file
132    def endFile(self):
133        ext_enabled_fcn = ''
134        device_table = ''
135        instance_table = ''
136
137        ext_enabled_fcn += self.OutputExtEnabledFunction()
138        device_table += self.OutputDispatchTableHelper('device')
139        instance_table += self.OutputDispatchTableHelper('instance')
140
141        for stub in self.dev_ext_stub_list:
142            write(stub, file=self.outFile)
143        write("\n\n", file=self.outFile)
144        write(ext_enabled_fcn, file=self.outFile)
145        write("\n", file=self.outFile)
146        write(device_table, file=self.outFile);
147        write("\n", file=self.outFile)
148        write(instance_table, file=self.outFile);
149
150        # Finish processing in superclass
151        OutputGenerator.endFile(self)
152    #
153    # Processing at beginning of each feature or extension
154    def beginFeature(self, interface, emit):
155        OutputGenerator.beginFeature(self, interface, emit)
156        self.featureExtraProtect = GetFeatureProtect(interface)
157        self.extension_type = interface.get('type')
158
159    #
160    # Process commands, adding to appropriate dispatch tables
161    def genCmd(self, cmdinfo, name, alias):
162        OutputGenerator.genCmd(self, cmdinfo, name, alias)
163
164        avoid_entries = ['vkCreateInstance',
165                         'vkCreateDevice']
166        # Get first param type
167        params = cmdinfo.elem.findall('param')
168        info = self.getTypeNameTuple(params[0])
169
170        if name not in avoid_entries:
171            self.AddCommandToDispatchList(name, info[0], self.featureExtraProtect, cmdinfo)
172
173    #
174    # Determine if this API should be ignored or added to the instance or device dispatch table
175    def AddCommandToDispatchList(self, name, handle_type, protect, cmdinfo):
176        if handle_type not in self.handle_types:
177            return
178        if handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice' and name != 'vkGetInstanceProcAddr':
179            self.device_dispatch_list.append((name, self.featureExtraProtect))
180            extension = "VK_VERSION" not in self.featureName
181            promoted = not extension and "VK_VERSION_1_0" != self.featureName
182            if promoted or extension:
183                # We want feature written for all promoted entrypoints, in addition to extensions
184                self.device_stub_list.append([name, self.featureName])
185                self.device_extension_list.append([name, self.featureName])
186                # Build up stub function
187                return_type = ''
188                decl = self.makeCDecls(cmdinfo.elem)[1]
189                if decl.startswith('typedef VkResult'):
190                    return_type = 'return VK_SUCCESS;'
191                elif decl.startswith('typedef VkDeviceAddress'):
192                    return_type = 'return 0;'
193                elif decl.startswith('typedef uint32_t'):
194                    return_type = 'return 0;'
195                pre_decl, decl = decl.split('*PFN_vk')
196                pre_decl = pre_decl.replace('typedef ', '')
197                pre_decl = pre_decl.split(' (')[0]
198                decl = decl.replace(')(', '(')
199                decl = 'static VKAPI_ATTR ' + pre_decl + ' VKAPI_CALL Stub' + decl
200                func_body = ' { ' + return_type + ' };'
201                decl = decl.replace (';', func_body)
202                if self.featureExtraProtect is not None:
203                    self.dev_ext_stub_list.append('#ifdef %s' % self.featureExtraProtect)
204                self.dev_ext_stub_list.append(decl)
205                if self.featureExtraProtect is not None:
206                    self.dev_ext_stub_list.append('#endif // %s' % self.featureExtraProtect)
207        else:
208            self.instance_dispatch_list.append((name, self.featureExtraProtect))
209        return
210    #
211    # Retrieve the type and name for a parameter
212    def getTypeNameTuple(self, param):
213        type = ''
214        name = ''
215        for elem in param:
216            if elem.tag == 'type':
217                type = noneStr(elem.text)
218            elif elem.tag == 'name':
219                name = noneStr(elem.text)
220        return (type, name)
221    #
222    # Output a function that'll determine if an extension is in the enabled list
223    def OutputExtEnabledFunction(self):
224        ##extension_functions = dict(self.device_dispatch_list)
225        ext_fcn  = ''
226        # First, write out our static data structure -- map of all APIs that are part of extensions to their extension.
227        ext_fcn += 'const std::unordered_map<std::string, std::string> api_extension_map {\n'
228        for extn in self.device_extension_list:
229            ext_fcn += '    {"%s", "%s"},\n' % (extn[0], extn[1])
230        ext_fcn += '};\n\n'
231        ext_fcn += '// Using the above code-generated map of APINames-to-parent extension names, this function will:\n'
232        ext_fcn += '//   o  Determine if the API has an associated extension\n'
233        ext_fcn += '//   o  If it does, determine if that extension name is present in the passed-in set of enabled_ext_names \n'
234        ext_fcn += '//   If the APIname has no parent extension, OR its parent extension name is IN the set, return TRUE, else FALSE\n'
235        ext_fcn += 'static inline bool ApiParentExtensionEnabled(const std::string api_name, const DeviceExtensions *device_extension_info) {\n'
236        ext_fcn += '    auto has_ext = api_extension_map.find(api_name);\n'
237        ext_fcn += '    // Is this API part of an extension or feature group?\n'
238        ext_fcn += '    if (has_ext != api_extension_map.end()) {\n'
239        ext_fcn += '        // Was the extension for this API enabled in the CreateDevice call?\n'
240        ext_fcn += '        auto info = device_extension_info->get_info(has_ext->second.c_str());\n'
241        ext_fcn += '        if ((!info.state) || (device_extension_info->*(info.state) != true)) {\n'
242        ext_fcn += '            return false;\n'
243        ext_fcn += '        }\n'
244        ext_fcn += '    }\n'
245        ext_fcn += '    return true;\n'
246        ext_fcn += '}\n'
247        return ext_fcn
248    #
249    # Create a dispatch table from the appropriate list and return it as a string
250    def OutputDispatchTableHelper(self, table_type):
251        entries = []
252        table = ''
253        if table_type == 'device':
254            entries = self.device_dispatch_list
255            table += 'static inline void layer_init_device_dispatch_table(VkDevice device, VkLayerDispatchTable *table, PFN_vkGetDeviceProcAddr gpa) {\n'
256            table += '    memset(table, 0, sizeof(*table));\n'
257            table += '    // Device function pointers\n'
258        else:
259            entries = self.instance_dispatch_list
260            table += 'static inline void layer_init_instance_dispatch_table(VkInstance instance, VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa) {\n'
261            table += '    memset(table, 0, sizeof(*table));\n'
262            table += '    // Instance function pointers\n'
263
264        stubbed_functions = dict(self.device_stub_list)
265        for item in entries:
266            # Remove 'vk' from proto name
267            base_name = item[0][2:]
268
269            if item[1] is not None:
270                table += '#ifdef %s\n' % item[1]
271
272            # If we're looking for the proc we are passing in, just point the table to it.  This fixes the issue where
273            # a layer overrides the function name for the loader.
274            if ('device' in table_type and base_name == 'GetDeviceProcAddr'):
275                table += '    table->GetDeviceProcAddr = gpa;\n'
276            elif ('device' not in table_type and base_name == 'GetInstanceProcAddr'):
277                table += '    table->GetInstanceProcAddr = gpa;\n'
278            else:
279                table += '    table->%s = (PFN_%s) gpa(%s, "%s");\n' % (base_name, item[0], table_type, item[0])
280                if 'device' in table_type and item[0] in stubbed_functions:
281                    stub_check = '    if (table->%s == nullptr) { table->%s = (PFN_%s)Stub%s; }\n' % (base_name, base_name, item[0], base_name)
282                    table += stub_check
283            if item[1] is not None:
284                table += '#endif // %s\n' % item[1]
285
286        table += '}'
287        return table
288