1# Copyright © 2020 Hoe Hao Cheng 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the "Software"), 5# to deal in the Software without restriction, including without limitation 6# the rights to use, copy, modify, merge, publish, distribute, sublicense, 7# and/or sell copies of the Software, and to permit persons to whom the 8# Software is furnished to do so, subject to the following conditions: 9# 10# The above copyright notice and this permission notice (including the next 11# paragraph) shall be included in all copies or substantial portions of the 12# Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21# 22# Authors: 23# Hoe Hao Cheng <haochengho12907@gmail.com> 24# 25 26from mako.template import Template 27from os import path 28import re 29import sys 30 31# constructor: Extensions(name, alias="", required=False, properties=False, features=False, have_feature=None, guard=False) 32# The attributes: 33# - required: the generated code debug_prints "ZINK: {name} required!" and 34# returns NULL if the extension is unavailable. 35# 36# - properties: enable the detection of extension properties in a physical 37# device in the generated code using vkGetPhysicalDeviceProperties2(), 38# and store the returned properties struct inside 39# `zink_device_info.{alias}_props`. 40# Example: the properties for `VK_EXT_transform_feedback`, is stored in 41# `VkPhysicalDeviceTransformFeedbackPropertiesEXT tf_props`. 42# 43# - have_feature: enable the fine-grained detection of extension features in a 44# device. Similar to `properties`, this stores the features 45# struct inside `zink_device_info.{alias}_feats`. 46# It sets `zink_device_info.have_{name} = true` only if 47# `{alias}_feats.{has_feature}` is true. 48# If have_feature is None, `have_{extension_name}` is true when the extensions 49# given by vkEnumerateDeviceExtensionProperties() include the extension. 50# Furthermore, `zink_device_info.{extension_alias}_feats` is unavailable. 51# 52# - features: enable the getting extension features in a 53# device. Similar to `have_feature`, this stores the features 54# struct inside `zink_device_info.{alias}_feats`. 55# Unlike `have_feature` it does not create a `have_` member. 56# 57# - guard: adds a #if defined(`extension_name`)/#endif guard around the code generated for this Extension. 58def EXTENSIONS(): 59 return [ 60 Extension("VK_KHR_maintenance1", required=True), 61 Extension("VK_KHR_external_memory"), 62 Extension("VK_KHR_external_memory_fd"), 63 Extension("VK_KHR_vulkan_memory_model"), 64 Extension("VK_EXT_conditional_rendering", alias="cond_render", have_feature="conditionalRendering"), 65 Extension("VK_EXT_transform_feedback", alias="tf", properties=True, have_feature="transformFeedback"), 66 Extension("VK_EXT_index_type_uint8", alias="index_uint8", have_feature="indexTypeUint8"), 67 Extension("VK_EXT_robustness2", alias="rb2", properties=True, have_feature="nullDescriptor"), 68 Extension("VK_EXT_vertex_attribute_divisor", alias="vdiv", properties=True, have_feature="vertexAttributeInstanceRateDivisor"), 69 Extension("VK_EXT_calibrated_timestamps"), 70 Extension("VK_EXT_custom_border_color", alias="border_color", properties=True, have_feature="customBorderColors"), 71 Extension("VK_EXT_blend_operation_advanced", alias="blend", properties=True), 72 Extension("VK_EXT_extended_dynamic_state", alias="dynamic_state", have_feature="extendedDynamicState"), 73 Extension("VK_EXT_pipeline_creation_cache_control", alias="pipeline_cache_control", have_feature="pipelineCreationCacheControl"), 74 Extension("VK_EXT_shader_stencil_export", alias="stencil_export"), 75 Extension("VK_EXTX_portability_subset", alias="portability_subset_extx", properties=True, features=True, guard=True), 76 ] 77 78# There exists some inconsistencies regarding the enum constants, fix them. 79# This is basically generated_code.replace(key, value). 80def REPLACEMENTS(): 81 return { 82 "ROBUSTNESS2": "ROBUSTNESS_2" 83 } 84 85 86header_code = """ 87#ifndef ZINK_DEVICE_INFO_H 88#define ZINK_DEVICE_INFO_H 89 90#include "util/u_memory.h" 91 92#include <vulkan/vulkan.h> 93 94#if defined(__APPLE__) 95// Source of MVK_VERSION 96// Source of VK_EXTX_PORTABILITY_SUBSET_EXTENSION_NAME 97#include "MoltenVK/vk_mvk_moltenvk.h" 98#endif 99 100struct zink_screen; 101 102struct zink_device_info { 103%for ext in extensions: 104 ${ext.guard_start()} 105 bool have_${ext.name_with_vendor()}; 106 ${ext.guard_end()} 107%endfor 108 109 VkPhysicalDeviceFeatures2 feats; 110 VkPhysicalDeviceProperties props; 111 VkPhysicalDeviceMemoryProperties mem_props; 112 113%for ext in extensions: 114 ${ext.guard_start()} 115%if ext.feature_field is not None or ext.has_features: 116 VkPhysicalDevice${ext.name_in_camel_case()}Features${ext.vendor()} ${ext.field("feats")}; 117%endif 118%if ext.has_properties: 119 VkPhysicalDevice${ext.name_in_camel_case()}Properties${ext.vendor()} ${ext.field("props")}; 120%endif 121 ${ext.guard_end()} 122%endfor 123 124 const char *extensions[${len(extensions)}]; 125 uint32_t num_extensions; 126}; 127 128bool 129zink_get_physical_device_info(struct zink_screen *screen); 130 131#endif 132""" 133 134 135impl_code = """ 136#include "zink_device_info.h" 137#include "zink_screen.h" 138 139bool 140zink_get_physical_device_info(struct zink_screen *screen) 141{ 142 struct zink_device_info *info = &screen->info; 143%for ext in extensions: 144 ${ext.guard_start()} 145 bool support_${ext.name_with_vendor()} = false; 146 ${ext.guard_end()} 147%endfor 148 uint32_t num_extensions = 0; 149 150 vkGetPhysicalDeviceMemoryProperties(screen->pdev, &info->mem_props); 151 152 // enumerate device supported extensions 153 if (vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, NULL) == VK_SUCCESS) { 154 if (num_extensions > 0) { 155 VkExtensionProperties *extensions = MALLOC(sizeof(VkExtensionProperties) * num_extensions); 156 if (!extensions) goto fail; 157 vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, extensions); 158 159 for (uint32_t i = 0; i < num_extensions; ++i) { 160 %for ext in extensions: 161 ${ext.guard_start()} 162 if (!strcmp(extensions[i].extensionName, "${ext.name}")) { 163 support_${ext.name_with_vendor()} = true; 164 } 165 ${ext.guard_end()} 166 %endfor 167 } 168 169 FREE(extensions); 170 } 171 } 172 173 if (screen->vk_GetPhysicalDeviceFeatures2) { 174 // check for device extension features 175 info->feats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 176 177%for ext in extensions: 178%if ext.feature_field is not None or ext.has_features: 179 ${ext.guard_start()} 180 if (support_${ext.name_with_vendor()}) { 181 info->${ext.field("feats")}.sType = ${ext.stype("FEATURES")}; 182 info->${ext.field("feats")}.pNext = info->feats.pNext; 183 info->feats.pNext = &info->${ext.field("feats")}; 184 } 185 ${ext.guard_end()} 186%endif 187%endfor 188 189 screen->vk_GetPhysicalDeviceFeatures2(screen->pdev, &info->feats); 190 191%for ext in extensions: 192 ${ext.guard_start()} 193%if ext.feature_field is None: 194 info->have_${ext.name_with_vendor()} = support_${ext.name_with_vendor()}; 195%else: 196 if (support_${ext.name_with_vendor()} && info->${ext.field("feats")}.${ext.feature_field}) { 197 info->have_${ext.name_with_vendor()} = true; 198 } 199%endif 200 ${ext.guard_end()} 201%endfor 202 } else { 203 vkGetPhysicalDeviceFeatures(screen->pdev, &info->feats.features); 204 205%for ext in extensions: 206 ${ext.guard_start()} 207%if ext.feature_field is None: 208 info->have_${ext.name_with_vendor()} = support_${ext.name_with_vendor()}; 209%endif 210 ${ext.guard_end()} 211%endfor 212 } 213 214 // check for device properties 215 if (screen->vk_GetPhysicalDeviceProperties2) { 216 VkPhysicalDeviceProperties2 props = {}; 217 props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; 218 219%for ext in extensions: 220%if ext.has_properties: 221 ${ext.guard_start()} 222 if (info->have_${ext.name_with_vendor()}) { 223 info->${ext.field("props")}.sType = ${ext.stype("PROPERTIES")}; 224 info->${ext.field("props")}.pNext = props.pNext; 225 props.pNext = &info->${ext.field("props")}; 226 } 227 ${ext.guard_end()} 228%endif 229%endfor 230 231 screen->vk_GetPhysicalDeviceProperties2(screen->pdev, &props); 232 memcpy(&info->props, &props.properties, sizeof(info->props)); 233 } else { 234 vkGetPhysicalDeviceProperties(screen->pdev, &info->props); 235 } 236 237 // generate extension list 238 num_extensions = 0; 239 240%for ext in extensions: 241 ${ext.guard_start()} 242 if (info->have_${ext.name_with_vendor()}) { 243 info->extensions[num_extensions++] = "${ext.name}"; 244%if ext.is_required: 245 } else { 246 debug_printf("ZINK: ${ext.name} required!\\n"); 247 goto fail; 248%endif 249 } 250 ${ext.guard_end()} 251%endfor 252 253 info->num_extensions = num_extensions; 254 255 return true; 256 257fail: 258 return false; 259} 260""" 261 262class Extension: 263 name : str = None 264 alias : str = None 265 is_required : bool = False 266 has_properties : bool = False 267 has_features : bool = False 268 feature_field : str = None 269 guard : bool = False 270 271 def __init__(self, name, alias="", required=False, properties=False, features=False, have_feature=None, guard=False): 272 self.name = name 273 self.alias = alias 274 self.is_required = required 275 self.has_properties = properties 276 self.has_features = features 277 self.feature_field = have_feature 278 self.guard = guard 279 280 if alias == "" and (properties == True or have_feature is not None): 281 raise RuntimeError("alias must be available when properties/feature is used") 282 283 # e.g.: "VK_EXT_robustness2" -> "robustness2" 284 def pure_name(self): 285 return '_'.join(self.name.split('_')[2:]) 286 287 # e.g.: "VK_EXT_robustness2" -> "EXT_robustness2" 288 def name_with_vendor(self): 289 return self.name[3:] 290 291 # e.g.: "VK_EXT_robustness2" -> "Robustness2" 292 def name_in_camel_case(self): 293 return "".join([x.title() for x in self.name.split('_')[2:]]) 294 295 # e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS2_EXTENSION_NAME" 296 # do note that inconsistencies exist, i.e. we have 297 # VK_EXT_ROBUSTNESS_2_EXTENSION_NAME defined in the headers, but then 298 # we also have VK_KHR_MAINTENANCE1_EXTENSION_NAME 299 def extension_name(self): 300 return self.name.upper() + "_EXTENSION_NAME" 301 302 # generate a C string literal for the extension 303 def extension_name_literal(self): 304 return '"' + self.name + '"' 305 306 # get the field in zink_device_info that refers to the extension's 307 # feature/properties struct 308 # e.g. rb2_<suffix> for VK_EXT_robustness2 309 def field(self, suffix: str): 310 return self.alias + '_' + suffix 311 312 # the sType of the extension's struct 313 # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT 314 # for VK_EXT_transform_feedback and struct="FEATURES" 315 def stype(self, struct: str): 316 return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_" 317 + self.pure_name().upper() 318 + '_' + struct + '_' 319 + self.vendor()) 320 321 # e.g. EXT in VK_EXT_robustness2 322 def vendor(self): 323 return self.name.split('_')[1] 324 325 # e.g. #if defined(VK_EXT_robustness2) 326 def guard_start(self): 327 if self.guard == False: 328 return "" 329 return ("#if defined(" 330 + self.extension_name() 331 + ")") 332 333 # e.g. #endif // VK_EXT_robustness2 334 def guard_end(self): 335 if self.guard == False: 336 return "" 337 return ("#endif //" 338 + self.extension_name()) 339 340 341def replace_code(code: str, replacement: dict): 342 for (k, v) in replacement.items(): 343 code = code.replace(k, v) 344 345 return code 346 347 348if __name__ == "__main__": 349 try: 350 header_path = sys.argv[1] 351 impl_path = sys.argv[2] 352 353 header_path = path.abspath(header_path) 354 impl_path = path.abspath(impl_path) 355 except: 356 print("usage: %s <path to .h> <path to .c>" % sys.argv[0]) 357 exit(1) 358 359 extensions = EXTENSIONS() 360 replacement = REPLACEMENTS() 361 362 with open(header_path, "w") as header_file: 363 header = Template(header_code).render(extensions=extensions).strip() 364 header = replace_code(header, replacement) 365 print(header, file=header_file) 366 367 with open(impl_path, "w") as impl_file: 368 impl = Template(impl_code).render(extensions=extensions).strip() 369 impl = replace_code(impl, replacement) 370 print(impl, file=impl_file) 371