1#!/usr/bin/python3 -i 2# 3# Copyright (c) 2013-2020 The Khronos Group Inc. 4# 5# SPDX-License-Identifier: Apache-2.0 6 7from generator import OutputGenerator, write 8from spec_tools.attributes import ExternSyncEntry 9from spec_tools.validity import ValidityCollection, ValidityEntry 10from spec_tools.util import getElemName 11 12 13class HostSynchronizationOutputGenerator(OutputGenerator): 14 """HostSynchronizationOutputGenerator - subclass of OutputGenerator. 15 Generates AsciiDoc includes of the externsync parameter table for the 16 fundamentals chapter of the API specification. Similar to 17 DocOutputGenerator. 18 19 ---- methods ---- 20 HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for 21 OutputGenerator. Defines additional internal state. 22 ---- methods overriding base class ---- 23 genCmd(cmdinfo)""" 24 # Generate Host Synchronized Parameters in a table at the top of the spec 25 26 threadsafety = { 27 'parameters': ValidityCollection(), 28 'parameterlists': ValidityCollection(), 29 'implicit': ValidityCollection() 30 } 31 32 def makeParameterName(self, name): 33 return 'pname:' + name 34 35 def makeFLink(self, name): 36 return 'flink:' + name 37 38 def writeBlock(self, basename, title, contents): 39 """Generate an include file. 40 41 - directory - subdirectory to put file in 42 - basename - base name of the file 43 - contents - contents of the file (Asciidoc boilerplate aside)""" 44 filename = self.genOpts.directory + '/' + basename 45 self.logMsg('diag', '# Generating include file:', filename) 46 with open(filename, 'w', encoding='utf-8') as fp: 47 write(self.genOpts.conventions.warning_comment, file=fp) 48 49 if contents: 50 write('.%s' % title, file=fp) 51 write('****', file=fp) 52 write(contents, file=fp, end='') 53 write('****', file=fp) 54 write('', file=fp) 55 else: 56 self.logMsg('diag', '# No contents for:', filename) 57 58 def writeInclude(self): 59 "Generates the asciidoc include files.""" 60 self.writeBlock('parameters.txt', 61 'Externally Synchronized Parameters', 62 self.threadsafety['parameters']) 63 self.writeBlock('parameterlists.txt', 64 'Externally Synchronized Parameter Lists', 65 self.threadsafety['parameterlists']) 66 self.writeBlock('implicit.txt', 67 'Implicit Externally Synchronized Parameters', 68 self.threadsafety['implicit']) 69 70 def paramIsArray(self, param): 71 """Check if the parameter passed in is a pointer to an array.""" 72 return param.get('len') is not None 73 74 def paramIsPointer(self, param): 75 """Check if the parameter passed in is a pointer.""" 76 tail = param.find('type').tail 77 return tail is not None and '*' in tail 78 79 def makeThreadSafetyBlocks(self, cmd, paramtext): 80 # See also makeThreadSafetyBlock in validitygenerator.py - similar but not entirely identical 81 protoname = cmd.find('proto/name').text 82 83 # Find and add any parameters that are thread unsafe 84 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]") 85 if explicitexternsyncparams is not None: 86 for param in explicitexternsyncparams: 87 self.makeThreadSafetyForParam(protoname, param) 88 89 # Find and add any "implicit" parameters that are thread unsafe 90 implicitexternsyncparams = cmd.find('implicitexternsyncparams') 91 if implicitexternsyncparams is not None: 92 for elem in implicitexternsyncparams: 93 entry = ValidityEntry() 94 entry += elem.text 95 entry += ' in ' 96 entry += self.makeFLink(protoname) 97 self.threadsafety['implicit'] += entry 98 99 # Add a VU for any command requiring host synchronization. 100 # This could be further parameterized, if a future non-Vulkan API 101 # requires it. 102 if self.genOpts.conventions.is_externsync_command(protoname): 103 entry = ValidityEntry() 104 entry += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in ' 105 entry += self.makeFLink(protoname) 106 self.threadsafety['implicit'] += entry 107 108 def makeThreadSafetyForParam(self, protoname, param): 109 """Create thread safety validity for a single param of a command.""" 110 externsyncattribs = ExternSyncEntry.parse_externsync_from_param(param) 111 param_name = getElemName(param) 112 113 for attrib in externsyncattribs: 114 entry = ValidityEntry() 115 is_array = False 116 if attrib.entirely_extern_sync: 117 # "true" or "true_with_children" 118 if self.paramIsArray(param): 119 entry += 'Each element of the ' 120 is_array = True 121 elif self.paramIsPointer(param): 122 entry += 'The object referenced by the ' 123 else: 124 entry += 'The ' 125 126 entry += self.makeParameterName(param_name) 127 entry += ' parameter' 128 129 if attrib.children_extern_sync: 130 entry += ', and any child handles,' 131 132 else: 133 # parameter/member reference 134 readable = attrib.get_human_readable(make_param_name=self.makeParameterName) 135 is_array = (' element of ' in readable) 136 entry += readable 137 138 entry += ' in ' 139 entry += self.makeFLink(protoname) 140 141 if is_array: 142 self.threadsafety['parameterlists'] += entry 143 else: 144 self.threadsafety['parameters'] += entry 145 146 def genCmd(self, cmdinfo, name, alias): 147 "Generate command." 148 OutputGenerator.genCmd(self, cmdinfo, name, alias) 149 150 # @@@ (Jon) something needs to be done here to handle aliases, probably 151 152 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param') 153 154 self.writeInclude() 155