1#!/usr/bin/env python3 2# Copyright 2019 The Dawn Authors 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import os, json, sys 17from collections import namedtuple 18import xml.etree.ElementTree as etree 19 20from generator_lib import Generator, run_generator, FileRender 21 22 23class ProcName: 24 def __init__(self, gl_name, proc_name=None): 25 assert gl_name.startswith('gl') 26 if proc_name == None: 27 proc_name = gl_name[2:] 28 29 self.gl_name = gl_name 30 self.proc_name = proc_name 31 32 def glProcName(self): 33 return self.gl_name 34 35 def ProcName(self): 36 return self.proc_name 37 38 def PFNPROCNAME(self): 39 return 'PFN' + self.gl_name.upper() + 'PROC' 40 41 def __repr__(self): 42 return 'Proc("{}", "{}")'.format(self.gl_name, self.proc_name) 43 44 45ProcParam = namedtuple('ProcParam', ['name', 'type']) 46 47 48class Proc: 49 def __init__(self, element): 50 # Type declaration for return values and arguments all have the same 51 # (weird) format. 52 # <element>[A][<ptype>B</ptype>][C]<other stuff.../></element> 53 # 54 # Some examples are: 55 # <proto>void <name>glFinish</name></proto> 56 # <proto><ptype>GLenum</ptype><name>glFenceSync</name></proto> 57 # <proto>const <ptype>GLubyte</ptype> *<name>glGetString</name></proto> 58 # 59 # This handles all the shapes found in gl.xml except for this one that 60 # has an array specifier after </name>: 61 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param> 62 def parse_type_declaration(element): 63 result = '' 64 if element.text != None: 65 result += element.text 66 ptype = element.find('ptype') 67 if ptype != None: 68 result += ptype.text 69 if ptype.tail != None: 70 result += ptype.tail 71 return result.strip() 72 73 proto = element.find('proto') 74 75 self.return_type = parse_type_declaration(proto) 76 77 self.params = [] 78 for param in element.findall('./param'): 79 self.params.append( 80 ProcParam( 81 param.find('name').text, parse_type_declaration(param))) 82 83 self.gl_name = proto.find('name').text 84 self.alias = None 85 if element.find('alias') != None: 86 self.alias = element.find('alias').attrib['name'] 87 88 def glProcName(self): 89 return self.gl_name 90 91 def ProcName(self): 92 assert self.gl_name.startswith('gl') 93 return self.gl_name[2:] 94 95 def PFNGLPROCNAME(self): 96 return 'PFN' + self.gl_name.upper() + 'PROC' 97 98 def __repr__(self): 99 return 'Proc("{}")'.format(self.gl_name) 100 101 102EnumDefine = namedtuple('EnumDefine', ['name', 'value']) 103Version = namedtuple('Version', ['major', 'minor']) 104VersionBlock = namedtuple('VersionBlock', ['version', 'procs', 'enums']) 105HeaderBlock = namedtuple('HeaderBlock', ['description', 'procs', 'enums']) 106ExtensionBlock = namedtuple('ExtensionBlock', 107 ['extension', 'procs', 'enums', 'supported_specs']) 108 109 110def parse_version(version): 111 return Version(*map(int, version.split('.'))) 112 113 114def compute_params(root, supported_extensions): 115 # Parse all the commands and enums 116 all_procs = {} 117 for command in root.findall('''commands[@namespace='GL']/command'''): 118 proc = Proc(command) 119 assert proc.gl_name not in all_procs 120 all_procs[proc.gl_name] = proc 121 122 all_enums = {} 123 for enum in root.findall('''enums[@namespace='GL']/enum'''): 124 enum_name = enum.attrib['name'] 125 # Special case an enum we'll never use that has different values in GL and GLES 126 if enum_name == 'GL_ACTIVE_PROGRAM_EXT': 127 continue 128 129 assert enum_name not in all_enums 130 all_enums[enum_name] = EnumDefine(enum_name, enum.attrib['value']) 131 132 # Get the list of all Desktop OpenGL function removed by the Core Profile. 133 core_removed_procs = set() 134 for proc in root.findall('''feature/remove[@profile='core']/command'''): 135 core_removed_procs.add(proc.attrib['name']) 136 137 # Get list of enums and procs per OpenGL ES/Desktop OpenGL version 138 def parse_version_blocks(api, removed_procs=set()): 139 blocks = [] 140 for section in root.findall('''feature[@api='{}']'''.format(api)): 141 section_procs = [] 142 for command in section.findall('./require/command'): 143 proc_name = command.attrib['name'] 144 assert all_procs[proc_name].alias == None 145 if proc_name not in removed_procs: 146 section_procs.append(all_procs[proc_name]) 147 148 section_enums = [] 149 for enum in section.findall('./require/enum'): 150 section_enums.append(all_enums[enum.attrib['name']]) 151 152 blocks.append( 153 VersionBlock(parse_version(section.attrib['number']), 154 section_procs, section_enums)) 155 156 return blocks 157 158 gles_blocks = parse_version_blocks('gles2') 159 desktop_gl_blocks = parse_version_blocks('gl', core_removed_procs) 160 161 def parse_extension_block(extension): 162 section = root.find( 163 '''extensions/extension[@name='{}']'''.format(extension)) 164 supported_specs = section.attrib['supported'].split('|') 165 section_procs = [] 166 for command in section.findall('./require/command'): 167 proc_name = command.attrib['name'] 168 assert all_procs[proc_name].alias == None 169 section_procs.append(all_procs[proc_name]) 170 171 section_enums = [] 172 for enum in section.findall('./require/enum'): 173 section_enums.append(all_enums[enum.attrib['name']]) 174 175 return ExtensionBlock(extension, section_procs, section_enums, 176 supported_specs) 177 178 extension_desktop_gl_blocks = [] 179 extension_gles_blocks = [] 180 for extension in supported_extensions: 181 extension_block = parse_extension_block(extension) 182 if 'gl' in extension_block.supported_specs: 183 extension_desktop_gl_blocks.append(extension_block) 184 if 'gles2' in extension_block.supported_specs: 185 extension_gles_blocks.append(extension_block) 186 187 # Compute the blocks for headers such that there is no duplicate definition 188 already_added_header_procs = set() 189 already_added_header_enums = set() 190 header_blocks = [] 191 192 def add_header_block(description, block): 193 block_procs = [] 194 for proc in block.procs: 195 if not proc.glProcName() in already_added_header_procs: 196 already_added_header_procs.add(proc.glProcName()) 197 block_procs.append(proc) 198 199 block_enums = [] 200 for enum in block.enums: 201 if not enum.name in already_added_header_enums: 202 already_added_header_enums.add(enum.name) 203 block_enums.append(enum) 204 205 if len(block_procs) > 0 or len(block_enums) > 0: 206 header_blocks.append( 207 HeaderBlock(description, block_procs, block_enums)) 208 209 for block in gles_blocks: 210 add_header_block( 211 'OpenGL ES {}.{}'.format(block.version.major, block.version.minor), 212 block) 213 214 for block in desktop_gl_blocks: 215 add_header_block( 216 'Desktop OpenGL {}.{}'.format(block.version.major, 217 block.version.minor), block) 218 219 for block in extension_desktop_gl_blocks: 220 add_header_block(block.extension, block) 221 222 for block in extension_gles_blocks: 223 add_header_block(block.extension, block) 224 225 return { 226 'gles_blocks': gles_blocks, 227 'desktop_gl_blocks': desktop_gl_blocks, 228 'extension_desktop_gl_blocks': extension_desktop_gl_blocks, 229 'extension_gles_blocks': extension_gles_blocks, 230 'header_blocks': header_blocks, 231 } 232 233 234class OpenGLLoaderGenerator(Generator): 235 def get_description(self): 236 return 'Generates code to load OpenGL function pointers' 237 238 def add_commandline_arguments(self, parser): 239 parser.add_argument('--gl-xml', 240 required=True, 241 type=str, 242 help='The Khronos gl.xml to use.') 243 parser.add_argument( 244 '--supported-extensions', 245 required=True, 246 type=str, 247 help= 248 'The JSON file that defines the OpenGL and GLES extensions to use.' 249 ) 250 251 def get_file_renders(self, args): 252 supported_extensions = [] 253 with open(args.supported_extensions) as f: 254 supported_extensions_json = json.loads(f.read()) 255 supported_extensions = supported_extensions_json[ 256 'supported_extensions'] 257 258 params = compute_params( 259 etree.parse(args.gl_xml).getroot(), supported_extensions) 260 261 return [ 262 FileRender( 263 'opengl/OpenGLFunctionsBase.cpp', 264 'src/dawn_native/opengl/OpenGLFunctionsBase_autogen.cpp', 265 [params]), 266 FileRender('opengl/OpenGLFunctionsBase.h', 267 'src/dawn_native/opengl/OpenGLFunctionsBase_autogen.h', 268 [params]), 269 FileRender('opengl/opengl_platform.h', 270 'src/dawn_native/opengl/opengl_platform_autogen.h', 271 [params]), 272 ] 273 274 def get_dependencies(self, args): 275 return [ 276 os.path.abspath(args.gl_xml), 277 os.path.abspath(args.supported_extensions) 278 ] 279 280 281if __name__ == '__main__': 282 sys.exit(run_generator(OpenGLLoaderGenerator())) 283