1e5c31af7Sopenharmony_ci#!/usr/bin/python3 -i
2e5c31af7Sopenharmony_ci#
3e5c31af7Sopenharmony_ci# Copyright 2013-2024 The Khronos Group Inc.
4e5c31af7Sopenharmony_ci#
5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0
6e5c31af7Sopenharmony_ci
7e5c31af7Sopenharmony_ci# Working-group-specific style conventions,
8e5c31af7Sopenharmony_ci# used in generation.
9e5c31af7Sopenharmony_ci
10e5c31af7Sopenharmony_ciimport re
11e5c31af7Sopenharmony_ciimport os
12e5c31af7Sopenharmony_ci
13e5c31af7Sopenharmony_cifrom spec_tools.conventions import ConventionsBase
14e5c31af7Sopenharmony_ci
15e5c31af7Sopenharmony_ci# Modified from default implementation - see category_requires_validation() below
16e5c31af7Sopenharmony_ciCATEGORIES_REQUIRING_VALIDATION = set(('handle', 'enum', 'bitmask'))
17e5c31af7Sopenharmony_ci
18e5c31af7Sopenharmony_ci# Tokenize into "words" for structure types, approximately per spec "Implicit Valid Usage" section 2.7.2
19e5c31af7Sopenharmony_ci# This first set is for things we recognize explicitly as words,
20e5c31af7Sopenharmony_ci# as exceptions to the general regex.
21e5c31af7Sopenharmony_ci# Ideally these would be listed in the spec as exceptions, as OpenXR does.
22e5c31af7Sopenharmony_ciSPECIAL_WORDS = set((
23e5c31af7Sopenharmony_ci    '16Bit',  # VkPhysicalDevice16BitStorageFeatures
24e5c31af7Sopenharmony_ci    '2D',     # VkPhysicalDeviceImage2DViewOf3DFeaturesEXT
25e5c31af7Sopenharmony_ci    '3D',     # VkPhysicalDeviceImage2DViewOf3DFeaturesEXT
26e5c31af7Sopenharmony_ci    '8Bit',  # VkPhysicalDevice8BitStorageFeaturesKHR
27e5c31af7Sopenharmony_ci    'AABB',  # VkGeometryAABBNV
28e5c31af7Sopenharmony_ci    'ASTC',  # VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT
29e5c31af7Sopenharmony_ci    'D3D12',  # VkD3D12FenceSubmitInfoKHR
30e5c31af7Sopenharmony_ci    'Float16',  # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
31e5c31af7Sopenharmony_ci    'ImagePipe',  # VkImagePipeSurfaceCreateInfoFUCHSIA
32e5c31af7Sopenharmony_ci    'Int64',  # VkPhysicalDeviceShaderAtomicInt64FeaturesKHR
33e5c31af7Sopenharmony_ci    'Int8',  # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
34e5c31af7Sopenharmony_ci    'MacOS',  # VkMacOSSurfaceCreateInfoMVK
35e5c31af7Sopenharmony_ci    'RGBA10X6', # VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT
36e5c31af7Sopenharmony_ci    'Uint8',  # VkPhysicalDeviceIndexTypeUint8FeaturesEXT
37e5c31af7Sopenharmony_ci    'Win32',  # VkWin32SurfaceCreateInfoKHR
38e5c31af7Sopenharmony_ci))
39e5c31af7Sopenharmony_ci# A regex to match any of the SPECIAL_WORDS
40e5c31af7Sopenharmony_ciEXCEPTION_PATTERN = r'(?P<exception>{})'.format(
41e5c31af7Sopenharmony_ci    '|'.join('(%s)' % re.escape(w) for w in SPECIAL_WORDS))
42e5c31af7Sopenharmony_ciMAIN_RE = re.compile(
43e5c31af7Sopenharmony_ci    # the negative lookahead is to prevent the all-caps pattern from being too greedy.
44e5c31af7Sopenharmony_ci    r'({}|([0-9]+)|([A-Z][a-z]+)|([A-Z][A-Z]*(?![a-z])))'.format(EXCEPTION_PATTERN))
45e5c31af7Sopenharmony_ci
46e5c31af7Sopenharmony_ci
47e5c31af7Sopenharmony_ciclass VulkanConventions(ConventionsBase):
48e5c31af7Sopenharmony_ci    @property
49e5c31af7Sopenharmony_ci    def null(self):
50e5c31af7Sopenharmony_ci        """Preferred spelling of NULL."""
51e5c31af7Sopenharmony_ci        return '`NULL`'
52e5c31af7Sopenharmony_ci
53e5c31af7Sopenharmony_ci    def formatVersion(self, name, apivariant, major, minor):
54e5c31af7Sopenharmony_ci        """Mark up an API version name as a link in the spec."""
55e5c31af7Sopenharmony_ci        version = f'{major}.{minor}'
56e5c31af7Sopenharmony_ci        if apivariant == 'VKSC':
57e5c31af7Sopenharmony_ci            # Vulkan SC has a different anchor pattern for version appendices
58e5c31af7Sopenharmony_ci            if version == '1.0':
59e5c31af7Sopenharmony_ci                return 'Vulkan SC 1.0'
60e5c31af7Sopenharmony_ci            else:
61e5c31af7Sopenharmony_ci                return f'<<versions-sc-{version}, Version SC {version}>>'
62e5c31af7Sopenharmony_ci        else:
63e5c31af7Sopenharmony_ci            return f'<<versions-{version}, Version {version}>>'
64e5c31af7Sopenharmony_ci
65e5c31af7Sopenharmony_ci    def formatExtension(self, name):
66e5c31af7Sopenharmony_ci        """Mark up an extension name as a link in the spec."""
67e5c31af7Sopenharmony_ci        return f'apiext:{name}'
68e5c31af7Sopenharmony_ci
69e5c31af7Sopenharmony_ci    @property
70e5c31af7Sopenharmony_ci    def struct_macro(self):
71e5c31af7Sopenharmony_ci        """Get the appropriate format macro for a structure.
72e5c31af7Sopenharmony_ci
73e5c31af7Sopenharmony_ci        Primarily affects generated valid usage statements.
74e5c31af7Sopenharmony_ci        """
75e5c31af7Sopenharmony_ci
76e5c31af7Sopenharmony_ci        return 'slink:'
77e5c31af7Sopenharmony_ci
78e5c31af7Sopenharmony_ci    @property
79e5c31af7Sopenharmony_ci    def constFlagBits(self):
80e5c31af7Sopenharmony_ci        """Returns True if static const flag bits should be generated, False if an enumerated type should be generated."""
81e5c31af7Sopenharmony_ci        return False
82e5c31af7Sopenharmony_ci
83e5c31af7Sopenharmony_ci    @property
84e5c31af7Sopenharmony_ci    def structtype_member_name(self):
85e5c31af7Sopenharmony_ci        """Return name of the structure type member"""
86e5c31af7Sopenharmony_ci        return 'sType'
87e5c31af7Sopenharmony_ci
88e5c31af7Sopenharmony_ci    @property
89e5c31af7Sopenharmony_ci    def nextpointer_member_name(self):
90e5c31af7Sopenharmony_ci        """Return name of the structure pointer chain member"""
91e5c31af7Sopenharmony_ci        return 'pNext'
92e5c31af7Sopenharmony_ci
93e5c31af7Sopenharmony_ci    @property
94e5c31af7Sopenharmony_ci    def valid_pointer_prefix(self):
95e5c31af7Sopenharmony_ci        """Return prefix to pointers which must themselves be valid"""
96e5c31af7Sopenharmony_ci        return 'valid'
97e5c31af7Sopenharmony_ci
98e5c31af7Sopenharmony_ci    def is_structure_type_member(self, paramtype, paramname):
99e5c31af7Sopenharmony_ci        """Determine if member type and name match the structure type member."""
100e5c31af7Sopenharmony_ci        return paramtype == 'VkStructureType' and paramname == self.structtype_member_name
101e5c31af7Sopenharmony_ci
102e5c31af7Sopenharmony_ci    def is_nextpointer_member(self, paramtype, paramname):
103e5c31af7Sopenharmony_ci        """Determine if member type and name match the next pointer chain member."""
104e5c31af7Sopenharmony_ci        return paramtype == 'void' and paramname == self.nextpointer_member_name
105e5c31af7Sopenharmony_ci
106e5c31af7Sopenharmony_ci    def generate_structure_type_from_name(self, structname):
107e5c31af7Sopenharmony_ci        """Generate a structure type name, like VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO"""
108e5c31af7Sopenharmony_ci
109e5c31af7Sopenharmony_ci        structure_type_parts = []
110e5c31af7Sopenharmony_ci        # Tokenize into "words"
111e5c31af7Sopenharmony_ci        for elem in MAIN_RE.findall(structname):
112e5c31af7Sopenharmony_ci            word = elem[0]
113e5c31af7Sopenharmony_ci            if word == 'Vk':
114e5c31af7Sopenharmony_ci                structure_type_parts.append('VK_STRUCTURE_TYPE')
115e5c31af7Sopenharmony_ci            else:
116e5c31af7Sopenharmony_ci                structure_type_parts.append(word.upper())
117e5c31af7Sopenharmony_ci        name = '_'.join(structure_type_parts)
118e5c31af7Sopenharmony_ci
119e5c31af7Sopenharmony_ci        # The simple-minded rules need modification for some structure names
120e5c31af7Sopenharmony_ci        subpats = [
121e5c31af7Sopenharmony_ci            [ r'_H_(26[45])_',              r'_H\1_' ],
122e5c31af7Sopenharmony_ci            [ r'_VULKAN_([0-9])([0-9])_',   r'_VULKAN_\1_\2_' ],
123e5c31af7Sopenharmony_ci            [ r'_VULKAN_SC_([0-9])([0-9])_',r'_VULKAN_SC_\1_\2_' ],
124e5c31af7Sopenharmony_ci            [ r'_DIRECT_FB_',               r'_DIRECTFB_' ],
125e5c31af7Sopenharmony_ci            [ r'_VULKAN_SC_10',             r'_VULKAN_SC_1_0' ],
126e5c31af7Sopenharmony_ci
127e5c31af7Sopenharmony_ci        ]
128e5c31af7Sopenharmony_ci
129e5c31af7Sopenharmony_ci        for subpat in subpats:
130e5c31af7Sopenharmony_ci            name = re.sub(subpat[0], subpat[1], name)
131e5c31af7Sopenharmony_ci        return name
132e5c31af7Sopenharmony_ci
133e5c31af7Sopenharmony_ci    @property
134e5c31af7Sopenharmony_ci    def warning_comment(self):
135e5c31af7Sopenharmony_ci        """Return warning comment to be placed in header of generated Asciidoctor files"""
136e5c31af7Sopenharmony_ci        return '// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry'
137e5c31af7Sopenharmony_ci
138e5c31af7Sopenharmony_ci    @property
139e5c31af7Sopenharmony_ci    def file_suffix(self):
140e5c31af7Sopenharmony_ci        """Return suffix of generated Asciidoctor files"""
141e5c31af7Sopenharmony_ci        return '.adoc'
142e5c31af7Sopenharmony_ci
143e5c31af7Sopenharmony_ci    def api_name(self, spectype='api'):
144e5c31af7Sopenharmony_ci        """Return API or specification name for citations in ref pages.ref
145e5c31af7Sopenharmony_ci           pages should link to for
146e5c31af7Sopenharmony_ci
147e5c31af7Sopenharmony_ci           spectype is the spec this refpage is for: 'api' is the Vulkan API
148e5c31af7Sopenharmony_ci           Specification. Defaults to 'api'. If an unrecognized spectype is
149e5c31af7Sopenharmony_ci           given, returns None.
150e5c31af7Sopenharmony_ci        """
151e5c31af7Sopenharmony_ci        if spectype == 'api' or spectype is None:
152e5c31af7Sopenharmony_ci            return 'Vulkan'
153e5c31af7Sopenharmony_ci        else:
154e5c31af7Sopenharmony_ci            return None
155e5c31af7Sopenharmony_ci
156e5c31af7Sopenharmony_ci    @property
157e5c31af7Sopenharmony_ci    def api_prefix(self):
158e5c31af7Sopenharmony_ci        """Return API token prefix"""
159e5c31af7Sopenharmony_ci        return 'VK_'
160e5c31af7Sopenharmony_ci
161e5c31af7Sopenharmony_ci    @property
162e5c31af7Sopenharmony_ci    def write_contacts(self):
163e5c31af7Sopenharmony_ci        """Return whether contact list should be written to extension appendices"""
164e5c31af7Sopenharmony_ci        return True
165e5c31af7Sopenharmony_ci
166e5c31af7Sopenharmony_ci    @property
167e5c31af7Sopenharmony_ci    def write_refpage_include(self):
168e5c31af7Sopenharmony_ci        """Return whether refpage include should be written to extension appendices"""
169e5c31af7Sopenharmony_ci        return True
170e5c31af7Sopenharmony_ci
171e5c31af7Sopenharmony_ci    @property
172e5c31af7Sopenharmony_ci    def member_used_for_unique_vuid(self):
173e5c31af7Sopenharmony_ci        """Return the member name used in the VUID-...-...-unique ID."""
174e5c31af7Sopenharmony_ci        return self.structtype_member_name
175e5c31af7Sopenharmony_ci
176e5c31af7Sopenharmony_ci    def is_externsync_command(self, protoname):
177e5c31af7Sopenharmony_ci        """Returns True if the protoname element is an API command requiring
178e5c31af7Sopenharmony_ci           external synchronization
179e5c31af7Sopenharmony_ci        """
180e5c31af7Sopenharmony_ci        return protoname is not None and 'vkCmd' in protoname
181e5c31af7Sopenharmony_ci
182e5c31af7Sopenharmony_ci    def is_api_name(self, name):
183e5c31af7Sopenharmony_ci        """Returns True if name is in the reserved API namespace.
184e5c31af7Sopenharmony_ci        For Vulkan, these are names with a case-insensitive 'vk' prefix, or
185e5c31af7Sopenharmony_ci        a 'PFN_vk' function pointer type prefix.
186e5c31af7Sopenharmony_ci        """
187e5c31af7Sopenharmony_ci        return name[0:2].lower() == 'vk' or name[0:6] == 'PFN_vk'
188e5c31af7Sopenharmony_ci
189e5c31af7Sopenharmony_ci    def specURL(self, spectype='api'):
190e5c31af7Sopenharmony_ci        """Return public registry URL which ref pages should link to for the
191e5c31af7Sopenharmony_ci           current all-extensions HTML specification, so xrefs in the
192e5c31af7Sopenharmony_ci           asciidoc source that are not to ref pages can link into it
193e5c31af7Sopenharmony_ci           instead. N.b. this may need to change on a per-refpage basis if
194e5c31af7Sopenharmony_ci           there are multiple documents involved.
195e5c31af7Sopenharmony_ci        """
196e5c31af7Sopenharmony_ci        return 'https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html'
197e5c31af7Sopenharmony_ci
198e5c31af7Sopenharmony_ci    @property
199e5c31af7Sopenharmony_ci    def xml_api_name(self):
200e5c31af7Sopenharmony_ci        """Return the name used in the default API XML registry for the default API"""
201e5c31af7Sopenharmony_ci        return 'vulkan'
202e5c31af7Sopenharmony_ci
203e5c31af7Sopenharmony_ci    @property
204e5c31af7Sopenharmony_ci    def registry_path(self):
205e5c31af7Sopenharmony_ci        """Return relpath to the default API XML registry in this project."""
206e5c31af7Sopenharmony_ci        return 'xml/vk.xml'
207e5c31af7Sopenharmony_ci
208e5c31af7Sopenharmony_ci    @property
209e5c31af7Sopenharmony_ci    def specification_path(self):
210e5c31af7Sopenharmony_ci        """Return relpath to the Asciidoctor specification sources in this project."""
211e5c31af7Sopenharmony_ci        return '{generated}/meta'
212e5c31af7Sopenharmony_ci
213e5c31af7Sopenharmony_ci    @property
214e5c31af7Sopenharmony_ci    def special_use_section_anchor(self):
215e5c31af7Sopenharmony_ci        """Return asciidoctor anchor name in the API Specification of the
216e5c31af7Sopenharmony_ci        section describing extension special uses in detail."""
217e5c31af7Sopenharmony_ci        return 'extendingvulkan-compatibility-specialuse'
218e5c31af7Sopenharmony_ci
219e5c31af7Sopenharmony_ci    @property
220e5c31af7Sopenharmony_ci    def extension_index_prefixes(self):
221e5c31af7Sopenharmony_ci        """Return a list of extension prefixes used to group extension refpages."""
222e5c31af7Sopenharmony_ci        return ['VK_KHR', 'VK_EXT', 'VK']
223e5c31af7Sopenharmony_ci
224e5c31af7Sopenharmony_ci    @property
225e5c31af7Sopenharmony_ci    def unified_flag_refpages(self):
226e5c31af7Sopenharmony_ci        """Return True if Flags/FlagBits refpages are unified, False if
227e5c31af7Sopenharmony_ci           they are separate.
228e5c31af7Sopenharmony_ci        """
229e5c31af7Sopenharmony_ci        return False
230e5c31af7Sopenharmony_ci
231e5c31af7Sopenharmony_ci    @property
232e5c31af7Sopenharmony_ci    def spec_reflow_path(self):
233e5c31af7Sopenharmony_ci        """Return the path to the spec source folder to reflow"""
234e5c31af7Sopenharmony_ci        return os.getcwd()
235e5c31af7Sopenharmony_ci
236e5c31af7Sopenharmony_ci    @property
237e5c31af7Sopenharmony_ci    def spec_no_reflow_dirs(self):
238e5c31af7Sopenharmony_ci        """Return a set of directories not to automatically descend into
239e5c31af7Sopenharmony_ci           when reflowing spec text
240e5c31af7Sopenharmony_ci        """
241e5c31af7Sopenharmony_ci        return ('scripts', 'style')
242e5c31af7Sopenharmony_ci
243e5c31af7Sopenharmony_ci    @property
244e5c31af7Sopenharmony_ci    def zero(self):
245e5c31af7Sopenharmony_ci        return '`0`'
246e5c31af7Sopenharmony_ci
247e5c31af7Sopenharmony_ci    def category_requires_validation(self, category):
248e5c31af7Sopenharmony_ci        """Return True if the given type 'category' always requires validation.
249e5c31af7Sopenharmony_ci
250e5c31af7Sopenharmony_ci        Overridden because Vulkan does not require "valid" text for basetype
251e5c31af7Sopenharmony_ci        in the spec right now."""
252e5c31af7Sopenharmony_ci        return category in CATEGORIES_REQUIRING_VALIDATION
253e5c31af7Sopenharmony_ci
254e5c31af7Sopenharmony_ci    @property
255e5c31af7Sopenharmony_ci    def should_skip_checking_codes(self):
256e5c31af7Sopenharmony_ci        """Return True if more than the basic validation of return codes should
257e5c31af7Sopenharmony_ci        be skipped for a command.
258e5c31af7Sopenharmony_ci
259e5c31af7Sopenharmony_ci        Vulkan mostly relies on the validation layers rather than API
260e5c31af7Sopenharmony_ci        builtin error checking, so these checks are not appropriate.
261e5c31af7Sopenharmony_ci
262e5c31af7Sopenharmony_ci        For example, passing in a VkFormat parameter will not potentially
263e5c31af7Sopenharmony_ci        generate a VK_ERROR_FORMAT_NOT_SUPPORTED code."""
264e5c31af7Sopenharmony_ci
265e5c31af7Sopenharmony_ci        return True
266e5c31af7Sopenharmony_ci
267e5c31af7Sopenharmony_ci    def extension_file_path(self, name):
268e5c31af7Sopenharmony_ci        """Return file path to an extension appendix relative to a directory
269e5c31af7Sopenharmony_ci           containing all such appendices.
270e5c31af7Sopenharmony_ci           - name - extension name"""
271e5c31af7Sopenharmony_ci
272e5c31af7Sopenharmony_ci        return f'{name}{self.file_suffix}'
273e5c31af7Sopenharmony_ci
274e5c31af7Sopenharmony_ci    def valid_flag_bit(self, bitpos):
275e5c31af7Sopenharmony_ci        """Return True if bitpos is an allowed numeric bit position for
276e5c31af7Sopenharmony_ci           an API flag bit.
277e5c31af7Sopenharmony_ci
278e5c31af7Sopenharmony_ci           Vulkan uses 32 bit Vk*Flags types, and assumes C compilers may
279e5c31af7Sopenharmony_ci           cause Vk*FlagBits values with bit 31 set to result in a 64 bit
280e5c31af7Sopenharmony_ci           enumerated type, so disallows such flags."""
281e5c31af7Sopenharmony_ci        return bitpos >= 0 and bitpos < 31
282e5c31af7Sopenharmony_ci
283e5c31af7Sopenharmony_ci    @property
284e5c31af7Sopenharmony_ci    def extra_refpage_headers(self):
285e5c31af7Sopenharmony_ci        """Return any extra text to add to refpage headers."""
286e5c31af7Sopenharmony_ci        return 'include::{config}/attribs.adoc[]'
287e5c31af7Sopenharmony_ci
288e5c31af7Sopenharmony_ci    @property
289e5c31af7Sopenharmony_ci    def extra_refpage_body(self):
290e5c31af7Sopenharmony_ci        """Return any extra text (following the title) for generated
291e5c31af7Sopenharmony_ci           reference pages."""
292e5c31af7Sopenharmony_ci        return 'include::{generated}/specattribs.adoc[]'
293e5c31af7Sopenharmony_ci
294e5c31af7Sopenharmony_ci
295e5c31af7Sopenharmony_ciclass VulkanSCConventions(VulkanConventions):
296e5c31af7Sopenharmony_ci
297e5c31af7Sopenharmony_ci    def specURL(self, spectype='api'):
298e5c31af7Sopenharmony_ci        """Return public registry URL which ref pages should link to for the
299e5c31af7Sopenharmony_ci           current all-extensions HTML specification, so xrefs in the
300e5c31af7Sopenharmony_ci           asciidoc source that are not to ref pages can link into it
301e5c31af7Sopenharmony_ci           instead. N.b. this may need to change on a per-refpage basis if
302e5c31af7Sopenharmony_ci           there are multiple documents involved.
303e5c31af7Sopenharmony_ci        """
304e5c31af7Sopenharmony_ci        return 'https://registry.khronos.org/vulkansc/specs/1.0-extensions/html/vkspec.html'
305e5c31af7Sopenharmony_ci
306e5c31af7Sopenharmony_ci    @property
307e5c31af7Sopenharmony_ci    def xml_api_name(self):
308e5c31af7Sopenharmony_ci        """Return the name used in the default API XML registry for the default API"""
309e5c31af7Sopenharmony_ci        return 'vulkansc'
310e5c31af7Sopenharmony_ci
311