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# gen_angle_format_table.py:
7#  Code generation for ANGLE format map.
8#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.
9#
10
11import angle_format
12import json
13import math
14import pprint
15import re
16import sys
17
18template_autogen_h = """// GENERATED FILE - DO NOT EDIT.
19// Generated by {script_name} using data from {data_source_name}
20//
21// Copyright 2020 The ANGLE Project Authors. All rights reserved.
22// Use of this source code is governed by a BSD-style license that can be
23// found in the LICENSE file.
24//
25// ANGLE format enumeration.
26
27#ifndef LIBANGLE_RENDERER_FORMATID_H_
28#define LIBANGLE_RENDERER_FORMATID_H_
29
30#include <cstdint>
31
32namespace angle
33{{
34
35enum class FormatID
36{{
37{angle_format_enum}
38}};
39
40constexpr uint32_t kNumANGLEFormats = {num_angle_formats};
41
42}}  // namespace angle
43
44#endif  // LIBANGLE_RENDERER_FORMATID_H_
45"""
46
47template_autogen_inl = """// GENERATED FILE - DO NOT EDIT.
48// Generated by {script_name} using data from {data_source_name}
49//
50// Copyright 2020 The ANGLE Project Authors. All rights reserved.
51// Use of this source code is governed by a BSD-style license that can be
52// found in the LICENSE file.
53//
54// ANGLE Format table:
55//   Queries for typed format information from the ANGLE format enum.
56
57#include "libANGLE/renderer/Format.h"
58
59#include "image_util/copyimage.h"
60#include "image_util/generatemip.h"
61#include "image_util/loadimage.h"
62
63namespace angle
64{{
65
66static constexpr rx::FastCopyFunctionMap::Entry BGRAEntry = {{angle::FormatID::R8G8B8A8_UNORM,
67                                                             CopyBGRA8ToRGBA8}};
68static constexpr rx::FastCopyFunctionMap BGRACopyFunctions = {{&BGRAEntry, 1}};
69static constexpr rx::FastCopyFunctionMap NoCopyFunctions;
70
71const Format gFormatInfoTable[] = {{
72    // clang-format off
73    {{ FormatID::NONE, GL_NONE, GL_NONE, nullptr, NoCopyFunctions, nullptr, nullptr, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, false, false, false, false, gl::VertexAttribType::InvalidEnum }},
74{angle_format_info_cases}    // clang-format on
75}};
76
77// static
78FormatID Format::InternalFormatToID(GLenum internalFormat)
79{{
80    switch (internalFormat)
81    {{
82{angle_format_switch}
83    }}
84}}
85
86const Format *GetFormatInfoTable()
87{{
88    return gFormatInfoTable;
89}}
90}}  // namespace angle
91"""
92
93
94def ceil_int(value, mod):
95    assert mod > 0 and value > 0, 'integer modulation should be larger than 0'
96    return (value + mod - 1) // mod
97
98
99def is_depth_stencil(angle_format):
100    if not 'channels' in angle_format or not angle_format['channels']:
101        return False
102    return 'd' in angle_format['channels'] or 's' in angle_format['channels']
103
104
105def get_component_suffix(angle_format):
106    if angle_format['componentType'] == 'float':
107        return 'F'
108    if angle_format['componentType'] == 'int' or angle_format['componentType'] == 'snorm':
109        return 'S'
110    return ""
111
112
113def get_channel_struct(angle_format):
114    if 'bits' not in angle_format or angle_format['bits'] is None:
115        return None
116    if 'BLOCK' in angle_format['id']:
117        return None
118    if 'VERTEX' in angle_format['id']:
119        return None
120
121    bits = angle_format['bits']
122
123    if 'channelStruct' in angle_format:
124        return angle_format['channelStruct']
125
126    struct_name = ''
127    component_suffix = get_component_suffix(angle_format)
128
129    for channel in angle_format['channels']:
130        if channel == 'r':
131            struct_name += 'R{}'.format(bits['R'])
132        if channel == 'g':
133            struct_name += 'G{}'.format(bits['G'])
134        if channel == 'b':
135            struct_name += 'B{}'.format(bits['B'])
136        if channel == 'a':
137            struct_name += 'A{}'.format(bits['A'])
138        if channel == 'l':
139            struct_name += 'L{}'.format(bits['L'])
140        if channel == 'd':
141            struct_name += 'D{}'.format(bits['D']) + component_suffix
142        if channel == 's':
143            struct_name += 'S{}'.format(bits['S'])
144        if channel == 'x':
145            struct_name += 'X{}'.format(bits['X'])
146
147    if not is_depth_stencil(angle_format):
148        struct_name += component_suffix
149
150    return struct_name
151
152
153def get_mip_generation_function(angle_format):
154    channel_struct = get_channel_struct(angle_format)
155    if is_depth_stencil(angle_format) or channel_struct == None \
156            or "BLOCK" in angle_format["id"] or "VERTEX" in angle_format["id"]:
157        return 'nullptr'
158    return 'GenerateMip<' + channel_struct + '>'
159
160
161def get_color_read_write_component_type(angle_format):
162    component_type_map = {
163        'uint': 'GLuint',
164        'int': 'GLint',
165        'unorm': 'GLfloat',
166        'snorm': 'GLfloat',
167        'float': 'GLfloat'
168    }
169    return component_type_map[angle_format['componentType']]
170
171
172def get_color_read_function(angle_format):
173    channel_struct = get_channel_struct(angle_format)
174    if channel_struct == None:
175        return 'nullptr'
176
177    if is_depth_stencil(angle_format):
178        return 'ReadDepthStencil<' + channel_struct + '>'
179
180    read_component_type = get_color_read_write_component_type(angle_format)
181    return 'ReadColor<' + channel_struct + ', ' + read_component_type + '>'
182
183
184def get_color_write_function(angle_format):
185    channel_struct = get_channel_struct(angle_format)
186    if channel_struct == None:
187        return 'nullptr'
188
189    if is_depth_stencil(angle_format):
190        return 'WriteDepthStencil<' + channel_struct + '>'
191
192    write_component_type = get_color_read_write_component_type(angle_format)
193    return 'WriteColor<' + channel_struct + ', ' + write_component_type + '>'
194
195
196format_entry_template = """    {{ FormatID::{id}, {glInternalFormat}, {fboImplementationInternalFormat}, {mipGenerationFunction}, {fastCopyFunctions}, {colorReadFunction}, {colorWriteFunction}, {namedComponentType}, {R}, {G}, {B}, {A}, {L}, {D}, {S}, {pixelBytes}, {componentAlignmentMask}, {isBlock}, {isFixed}, {isScaled}, {isSRGB}, {isYUV}, {vertexAttribType} }},
197"""
198
199
200def get_named_component_type(component_type):
201    if component_type == "snorm":
202        return "GL_SIGNED_NORMALIZED"
203    elif component_type == "unorm":
204        return "GL_UNSIGNED_NORMALIZED"
205    elif component_type == "float":
206        return "GL_FLOAT"
207    elif component_type == "uint":
208        return "GL_UNSIGNED_INT"
209    elif component_type == "int":
210        return "GL_INT"
211    elif component_type == "none":
212        return "GL_NONE"
213    else:
214        raise ValueError("Unknown component type for " + component_type)
215
216
217def get_component_alignment_mask(channels, bits):
218    if channels == None or bits == None:
219        return "std::numeric_limits<GLuint>::max()"
220    bitness = bits[channels[0].upper()]
221    for channel in channels:
222        if channel not in "rgba":
223            return "std::numeric_limits<GLuint>::max()"
224        # Can happen for RGB10A2 formats.
225        if bits[channel.upper()] != bitness:
226            return "std::numeric_limits<GLuint>::max()"
227    component_bytes = (int(bitness) >> 3)
228
229    if component_bytes == 1:
230        return "0"
231    elif component_bytes == 2:
232        return "1"
233    elif component_bytes == 4:
234        return "3"
235    else:
236        # Can happen for 4-bit RGBA.
237        return "std::numeric_limits<GLuint>::max()"
238
239
240def get_vertex_attrib_type(format_id):
241
242    has_u = "_U" in format_id
243    has_s = "_S" in format_id
244    has_float = "_FLOAT" in format_id
245    has_fixed = "_FIXED" in format_id
246    has_r8 = "R8" in format_id
247    has_r16 = "R16" in format_id
248    has_r32 = "R32" in format_id
249    has_r10 = "R10" in format_id
250    has_vertex = "VERTEX" in format_id
251
252    if has_fixed:
253        return "Fixed"
254
255    if has_float:
256        return "HalfFloat" if has_r16 else "Float"
257
258    if has_r8:
259        return "Byte" if has_s else "UnsignedByte"
260
261    if has_r10:
262        if has_vertex:
263            return "Int1010102" if has_s else "UnsignedInt1010102"
264        else:
265            return "Int2101010" if has_s else "UnsignedInt2101010"
266
267    if has_r16:
268        return "Short" if has_s else "UnsignedShort"
269
270    if has_r32:
271        return "Int" if has_s else "UnsignedInt"
272
273    # Many ANGLE formats don't correspond with vertex formats.
274    return "InvalidEnum"
275
276
277def bool_str(cond):
278    return "true" if cond else "false"
279
280
281def json_to_table_data(format_id, json, angle_to_gl):
282
283    table_data = ""
284
285    parsed = {
286        "id": format_id,
287        "fastCopyFunctions": "NoCopyFunctions",
288    }
289
290    for k, v in sorted(json.items()):
291        parsed[k] = v
292
293    if "glInternalFormat" not in parsed:
294        parsed["glInternalFormat"] = angle_to_gl[format_id]
295
296    if "fboImplementationInternalFormat" not in parsed:
297        parsed["fboImplementationInternalFormat"] = parsed["glInternalFormat"]
298
299    if "componentType" not in parsed:
300        parsed["componentType"] = angle_format.get_component_type(format_id)
301
302    if "channels" not in parsed:
303        parsed["channels"] = angle_format.get_channels(format_id)
304
305    if "bits" not in parsed:
306        parsed["bits"] = angle_format.get_bits(format_id)
307
308    # Derived values.
309    parsed["mipGenerationFunction"] = get_mip_generation_function(parsed)
310    parsed["colorReadFunction"] = get_color_read_function(parsed)
311    parsed["colorWriteFunction"] = get_color_write_function(parsed)
312
313    for channel in angle_format.kChannels:
314        if parsed["bits"] != None and channel in parsed["bits"]:
315            parsed[channel] = parsed["bits"][channel]
316        else:
317            parsed[channel] = "0"
318
319    parsed["namedComponentType"] = get_named_component_type(parsed["componentType"])
320
321    if format_id == "B8G8R8A8_UNORM":
322        parsed["fastCopyFunctions"] = "BGRACopyFunctions"
323
324    is_block = format_id.endswith("_BLOCK")
325
326    pixel_bytes = 0
327    if is_block:
328        assert 'blockPixelBytes' in parsed, \
329            'Compressed format %s requires its block size to be specified in angle_format_data.json' % \
330                format_id
331        pixel_bytes = parsed['blockPixelBytes']
332    else:
333        sum_of_bits = 0
334        for channel in angle_format.kChannels:
335            sum_of_bits += int(parsed[channel])
336        pixel_bytes = ceil_int(sum_of_bits, 8)
337    parsed["pixelBytes"] = pixel_bytes
338    parsed["componentAlignmentMask"] = get_component_alignment_mask(parsed["channels"],
339                                                                    parsed["bits"])
340    parsed["isBlock"] = bool_str(is_block)
341    parsed["isFixed"] = bool_str("FIXED" in format_id)
342    parsed["isScaled"] = bool_str("SCALED" in format_id)
343    parsed["isSRGB"] = bool_str("SRGB" in format_id)
344    # For now we only look for the 'PLANE' substring in format string. Expand this condition
345    # when adding support for YUV formats that have different identifying markers.
346    parsed["isYUV"] = bool_str("PLANE" in format_id)
347
348    parsed["vertexAttribType"] = "gl::VertexAttribType::" + get_vertex_attrib_type(format_id)
349
350    return format_entry_template.format(**parsed)
351
352
353# For convenience of the Vulkan backend, place depth/stencil formats first.  This allows
354# depth/stencil format IDs to be placed in only a few bits.
355def sorted_ds_first(all_angle):
356    ds_sorted = []
357    color_sorted = []
358    for format_id in sorted(all_angle):
359        if format_id == 'NONE':
360            continue
361        if format_id[0] == 'D' or format_id[0] == 'S':
362            ds_sorted.append(format_id)
363        else:
364            color_sorted.append(format_id)
365
366    return ds_sorted + color_sorted
367
368
369def parse_angle_format_table(all_angle, json_data, angle_to_gl):
370    table_data = ''
371    for format_id in sorted_ds_first(all_angle):
372        assert (format_id != 'NONE')
373        format_info = json_data[format_id] if format_id in json_data else {}
374        table_data += json_to_table_data(format_id, format_info, angle_to_gl)
375
376    return table_data
377
378
379def gen_enum_string(all_angle):
380    enum_data = '    NONE'
381    for format_id in sorted_ds_first(all_angle):
382        assert (format_id != 'NONE')
383        enum_data += ',\n    ' + format_id
384    return enum_data
385
386
387case_template = """        case {gl_format}:
388            return FormatID::{angle_format};
389"""
390
391
392def gen_map_switch_string(gl_to_angle):
393    switch_data = ''
394    for gl_format in sorted(gl_to_angle.keys()):
395        angle_format = gl_to_angle[gl_format]
396        switch_data += case_template.format(gl_format=gl_format, angle_format=angle_format)
397    switch_data += "        default:\n"
398    switch_data += "            return FormatID::NONE;"
399    return switch_data
400
401
402def main():
403
404    # auto_script parameters.
405    if len(sys.argv) > 1:
406        inputs = ['angle_format.py', 'angle_format_data.json', 'angle_format_map.json']
407        outputs = ['Format_table_autogen.cpp', 'FormatID_autogen.h']
408
409        if sys.argv[1] == 'inputs':
410            print(','.join(inputs))
411        elif sys.argv[1] == 'outputs':
412            print(','.join(outputs))
413        else:
414            print('Invalid script parameters')
415            return 1
416        return 0
417
418    gl_to_angle = angle_format.load_forward_table('angle_format_map.json')
419    angle_to_gl = angle_format.load_inverse_table('angle_format_map.json')
420    data_source_name = 'angle_format_data.json'
421    json_data = angle_format.load_json(data_source_name)
422    all_angle = angle_to_gl.keys()
423
424    angle_format_cases = parse_angle_format_table(all_angle, json_data, angle_to_gl)
425    switch_data = gen_map_switch_string(gl_to_angle)
426    output_cpp = template_autogen_inl.format(
427        script_name=sys.argv[0],
428        angle_format_info_cases=angle_format_cases,
429        angle_format_switch=switch_data,
430        data_source_name=data_source_name)
431    with open('Format_table_autogen.cpp', 'wt') as out_file:
432        out_file.write(output_cpp)
433        out_file.close()
434
435    enum_data = gen_enum_string(all_angle)
436    num_angle_formats = len(all_angle)
437    output_h = template_autogen_h.format(
438        script_name=sys.argv[0],
439        angle_format_enum=enum_data,
440        data_source_name=data_source_name,
441        num_angle_formats=num_angle_formats)
442    with open('FormatID_autogen.h', 'wt') as out_file:
443        out_file.write(output_h)
444        out_file.close()
445
446    return 0
447
448
449if __name__ == '__main__':
450    sys.exit(main())
451