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