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