Files
astromaddie--HaloCE-VR/tools/android_gl_stubs.py
T
2026-09-26 16:16:00 +12:00

184 lines
7.3 KiB
Python

#!/usr/bin/env python3
"""Generate the Android guest's OpenGL ES entry points.
The guest (ILP32 code, see port/android/README.md) cannot link against the
host's libGLESv3, but it can call it: a host function receives the guest's
pointer arguments zero-extended, and 32-bit integers and floats go in the
same registers under both ABIs. What differs is
- GLsizeiptr and GLintptr, 32-bit in the guest and 64-bit in the host;
- arguments passed on the stack (beyond the eighth integer argument), which
the guest packs by size while the host expects 8-byte slots;
- returned pointers, which point at host memory.
For each function in the Android list in port/linux/src/gl.h this writes a
guest wrapper that widens such arguments and calls an import named
hostgl_<function>, which the host resolves to the real entry point. Functions
returning a string go through host_gl_get_string instead.
Usage: android_gl_stubs.py gl.h gl32.h gl2ext.h output.c imports.list
"""
import re
import sys
INTEGER_REGISTER_COUNT = 8
WIDE_TYPES = {"GLsizeiptr", "GLintptr", "GLint64", "GLuint64", "GLint64EXT", "GLuint64EXT"}
FLOAT_TYPES = {"GLfloat", "GLclampf", "GLdouble", "GLclampd", "float", "double"}
def android_functions(gl_header: str):
text = open(gl_header, encoding="utf-8").read()
start = text.index("/* ANDROID_GL_FUNCTIONS_BEGIN */")
end = text.index("/* ANDROID_GL_FUNCTIONS_END */")
return re.findall(r"X\((\w+)\)", text[start:end])
def prototypes(*headers):
result = {}
pattern = re.compile(r"GL_APICALL\s+(.+?)\s*GL_APIENTRY\s+(\w+)\s*\((.*?)\);", re.S)
for header in headers:
text = open(header, encoding="utf-8").read()
for ret, name, params in pattern.findall(text):
params = " ".join(params.split())
result.setdefault(name, (ret.strip(), params))
return result
def split_parameter(param: str):
param = param.strip()
m = re.match(r"(.*?)(\w+)\s*$", param)
ptype, pname = m.group(1).strip(), m.group(2)
if not ptype: # unnamed
ptype, pname = param, None
return ptype, pname
def main():
gl_header, gl32, gl2ext, output, imports = sys.argv[1:6]
functions = android_functions(gl_header)
protos = prototypes(gl32, gl2ext)
out = [
"/* generated by tools/android_gl_stubs.py from port/linux/src/gl.h; do not edit */",
"#include <GLES3/gl32.h>",
"#include <GLES2/gl2ext.h>",
"#include <string.h>",
"",
"void host_gl_get_string(unsigned int name, int index, char *buffer, unsigned int size);",
"",
]
table = []
import_names = []
for name in functions:
if name not in protos:
raise SystemExit(f"{name}: no prototype in the GLES headers")
ret, params = protos[name]
if params in ("void", ""):
plist = []
else:
plist = [split_parameter(p) for p in params.split(",")]
if name == "glGetString":
out += [
"static const GLubyte *GL_APIENTRY guest_glGetString(GLenum name)",
"{",
"\tstatic __thread char buffer[4][4096];",
"\tstatic __thread int next;",
"\tchar *result = buffer[next++ & 3];",
"",
"\thost_gl_get_string(name, -1, result, sizeof(buffer[0]));",
"\treturn (const GLubyte *)result;",
"}",
"",
]
table.append(name)
continue
if name == "glShaderSource":
# an array of pointers: widen each to a host pointer
import_names.append("hostgl_glShaderSource")
out += [
"void hostgl_glShaderSource(GLuint shader, GLsizei count, const unsigned long long *strings, "
"const GLint *lengths);",
"static void GL_APIENTRY guest_glShaderSource(GLuint shader, GLsizei count, "
"const GLchar *const *strings, const GLint *lengths)",
"{",
"\tunsigned long long wide[16];",
"\tGLsizei index;",
"",
"\tif (count > 16)",
"\t\tcount = 16;",
"\tfor (index = 0; index < count; index++)",
"\t\twide[index] = (unsigned long long)(unsigned int)strings[index];",
"\thostgl_glShaderSource(shader, count, wide, lengths);",
"}",
"",
]
table.append(name)
continue
if "*" in ret:
raise SystemExit(f"{name}: returns a pointer; add a special case")
if any(p.count("*") > 1 for p, _ in plist):
raise SystemExit(f"{name}: takes an array of pointers; add a special case")
decl_params, host_params, call_args = [], [], []
integer_index = 0
for index, (ptype, pname) in enumerate(plist):
arg = f"a{index}"
decl_params.append(f"{ptype} {arg}")
is_pointer = "*" in ptype
base = ptype.replace("const", "").strip()
if base in FLOAT_TYPES and not is_pointer:
host_params.append(ptype)
call_args.append(arg)
continue
wide = base in WIDE_TYPES and not is_pointer
on_stack = integer_index >= INTEGER_REGISTER_COUNT
integer_index += 1
if is_pointer:
if on_stack:
host_params.append("unsigned long long")
call_args.append(f"(unsigned long long)(unsigned int){arg}")
else:
host_params.append(ptype)
call_args.append(arg)
elif wide or on_stack:
host_params.append("long long")
call_args.append(f"(long long){arg}")
else:
host_params.append(ptype)
call_args.append(arg)
host_name = "hostgl_" + name
import_names.append(host_name)
out.append(f"{ret} {host_name}({', '.join(host_params) or 'void'});")
out.append(f"static {ret} GL_APIENTRY guest_{name}({', '.join(decl_params) or 'void'})")
out.append("{")
call = f"{host_name}({', '.join(call_args)})"
out.append(f"\t{'return ' if ret != 'void' else ''}{call};")
out.append("}")
out.append("")
table.append(name)
out.append("static const struct { const char *name; void *function; } guest_gl_functions[] =")
out.append("{")
for name in table:
out.append(f'\t{{ "{name}", (void *)guest_{name} }},')
out.append("};")
out.append("")
out.append("typedef void (*guest_gl_function)(void);")
out.append("guest_gl_function guest_gl_get_proc_address(const char *name)")
out.append("{")
out.append("\tunsigned long index;")
out.append("")
out.append("\tfor (index = 0; index < sizeof(guest_gl_functions) / sizeof(guest_gl_functions[0]); index++)")
out.append("\t{")
out.append("\t\tif (!strcmp(guest_gl_functions[index].name, name))")
out.append("\t\t\treturn (guest_gl_function)guest_gl_functions[index].function;")
out.append("\t}")
out.append("\treturn 0;")
out.append("}")
with open(output, "w", encoding="utf-8") as f:
f.write("\n".join(out) + "\n")
with open(imports, "w", encoding="utf-8") as f:
f.write("\n".join(import_names) + "\n")
if __name__ == "__main__":
main()