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