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 mako.lookup import TemplateLookup 28from os import path 29from zink_extensions import Extension,ExtensionRegistry,Version 30import sys 31 32# constructor: 33# Extension(name, alias="", required=False, properties=False, features=False, conditions=None, guard=False) 34# The attributes: 35# - required: the generated code debug_prints "ZINK: {name} required!" and 36# returns NULL if the extension is unavailable. 37# 38# - properties: enable the detection of extension properties in a physical 39# device in the generated code using vkGetPhysicalDeviceProperties2(), 40# and store the returned properties struct inside 41# `zink_device_info.{alias}_props`. 42# Example: the properties for `VK_EXT_transform_feedback`, is stored in 43# `VkPhysicalDeviceTransformFeedbackPropertiesEXT tf_props`. 44# 45# - features: enable the getting extension features in a 46# device. Similar to `properties`, this stores the features 47# struct inside `zink_device_info.{alias}_feats`. 48# 49# - conditions: criteria for enabling an extension. This is an array of strings, 50# where each string is a condition, and all conditions have to be true 51# for `zink_device_info.have_{name}` to be true. 52# 53# The code generator will replace "$feats" and "$props" with the 54# respective variables, e.g. "$feats.nullDescriptor" becomes 55# "info->rb2_feats.nullDescriptor" in the final code for VK_EXT_robustness2. 56# 57# When empty or None, the extension is enabled when the extensions 58# given by vkEnumerateDeviceExtensionProperties() include the extension. 59# 60# - guard: adds a #if defined(`extension_name`)/#endif guard around the code generated for this Extension. 61EXTENSIONS = [ 62 Extension("VK_KHR_maintenance1", 63 required=True), 64 Extension("VK_KHR_maintenance2"), 65 Extension("VK_KHR_maintenance3"), 66 Extension("VK_KHR_external_memory"), 67 Extension("VK_KHR_external_memory_fd"), 68 Extension("VK_EXT_external_memory_dma_buf"), 69 Extension("VK_EXT_queue_family_foreign"), 70 Extension("VK_EXT_provoking_vertex", 71 alias="pv", 72 features=True, 73 properties=True, 74 conditions=["$feats.provokingVertexLast"]), 75 Extension("VK_EXT_shader_viewport_index_layer"), 76 Extension("VK_KHR_get_memory_requirements2"), 77 Extension("VK_EXT_post_depth_coverage"), 78 Extension("VK_EXT_shader_subgroup_ballot"), 79 Extension("VK_KHR_8bit_storage", 80 alias="storage_8bit", 81 features=True, 82 conditions=["$feats.storageBuffer8BitAccess"]), 83 Extension("VK_KHR_16bit_storage", 84 alias="storage_16bit", 85 features=True, 86 conditions=["$feats.storageBuffer16BitAccess"]), 87 Extension("VK_KHR_driver_properties", 88 alias="driver", 89 properties=True), 90 Extension("VK_EXT_memory_budget"), 91 Extension("VK_KHR_draw_indirect_count"), 92 Extension("VK_EXT_fragment_shader_interlock", 93 alias="interlock", 94 features=True, 95 conditions=["$feats.fragmentShaderSampleInterlock", "$feats.fragmentShaderPixelInterlock"]), 96 Extension("VK_EXT_sample_locations", 97 alias="sample_locations", 98 properties=True), 99 Extension("VK_EXT_conservative_rasterization", 100 alias="cons_raster", 101 properties=True, 102 conditions=["$props.fullyCoveredFragmentShaderInputVariable"]), 103 Extension("VK_KHR_shader_draw_parameters"), 104 Extension("VK_KHR_sampler_mirror_clamp_to_edge"), 105 Extension("VK_EXT_conditional_rendering", 106 alias="cond_render", 107 features=True, 108 conditions=["$feats.conditionalRendering"]), 109 Extension("VK_EXT_transform_feedback", 110 alias="tf", 111 properties=True, 112 features=True, 113 conditions=["$feats.transformFeedback"]), 114 Extension("VK_EXT_index_type_uint8", 115 alias="index_uint8", 116 features=True, 117 conditions=["$feats.indexTypeUint8"]), 118 Extension("VK_KHR_imageless_framebuffer", 119 alias="imgless", 120 features=True, 121 conditions=["$feats.imagelessFramebuffer"]), 122 Extension("VK_EXT_robustness2", 123 alias="rb2", 124 properties=True, 125 features=True, 126 conditions=["$feats.nullDescriptor"]), 127 Extension("VK_EXT_image_drm_format_modifier"), 128 Extension("VK_EXT_vertex_attribute_divisor", 129 alias="vdiv", 130 properties=True, 131 features=True, 132 conditions=["$feats.vertexAttributeInstanceRateDivisor"]), 133 Extension("VK_EXT_calibrated_timestamps"), 134 Extension("VK_KHR_shader_clock", 135 alias="shader_clock", 136 features=True, 137 conditions=["$feats.shaderSubgroupClock"]), 138 Extension("VK_EXT_sampler_filter_minmax", 139 alias="reduction", 140 properties=True), 141 Extension("VK_EXT_custom_border_color", 142 alias="border_color", 143 properties=True, 144 features=True, 145 conditions=["$feats.customBorderColors"]), 146 Extension("VK_EXT_blend_operation_advanced", 147 alias="blend", 148 properties=True, 149 # TODO: we can probably support non-premul here with some work? 150 conditions=["$props.advancedBlendNonPremultipliedSrcColor", "$props.advancedBlendNonPremultipliedDstColor"]), 151 Extension("VK_EXT_extended_dynamic_state", 152 alias="dynamic_state", 153 features=True, 154 conditions=["$feats.extendedDynamicState"]), 155 Extension("VK_EXT_extended_dynamic_state2", 156 alias="dynamic_state2", 157 features=True, 158 conditions=["$feats.extendedDynamicState2"]), 159 Extension("VK_EXT_pipeline_creation_cache_control", 160 alias="pipeline_cache_control", 161 features=True, 162 conditions=["$feats.pipelineCreationCacheControl"]), 163 Extension("VK_EXT_shader_stencil_export", 164 alias="stencil_export"), 165 Extension("VK_EXTX_portability_subset", 166 alias="portability_subset_extx", 167 nonstandard=True, 168 properties=True, 169 features=True, 170 guard=True), 171 Extension("VK_KHR_timeline_semaphore", alias="timeline", features=True), 172 Extension("VK_EXT_4444_formats", 173 alias="format_4444", 174 features=True), 175 Extension("VK_EXT_scalar_block_layout", 176 alias="scalar_block_layout", 177 features=True, 178 conditions=["$feats.scalarBlockLayout"]), 179 Extension("VK_KHR_swapchain"), 180 Extension("VK_KHR_shader_float16_int8", 181 alias="shader_float16_int8", 182 features=True), 183 Extension("VK_EXT_multi_draw", 184 alias="multidraw", 185 features=True, 186 properties=True, 187 conditions=["$feats.multiDraw"]), 188 Extension("VK_KHR_push_descriptor", 189 alias="push", 190 properties=True), 191 Extension("VK_KHR_descriptor_update_template", 192 alias="template"), 193 Extension("VK_EXT_line_rasterization", 194 alias="line_rast", 195 properties=True, 196 features=True), 197 Extension("VK_EXT_vertex_input_dynamic_state", 198 alias="vertex_input", 199 features=True, 200 conditions=["$feats.vertexInputDynamicState"]), 201 Extension("VK_EXT_primitive_topology_list_restart", 202 alias="list_restart", 203 features=True, 204 conditions=["$feats.primitiveTopologyListRestart"]), 205 Extension("VK_KHR_dedicated_allocation", 206 alias="dedicated"), 207 Extension("VK_EXT_descriptor_indexing", 208 alias="desc_indexing", 209 features=True, 210 properties=True, 211 conditions=["$feats.descriptorBindingPartiallyBound"]), 212] 213 214# constructor: Versions(device_version(major, minor, patch), struct_version(major, minor)) 215# The attributes: 216# - device_version: Vulkan version, as tuple, to use with 217# VK_MAKE_VERSION(version_major, version_minor, version_patch) 218# 219# - struct_version: Vulkan version, as tuple, to use with structures and macros 220VERSIONS = [ 221 Version((1,1,0), (1,1)), 222 Version((1,2,0), (1,2)), 223] 224 225# There exists some inconsistencies regarding the enum constants, fix them. 226# This is basically generated_code.replace(key, value). 227REPLACEMENTS = { 228 "ROBUSTNESS2": "ROBUSTNESS_2", 229 "PROPERTIES_PROPERTIES": "PROPERTIES", 230 "EXTENDED_DYNAMIC_STATE2": "EXTENDED_DYNAMIC_STATE_2", 231} 232 233 234# This template provides helper functions for the other templates. 235# Right now, the following functions are defined: 236# - guard(ext) : surrounds the body with an if-def guard according to 237# `ext.extension_name()` if `ext.guard` is True. 238include_template = """ 239<%def name="guard_(ext, body)"> 240%if ext.guard: 241#ifdef ${ext.extension_name()} 242%endif 243 ${capture(body)|trim} 244%if ext.guard: 245#endif 246%endif 247</%def> 248 249## This ugliness is here to prevent mako from adding tons of excessive whitespace 250<%def name="guard(ext)">${capture(guard_, ext, body=caller.body).strip('\\r\\n')}</%def> 251""" 252 253header_code = """ 254<%namespace name="helpers" file="helpers"/> 255 256#ifndef ZINK_DEVICE_INFO_H 257#define ZINK_DEVICE_INFO_H 258 259#include "util/u_memory.h" 260 261#include <vulkan/vulkan.h> 262 263struct zink_screen; 264 265struct zink_device_info { 266 uint32_t device_version; 267 268%for ext in extensions: 269<%helpers:guard ext="${ext}"> 270 bool have_${ext.name_with_vendor()}; 271</%helpers:guard> 272%endfor 273%for version in versions: 274 bool have_vulkan${version.struct()}; 275%endfor 276 277 VkPhysicalDeviceFeatures2 feats; 278%for version in versions: 279 VkPhysicalDeviceVulkan${version.struct()}Features feats${version.struct()}; 280%endfor 281 282 VkPhysicalDeviceProperties props; 283%for version in versions: 284 VkPhysicalDeviceVulkan${version.struct()}Properties props${version.struct()}; 285%endfor 286 287 VkPhysicalDeviceMemoryProperties mem_props; 288 289%for ext in extensions: 290<%helpers:guard ext="${ext}"> 291%if ext.has_features: 292 ${ext.physical_device_struct("Features")} ${ext.field("feats")}; 293%endif 294%if ext.has_properties: 295 ${ext.physical_device_struct("Properties")} ${ext.field("props")}; 296%endif 297</%helpers:guard> 298%endfor 299 300 const char *extensions[${len(extensions)}]; 301 uint32_t num_extensions; 302}; 303 304bool 305zink_get_physical_device_info(struct zink_screen *screen); 306 307void 308zink_verify_device_extensions(struct zink_screen *screen); 309 310/* stub functions that get inserted into the dispatch table if they are not 311 * properly loaded. 312 */ 313%for ext in extensions: 314%if registry.in_registry(ext.name): 315%for cmd in registry.get_registry_entry(ext.name).device_commands: 316void zink_stub_${cmd.lstrip("vk")}(void); 317%endfor 318%endif 319%endfor 320 321#endif 322""" 323 324 325impl_code = """ 326<%namespace name="helpers" file="helpers"/> 327 328#include "zink_device_info.h" 329#include "zink_screen.h" 330 331bool 332zink_get_physical_device_info(struct zink_screen *screen) 333{ 334 struct zink_device_info *info = &screen->info; 335%for ext in extensions: 336<%helpers:guard ext="${ext}"> 337 bool support_${ext.name_with_vendor()} = false; 338</%helpers:guard> 339%endfor 340 uint32_t num_extensions = 0; 341 342 // get device memory properties 343 vkGetPhysicalDeviceMemoryProperties(screen->pdev, &info->mem_props); 344 345 // enumerate device supported extensions 346 if (vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, NULL) == VK_SUCCESS) { 347 if (num_extensions > 0) { 348 VkExtensionProperties *extensions = MALLOC(sizeof(VkExtensionProperties) * num_extensions); 349 if (!extensions) goto fail; 350 vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, extensions); 351 352 for (uint32_t i = 0; i < num_extensions; ++i) { 353 %for ext in extensions: 354 <%helpers:guard ext="${ext}"> 355 if (!strcmp(extensions[i].extensionName, "${ext.name}")) { 356 %if not (ext.has_features or ext.has_properties): 357 info->have_${ext.name_with_vendor()} = true; 358 %else: 359 support_${ext.name_with_vendor()} = true; 360 %endif 361 } 362 </%helpers:guard> 363 %endfor 364 } 365 366 FREE(extensions); 367 } 368 } 369 370 // get device features 371 if (screen->vk.GetPhysicalDeviceFeatures2) { 372 // check for device extension features 373 info->feats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 374 375%for version in versions: 376%if version.device_version < (1,2,0): 377 if (VK_MAKE_VERSION(1,2,0) <= screen->vk_version) { 378 /* VkPhysicalDeviceVulkan11Features was added in 1.2, not 1.1 as one would think */ 379%else: 380 if (${version.version()} <= screen->vk_version) { 381%endif 382 info->feats${version.struct()}.sType = ${version.stype("FEATURES")}; 383 info->feats${version.struct()}.pNext = info->feats.pNext; 384 info->feats.pNext = &info->feats${version.struct()}; 385 info->have_vulkan${version.struct()} = true; 386 } 387%endfor 388 389%for ext in extensions: 390%if ext.has_features: 391<%helpers:guard ext="${ext}"> 392 if (support_${ext.name_with_vendor()}) { 393 info->${ext.field("feats")}.sType = ${ext.stype("FEATURES")}; 394 info->${ext.field("feats")}.pNext = info->feats.pNext; 395 info->feats.pNext = &info->${ext.field("feats")}; 396 } 397</%helpers:guard> 398%endif 399%endfor 400 401 screen->vk.GetPhysicalDeviceFeatures2(screen->pdev, &info->feats); 402 } else { 403 vkGetPhysicalDeviceFeatures(screen->pdev, &info->feats.features); 404 } 405 406 // check for device properties 407 if (screen->vk.GetPhysicalDeviceProperties2) { 408 VkPhysicalDeviceProperties2 props = {0}; 409 props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; 410 411%for version in versions: 412%if version.device_version < (1,2,0): 413 if (VK_MAKE_VERSION(1,2,0) <= screen->vk_version) { 414 /* VkPhysicalDeviceVulkan11Properties was added in 1.2, not 1.1 as one would think */ 415%else: 416 if (${version.version()} <= screen->vk_version) { 417%endif 418 info->props${version.struct()}.sType = ${version.stype("PROPERTIES")}; 419 info->props${version.struct()}.pNext = props.pNext; 420 props.pNext = &info->props${version.struct()}; 421 } 422%endfor 423 424%for ext in extensions: 425%if ext.has_properties: 426<%helpers:guard ext="${ext}"> 427 if (support_${ext.name_with_vendor()}) { 428 info->${ext.field("props")}.sType = ${ext.stype("PROPERTIES")}; 429 info->${ext.field("props")}.pNext = props.pNext; 430 props.pNext = &info->${ext.field("props")}; 431 } 432</%helpers:guard> 433%endif 434%endfor 435 436 // note: setting up local VkPhysicalDeviceProperties2. 437 screen->vk.GetPhysicalDeviceProperties2(screen->pdev, &props); 438 } 439 440 // enable the extensions if they match the conditions given by ext.enable_conds 441 if (screen->vk.GetPhysicalDeviceProperties2) { 442 %for ext in extensions: 443<%helpers:guard ext="${ext}"> 444<% 445 conditions = "" 446 if ext.enable_conds: 447 for cond in ext.enable_conds: 448 cond = cond.replace("$feats", "info->" + ext.field("feats")) 449 cond = cond.replace("$props", "info->" + ext.field("props")) 450 conditions += "&& (" + cond + ")\\n" 451 conditions = conditions.strip() 452%>\ 453 info->have_${ext.name_with_vendor()} |= support_${ext.name_with_vendor()} 454 ${conditions}; 455</%helpers:guard> 456 %endfor 457 } 458 459 // generate extension list 460 num_extensions = 0; 461 462%for ext in extensions: 463<%helpers:guard ext="${ext}"> 464 if (info->have_${ext.name_with_vendor()}) { 465 info->extensions[num_extensions++] = "${ext.name}"; 466%if ext.is_required: 467 } else { 468 debug_printf("ZINK: ${ext.name} required!\\n"); 469 goto fail; 470%endif 471 } 472</%helpers:guard> 473%endfor 474 475 info->num_extensions = num_extensions; 476 477 return true; 478 479fail: 480 return false; 481} 482 483void 484zink_verify_device_extensions(struct zink_screen *screen) 485{ 486%for ext in extensions: 487%if registry.in_registry(ext.name): 488 if (screen->info.have_${ext.name_with_vendor()}) { 489%for cmd in registry.get_registry_entry(ext.name).device_commands: 490 if (!screen->vk.${cmd.lstrip("vk")}) { 491#ifndef NDEBUG 492 screen->vk.${cmd.lstrip("vk")} = (PFN_${cmd})zink_stub_${cmd.lstrip("vk")}; 493#else 494 screen->vk.${cmd.lstrip("vk")} = (PFN_${cmd})zink_stub_function_not_loaded; 495#endif 496 } 497%endfor 498 } 499%endif 500%endfor 501} 502 503#ifndef NDEBUG 504/* generated stub functions */ 505## remember the stub functions that are already generated 506<% generated_funcs = set() %> 507 508%for ext in extensions: 509%if registry.in_registry(ext.name): 510%for cmd in registry.get_registry_entry(ext.name).device_commands: 511## 512## some functions are added by multiple extensions, which creates duplication 513## and thus redefinition of stubs (eg. vkCmdPushDescriptorSetWithTemplateKHR) 514## 515%if cmd in generated_funcs: 516 <% continue %> 517%else: 518 <% generated_funcs.add(cmd) %> 519%endif 520void 521zink_stub_${cmd.lstrip("vk")}() 522{ 523 mesa_loge("ZINK: ${cmd} is not loaded properly!"); 524 abort(); 525} 526%endfor 527%endif 528%endfor 529#endif 530""" 531 532 533def replace_code(code: str, replacement: dict): 534 for (k, v) in replacement.items(): 535 code = code.replace(k, v) 536 537 return code 538 539 540if __name__ == "__main__": 541 try: 542 header_path = sys.argv[1] 543 impl_path = sys.argv[2] 544 vkxml_path = sys.argv[3] 545 546 header_path = path.abspath(header_path) 547 impl_path = path.abspath(impl_path) 548 vkxml_path = path.abspath(vkxml_path) 549 except: 550 print("usage: %s <path to .h> <path to .c> <path to vk.xml>" % sys.argv[0]) 551 exit(1) 552 553 registry = ExtensionRegistry(vkxml_path) 554 555 extensions = EXTENSIONS 556 versions = VERSIONS 557 replacement = REPLACEMENTS 558 559 # Perform extension validation and set core_since for the extension if available 560 error_count = 0 561 for ext in extensions: 562 if not registry.in_registry(ext.name): 563 # disable validation for nonstandard extensions 564 if ext.is_nonstandard: 565 continue 566 567 error_count += 1 568 print("The extension {} is not registered in vk.xml - a typo?".format(ext.name)) 569 continue 570 571 entry = registry.get_registry_entry(ext.name) 572 573 if entry.ext_type != "device": 574 error_count += 1 575 print("The extension {} is {} extension - expected a device extension.".format(ext.name, entry.ext_type)) 576 continue 577 578 if ext.has_features: 579 if not (entry.features_struct and ext.physical_device_struct("Features") == entry.features_struct): 580 error_count += 1 581 print("The extension {} does not provide a features struct.".format(ext.name)) 582 583 if ext.has_properties: 584 if not (entry.properties_struct and ext.physical_device_struct("Properties") == entry.properties_struct): 585 error_count += 1 586 print("The extension {} does not provide a properties struct.".format(ext.name)) 587 print(entry.properties_struct, ext.physical_device_struct("Properties")) 588 589 if entry.promoted_in: 590 ext.core_since = Version((*entry.promoted_in, 0)) 591 592 if error_count > 0: 593 print("zink_device_info.py: Found {} error(s) in total. Quitting.".format(error_count)) 594 exit(1) 595 596 lookup = TemplateLookup() 597 lookup.put_string("helpers", include_template) 598 599 with open(header_path, "w") as header_file: 600 header = Template(header_code, lookup=lookup).render(extensions=extensions, versions=versions, registry=registry).strip() 601 header = replace_code(header, replacement) 602 print(header, file=header_file) 603 604 with open(impl_path, "w") as impl_file: 605 impl = Template(impl_code, lookup=lookup).render(extensions=extensions, versions=versions, registry=registry).strip() 606 impl = replace_code(impl, replacement) 607 print(impl, file=impl_file) 608