summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/libANGLE/renderer/d3d/d3d11/gen_texture_format_table.py
blob: 981a77f51abb1db49c75552b6113c0b31345bc87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#!/usr/bin/python
# Copyright 2015 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# gen_texture_format_table.py:
#  Code generation for texture format map
#

from datetime import date
import json
import math
import pprint
import os
import re
import sys

sys.path.append('../..')
import angle_format

template_texture_format_table_autogen_cpp = """// GENERATED FILE - DO NOT EDIT.
// Generated by gen_texture_format_table.py using data from texture_format_data.json
//
// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// texture_format_table:
//   Queries for full textureFormat information based in internalFormat
//

#include "libANGLE/renderer/d3d/d3d11/texture_format_table.h"

#include "image_util/copyimage.h"
#include "image_util/generatemip.h"
#include "image_util/loadimage.h"

#include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
#include "libANGLE/renderer/d3d/d3d11/texture_format_table_utils.h"

using namespace angle;

namespace rx
{{

namespace d3d11
{{

// static
const Format &Format::Get(GLenum internalFormat, const Renderer11DeviceCaps &deviceCaps)
{{
    // clang-format off
    switch (internalFormat)
    {{
{angle_format_info_cases}
        default:
            break;
    }}
    // clang-format on

    UNREACHABLE();
    static const Format defaultInfo;
    return defaultInfo;
}}

}}  // namespace d3d11

}}  // namespace rx
"""

# TODO(oetuaho): Expand this code so that it could generate the gl format info tables as well.
def gl_format_channels(internal_format):
    if internal_format == 'GL_BGR5_A1_ANGLEX':
        return 'bgra'
    if internal_format == 'GL_R11F_G11F_B10F':
        return 'rgb'
    if internal_format == 'GL_RGB5_A1':
        return 'rgba'
    if internal_format.find('GL_RGB10_A2') == 0:
        return 'rgba'

    channels_pattern = re.compile('GL_(COMPRESSED_)?(SIGNED_)?(ETC\d_)?([A-Z]+)')
    match = re.search(channels_pattern, internal_format)
    channels_string = match.group(4)

    if channels_string == 'ALPHA':
        return 'a'
    if channels_string == 'LUMINANCE':
        if (internal_format.find('ALPHA') >= 0):
            return 'la'
        return 'l'
    if channels_string == 'SRGB':
        if (internal_format.find('ALPHA') >= 0):
            return 'rgba'
        return 'rgb'
    if channels_string == 'DEPTH':
        if (internal_format.find('STENCIL') >= 0):
            return 'ds'
        return 'd'
    if channels_string == 'STENCIL':
        return 's'
    return channels_string.lower()

def get_internal_format_initializer(internal_format, angle_format):
    internal_format_initializer = 'nullptr'
    gl_channels = gl_format_channels(internal_format)
    gl_format_no_alpha = gl_channels == 'rgb' or gl_channels == 'l'
    if gl_format_no_alpha and angle_format['channels'] == 'rgba':
        if angle_format['texFormat'] == 'DXGI_FORMAT_BC1_UNORM':
            # BC1 is a special case since the texture data determines whether each block has an alpha channel or not.
            # This if statement is hit by COMPRESSED_RGB_S3TC_DXT1, which is a bit of a mess.
            # TODO(oetuaho): Look into whether COMPRESSED_RGB_S3TC_DXT1 works right in general.
            # Reference: https://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt
            pass
        elif 'componentType' not in angle_format:
            raise ValueError('warning: internal format initializer could not be generated and may be needed for ' + internal_format)
        elif angle_format['componentType'] == 'uint' and angle_format['bits']['red'] == 8:
            internal_format_initializer = 'Initialize4ComponentData<GLubyte, 0x00, 0x00, 0x00, 0x01>'
        elif angle_format['componentType'] == 'unorm' and angle_format['bits']['red'] == 8:
            internal_format_initializer = 'Initialize4ComponentData<GLubyte, 0x00, 0x00, 0x00, 0xFF>'
        elif angle_format['componentType'] == 'unorm' and angle_format['bits']['red'] == 16:
            internal_format_initializer = 'Initialize4ComponentData<GLubyte, 0x0000, 0x0000, 0x0000, 0xFFFF>'
        elif angle_format['componentType'] == 'int' and angle_format['bits']['red'] == 8:
            internal_format_initializer = 'Initialize4ComponentData<GLbyte, 0x00, 0x00, 0x00, 0x01>'
        elif angle_format['componentType'] == 'snorm' and angle_format['bits']['red'] == 8:
            internal_format_initializer = 'Initialize4ComponentData<GLbyte, 0x00, 0x00, 0x00, 0x7F>'
        elif angle_format['componentType'] == 'snorm' and angle_format['bits']['red'] == 16:
            internal_format_initializer = 'Initialize4ComponentData<GLushort, 0x0000, 0x0000, 0x0000, 0x7FFF>'
        elif angle_format['componentType'] == 'float' and angle_format['bits']['red'] == 16:
            internal_format_initializer = 'Initialize4ComponentData<GLhalf, 0x0000, 0x0000, 0x0000, gl::Float16One>'
        elif angle_format['componentType'] == 'uint' and angle_format['bits']['red'] == 16:
            internal_format_initializer = 'Initialize4ComponentData<GLushort, 0x0000, 0x0000, 0x0000, 0x0001>'
        elif angle_format['componentType'] == 'int' and angle_format['bits']['red'] == 16:
            internal_format_initializer = 'Initialize4ComponentData<GLshort, 0x0000, 0x0000, 0x0000, 0x0001>'
        elif angle_format['componentType'] == 'float' and angle_format['bits']['red'] == 32:
            internal_format_initializer = 'Initialize4ComponentData<GLfloat, 0x00000000, 0x00000000, 0x00000000, gl::Float32One>'
        elif angle_format['componentType'] == 'int' and angle_format['bits']['red'] == 32:
            internal_format_initializer = 'Initialize4ComponentData<GLint, 0x00000000, 0x00000000, 0x00000000, 0x00000001>'
        elif angle_format['componentType'] == 'uint' and angle_format['bits']['red'] == 32:
            internal_format_initializer = 'Initialize4ComponentData<GLuint, 0x00000000, 0x00000000, 0x00000000, 0x00000001>'
        else:
            raise ValueError('warning: internal format initializer could not be generated and may be needed for ' + internal_format)

    return internal_format_initializer

