From e1c178f0499e7278c0165c6a1161684eaaa0a8a0 Mon Sep 17 00:00:00 2001 From: Boris Mikic Date: Wed, 29 Apr 2026 11:40:00 +0200 Subject: [PATCH] - Added preprocessor for iOS OpenGL/ES 3.0. The preprocessor is RIVE_IOS_GLES. Edited all code to compile. - Added preprocessor for Win32 OpenGL/ES 3.1+ through PowerVR SDK. The preprocessor is RIVE_DESKTOP_GLES_PVR. Edited all code to compile. - Changed a few "private" declarations to "protected" to make it easier to inherit and reuse SDK code rather than rewriting. - Marked Factory::decodeFont() and Factory::decodeAudio() virtual. --- include/rive/factory.hpp | 4 +- renderer/include/rive/renderer/gl/gles3.hpp | 46 ++++++++++++++++++- .../renderer/gl/render_context_gl_impl.hpp | 2 +- .../include/rive/renderer/render_context.hpp | 4 +- .../include/rive/renderer/rive_renderer.hpp | 2 +- renderer/src/gl/gl_state.cpp | 6 +-- renderer/src/gl/load_gles_extensions.cpp | 3 ++ renderer/src/gl/pls_impl_ext_native.cpp | 4 ++ renderer/src/gl/pls_impl_rw_texture.cpp | 6 +++ renderer/src/gl/pls_impl_webgl.cpp | 8 ++++ renderer/src/gl/render_context_gl_impl.cpp | 23 ++++++---- renderer/src/gl/render_target_gl.cpp | 4 +- 12 files changed, 92 insertions(+), 20 deletions(-) diff --git a/include/rive/factory.hpp b/include/rive/factory.hpp index 4fedd2ee1..b6776ff9c 100644 --- a/include/rive/factory.hpp +++ b/include/rive/factory.hpp @@ -60,9 +60,9 @@ class Factory virtual rcp decodeImage(Span) = 0; - rcp decodeFont(Span); + virtual rcp decodeFont(Span); - rcp decodeAudio(Span); + virtual rcp decodeAudio(Span); // Non-virtual helpers diff --git a/renderer/include/rive/renderer/gl/gles3.hpp b/renderer/include/rive/renderer/gl/gles3.hpp index 20caae95b..e2421a80a 100644 --- a/renderer/include/rive/renderer/gl/gles3.hpp +++ b/renderer/include/rive/renderer/gl/gles3.hpp @@ -17,6 +17,16 @@ #define glClearPixelLocalStorageuiEXT(...) RIVE_UNREACHABLE() #endif +#ifdef RIVE_DESKTOP_GLES_PVR +#include +#include +#define GL_GLEXT_PROTOTYPES +#include // not a mistake, https://registry.khronos.org/OpenGL/index_es.php + +#define GL_COMPLETION_STATUS_KHR 0x91B1 + +#endif + #ifdef RIVE_ANDROID #include #include @@ -24,6 +34,40 @@ #include #endif +#ifdef RIVE_IOS_GLES +#define GL_OES_framebuffer_object +#define GL_PROTOTYPES +#include +#include + +#define GL_COMPLETION_STATUS_KHR 0x91B1 + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +#endif + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif + +#endif + #ifdef RIVE_WEBGL #include #include @@ -112,7 +156,7 @@ extern void glProvokingVertexANGLE(GLenum provokeMode); #endif // RIVE_WEBGL -#if defined(RIVE_ANDROID) || defined(RIVE_WEBGL) +#if defined(RIVE_ANDROID) || defined(RIVE_IOS_GLES) || defined(RIVE_WEBGL) // GLES 3.1 functionality is pulled in as an extension. Define these to avoid // compile errors, even if we won't use them. #define GL_SHADER_STORAGE_BUFFER 0x90D2 diff --git a/renderer/include/rive/renderer/gl/render_context_gl_impl.hpp b/renderer/include/rive/renderer/gl/render_context_gl_impl.hpp index 811cd0b28..0139e12fc 100644 --- a/renderer/include/rive/renderer/gl/render_context_gl_impl.hpp +++ b/renderer/include/rive/renderer/gl/render_context_gl_impl.hpp @@ -114,7 +114,7 @@ class RenderContextGLImpl : public RenderContextHelperImpl AtlasRenderType atlasDesiredRenderType); #endif -private: +protected: class DrawProgram; // Manages how we implement pixel local storage in shaders. diff --git a/renderer/include/rive/renderer/render_context.hpp b/renderer/include/rive/renderer/render_context.hpp index 4a4fb3c2c..30515322f 100644 --- a/renderer/include/rive/renderer/render_context.hpp +++ b/renderer/include/rive/renderer/render_context.hpp @@ -305,7 +305,7 @@ class RenderContext : public RiveRenderFactory // (for rendering into) and a render image (for compositing into draws). rcp makeRenderCanvas(uint32_t width, uint32_t height); -private: +protected: friend class Draw; friend class PathDraw; friend class ImageRectDraw; @@ -963,7 +963,7 @@ class RenderContext : public RiveRenderFactory uint32_t joinSegmentCount, uint32_t contourIDWithFlags); - private: + protected: LogicalFlush* const m_flush; WriteOnlyMappedMemory& m_tessSpanData; const uint32_t m_pathID; diff --git a/renderer/include/rive/renderer/rive_renderer.hpp b/renderer/include/rive/renderer/rive_renderer.hpp index dd8d930e3..06094bbf4 100644 --- a/renderer/include/rive/renderer/rive_renderer.hpp +++ b/renderer/include/rive/renderer/rive_renderer.hpp @@ -69,7 +69,7 @@ class RiveRenderer : public Renderer } #endif -private: +protected: void clipRectImpl(AABB, const RiveRenderPath* originalPath); void clipPathImpl(const RiveRenderPath*); diff --git a/renderer/src/gl/gl_state.cpp b/renderer/src/gl/gl_state.cpp index 54ce6558f..3e2684d1b 100644 --- a/renderer/src/gl/gl_state.cpp +++ b/renderer/src/gl/gl_state.cpp @@ -47,7 +47,7 @@ void GLState::invalidate() glPixelStorei(GL_PACK_ALIGNMENT, 4); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); -#ifndef RIVE_ANDROID +#if !defined(RIVE_ANDROID) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) // D3D and Metal both have a provoking vertex convention of "first" for flat // varyings, and it's very costly for ANGLE to implement the OpenGL // convention of "last" on these backends. To workaround this, ANGLE @@ -60,8 +60,8 @@ void GLState::invalidate() } #endif - // WebGL doesn't support glMaxShaderCompilerThreadsKHR. -#ifndef RIVE_WEBGL + // WebGL, iOS GLES 3.0 and Win32 GLES PVR don't support glMaxShaderCompilerThreadsKHR. +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) if (m_capabilities.KHR_parallel_shader_compile) { // Allow GL's shader compilation to use 2 background threads. diff --git a/renderer/src/gl/load_gles_extensions.cpp b/renderer/src/gl/load_gles_extensions.cpp index 0bf387155..d9b0872ff 100644 --- a/renderer/src/gl/load_gles_extensions.cpp +++ b/renderer/src/gl/load_gles_extensions.cpp @@ -2,6 +2,8 @@ * Copyright 2023 Rive */ +#if !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) + #include "rive/renderer/gl/gles3.hpp" #include @@ -135,3 +137,4 @@ void LoadAndValidateGLESExtensions(GLCapabilities* extensions) } } } +#endif diff --git a/renderer/src/gl/pls_impl_ext_native.cpp b/renderer/src/gl/pls_impl_ext_native.cpp index 272250f79..df1f0c2dd 100644 --- a/renderer/src/gl/pls_impl_ext_native.cpp +++ b/renderer/src/gl/pls_impl_ext_native.cpp @@ -105,6 +105,7 @@ class RenderContextGLImpl::PLSImplEXTNative void activatePixelLocalStorage(RenderContextGLImpl* impl, const FlushDescriptor& desc) override { +#ifndef RIVE_IOS_GLES assert(impl->m_capabilities.EXT_shader_pixel_local_storage); assert(impl->m_capabilities.EXT_shader_framebuffer_fetch || impl->m_capabilities.ARM_shader_framebuffer_fetch); @@ -173,11 +174,13 @@ class RenderContextGLImpl::PLSImplEXTNative m_state->bindVAO(m_plsLoadStoreVAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } +#endif } void deactivatePixelLocalStorage(RenderContextGLImpl* impl, const FlushDescriptor& desc) override { +#ifndef RIVE_IOS_GLES if (!desc.fixedFunctionColorOutput) { // EXT_shader_pixel_local_storage doesn't support concurrent @@ -194,6 +197,7 @@ class RenderContextGLImpl::PLSImplEXTNative } glDisable(GL_SHADER_PIXEL_LOCAL_STORAGE_EXT); +#endif } void pushShaderDefines(gpu::InterlockMode, diff --git a/renderer/src/gl/pls_impl_rw_texture.cpp b/renderer/src/gl/pls_impl_rw_texture.cpp index d21535118..11925c43e 100644 --- a/renderer/src/gl/pls_impl_rw_texture.cpp +++ b/renderer/src/gl/pls_impl_rw_texture.cpp @@ -140,6 +140,7 @@ class RenderContextGLImpl::PLSImplRWTexture void activatePixelLocalStorage(RenderContextGLImpl* renderContextImpl, const FlushDescriptor& desc) override { +#ifndef RIVE_IOS_GLES auto renderTarget = static_cast(desc.renderTarget); // Bind and initialize the PLS backing textures. @@ -277,11 +278,13 @@ class RenderContextGLImpl::PLSImplRWTexture } glMemoryBarrierByRegion(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); +#endif } void deactivatePixelLocalStorage(RenderContextGLImpl* renderContextImpl, const FlushDescriptor& desc) override { +#ifndef RIVE_IOS_GLES glMemoryBarrierByRegion(GL_ALL_BARRIER_BITS); if (!desc.fixedFunctionColorOutput && @@ -303,6 +306,7 @@ class RenderContextGLImpl::PLSImplRWTexture framebufferRenderTarget->height()); } } +#endif } void pushShaderDefines(gpu::InterlockMode, @@ -314,7 +318,9 @@ class RenderContextGLImpl::PLSImplRWTexture void onBarrier(const gpu::FlushDescriptor&) override { +#ifndef RIVE_IOS_GLES return glMemoryBarrierByRegion(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); +#endif } private: diff --git a/renderer/src/gl/pls_impl_webgl.cpp b/renderer/src/gl/pls_impl_webgl.cpp index 7039e5300..667cb59a2 100644 --- a/renderer/src/gl/pls_impl_webgl.cpp +++ b/renderer/src/gl/pls_impl_webgl.cpp @@ -203,6 +203,7 @@ void glProvokingVertexANGLE(GLenum provokeMode) namespace rive::gpu { +#ifdef RIVE_WEBGL static GLenum webgl_load_op(gpu::LoadAction loadAction) { switch (loadAction) @@ -327,4 +328,11 @@ RenderContextGLImpl::MakePLSImplWebGL() { return std::make_unique(); } +#else +std::unique_ptr +RenderContextGLImpl::MakePLSImplWebGL() +{ + return nullptr; +} +#endif } // namespace rive::gpu diff --git a/renderer/src/gl/render_context_gl_impl.cpp b/renderer/src/gl/render_context_gl_impl.cpp index 478f99dc6..65d8d1b85 100644 --- a/renderer/src/gl/render_context_gl_impl.cpp +++ b/renderer/src/gl/render_context_gl_impl.cpp @@ -2,6 +2,7 @@ * Copyright 2022 Rive */ +#include "rive/renderer/gl/gles3.hpp" #include "rive/renderer/gl/render_context_gl_impl.hpp" #include "rive/renderer/gl/render_buffer_gl_impl.hpp" @@ -1164,7 +1165,7 @@ void RenderContextGLImpl::resizeAtlasTexture(uint32_t width, uint32_t height) } case AtlasRenderType::r32uiPixelLocalStorageANGLE: { -#ifndef RIVE_ANDROID +#if !defined(RIVE_ANDROID) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) // ANGLE_shader_pixel_local_storage can just resolve and output the // render pass at the end of the PLS render pass. assert(m_atlasRenderTexture != 0); @@ -2044,7 +2045,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) } case AtlasRenderType::r32uiPixelLocalStorageANGLE: { -#ifndef RIVE_ANDROID +#if !defined(RIVE_ANDROID) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) glBeginPixelLocalStorageANGLE( 1, std::array{GL_LOAD_OP_ZERO_ANGLE}.data()); @@ -2055,7 +2056,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) } case AtlasRenderType::r32iAtomicTexture: { -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) constexpr GLint clearZero4i[4]{}; glClearBufferiv(GL_COLOR, 0, clearZero4i); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | @@ -2129,7 +2130,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) // into a GL_R8 texture that can be sampled. if (m_atlasRenderType == AtlasRenderType::r32iAtomicTexture) { -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); #else RIVE_UNREACHABLE(); @@ -2210,7 +2211,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) } case AtlasRenderType::r32uiPixelLocalStorageANGLE: { -#ifndef RIVE_ANDROID +#if !defined(RIVE_ANDROID) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) // Discard PLS now that we've resolved it to GL_R8. glEndPixelLocalStorageANGLE( 1, @@ -2222,7 +2223,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) } case AtlasRenderType::r32iAtomicTexture: { -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); #else @@ -2387,6 +2388,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) m_platformFeatures, &pipelineState); } +#ifndef RIVE_IOS_GLES else { // Set up the next clipRect. @@ -2404,6 +2406,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) clipPlanesEnabled = needsClipPlanes; } } +#endif m_state->setPipelineState(pipelineState); if (enums::any_flag_set(batch.barriers, @@ -2416,11 +2419,13 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) else if (enums::is_flag_set(batch.barriers, BarrierFlags::dstBlend)) { assert(!m_capabilities.KHR_blend_equation_advanced_coherent); +#if !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) if (m_capabilities.KHR_blend_equation_advanced) { glBlendBarrierKHR(); } else +#endif { // Read back the framebuffer where we need a dstColor for // blending. @@ -2609,6 +2614,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) { glDisable(GL_BLEND_ADVANCED_COHERENT_KHR); } +#ifndef RIVE_IOS_GLES if (clipPlanesEnabled) { glDisable(GL_CLIP_DISTANCE0_EXT); @@ -2616,6 +2622,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) glDisable(GL_CLIP_DISTANCE2_EXT); glDisable(GL_CLIP_DISTANCE3_EXT); } +#endif } #ifdef RIVE_DESKTOP_GL @@ -2629,7 +2636,7 @@ void RenderContextGLImpl::flush(const FlushDescriptor& desc) // flushes per frame if we don't call glFlush in between. glFlush(); -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) // ARM Mali-G78 also needs a memory barrier sometimes to ensure a resolve of // EXT_multisampled_render_to_texture. (Note that the spec says these // resolves should all be implicit and automatic.) @@ -2663,7 +2670,7 @@ void RenderContextGLImpl::drawIndexedInstancedNoInstancedAttribs( m_capabilities.maxSupportedInstancesPerFlush)) { flushInjector->flushBeforeInstancedDrawIfNeeded(chunkInstanceCount); -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) if (m_capabilities.ANGLE_base_vertex_base_instance_shader_builtin) { glDrawElementsInstancedBaseInstanceEXT(primitiveTopology, diff --git a/renderer/src/gl/render_target_gl.cpp b/renderer/src/gl/render_target_gl.cpp index 081945401..d828d173e 100644 --- a/renderer/src/gl/render_target_gl.cpp +++ b/renderer/src/gl/render_target_gl.cpp @@ -68,7 +68,7 @@ void TextureRenderTargetGL::bindHeadlessFramebuffer( { m_headlessFramebuffer = glutils::Framebuffer(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_headlessFramebuffer); -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) if (capabilities.ARB_shader_image_load_store) { glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, @@ -151,7 +151,7 @@ RenderTargetGL::MSAAResolveAction TextureRenderTargetGL::bindMSAAFramebuffer( glBindRenderbuffer(GL_RENDERBUFFER, m_msaaDepthStencilBuffer); glBindFramebuffer(GL_FRAMEBUFFER, m_msaaFramebuffer); -#ifndef RIVE_WEBGL +#if !defined(RIVE_WEBGL) && !defined(RIVE_IOS_GLES) && !defined(RIVE_DESKTOP_GLES_PVR) if (renderContextImpl->capabilities() .EXT_multisampled_render_to_texture) {