• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Requirements, 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    ('DeviceGeneratedCommandsFeaturesNV', 'deviceGeneratedCommands'): 'deviceGeneratedCommandsNV',
53}
54
55KNOWN_ALIASES = [
56    (['Vulkan11Features', '16BitStorageFeatures'], ['storageBuffer16BitAccess', 'uniformAndStorageBuffer16BitAccess', 'storagePushConstant16', 'storageInputOutput16']),
57    (['Vulkan11Features', 'MultiviewFeatures'], ['multiview', 'multiviewGeometryShader', 'multiviewTessellationShader']),
58    (['Vulkan11Features', 'VariablePointersFeatures'], ['variablePointersStorageBuffer', 'variablePointers']),
59    (['Vulkan11Features', 'ProtectedMemoryFeatures'], ['protectedMemory']),
60    (['Vulkan11Features', 'SamplerYcbcrConversionFeatures'], ['samplerYcbcrConversion']),
61    (['Vulkan11Features', 'ShaderDrawParametersFeatures'], ['shaderDrawParameters']),
62
63    (['Vulkan12Features', '8BitStorageFeatures'], ['storageBuffer8BitAccess', 'uniformAndStorageBuffer8BitAccess', 'storagePushConstant8']),
64    (['Vulkan12Features', 'ShaderAtomicInt64Features'], ['shaderBufferInt64Atomics', 'shaderSharedInt64Atomics']),
65    (['Vulkan12Features', 'ShaderFloat16Int8Features'], ['shaderFloat16', 'shaderInt8']),
66    (
67        ['Vulkan12Features', 'DescriptorIndexingFeatures'],
68        [
69            'shaderInputAttachmentArrayDynamicIndexing',
70            'shaderUniformTexelBufferArrayDynamicIndexing',
71            'shaderStorageTexelBufferArrayDynamicIndexing',
72            'shaderUniformBufferArrayNonUniformIndexing',
73            'shaderSampledImageArrayNonUniformIndexing',
74            'shaderStorageBufferArrayNonUniformIndexing',
75            'shaderStorageImageArrayNonUniformIndexing',
76            'shaderInputAttachmentArrayNonUniformIndexing',
77            'shaderUniformTexelBufferArrayNonUniformIndexing',
78            'shaderStorageTexelBufferArrayNonUniformIndexing',
79            'descriptorBindingUniformBufferUpdateAfterBind',
80            'descriptorBindingSampledImageUpdateAfterBind',
81            'descriptorBindingStorageImageUpdateAfterBind',
82            'descriptorBindingStorageBufferUpdateAfterBind',
83            'descriptorBindingUniformTexelBufferUpdateAfterBind',
84            'descriptorBindingStorageTexelBufferUpdateAfterBind',
85            'descriptorBindingUpdateUnusedWhilePending',
86            'descriptorBindingPartiallyBound',
87            'descriptorBindingVariableDescriptorCount',
88            'runtimeDescriptorArray',
89        ],
90    ),
91    (['Vulkan12Features', 'ScalarBlockLayoutFeatures'], ['scalarBlockLayout']),
92    (['Vulkan12Features', 'ImagelessFramebufferFeatures'], ['imagelessFramebuffer']),
93    (['Vulkan12Features', 'UniformBufferStandardLayoutFeatures'], ['uniformBufferStandardLayout']),
94    (['Vulkan12Features', 'ShaderSubgroupExtendedTypesFeatures'], ['shaderSubgroupExtendedTypes']),
95    (['Vulkan12Features', 'SeparateDepthStencilLayoutsFeatures'], ['separateDepthStencilLayouts']),
96    (['Vulkan12Features', 'HostQueryResetFeatures'], ['hostQueryReset']),
97    (['Vulkan12Features', 'TimelineSemaphoreFeatures'], ['timelineSemaphore']),
98    (['Vulkan12Features', 'BufferDeviceAddressFeatures', 'BufferDeviceAddressFeaturesEXT'], ['bufferDeviceAddress', 'bufferDeviceAddressMultiDevice']),
99    (['Vulkan12Features', 'BufferDeviceAddressFeatures'], ['bufferDeviceAddressCaptureReplay']),
100    (['Vulkan12Features', 'VulkanMemoryModelFeatures'], ['vulkanMemoryModel', 'vulkanMemoryModelDeviceScope', 'vulkanMemoryModelAvailabilityVisibilityChains']),
101
102    (['Vulkan13Features', 'ImageRobustnessFeatures'], ['robustImageAccess']),
103    (['Vulkan13Features', 'InlineUniformBlockFeatures'], ['inlineUniformBlock', 'descriptorBindingInlineUniformBlockUpdateAfterBind']),
104    (['Vulkan13Features', 'PipelineCreationCacheControlFeatures'], ['pipelineCreationCacheControl']),
105    (['Vulkan13Features', 'PrivateDataFeatures'], ['privateData']),
106    (['Vulkan13Features', 'ShaderDemoteToHelperInvocationFeatures'], ['shaderDemoteToHelperInvocation']),
107    (['Vulkan13Features', 'ShaderTerminateInvocationFeatures'], ['shaderTerminateInvocation']),
108    (['Vulkan13Features', 'SubgroupSizeControlFeatures'], ['subgroupSizeControl', 'computeFullSubgroups']),
109    (['Vulkan13Features', 'Synchronization2Features'], ['synchronization2']),
110    (['Vulkan13Features', 'TextureCompressionASTCHDRFeatures'], ['textureCompressionASTC_HDR']),
111    (['Vulkan13Features', 'ZeroInitializeWorkgroupMemoryFeatures'], ['shaderZeroInitializeWorkgroupMemory']),
112    (['Vulkan13Features', 'DynamicRenderingFeatures'], ['dynamicRendering']),
113    (['Vulkan13Features', 'ShaderIntegerDotProductFeatures'], ['shaderIntegerDotProduct']),
114    (['Vulkan13Features', 'Maintenance4Features'], ['maintenance4']),
115    (['Vulkan14Features', 'GlobalPriorityQueryFeatures'], ['globalPriorityQuery']),
116    (
117        ['Vulkan14Features', 'ShaderSubgroupRotateFeatures'],
118        ['shaderSubgroupRotate', 'shaderSubgroupRotateClustered'],
119    ),
120    (['Vulkan14Features', 'ShaderFloatControls2Features'], ['shaderFloatControls2']),
121    (['Vulkan14Features', 'ShaderExpectAssumeFeatures'], ['shaderExpectAssume']),
122    (
123        ['Vulkan14Features', 'LineRasterizationFeatures'],
124        [
125            'rectangularLines',
126            'bresenhamLines',
127            'smoothLines',
128            'stippledRectangularLines',
129            'stippledBresenhamLines',
130            'stippledSmoothLines',
131        ],
132    ),
133    (
134        ['Vulkan14Features', 'VertexAttributeDivisorFeatures'],
135        [
136            'vertexAttributeInstanceRateDivisor',
137            'vertexAttributeInstanceRateZeroDivisor',
138        ],
139    ),
140    (['Vulkan14Features', 'IndexTypeUint8Features'], ['indexTypeUint8']),
141    (['Vulkan14Features', 'DynamicRenderingLocalReadFeatures'], ['dynamicRenderingLocalRead']),
142    (['Vulkan14Features', 'Maintenance5Features'], ['maintenance5']),
143    (['Vulkan14Features', 'Maintenance6Features'], ['maintenance6']),
144    (['Vulkan14Features', 'PipelineProtectedAccessFeatures'], ['pipelineProtectedAccess']),
145    (['Vulkan14Features', 'PipelineRobustnessFeatures'], ['pipelineRobustness']),
146    (['Vulkan14Features', 'HostImageCopyFeatures'], ['hostImageCopy']),
147]
148
149for (feature_structs, features) in KNOWN_ALIASES:
150    for flag in features:
151        for f in feature_structs:
152            rename = (f, flag)
153            assert rename not in RENAMED_FEATURES, f"{rename} already exists in RENAMED_FEATURES"
154            RENAMED_FEATURES[rename] = flag
155
156def get_renamed_feature(c_type, feature):
157    return RENAMED_FEATURES.get((str_removeprefix(c_type, 'VkPhysicalDevice'), feature), feature)
158
159@dataclass
160class FeatureStruct:
161    reqs: Requirements
162    c_type: str
163    s_type: str
164    features: typing.List[str]
165
166    def condition(self, physical_dev):
167        conds = []
168        if self.reqs.core_version:
169            conds.append(physical_dev + '->properties.apiVersion >= ' +
170                         self.reqs.core_version.c_vk_version())
171        for ext in self.reqs.extensions:
172            conds.append(physical_dev + '->supported_extensions.' +
173                         ext.name[3:])
174        if not conds:
175            return None
176        return '(' + ' || '.join(conds) + ')'
177
178TEMPLATE_H = Template(COPYRIGHT + """
179/* This file generated from ${filename}, don't edit directly. */
180#ifndef VK_FEATURES_H
181#define VK_FEATURES_H
182
183#ifdef __cplusplus
184extern "C" {
185#endif
186
187struct vk_features {
188% for flag in all_flags:
189   bool ${flag};
190% endfor
191};
192
193void
194vk_set_physical_device_features(struct vk_features *all_features,
195                                const VkPhysicalDeviceFeatures2 *pFeatures);
196
197void
198vk_set_physical_device_features_1_0(struct vk_features *all_features,
199                                    const VkPhysicalDeviceFeatures *pFeatures);
200
201#ifdef __cplusplus
202}
203#endif
204
205#endif
206""")
207
208TEMPLATE_C = Template(COPYRIGHT + """
209/* This file generated from ${filename}, don't edit directly. */
210
211#include "vk_common_entrypoints.h"
212#include "vk_log.h"
213#include "vk_physical_device.h"
214#include "vk_physical_device_features.h"
215#include "vk_util.h"
216
217static VkResult
218check_physical_device_features(struct vk_physical_device *physical_device,
219                               const VkPhysicalDeviceFeatures *supported,
220                               const VkPhysicalDeviceFeatures *enabled,
221                               const char *struct_name)
222{
223% for flag in pdev_features:
224   if (enabled->${flag} && !supported->${flag})
225      return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT,
226                       "%s.%s not supported", struct_name, "${flag}");
227% endfor
228
229   return VK_SUCCESS;
230}
231
232VkResult
233vk_physical_device_check_device_features(struct vk_physical_device *physical_device,
234                                         const VkDeviceCreateInfo *pCreateInfo)
235{
236   VkPhysicalDevice vk_physical_device =
237      vk_physical_device_to_handle(physical_device);
238
239   /* Query the device what kind of features are supported. */
240   VkPhysicalDeviceFeatures2 supported_features2 = {
241      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
242   };
243
244% for f in feature_structs:
245   ${f.c_type} supported_${f.c_type} = { .pNext = NULL };
246% endfor
247
248   vk_foreach_struct_const(features, pCreateInfo->pNext) {
249      VkBaseOutStructure *supported = NULL;
250      switch (features->sType) {
251% for f in feature_structs:
252      case ${f.s_type}:
253% if f.condition("physical_device") is not None:
254         if (!${f.condition("physical_device")})
255            break;
256% endif
257         supported = (VkBaseOutStructure *) &supported_${f.c_type};
258         break;
259% endfor
260      default:
261         break;
262      }
263
264      /* Not a feature struct. */
265      if (!supported)
266         continue;
267
268      /* Check for cycles in the list */
269      if (supported->pNext != NULL || supported->sType != 0)
270         return VK_ERROR_UNKNOWN;
271
272      supported->sType = features->sType;
273      __vk_append_struct(&supported_features2, supported);
274   }
275
276   physical_device->dispatch_table.GetPhysicalDeviceFeatures2(
277      vk_physical_device, &supported_features2);
278
279   if (pCreateInfo->pEnabledFeatures) {
280      VkResult result =
281        check_physical_device_features(physical_device,
282                                       &supported_features2.features,
283                                       pCreateInfo->pEnabledFeatures,
284                                       "VkPhysicalDeviceFeatures");
285      if (result != VK_SUCCESS)
286         return result;
287   }
288
289   /* Iterate through additional feature structs */
290   vk_foreach_struct_const(features, pCreateInfo->pNext) {
291      /* Check each feature boolean for given structure. */
292      switch (features->sType) {
293      case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: {
294         const VkPhysicalDeviceFeatures2 *features2 = (const void *)features;
295         VkResult result =
296            check_physical_device_features(physical_device,
297                                           &supported_features2.features,
298                                           &features2->features,
299                                           "VkPhysicalDeviceFeatures2.features");
300         if (result != VK_SUCCESS)
301            return result;
302        break;
303      }
304% for f in feature_structs:
305      case ${f.s_type}: {
306% if f.condition("physical_device") is not None:
307         if (!${f.condition("physical_device")})
308            break;
309% endif
310         const ${f.c_type} *a = &supported_${f.c_type};
311         const ${f.c_type} *b = (const void *) features;
312% for flag in f.features:
313         if (b->${flag} && !a->${flag})
314            return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT,
315                             "%s.%s not supported", "${f.c_type}", "${flag}");
316% endfor
317         break;
318      }
319% endfor
320      default:
321         break;
322      }
323   } // for each extension structure
324   return VK_SUCCESS;
325}
326
327VKAPI_ATTR void VKAPI_CALL
328vk_common_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
329                                     VkPhysicalDeviceFeatures2 *pFeatures)
330{
331   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
332
333% for flag in pdev_features:
334   pFeatures->features.${flag} = pdevice->supported_features.${flag};
335% endfor
336
337   vk_foreach_struct(ext, pFeatures) {
338      switch (ext->sType) {
339% for f in feature_structs:
340      case ${f.s_type}: {
341         ${f.c_type} *features = (void *) ext;
342% for flag in f.features:
343         features->${flag} = pdevice->supported_features.${get_renamed_feature(f.c_type, flag)};
344% endfor
345         break;
346      }
347
348% endfor
349      default:
350         break;
351      }
352   }
353}
354
355void
356vk_set_physical_device_features(struct vk_features *all_features,
357                                const VkPhysicalDeviceFeatures2 *pFeatures)
358{
359   vk_foreach_struct_const(ext, pFeatures) {
360      switch (ext->sType) {
361      case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: {
362         const VkPhysicalDeviceFeatures2 *features = (const void *) ext;
363         vk_set_physical_device_features_1_0(all_features, &features->features);
364         break;
365      }
366
367% for f in feature_structs:
368      case ${f.s_type}: {
369         const ${f.c_type} *features = (const void *) ext;
370% for flag in f.features:
371         if (features->${flag})
372            all_features->${get_renamed_feature(f.c_type, flag)} = true;
373% endfor
374         break;
375      }
376
377% endfor
378      default:
379         break;
380      }
381   }
382}
383
384void
385vk_set_physical_device_features_1_0(struct vk_features *all_features,
386                                    const VkPhysicalDeviceFeatures *pFeatures)
387{
388% for flag in pdev_features:
389   if (pFeatures->${flag})
390      all_features->${flag} = true;
391% endfor
392}
393""")
394
395def get_pdev_features(doc):
396    _type = doc.find(".types/type[@name='VkPhysicalDeviceFeatures']")
397    if _type is not None:
398        flags = []
399        for p in _type.findall('./member'):
400            assert p.find('./type').text == 'VkBool32'
401            flags.append(p.find('./name').text)
402        return flags
403    return None
404
405def filter_api(elem, api):
406    if 'api' not in elem.attrib:
407        return True
408
409    return api in elem.attrib['api'].split(',')
410
411def get_feature_structs(doc, api, beta):
412    feature_structs = OrderedDict()
413
414    required = get_all_required(doc, 'type', api, beta)
415
416    # parse all struct types where structextends VkPhysicalDeviceFeatures2
417    for _type in doc.findall('./types/type[@category="struct"]'):
418        if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo':
419            continue
420        if _type.attrib['name'] not in required:
421            continue
422
423        reqs = required[_type.attrib['name']]
424        # Skip extensions with a define for now
425        guard = reqs.guard
426        if guard is not None and (guard != "VK_ENABLE_BETA_EXTENSIONS" or beta != "true"):
427            continue
428
429        # find Vulkan structure type
430        for elem in _type:
431            if "STRUCTURE_TYPE" in str(elem.attrib):
432                s_type = elem.attrib.get('values')
433
434        # collect a list of feature flags
435        flags = []
436
437        for p in _type.findall('./member'):
438            if not filter_api(p, api):
439                continue
440
441            m_name = p.find('./name').text
442            if m_name == 'pNext':
443                pass
444            elif m_name == 'sType':
445                s_type = p.attrib.get('values')
446            else:
447                assert p.find('./type').text == 'VkBool32'
448                flags.append(m_name)
449
450        feature_struct = FeatureStruct(reqs=reqs, c_type=_type.attrib.get('name'), s_type=s_type, features=flags)
451        feature_structs[feature_struct.c_type] = feature_struct
452
453    return feature_structs.values()
454
455def get_feature_structs_from_xml(xml_files, beta, api='vulkan'):
456    diagnostics = []
457
458    pdev_features = None
459    feature_structs = []
460
461    for filename in xml_files:
462        doc = et.parse(filename)
463        feature_structs += get_feature_structs(doc, api, beta)
464        if not pdev_features:
465            pdev_features = get_pdev_features(doc)
466
467    unused_renames = {**RENAMED_FEATURES}
468
469    features = OrderedDict()
470
471    for flag in pdev_features:
472        features[flag] = 'VkPhysicalDeviceFeatures'
473
474    for f in feature_structs:
475        for flag in f.features:
476            renamed_flag = get_renamed_feature(f.c_type, flag)
477            if renamed_flag not in features:
478                features[renamed_flag] = f.c_type
479            else:
480                a = str_removeprefix(features[renamed_flag], 'VkPhysicalDevice')
481                b = str_removeprefix(f.c_type, 'VkPhysicalDevice')
482                if (a, flag) not in RENAMED_FEATURES or (b, flag) not in RENAMED_FEATURES:
483                    diagnostics.append(f'{a} and {b} both define {flag}')
484
485            unused_renames.pop((str_removeprefix(f.c_type, 'VkPhysicalDevice'), flag), None)
486
487    for rename in unused_renames:
488        diagnostics.append(f'unused rename {rename}')
489
490    assert len(diagnostics) == 0, '\n'.join(diagnostics)
491
492    return pdev_features, feature_structs, features
493
494
495def main():
496    parser = argparse.ArgumentParser()
497    parser.add_argument('--out-c', required=True, help='Output C file.')
498    parser.add_argument('--out-h', required=True, help='Output H file.')
499    parser.add_argument('--beta', required=True, help='Enable beta extensions.')
500    parser.add_argument('--xml',
501                        help='Vulkan API XML file.',
502                        required=True, action='append', dest='xml_files')
503    args = parser.parse_args()
504
505    pdev_features, feature_structs, all_flags = get_feature_structs_from_xml(args.xml_files, args.beta)
506
507    environment = {
508        'filename': os.path.basename(__file__),
509        'pdev_features': pdev_features,
510        'feature_structs': feature_structs,
511        'all_flags': all_flags,
512        'get_renamed_feature': get_renamed_feature,
513    }
514
515    try:
516        with open(args.out_c, 'w', encoding='utf-8') as f:
517            f.write(TEMPLATE_C.render(**environment))
518        with open(args.out_h, 'w', encoding='utf-8') as f:
519            f.write(TEMPLATE_H.render(**environment))
520    except Exception:
521        # In the event there's an error, this uses some helpers from mako
522        # to print a useful stack trace and prints it, then exits with
523        # status 1, if python is run with debug; otherwise it just raises
524        # the exception
525        print(mako.exceptions.text_error_template().render(), file=sys.stderr)
526        sys.exit(1)
527
528if __name__ == '__main__':
529    main()
530