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_MVK_moltenvk",
42        nonstandard=True),
43    Extension("VK_KHR_surface"),
44    Extension("VK_EXT_headless_surface"),
45    Extension("VK_KHR_wayland_surface"),
46    Extension("VK_KHR_xcb_surface",
47              conditions=["!instance_info->disable_xcb_surface"]),
48    Extension("VK_KHR_win32_surface"),
49]
50
51# constructor: Layer(name, conditions=[])
52# - conditions: See documentation of EXTENSIONS.
53LAYERS = [
54    # if we have debug_util, allow a validation layer to be added.
55    Layer("VK_LAYER_KHRONOS_validation",
56      conditions=["zink_debug & ZINK_DEBUG_VALIDATION"]),
57    Layer("VK_LAYER_LUNARG_standard_validation",
58      conditions=["zink_debug & ZINK_DEBUG_VALIDATION", "!have_layer_KHRONOS_validation"]),
59]
60
61REPLACEMENTS = {
62    "VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION_NAME" : "VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME"
63}
64
65header_code = """
66#ifndef ZINK_INSTANCE_H
67#define ZINK_INSTANCE_H
68
69#include "os/os_process.h"
70
71#include <vulkan/vulkan.h>
72
73#if defined(__APPLE__)
74// Source of MVK_VERSION
75#include "MoltenVK/vk_mvk_moltenvk.h"
76#endif
77
78struct pipe_screen;
79struct zink_screen;
80
81struct zink_instance_info {
82   uint32_t loader_version;
83   bool disable_xcb_surface;
84
85%for ext in extensions:
86   bool have_${ext.name_with_vendor()};
87%endfor
88
89%for layer in layers:
90   bool have_layer_${layer.pure_name()};
91%endfor
92};
93
94bool
95zink_create_instance(struct zink_screen *screen);
96
97void
98zink_verify_instance_extensions(struct zink_screen *screen);
99
100/* stub functions that get inserted into the dispatch table if they are not
101 * properly loaded.
102 */
103%for ext in extensions:
104%if registry.in_registry(ext.name):
105%for cmd in registry.get_registry_entry(ext.name).instance_commands:
106void zink_stub_${cmd.lstrip("vk")}(void);
107%endfor
108%for cmd in registry.get_registry_entry(ext.name).pdevice_commands:
109void zink_stub_${cmd.lstrip("vk")}(void);
110%endfor
111%endif
112%endfor
113
114struct pipe_screen;
115struct pipe_resource;
116
117#endif
118"""
119
120impl_code = """
121#include "vk_enum_to_str.h"
122#include "zink_instance.h"
123#include "zink_screen.h"
124
125bool
126zink_create_instance(struct zink_screen *screen)
127{
128   struct zink_instance_info *instance_info = &screen->instance_info;
129
130   /* reserve one slot for MoltenVK */
131   const char *layers[${len(layers) + 1}] = {0};
132   uint32_t num_layers = 0;
133
134   const char *extensions[${len(extensions) + 1}] = {0};
135   uint32_t num_extensions = 0;
136
137%for ext in extensions:
138   bool have_${ext.name_with_vendor()} = false;
139%endfor
140
141%for layer in layers:
142   bool have_layer_${layer.pure_name()} = false;
143%endfor
144
145#if defined(MVK_VERSION)
146   bool have_moltenvk_layer = false;
147#endif
148
149   GET_PROC_ADDR_INSTANCE_LOCAL(screen, NULL, EnumerateInstanceExtensionProperties);
150   GET_PROC_ADDR_INSTANCE_LOCAL(screen, NULL, EnumerateInstanceLayerProperties);
151   if (!vk_EnumerateInstanceExtensionProperties ||
152       !vk_EnumerateInstanceLayerProperties)
153      return false;
154
155   // Build up the extensions from the reported ones but only for the unnamed layer
156   uint32_t extension_count = 0;
157   if (vk_EnumerateInstanceExtensionProperties(NULL, &extension_count, NULL) != VK_SUCCESS) {
158       mesa_loge("ZINK: vkEnumerateInstanceExtensionProperties failed");
159   } else {
160       VkExtensionProperties *extension_props = malloc(extension_count * sizeof(VkExtensionProperties));
161       if (extension_props) {
162           if (vk_EnumerateInstanceExtensionProperties(NULL, &extension_count, extension_props) != VK_SUCCESS) {
163              mesa_loge("ZINK: vkEnumerateInstanceExtensionProperties failed");
164           } else {
165              for (uint32_t i = 0; i < extension_count; i++) {
166        %for ext in extensions:
167                if (!strcmp(extension_props[i].extensionName, ${ext.extension_name_literal()})) {
168                    have_${ext.name_with_vendor()} = true;
169                }
170        %endfor
171              }
172           }
173       free(extension_props);
174       }
175   }
176
177    // Build up the layers from the reported ones
178    uint32_t layer_count = 0;
179
180    if (vk_EnumerateInstanceLayerProperties(&layer_count, NULL) != VK_SUCCESS) {
181        mesa_loge("ZINK: vkEnumerateInstanceLayerProperties failed");
182    } else {
183        VkLayerProperties *layer_props = malloc(layer_count * sizeof(VkLayerProperties));
184        if (layer_props) {
185            if (vk_EnumerateInstanceLayerProperties(&layer_count, layer_props) != VK_SUCCESS) {
186                mesa_loge("ZINK: vkEnumerateInstanceLayerProperties failed");
187            } else {
188               for (uint32_t i = 0; i < layer_count; i++) {
189%for layer in layers:
190                  if (!strcmp(layer_props[i].layerName, ${layer.extension_name_literal()})) {
191                     have_layer_${layer.pure_name()} = true;
192                  }
193%endfor
194#if defined(MVK_VERSION)
195                  if (!strcmp(layer_props[i].layerName, "MoltenVK")) {
196                     have_moltenvk_layer = true;
197                     layers[num_layers++] = "MoltenVK";
198                  }
199#endif
200               }
201            }
202        free(layer_props);
203        }
204    }
205
206%for ext in extensions:
207<%
208    conditions = ""
209    if ext.enable_conds:
210        for cond in ext.enable_conds:
211            conditions += "&& (" + cond + ") "
212    conditions = conditions.strip()
213%>\
214   if (have_${ext.name_with_vendor()} ${conditions}) {
215      instance_info->have_${ext.name_with_vendor()} = have_${ext.name_with_vendor()};
216      extensions[num_extensions++] = ${ext.extension_name_literal()};
217   }
218%endfor
219
220%for layer in layers:
221<%
222    conditions = ""
223    if layer.enable_conds:
224        for cond in layer.enable_conds:
225            conditions += "&& (" + cond + ") "
226    conditions = conditions.strip()
227%>\
228   if (have_layer_${layer.pure_name()} ${conditions}) {
229      layers[num_layers++] = ${layer.extension_name_literal()};
230      instance_info->have_layer_${layer.pure_name()} = true;
231   }
232%endfor
233
234   VkApplicationInfo ai = {0};
235   ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
236
237   char proc_name[128];
238   if (os_get_process_name(proc_name, ARRAY_SIZE(proc_name)))
239      ai.pApplicationName = proc_name;
240   else
241      ai.pApplicationName = "unknown";
242
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") 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") 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