• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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