File: valid_enum_values_generator.py

package info (click to toggle)
vulkan-validationlayers 1.4.321.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,412 kB
  • sloc: cpp: 594,175; python: 11,321; sh: 24; makefile: 20; xml: 14
file content (169 lines) | stat: -rw-r--r-- 8,378 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2025 The Khronos Group Inc.
# Copyright (c) 2015-2025 Valve Corporation
# Copyright (c) 2015-2025 LunarG, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from collections import defaultdict
from base_generator import BaseGenerator
from generators.generator_utils import PlatformGuardHelper

class ValidEnumValuesOutputGenerator(BaseGenerator):
    def __init__(self):
        BaseGenerator.__init__(self)
        self.ignoreList = [
            'VkStructureType', # Structs are checked as there own thing
            'VkResult' # The spots it is used (VkBindMemoryStatusKHR, VkPresentInfoKHR) are really returrn values
        ]

    def generate(self):
        self.write(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
            // See {os.path.basename(__file__)} for modifications

            /***************************************************************************
            *
            * Copyright (c) 2015-2025 The Khronos Group Inc.
            * Copyright (c) 2015-2025 Valve Corporation
            * Copyright (c) 2015-2025 LunarG, Inc.
            *
            * Licensed under the Apache License, Version 2.0 (the "License");
            * you may not use this file except in compliance with the License.
            * You may obtain a copy of the License at
            *
            *     http://www.apache.org/licenses/LICENSE-2.0
            *
            * Unless required by applicable law or agreed to in writing, software
            * distributed under the License is distributed on an "AS IS" BASIS,
            * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
            * See the License for the specific language governing permissions and
            * limitations under the License.
            ****************************************************************************/\n''')
        self.write('// NOLINTBEGIN') # Wrap for clang-tidy to ignore

        if self.filename == 'valid_enum_values.h':
            self.generateHeader()
        elif self.filename == 'valid_enum_values.cpp':
            self.generateSource()
        else:
            self.write(f'\nFile name {self.filename} has no code to generate\n')

        self.write('// NOLINTEND') # Wrap for clang-tidy to ignore

    def generateHeader(self):
        out = []
        guard_helper = PlatformGuardHelper()
        for enum in [x for x in self.vk.enums.values() if x.name not in self.ignoreList and not x.returnedOnly]:
            out.extend(guard_helper.add_guard(enum.protect))
            out.append(f'template<> ValidValue stateless::Context::IsValidEnumValue({enum.name} value) const;\n')
        out.extend(guard_helper.add_guard(None))

        self.write("".join(out))

    def generateSource(self):
        out = []
        out.append('''
            #include "stateless/stateless_validation.h"
            #include <vulkan/vk_enum_string_helper.h>

            //  Checking for values is a 2 part process
            //    1. Check if is valid at all
            //    2. If invalid, spend more time to figure out how and what info to report to the user
            //
            //  While this might not seem ideal to compute the enabled extensions every time this function is called, the
            //  other solution would be to build a list at vkCreateDevice time of all the valid values. This adds much higher
            //  memory overhead.
            //
            //  Another key point to consider is being able to tell the user a value is invalid because it "doesn't exist" vs
            //  "forgot to enable an extension" is VERY important

            ''')
        guard_helper = PlatformGuardHelper()

        for enum in [x for x in self.vk.enums.values() if x.name not in self.ignoreList and not x.returnedOnly]:
            out.extend(guard_helper.add_guard(enum.protect, extra_newline=True))
            out.append(f'template<> ValidValue stateless::Context::IsValidEnumValue({enum.name} value) const {{\n')
            out.append('    switch (value) {\n')
            # If the field has same/subset extensions as enum, we count it as "core" for the struct
            coreEnums = [x for x in enum.fields if not x.extensions or (x.extensions and all(e in enum.extensions for e in x.extensions))]
            for coreEnum in coreEnums:
                out.append(f'    case {coreEnum.name}:\n')
            out.append('    return ValidValue::Valid;\n')

            # Build up list of expressions so case statements with same expression can have fallthrough
            expressionMap = defaultdict(list)
            for field in [x for x in enum.fields if len(x.extensions) > 0]:
                expression = []
                # Ignore the base extensions needed to use the enum, only focus on the field specific extensions
                for extension in [x for x in field.extensions if x not in enum.extensions]:
                    expression.append(f'IsExtEnabled(extensions.{extension.lower()})')
                if (len(expression) == 0):
                    continue
                expression = " || ".join(expression)
                expressionMap[expression].append(field.name)

            for expression, names in expressionMap.items():
                for name in names:
                    out.append(f'    case {name}:\n')
                out.append(f'return {expression} ? ValidValue::Valid : ValidValue::NoExtension;\n')

            out.append('''default:
                            return ValidValue::NotFound;
                        };
                    }
                ''')
            out.extend(guard_helper.add_guard(None, extra_newline=True))

        # For those that had an extension on field, provide a way to get it to print a useful error message out
        for enum in [x for x in self.vk.enums.values() if x.name not in self.ignoreList and not x.returnedOnly]:
            out.extend(guard_helper.add_guard(enum.protect, extra_newline=True))

            # Need empty functions to resolve all template variations
            if len(enum.fieldExtensions) <= len(enum.extensions):
                out.append(f'template<> vvl::Extensions stateless::Context::GetEnumExtensions({enum.name} value) const {{ return {{}}; }}\n')
                out.append(f'template<> const char* stateless::Context::DescribeEnum({enum.name} value) const {{ return nullptr; }}\n')
                out.extend(guard_helper.add_guard(None, extra_newline=True))
                continue

            out.append(f'template<> vvl::Extensions stateless::Context::GetEnumExtensions({enum.name} value) const {{\n')
            out.append('    switch (value) {\n')

            expressionMap = defaultdict(list)
            for field in [x for x in enum.fields if len(x.extensions) > 0]:
                expression = []
                for extension in [x for x in field.extensions if x not in enum.extensions]:
                    expression.append(f'vvl::Extension::_{extension}')
                if (len(expression) == 0):
                    continue
                expression = ", ".join(expression)
                expressionMap[expression].append(field.name)

            for expression, names in expressionMap.items():
                for name in names:
                    out.append(f'    case {name}:\n')
                out.append(f'return {{{expression}}};\n')

            out.append('''default:
                            return {};
                        };
                    }
                ''')

            out.append(f'template<> const char* stateless::Context::DescribeEnum({enum.name} value) const {{\n')
            out.append(f'   return string_{enum.name}(value);\n')
            out.append('}\n')
            out.extend(guard_helper.add_guard(None, extra_newline=True))

        self.write(''.join(out))