aboutsummaryrefslogtreecommitdiffhomepage
path: root/registry/vkconventions.py
blob: 3597a37e0feb566e30570316f0a55178c8af2fae (plain)
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#!/usr/bin/env python3 -i
#
# Copyright 2013-2024 The Khronos Group Inc.
#
# SPDX-License-Identifier: Apache-2.0

# Working-group-specific style conventions,
# used in generation.

import re
import os

from spec_tools.conventions import ConventionsBase

# Modified from default implementation - see category_requires_validation() below
CATEGORIES_REQUIRING_VALIDATION = set(('handle', 'enum', 'bitmask'))

# Tokenize into "words" for structure types, approximately per spec "Implicit Valid Usage" section 2.7.2
# This first set is for things we recognize explicitly as words,
# as exceptions to the general regex.
# Ideally these would be listed in the spec as exceptions, as OpenXR does.
SPECIAL_WORDS = set((
    '16Bit',  # VkPhysicalDevice16BitStorageFeatures
    '2D',     # VkPhysicalDeviceImage2DViewOf3DFeaturesEXT
    '3D',     # VkPhysicalDeviceImage2DViewOf3DFeaturesEXT
    '8Bit',  # VkPhysicalDevice8BitStorageFeaturesKHR
    'AABB',  # VkGeometryAABBNV
    'ASTC',  # VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT
    'D3D12',  # VkD3D12FenceSubmitInfoKHR
    'Float16',  # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
    'ImagePipe',  # VkImagePipeSurfaceCreateInfoFUCHSIA
    'Int64',  # VkPhysicalDeviceShaderAtomicInt64FeaturesKHR
    'Int8',  # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
    'MacOS',  # VkMacOSSurfaceCreateInfoMVK
    'RGBA10X6', # VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT
    'Uint8',  # VkPhysicalDeviceIndexTypeUint8FeaturesEXT
    'Win32',  # VkWin32SurfaceCreateInfoKHR
))
# A regex to match any of the SPECIAL_WORDS
EXCEPTION_PATTERN = r'(?P<exception>{})'.format(
    '|'.join('(%s)' % re.escape(w) for w in SPECIAL_WORDS))
MAIN_RE = re.compile(
    # the negative lookahead is to prevent the all-caps pattern from being too greedy.
    r'({}|([0-9]+)|([A-Z][a-z]+)|([A-Z][A-Z]*(?![a-z])))'.format(EXCEPTION_PATTERN))


