xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/angle_format.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/python3
2# Copyright 2016 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# angle_format.py:
7#  Utils for ANGLE formats.
8
9import json
10import os
11import re
12
13kChannels = "ABDEGLRSX"
14
15
16def get_angle_format_map_abs_path():
17    return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'angle_format_map.json')
18
19
20def reject_duplicate_keys(pairs):
21    found_keys = {}
22    for key, value in pairs:
23        if key in found_keys:
24            raise ValueError("duplicate key: %r" % (key,))
25        else:
26            found_keys[key] = value
27    return found_keys
28
29
30def load_json(path):
31    with open(path) as map_file:
32        return json.loads(map_file.read(), object_pairs_hook=reject_duplicate_keys)
33
34
35def load_forward_table(path, key=None):
36    pairs = load_json(path)
37    if key is not None:
38        pairs = pairs[key]
39    reject_duplicate_keys(pairs)
40    return {gl: angle for gl, angle in pairs}
41
42
43def load_inverse_table(path):
44    pairs = load_json(path)
45    reject_duplicate_keys(pairs)
46    for x in range(0, 8):
47        pairs.append(("GL_NONE", "EXTERNAL" + str(x)))
48    return {angle: gl for gl, angle in pairs}
49
50
51def load_without_override():
52    map_path = get_angle_format_map_abs_path()
53    return load_forward_table(map_path)
54
55
56def load_with_override(override_path):
57    results = load_without_override()
58    overrides = load_json(override_path)
59
60    for k, v in sorted(overrides.items()):
61        results[k] = v
62
63    return results
64
65
66def get_all_angle_formats():
67    map_path = get_angle_format_map_abs_path()
68    return load_inverse_table(map_path).keys()
69
70
71def get_component_type(format_id):
72    if "SNORM" in format_id:
73        return "snorm"
74    elif "UNORM" in format_id:
75        return "unorm"
76    elif "FLOAT" in format_id:
77        return "float"
78    elif "FIXED" in format_id:
79        return "float"
80    elif "UINT" in format_id:
81        return "uint"
82    elif "SINT" in format_id:
83        return "int"
84    elif "USCALED" in format_id:
85        return "uint"
86    elif "SSCALED" in format_id:
87        return "int"
88    elif format_id == "NONE":
89        return "none"
90    elif "SRGB" in format_id:
91        return "unorm"
92    elif "TYPELESS" in format_id:
93        return "unorm"
94    elif "EXTERNAL" in format_id:
95        return "unorm"
96    elif format_id == "R9G9B9E5_SHAREDEXP":
97        return "float"
98    else:
99        raise ValueError("Unknown component type for " + format_id)
100
101
102def get_channel_tokens(format_id):
103    if 'EXTERNAL' in format_id:
104        return ['R8', 'G8', 'B8', 'A8']
105    r = re.compile(r'([' + kChannels + '][\d]+)')
106    return list(filter(r.match, r.split(format_id)))
107
108
109def get_channels(format_id):
110    channels = ''
111    tokens = get_channel_tokens(format_id)
112    if len(tokens) == 0:
113        return None
114    for token in tokens:
115        channels += token[0].lower()
116
117    return channels
118
119
120def get_bits(format_id):
121    bits = {}
122    if "_RED_" in format_id:
123        # BC4
124        bits["R"] = 16
125    elif "_RG_" in format_id:
126        # BC5
127        bits["R"] = bits["G"] = 16
128    elif "_RGB_" in format_id:
129        # BC1-3, BC6H, PVRTC
130        bits["R"] = bits["G"] = bits["B"] = 16 if "BC6H" in format_id else 8
131    elif "_RGBA_" in format_id or "ASTC_" in format_id:
132        # ASTC, BC7, PVRTC
133        bits["R"] = bits["G"] = bits["B"] = bits["A"] = 8
134    else:
135        tokens = get_channel_tokens(format_id)
136        for token in tokens:
137            bits[token[0]] = int(token[1:])
138    return bits
139
140
141def get_format_info(format_id):
142    return get_component_type(format_id), get_bits(format_id), get_channels(format_id)
143
144
145# TODO(oetuaho): Expand this code so that it could generate the gl format info tables as well.
146def gl_format_channels(internal_format):
147    if internal_format == 'GL_BGR5_A1_ANGLEX':
148        return 'bgra'
149    if internal_format == 'GL_R11F_G11F_B10F':
150        return 'rgb'
151    if internal_format == 'GL_RGB5_A1':
152        return 'rgba'
153    if internal_format.find('GL_RGB10_A2') == 0:
154        return 'rgba'
155    if internal_format.find('GL_RGB10') == 0:
156        return 'rgb'
157    # signed/unsigned int_10_10_10_2 for vertex format
158    if internal_format.find('INT_10_10_10_2_OES') == 0:
159        return 'rgba'
160
161    channels_pattern = re.compile('GL_(COMPRESSED_)?(SIGNED_)?(ETC\d_)?([A-Z]+)')
162    match = re.search(channels_pattern, internal_format)
163    channels_string = match.group(4)
164
165    if channels_string == 'ALPHA':
166        return 'a'
167    if channels_string == 'LUMINANCE':
168        if (internal_format.find('ALPHA') >= 0):
169            return 'la'
170        return 'l'
171    if channels_string == 'SRGB' or channels_string == 'RGB':
172        if (internal_format.find('ALPHA') >= 0):
173            return 'rgba'
174        return 'rgb'
175    if channels_string == 'DEPTH':
176        if (internal_format.find('STENCIL') >= 0):
177            return 'ds'
178        return 'd'
179    if channels_string == 'STENCIL':
180        return 's'
181    return channels_string.lower()
182
183
184def get_internal_format_initializer(internal_format, format_id):
185    gl_channels = gl_format_channels(internal_format)
186    gl_format_no_alpha = gl_channels == 'rgb' or gl_channels == 'l'
187    component_type, bits, channels = get_format_info(format_id)
188
189    # ETC2 punchthrough formats have per-pixel alpha values but a zero-filled block is parsed as opaque black.
190    # Ensure correct initialization when the formats are emulated.
191    if 'PUNCHTHROUGH_ALPHA1_ETC2' in internal_format and 'ETC2' not in format_id:
192        return 'Initialize4ComponentData<GLubyte, 0x00, 0x00, 0x00, 0xFF>'
193
194    if not gl_format_no_alpha or channels != 'rgba':
195        return 'nullptr'
196
197    elif internal_format == 'GL_RGB10_EXT':
198        return 'nullptr'
199
200    elif 'BC1_' in format_id:
201        # BC1 is a special case since the texture data determines whether each block has an alpha channel or not.
202        # This if statement is hit by COMPRESSED_RGB_S3TC_DXT1, which is a bit of a mess.
203        # TODO(oetuaho): Look into whether COMPRESSED_RGB_S3TC_DXT1 works right in general.
204        # Reference: https://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt
205        return 'nullptr'
206
207    elif component_type == 'uint' and bits['R'] == 8:
208        return 'Initialize4ComponentData<GLubyte, 0x00, 0x00, 0x00, 0x01>'
209    elif component_type == 'unorm' and bits['R'] == 8:
210        return 'Initialize4ComponentData<GLubyte, 0x00, 0x00, 0x00, 0xFF>'
211    elif component_type == 'unorm' and bits['R'] == 16:
212        return 'Initialize4ComponentData<GLushort, 0x0000, 0x0000, 0x0000, 0xFFFF>'
213    elif component_type == 'int' and bits['R'] == 8:
214        return 'Initialize4ComponentData<GLbyte, 0x00, 0x00, 0x00, 0x01>'
215    elif component_type == 'snorm' and bits['R'] == 8:
216        return 'Initialize4ComponentData<GLbyte, 0x00, 0x00, 0x00, 0x7F>'
217    elif component_type == 'snorm' and bits['R'] == 16:
218        return 'Initialize4ComponentData<GLushort, 0x0000, 0x0000, 0x0000, 0x7FFF>'
219    elif component_type == 'float' and bits['R'] == 16:
220        return 'Initialize4ComponentData<GLhalf, 0x0000, 0x0000, 0x0000, gl::Float16One>'
221    elif component_type == 'uint' and bits['R'] == 16:
222        return 'Initialize4ComponentData<GLushort, 0x0000, 0x0000, 0x0000, 0x0001>'
223    elif component_type == 'int' and bits['R'] == 16:
224        return 'Initialize4ComponentData<GLshort, 0x0000, 0x0000, 0x0000, 0x0001>'
225    elif component_type == 'float' and bits['R'] == 32:
226        return 'Initialize4ComponentData<GLfloat, 0x00000000, 0x00000000, 0x00000000, gl::Float32One>'
227    elif component_type == 'int' and bits['R'] == 32:
228        return 'Initialize4ComponentData<GLint, 0x00000000, 0x00000000, 0x00000000, 0x00000001>'
229    elif component_type == 'uint' and bits['R'] == 32:
230        return 'Initialize4ComponentData<GLuint, 0x00000000, 0x00000000, 0x00000000, 0x00000001>'
231    else:
232        raise ValueError(
233            'warning: internal format initializer could not be generated and may be needed for ' +
234            internal_format)
235
236
237def get_format_gl_type(format):
238    sign = ''
239    base_type = None
240    if 'FLOAT' in format:
241        bits = get_bits(format)
242        redbits = bits and bits.get('R')
243        base_type = 'float'
244        if redbits == 16:
245            base_type = 'half'
246    else:
247        bits = get_bits(format)
248        redbits = bits and bits.get('R')
249        if redbits == 8:
250            base_type = 'byte'
251        elif redbits == 16:
252            base_type = 'short'
253        elif redbits == 32:
254            base_type = 'int'
255
256        if 'UINT' in format or 'UNORM' in format or 'USCALED' in format:
257            sign = 'u'
258
259    if base_type is None:
260        return None
261
262    return 'GL' + sign + base_type
263
264
265def get_vertex_copy_function(src_format, dst_format):
266    if dst_format == "NONE":
267        return "nullptr"
268
269    src_num_channel = len(get_channel_tokens(src_format))
270    dst_num_channel = len(get_channel_tokens(dst_format))
271    if src_num_channel < 1 or src_num_channel > 4:
272        return "nullptr"
273
274    if src_format.endswith('_VERTEX'):
275        is_signed = 'true' if 'SINT' in src_format or 'SNORM' in src_format or 'SSCALED' in src_format else 'false'
276        is_normal = 'true' if 'NORM' in src_format else 'false'
277        if 'A2' in src_format:
278            return 'CopyW2XYZ10ToXYZWFloatVertexData<%s, %s, true>' % (is_signed, is_normal)
279        else:
280            return 'CopyXYZ10ToXYZWFloatVertexData<%s, %s, true>' % (is_signed, is_normal)
281
282    if 'FIXED' in src_format:
283        assert 'FLOAT' in dst_format, (
284            'get_vertex_copy_function: can only convert fixed to float,' + ' not to ' + dst_format)
285        return 'Copy32FixedTo32FVertexData<%d, %d>' % (src_num_channel, dst_num_channel)
286
287    src_gl_type = get_format_gl_type(src_format)
288    dst_gl_type = get_format_gl_type(dst_format)
289
290    if src_gl_type == None:
291        return "nullptr"
292
293    if src_gl_type == dst_gl_type:
294        default_alpha = '1'
295
296        if src_num_channel == dst_num_channel or dst_num_channel < 4:
297            default_alpha = '0'
298        elif 'A16_FLOAT' in dst_format:
299            default_alpha = 'gl::Float16One'
300        elif 'A32_FLOAT' in dst_format:
301            default_alpha = 'gl::Float32One'
302        elif 'NORM' in dst_format:
303            default_alpha = 'std::numeric_limits<%s>::max()' % (src_gl_type)
304
305        return 'CopyNativeVertexData<%s, %d, %d, %s>' % (src_gl_type, src_num_channel,
306                                                         dst_num_channel, default_alpha)
307
308    assert 'FLOAT' in dst_format, (
309        'get_vertex_copy_function: can only convert to float,' + ' not to ' + dst_format)
310    normalized = 'true' if 'NORM' in src_format else 'false'
311
312    dst_is_half = 'true' if dst_gl_type == 'GLhalf' else 'false'
313    return "CopyToFloatVertexData<%s, %d, %d, %s, %s>" % (src_gl_type, src_num_channel,
314                                                          dst_num_channel, normalized, dst_is_half)
315