Skip to content

OpenGLES 3.1 - #8533

Open
danoli3 wants to merge 51 commits into
openframeworks:masterfrom
danoli3:OpenGLES3.1
Open

danoli3 wants to merge 51 commits into
openframeworks:masterfrom
danoli3:OpenGLES3.1

Conversation

@danoli3

@danoli3 danoli3 commented Aug 11, 2026 •

Copy link
Copy Markdown
Member

Overview

Adds OpenGL ES 3.0 and 3.1 support to openFrameworks, extending the existing rendering pipeline while maintaining compatibility with OpenGL ES 2.0 and desktop OpenGL.

This work builds upon #8512 by @mruegenberg, integrating the original GLES 3.x implementation with additional renderer fixes, platform improvements, and compatibility updates.

The goal is to modernise openFrameworks’ OpenGL ES support and improve portability across Android, iOS, Emscripten/WebGL, and other GLES-based environments.

Key Changes

OpenGL ES 3.0 / 3.1 Support

  • Adds GLES 3.0 and 3.1 functionality throughout the core rendering pipeline.
  • Updates ofGLProgrammableRenderer, ofGLUtils, and associated rendering infrastructure.
  • Improves GLES context creation and version handling.
  • Maintains compatibility with existing GLES 2.0 rendering paths.

Rendering Improvements

  • Updates ofShader, ofMaterial, ofFbo, ofVbo, and ofBufferObject for GLES 3.x compatibility.
  • Fixes shader defaults and rendering behaviour across GLES versions.
  • Addresses mesh indexing, primitive topology, and buffer handling.
  • Improves compatibility between desktop OpenGL and OpenGL ES.

Platform Support

  • Android: Updates GLES context handling, window lifecycle management, and rendering compatibility.
  • Emscripten: Improves WebGL 2 / GLES 3 support and associated compilation definitions.
  • iOS: Integrates GLES version handling into the cross-platform rendering changes.
  • Desktop: Preserves existing OpenGL behaviour while improving shared renderer compatibility.

Tessellation

  • Integrates tess2 improvements for automatic index-width selection.
  • Reduces reliance on platform-specific defines.
  • Improves compatibility with GLES index buffer requirements.

Testing and Stability

  • Adds OpenGL / OpenGL ES rendering tests.
  • Adds Android GLES lifecycle and hardware stress testing.
  • Fixes Android view lifecycle issues.
  • Addresses GLES 2.0 regressions involving mesh indices and buffer operations.

Why This Matters

OpenGL ES 3.x introduces capabilities beyond GLES 2.0, including improved buffer handling, texture formats, and additional GPU functionality.

Supporting these versions allows openFrameworks to take better advantage of modern mobile GPUs and improves compatibility with graphics translation layers and backends such as ANGLE, Dawn, Metal, and Vulkan.

This also establishes a stronger foundation for future rendering improvements without requiring separate rendering implementations for each platform.

Compatibility

The intention is to preserve existing behaviour for projects using GLES 2.0 or desktop OpenGL.

GLES 3.x functionality is enabled through the appropriate rendering configuration and platform support.

Remaining Validation

  • Review cross-platform rendering changes.
  • Verify GLES 1.x and GLES 2.0 compatibility.
  • Complete GLES 3.0 / 3.1 rendering tests.
  • Validate Android emulator and physical devices.
  • Validate iOS simulator and physical devices.
  • Verify Emscripten / WebGL compatibility.
  • Confirm desktop OpenGL regression tests pass.

Credits

Based on the original OpenGL ES 3.0 / 3.1 work by @mruegenberg in #8512, with additional integration, fixes, and platform compatibility improvements.
@danoli3 for updating ancient PR by hand
Qwen 3.8 for code updating and analysis
Claude for PR / Rebasing / Test cases and running simulators

@danoli3

danoli3 commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

Smoke tests on GL failing will investigate

@danoli3

danoli3 commented Sep 12, 2026

Copy link
Copy Markdown
Member Author

This is almost complete just to do physical device tests

@danoli3

danoli3 commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

Rebased on master + GLES fixes + GL / GLES test matrix