class VulkanConventions(ConventionsBase):
    @property
    def null(self):
        """Preferred spelling of NULL."""
        return '`NULL`'

    def formatVersion(self, name, apivariant, major, minor):
        """Mark up an API version name as a link in the spec."""
        version = f'{major}.{minor}'
        if apivariant == 'VKSC':
            # Vulkan SC has a different anchor pattern for version appendices
            if version == '1.0':
                return 'Vulkan SC 1.0'
            else:
                return f'<<versions-sc-{version}, Vulkan SC Version {version}>>'
        else:
            return f'<<versions-{version}, Vulkan Version {version}>>'

    def formatExtension(self, name):
        """Mark up an extension name as a link in the spec."""
        return f'apiext:{name}'

    @property
    def struct_macro(self):
        """Get the appropriate format macro for a structure.

        Primarily affects generated valid usage statements.
        """

        return 'slink:'

    @property
    def constFlagBits(self):
        """Returns True if static const flag bits should be generated, False if an enumerated type should be generated."""
        return False

    @property
    def structtype_member_name(self):
        """Return name of the structure type member"""
        return 'sType'

    @property
    def nextpointer_member_name(self):
        """Return name of the structure pointer chain member"""
        return 'pNext'

    @property
    def valid_pointer_prefix(self):
        """Return prefix to pointers which must themselves be valid"""
        return 'valid'

    def is_structure_type_member(self, paramtype, paramname):
        """Determine if member type and name match the structure type member."""
        return paramtype == 'VkStructureType' and paramname == self.structtype_member_name

    def is_nextpointer_member(self, paramtype, paramname):
        """Determine if member type and name match the next pointer chain member."""
        return paramtype == 'void' and paramname == self.nextpointer_member_name

    def generate_structure_type_from_name(self, structname):
        """Generate a structure type name, like VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO"""

        structure_type_parts = []
        # Tokenize into "words"
        for elem in MAIN_RE.findall(structname):
            word = elem[0]
            if word == 'Vk':
                structure_type_parts.append('VK_STRUCTURE_TYPE')
            else:
                structure_type_parts.append(word.upper())
        name = '_'.join(structure_type_parts)

        # The simple-minded rules need modification for some structure names
        subpats = [
            [ r'_H_(26[45])_',              r'_H\1_' ],
            [ r'_AV_1_',                    r'_AV1_' ],
            [ r'_VULKAN_([0-9])([0-9])_',   r'_VULKAN_\1_\2_' ],
            [ r'_VULKAN_SC_([0-9])([0-9])_',r'_VULKAN_SC_\1_\2_' ],
            [ r'_DIRECT_FB_',               r'_DIRECTFB_' ],
            [ r'_VULKAN_SC_10',             r'_VULKAN_SC_1_0' ],

        ]

        for subpat in subpats:
            name = re.sub(subpat[0], subpat[1], name)
        return name

    @property
    def warning_comment(self):
        """Return warning comment to be placed in header of generated Asciidoctor files"""
        return '// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry'

    @property
    def file_suffix(self):
        """Return suffix of generated Asciidoctor files"""
        return '.adoc'

    def api_name(self, spectype='api'):
        """Return API or specification name for citations in ref pages.ref
           pages should link to for

           spectype is the spec this refpage is for: 'api' is the Vulkan API
           Specification. Defaults to 'api'. If an unrecognized spectype is
           given, returns None.
        """
        if spectype == 'api' or spectype is None:
            return 'Vulkan'
        else:
            return None

    @property
    def api_prefix(self):
        """Return API token prefix"""
        return 'VK_'

    @property
    def write_contacts(self):
        """Return whether contact list should be written to extension appendices"""
        return True

    @property
    def write_refpage_include(self):
        """Return whether refpage include should be written to extension appendices"""
        return True

    @property
    def member_used_for_unique_vuid(self):
        """Return the member name used in the VUID-...-...-unique ID."""
        return self.structtype_member_name

    def is_externsync_command(self, protoname):
        """Returns True if the protoname element is an API command requiring
           external synchronization
        """
        return protoname is not None and 'vkCmd' in protoname

    def is_api_name(self, name):
        """Returns True if name is in the reserved API namespace.
        For Vulkan, these are names with a case-insensitive 'vk' prefix, or
        a 'PFN_vk' function pointer type prefix.
        """
        return name[0:2].lower() == 'vk' or name.startswith('PFN_vk')

    def specURL(self, spectype='api'):
        """Return public registry URL which ref pages should link to for the
           current all-extensions HTML specification, so xrefs in the
           asciidoc source that are not to ref pages can link into it
           instead. N.b. this may need to change on a per-refpage basis if
           there are multiple documents involved.
        """
        return 'https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html'

    @property
    def xml_api_name(self):
        """Return the name used in the default API XML registry for the default API"""
        return 'vulkan'

    @property
    def registry_path(self):
        """Return relpath to the default API XML registry in this project."""
        return 'xml/vk.xml'

    @property
    def specification_path(self):
        """Return relpath to the Asciidoctor specification sources in this project."""
        return '{generated}/meta'

    @property
    def special_use_section_anchor(self):
        """Return asciidoctor anchor name in the API Specification of the
        section describing extension special uses in detail."""
        return 'extendingvulkan-compatibility-specialuse'

    @property
    def extension_index_prefixes(self):
        """Return a list of extension prefixes used to group extension refpages."""
        return ['VK_KHR', 'VK_EXT', 'VK']

    @property
    def unified_flag_refpages(self):
        """Return True if Flags/FlagBits refpages are unified, False if
           they are separate.
        """
        return False

    @property
    def spec_reflow_path(self):
        """Return the path to the spec source folder to reflow"""
        return os.getcwd()

    @property
    def spec_no_reflow_dirs(self):
        """Return a set of directories not to automatically descend into
           when reflowing spec text
        """
        return ('scripts', 'style')

    @property
    def zero(self):
        return '`0`'

    def category_requires_validation(self, category):
        """Return True if the given type 'category' always requires validation.

        Overridden because Vulkan does not require "valid" text for basetype
        in the spec right now."""
        return category in CATEGORIES_REQUIRING_VALIDATION

    @property
    def should_skip_checking_codes(self):
        """Return True if more than the basic validation of return codes should
        be skipped for a command.

        Vulkan mostly relies on the validation layers rather than API
        builtin error checking, so these checks are not appropriate.

        For example, passing in a VkFormat parameter will not potentially
        generate a VK_ERROR_FORMAT_NOT_SUPPORTED code."""

        return True

    def extension_file_path(self, name):
        """Return file path to an extension appendix relative to a directory
           containing all such appendices.
           - name - extension name"""

        return f'{name}{self.file_suffix}'

    def valid_flag_bit(self, bitpos):
        """Return True if bitpos is an allowed numeric bit position for
           an API flag bit.

           Vulkan uses 32 bit Vk*Flags types, and assumes C compilers may
           cause Vk*FlagBits values with bit 31 set to result in a 64 bit
           enumerated type, so disallows such flags."""
        return bitpos >= 0 and bitpos < 31

    @property
    def extra_refpage_headers(self):
        """Return any extra text to add to refpage headers."""
        return 'include::{config}/attribs.adoc[]'

    @property
    def extra_refpage_body(self):
        """Return any extra text (following the title) for generated
           reference pages."""
        return 'include::{generated}/specattribs.adoc[]'


class VulkanSCConventions(VulkanConventions):

    def specURL(self, spectype='api'):
        """Return public registry URL which ref pages should link to for the
           current all-extensions HTML specification, so xrefs in the
           asciidoc source that are not to ref pages can link into it
           instead. N.b. this may need to change on a per-refpage basis if
           there are multiple documents involved.
        """
        return 'https://registry.khronos.org/vulkansc/specs/1.0-extensions/html/vkspec.html'

    @property
    def xml_api_name(self):
        """Return the name used in the default API XML registry for the default API"""
        return 'vulkansc'