xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/zink/zink_instance.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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