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 28from xml.etree import ElementTree 29from zink_extensions import Extension,Layer,ExtensionRegistry,Version 30import sys 31 32# constructor: Extension(name, conditions=[], nonstandard=False) 33# The attributes: 34# - conditions: If the extension is provided by the Vulkan implementation, then 35# these are the extra conditions needed to enable the extension. 36# - nonstandard: Disables validation (cross-checking with vk.xml) if True. 37EXTENSIONS = [ 38 Extension("VK_EXT_debug_utils"), 39 Extension("VK_KHR_get_physical_device_properties2"), 40 Extension("VK_MVK_moltenvk", 41 nonstandard=True), 42 Extension("VK_KHR_surface"), 43] 44 45# constructor: Layer(name, conditions=[]) 46# - conditions: See documentation of EXTENSIONS. 47LAYERS = [ 48 # if we have debug_util, allow a validation layer to be added. 49 Layer("VK_LAYER_KHRONOS_validation", 50 conditions=["zink_debug & ZINK_DEBUG_VALIDATION"]), 51 Layer("VK_LAYER_LUNARG_standard_validation", 52 conditions=["zink_debug & ZINK_DEBUG_VALIDATION", "!have_layer_KHRONOS_validation"]), 53] 54 55REPLACEMENTS = { 56 "VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION_NAME" : "VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME" 57} 58 59header_code = """ 60#ifndef ZINK_INSTANCE_H 61#define ZINK_INSTANCE_H 62 63#include "os/os_process.h" 64 65#include <vulkan/vulkan.h> 66 67#if defined(__APPLE__) 68// Source of MVK_VERSION 69#include "MoltenVK/vk_mvk_moltenvk.h" 70#endif 71 72struct zink_screen; 73 74struct zink_instance_info { 75 uint32_t loader_version; 76 77%for ext in extensions: 78 bool have_${ext.name_with_vendor()}; 79%endfor 80 81%for layer in layers: 82 bool have_layer_${layer.pure_name()}; 83%endfor 84}; 85 86VkInstance 87zink_create_instance(struct zink_instance_info *instance_info); 88 89void 90zink_verify_instance_extensions(struct zink_screen *screen); 91 92/* stub functions that get inserted into the dispatch table if they are not 93 * properly loaded. 94 */ 95%for ext in extensions: 96%if registry.in_registry(ext.name): 97%for cmd in registry.get_registry_entry(ext.name).instance_commands: 98void zink_stub_${cmd.lstrip("vk")}(void); 99%endfor 100%for cmd in registry.get_registry_entry(ext.name).pdevice_commands: 101void zink_stub_${cmd.lstrip("vk")}(void); 102%endfor 103%endif 104%endfor 105 106#endif 107""" 108 109impl_code = """ 110#include "zink_instance.h" 111#include "zink_screen.h" 112 113VkInstance 114zink_create_instance(struct zink_instance_info *instance_info) 115{ 116 /* reserve one slot for MoltenVK */ 117 const char *layers[${len(layers) + 1}] = {0}; 118 uint32_t num_layers = 0; 119 120 const char *extensions[${len(extensions) + 1}] = {0}; 121 uint32_t num_extensions = 0; 122 123%for ext in extensions: 124 bool have_${ext.name_with_vendor()} = false; 125%endfor 126 127%for layer in layers: 128 bool have_layer_${layer.pure_name()} = false; 129%endfor 130 131#if defined(MVK_VERSION) 132 bool have_moltenvk_layer = false; 133#endif 134 135 // Build up the extensions from the reported ones but only for the unnamed layer 136 uint32_t extension_count = 0; 137 if (vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL) == VK_SUCCESS) { 138 VkExtensionProperties *extension_props = malloc(extension_count * sizeof(VkExtensionProperties)); 139 if (extension_props) { 140 if (vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extension_props) == VK_SUCCESS) { 141 for (uint32_t i = 0; i < extension_count; i++) { 142 %for ext in extensions: 143 if (!strcmp(extension_props[i].extensionName, ${ext.extension_name_literal()})) { 144 have_${ext.name_with_vendor()} = true; 145 } 146 %endfor 147 } 148 } 149 free(extension_props); 150 } 151 } 152 153 // Build up the layers from the reported ones 154 uint32_t layer_count = 0; 155 156 if (vkEnumerateInstanceLayerProperties(&layer_count, NULL) == VK_SUCCESS) { 157 VkLayerProperties *layer_props = malloc(layer_count * sizeof(VkLayerProperties)); 158 if (layer_props) { 159 if (vkEnumerateInstanceLayerProperties(&layer_count, layer_props) == VK_SUCCESS) { 160 for (uint32_t i = 0; i < layer_count; i++) { 161%for layer in layers: 162 if (!strcmp(layer_props[i].layerName, ${layer.extension_name_literal()})) { 163 have_layer_${layer.pure_name()} = true; 164 } 165%endfor 166#if defined(MVK_VERSION) 167 if (!strcmp(layer_props[i].layerName, "MoltenVK")) { 168 have_moltenvk_layer = true; 169 layers[num_layers++] = "MoltenVK"; 170 } 171#endif 172 } 173 } 174 free(layer_props); 175 } 176 } 177 178%for ext in extensions: 179<% 180 conditions = "" 181 if ext.enable_conds: 182 for cond in ext.enable_conds: 183 conditions += "&& (" + cond + ") " 184 conditions = conditions.strip() 185%>\ 186 if (have_${ext.name_with_vendor()} ${conditions}) { 187 instance_info->have_${ext.name_with_vendor()} = have_${ext.name_with_vendor()}; 188 extensions[num_extensions++] = ${ext.extension_name_literal()}; 189 } 190%endfor 191 192%for layer in layers: 193<% 194 conditions = "" 195 if layer.enable_conds: 196 for cond in layer.enable_conds: 197 conditions += "&& (" + cond + ") " 198 conditions = conditions.strip() 199%>\ 200 if (have_layer_${layer.pure_name()} ${conditions}) { 201 layers[num_layers++] = ${layer.extension_name_literal()}; 202 instance_info->have_layer_${layer.pure_name()} = true; 203 } 204%endfor 205 206 VkApplicationInfo ai = {0}; 207 ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 208 209 char proc_name[128]; 210 if (os_get_process_name(proc_name, ARRAY_SIZE(proc_name))) 211 ai.pApplicationName = proc_name; 212 else 213 ai.pApplicationName = "unknown"; 214 215 ai.pEngineName = "mesa zink"; 216 ai.apiVersion = instance_info->loader_version; 217 218 VkInstanceCreateInfo ici = {0}; 219 ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 220 ici.pApplicationInfo = &ai; 221 ici.ppEnabledExtensionNames = extensions; 222 ici.enabledExtensionCount = num_extensions; 223 ici.ppEnabledLayerNames = layers; 224 ici.enabledLayerCount = num_layers; 225 226 VkInstance instance = VK_NULL_HANDLE; 227 VkResult err = vkCreateInstance(&ici, NULL, &instance); 228 if (err != VK_SUCCESS) 229 return VK_NULL_HANDLE; 230 231 return instance; 232} 233 234void 235zink_verify_instance_extensions(struct zink_screen *screen) 236{ 237%for ext in extensions: 238%if registry.in_registry(ext.name): 239 if (screen->instance_info.have_${ext.name_with_vendor()}) { 240%for cmd in registry.get_registry_entry(ext.name).instance_commands: 241 if (!screen->vk.${cmd.lstrip("vk")}) { 242#ifndef NDEBUG 243 screen->vk.${cmd.lstrip("vk")} = (PFN_${cmd})zink_stub_${cmd.lstrip("vk")}; 244#else 245 screen->vk.${cmd.lstrip("vk")} = (PFN_${cmd})zink_stub_function_not_loaded; 246#endif 247 } 248%endfor 249%for cmd in registry.get_registry_entry(ext.name).pdevice_commands: 250 if (!screen->vk.${cmd.lstrip("vk")}) { 251#ifndef NDEBUG 252 screen->vk.${cmd.lstrip("vk")} = (PFN_${cmd})zink_stub_${cmd.lstrip("vk")}; 253#else 254 screen->vk.${cmd.lstrip("vk")} = (PFN_${cmd})zink_stub_function_not_loaded; 255#endif 256 } 257%endfor 258 } 259%endif 260%endfor 261} 262 263#ifndef NDEBUG 264/* generated stub functions */ 265## see zink_device_info.py for why this is needed 266<% generated_funcs = set() %> 267 268%for ext in extensions: 269%if registry.in_registry(ext.name): 270%for cmd in registry.get_registry_entry(ext.name).instance_commands + registry.get_registry_entry(ext.name).pdevice_commands: 271%if cmd in generated_funcs: 272 <% continue %> 273%else: 274 <% generated_funcs.add(cmd) %> 275%endif 276void 277zink_stub_${cmd.lstrip("vk")}() 278{ 279 mesa_loge("ZINK: ${cmd} is not loaded properly!"); 280 abort(); 281} 282%endfor 283%endif 284%endfor 285 286#endif 287""" 288 289 290def replace_code(code: str, replacement: dict): 291 for (k, v) in replacement.items(): 292 code = code.replace(k, v) 293 294 return code 295 296 297if __name__ == "__main__": 298 try: 299 header_path = sys.argv[1] 300 impl_path = sys.argv[2] 301 vkxml_path = sys.argv[3] 302 303 header_path = path.abspath(header_path) 304 impl_path = path.abspath(impl_path) 305 vkxml_path = path.abspath(vkxml_path) 306 except: 307 print("usage: %s <path to .h> <path to .c> <path to vk.xml>" % sys.argv[0]) 308 exit(1) 309 310 registry = ExtensionRegistry(vkxml_path) 311 312 extensions = EXTENSIONS 313 layers = LAYERS 314 replacement = REPLACEMENTS 315 316 # Perform extension validation and set core_since for the extension if available 317 error_count = 0 318 for ext in extensions: 319 if not registry.in_registry(ext.name): 320 # disable validation for nonstandard extensions 321 if ext.is_nonstandard: 322 continue 323 324 error_count += 1 325 print("The extension {} is not registered in vk.xml - a typo?".format(ext.name)) 326 continue 327 328 entry = registry.get_registry_entry(ext.name) 329 330 if entry.ext_type != "instance": 331 error_count += 1 332 print("The extension {} is {} extension - expected an instance extension.".format(ext.name, entry.ext_type)) 333 continue 334 335 if entry.promoted_in: 336 ext.core_since = Version((*entry.promoted_in, 0)) 337 338 if error_count > 0: 339 print("zink_instance.py: Found {} error(s) in total. Quitting.".format(error_count)) 340 exit(1) 341 342 with open(header_path, "w") as header_file: 343 header = Template(header_code).render(extensions=extensions, layers=layers, registry=registry).strip() 344 header = replace_code(header, replacement) 345 print(header, file=header_file) 346 347 with open(impl_path, "w") as impl_file: 348 impl = Template(impl_code).render(extensions=extensions, layers=layers, registry=registry).strip() 349 impl = replace_code(impl, replacement) 350 print(impl, file=impl_file) 351