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