|  | #!/usr/bin/env python3 | 
|  | # Copyright 2022 The Dawn Authors | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  |  | 
|  | import json, os, sys | 
|  | from collections import namedtuple | 
|  |  | 
|  | from generator_lib import Generator, run_generator, FileRender | 
|  |  | 
|  |  | 
|  | def parse_mask(mask): | 
|  | if mask: | 
|  | return int(mask, 0) | 
|  | return 0xffffffff | 
|  |  | 
|  |  | 
|  | class Name: | 
|  | def __init__(self, name): | 
|  | self.name = name | 
|  | self.chunks = name.split(' ') | 
|  |  | 
|  | def get(self): | 
|  | return self.name | 
|  |  | 
|  | def CamelChunk(self, chunk): | 
|  | return chunk[0].upper() + chunk[1:] | 
|  |  | 
|  | def canonical_case(self): | 
|  | return (' '.join(self.chunks)).lower() | 
|  |  | 
|  | def concatcase(self): | 
|  | return ''.join(self.chunks) | 
|  |  | 
|  | def camelCase(self): | 
|  | return self.chunks[0] + ''.join( | 
|  | [self.CamelChunk(chunk) for chunk in self.chunks[1:]]) | 
|  |  | 
|  | def CamelCase(self): | 
|  | return ''.join([self.CamelChunk(chunk) for chunk in self.chunks]) | 
|  |  | 
|  | def SNAKE_CASE(self): | 
|  | return '_'.join([chunk.upper() for chunk in self.chunks]) | 
|  |  | 
|  | def snake_case(self): | 
|  | return '_'.join(self.chunks) | 
|  |  | 
|  | def js_enum_case(self): | 
|  | result = self.chunks[0].lower() | 
|  | for chunk in self.chunks[1:]: | 
|  | if not result[-1].isdigit(): | 
|  | result += '-' | 
|  | result += chunk.lower() | 
|  | return result | 
|  |  | 
|  |  | 
|  | class Architecture: | 
|  | def __init__(self, name, json_data, mask): | 
|  | self.name = Name(name) | 
|  | self.devices = [] | 
|  |  | 
|  | mask_num = parse_mask(mask) | 
|  |  | 
|  | for device in json_data: | 
|  | device_num = int(device, 0) | 
|  |  | 
|  | # Don't allow duplicate entries | 
|  | assert device not in self.devices, 'Architecture "{}" contained duplicate deviceID "{}"'.format( | 
|  | self.name.get(), device) | 
|  | # Ensure that all device IDs don't contain bits outside the mask | 
|  | assert device_num & mask_num == device_num, 'Architecture "{}" contained deviceID "{}" which doesn\'t match the given mask of "{}"'.format( | 
|  | self.name.get(), device, mask) | 
|  |  | 
|  | self.devices.append(device) | 
|  |  | 
|  |  | 
|  | class DeviceSet: | 
|  | def __init__(self, json_data): | 
|  | self.mask = None | 
|  | self.internal = False | 
|  |  | 
|  | if 'mask' in json_data: | 
|  | self.mask = json_data['mask'] | 
|  |  | 
|  | if 'internal' in json_data: | 
|  | self.internal = json_data['internal'] | 
|  |  | 
|  | self.architectures = [] | 
|  | if 'architecture' in json_data: | 
|  | for (arch_name, arch_data) in json_data['architecture'].items(): | 
|  | # Skip any entries that start with an underscore. Used for comments. | 
|  | if arch_name[0] == '_': | 
|  | continue | 
|  |  | 
|  | architecture = Architecture(arch_name, arch_data, self.mask) | 
|  |  | 
|  | # Validate that deviceIDs are only allowed to be in one Architecture at a time | 
|  | for other_architecture in self.architectures: | 
|  | for device in architecture.devices: | 
|  | assert device not in other_architecture.devices, 'Architectures "{}" and "{}" both contain deviceID "{}"'.format( | 
|  | architecture.name.get(), | 
|  | other_architecture.name.get(), device) | 
|  |  | 
|  | self.architectures.append(architecture) | 
|  |  | 
|  | def validate_devices(self, other_devices, other_mask): | 
|  | combined_mask = parse_mask(self.mask) & parse_mask(other_mask) | 
|  |  | 
|  | for other_device in other_devices: | 
|  | other_device_num = int(other_device, 0) & combined_mask | 
|  | for architecture in self.architectures: | 
|  | for device in architecture.devices: | 
|  | device_num = int(device, 0) & combined_mask | 
|  | assert device_num != other_device_num, 'DeviceID "{}" & mask "{}" conflicts with deviceId "{}" & mask "{}" in architecture "{}"'.format( | 
|  | other_device, other_mask, device, self.mask, | 
|  | architecture.name.get()) | 
|  |  | 
|  | def maskDeviceId(self): | 
|  | if not self.mask: | 
|  | return '' | 
|  | return ' & ' + self.mask | 
|  |  | 
|  |  | 
|  | class Vendor: | 
|  | def __init__(self, name, json_data): | 
|  | self.name = Name(name) | 
|  | self.id = json_data['id'] | 
|  |  | 
|  | architecture_dict = {} | 
|  | internal_architecture_dict = {} | 
|  |  | 
|  | self.device_sets = [] | 
|  | if 'devices' in json_data: | 
|  | for device_data in json_data['devices']: | 
|  | device_set = DeviceSet(device_data) | 
|  |  | 
|  | for architecture in device_set.architectures: | 
|  |  | 
|  | # Validate that deviceIDs are unique across device sets | 
|  | for other_device_set in self.device_sets: | 
|  | # Only validate device IDs between internal and public device sets. | 
|  | if other_device_set.internal == device_set.internal: | 
|  | assert device_set.mask != other_device_set.mask, 'Vendor "{}" contained duplicate device masks "{}"'.format( | 
|  | self.name.get(), device_set.mask) | 
|  | other_device_set.validate_devices( | 
|  | architecture.devices, device_set.mask) | 
|  |  | 
|  | # Validate that architecture names are unique between internal and public device sets. | 
|  | else: | 
|  | for other_architecture in other_device_set.architectures: | 
|  | assert architecture.name.canonical_case( | 
|  | ) != other_architecture.name.canonical_case( | 
|  | ), '"{}" is defined as both an internal and public architecture'.format( | 
|  | architecture.name.get()) | 
|  |  | 
|  | if device_set.internal: | 
|  | internal_architecture_dict[ | 
|  | architecture.name.canonical_case( | 
|  | )] = architecture.name | 
|  | else: | 
|  | architecture_dict[architecture.name.canonical_case( | 
|  | )] = architecture.name | 
|  |  | 
|  | self.device_sets.append(device_set) | 
|  |  | 
|  | # List of unique architecture names under this vendor | 
|  | self.architecture_names = architecture_dict.values() | 
|  | self.internal_architecture_names = internal_architecture_dict.values() | 
|  |  | 
|  |  | 
|  | def parse_json(json): | 
|  | vendors = [] | 
|  | internal_architecture_count = 0 | 
|  |  | 
|  | for (vendor_name, vendor_data) in json['vendors'].items(): | 
|  | # Skip vendors that have a leading underscore. Those are intended to be "comments". | 
|  | if vendor_name[0] == '_': | 
|  | continue | 
|  |  | 
|  | vendor = Vendor(vendor_name, vendor_data) | 
|  | vendors.append(vendor) | 
|  | internal_architecture_count += len(vendor.internal_architecture_names) | 
|  |  | 
|  | return { | 
|  | 'vendors': vendors, | 
|  | 'has_internal': internal_architecture_count > 0 | 
|  | } | 
|  |  | 
|  |  | 
|  | class DawnGpuInfoGenerator(Generator): | 
|  | def get_description(self): | 
|  | return "Generates GPU Info Dawn code." | 
|  |  | 
|  | def add_commandline_arguments(self, parser): | 
|  | parser.add_argument('--gpu-info-json', | 
|  | required=True, | 
|  | type=str, | 
|  | help='The GPU Info JSON definition to use.') | 
|  |  | 
|  | def get_dependencies(self, args): | 
|  | return [os.path.abspath(args.gpu_info_json)] | 
|  |  | 
|  | def get_file_renders(self, args): | 
|  | with open(args.gpu_info_json) as f: | 
|  | loaded_json = json.loads(f.read()) | 
|  |  | 
|  | params = parse_json(loaded_json) | 
|  |  | 
|  | return [ | 
|  | FileRender("dawn/common/GPUInfo.h", | 
|  | "src/dawn/common/GPUInfo_autogen.h", [params]), | 
|  | FileRender("dawn/common/GPUInfo.cpp", | 
|  | "src/dawn/common/GPUInfo_autogen.cpp", [params]), | 
|  | ] | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | sys.exit(run_generator(DawnGpuInfoGenerator())) |