def get_swizzle_format_id(internal_format, angle_format):
    angle_format_id = angle_format["formatName"]
    if (internal_format == 'GL_NONE') or (angle_format_id == 'NONE'):
        return 'GL_NONE'

    elif 'swizzleFormat' in angle_format:
        # For some special formats like compressed formats that don't have a clearly defined number
        # of bits per channel, swizzle format needs to be specified manually.
        return angle_format['swizzleFormat']

    if 'bits' not in angle_format:
        raise ValueError('no bits information for determining swizzleformat for format: ' + internal_format)

    bits = angle_format['bits']
    max_component_bits = max(bits.itervalues())
    channels_different = not all([component_bits == bits.itervalues().next() for component_bits in bits.itervalues()])

    # The format itself can be used for swizzles if it can be accessed as a render target and
    # sampled and the bit count for all 4 channels is the same.
    if "rtvFormat" in angle_format and "srvFormat" in angle_format and not channels_different and len(angle_format['channels']) == 4:
        return angle_format["glInternalFormat"] if "glInternalFormat" in angle_format else internal_format

    b = int(math.ceil(float(max_component_bits) / 8) * 8)

    # Depth formats need special handling, since combined depth/stencil formats don't have a clearly
    # defined component type.
    if angle_format['channels'].find('d') >= 0:
        if b == 24 or b == 32:
            return 'GL_RGBA32F'
        if b == 16:
            return 'GL_RGBA16_EXT'

    if b == 24:
        raise ValueError('unexpected 24-bit format when determining swizzleformat for format: ' + internal_format)

    if 'componentType' not in angle_format:
        raise ValueError('no component type information for determining swizzleformat for format: ' + internal_format)

    component_type = angle_format['componentType']

    swizzle = "GL_RGBA" + str(b)

    if component_type == 'uint':
        swizzle += "I"
    elif component_type == 'int':
        swizzle += "I"
    elif component_type == 'unorm':
        if (b == 16):
            swizzle += "_EXT"
    elif component_type == 'snorm':
        swizzle += "_SNORM"
        if (b == 16):
            swizzle += "_EXT"
    elif component_type == 'float':
        swizzle += "F"
        if (b == 16):
            swizzle += "_EXT"
    else:
        raise ValueError('could not determine swizzleformat based on componentType for format: ' + internal_format)

    return swizzle

def get_blit_srv_format(angle_format):
    if 'channels' not in angle_format:
        return 'DXGI_FORMAT_UNKNOWN'
    if 'r' in angle_format['channels'] and angle_format['componentType'] in ['int', 'uint']:
        return angle_format['rtvFormat']

    return angle_format["srvFormat"] if "srvFormat" in angle_format else "DXGI_FORMAT_UNKNOWN"


