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_cifrom generator import OutputGenerator, write
8e5c31af7Sopenharmony_cifrom spec_tools.attributes import ExternSyncEntry
9e5c31af7Sopenharmony_cifrom spec_tools.util import getElemName
10e5c31af7Sopenharmony_ci
11e5c31af7Sopenharmony_ciimport pdb
12e5c31af7Sopenharmony_ci
13e5c31af7Sopenharmony_ciclass FormatsOutputGenerator(OutputGenerator):
14e5c31af7Sopenharmony_ci    """FormatsOutputGenerator - subclass of OutputGenerator.
15e5c31af7Sopenharmony_ci    Generates AsciiDoc includes of the table for the format chapters
16e5c31af7Sopenharmony_ci    of the API specification.
17e5c31af7Sopenharmony_ci
18e5c31af7Sopenharmony_ci    ---- methods ----
19e5c31af7Sopenharmony_ci    FormatsOutputGenerator(errFile, warnFile, diagFile) - args as for
20e5c31af7Sopenharmony_ci      OutputGenerator. Defines additional internal state.
21e5c31af7Sopenharmony_ci    ---- methods overriding base class ----
22e5c31af7Sopenharmony_ci    genCmd(cmdinfo)"""
23e5c31af7Sopenharmony_ci
24e5c31af7Sopenharmony_ci    def __init__(self, *args, **kwargs):
25e5c31af7Sopenharmony_ci        super().__init__(*args, **kwargs)
26e5c31af7Sopenharmony_ci
27e5c31af7Sopenharmony_ci    def beginFile(self, genOpts):
28e5c31af7Sopenharmony_ci        OutputGenerator.beginFile(self, genOpts)
29e5c31af7Sopenharmony_ci
30e5c31af7Sopenharmony_ci        # List of all the formats elements
31e5c31af7Sopenharmony_ci        self.formats = []
32e5c31af7Sopenharmony_ci        # <format, condition as asciidoc string>
33e5c31af7Sopenharmony_ci        self.format_conditions = dict()
34e5c31af7Sopenharmony_ci        # <class, {'formats' : [], 'meta' : {} }>
35e5c31af7Sopenharmony_ci        self.format_classes = dict()
36e5c31af7Sopenharmony_ci        # {'packedSize' : ['format', 'format', ...]}
37e5c31af7Sopenharmony_ci        self.packed_info = dict()
38e5c31af7Sopenharmony_ci        # {VkFormat : SpirvFormat}
39e5c31af7Sopenharmony_ci        self.spirv_image_format = dict()
40e5c31af7Sopenharmony_ci        # <format, [{plane_info}, ...]>
41e5c31af7Sopenharmony_ci        self.plane_format = dict()
42e5c31af7Sopenharmony_ci
43e5c31af7Sopenharmony_ci    def endFile(self):
44e5c31af7Sopenharmony_ci
45e5c31af7Sopenharmony_ci        # Generate compatibility table
46e5c31af7Sopenharmony_ci        compatibility_table = []
47e5c31af7Sopenharmony_ci        for class_name, info in self.format_classes.items():
48e5c31af7Sopenharmony_ci            # Do an initial loop of formats in class to see if whole class is a single condition
49e5c31af7Sopenharmony_ci            class_condition = None
50e5c31af7Sopenharmony_ci            for index, format in enumerate(info['formats']):
51e5c31af7Sopenharmony_ci                condition = self.format_conditions[format]
52e5c31af7Sopenharmony_ci                if (condition == None) or (class_condition != None and class_condition != condition):
53e5c31af7Sopenharmony_ci                    class_condition = None
54e5c31af7Sopenharmony_ci                    break
55e5c31af7Sopenharmony_ci                else:
56e5c31af7Sopenharmony_ci                    class_condition = condition
57e5c31af7Sopenharmony_ci
58e5c31af7Sopenharmony_ci            # If not single class condition for the class, next check if a single format has a condition
59e5c31af7Sopenharmony_ci            # Move all condition formats to the front of array to make listing the formats in table
60e5c31af7Sopenharmony_ci            if class_condition == None:
61e5c31af7Sopenharmony_ci                condition_list = []
62e5c31af7Sopenharmony_ci                noncondition_list = []
63e5c31af7Sopenharmony_ci                for index, format in enumerate(info['formats']):
64e5c31af7Sopenharmony_ci                    if self.format_conditions[format] == None:
65e5c31af7Sopenharmony_ci                        noncondition_list.append(format)
66e5c31af7Sopenharmony_ci                    else:
67e5c31af7Sopenharmony_ci                        condition_list.append(format)
68e5c31af7Sopenharmony_ci                info['formats'] = condition_list + noncondition_list
69e5c31af7Sopenharmony_ci
70e5c31af7Sopenharmony_ci            if class_condition != None:
71e5c31af7Sopenharmony_ci                compatibility_table.append('ifdef::{}[]'.format(class_condition))
72e5c31af7Sopenharmony_ci
73e5c31af7Sopenharmony_ci            compatibility_table.append("| {} +".format(class_name))
74e5c31af7Sopenharmony_ci            compatibility_table.append("  Block size {} byte +".format(info['meta']['blockSize']))
75e5c31af7Sopenharmony_ci            compatibility_table.append("  {} block extent +".format(info['meta']['blockExtent'].replace(",", "x")))
76e5c31af7Sopenharmony_ci            compatibility_table.append("  {} texel/block |".format(info['meta']['texelsPerBlock']))
77e5c31af7Sopenharmony_ci
78e5c31af7Sopenharmony_ci            for index, format in enumerate(info['formats']):
79e5c31af7Sopenharmony_ci                format_condition = self.format_conditions[format]
80e5c31af7Sopenharmony_ci                if format_condition != None and class_condition == None:
81e5c31af7Sopenharmony_ci                    compatibility_table.append('ifdef::{}[]'.format(format_condition))
82e5c31af7Sopenharmony_ci                suffix = ", +" if index != len(info['formats']) - 1 else ""
83e5c31af7Sopenharmony_ci                compatibility_table.append("                    ename:{}{}".format(format, suffix))
84e5c31af7Sopenharmony_ci                if format_condition != None and class_condition == None:
85e5c31af7Sopenharmony_ci                    compatibility_table.append('endif::{}[]'.format(format_condition))
86e5c31af7Sopenharmony_ci
87e5c31af7Sopenharmony_ci            if class_condition != None:
88e5c31af7Sopenharmony_ci                compatibility_table.append('endif::{}[]'.format(class_condition))
89e5c31af7Sopenharmony_ci        self.writeBlock(f'compatibility{self.file_suffix}', compatibility_table)
90e5c31af7Sopenharmony_ci
91e5c31af7Sopenharmony_ci        # Generate packed format list
92e5c31af7Sopenharmony_ci        packed_table = []
93e5c31af7Sopenharmony_ci        for packed_size, formats in self.packed_info.items():
94e5c31af7Sopenharmony_ci            packed_table.append('  * <<formats-packed-{}-bit,Packed into {}-bit data types>>:'.format(packed_size, packed_size))
95e5c31af7Sopenharmony_ci            # Do an initial loop of formats with same packed size to group conditional together for easier reading of final asciidoc
96e5c31af7Sopenharmony_ci            sorted_formats = dict() # {condition : formats}
97e5c31af7Sopenharmony_ci            for format in formats:
98e5c31af7Sopenharmony_ci                format_condition = self.format_conditions[format]
99e5c31af7Sopenharmony_ci                if format_condition == None:
100e5c31af7Sopenharmony_ci                    format_condition = "None" # to allow as a key in the dict
101e5c31af7Sopenharmony_ci                if format_condition not in sorted_formats:
102e5c31af7Sopenharmony_ci                    sorted_formats[format_condition] = []
103e5c31af7Sopenharmony_ci                sorted_formats[format_condition].append(format)
104e5c31af7Sopenharmony_ci
105e5c31af7Sopenharmony_ci            for condition, condition_formats in sorted_formats.items():
106e5c31af7Sopenharmony_ci                if condition != "None":
107e5c31af7Sopenharmony_ci                    packed_table.append('ifdef::{}[]'.format(condition))
108e5c31af7Sopenharmony_ci                for format in condition_formats:
109e5c31af7Sopenharmony_ci                    packed_table.append('  ** ename:{}'.format(format))
110e5c31af7Sopenharmony_ci                if condition != "None":
111e5c31af7Sopenharmony_ci                    packed_table.append('endif::{}[]'.format(condition))
112e5c31af7Sopenharmony_ci        self.writeBlock(f'packed{self.file_suffix}', packed_table)
113e5c31af7Sopenharmony_ci
114e5c31af7Sopenharmony_ci        # Generate SPIR-V Image Format Compatibility
115e5c31af7Sopenharmony_ci        spirv_image_format_table = []
116e5c31af7Sopenharmony_ci        spirv_image_format_table.append('|code:Unknown|Any')
117e5c31af7Sopenharmony_ci        for vk_format, spirv_format in self.spirv_image_format.items():
118e5c31af7Sopenharmony_ci            spirv_image_format_table.append('|code:{}|ename:{}'.format(spirv_format, vk_format))
119e5c31af7Sopenharmony_ci        self.writeBlock(f'spirvimageformat{self.file_suffix}', spirv_image_format_table)
120e5c31af7Sopenharmony_ci
121e5c31af7Sopenharmony_ci        # Generate Plane Format Compatibility Table
122e5c31af7Sopenharmony_ci        plane_format_table = []
123e5c31af7Sopenharmony_ci        for format_name, plane_infos in self.plane_format.items():
124e5c31af7Sopenharmony_ci            format_condition = self.format_conditions[format_name]
125e5c31af7Sopenharmony_ci            # The table is already in a ifdef::VK_VERSION_1_1,VK_KHR_sampler_ycbcr_conversion[]
126e5c31af7Sopenharmony_ci            # so no need to duplicate the condition
127e5c31af7Sopenharmony_ci            add_condition = False if format_condition == 'None' or format_condition == 'VK_VERSION_1_1,VK_KHR_sampler_ycbcr_conversion' else True
128e5c31af7Sopenharmony_ci
129e5c31af7Sopenharmony_ci            if add_condition:
130e5c31af7Sopenharmony_ci                plane_format_table.append('ifdef::{}[]'.format(format_condition))
131e5c31af7Sopenharmony_ci
132e5c31af7Sopenharmony_ci            plane_format_table.append('4+| *ename:{}*'.format(format_name))
133e5c31af7Sopenharmony_ci            for plane_info in plane_infos:
134e5c31af7Sopenharmony_ci                width_divisor = 'w'
135e5c31af7Sopenharmony_ci                height_divisor = 'h'
136e5c31af7Sopenharmony_ci                if plane_info['widthDivisor'] != 1:
137e5c31af7Sopenharmony_ci                    width_divisor += '/{}'.format(plane_info['widthDivisor'])
138e5c31af7Sopenharmony_ci                if plane_info['heightDivisor'] != 1:
139e5c31af7Sopenharmony_ci                    height_divisor += '/{}'.format(plane_info['heightDivisor'])
140e5c31af7Sopenharmony_ci
141e5c31af7Sopenharmony_ci                plane_format_table.append('^| {} ^| ename:{} ^| {} ^| {}'.format(plane_info['index'],
142e5c31af7Sopenharmony_ci                                                                                 plane_info['compatible'],
143e5c31af7Sopenharmony_ci                                                                                 width_divisor,
144e5c31af7Sopenharmony_ci                                                                                 height_divisor))
145e5c31af7Sopenharmony_ci            if add_condition:
146e5c31af7Sopenharmony_ci                plane_format_table.append('endif::{}[]'.format(format_condition))
147e5c31af7Sopenharmony_ci        self.writeBlock(f'planeformat{self.file_suffix}', plane_format_table)
148e5c31af7Sopenharmony_ci
149e5c31af7Sopenharmony_ci        # Finish processing in superclass
150e5c31af7Sopenharmony_ci        OutputGenerator.endFile(self)
151e5c31af7Sopenharmony_ci
152e5c31af7Sopenharmony_ci    def writeBlock(self, basename, contents):
153e5c31af7Sopenharmony_ci        """Generate an include file.
154e5c31af7Sopenharmony_ci
155e5c31af7Sopenharmony_ci        - directory - subdirectory to put file in
156e5c31af7Sopenharmony_ci        - basename - base name of the file
157e5c31af7Sopenharmony_ci        - contents - contents of the file (Asciidoc boilerplate aside)"""
158e5c31af7Sopenharmony_ci
159e5c31af7Sopenharmony_ci        filename = self.genOpts.directory + '/' + basename
160e5c31af7Sopenharmony_ci        self.logMsg('diag', '# Generating include file:', filename)
161e5c31af7Sopenharmony_ci        with open(filename, 'w', encoding='utf-8') as fp:
162e5c31af7Sopenharmony_ci            write(self.genOpts.conventions.warning_comment, file=fp)
163e5c31af7Sopenharmony_ci
164e5c31af7Sopenharmony_ci            if len(contents) > 0:
165e5c31af7Sopenharmony_ci                for str in contents:
166e5c31af7Sopenharmony_ci                    write(str, file=fp)
167e5c31af7Sopenharmony_ci            else:
168e5c31af7Sopenharmony_ci                self.logMsg('diag', '# No contents for:', filename)
169e5c31af7Sopenharmony_ci
170e5c31af7Sopenharmony_ci    def genFormat(self, format, formatinfo, alias):
171e5c31af7Sopenharmony_ci        """Generate Formats
172e5c31af7Sopenharmony_ci
173e5c31af7Sopenharmony_ci        formatinfo - dictionary entry for an XML <format> element
174e5c31af7Sopenharmony_ci        name - name attribute of format.elem"""
175e5c31af7Sopenharmony_ci
176e5c31af7Sopenharmony_ci        OutputGenerator.genFormat(self, format, formatinfo, alias)
177e5c31af7Sopenharmony_ci        elem = format.elem
178e5c31af7Sopenharmony_ci        format_name = elem.get('name')
179e5c31af7Sopenharmony_ci
180e5c31af7Sopenharmony_ci        self.formats.append(elem)
181e5c31af7Sopenharmony_ci        self.format_conditions[format_name] = format.condition
182e5c31af7Sopenharmony_ci
183e5c31af7Sopenharmony_ci        # Create format class data structure to be processed later
184e5c31af7Sopenharmony_ci        class_name = elem.get('class')
185e5c31af7Sopenharmony_ci        class_meta = {
186e5c31af7Sopenharmony_ci            'blockSize' : elem.get('blockSize'),
187e5c31af7Sopenharmony_ci            'texelsPerBlock' : elem.get('texelsPerBlock'),
188e5c31af7Sopenharmony_ci            # default extent
189e5c31af7Sopenharmony_ci            'blockExtent' : "1,1,1" if elem.get('blockExtent') == None else elem.get('blockExtent')
190e5c31af7Sopenharmony_ci        }
191e5c31af7Sopenharmony_ci
192e5c31af7Sopenharmony_ci        if class_name in self.format_classes:
193e5c31af7Sopenharmony_ci            self.format_classes[class_name]['formats'].append(format_name)
194e5c31af7Sopenharmony_ci            # Assert all classes are using same meta info
195e5c31af7Sopenharmony_ci            if class_meta != self.format_classes[class_name]['meta']:
196e5c31af7Sopenharmony_ci                self.logMsg('error', 'Class meta info is not consistent for class ', class_name)
197e5c31af7Sopenharmony_ci        else:
198e5c31af7Sopenharmony_ci            self.format_classes[class_name] = {
199e5c31af7Sopenharmony_ci                'formats' : [format_name],
200e5c31af7Sopenharmony_ci                'meta' : class_meta
201e5c31af7Sopenharmony_ci            }
202e5c31af7Sopenharmony_ci
203e5c31af7Sopenharmony_ci        # Build list of formats with packed info in xml
204e5c31af7Sopenharmony_ci        packed = elem.get('packed')
205e5c31af7Sopenharmony_ci        if packed is not None:
206e5c31af7Sopenharmony_ci            if packed not in self.packed_info:
207e5c31af7Sopenharmony_ci                self.packed_info[packed] = []
208e5c31af7Sopenharmony_ci            self.packed_info[packed].append(format_name)
209e5c31af7Sopenharmony_ci
210e5c31af7Sopenharmony_ci        # Currently there is only at most one <spirvimageformat>
211e5c31af7Sopenharmony_ci        spirv_image_format = elem.find('spirvimageformat')
212e5c31af7Sopenharmony_ci        if (spirv_image_format is not None):
213e5c31af7Sopenharmony_ci            self.spirv_image_format[format_name] = spirv_image_format.get('name')
214e5c31af7Sopenharmony_ci
215e5c31af7Sopenharmony_ci        for plane in elem.iterfind('plane'):
216e5c31af7Sopenharmony_ci            if format_name not in self.plane_format:
217e5c31af7Sopenharmony_ci                # create list if first time
218e5c31af7Sopenharmony_ci                self.plane_format[format_name] = []
219e5c31af7Sopenharmony_ci            self.plane_format[format_name].append({
220e5c31af7Sopenharmony_ci                'index' : int(plane.get('index')),
221e5c31af7Sopenharmony_ci                'widthDivisor' : int(plane.get('widthDivisor')),
222e5c31af7Sopenharmony_ci                'heightDivisor' : int(plane.get('heightDivisor')),
223e5c31af7Sopenharmony_ci                'compatible' : plane.get('compatible'),
224e5c31af7Sopenharmony_ci            })
225