1#!/usr/bin/env python 2 3# Copyright 2019 The Amber Authors. All rights reserved. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17# Generates vk-wrappers.inc in the src/ directory. 18 19from __future__ import print_function 20 21import os.path 22import re 23import sys 24import xml.etree.ElementTree as ET 25from string import Template 26 27def read_inc(file): 28 methods = [] 29 pattern = re.compile(r"(|OPTIONAL )AMBER_VK_FUNC\((\w+)\)") 30 with open(file, 'r') as f: 31 for line in f: 32 match = pattern.search(line) 33 if match == None: 34 raise Exception("FAILED TO MATCH PATTERN"); 35 36 b = False 37 if match.group(1) != None and match.group(1) == "OPTIONAL ": 38 b = True 39 methods.append((match.group(2), b)) 40 41 return methods 42 43 44def read_vk(file): 45 methods = {} 46 tree = ET.parse(file) 47 root = tree.getroot(); 48 for command in root.iter("command"): 49 proto = command.find('proto') 50 if proto == None: 51 continue 52 53 return_type = proto.find('type').text 54 name = proto.find('name').text 55 56 param_list = [] 57 for param in command.findall('param'): 58 param_val = "".join(param.itertext()) 59 param_name = param.find('name').text 60 param_list.append({ 61 'def': param_val, 62 'name': param_name 63 }) 64 65 methods[name] = { 66 'return_type': return_type, 67 'name': name, 68 'params': param_list 69 } 70 71 return methods 72 73 74def gen_wrappers(methods, xml): 75 content = "" 76 for method_ in methods: 77 method = method_[0] 78 data = xml[method] 79 if data == None: 80 raise Exception("Failed to find {}".format(method)) 81 82 param_vals = [] 83 param_names = [] 84 for param in data['params']: 85 param_vals.append(param['def']) 86 param_names.append(param['name']) 87 88 signature = ', '.join(str(x) for x in param_vals) 89 arguments = ', '.join(str(x) for x in param_names) 90 return_type = data['return_type'] 91 return_variable = '' 92 call_prefix = '' 93 if return_type != 'void': 94 return_variable = 'ret' 95 call_prefix = return_type + ' ' + return_variable + ' = ' 96 97 template = Template(R'''{ 98 PFN_${method} ptr = reinterpret_cast<PFN_${method}>(getInstanceProcAddr(instance_, "${method}")); 99 if (!ptr) { 100 return Result("Vulkan: Unable to load ${method} pointer"); 101 } 102 if (delegate && delegate->LogGraphicsCalls()) { 103 ptrs_.${method} = [ptr, delegate](${signature}) -> ${return_type} { 104 delegate->Log("${method}"); 105 uint64_t timestamp_start = 0; 106 if (delegate->LogGraphicsCallsTime()) { 107 timestamp_start = delegate->GetTimestampNs(); 108 } 109 ${call_prefix}ptr(${arguments}); 110 if (delegate->LogGraphicsCallsTime()) { 111 uint64_t timestamp_end = delegate->GetTimestampNs(); 112 uint64_t duration = timestamp_end - timestamp_start; 113 std::ostringstream out; 114 out << "time "; 115 // name of method on 40 characters 116 out << std::left << std::setw(40) << "${method}"; 117 // duration in nanoseconds on 12 characters, right-aligned 118 out << std::right << std::setw(12) << duration; 119 out << " ns"; 120 delegate->Log(out.str()); 121 } 122 return ${return_variable}; 123 }; 124 } else { 125 ptrs_.${method} = [ptr](${signature}) -> ${return_type} { 126 ${call_prefix}ptr(${arguments}); 127 return ${return_variable}; 128 }; 129 } 130} 131''') 132 133 content += template.substitute(method=method, 134 signature=signature, 135 arguments=arguments, 136 return_type=return_type, 137 return_variable=return_variable, 138 call_prefix=call_prefix) 139 140 return content 141 142 143def gen_headers(methods, xml): 144 content = "" 145 for method_ in methods: 146 method = method_[0] 147 data = xml[method] 148 if data == None: 149 raise Exception("Failed to find {}".format(method)) 150 151 param_vals = [] 152 param_names = [] 153 for param in data['params']: 154 param_vals.append(param['def']) 155 param_names.append(param['name']) 156 157 content += "std::function<{}({})> {};\n".format(data['return_type'], 158 ', '.join(str(x) for x in param_vals), method) 159 160 return content 161 162 163def gen_direct(methods): 164 content = ""; 165 166 template = Template(R''' 167if (!(ptrs_.${method} = reinterpret_cast<PFN_${method}>(getInstanceProcAddr(instance_, "${method}")))) { 168 return Result("Vulkan: Unable to load ${method} pointer"); 169} 170''') 171 template_optional = Template(R''' 172ptrs_.${method} = reinterpret_cast<PFN_${method}>(getInstanceProcAddr(instance_, "${method}")); 173''') 174 175 for method_ in methods: 176 method = method_[0] 177 optional = method_[1] 178 if (optional): 179 content += template_optional.substitute(method=method) 180 else: 181 content += template.substitute(method=method) 182 183 return content 184 185 186def gen_direct_headers(methods): 187 content = "" 188 for method_ in methods: 189 method = method_[0] 190 content += "PFN_{} {};\n".format(method, method); 191 192 return content 193 194 195def main(): 196 if len(sys.argv) != 3: 197 print('usage: {} <outdir> <src_dir>'.format( 198 sys.argv[0])) 199 sys.exit(1) 200 201 outdir = sys.argv[1] 202 srcdir = sys.argv[2] 203 204 vulkan_versions = ("1-0", "1-1") 205 206 for vulkan_version in vulkan_versions: 207 208 vkfile = os.path.join(srcdir, 'third_party', 'vulkan-headers', 'registry', 'vk.xml') 209 incfile = os.path.join(srcdir, 'src', 'vulkan', 'vk-funcs-%s.inc' % vulkan_version) 210 211 data = read_inc(incfile) 212 213 wrapper_content = '' 214 header_content = '' 215 if os.path.isfile(vkfile): 216 vk_data = read_vk(vkfile) 217 wrapper_content = gen_wrappers(data, vk_data) 218 header_content = gen_headers(data, vk_data) 219 else: 220 wrapper_content = gen_direct(data) 221 header_content = gen_direct_headers(data) 222 223 outfile = os.path.join(outdir, 'vk-wrappers-%s.inc' % vulkan_version) 224 if os.path.isfile(outfile): 225 with open(outfile, 'r') as f: 226 if wrapper_content == f.read(): 227 return 228 with open(outfile, 'w') as f: 229 f.write(wrapper_content) 230 231 hdrfile = os.path.join(outdir, 'vk-wrappers-%s.h' % vulkan_version) 232 if os.path.isfile(hdrfile): 233 with open(hdrfile, 'r') as f: 234 if header_content == f.read(): 235 return 236 with open(hdrfile, 'w') as f: 237 f.write(header_content) 238 239 240if __name__ == '__main__': 241 main() 242