#!/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 {script_name} using data from {data_source_name} // // 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 constexpr 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' elif angle_format['componentType'] == 'unorm' and angle_format['bits']['red'] == 8: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'unorm' and angle_format['bits']['red'] == 16: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'int' and angle_format['bits']['red'] == 8: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'snorm' and angle_format['bits']['red'] == 8: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'snorm' and angle_format['bits']['red'] == 16: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'float' and angle_format['bits']['red'] == 16: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'uint' and angle_format['bits']['red'] == 16: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'int' and angle_format['bits']['red'] == 16: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'float' and angle_format['bits']['red'] == 32: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'int' and angle_format['bits']['red'] == 32: internal_format_initializer = 'Initialize4ComponentData' elif angle_format['componentType'] == 'uint' and angle_format['bits']['red'] == 32: internal_format_initializer = 'Initialize4ComponentData' 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 constexpr Format info({internalFormat}, {space} angle::Format::ID::{formatName}, {space} {texFormat}, {space} {srvFormat}, {space} {rtvFormat}, {space} {dsvFormat}, {space} {blitSRVFormat}, {space} {swizzleFormat}, {space} {initializer}); {space} return info; {space}}} """ split_format_entry_template = """{space} {condition} {space} {{ {space} static constexpr Format info({internalFormat}, {space} angle::Format::ID::{formatName}, {space} {texFormat}, {space} {srvFormat}, {space} {rtvFormat}, {space} {dsvFormat}, {space} {blitSRVFormat}, {space} {swizzleFormat}, {space} {initializer}); {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')) data_source_name = 'texture_format_data.json' with open(data_source_name) 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( script_name = sys.argv[0], copyright_year = date.today().year, angle_format_info_cases = angle_format_cases, data_source_name = data_source_name) with open('texture_format_table_autogen.cpp', 'wt') as out_file: out_file.write(output_cpp) out_file.close()