Home | History | Annotate | Download | only in vulkan-validation-layers
      1 #!/usr/bin/python3 -i
      2 #
      3 # Copyright (c) 2015-2016 The Khronos Group Inc.
      4 # Copyright (c) 2015-2016 Valve Corporation
      5 # Copyright (c) 2015-2016 LunarG, Inc.
      6 # Copyright (c) 2015-2016 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: Mike Stroyan <stroyan (at] google.com>
     21 
     22 import os,re,sys
     23 from generator import *
     24 
     25 # ThreadGeneratorOptions - subclass of GeneratorOptions.
     26 #
     27 # Adds options used by ThreadOutputGenerator objects during threading
     28 # layer generation.
     29 #
     30 # Additional members
     31 #   prefixText - list of strings to prefix generated header with
     32 #     (usually a copyright statement + calling convention macros).
     33 #   protectFile - True if multiple inclusion protection should be
     34 #     generated (based on the filename) around the entire header.
     35 #   protectFeature - True if #ifndef..#endif protection should be
     36 #     generated around a feature interface in the header file.
     37 #   genFuncPointers - True if function pointer typedefs should be
     38 #     generated
     39 #   protectProto - If conditional protection should be generated
     40 #     around prototype declarations, set to either '#ifdef'
     41 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
     42 #     to require opt-out (#ifndef protectProtoStr). Otherwise
     43 #     set to None.
     44 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
     45 #     declarations, if protectProto is set
     46 #   apicall - string to use for the function declaration prefix,
     47 #     such as APICALL on Windows.
     48 #   apientry - string to use for the calling convention macro,
     49 #     in typedefs, such as APIENTRY.
     50 #   apientryp - string to use for the calling convention macro
     51 #     in function pointer typedefs, such as APIENTRYP.
     52 #   indentFuncProto - True if prototype declarations should put each
     53 #     parameter on a separate line
     54 #   indentFuncPointer - True if typedefed function pointers should put each
     55 #     parameter on a separate line
     56 #   alignFuncParam - if nonzero and parameters are being put on a
     57 #     separate line, align parameter names at the specified column
     58 class ThreadGeneratorOptions(GeneratorOptions):
     59     def __init__(self,
     60                  filename = None,
     61                  directory = '.',
     62                  apiname = None,
     63                  profile = None,
     64                  versions = '.*',
     65                  emitversions = '.*',
     66                  defaultExtensions = None,
     67                  addExtensions = None,
     68                  removeExtensions = None,
     69                  sortProcedure = regSortFeatures,
     70                  prefixText = "",
     71                  genFuncPointers = True,
     72                  protectFile = True,
     73                  protectFeature = True,
     74                  protectProto = None,
     75                  protectProtoStr = None,
     76                  apicall = '',
     77                  apientry = '',
     78                  apientryp = '',
     79                  indentFuncProto = True,
     80                  indentFuncPointer = False,
     81                  alignFuncParam = 0):
     82         GeneratorOptions.__init__(self, filename, directory, apiname, profile,
     83                                   versions, emitversions, defaultExtensions,
     84                                   addExtensions, removeExtensions, sortProcedure)
     85         self.prefixText      = prefixText
     86         self.genFuncPointers = genFuncPointers
     87         self.protectFile     = protectFile
     88         self.protectFeature  = protectFeature
     89         self.protectProto    = protectProto
     90         self.protectProtoStr = protectProtoStr
     91         self.apicall         = apicall
     92         self.apientry        = apientry
     93         self.apientryp       = apientryp
     94         self.indentFuncProto = indentFuncProto
     95         self.indentFuncPointer = indentFuncPointer
     96         self.alignFuncParam  = alignFuncParam
     97 
     98 # ThreadOutputGenerator - subclass of OutputGenerator.
     99 # Generates Thread checking framework
    100 #
    101 # ---- methods ----
    102 # ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
    103 #   OutputGenerator. Defines additional internal state.
    104 # ---- methods overriding base class ----
    105 # beginFile(genOpts)
    106 # endFile()
    107 # beginFeature(interface, emit)
    108 # endFeature()
    109 # genType(typeinfo,name)
    110 # genStruct(typeinfo,name)
    111 # genGroup(groupinfo,name)
    112 # genEnum(enuminfo, name)
    113 # genCmd(cmdinfo)
    114 class ThreadOutputGenerator(OutputGenerator):
    115     """Generate specified API interfaces in a specific style, such as a C header"""
    116     # This is an ordered list of sections in the header file.
    117     TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
    118                      'group', 'bitmask', 'funcpointer', 'struct']
    119     ALL_SECTIONS = TYPE_SECTIONS + ['command']
    120     def __init__(self,
    121                  errFile = sys.stderr,
    122                  warnFile = sys.stderr,
    123                  diagFile = sys.stdout):
    124         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    125         # Internal state - accumulators for different inner block text
    126         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    127         self.intercepts = []
    128 
    129     # Check if the parameter passed in is a pointer to an array
    130     def paramIsArray(self, param):
    131         return param.attrib.get('len') is not None
    132 
    133     # Check if the parameter passed in is a pointer
    134     def paramIsPointer(self, param):
    135         ispointer = False
    136         for elem in param:
    137             #write('paramIsPointer '+elem.text, file=sys.stderr)
    138             #write('elem.tag '+elem.tag, file=sys.stderr)
    139             #if (elem.tail is None):
    140             #    write('elem.tail is None', file=sys.stderr)
    141             #else:
    142             #    write('elem.tail '+elem.tail, file=sys.stderr)
    143             if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
    144                 ispointer = True
    145             #    write('is pointer', file=sys.stderr)
    146         return ispointer
    147     def makeThreadUseBlock(self, cmd, functionprefix):
    148         """Generate C function pointer typedef for <command> Element"""
    149         paramdecl = ''
    150         thread_check_dispatchable_objects = [
    151             "VkCommandBuffer",
    152             "VkDevice",
    153             "VkInstance",
    154             "VkQueue",
    155         ]
    156         thread_check_nondispatchable_objects = [
    157             "VkBuffer",
    158             "VkBufferView",
    159             "VkCommandPool",
    160             "VkDescriptorPool",
    161             "VkDescriptorSetLayout",
    162             "VkDeviceMemory",
    163             "VkEvent",
    164             "VkFence",
    165             "VkFramebuffer",
    166             "VkImage",
    167             "VkImageView",
    168             "VkPipeline",
    169             "VkPipelineCache",
    170             "VkPipelineLayout",
    171             "VkQueryPool",
    172             "VkRenderPass",
    173             "VkSampler",
    174             "VkSemaphore",
    175             "VkShaderModule",
    176         ]
    177 
    178         # Find and add any parameters that are thread unsafe
    179         params = cmd.findall('param')
    180         for param in params:
    181             paramname = param.find('name')
    182             if False: # self.paramIsPointer(param):
    183                 paramdecl += '    // not watching use of pointer ' + paramname.text + '\n'
    184             else:
    185                 externsync = param.attrib.get('externsync')
    186                 if externsync == 'true':
    187                     if self.paramIsArray(param):
    188                         paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
    189                         paramdecl += '        ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
    190                         paramdecl += '    }\n'
    191                     else:
    192                         paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
    193                 elif (param.attrib.get('externsync')):
    194                     if self.paramIsArray(param):
    195                         # Externsync can list pointers to arrays of members to synchronize
    196                         paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
    197                         for member in externsync.split(","):
    198                             # Replace first empty [] in member name with index
    199                             element = member.replace('[]','[index]',1)
    200                             if '[]' in element:
    201                                 # Replace any second empty [] in element name with
    202                                 # inner array index based on mapping array names like
    203                                 # "pSomeThings[]" to "someThingCount" array size.
    204                                 # This could be more robust by mapping a param member
    205                                 # name to a struct type and "len" attribute.
    206                                 limit = element[0:element.find('s[]')] + 'Count'
    207                                 dotp = limit.rfind('.p')
    208                                 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
    209                                 paramdecl += '        for(uint32_t index2=0;index2<'+limit+';index2++)\n'
    210                                 element = element.replace('[]','[index2]')
    211                             paramdecl += '            ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
    212                         paramdecl += '    }\n'
    213                     else:
    214                         # externsync can list members to synchronize
    215                         for member in externsync.split(","):
    216                             member = str(member).replace("::", "->")
    217                             paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
    218                 else:
    219                     paramtype = param.find('type')
    220                     if paramtype is not None:
    221                         paramtype = paramtype.text
    222                     else:
    223                         paramtype = 'None'
    224                     if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
    225                         if self.paramIsArray(param) and ('pPipelines' != paramname.text):
    226                             paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
    227                             paramdecl += '        ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
    228                             paramdecl += '    }\n'
    229                         elif not self.paramIsPointer(param):
    230                             # Pointer params are often being created.
    231                             # They are not being read from.
    232                             paramdecl += '    ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
    233         explicitexternsyncparams = cmd.findall("param[@externsync]")
    234         if (explicitexternsyncparams is not None):
    235             for param in explicitexternsyncparams:
    236                 externsyncattrib = param.attrib.get('externsync')
    237                 paramname = param.find('name')
    238                 paramdecl += '    // Host access to '
    239                 if externsyncattrib == 'true':
    240                     if self.paramIsArray(param):
    241                         paramdecl += 'each member of ' + paramname.text
    242                     elif self.paramIsPointer(param):
    243                         paramdecl += 'the object referenced by ' + paramname.text
    244                     else:
    245                         paramdecl += paramname.text
    246                 else:
    247                     paramdecl += externsyncattrib
    248                 paramdecl += ' must be externally synchronized\n'
    249 
    250         # Find and add any "implicit" parameters that are thread unsafe
    251         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
    252         if (implicitexternsyncparams is not None):
    253             for elem in implicitexternsyncparams:
    254                 paramdecl += '    // '
    255                 paramdecl += elem.text
    256                 paramdecl += ' must be externally synchronized between host accesses\n'
    257 
    258         if (paramdecl == ''):
    259             return None
    260         else:
    261             return paramdecl
    262     def beginFile(self, genOpts):
    263         OutputGenerator.beginFile(self, genOpts)
    264         # C-specific
    265         #
    266         # Multiple inclusion protection & C++ namespace.
    267         if (genOpts.protectFile and self.genOpts.filename):
    268             headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
    269             write('#ifndef', headerSym, file=self.outFile)
    270             write('#define', headerSym, '1', file=self.outFile)
    271             self.newline()
    272         write('namespace threading {', file=self.outFile)
    273         self.newline()
    274         #
    275         # User-supplied prefix text, if any (list of strings)
    276         if (genOpts.prefixText):
    277             for s in genOpts.prefixText:
    278                 write(s, file=self.outFile)
    279     def endFile(self):
    280         # C-specific
    281         # Finish C++ namespace and multiple inclusion protection
    282         self.newline()
    283         # record intercepted procedures
    284         write('// intercepts', file=self.outFile)
    285         write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
    286         write('\n'.join(self.intercepts), file=self.outFile)
    287         write('};\n', file=self.outFile)
    288         self.newline()
    289         write('} // namespace threading', file=self.outFile)
    290         if (self.genOpts.protectFile and self.genOpts.filename):
    291             self.newline()
    292             write('#endif', file=self.outFile)
    293         # Finish processing in superclass
    294         OutputGenerator.endFile(self)
    295     def beginFeature(self, interface, emit):
    296         #write('// starting beginFeature', file=self.outFile)
    297         # Start processing in superclass
    298         OutputGenerator.beginFeature(self, interface, emit)
    299         # C-specific
    300         # Accumulate includes, defines, types, enums, function pointer typedefs,
    301         # end function prototypes separately for this feature. They're only
    302         # printed in endFeature().
    303         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    304         #write('// ending beginFeature', file=self.outFile)
    305     def endFeature(self):
    306         # C-specific
    307         # Actually write the interface to the output file.
    308         #write('// starting endFeature', file=self.outFile)
    309         if (self.emit):
    310             self.newline()
    311             if (self.genOpts.protectFeature):
    312                 write('#ifndef', self.featureName, file=self.outFile)
    313             # If type declarations are needed by other features based on
    314             # this one, it may be necessary to suppress the ExtraProtect,
    315             # or move it below the 'for section...' loop.
    316             #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
    317             if (self.featureExtraProtect != None):
    318                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
    319             #write('#define', self.featureName, '1', file=self.outFile)
    320             for section in self.TYPE_SECTIONS:
    321                 #write('// endFeature writing section'+section, file=self.outFile)
    322                 contents = self.sections[section]
    323                 if contents:
    324                     write('\n'.join(contents), file=self.outFile)
    325                     self.newline()
    326             #write('// endFeature looking at self.sections[command]', file=self.outFile)
    327             if (self.sections['command']):
    328                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
    329                 self.newline()
    330             if (self.featureExtraProtect != None):
    331                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
    332             if (self.genOpts.protectFeature):
    333                 write('#endif /*', self.featureName, '*/', file=self.outFile)
    334         # Finish processing in superclass
    335         OutputGenerator.endFeature(self)
    336         #write('// ending endFeature', file=self.outFile)
    337     #
    338     # Append a definition to the specified section
    339     def appendSection(self, section, text):
    340         # self.sections[section].append('SECTION: ' + section + '\n')
    341         self.sections[section].append(text)
    342     #
    343     # Type generation
    344     def genType(self, typeinfo, name):
    345         pass
    346     #
    347     # Struct (e.g. C "struct" type) generation.
    348     # This is a special case of the <type> tag where the contents are
    349     # interpreted as a set of <member> tags instead of freeform C
    350     # C type declarations. The <member> tags are just like <param>
    351     # tags - they are a declaration of a struct or union member.
    352     # Only simple member declarations are supported (no nested
    353     # structs etc.)
    354     def genStruct(self, typeinfo, typeName):
    355         OutputGenerator.genStruct(self, typeinfo, typeName)
    356         body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
    357         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
    358         for member in typeinfo.elem.findall('.//member'):
    359             body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
    360             body += ';\n'
    361         body += '} ' + typeName + ';\n'
    362         self.appendSection('struct', body)
    363     #
    364     # Group (e.g. C "enum" type) generation.
    365     # These are concatenated together with other types.
    366     def genGroup(self, groupinfo, groupName):
    367         pass
    368     # Enumerant generation
    369     # <enum> tags may specify their values in several ways, but are usually
    370     # just integers.
    371     def genEnum(self, enuminfo, name):
    372         pass
    373     #
    374     # Command generation
    375     def genCmd(self, cmdinfo, name):
    376         # Commands shadowed by interface functions and are not implemented
    377         interface_functions = [
    378             'vkEnumerateInstanceLayerProperties',
    379             'vkEnumerateInstanceExtensionProperties',
    380             'vkEnumerateDeviceLayerProperties',
    381         ]
    382         if name in interface_functions:
    383             return
    384         special_functions = [
    385             'vkGetDeviceProcAddr',
    386             'vkGetInstanceProcAddr',
    387             'vkCreateDevice',
    388             'vkDestroyDevice',
    389             'vkCreateInstance',
    390             'vkDestroyInstance',
    391             'vkAllocateCommandBuffers',
    392             'vkFreeCommandBuffers',
    393             'vkCreateDebugReportCallbackEXT',
    394             'vkDestroyDebugReportCallbackEXT',
    395         ]
    396         if name in special_functions:
    397             decls = self.makeCDecls(cmdinfo.elem)
    398             self.appendSection('command', '')
    399             self.appendSection('command', '// declare only')
    400             self.appendSection('command', decls[0])
    401             self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
    402             return
    403         if "KHR" in name:
    404             self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
    405             return
    406         if ("DebugMarker" in name) and ("EXT" in name):
    407             self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
    408             return
    409         # Determine first if this function needs to be intercepted
    410         startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
    411         if startthreadsafety is None:
    412             return
    413         finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
    414         # record that the function will be intercepted
    415         if (self.featureExtraProtect != None):
    416             self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
    417         self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
    418         if (self.featureExtraProtect != None):
    419             self.intercepts += [ '#endif' ]
    420 
    421         OutputGenerator.genCmd(self, cmdinfo, name)
    422         #
    423         decls = self.makeCDecls(cmdinfo.elem)
    424         self.appendSection('command', '')
    425         self.appendSection('command', decls[0][:-1])
    426         self.appendSection('command', '{')
    427         # setup common to call wrappers
    428         # first parameter is always dispatchable
    429         dispatchable_type = cmdinfo.elem.find('param/type').text
    430         dispatchable_name = cmdinfo.elem.find('param/name').text
    431         self.appendSection('command', '    dispatch_key key = get_dispatch_key('+dispatchable_name+');')
    432         self.appendSection('command', '    layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
    433         if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
    434             self.appendSection('command', '    VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
    435         else:
    436             self.appendSection('command', '    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
    437         # Declare result variable, if any.
    438         resulttype = cmdinfo.elem.find('proto/type')
    439         if (resulttype != None and resulttype.text == 'void'):
    440           resulttype = None
    441         if (resulttype != None):
    442             self.appendSection('command', '    ' + resulttype.text + ' result;')
    443             assignresult = 'result = '
    444         else:
    445             assignresult = ''
    446 
    447         self.appendSection('command', '    bool threadChecks = startMultiThread();')
    448         self.appendSection('command', '    if (threadChecks) {')
    449         self.appendSection('command', "    "+"\n    ".join(str(startthreadsafety).rstrip().split("\n")))
    450         self.appendSection('command', '    }')
    451         params = cmdinfo.elem.findall('param/name')
    452         paramstext = ','.join([str(param.text) for param in params])
    453         API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
    454         self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');')
    455         self.appendSection('command', '    if (threadChecks) {')
    456         self.appendSection('command', "    "+"\n    ".join(str(finishthreadsafety).rstrip().split("\n")))
    457         self.appendSection('command', '    } else {')
    458         self.appendSection('command', '        finishMultiThread();')
    459         self.appendSection('command', '    }')
    460         # Return result variable, if any.
    461         if (resulttype != None):
    462             self.appendSection('command', '    return result;')
    463         self.appendSection('command', '}')
    464     #
    465     # override makeProtoName to drop the "vk" prefix
    466     def makeProtoName(self, name, tail):
    467         return self.genOpts.apientry + name[2:] + tail
    468