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
26import os
27from collections import OrderedDict, namedtuple
28import xml.etree.ElementTree as et
29
30from mako.template import Template
31
32TEMPLATE_C = Template(COPYRIGHT + """
33/* This file generated from ${filename}, don't edit directly. */
34
35#include "vk_log.h"
36#include "vk_physical_device.h"
37#include "vk_util.h"
38
39static VkResult
40check_physical_device_features(struct vk_physical_device *physical_device,
41                               const VkPhysicalDeviceFeatures *supported,
42                               const VkPhysicalDeviceFeatures *enabled,
43                               const char *struct_name)
44{
45% for flag in pdev_features:
46   if (enabled->${flag} && !supported->${flag})
47      return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT,
48                       "%s.%s not supported", struct_name, "${flag}");
49% endfor
50
51   return VK_SUCCESS;
52}
53
54VkResult
55vk_physical_device_check_device_features(struct vk_physical_device *physical_device,
56                                         const VkDeviceCreateInfo *pCreateInfo)
57{
58   VkPhysicalDevice vk_physical_device =
59      vk_physical_device_to_handle(physical_device);
60
61   /* Query the device what kind of features are supported. */
62   VkPhysicalDeviceFeatures2 supported_features2 = {
63      .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
64   };
65
66% for f in features:
67   ${f.name} supported_${f.name} = { .pNext = NULL };
68% endfor
69
70   vk_foreach_struct_const(feat, pCreateInfo->pNext) {
71      VkBaseOutStructure *supported = NULL;
72      switch (feat->sType) {
73% for f in features:
74      case ${f.vk_type}:
75         supported = (VkBaseOutStructure *) &supported_${f.name};
76         break;
77% endfor
78      default:
79         break;
80      }
81
82      /* Not a feature struct. */
83      if (!supported)
84         continue;
85
86      /* Check for cycles in the list */
87      if (supported->pNext != NULL || supported->sType != 0)
88         return VK_ERROR_UNKNOWN;
89
90      supported->sType = feat->sType;
91      __vk_append_struct(&supported_features2, supported);
92   }
93
94   physical_device->dispatch_table.GetPhysicalDeviceFeatures2(
95      vk_physical_device, &supported_features2);
96
97   if (pCreateInfo->pEnabledFeatures) {
98      VkResult result =
99        check_physical_device_features(physical_device,
100                                       &supported_features2.features,
101                                       pCreateInfo->pEnabledFeatures,
102                                       "VkPhysicalDeviceFeatures");
103      if (result != VK_SUCCESS)
104         return result;
105   }
106
107   /* Iterate through additional feature structs */
108   vk_foreach_struct_const(feat, pCreateInfo->pNext) {
109      /* Check each feature boolean for given structure. */
110      switch (feat->sType) {
111      case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: {
112         const VkPhysicalDeviceFeatures2 *features2 = (const void *)feat;
113         VkResult result =
114            check_physical_device_features(physical_device,
115                                           &supported_features2.features,
116                                           &features2->features,
117                                           "VkPhysicalDeviceFeatures2.features");
118         if (result != VK_SUCCESS)
119            return result;
120        break;
121      }
122% for f in features:
123      case ${f.vk_type} : {
124         ${f.name} *a = &supported_${f.name};
125         ${f.name} *b = (${f.name} *) feat;
126% for flag in f.vk_flags:
127         if (b->${flag} && !a->${flag})
128            return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT,
129                             "%s.%s not supported", "${f.name}", "${flag}");
130% endfor
131         break;
132      }
133% endfor
134      default:
135         break;
136      }
137   } // for each extension structure
138   return VK_SUCCESS;
139}
140
141""", output_encoding='utf-8')
142
143Feature = namedtuple('Feature', 'name vk_type vk_flags')
144
145def get_pdev_features(doc):
146    for _type in doc.findall('./types/type'):
147        if _type.attrib.get('name') != 'VkPhysicalDeviceFeatures':
148            continue
149
150        flags = []
151
152        for p in _type.findall('./member'):
153            assert p.find('./type').text == 'VkBool32'
154            flags.append(p.find('./name').text)
155
156        return flags
157
158    return None
159
160def get_features(doc):
161    features = OrderedDict()
162
163    provisional_structs = set()
164
165    # we want to ignore struct types that are part of provisional extensions
166    for _extension in doc.findall('./extensions/extension'):
167        if _extension.attrib.get('provisional') != 'true':
168            continue
169        for p in _extension.findall('./require/type'):
170            provisional_structs.add(p.attrib.get('name'))
171
172    # parse all struct types where structextends VkPhysicalDeviceFeatures2
173    for _type in doc.findall('./types/type'):
174        if _type.attrib.get('category') != 'struct':
175            continue
176        if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo':
177            continue
178        if _type.attrib.get('name') in provisional_structs:
179            continue
180
181        # find Vulkan structure type
182        for elem in _type:
183            if "STRUCTURE_TYPE" in str(elem.attrib):
184                s_type = elem.attrib.get('values')
185
186        # collect a list of feature flags
187        flags = []
188
189        for p in _type.findall('./member'):
190            m_name = p.find('./name').text
191            if m_name == 'pNext':
192                pass
193            elif m_name == 'sType':
194                s_type = p.attrib.get('values')
195            else:
196                assert p.find('./type').text == 'VkBool32'
197                flags.append(m_name)
198
199        feat = Feature(name=_type.attrib.get('name'), vk_type=s_type, vk_flags=flags)
200        features[_type.attrib.get('name')] = feat
201
202    return features.values()
203
204def get_features_from_xml(xml_files):
205    pdev_features = None
206    features = []
207
208    for filename in xml_files:
209        doc = et.parse(filename)
210        features += get_features(doc)
211        if not pdev_features:
212            pdev_features = get_pdev_features(doc)
213
214    return pdev_features, features
215
216
217def main():
218    parser = argparse.ArgumentParser()
219    parser.add_argument('--out-c', required=True, help='Output C file.')
220    parser.add_argument('--xml',
221                        help='Vulkan API XML file.',
222                        required=True, action='append', dest='xml_files')
223    args = parser.parse_args()
224
225    pdev_features, features = get_features_from_xml(args.xml_files)
226
227    environment = {
228        'filename': os.path.basename(__file__),
229        'pdev_features': pdev_features,
230        'features': features,
231    }
232
233    try:
234        with open(args.out_c, 'wb') as f:
235            f.write(TEMPLATE_C.render(**environment))
236    except Exception:
237        # In the event there's an error, this imports some helpers from mako
238        # to print a useful stack trace and prints it, then exits with
239        # status 1, if python is run with debug; otherwise it just raises
240        # the exception
241        import sys
242        from mako import exceptions
243        print(exceptions.text_error_template().render(), file=sys.stderr)
244        sys.exit(1)
245
246if __name__ == '__main__':
247    main()
248