Files
filament/libs/bluegl/bluegl-gen.py
2023-01-30 15:56:15 -08:00

556 lines
17 KiB
Python
Executable File

#!/usr/bin/python
# Copyright (C) 2018 The Android Open Source Project
#
# 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.
# This script generates C++ and ASM code that will dynamically bind
# OpenGL functions at runtime
# TODO: This script has rudimentary support for multiple GL APIs
import argparse
import os
import platform
import re
from datetime import datetime
def printApis(gl_apis):
for api in gl_apis:
print('%s\t%s' % (api['api'], api['name']))
def parseFunctions(include_file, matcher):
print('Parsing header %s...' % include_file)
regex = re.compile(matcher)
functions = []
with open(include_file) as file:
for line in file:
match = regex.match(line)
if match is not None:
functions.append({'return': match.group(1),
'name': match.group(2),
'parameters': match.group(3)})
return functions
def generateHeader(api, functions, include_dir, output_dir):
suffix = api['suffix'] if api['suffix'] != 'Core' else ''
gl_suffix = '_gl_' + suffix.lower() if len(suffix) > 0 else ''
src_file = os.path.join(include_dir, 'bluegl', 'BlueGL%s.h' % suffix)
print('Generating public header %s...' % src_file)
headers = ''
defines = api['defines']
if len(defines) > 0:
# special case for GL_GLEXT_PROTOTYPES
if 'GL_GLEXT_PROTOTYPES' in defines:
headers += '''
// MSVC includes .../Windows Kits\\10\\Include\\10.0.17763.0\\um\GL/gl.h, with gl APIs conflicting with
// bluegl\\include\\GL/glcorearb.h, causing errors for OpenGL APIs such as:
// error C2375: 'glBindTexture': redefinition; different linkage
#ifndef FILAMENT_PLATFORM_WGL
#define GL_GLEXT_PROTOTYPES 1
#endif
'''
defines.remove('GL_GLEXT_PROTOTYPES')
headers += '\n'.join('#define %s 1' % d for d in defines)
headers += '\n\n'
headers += '\n'.join('#include <%s/%s>' % (api['directory'], r) for r in api['headers'])
header = '''/*
* Copyright (C) %(year)d The Android Open Source Project
*
* 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.
*/
/**********************************************************************************************
* Generated by bluegl/bluegl-gen.py
* DO NOT EDIT
**********************************************************************************************/
#ifndef TNT_FILAMENT_BLUEGL_%(suffix)s_H
#define TNT_FILAMENT_BLUEGL_%(suffix)s_H
%(headers)s
''' % {
'year': datetime.now().year,
'suffix': suffix.upper(),
'headers': headers
}
content = '''
#if defined(WIN32)
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#ifdef far
#undef far
#endif
#ifdef near
#undef near
#endif
#ifdef ERROR
#undef ERROR
#endif
#ifdef OPAQUE
#undef OPAQUE
#endif
#ifdef TRANSPARENT
#undef TRANSPARENT
#endif
#ifdef PURE
#undef PURE
#endif
#endif
namespace bluegl {
/**
* Looks up and binds all available OpenGL %(api_suffix)s functions.
* Every call to this function will increase an internal reference
* counter that can be decreased by calling unbind().
*
* @return 0 on success or -1 if an error occurred.
*/
int bind%(gl_suffix)s();
/**
* Unbinds all available OpenGL %(api_suffix)s functions.
* Every call to this function will decrease an internal reference
* counter and unbind all OpenGL functions when the counter reaches 0.
* As such you should assume that no OpenGL calls can be made after
* calling this function.
*/
void unbind%(gl_suffix)s();
''' % {
'api_suffix': api['suffix'],
'gl_suffix': gl_suffix
}
footer = '''
} // namespace bluegl
#endif // TNT_FILAMENT_BLUEGL_%s_H
''' % suffix.upper()
directory = os.path.dirname(src_file)
if not os.path.exists(directory):
os.makedirs(directory)
with open(src_file, 'w') as file:
file.write(header)
file.write(content)
file.write(footer)
def generateDefineHeader(api, functions, include_dir):
suffix = api['suffix'] if api['suffix'] != 'Core' else ''
src_file = os.path.join(include_dir, 'bluegl', 'BlueGLDefines%s.h' % suffix)
print('Generating define header %s...' % src_file)
header = '''/*
* Copyright (C) %(year)d The Android Open Source Project
*
* 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.
*/
/**********************************************************************************************
* Generated by bluegl/bluegl-gen.py
* DO NOT EDIT
**********************************************************************************************/
#ifndef TNT_FILAMENT_BLUEGL_%(suffix)s_DEFINES_H
#define TNT_FILAMENT_BLUEGL_%(suffix)s_DEFINES_H
''' % {
'year': datetime.now().year,
'suffix': suffix.upper()
}
footer = '''
#endif // TNT_FILAMENT_BLUEGL_%s_DEFINES_H
''' % suffix.upper()
directory = os.path.dirname(src_file)
if not os.path.exists(directory):
os.makedirs(directory)
with open(src_file, 'w') as file:
file.write(header)
for function in functions:
file.write('#define %s %s\n' % (function['name'], 'bluegl_' + function['name']))
file.write(footer)
def parseArguments(parameters):
arguments = []
if parameters != 'void':
for parameter in parameters.split(","):
beg = parameter.rfind('*')
if beg == -1:
beg = parameter.rfind(' ')
beg += 1
end = parameter.rfind('[')
if end == -1:
end = len(parameter)
arguments.append(parameter[beg:end])
return arguments
def generateProxies(api, functions, output_dir, platforms):
suffix = api['suffix']
header = '''/*
* Copyright (C) %d The Android Open Source Project
*
* 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.
*/
/**********************************************************************************************
* Generated by bluegl/bluegl-gen.py
* DO NOT EDIT
**********************************************************************************************/
''' % datetime.now().year
headerMasM = ''';
; Copyright (C) %(year)d The Android Open Source Project
;
; 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.
;
;
; Generated by bluegl/bluegl-gen.py
; DO NOT EDIT
;
.code
''' % {
'year': datetime.now().year,
}
headerC = '''/*
* Copyright (C) %(year)d The Android Open Source Project
*
* 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.
*/
/**********************************************************************************************
* Generated by bluegl/bluegl-gen.py
* DO NOT EDIT
**********************************************************************************************/
#include <bluegl/BlueGL.h>
#ifdef __cplusplus
extern "C" {
#endif
''' % {
'year': datetime.now().year,
}
osSpecificHeader = {
'Linux': header,
'LinuxAArch64': header,
'Darwin': header,
'DarwinAArch64': header,
'Windows': headerMasM,
'Windows32': headerC,
}
osSpecificFooter = {
'Linux': '',
'LinuxAArch64': '' ,
'Darwin': '',
'DarwinAArch64': '',
'Windows': 'end\n',
'Windows32': '''
#ifdef __cplusplus
}
#endif
'''
}
code = {
'Linux': '''
.global bluegl_%(name)s
.type bluegl_%(name)s, %%function
bluegl_%(name)s:
mov __blue_gl%(suffix)s_%(name)s@GOTPCREL(%%rip), %%r11
jmp *(%%r11)
''',
'LinuxAArch64': '''
.align 2
.global bluegl_%(name)s
.type bluegl_%(name)s, %%function
bluegl_%(name)s:
adrp x16, :got:__blue_gl%(suffix)s_%(name)s
ldr x16, [x16, #:got_lo12:__blue_gl%(suffix)s_%(name)s]
ldr x16, [x16]
br x16
.size bluegl_%(name)s, .-bluegl_%(name)s
''',
'Darwin': '''
.private_extern _bluegl_%(name)s
_bluegl_%(name)s:
mov ___blue_gl%(suffix)s_%(name)s@GOTPCREL(%%rip), %%r11
jmp *(%%r11)
''',
'DarwinAArch64': '''
.private_extern _bluegl_%(name)s
.align 2
_bluegl_%(name)s:
adrp x16, ___blue_gl%(suffix)s_%(name)s@GOTPAGE
ldr x16, [x16, ___blue_gl%(suffix)s_%(name)s@GOTPAGEOFF]
ldr x16, [x16]
br x16
''',
'Windows': '''
extrn __blue_gl%(suffix)s_%(name)s: qword
bluegl_%(name)s proc
mov r11, __blue_gl%(suffix)s_%(name)s
jmp r11
bluegl_%(name)s endp
''',
'Windows32': '''
extern void* __blue_gl%(suffix)s_%(name)s;
%(return)sAPIENTRY bluegl_%(name)s (%(parameters)s) {
typedef %(return)s(APIENTRYP PFN_%(name)s) (%(parameters)s);
return ((PFN_%(name)s)__blue_gl%(suffix)s_%(name)s)(%(arguments)s);
}
'''
}
extension = {
'Linux': 'S',
'LinuxAArch64': 'S',
'Darwin': 'S',
'DarwinAArch64': 'S',
'Windows': 'S',
'Windows32': 'cpp',
}
for platform in platforms:
src_file = os.path.join(output_dir, 'BlueGL%s%sImpl.%s' % (suffix, platform, extension[platform]))
print('Generating proxy %s...' % src_file)
with open(src_file, 'w') as file:
file.write(osSpecificHeader[platform])
for function in functions:
file.write(code[platform] % {
'suffix': suffix,
'return': function['return'],
'name': function['name'],
'parameters': function['parameters'],
"arguments": ", ".join(parseArguments(function['parameters']))
})
file.write(osSpecificFooter[platform])
def generateSource(api, functions, output_dir):
suffix = api['suffix'] if api['suffix'] != 'Core' else ''
gl_suffix = '_gl_' + suffix.lower() if len(suffix) > 0 else ''
src_file = os.path.join(output_dir, 'private_BlueGL%s.h' % suffix)
print('Generating source %s...' % src_file)
header = '''/*
* Copyright (C) %(year)d The Android Open Source Project
*
* 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.
*/
/**********************************************************************************************
* Generated by bluegl/bluegl-gen.py
* DO NOT EDIT
**********************************************************************************************/
''' % {
'year': datetime.now().year,
}
struct_header = '''
namespace bluegl {
// This mutex protect g_library_refcount below.
static std::mutex g_library_mutex;
static uint32_t g_library_refcount = 0;
struct {
void** api_call;
const char* api_name;
} g_gl%s_stubs[] = {
''' % (suffix)
functions_footer = '''};
'''
with open(src_file, 'w') as file:
file.write(header)
file.write("extern \"C\" {\n")
for function in functions:
file.write('void* __blue_gl%s_%s;\n' % (api['suffix'], function['name']))
file.write("}\n")
file.write(struct_header)
for function in functions:
file.write(' { &__blue_gl%s_%s, "%s" },\n' % (api['suffix'], function['name'], function['name']))
file.write(functions_footer)
file.write("size_t %s%sNumFunctions = %d;" % ("blue", api['suffix'], len(functions)))
file.write("\n} // namespace bluegl")
def generateApis(apis, include_dir, output_dir):
platforms = ['Linux', 'LinuxAArch64', 'Darwin', 'DarwinAArch64', 'Windows', 'Windows32']
for api in apis:
functions = []
for header in api['headers']:
include_file = os.path.join(include_dir, api['directory'], header)
functions += parseFunctions(include_file, api['matcher'])
# remove duplicates
functions = list({function['name']: function for function in functions}.values())
print('Found %s functions' % len(functions))
generateHeader(api, functions, include_dir, output_dir)
generateDefineHeader(api, functions, include_dir)
generateProxies(api, functions, output_dir, platforms)
generateSource(api, functions, output_dir)
if __name__ == '__main__':
gl_apis = [
# OpenGL Core (4.x+) support
{
'api': 'gl',
'name': 'OpenGL Core',
'prefix': 'gl',
'suffix': 'Core',
'directory': 'GL',
'headers': ['glcorearb.h', 'glext.h'],
'matcher': r'GLAPI\s+(.*)APIENTRY\s+(\w+)\s\((.*)\)',
'defines': ['GL_GLEXT_PROTOTYPES']
},
# Legacy OpenGL support
{
'api': 'legacy',
'name': 'OpenGL Legacy',
'prefix': 'gl',
'suffix': 'Legacy',
'directory': 'Legacy',
'headers': ['gl.h', 'glext.h'],
'matcher': r'GLAPI\s+(.*)APIENTRY\s+(\w+)\s\((.*)\)',
'defines': []
}
]
parser = argparse.ArgumentParser(description='Blue OpenGL bindings generator.')
parser.add_argument('-I', '--include', default='include', type=str, metavar='DIR',
help='Include directory')
parser.add_argument('-o', '--output', default='src', type=str, metavar='DIR',
help='Output directory')
parser.add_argument('-l', '--list', action='store_true',
help='List known APIs')
parser.add_argument('--api', type=str, metavar='API', nargs="*",
choices=[ 'gl', 'legacy' ], help='Generate only specific API')
args = parser.parse_args()
include_dir = args.include if args.include is not None else os.path.join(os.getcwd(), 'include')
output_dir = args.output if args.output is not None else os.path.join(os.getcwd(), 'src')
if args.list:
printApis(gl_apis)
exit(0)
selected_apis = args.api if args.api else ['gl']
apis = [api for api in gl_apis if api['api'] in selected_apis]
generateApis(apis, include_dir, output_dir)