1#!/usr/bin/python3 2# Copyright 2024 The ANGLE Project Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5# 6# gen_wgpu_format_table.py: 7# Code generation for wgpu format map. See wgpu_format_map.json for data source. 8# NOTE: don't run this script directly. Run scripts/run_code_generation.py. 9 10import json 11import math 12import pprint 13import os 14import re 15import sys 16 17sys.path.append('..') 18import angle_format 19 20template_table_autogen_cpp = """// GENERATED FILE - DO NOT EDIT. 21// Generated by {script_name} using data from {input_file_name} 22// 23// Copyright 2024 The ANGLE Project Authors. All rights reserved. 24// Use of this source code is governed by a BSD-style license that can be 25// found in the LICENSE file. 26// 27// {out_file_name}: 28// Queries for full WebGPU format information based on GL format. 29 30#include "libANGLE/renderer/wgpu/wgpu_format_utils.h" 31 32#include "image_util/loadimage.h" 33 34using namespace angle; 35 36namespace rx 37{{ 38namespace webgpu 39{{ 40 41void Format::initialize(const angle::Format &angleFormat) 42{{ 43 switch (angleFormat.id) 44 {{ 45{format_case_data} 46 default: 47 UNREACHABLE(); 48 break; 49 }} 50}} 51 52wgpu::TextureFormat GetWgpuTextureFormatFromFormatID(angle::FormatID formatID) 53{{ 54 static constexpr angle::FormatMap<wgpu::TextureFormat> kMap = {{ 55{image_format_id_cases} 56 }}; 57 58 return kMap[formatID]; 59}} 60 61angle::FormatID GetFormatIDFromWgpuTextureFormat(wgpu::TextureFormat wgpuFormat) 62{{ 63 switch (wgpuFormat) 64 {{ 65{wgpu_image_format_cases} 66 default: 67 UNREACHABLE(); 68 return angle::FormatID::NONE; 69 }} 70}} 71 72wgpu::VertexFormat GetWgpuVertexFormatFromFormatID(angle::FormatID formatID) 73{{ 74 static constexpr angle::FormatMap<wgpu::VertexFormat> kMap = {{ 75{buffer_format_id_cases} 76 }}; 77 78 return kMap[formatID]; 79}} 80 81angle::FormatID GetFormatIDFromWgpuBufferFormat(wgpu::VertexFormat wgpuFormat) 82{{ 83 switch (wgpuFormat) 84 {{ 85{wgpu_buffer_format_cases} 86 default: 87 UNREACHABLE(); 88 return angle::FormatID::NONE; 89 }} 90}} 91}} // namespace webgpu 92}} // namespace rx 93""" 94 95empty_format_entry_template = """case angle::FormatID::{format_id}: 96// This format is not implemented in WebGPU. 97break; 98""" 99 100format_entry_template = """case angle::FormatID::{format_id}: 101mIntendedGLFormat = {internal_format}; 102{image_template} 103{buffer_template} 104break; 105""" 106 107image_snorm_template = """mActualImageFormatID = {image}; 108mImageInitializerFunction = {image_initializer}; 109mIsRenderable = false;""" 110 111image_basic_template = """mActualImageFormatID = {image}; 112mImageInitializerFunction = {image_initializer}; 113mIsRenderable = true;""" 114 115image_struct_template = "{{{image}, {image_initializer}}}" 116 117image_fallback_template = """{{ 118static constexpr ImageFormatInitInfo kInfo[] = {{{image_list}}}; 119initImageFallback(kInfo, ArraySize(kInfo)); 120}}""" 121 122buffer_basic_template = """mActualBufferFormatID = {buffer}; 123mVertexLoadFunction = {vertex_load_function}; 124mVertexLoadRequiresConversion = {vertex_load_converts};""" 125 126buffer_struct_template = """{{{buffer}, {vertex_load_function}, {vertex_load_converts}}}""" 127 128buffer_fallback_template = """{{ 129static constexpr BufferFormatInitInfo kInfo[] = {{{buffer_list}}}; 130initBufferFallback(kInfo, ArraySize(kInfo)); 131}}""" 132 133 134def verify_wgpu_image_map_keys(angle_to_gl, wgpu_json_data): 135 """Verify that the keys in WebGPU format tables exist in the ANGLE table. If they don't, the 136 entry in the WebGPU file is incorrect and needs to be fixed.""" 137 138 no_error = True 139 for table in ["image_map", "buffer_map", "fallbacks"]: 140 for angle_format in wgpu_json_data[table].keys(): 141 if not angle_format in angle_to_gl.keys(): 142 print("Invalid format " + angle_format + " in wgpu_format_map.json in " + table) 143 no_error = False 144 145 return no_error 146 147 148def get_vertex_copy_function(src_format, dst_format, wgpu_format): 149 if 'R10G10B10A2' in src_format: 150 # When the R10G10B10A2 type can't be used by the vertex buffer, 151 # it needs to be converted to the type which can be used by it. 152 is_signed = 'false' if 'UINT' in src_format or 'UNORM' in src_format or 'USCALED' in src_format else 'true' 153 normalized = 'true' if 'NORM' in src_format else 'false' 154 to_float = 'false' if 'INT' in src_format else 'true' 155 to_half = to_float 156 return 'CopyXYZ10W2ToXYZWFloatVertexData<%s, %s, %s, %s>' % (is_signed, normalized, 157 to_float, to_half) 158 return angle_format.get_vertex_copy_function(src_format, dst_format) 159 160 161def gen_format_case(angle, internal_format, wgpu_json_data): 162 wgpu_image_map = wgpu_json_data["image_map"] 163 wgpu_buffer_map = wgpu_json_data["buffer_map"] 164 wgpu_fallbacks = wgpu_json_data["fallbacks"] 165 args = dict( 166 format_id=angle, internal_format=internal_format, image_template="", buffer_template="") 167 168 if ((angle not in wgpu_image_map) and (angle not in wgpu_buffer_map) and 169 (angle not in wgpu_fallbacks)): 170 return empty_format_entry_template.format(**args) 171 172 # get_formats returns override format (if any) + fallbacks 173 # this was necessary to support D32_UNORM. There is no appropriate override that allows 174 # us to fallback to D32_FLOAT, so now we leave the image override empty and function will 175 # give us the fallbacks. 176 def get_formats(format, type): 177 fallbacks = wgpu_fallbacks.get(format, {}).get(type, []) 178 if not isinstance(fallbacks, list): 179 fallbacks = [fallbacks] 180 181 if (format in wgpu_image_map and type == "image") or (format in wgpu_buffer_map and 182 type == "buffer"): 183 assert format not in fallbacks 184 fallbacks = [format] + fallbacks 185 186 return fallbacks 187 188 def image_args(format): 189 return dict( 190 image="angle::FormatID::" + format, 191 image_initializer=angle_format.get_internal_format_initializer( 192 internal_format, format)) 193 194 def buffer_args(format): 195 wgpu_buffer_format = wgpu_buffer_map[format] 196 return dict( 197 buffer="angle::FormatID::" + format, 198 vertex_load_function=get_vertex_copy_function(angle, format, wgpu_buffer_format), 199 vertex_load_converts='false' if angle == format else 'true', 200 ) 201 202 images = get_formats(angle, "image") 203 if len(images) == 1: 204 if 'SNORM' in angle: 205 args.update(image_template=image_snorm_template) 206 else: 207 args.update(image_template=image_basic_template) 208 args.update(image_args(images[0])) 209 elif len(images) > 1: 210 args.update( 211 image_template=image_fallback_template, 212 image_list=", ".join(image_struct_template.format(**image_args(i)) for i in images)) 213 214 buffers = get_formats(angle, "buffer") 215 if len(buffers) == 1 and buffers[0] in wgpu_buffer_map: 216 args.update(buffer_template=buffer_basic_template) 217 args.update(buffer_args(buffers[0])) 218 elif len(buffers) > 1: 219 args.update( 220 buffer_template=buffer_fallback_template, 221 buffer_list=", ".join( 222 buffer_struct_template.format(**buffer_args(i)) for i in buffers)) 223 224 return format_entry_template.format(**args).format(**args) 225 226 227def get_format_id_case(format_id, format_type, wgpu_format): 228 # wgpu::VertexFormat::Undefined was replaced with wgpu::VertexFormat(0u) 229 # in https://dawn-review.googlesource.com/c/dawn/+/193360 230 if 'Undefined' in wgpu_format and 'VertexFormat' in format_type: 231 return "{angle::FormatID::%s, wgpu::%s(0u)}" % (format_id, format_type) 232 return "{angle::FormatID::%s, wgpu::%s::%s}" % (format_id, format_type, wgpu_format) 233 234 235def get_wgpu_format_case(format_type, format_id, wgpu_format): 236 # wgpu::VertexFormat::Undefined was replaced with wgpu::VertexFormat(0u) 237 # in https://dawn-review.googlesource.com/c/dawn/+/193360 238 # so there is no 'case' needed for it. 239 if 'Undefined' in wgpu_format and 'VertexFormat' in format_type: 240 return '' 241 # don't generate the reverse mapping for the external format slots because they _all_ map 242 # to WGPU_FORMAT_UNDEFINED and so clash with NONE 243 if 'EXTERNAL' in format_id: 244 return '' 245 return """\ 246 case wgpu::%s::%s: 247 return angle::FormatID::%s; 248""" % (format_type, wgpu_format, format_id) 249 250 251def main(): 252 253 input_file_name = 'wgpu_format_map.json' 254 out_file_name = 'wgpu_format_table_autogen.cpp' 255 256 # auto_script parameters. 257 if len(sys.argv) > 1: 258 inputs = ['../angle_format.py', '../angle_format_map.json', input_file_name] 259 outputs = [out_file_name] 260 261 if sys.argv[1] == 'inputs': 262 print(','.join(inputs)) 263 elif sys.argv[1] == 'outputs': 264 print(','.join(outputs)) 265 else: 266 print('Invalid script parameters') 267 return 1 268 return 0 269 270 angle_to_gl = angle_format.load_inverse_table(os.path.join('..', 'angle_format_map.json')) 271 wgpu_json_data = angle_format.load_json(input_file_name) 272 273 if not verify_wgpu_image_map_keys(angle_to_gl, wgpu_json_data): 274 return 1 275 276 image_format_id_cases = [ 277 get_format_id_case(format_id, "TextureFormat", wgpu_format) 278 for format_id, wgpu_format in sorted(wgpu_json_data["image_map"].items()) 279 ] 280 281 wgpu_image_format_cases = [ 282 get_wgpu_format_case("TextureFormat", format_id, wgpu_format) 283 for format_id, wgpu_format in sorted(wgpu_json_data["image_map"].items()) 284 ] 285 286 buffer_format_id_cases = [ 287 get_format_id_case(format_id, "VertexFormat", wgpu_format) 288 for format_id, wgpu_format in sorted(wgpu_json_data["buffer_map"].items()) 289 ] 290 291 wgpu_buffer_format_cases = [ 292 get_wgpu_format_case("VertexFormat", format_id, wgpu_format) 293 for format_id, wgpu_format in sorted(wgpu_json_data["buffer_map"].items()) 294 ] 295 296 wgpu_cases = [ 297 gen_format_case(angle, gl, wgpu_json_data) for angle, gl in sorted(angle_to_gl.items()) 298 ] 299 300 output_cpp = template_table_autogen_cpp.format( 301 format_case_data="\n".join(wgpu_cases), 302 image_format_id_cases=",\n".join(image_format_id_cases), 303 wgpu_image_format_cases="".join(wgpu_image_format_cases), 304 buffer_format_id_cases=",\n".join(buffer_format_id_cases), 305 wgpu_buffer_format_cases="".join(wgpu_buffer_format_cases), 306 script_name=os.path.basename(__file__), 307 out_file_name=out_file_name, 308 input_file_name=input_file_name) 309 310 with open(out_file_name, 'wt') as out_file: 311 out_file.write(output_cpp) 312 out_file.close() 313 return 0 314 315 316if __name__ == '__main__': 317 sys.exit(main()) 318