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