xref: /aosp_15_r20/external/mesa3d/src/intel/isl/gen_format_layout.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1# encoding=utf-8
2# Copyright © 2016 Intel Corporation
3
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20# SOFTWARE.
21
22"""Generates isl_format_layout.c."""
23
24import argparse
25import csv
26import re
27
28from mako import template
29
30# Load the template and set the bytes encoding to be utf-8.
31TEMPLATE = template.Template(text="""\
32/* This file is autogenerated by gen_format_layout.py. DO NOT EDIT! */
33
34/*
35 * Copyright 2015 Intel Corporation
36 *
37 * Permission is hereby granted, free of charge, to any person obtaining a
38 * copy of this software and associated documentation files (the "Software"),
39 * to deal in the Software without restriction, including without limitation
40 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
41 * and/or sell copies of the Software, and to permit persons to whom the
42 * Software is furnished to do so, subject to the following conditions:
43 *
44 * The above copyright notice and this permission notice (including the next
45 * paragraph) shall be included in all copies or substantial portions of the
46 * Software.
47 *
48 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
51 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
53 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
54 * IN THE SOFTWARE.
55 */
56
57#include "isl/isl.h"
58
59const uint16_t isl_format_name_offsets[] = { <% offset = 0 %>
60% for format in formats:
61    [ISL_FORMAT_${format.name}] = ${offset}, <% offset += 11 + len(format.name) + 1 %>
62% endfor
63};
64
65const char isl_format_names[] = {
66% for format in formats:
67  "ISL_FORMAT_${format.name}\\0"
68% endfor
69};
70
71const struct isl_format_layout
72isl_format_layouts[] = {
73% for format in formats:
74  [ISL_FORMAT_${format.name}] = {
75    .format = ISL_FORMAT_${format.name},
76    .bpb = ${format.bpb},
77    .bw = ${format.bw},
78    .bh = ${format.bh},
79    .bd = ${format.bd},
80    .channels = {
81    % for mask in ['r', 'g', 'b', 'a', 'l', 'i', 'p']:
82      <% channel = getattr(format, mask, None) %>\\
83      % if channel.type is not None:
84        .${mask} = { ISL_${channel.type}, ${channel.start}, ${channel.size} },
85      % else:
86        .${mask} = {},
87      % endif
88    % endfor
89    },
90    .uniform_channel_type = ISL_${format.uniform_channel_type},
91    .colorspace = ISL_COLORSPACE_${format.colorspace},
92    .txc = ISL_TXC_${format.txc},
93  },
94
95% endfor
96};
97
98bool
99isl_format_is_valid(enum isl_format format)
100{
101    if (format >= sizeof(isl_format_layouts) / sizeof(isl_format_layouts[0]))
102        return false;
103
104    /* Only ISL_FORMAT_R32G32B32A32_FLOAT == 0 but that's a valid format.
105     * For all others, if this doesn't match then the entry in the table
106     * must not exist.
107     */
108    return isl_format_layouts[format].format == format;
109}
110
111enum isl_format
112isl_format_srgb_to_linear(enum isl_format format)
113{
114    switch (format) {
115% for srgb, rgb in srgb_to_linear_map:
116    case ISL_FORMAT_${srgb}:
117        return ISL_FORMAT_${rgb};
118%endfor
119    default:
120        return format;
121    }
122}
123""")
124
125
126class Channel(object):
127    """Class representing a Channel.
128
129    Converts the csv encoded data into the format that the template (and thus
130    the consuming C code) expects.
131
132    """
133    # If the csv file grew very large this class could be put behind a factory
134    # to increase efficiency. Right now though it's fast enough that It didn't
135    # seem worthwhile to add all of the boilerplate
136    _types = {
137        'x': 'void',
138        'r': 'raw',
139        'un': 'unorm',
140        'sn': 'snorm',
141        'uf': 'ufloat',
142        'sf': 'sfloat',
143        'ux': 'ufixed',
144        'sx': 'sfixed',
145        'ui': 'uint',
146        'si': 'sint',
147        'us': 'uscaled',
148        'ss': 'sscaled',
149    }
150    _splitter = re.compile(r'\s*(?P<type>[a-z]+)(?P<size>[0-9]+)')
151
152    def __init__(self, line):
153        # If the line is just whitespace then just set everything to None to
154        # save on the regex cost and let the template skip on None.
155        if line.isspace():
156            self.size = None
157            self.type = None
158        else:
159            grouped = self._splitter.match(line)
160            self.type = self._types[grouped.group('type')].upper()
161            self.size = int(grouped.group('size'))
162
163        # Default the start bit to -1
164        self.start = -1
165
166
167class Format(object):
168    """Class that contains all values needed by the template."""
169    def __init__(self, line):
170        # pylint: disable=invalid-name
171        self.name = line[0].strip()
172
173        self.bpb = int(line[1])
174        self.bw = line[2].strip()
175        self.bh = line[3].strip()
176        self.bd = line[4].strip()
177        self.r = Channel(line[5])
178        self.g = Channel(line[6])
179        self.b = Channel(line[7])
180        self.a = Channel(line[8])
181        self.l = Channel(line[9])
182        self.i = Channel(line[10])
183        self.p = Channel(line[11])
184
185        # Set the start bit value for each channel
186        self.order = line[12].strip()
187        bit = 0
188        for c in self.order:
189            chan = getattr(self, c)
190            chan.start = bit
191            bit = bit + chan.size
192
193        # Set the uniform channel type, if the format has one.
194        #
195        # Iterate over all channels, not just those in self.order, because
196        # some formats have an empty 'order' field in the CSV (such as
197        # YCRCB_NORMAL).
198        self.uniform_channel_type = 'VOID'
199        for chan in self.channels:
200            if chan.type in (None, 'VOID'):
201                pass
202            elif self.uniform_channel_type == 'VOID':
203                self.uniform_channel_type = chan.type
204            elif self.uniform_channel_type == chan.type:
205                pass
206            else:
207                self.uniform_channel_type = 'VOID'
208                break
209
210        # alpha doesn't have a colorspace of it's own.
211        self.colorspace = line[13].strip().upper()
212        if self.colorspace in ['']:
213            self.colorspace = 'NONE'
214
215        # This sets it to the line value, or if it's an empty string 'NONE'
216        self.txc = line[14].strip().upper() or 'NONE'
217
218
219    @property
220    def channels(self):
221        yield self.r
222        yield self.g
223        yield self.b
224        yield self.a
225        yield self.l
226        yield self.i
227        yield self.p
228
229
230def reader(csvfile):
231    """Wrapper around csv.reader that skips comments and blanks."""
232    # csv.reader actually reads the file one line at a time (it was designed to
233    # open excel generated sheets), so hold the file until all of the lines are
234    # read.
235    with open(csvfile, 'r') as f:
236        for line in csv.reader(f):
237            if line and not line[0].startswith('#'):
238                yield line
239
240def get_srgb_to_linear_map(formats):
241    """Compute a map from sRGB to linear formats.
242
243    This function uses some probably somewhat fragile string munging to do
244    the conversion.  However, we do assert that, if it's SRGB, the munging
245    succeeded so that gives some safety.
246    """
247    names = {f.name for f in formats}
248    for fmt in formats:
249        if fmt.colorspace != 'SRGB':
250            continue
251
252        replacements = [
253            ('_SRGB',   ''),
254            ('SRGB',    'RGB'),
255            ('U8SRGB',  'FLT16'),
256            # Quirk: ETC2_EAC_SRGB8_A8 -> ETC2_EAC_RGBA8
257            ('SRGB8_A8', 'RGBA8'),
258        ]
259
260        found = False
261        for rep in replacements:
262            rgb_name = fmt.name.replace(rep[0], rep[1])
263            if rgb_name in names and rgb_name != fmt.name:
264                found = True
265                yield fmt.name, rgb_name
266                break
267
268        # We should have found a format name
269        assert found
270
271def main():
272    """Main function."""
273    parser = argparse.ArgumentParser()
274    parser.add_argument('--csv', action='store', help='The CSV file to parse.')
275    parser.add_argument(
276        '--out',
277        action='store',
278        help='The location to put the generated C file.')
279    args = parser.parse_args()
280
281    # This generator opens and writes the file itself, and it does so in bytes
282    # mode. This solves the locale problem: Unicode can be rendered even
283    # if the shell calling this script doesn't.
284    with open(args.out, 'w', encoding='utf-8') as f:
285        formats = [Format(l) for l in reader(args.csv)]
286        try:
287            # This basically does lazy evaluation and initialization, which
288            # saves on memory and startup overhead.
289            f.write(TEMPLATE.render(
290                formats             = formats,
291                srgb_to_linear_map  = list(get_srgb_to_linear_map(formats)),
292            ))
293        except Exception:
294            # In the even there's an error this imports some helpers from mako
295            # to print a useful stack trace and prints it, then exits with
296            # status 1, if python is run with debug; otherwise it just raises
297            # the exception
298            if __debug__:
299                import sys
300                from mako import exceptions
301                print(exceptions.text_error_template().render(),
302                      file=sys.stderr)
303                sys.exit(1)
304            raise
305
306
307if __name__ == '__main__':
308    main()
309