• 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
28import re
29import sys
30
31# constructor: Extensions(name, alias="", required=False, properties=False, features=False, have_feature=None, guard=False)
32# The attributes:
33#  - required: the generated code debug_prints "ZINK: {name} required!" and
34#              returns NULL if the extension is unavailable.
35#
36#  - properties: enable the detection of extension properties in a physical
37#                device in the generated code using vkGetPhysicalDeviceProperties2(),
38#                and store the returned properties struct inside
39#                `zink_device_info.{alias}_props`.
40#                Example: the properties for `VK_EXT_transform_feedback`, is stored in
41#                `VkPhysicalDeviceTransformFeedbackPropertiesEXT tf_props`.
42#
43#  - have_feature: enable the fine-grained detection of extension features in a
44#             device. Similar to `properties`, this stores the features
45#             struct inside `zink_device_info.{alias}_feats`.
46#             It sets `zink_device_info.have_{name} = true` only if
47#             `{alias}_feats.{has_feature}` is true.
48#             If have_feature is None, `have_{extension_name}` is true when the extensions
49#             given by vkEnumerateDeviceExtensionProperties() include the extension.
50#             Furthermore, `zink_device_info.{extension_alias}_feats` is unavailable.
51#
52#  - features: enable the getting extension features in a
53#              device. Similar to `have_feature`, this stores the features
54#              struct inside `zink_device_info.{alias}_feats`.
55#              Unlike `have_feature` it does not create a `have_` member.
56#
57#  - guard: adds a #if defined(`extension_name`)/#endif guard around the code generated for this Extension.
58def EXTENSIONS():
59    return [
60        Extension("VK_KHR_maintenance1",             required=True),
61        Extension("VK_KHR_external_memory"),
62        Extension("VK_KHR_external_memory_fd"),
63        Extension("VK_KHR_vulkan_memory_model"),
64        Extension("VK_EXT_conditional_rendering",    alias="cond_render", have_feature="conditionalRendering"),
65        Extension("VK_EXT_transform_feedback",       alias="tf", properties=True, have_feature="transformFeedback"),
66        Extension("VK_EXT_index_type_uint8",         alias="index_uint8", have_feature="indexTypeUint8"),
67        Extension("VK_EXT_robustness2",              alias="rb2", properties=True, have_feature="nullDescriptor"),
68        Extension("VK_EXT_vertex_attribute_divisor", alias="vdiv", properties=True, have_feature="vertexAttributeInstanceRateDivisor"),
69        Extension("VK_EXT_calibrated_timestamps"),
70        Extension("VK_EXT_custom_border_color",      alias="border_color", properties=True, have_feature="customBorderColors"),
71        Extension("VK_EXT_blend_operation_advanced", alias="blend", properties=True),
72        Extension("VK_EXT_extended_dynamic_state",   alias="dynamic_state", have_feature="extendedDynamicState"),
73        Extension("VK_EXT_pipeline_creation_cache_control",   alias="pipeline_cache_control", have_feature="pipelineCreationCacheControl"),
74        Extension("VK_EXT_shader_stencil_export",    alias="stencil_export"),
75        Extension("VK_EXTX_portability_subset",      alias="portability_subset_extx", properties=True, features=True, guard=True),
76    ]
77
78# There exists some inconsistencies regarding the enum constants, fix them.
79# This is basically generated_code.replace(key, value).
80def REPLACEMENTS():
81    return {
82        "ROBUSTNESS2": "ROBUSTNESS_2"
83    }
84
85
86header_code = """
87#ifndef ZINK_DEVICE_INFO_H
88#define ZINK_DEVICE_INFO_H
89
90#include "util/u_memory.h"
91
92#include <vulkan/vulkan.h>
93
94#if defined(__APPLE__)
95// Source of MVK_VERSION
96// Source of VK_EXTX_PORTABILITY_SUBSET_EXTENSION_NAME
97#include "MoltenVK/vk_mvk_moltenvk.h"
98#endif
99
100struct zink_screen;
101
102struct zink_device_info {
103%for ext in extensions:
104   ${ext.guard_start()}
105   bool have_${ext.name_with_vendor()};
106   ${ext.guard_end()}
107%endfor
108
109   VkPhysicalDeviceFeatures2 feats;
110   VkPhysicalDeviceProperties props;
111   VkPhysicalDeviceMemoryProperties mem_props;
112
113%for ext in extensions:
114   ${ext.guard_start()}
115%if ext.feature_field is not None or ext.has_features:
116   VkPhysicalDevice${ext.name_in_camel_case()}Features${ext.vendor()} ${ext.field("feats")};
117%endif
118%if ext.has_properties:
119   VkPhysicalDevice${ext.name_in_camel_case()}Properties${ext.vendor()} ${ext.field("props")};
120%endif
121   ${ext.guard_end()}
122%endfor
123
124    const char *extensions[${len(extensions)}];
125    uint32_t num_extensions;
126};
127
128bool
129zink_get_physical_device_info(struct zink_screen *screen);
130
131#endif
132"""
133
134
135impl_code = """
136#include "zink_device_info.h"
137#include "zink_screen.h"
138
139bool
140zink_get_physical_device_info(struct zink_screen *screen)
141{
142   struct zink_device_info *info = &screen->info;
143%for ext in extensions:
144   ${ext.guard_start()}
145   bool support_${ext.name_with_vendor()} = false;
146   ${ext.guard_end()}
147%endfor
148   uint32_t num_extensions = 0;
149
150   vkGetPhysicalDeviceMemoryProperties(screen->pdev, &info->mem_props);
151
152   // enumerate device supported extensions
153   if (vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, NULL) == VK_SUCCESS) {
154      if (num_extensions > 0) {
155         VkExtensionProperties *extensions = MALLOC(sizeof(VkExtensionProperties) * num_extensions);
156         if (!extensions) goto fail;
157         vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, extensions);
158
159         for (uint32_t i = 0; i < num_extensions; ++i) {
160         %for ext in extensions:
161            ${ext.guard_start()}
162            if (!strcmp(extensions[i].extensionName, "${ext.name}")) {
163               support_${ext.name_with_vendor()} = true;
164            }
165            ${ext.guard_end()}
166         %endfor
167         }
168
169         FREE(extensions);
170      }
171   }
172
173   if (screen->vk_GetPhysicalDeviceFeatures2) {
174      // check for device extension features
175      info->feats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
176
177%for ext in extensions:
178%if ext.feature_field is not None or ext.has_features:
179      ${ext.guard_start()}
180      if (support_${ext.name_with_vendor()}) {
181         info->${ext.field("feats")}.sType = ${ext.stype("FEATURES")};
182         info->${ext.field("feats")}.pNext = info->feats.pNext;
183         info->feats.pNext = &info->${ext.field("feats")};
184      }
185      ${ext.guard_end()}
186%endif
187%endfor
188
189      screen->vk_GetPhysicalDeviceFeatures2(screen->pdev, &info->feats);
190
191%for ext in extensions:
192      ${ext.guard_start()}
193%if ext.feature_field is None:
194      info->have_${ext.name_with_vendor()} = support_${ext.name_with_vendor()};
195%else:
196      if (support_${ext.name_with_vendor()} && info->${ext.field("feats")}.${ext.feature_field}) {
197         info->have_${ext.name_with_vendor()} = true;
198      }
199%endif
200      ${ext.guard_end()}
201%endfor
202   } else {
203      vkGetPhysicalDeviceFeatures(screen->pdev, &info->feats.features);
204
205%for ext in extensions:
206      ${ext.guard_start()}
207%if ext.feature_field is None:
208      info->have_${ext.name_with_vendor()} = support_${ext.name_with_vendor()};
209%endif
210      ${ext.guard_end()}
211%endfor
212   }
213
214   // check for device properties
215   if (screen->vk_GetPhysicalDeviceProperties2) {
216      VkPhysicalDeviceProperties2 props = {};
217      props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
218
219%for ext in extensions:
220%if ext.has_properties:
221      ${ext.guard_start()}
222      if (info->have_${ext.name_with_vendor()}) {
223         info->${ext.field("props")}.sType = ${ext.stype("PROPERTIES")};
224         info->${ext.field("props")}.pNext = props.pNext;
225         props.pNext = &info->${ext.field("props")};
226      }
227      ${ext.guard_end()}
228%endif
229%endfor
230
231      screen->vk_GetPhysicalDeviceProperties2(screen->pdev, &props);
232      memcpy(&info->props, &props.properties, sizeof(info->props));
233   } else {
234      vkGetPhysicalDeviceProperties(screen->pdev, &info->props);
235   }
236
237   // generate extension list
238   num_extensions = 0;
239
240%for ext in extensions:
241   ${ext.guard_start()}
242   if (info->have_${ext.name_with_vendor()}) {
243       info->extensions[num_extensions++] = "${ext.name}";
244%if ext.is_required:
245   } else {
246       debug_printf("ZINK: ${ext.name} required!\\n");
247       goto fail;
248%endif
249   }
250   ${ext.guard_end()}
251%endfor
252
253   info->num_extensions = num_extensions;
254
255   return true;
256
257fail:
258   return false;
259}
260"""
261
262class Extension:
263    name           : str  = None
264    alias          : str  = None
265    is_required    : bool = False
266    has_properties : bool = False
267    has_features   : bool = False
268    feature_field  : str  = None
269    guard          : bool = False
270
271    def __init__(self, name, alias="", required=False, properties=False, features=False, have_feature=None, guard=False):
272        self.name = name
273        self.alias = alias
274        self.is_required = required
275        self.has_properties = properties
276        self.has_features = features
277        self.feature_field = have_feature
278        self.guard = guard
279
280        if alias == "" and (properties == True or have_feature is not None):
281            raise RuntimeError("alias must be available when properties/feature is used")
282
283    # e.g.: "VK_EXT_robustness2" -> "robustness2"
284    def pure_name(self):
285        return '_'.join(self.name.split('_')[2:])
286
287    # e.g.: "VK_EXT_robustness2" -> "EXT_robustness2"
288    def name_with_vendor(self):
289        return self.name[3:]
290
291    # e.g.: "VK_EXT_robustness2" -> "Robustness2"
292    def name_in_camel_case(self):
293        return "".join([x.title() for x in self.name.split('_')[2:]])
294
295    # e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS2_EXTENSION_NAME"
296    # do note that inconsistencies exist, i.e. we have
297    # VK_EXT_ROBUSTNESS_2_EXTENSION_NAME defined in the headers, but then
298    # we also have VK_KHR_MAINTENANCE1_EXTENSION_NAME
299    def extension_name(self):
300        return self.name.upper() + "_EXTENSION_NAME"
301
302    # generate a C string literal for the extension
303    def extension_name_literal(self):
304        return '"' + self.name + '"'
305
306    # get the field in zink_device_info that refers to the extension's
307    # feature/properties struct
308    # e.g. rb2_<suffix> for VK_EXT_robustness2
309    def field(self, suffix: str):
310        return self.alias + '_' + suffix
311
312    # the sType of the extension's struct
313    # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT
314    # for VK_EXT_transform_feedback and struct="FEATURES"
315    def stype(self, struct: str):
316        return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_"
317                + self.pure_name().upper()
318                + '_' + struct + '_'
319                + self.vendor())
320
321    # e.g. EXT in VK_EXT_robustness2
322    def vendor(self):
323        return self.name.split('_')[1]
324
325    # e.g. #if defined(VK_EXT_robustness2)
326    def guard_start(self):
327        if self.guard == False:
328            return ""
329        return ("#if defined("
330                + self.extension_name()
331                + ")")
332
333    # e.g. #endif // VK_EXT_robustness2
334    def guard_end(self):
335        if self.guard == False:
336            return ""
337        return ("#endif //"
338                + self.extension_name())
339
340
341def replace_code(code: str, replacement: dict):
342    for (k, v) in replacement.items():
343        code = code.replace(k, v)
344
345    return code
346
347
348if __name__ == "__main__":
349    try:
350        header_path = sys.argv[1]
351        impl_path = sys.argv[2]
352
353        header_path = path.abspath(header_path)
354        impl_path = path.abspath(impl_path)
355    except:
356        print("usage: %s <path to .h> <path to .c>" % sys.argv[0])
357        exit(1)
358
359    extensions = EXTENSIONS()
360    replacement = REPLACEMENTS()
361
362    with open(header_path, "w") as header_file:
363        header = Template(header_code).render(extensions=extensions).strip()
364        header = replace_code(header, replacement)
365        print(header, file=header_file)
366
367    with open(impl_path, "w") as impl_file:
368        impl = Template(impl_code).render(extensions=extensions).strip()
369        impl = replace_code(impl, replacement)
370        print(impl, file=impl_file)
371