format_entry_template = """{space}{{
{space}    static const Format info({internalFormat},
{space}                             angle::Format::ID::{formatName},
{space}                             {texFormat},
{space}                             {srvFormat},
{space}                             {rtvFormat},
{space}                             {dsvFormat},
{space}                             {blitSRVFormat},
{space}                             {swizzleFormat},
{space}                             {initializer},
{space}                             deviceCaps);
{space}    return info;
{space}}}
"""

split_format_entry_template = """{space}    {condition}
{space}    {{
{space}        static const Format info({internalFormat},
{space}                                 angle::Format::ID::{formatName},
{space}                                 {texFormat},
{space}                                 {srvFormat},
{space}                                 {rtvFormat},
{space}                                 {dsvFormat},
{space}                                 {blitSRVFormat},
{space}                                 {swizzleFormat},
{space}                                 {initializer},
{space}                                 deviceCaps);
{space}        return info;
{space}    }}
"""

def json_to_table_data(internal_format, format_name, prefix, json):

    table_data = ""

    parsed = {
        "space": "        ",
        "internalFormat": internal_format,
        "formatName": format_name,
        "texFormat": "DXGI_FORMAT_UNKNOWN",
        "srvFormat": "DXGI_FORMAT_UNKNOWN",
        "rtvFormat": "DXGI_FORMAT_UNKNOWN",
        "dsvFormat": "DXGI_FORMAT_UNKNOWN",
        "condition": prefix,
    }

    for k, v in json.iteritems():
        parsed[k] = v

    # Derived values.
    parsed["blitSRVFormat"] = get_blit_srv_format(parsed)
    parsed["swizzleFormat"] = get_swizzle_format_id(internal_format, parsed)
    parsed["initializer"]   = get_internal_format_initializer(internal_format, json)

    if len(prefix) > 0:
        return split_format_entry_template.format(**parsed)
    else:
        return format_entry_template.format(**parsed)

def parse_json_angle_format_case(format_name, angle_format, json_data):
    supported_case = {}
    unsupported_case = {}
    support_test = None
    fallback = None

    for k, v in angle_format.iteritems():
        if k == "FL10Plus":
            assert support_test is None
            support_test = "OnlyFL10Plus(deviceCaps)"
            for k2, v2 in v.iteritems():
                supported_case[k2] = v2
        elif k == "FL9_3":
            split = True
            for k2, v2 in v.iteritems():
                unsupported_case[k2] = v2
        elif k == "supportTest":
            assert support_test is None
            support_test = v
        elif k == "fallbackFormat":
            fallback = v
        else:
            supported_case[k] = v
            unsupported_case[k] = v

    if fallback != None:
        unsupported_case, _, _ = parse_json_angle_format_case(
            fallback, json_data[fallback], json_data)
        unsupported_case["formatName"] = fallback

    if support_test != None:
        return supported_case, unsupported_case, support_test
    else:
        return supported_case, None, None

def parse_json_into_switch_angle_format_string(json_map, json_data):
    table_data = ''

    for internal_format, format_name in sorted(json_map.iteritems()):

        if format_name not in json_data:
            continue

        angle_format = json_data[format_name]

        supported_case, unsupported_case, support_test = parse_json_angle_format_case(
            format_name, angle_format, json_data)

        table_data += '        case ' + internal_format + ':\n'

        if support_test != None:
            table_data += "        {\n"
            table_data += json_to_table_data(internal_format, format_name, "if (" + support_test + ")", supported_case)
            table_data += json_to_table_data(internal_format, format_name, "else", unsupported_case)
            table_data += "        }\n"
        else:
            table_data += json_to_table_data(internal_format, format_name, "", supported_case)

    return table_data

def reject_duplicate_keys(pairs):
    found_keys = {}
    for key, value in pairs:
        if key in found_keys:
           raise ValueError("duplicate key: %r" % (key,))
        else:
           found_keys[key] = value
    return found_keys

json_map = angle_format.load_with_override(os.path.abspath('texture_format_map.json'))

with open('texture_format_data.json') as texture_format_json_file:
    texture_format_data = texture_format_json_file.read()
    texture_format_json_file.close()
    json_data = json.loads(texture_format_data, object_pairs_hook=angle_format.reject_duplicate_keys)

    angle_format_cases = parse_json_into_switch_angle_format_string(json_map, json_data)
    output_cpp = template_texture_format_table_autogen_cpp.format(
        copyright_year=date.today().year,
        angle_format_info_cases=angle_format_cases)
    with open('texture_format_table_autogen.cpp', 'wt') as out_file:
        out_file.write(output_cpp)
        out_file.close()