• 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
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