1COPYRIGHT=u"""
2/* Copyright © 2021 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23"""
24
25import argparse
26from collections import OrderedDict
27from dataclasses import dataclass
28import os
29import sys
30import typing
31import xml.etree.ElementTree as et
32
33import mako
34from mako.template import Template
35from vk_extensions import get_all_required, filter_api
36
37def str_removeprefix(s, prefix):
38    if s.startswith(prefix):
39        return s[len(prefix):]
40    return s
41
42RENAMED_FEATURES = {
43    # See https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17272#note_1446477 for details
44    ('BufferDeviceAddressFeaturesEXT', 'bufferDeviceAddressCaptureReplay'): 'bufferDeviceAddressCaptureReplayEXT',
45
46    ('MeshShaderFeaturesNV', 'taskShader'): 'taskShaderNV',
47    ('MeshShaderFeaturesNV', 'meshShader'): 'meshShaderNV',
48
49    ('CooperativeMatrixFeaturesNV', 'cooperativeMatrix'): 'cooperativeMatrixNV',
50    ('CooperativeMatrixFeaturesNV', 'cooperativeMatrixRobustBufferAccess'): 'cooperativeMatrixRobustBufferAccessNV',
51}
52
53KNOWN_ALIASES = [
54    (['Vulkan11Features', '16BitStorageFeatures'], ['storageBuffer16BitAccess', 'uniformAndStorageBuffer16BitAccess', 'storagePushConstant16', 'storageInputOutput16']),
55    (['Vulkan11Features', 'MultiviewFeatures'], ['multiview', 'multiviewGeometryShader', 'multiviewTessellationShader']),
56    (['Vulkan11Features', 'VariablePointersFeatures'], ['variablePointersStorageBuffer', 'variablePointers']),
57    (['Vulkan11Features', 'ProtectedMemoryFeatures'], ['protectedMemory']),
58    (['Vulkan11Features', 'SamplerYcbcrConversionFeatures'], ['samplerYcbcrConversion']),
59    (['Vulkan11Features', 'ShaderDrawParametersFeatures'], ['shaderDrawParameters']),
60
61    (['Vulkan12Features', '8BitStorageFeatures'], ['storageBuffer8BitAccess', 'uniformAndStorageBuffer8BitAccess', 'storagePushConstant8']),
62    (['Vulkan12Features', 'ShaderAtomicInt64Features'], ['shaderBufferInt64Atomics', 'shaderSharedInt64Atomics']),
63    (['Vulkan12Features', 'ShaderFloat16Int8Features'], ['shaderFloat16', 'shaderInt8']),
64    (
65        ['Vulkan12Features', 'DescriptorIndexingFeatures'],
66        [
67            'shaderInputAttachmentArrayDynamicIndexing',
68            'shaderUniformTexelBufferArrayDynamicIndexing',
69            'shaderStorageTexelBufferArrayDynamicIndexing',
70            'shaderUniformBufferArrayNonUniformIndexing',
71            'shaderSampledImageArrayNonUniformIndexing',
72            'shaderStorageBufferArrayNonUniformIndexing',
73            'shaderStorageImageArrayNonUniformIndexing',
74            'shaderInputAttachmentArrayNonUniformIndexing',
75            'shaderUniformTexelBufferArrayNonUniformIndexing',
76            'shaderStorageTexelBufferArrayNonUniformIndexing',
77            'descriptorBindingUniformBufferUpdateAfterBind',
78            'descriptorBindingSampledImageUpdateAfterBind',
79            'descriptorBindingStorageImageUpdateAfterBind',
80            'descriptorBindingStorageBufferUpdateAfterBind',
81            'descriptorBindingUniformTexelBufferUpdateAfterBind',
82            'descriptorBindingStorageTexelBufferUpdateAfterBind',
83            'descriptorBindingUpdateUnusedWhilePending',
84            'descriptorBindingPartiallyBound',
85            'descriptorBindingVariableDescriptorCount',
86            'runtimeDescriptorArray',
87        ],
88    ),
89    (['Vulkan12Features', 'ScalarBlockLayoutFeatures'], ['scalarBlockLayout']),
90    (['Vulkan12Features', 'ImagelessFramebufferFeatures'], ['imagelessFramebuffer']),
91    (['Vulkan12Features', 'UniformBufferStandardLayoutFeatures'], ['uniformBufferStandardLayout']),
92    (['Vulkan12Features', 'ShaderSubgroupExtendedTypesFeatures'], ['shaderSubgroupExtendedTypes']),
93    (['Vulkan12Features', 'SeparateDepthStencilLayoutsFeatures'], ['separateDepthStencilLayouts']),
94    (['Vulkan12Features', 'HostQueryResetFeatures'], ['hostQueryReset']),
95    (['Vulkan12Features', 'TimelineSemaphoreFeatures'], ['timelineSemaphore']),
96    (['Vulkan12Features', 'BufferDeviceAddressFeatures', 'BufferDeviceAddressFeaturesEXT'], ['bufferDeviceAddress', 'bufferDeviceAddressMultiDevice']),
97    (['Vulkan12Features', 'BufferDeviceAddressFeatures'], ['bufferDeviceAddressCaptureReplay']),
98    (['Vulkan12Features', 'VulkanMemoryModelFeatures'], ['vulkanMemoryModel', 'vulkanMemoryModelDeviceScope', 'vulkanMemoryModelAvailabilityVisibilityChains']),
99
100    (['Vulkan13Features', 'ImageRobustnessFeatures'], ['robustImageAccess']),
101    (['Vulkan13Features', 'InlineUniformBlockFeatures'], ['inlineUniformBlock', 'descriptorBindingInlineUniformBlockUpdateAfterBind']),
102    (['Vulkan13Features', 'PipelineCreationCacheControlFeatures'], ['pipelineCreationCacheControl']),
103    (['Vulkan13Features', 'PrivateDataFeatures'], ['privateData']),
104    (['Vulkan13Features', 'ShaderDemoteToHelperInvocationFeatures'], ['shaderDemoteToHelperInvocation']),
105    (['Vulkan13Features', 'ShaderTerminateInvocationFeatures'], ['shaderTerminateInvocation']),
106    (['Vulkan13Features', 'SubgroupSizeControlFeatures'], ['subgroupSizeControl', 'computeFullSubgroups']),
107    (['Vulkan13Features', 'Synchronization2Features'], ['synchronization2']),
108    (['Vulkan13Features', 'TextureCompressionASTCHDRFeatures'], ['textureCompressionASTC_HDR']),
109    (['Vulkan13Features', 'ZeroInitializeWorkgroupMemoryFeatures'], ['shaderZeroInitializeWorkgroupMemory']),
110    (['Vulkan13Features', 'DynamicRenderingFeatures'], ['dynamicRendering']),
111    (['Vulkan13Features', 'ShaderIntegerDotProductFeatures'], ['shaderIntegerDotProduct']),
112    (['Vulkan13Features', 'Maintenance4Features'], ['maintenance4']),
113]
114
115for (feature_structs, features) in KNOWN_ALIASES:
116    for flag in features:
117        for f in feature_structs:
118            rename = (f, flag)
119            assert rename not in RENAMED_FEATURES, f"{rename} already exists in RENAMED_FEATURES"
120            RENAMED_FEATURES[rename] = flag
121
122def get_renamed_feature(c_type, feature):
123    return RENAMED_FEATURES.get((str_removeprefix(c_type, 'VkPhysicalDevice'), feature), feature)
124
125@dataclass
126class FeatureStruct:
127    c_type: str
128    s_type: str
129    features: typing.List[str]
130
131TEMPLATE_H = Template(COPYRIGHT + """
132/* This file generated from ${filename}, don't edit directly. */
133#ifndef VK_FEATURES_H
134#define VK_FEATURES_H
135
136#ifdef __cplusplus
137extern "C" {
138#endif
139
140struct vk_features {
141% for flag in all_flags:
142   bool ${flag};
143% endfor
144};
145
146void
147vk_set_physical_device_features(struct vk_features *all_features,
148                                const VkPhysicalDeviceFeatures2 *pFeatures);
149
150void
151vk_set_physical_device_features_1_0(struct vk_features *all_features,
152                                    const VkPhysicalDeviceFeatures *pFeatures);
153
154#ifdef __cplusplus
155}
156#endif
157
158#endif
159""", output_encoding='utf-8')
160
161TEMPLATE_C = Template(COPYRIGHT + """
162/* This file generated from ${filename}, don't edit directly. */
163
164#include "vk_common_entrypoints.h"
165#include "vk_log.h"
166#include "vk_physical_device.h"
167#include "vk_physical_device_features.h"
168#include "vk_util.h"
169
170static VkResult
171check_physical_device_features(struct vk_physical_device *physical_device,
172                               const VkPhysicalDeviceFeatures *supported,
173                               const VkPhysicalDeviceFeatures *enabled,
174                               const char *struct_name)
175{
176% for flag in pdev_features:
177   if (enabled->${flag} && !supported->${flag})
178      return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT,
179                       "%s.%s not supported", struct_name, "${flag}");
180% endfor
181
182   return VK_SUCCESS;
183}
184
185VkResult
186vk_physical_device_check_device_features(struct vk_physical_device *physical_device,
187                                         const VkDeviceCreateInfo *pCreateInfo)
188{
189   VkPhysicalDevice vk_physical_device =
190      vk_physical_device_to_handle(physical_device);
191
192   /* Query the device what kind of features are supported. */
193   VkPhysicalDeviceFeatures2 supported_features2 = {
194      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
195   };
196
197% for f in feature_structs:
198   ${f.c_type} supported_${f.c_type} = { .pNext = NULL };
199% endfor
200
201   vk_foreach_struct_const(features, pCreateInfo->pNext) {
202      VkBaseOutStructure *supported = NULL;
203      switch (features->sType) {
204% for f in feature_structs:
205      case ${f.s_type}:
206         supported = (VkBaseOutStructure *) &supported_${f.c_type};
207         break;
208% endfor
209      default:
210         break;
211      }
212
213      /* Not a feature struct. */
214      if (!supported)
215         continue;
216
217      /* Check for cycles in the list */
218      if (supported->pNext != NULL || supported->sType != 0)
219         return VK_ERROR_UNKNOWN;
220
221      supported->sType = features->sType;
222      __vk_append_struct(&supported_features2, supported);
223   }
224
225   physical_device->dispatch_table.GetPhysicalDeviceFeatures2(
226      vk_physical_device, &supported_features2);
227
228   if (pCreateInfo->pEnabledFeatures) {
229      VkResult result =
230        check_physical_device_features(physical_device,
231                                       &supported_features2.features,
232                                       pCreateInfo->pEnabledFeatures,
233                                       "VkPhysicalDeviceFeatures");
234      if (result != VK_SUCCESS)
235         return result;
236   }
237
238   /* Iterate through additional feature structs */
239   vk_foreach_struct_const(features, pCreateInfo->pNext) {
240      /* Check each feature boolean for given structure. */
241      switch (features->sType) {
242      case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: {
243         const VkPhysicalDeviceFeatures2 *features2 = (const void *)features;
244         VkResult result =
245            check_physical_device_features(physical_device,
246                                           &supported_features2.features,
247                                           &features2->features,
248                                           "VkPhysicalDeviceFeatures2.features");
249         if (result != VK_SUCCESS)
250            return result;
251        break;
252      }
253% for f in feature_structs:
254      case ${f.s_type}: {
255         const ${f.c_type} *a = &supported_${f.c_type};
256         const ${f.c_type} *b = (const void *) features;
257% for flag in f.features:
258         if (b->${flag} && !a->${flag})
259            return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT,
260                             "%s.%s not supported", "${f.c_type}", "${flag}");
261% endfor
262         break;
263      }
264% endfor
265      default:
266         break;
267      }
268   } // for each extension structure
269   return VK_SUCCESS;
270}
271
272VKAPI_ATTR void VKAPI_CALL
273vk_common_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
274                                     VkPhysicalDeviceFeatures2 *pFeatures)
275{
276   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
277
278% for flag in pdev_features:
279   pFeatures->features.${flag} = pdevice->supported_features.${flag};
280% endfor
281
282   vk_foreach_struct(ext, pFeatures) {
283      switch (ext->sType) {
284% for f in feature_structs:
285      case ${f.s_type}: {
286         ${f.c_type} *features = (void *) ext;
287% for flag in f.features:
288         features->${flag} = pdevice->supported_features.${get_renamed_feature(f.c_type, flag)};
289% endfor
290         break;
291      }
292
293% endfor
294      default:
295         break;
296      }
297   }
298}
299
300void
301vk_set_physical_device_features(struct vk_features *all_features,
302                                const VkPhysicalDeviceFeatures2 *pFeatures)
303{
304   vk_foreach_struct_const(ext, pFeatures) {
305      switch (ext->sType) {
306      case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: {
307         const VkPhysicalDeviceFeatures2 *features = (const void *) ext;
308         vk_set_physical_device_features_1_0(all_features, &features->features);
309         break;
310      }
311
312% for f in feature_structs:
313      case ${f.s_type}: {
314         const ${f.c_type} *features = (const void *) ext;
315% for flag in f.features:
316         if (features->${flag})
317            all_features->${get_renamed_feature(f.c_type, flag)} = true;
318% endfor
319         break;
320      }
321
322% endfor
323      default:
324         break;
325      }
326   }
327}
328
329void
330vk_set_physical_device_features_1_0(struct vk_features *all_features,
331                                    const VkPhysicalDeviceFeatures *pFeatures)
332{
333% for flag in pdev_features:
334   if (pFeatures->${flag})
335      all_features->${flag} = true;
336% endfor
337}
338""", output_encoding='utf-8')
339
340def get_pdev_features(doc):
341    _type = doc.find(".types/type[@name='VkPhysicalDeviceFeatures']")
342    if _type is not None:
343        flags = []
344        for p in _type.findall('./member'):
345            assert p.find('./type').text == 'VkBool32'
346            flags.append(p.find('./name').text)
347        return flags
348    return None
349
350def filter_api(elem, api):
351    if 'api' not in elem.attrib:
352        return True
353
354    return api in elem.attrib['api'].split(',')
355
356def get_feature_structs(doc, api, beta):
357    feature_structs = OrderedDict()
358
359    required = get_all_required(doc, 'type', api, beta)
360
361    # parse all struct types where structextends VkPhysicalDeviceFeatures2
362    for _type in doc.findall('./types/type[@category="struct"]'):
363        if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo':
364            continue
365        if _type.attrib['name'] not in required:
366            continue
367
368        # Skip extensions with a define for now
369        guard = required[_type.attrib['name']].guard
370        if guard is not None and (guard != "VK_ENABLE_BETA_EXTENSIONS" or not beta):
371            continue
372
373        # find Vulkan structure type
374        for elem in _type:
375            if "STRUCTURE_TYPE" in str(elem.attrib):
376                s_type = elem.attrib.get('values')
377
378        # collect a list of feature flags
379        flags = []
380
381        for p in _type.findall('./member'):
382            if not filter_api(p, api):
383                continue
384
385            m_name = p.find('./name').text
386            if m_name == 'pNext':
387                pass
388            elif m_name == 'sType':
389                s_type = p.attrib.get('values')
390            else:
391                assert p.find('./type').text == 'VkBool32'
392                flags.append(m_name)
393
394        feature_struct = FeatureStruct(c_type=_type.attrib.get('name'), s_type=s_type, features=flags)
395        feature_structs[feature_struct.c_type] = feature_struct
396
397    return feature_structs.values()
398
399def get_feature_structs_from_xml(xml_files, beta, api='vulkan'):
400    diagnostics = []
401
402    pdev_features = None
403    feature_structs = []
404
405    for filename in xml_files:
406        doc = et.parse(filename)
407        feature_structs += get_feature_structs(doc, api, beta)
408        if not pdev_features:
409            pdev_features = get_pdev_features(doc)
410
411    unused_renames = {**RENAMED_FEATURES}
412
413    features = OrderedDict()
414
415    for flag in pdev_features:
416        features[flag] = 'VkPhysicalDeviceFeatures'
417
418    for f in feature_structs:
419        for flag in f.features:
420            renamed_flag = get_renamed_feature(f.c_type, flag)
421            if renamed_flag not in features:
422                features[renamed_flag] = f.c_type
423            else:
424                a = str_removeprefix(features[renamed_flag], 'VkPhysicalDevice')
425                b = str_removeprefix(f.c_type, 'VkPhysicalDevice')
426                if (a, flag) not in RENAMED_FEATURES or (b, flag) not in RENAMED_FEATURES:
427                    diagnostics.append(f'{a} and {b} both define {flag}')
428
429            unused_renames.pop((str_removeprefix(f.c_type, 'VkPhysicalDevice'), flag), None)
430
431    for rename in unused_renames:
432        diagnostics.append(f'unused rename {rename}')
433
434    assert len(diagnostics) == 0, '\n'.join(diagnostics)
435
436    return pdev_features, feature_structs, features
437
438
439def main():
440    parser = argparse.ArgumentParser()
441    parser.add_argument('--out-c', required=True, help='Output C file.')
442    parser.add_argument('--out-h', required=True, help='Output H file.')
443    parser.add_argument('--beta', required=True, help='Enable beta extensions.')
444    parser.add_argument('--xml',
445                        help='Vulkan API XML file.',
446                        required=True, action='append', dest='xml_files')
447    args = parser.parse_args()
448
449    pdev_features, feature_structs, all_flags = get_feature_structs_from_xml(args.xml_files, args.beta)
450
451    environment = {
452        'filename': os.path.basename(__file__),
453        'pdev_features': pdev_features,
454        'feature_structs': feature_structs,
455        'all_flags': all_flags,
456        'get_renamed_feature': get_renamed_feature,
457    }
458
459    try:
460        with open(args.out_c, 'wb') as f:
461            f.write(TEMPLATE_C.render(**environment))
462        with open(args.out_h, 'wb') as f:
463            f.write(TEMPLATE_H.render(**environment))
464    except Exception:
465        # In the event there's an error, this uses some helpers from mako
466        # to print a useful stack trace and prints it, then exits with
467        # status 1, if python is run with debug; otherwise it just raises
468        # the exception
469        print(mako.exceptions.text_error_template().render(), file=sys.stderr)
470        sys.exit(1)
471
472if __name__ == '__main__':
473    main()
474