1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2015-2017 The Khronos Group Inc.
4# Copyright (c) 2015-2017 Valve Corporation
5# Copyright (c) 2015-2017 LunarG, Inc.
6# Copyright (c) 2015-2017 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
26
27#
28# DispatchTableHelperOutputGeneratorOptions - subclass of GeneratorOptions.
29class DispatchTableHelperOutputGeneratorOptions(GeneratorOptions):
30    def __init__(self,
31                 filename = None,
32                 directory = '.',
33                 apiname = None,
34                 profile = None,
35                 versions = '.*',
36                 emitversions = '.*',
37                 defaultExtensions = None,
38                 addExtensions = None,
39                 removeExtensions = None,
40                 sortProcedure = regSortFeatures,
41                 prefixText = "",
42                 genFuncPointers = True,
43                 protectFile = True,
44                 protectFeature = True,
45                 protectProto = None,
46                 protectProtoStr = None,
47                 apicall = '',
48                 apientry = '',
49                 apientryp = '',
50                 alignFuncParam = 0):
51        GeneratorOptions.__init__(self, filename, directory, apiname, profile,
52                                  versions, emitversions, defaultExtensions,
53                                  addExtensions, removeExtensions, sortProcedure)
54        self.prefixText      = prefixText
55        self.genFuncPointers = genFuncPointers
56        self.prefixText      = None
57        self.protectFile     = protectFile
58        self.protectFeature  = protectFeature
59        self.protectProto    = protectProto
60        self.protectProtoStr = protectProtoStr
61        self.apicall         = apicall
62        self.apientry        = apientry
63        self.apientryp       = apientryp
64        self.alignFuncParam  = alignFuncParam
65#
66# DispatchTableHelperOutputGenerator - subclass of OutputGenerator.
67# Generates dispatch table helper header files for LVL
68class DispatchTableHelperOutputGenerator(OutputGenerator):
69    """Generate dispatch table helper header based on XML element attributes"""
70    def __init__(self,
71                 errFile = sys.stderr,
72                 warnFile = sys.stderr,
73                 diagFile = sys.stdout):
74        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
75        # Internal state - accumulators for different inner block text
76        self.instance_dispatch_list = []      # List of entries for instance dispatch list
77        self.device_dispatch_list = []        # List of entries for device dispatch list
78    #
79    # Called once at the beginning of each run
80    def beginFile(self, genOpts):
81        OutputGenerator.beginFile(self, genOpts)
82        # Protect against multiple inclusions
83        self.protect_header = False
84        if (genOpts.protectFile and genOpts.filename):
85            self.protect_header = True
86            headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(genOpts.filename))
87            write('#ifndef', headerSym, file=self.outFile)
88            write('#define', headerSym, '1', file=self.outFile)
89            self.newline()
90        # User-supplied prefix text, if any (list of strings)
91        if (genOpts.prefixText):
92            for s in genOpts.prefixText:
93                write(s, file=self.outFile)
94        # File Comment
95        file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
96        file_comment += '// See dispatch_helper_generator.py for modifications\n'
97        write(file_comment, file=self.outFile)
98        # Copyright Notice
99        copyright =  '/*\n'
100        copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
101        copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
102        copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
103        copyright += ' *\n'
104        copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
105        copyright += ' * you may not use this file except in compliance with the License.\n'
106        copyright += ' * You may obtain a copy of the License at\n'
107        copyright += ' *\n'
108        copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
109        copyright += ' *\n'
110        copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
111        copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
112        copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
113        copyright += ' * See the License for the specific language governing permissions and\n'
114        copyright += ' * limitations under the License.\n'
115        copyright += ' *\n'
116        copyright += ' * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>\n'
117        copyright += ' * Author: Jon Ashburn <jon@lunarg.com>\n'
118        copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
119        copyright += ' */\n'
120
121        preamble = ''
122        preamble += '#include <vulkan/vulkan.h>\n'
123        preamble += '#include <vulkan/vk_layer.h>\n'
124        preamble += '#include <string.h>\n'
125
126        write(copyright, file=self.outFile)
127        write(preamble, file=self.outFile)
128    #
129    # Write generate and write dispatch tables to output file
130    def endFile(self):
131        device_table = ''
132        instance_table = ''
133
134        device_table += self.OutputDispatchTableHelper('device')
135        instance_table += self.OutputDispatchTableHelper('instance')
136
137        write(device_table, file=self.outFile);
138        write("\n", file=self.outFile)
139        write(instance_table, file=self.outFile);
140
141        if self.protect_header:
142            self.newline()
143            write('#endif', file=self.outFile)
144        # Finish processing in superclass
145        OutputGenerator.endFile(self)
146    #
147    # Process commands, adding to appropriate dispatch tables
148    def genCmd(self, cmdinfo, name):
149        OutputGenerator.genCmd(self, cmdinfo, name)
150
151        avoid_entries = ['vkCreateInstance',
152                         'vkCreateDevice']
153        # Get first param type
154        params = cmdinfo.elem.findall('param')
155        info = self.getTypeNameTuple(params[0])
156
157        if name not in avoid_entries:
158            self.AddCommandToDispatchList(name, info[0], self.featureExtraProtect)
159
160    #
161    # Determine if this API should be ignored or added to the instance or device dispatch table
162    def AddCommandToDispatchList(self, name, handle_type, protect):
163        handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
164        if handle == None:
165            return
166        if handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice' and name != 'vkGetInstanceProcAddr':
167            self.device_dispatch_list.append((name, self.featureExtraProtect))
168        else:
169            self.instance_dispatch_list.append((name, self.featureExtraProtect))
170        return
171    #
172    # Retrieve the type and name for a parameter
173    def getTypeNameTuple(self, param):
174        type = ''
175        name = ''
176        for elem in param:
177            if elem.tag == 'type':
178                type = noneStr(elem.text)
179            elif elem.tag == 'name':
180                name = noneStr(elem.text)
181        return (type, name)
182    #
183    # Create a dispatch table from the appropriate list and return it as a string
184    def OutputDispatchTableHelper(self, table_type):
185        entries = []
186        table = ''
187        if table_type == 'device':
188            entries = self.device_dispatch_list
189            table += 'static inline void layer_init_device_dispatch_table(VkDevice device, VkLayerDispatchTable *table, PFN_vkGetDeviceProcAddr gpa) {\n'
190            table += '    memset(table, 0, sizeof(*table));\n'
191            table += '    // Device function pointers\n'
192        else:
193            entries = self.instance_dispatch_list
194            table += 'static inline void layer_init_instance_dispatch_table(VkInstance instance, VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa) {\n'
195            table += '    memset(table, 0, sizeof(*table));\n'
196            table += '    // Instance function pointers\n'
197
198        for item in entries:
199            # Remove 'vk' from proto name
200            base_name = item[0][2:]
201
202            if item[1] is not None:
203                table += '#ifdef %s\n' % item[1]
204
205            # If we're looking for the proc we are passing in, just point the table to it.  This fixes the issue where
206            # a layer overrides the function name for the loader.
207            if (table_type == 'device' and base_name == 'GetDeviceProcAddr'):
208                table += '    table->GetDeviceProcAddr = gpa;\n'
209            elif (table_type != 'device' and base_name == 'GetInstanceProcAddr'):
210                table += '    table->GetInstanceProcAddr = gpa;\n'
211            else:
212                table += '    table->%s = (PFN_%s) gpa(%s, "%s");\n' % (base_name, item[0], table_type, item[0])
213
214            if item[1] is not None:
215                table += '#endif // %s\n' % item[1]
216        table += '}'
217        return table
218