• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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