Files
Arch-R/scripts/test-kmsdrm.py
Douglas Teles 6e0e085eb8 gl4es GPU pipeline, ES display fixes, runtime hardening
GPU rendering:
- Panfrost driver working (6 root causes fixed)
- gl4es cross-compiled for aarch64 (GOA_CLONE=ON)
- ES switched to Desktop GL renderer (-DGL=ON, Renderer_GL21.cpp)
- Pipeline: ES (GL 2.1) → gl4es → GLES 2.0 → Panfrost (Mali-G31)

ES display (5 root causes fixed):
- SDL3 KMSDRM rebuild (ALARM ships without it)
- Autologin approach (systemd service can't get DRM master)
- MAJOR/MINOR version bug fix
- GL context restore after setIcon()
- Null safety for glGetString

New files:
- rebuild-es-sdcard.sh (quick SD card rebuild)
- config/PanCho.ini (18-panel selection)
- config/es_input.cfg (gpio-keys + adc-joystick)
- config/gamecontrollerdb.txt (SDL controller mappings)
- scripts/archr-hotkeys.py (volume, brightness, headphone)
- scripts/test-kmsdrm.py (KMSDRM diagnostic)
- bootloader/u-boot-r36s-working/ (fixed U-Boot)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 21:42:50 -03:00

301 lines
10 KiB
Python

#!/usr/bin/env python3
"""
Arch R — SDL2 KMSDRM + GL Context diagnostic
Tests SDL_CreateRenderer AND SDL_GL_CreateContext in a SINGLE SDL session.
Test 3 forces eglBindAPI(EGL_OPENGL_ES_API) before context creation.
Run manually: python3 /usr/bin/emulationstation/test-kmsdrm.py
"""
import ctypes
import ctypes.util
import os
import sys
import time
# Force KMSDRM + GLES (not Desktop GL)
os.environ['SDL_VIDEODRIVER'] = 'KMSDRM'
os.environ['SDL_VIDEO_DRIVER'] = 'KMSDRM'
os.environ['SDL_OPENGL_ES_DRIVER'] = '1'
os.environ['SDL_LOGGING'] = '*=verbose'
# DO NOT set MESA_LOADER_DRIVER_OVERRIDE — it breaks kmsro render-offload!
# Mesa auto-detects: card0 (rockchip) → kmsro → finds renderD129 (panfrost)
os.environ.pop('MESA_LOADER_DRIVER_OVERRIDE', None)
print("=" * 60)
print("Arch R KMSDRM + GL Context Diagnostic")
print("=" * 60)
# Load SDL2
try:
sdl = ctypes.CDLL('libSDL2-2.0.so.0')
except OSError as e:
print(f"FATAL: Cannot load libSDL2: {e}")
sys.exit(1)
# Load EGL for direct API binding
try:
egl = ctypes.CDLL('libEGL.so')
HAS_EGL = True
print("libEGL loaded OK")
except OSError as e:
print(f"WARNING: Cannot load libEGL: {e}")
HAS_EGL = False
# Set return types
sdl.SDL_CreateWindow.restype = ctypes.c_void_p
sdl.SDL_CreateRenderer.restype = ctypes.c_void_p
sdl.SDL_GL_CreateContext.restype = ctypes.c_void_p
sdl.SDL_GetError.restype = ctypes.c_char_p
# SDL Constants
SDL_INIT_VIDEO = 0x20
SDL_WINDOW_FULLSCREEN = 0x01
SDL_WINDOW_OPENGL = 0x02
SDL_WINDOW_SHOWN = 0x04
SDL_WINDOW_ALLOW_HIGHDPI = 0x2000
# GL attributes (from SDL2 headers)
SDL_GL_RED_SIZE = 0
SDL_GL_GREEN_SIZE = 1
SDL_GL_BLUE_SIZE = 2
SDL_GL_DEPTH_SIZE = 6
SDL_GL_DOUBLEBUFFER = 5
SDL_GL_CONTEXT_MAJOR_VERSION = 17
SDL_GL_CONTEXT_MINOR_VERSION = 18
SDL_GL_CONTEXT_PROFILE_MASK = 21
SDL_GL_CONTEXT_PROFILE_ES = 0x0004
# EGL Constants
EGL_OPENGL_ES_API = 0x30A0
EGL_OPENGL_API = 0x30A2
# Single SDL_Init for ALL tests
ret = sdl.SDL_Init(SDL_INIT_VIDEO)
if ret != 0:
print(f"FATAL: SDL_Init failed: {sdl.SDL_GetError()}")
sys.exit(1)
print("SDL_Init OK")
# ---- TEST 1: SDL_CreateRenderer (known working) ----
print("\n--- TEST 1: SDL_CreateRenderer (indirect GL) ---")
win1 = sdl.SDL_CreateWindow(
b"Test1-Renderer",
0x1FFF0000, 0x1FFF0000,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
)
if not win1:
print(f"FATAL: CreateWindow failed: {sdl.SDL_GetError()}")
sdl.SDL_Quit()
sys.exit(1)
print(f" Window: {win1:#x}")
renderer = sdl.SDL_CreateRenderer(ctypes.c_void_p(win1), -1, 0)
if not renderer:
print(f"FATAL: CreateRenderer failed: {sdl.SDL_GetError()}")
else:
print(f" Renderer: {renderer:#x}")
# Query renderer info
class SDL_RendererInfo(ctypes.Structure):
_fields_ = [
("name", ctypes.c_char_p),
("flags", ctypes.c_uint32),
("num_texture_formats", ctypes.c_uint32),
("texture_formats", ctypes.c_uint32 * 16),
("max_texture_width", ctypes.c_int),
("max_texture_height", ctypes.c_int),
]
info = SDL_RendererInfo()
sdl.SDL_GetRendererInfo.argtypes = [ctypes.c_void_p, ctypes.POINTER(SDL_RendererInfo)]
ret = sdl.SDL_GetRendererInfo(ctypes.c_void_p(renderer), ctypes.byref(info))
if ret == 0:
print(f" Renderer name: {info.name}")
sdl.SDL_SetRenderDrawColor(ctypes.c_void_p(renderer), 0, 64, 200, 255)
sdl.SDL_RenderClear(ctypes.c_void_p(renderer))
sdl.SDL_RenderPresent(ctypes.c_void_p(renderer))
print(" Blue screen — TEST 1 PASSED")
time.sleep(1)
sdl.SDL_DestroyRenderer(ctypes.c_void_p(renderer))
sdl.SDL_DestroyWindow(ctypes.c_void_p(win1))
def query_gl_info(label):
"""Query and print GL context info using libGLESv1_CM"""
try:
gles = ctypes.CDLL('libGLESv1_CM.so')
gles.glGetString.restype = ctypes.c_char_p
vendor = gles.glGetString(0x1F00)
renderer_str = gles.glGetString(0x1F01)
version = gles.glGetString(0x1F02)
exts = gles.glGetString(0x1F03)
print(f" GL_VENDOR: {vendor}")
print(f" GL_RENDERER: {renderer_str}")
print(f" GL_VERSION: {version}")
if exts:
print(f" GL_EXTENSIONS: {len(exts)} bytes")
else:
print(f" GL_EXTENSIONS: NULL!")
# Check if it's Panfrost (hardware) or llvmpipe (software)
if renderer_str and b'panfrost' in renderer_str.lower():
print(f" >>> PANFROST HARDWARE GPU — {label} WORKS!")
return True
elif renderer_str and b'llvmpipe' in renderer_str.lower():
print(f" >>> LLVMPIPE SOFTWARE — {label} FAILED (wrong API)")
return False
elif renderer_str and b'mali' in renderer_str.lower():
print(f" >>> MALI GPU — {label} WORKS!")
return True
else:
print(f" >>> UNKNOWN RENDERER")
return False
except Exception as e:
print(f" GL query error: {e}")
return False
def draw_green(sdl_lib, window):
"""Draw a green screen to confirm rendering works"""
try:
gles = ctypes.CDLL('libGLESv1_CM.so')
gles.glClearColor(ctypes.c_float(0.0), ctypes.c_float(0.8), ctypes.c_float(0.2), ctypes.c_float(1.0))
gles.glClear(0x4000) # GL_COLOR_BUFFER_BIT
sdl_lib.SDL_GL_SwapWindow(ctypes.c_void_p(window))
print(f" Green screen drawn!")
time.sleep(2)
except Exception as e:
print(f" Draw error: {e}")
# ---- TEST 2: SDL_GL_CreateContext WITHOUT eglBindAPI (baseline — expect llvmpipe) ----
print("\n--- TEST 2: SDL_GL_CreateContext (SDL2 attrs only — NO eglBindAPI) ---")
sdl.SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)
sdl.SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0)
win2 = sdl.SDL_CreateWindow(
b"Test2-NoEglBind",
0x1FFF0000, 0x1FFF0000,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI
)
if not win2:
print(f" Window FAILED: {sdl.SDL_GetError()}")
else:
ctx2 = sdl.SDL_GL_CreateContext(ctypes.c_void_p(win2))
if not ctx2:
print(f" GL Context FAILED: {sdl.SDL_GetError()}")
else:
print(f" GL Context OK: {ctx2:#x}")
sdl.SDL_GL_MakeCurrent(ctypes.c_void_p(win2), ctypes.c_void_p(ctx2))
test2_ok = query_gl_info("Test2")
sdl.SDL_GL_DeleteContext(ctypes.c_void_p(ctx2))
sdl.SDL_DestroyWindow(ctypes.c_void_p(win2))
# ---- TEST 3: eglBindAPI(EGL_OPENGL_ES_API) BEFORE SDL_GL_CreateContext ----
print("\n--- TEST 3: eglBindAPI(EGL_OPENGL_ES_API) + SDL_GL_CreateContext ---")
print(" This is the proposed fix for EmulationStation!")
if not HAS_EGL:
print(" SKIPPED — libEGL not available")
else:
# Force EGL to use OpenGL ES API — this should make SDL3 create a GLES context
ret = egl.eglBindAPI(EGL_OPENGL_ES_API)
print(f" eglBindAPI(EGL_OPENGL_ES_API) = {ret} (1=OK)")
sdl.SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)
sdl.SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0)
win3 = sdl.SDL_CreateWindow(
b"Test3-EglBind",
0x1FFF0000, 0x1FFF0000,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI
)
if not win3:
print(f" Window FAILED: {sdl.SDL_GetError()}")
else:
ctx3 = sdl.SDL_GL_CreateContext(ctypes.c_void_p(win3))
if not ctx3:
print(f" GL Context FAILED: {sdl.SDL_GetError()}")
else:
print(f" GL Context OK: {ctx3:#x}")
sdl.SDL_GL_MakeCurrent(ctypes.c_void_p(win3), ctypes.c_void_p(ctx3))
test3_ok = query_gl_info("Test3")
if test3_ok:
draw_green(sdl, win3)
print(" >>> TEST 3 PASSED — eglBindAPI fix works!")
print(" >>> This fix should be applied to EmulationStation!")
sdl.SDL_GL_DeleteContext(ctypes.c_void_p(ctx3))
sdl.SDL_DestroyWindow(ctypes.c_void_p(win3))
# ---- TEST 4: eglBindAPI + GLES 2.0 (fallback) ----
print("\n--- TEST 4: eglBindAPI + GLES 2.0 (fallback if GLES 1.0 fails) ---")
if not HAS_EGL:
print(" SKIPPED — libEGL not available")
else:
ret = egl.eglBindAPI(EGL_OPENGL_ES_API)
print(f" eglBindAPI(EGL_OPENGL_ES_API) = {ret} (1=OK)")
sdl.SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)
sdl.SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0)
sdl.SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2)
sdl.SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0)
win4 = sdl.SDL_CreateWindow(
b"Test4-GLES20",
0x1FFF0000, 0x1FFF0000,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI
)
if not win4:
print(f" Window FAILED: {sdl.SDL_GetError()}")
else:
ctx4 = sdl.SDL_GL_CreateContext(ctypes.c_void_p(win4))
if not ctx4:
print(f" GL Context FAILED: {sdl.SDL_GetError()}")
else:
print(f" GL Context OK: {ctx4:#x}")
sdl.SDL_GL_MakeCurrent(ctypes.c_void_p(win4), ctypes.c_void_p(ctx4))
test4_ok = query_gl_info("Test4")
if test4_ok:
draw_green(sdl, win4)
print(" >>> TEST 4 PASSED — GLES 2.0 + eglBindAPI works!")
sdl.SDL_GL_DeleteContext(ctypes.c_void_p(ctx4))
sdl.SDL_DestroyWindow(ctypes.c_void_p(win4))
sdl.SDL_Quit()
print("\n" + "=" * 60)
print("Diagnostic complete")
print("=" * 60)
print("\nSummary:")
print(" Test 1: SDL_CreateRenderer → should always work")
print(" Test 2: SDL_GL_CreateContext (no eglBindAPI) → expect llvmpipe")
print(" Test 3: eglBindAPI(ES) + SDL_GL_CreateContext GLES1.0 → THE FIX")
print(" Test 4: eglBindAPI(ES) + SDL_GL_CreateContext GLES2.0 → fallback")
print(" If Test 3 or 4 shows Panfrost → apply eglBindAPI patch to ES!")