Rebased onto current master (46 commits, no conflicts; emsdk stays 6.0.11). The previous msys2 failures were the CI mirror/fail-fast issue fixed on master (#8566 / #8568), not this PR.

New commit b226140 — fixes for regressions found while testing ES 1.1 / 2.0 / 3.0 on the iOS Simulator (before it, every iOS ES version failed and unfilled circles/polylines were invisible on ES):

# Problem Fix
1 ofFbo::readToPixels returned no pixels on ES 3: it called ofTexture::readToPixels, which is compiled out on GLES (no glGetTexImage in any ES) glReadPixels on all GLES again (as on master), with glReadBuffer for the attachment on ES 3 contexts
2 Circle / polyline outlines missing on ES: line modes use the lines shader, but the new ES client-array path never built the line bundle, so GL_LINE_STRIP / GL_LINE_LOOP collapsed to nothing ES line modes build the bundle like desktop (also brings back ofSetLineWidth on ES)
3 ofFbo::allocate(w, h, fmt) defaulted to depth+stencil on ES → FRAMEBUFFER_INCOMPLETE_ATTACHMENT colour-only default on ES, as on master (ofFboSettings still requests depth/stencil)
4 TARGET_OPENGLES_3 is a compile-time header check, true on iOS even for ES 1 / ES 2 contexts → ES 2 textures got GL_RGBA8 (GL_INVALID_OPERATION), ES 1.1 FBOs reported "not supported" new ofIsGLES3Context() (headers and live context ≥ 3) for the sized internal formats; ofFbo::checkGLSupport also accepts GL_OES_framebuffer_object

Note: the remaining ~75 TARGET_OPENGLES_3 compile-time branches (ofFbo MSAA/draw buffers, ofShader, ofBufferObject, …) have the same ES1/ES2-runtime caveat; I only switched the ones the tests hit. Worth a follow-up pass with ofIsGLES3Context().

Test matrix

Test app = graphicsExample's scene + the PR's GLSmokeTestCore + extra checks, version picked at launch. Built against the latest Apothecary libraries (macOS / iOS / emscripten / Android downloaded fresh).

Checks: context (renderer set up at the requested version) · texture upload → draw into FBO → readback colour · shader compile/link/draw → readback (programmable only) · smoke (indexed ofVboMesh, tess2 ofPath, FBO readback, instancing where available) · screen readback of graphicsExample's circle · tess2 star (index type matches ofIndexType + star arm pixels) · no GL errors

Platform Requested Context reported Renderer Context Texture Shader Smoke Screen tess2 star GL errors Result
macOS (M1 Max) GL 2.1 2.1 Metal - 90.5 ofGLRenderer (fixed) ✅ ✅ n/a ✅ ✅ ✅ ✅ PASS
macOS GL 3.2 core 4.1 Metal - 90.5 ofGLProgrammableRenderer ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS
macOS GL 3.3 core 4.1 Metal - 90.5 ofGLProgrammableRenderer ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS
macOS GL 4.0 core 4.1 Metal - 90.5 ofGLProgrammableRenderer ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS
macOS GL 4.1 core 4.1 Metal - 90.5 ofGLProgrammableRenderer ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS
iOS 26.5 Sim (iPhone 17 Pro) ES 1.1 OpenGL ES-CM 1.1 APPLE-23.1.1 ofGLRenderer (fixed) ✅ ✅ n/a ✅ ✅ ✅ ✅ PASS
iOS 26.5 Sim ES 2.0 OpenGL ES 2.0 APPLE-23.1.1 ofGLProgrammableRenderer ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS
iOS 26.5 Sim ES 3.0 OpenGL ES 3.0 APPLE-23.1.1 ofGLProgrammableRenderer ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS

macOS tops out at GL 4.1 (any 3.2+ core request returns 4.1); GL 4.2+ needs Linux/Windows. Android emulator (ES 1.1 / 2.0 / 3.0 / 3.1) and emscripten (WebGL 1 / 2) runs to follow.

Also found: with older downloaded libs, ofPath fills drew a single triangle on macOS — tess2's tesselator.h used defined(TARGET_OS_IPHONE) (true on macOS), so oF read tess2's 32-bit indices as 16-bit. Current Apothecary latest already fixes the header; the new tess2 star check catches it.

Screenshots

macOS — GL 2.1 (fixed function)
macOS GL 2.1

macOS — GL 3.2 core
macOS GL 3.2

macOS — GL 3.3 / 4.0 / 4.1 core

macOS GL 3.3
macOS GL 4.0
macOS GL 4.1

iOS Simulator — ES 1.1 (fixed function)
iOS ES 1.1

iOS Simulator — ES 2.0
iOS ES 2.0

iOS Simulator — ES 3.0
iOS ES 3.0

@danoli3

danoli3 commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

This is looking great! Getting Qwen 3.8 local ai to test and analyse everything and doing test qa passes

danoli3 added a commit to danoli3/openFrameworks that referenced this pull request Oct 6, 2026
@danoli3

danoli3 commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

Emscripten results (WebGL 1 / WebGL 2)

Same test app (graphicsExample scene + GLSmokeTestCore + extra checks) built with emsdk 6.0.11 (matches CI) against the latest Apothecary emscripten libs, at b226140. No further code changes were needed — both pass with the fixes already in this PR.

Run in headless Chrome on the real GPU (--use-angle=metal), driven over CDP; the result line is read from the page and the browser's own WebGL context type / renderer are recorded as a cross-check.

Platform Requested Context reported (oF) Browser context GPU Context Texture Shader Smoke Screen tess2 star GL errors Result
Chrome / emscripten WebGL 2 (ES 3.0) OpenGL ES 3.0 (WebGL 2.0 (OpenGL ES 3.0 Chromium)) webgl2 ANGLE Metal, Apple M1 Max ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS
Chrome / emscripten, --disable-webgl2 WebGL 1 (ES 2.0) OpenGL ES 2.0 (WebGL 1.0 (OpenGL ES 2.0 Chromium)) webgl1 ANGLE Metal, Apple M1 Max ✅ ✅ ✅ ✅ ✅ ✅ ✅ PASS

Note: ofxAppEmscriptenWindow::setup ignores settings.glesVersion — it always tries WebGL 2 and falls back to WebGL 1 if that fails. So WebGL 1 was tested through that fallback (WebGL 2 disabled in the browser), which is what users without WebGL 2 get. Honouring an explicit setGLESVersion(2) would be possible, but the ofGLESWindowSettings default is glesVersion = 1, so it would need a "not set" state first.

Running total: macOS GL 2.1 / 3.2 / 3.3 / 4.0 / 4.1, iOS Simulator ES 1.1 / 2.0 / 3.0, emscripten WebGL 1 / 2 — all PASS. Android emulator (ES 1.1 / 2.0 / 3.0 / 3.1) next.

Screenshots

Emscripten — WebGL 2 (ES 3.0)
emscripten WebGL 2

Emscripten — WebGL 1 (ES 2.0, fallback)
emscripten WebGL 1

danoli3 added 25 commits October 9, 2026 11:01
# Conflicts:
#	libs/openFrameworks/gl/ofGLProgrammableRenderer.cpp
The shared test always called drawInstanced, which is not in ES1/ES2 or
GL 2.1, and the programmable GLES path never instanced even on ES3
(gated on 3.1, and ofVboMesh ignored primCount). Skip instancing unless
the live context is GL 3.1+ / ES 3.0+, match VBO index type to
ofIndexType on GLES, report WebGL2 as GLES 3, and give the Android
lifecycle test ES2 shaders / an ES1 no-shader path.
- ofGraphics/ofGLRenderer: revert background-gradient relocation that
  used ofVboMesh without its header and dropped the renderer's
  drawBackgroundGradient definition (broke every build + link)
- ofGraphics: revert illegal static_cast to ofBaseGLRenderer in
  ofEnable/DisablePointSprites, virtual dispatch already covers it
- ofAppEGLWindow: rename glesVersion member to glesVersionMajor to
  match ofAppEGLWindow.cpp usage
- ofAppAndroidWindow: drop dead overwrite of glesVersion from legacy
  field so settings getters take effect, init glesVersionMinor
- ofxEmscriptenURLFileLoader: restore lowercase __EMSCRIPTEN_major__/
  __EMSCRIPTEN_minor__ macros (uppercase form is never defined)
- ofTexture: restore GL_TEXTURE_CUBE_MAP mipmap case on desktop,
  align bindAsImage cpp guard with header
- ofConstants: make GLES3 includes live (unconditional on Linux ARM
  and Emscripten, API-gated on Android) so TARGET_OPENGLES_3
  auto-detection actually works
- ofFbo::readToPixels: GLES has no glGetTexImage (ES 1/2/3), so the ES 3
  branch returned empty pixels; read the bound FBO with glReadPixels on all
  GLES again, selecting the attachment with glReadBuffer on ES 3 contexts.
- ofGLProgrammableRenderer::draw(ofMesh) on ES: line modes use the lines
  shader, which needs the line bundle; raw client arrays made
  GL_LINE_STRIP / GL_LINE_LOOP (circle and polyline outlines) collapse to
  nothing. Build the bundle like desktop (ofSetLineWidth works on ES again).
- ofFbo::allocate(w, h, format): keep colour-only on ES; the combined
  depth+stencil renderbuffer was FRAMEBUFFER_INCOMPLETE_ATTACHMENT.
- TARGET_OPENGLES_3 only means the ES 3 headers exist, which is true on iOS
  even for ES 1 / ES 2 contexts. Add ofIsGLES3Context() (headers + live
  context >= 3) and use it for sized RGBA8 / RGB8 internal formats (ES 2
  rejects them with GL_INVALID_OPERATION); ofFbo::checkGLSupport accepts
  GL_OES_framebuffer_object for ES 1.1 contexts.
On Android an ES 1 context has no GLES2 entry points, so
glGenFramebuffers resolved to a null function pointer and ofFbo::allocate
crashed (SIGSEGV, pc 0) on ES 1.1 contexts. iOS shares one GL library
across ES versions, so the OES path stays enabled there; Android ES 1.1
now reports FBOs as unsupported instead of crashing.
Found with a glGetError trace after every setup/draw step on the
Android emulator (ES 1.1) and iOS Simulator (ES 1.1 / 2.0):

- ofGLRenderer: loadViewMatrix / enableLighting / setLightPosition /
  setLightSpotDirection queried glGetIntegerv(GL_MATRIX_MODE), which some
  ES 1.1 drivers reject, then restored glMatrixMode() from the
  uninitialised result (two errors per setupScreen, every frame). Restore
  the matrix mode oF already tracks in matrixStack instead.
- ofTexture::loadData: on ES 1.1 (fixed function) the Android emulator
  rejects glTexSubImage2D with GL_LUMINANCE / GL_LUMINANCE_ALPHA /
  GL_ALPHA and leaves the texture empty, so ofDrawBitmapString drew
  nothing. Full-size updates re-specify the level with glTexImage2D.
- ofFbo::checkGLSupport: GL_MAX_COLOR_ATTACHMENTS / GL_MAX_DRAW_BUFFERS /
  GL_MAX_SAMPLES are ES 3 queries; on ES 1 / ES 2 contexts built with
  ES 3 headers (iOS) use 1 / 1 / 0 instead of querying.

Now zero GL errors on macOS GL 2.1 / 3.2, iOS ES 1.1 / 2.0 / 3.0 and
Android ES 1.1 / 2.0 / 3.0 / 3.1.
danoli3 added a commit to danoli3/openFrameworks that referenced this pull request Oct 8, 2026
@danoli3

danoli3 commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

Stacked on #8575 + full retest (all platforms)

Rebased onto #8575 (Android: NDK r29 / min API 25 / build-tools 37, synced with apothecary) — no conflicts. This branch is now #8575's commit + this PR's 49 commits (head 0870ca0); merge #8575 first.

Everything rebuilt clean from this exact branch and re-run with the same GL test app (graphicsExample scene + GLSmokeTestCore + context / texture / shader / screen-readback / tess2-star checks, plus a glGetError trace after every setup and draw step):

Platform Toolchain Requested Context Result GL errors
macOS (M1 Max) make, clean core GL 2.1 2.1 Metal (fixed function) ✅ PASS 0
macOS GL 3.2 / 3.3 / 4.0 / 4.1 4.1 Metal core ✅ PASS ×4 0
iOS 26.5 Simulator xcodebuild clean ES 1.1 ES-CM 1.1 (fixed) ✅ PASS 0
iOS 26.5 Simulator ES 2.0 / 3.0 ES 2.0 / ES 3.0 ✅ PASS ×2 0
Emscripten 6.0.11 (Chrome, ANGLE Metal) emmake, clean core WebGL 2 / WebGL 1 ES 3.0 / ES 2.0 ✅ PASS ×2 0
Android 14 emulator (arm64) cmake.sh NDK 29.0.14206865 (no overrides), gradle minSdk 25 / build-tools 37 ES 2.0 / 3.0 / 3.1 ES 3.0 * ✅ PASS ×3 0
Android 14 emulator ES 1.1 ES-CM 1.1 (fixed) FBO checks fail (no FBOs on Android ES 1 — expected); everything else PASS 0

* the emulator only provides an ES 3.0 context. Android uses the tess2 __ANDROID__ fix from apothecary b5ee7baa (local rebuild until the release lands).

Screenshots

macOS GL 2.1 (fixed function)
macOS GL 2.1

iOS ES 1.1 / 2.0 / 3.0
iOS ES 1.1
iOS ES 2.0
iOS ES 3.0

Android ES 3.1 (NDK 29 / API 25 build)
Android ES 3.1

Emscripten WebGL 2
WebGL 2

macOS GL 3.2 / 3.3 / 4.0 / 4.1, Android ES 1.1 / 2.0 / 3.0, Emscripten WebGL 1

macOS GL 3.2
macOS GL 3.3
macOS GL 4.0
macOS GL 4.1
Android ES 1.1
Android ES 2.0
Android ES 3.0
WebGL 1

On Android an ES 1.1 context provides FBOs through GL_OES_framebuffer_object
in libGLESv1_CM (glGenFramebuffersOES, ...); the core names resolve to
libGLESv2 and are null in an ES 1 context. Add ofGLFramebuffer.h with
ofGLGenFramebuffers / ofGLBindFramebuffer / ... wrappers that call the OES
entry points on Android fixed-function contexts and the core names
everywhere else (enum values are identical), and use them in ofFbo,
ofGLRenderer and ofTexture (generateMipmap).

The Android emulator's ES 1 driver (goldfish GLESv1_enc) advertises the
extension but can't be used: glDeleteFramebuffersOES crashes
(GLClientState::removeFramebuffers locks renderbuffer state that is only
set up for ES 2+) and rebinding framebuffer 0 doesn't return drawing to
the window. ofIsAndroidEmulatorGLES1() detects it from GL_RENDERER;
ofGLRenderer::setup() logs a startup warning that FBOs won't work on the
emulator with ES 1.1 but will on devices, and ofFbo reports them as
unsupported there.
@danoli3

danoli3 commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

Android ES 1.1 FBOs via GL_OES_framebuffer_object (2f2fbff)

Why Android ES 1.1 had no FBOs: libGLESv1_CM is linked, but it only exports the OES names (glGenFramebuffersOES, …). oF called the core names (glGenFramebuffers, …), which resolve to libGLESv2 and are null in an ES 1 context (that was the crash fixed earlier in f22984a).

Change: new internal libs/openFrameworks/gl/ofGLFramebuffer.h with ofGLGenFramebuffers / ofGLBindFramebuffer / ofGLFramebufferTexture2D / … wrappers — OES entry points on Android fixed-function contexts, core names everywhere else (identical enums). 30 call sites in ofFbo, ofGLRenderer, ofTexture (mipmaps) use them; checkGLSupport accepts GL_OES_framebuffer_object on Android again.

Android emulator exception: the emulator's ES 1 driver advertises the extension but is broken for it — verified against its source (goldfish-opengl android14-release):

  • glDeleteFramebuffersOES → GLEncoder::s_glDeleteFramebuffersOES → GLClientState::removeFramebuffers, which locks mRboState.rboData; that's only set via setRenderbufferInfo(), never for ES 1 → null mutex crash.
  • glBindFramebufferOES(…, 0) isn't remapped to the window surface on the ES 1 path, so after any FBO use nothing reaches the screen (ES 2+ is fine).

With the OES path enabled on the emulator, FBO render + readback actually passed (texture + smoke checks) before those two bugs hit. So ofIsAndroidEmulatorGLES1() (fixed-function context + GL_RENDERER contains "Android Emulator") disables FBOs there, with a startup log:

[warning] ofGLRenderer: Android emulator + OpenGL ES 1.1 detected: FBOs (ofFbo) will not work here - the emulator's ES 1 driver can't delete or switch back from OES framebuffers. ES 1.1 FBOs work on real devices (GL_OES_framebuffer_object); use ES 2.0+ to test FBOs on the emulator.
[ error ] ofFbo: FBOs are disabled on the Android emulator with OpenGL ES 1.1 (emulator driver bug); they work on real devices. Use ES 2.0+ on the emulator for ofFbo.   (logged once)

Retest after this commit: macOS GL 2.1 / 3.2 / 3.3 / 4.0 / 4.1, iOS Simulator ES 1.1 / 2.0 / 3.0 (ES 1.1 FBOs still work, no emulator warning), emscripten WebGL 1 / 2, Android emulator ES 2.0 / 3.0 / 3.1 — all PASS, zero GL errors. Android emulator ES 1.1: warning logged, no crash, scene draws, zero GL errors; FBO-based checks report unsupported (by design).

⚠️ Real-device Android ES 1.1 FBOs are untested (spec-correct OES path; needs a device with an ES 1.1 context).

Ref: goldfish-opengl GLClientState, OES_framebuffer_object; similar emulator-only default-framebuffer issue on GLES3: godotengine/godot#74945.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant