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