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