diff --git a/.rive_head b/.rive_head index 216f57bf..374e1b7f 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -16cf8082fd9a66014305994c736d98cc12d96c73 +f2f5b297f4c00c96c99e2c0101dc25faee9217c9 diff --git a/.rive_renderer b/.rive_renderer index 40666587..b5d001aa 100644 --- a/.rive_renderer +++ b/.rive_renderer @@ -1 +1 @@ -aebf0cd5e3e2fa712b6838022d9e9c8642d9002d +9101f852f8483bd703fc6b3bbcf43f831cf038a5 diff --git a/kotlin/src/main/cpp/build.rive.for.sh b/kotlin/src/main/cpp/build.rive.for.sh index a470668e..1e501d34 100755 --- a/kotlin/src/main/cpp/build.rive.for.sh +++ b/kotlin/src/main/cpp/build.rive.for.sh @@ -155,7 +155,7 @@ buildFor() { # Build librive_pls_renderer (internally builds librive) pushd "$RIVE_RUNTIME_DIR"/../pls/out - premake5 --scripts="$RIVE_RUNTIME_DIR"/build --with_rive_text --os=android --arch="$SKIA_ARCH" gmake2 + premake5 --scripts="$RIVE_RUNTIME_DIR"/build --with_rive_text --no-rive-decoders --os=android --arch="$SKIA_ARCH" gmake2 make config=$CONFIG -j20 rive rive_pls_renderer popd diff --git a/kotlin/src/main/cpp/include/helpers/android_factories.hpp b/kotlin/src/main/cpp/include/helpers/android_factories.hpp index 7f1d42af..07fe03ac 100644 --- a/kotlin/src/main/cpp/include/helpers/android_factories.hpp +++ b/kotlin/src/main/cpp/include/helpers/android_factories.hpp @@ -25,6 +25,9 @@ class AndroidSkiaFactory : public rive::SkiaFactory class AndroidPLSFactory : public rive::pls::PLSFactory { public: + rive::rcp makeBufferU16(rive::Span) override; + rive::rcp makeBufferU32(rive::Span) override; + rive::rcp makeBufferF32(rive::Span) override; std::unique_ptr decodeImage(rive::Span) override; }; diff --git a/kotlin/src/main/cpp/include/helpers/thread_state_egl.hpp b/kotlin/src/main/cpp/include/helpers/thread_state_egl.hpp index bd136510..38abab59 100644 --- a/kotlin/src/main/cpp/include/helpers/thread_state_egl.hpp +++ b/kotlin/src/main/cpp/include/helpers/thread_state_egl.hpp @@ -27,15 +27,9 @@ class EGLThreadState void swapBuffers(); protected: - virtual void releaseContext() = 0; - EGLSurface m_currentSurface = EGL_NO_SURFACE; - EGLDisplay m_display = EGL_NO_DISPLAY; - EGLContext m_context = EGL_NO_CONTEXT; - -private: EGLConfig m_config = static_cast(0); }; } // namespace rive_android diff --git a/kotlin/src/main/cpp/include/helpers/thread_state_pls.hpp b/kotlin/src/main/cpp/include/helpers/thread_state_pls.hpp index 632b6a49..082e5734 100644 --- a/kotlin/src/main/cpp/include/helpers/thread_state_pls.hpp +++ b/kotlin/src/main/cpp/include/helpers/thread_state_pls.hpp @@ -14,9 +14,8 @@ namespace rive_android class PLSThreadState : public EGLThreadState { public: - PLSThreadState() = default; - - ~PLSThreadState() { releaseContext(); } + PLSThreadState(); + ~PLSThreadState(); rive::pls::PLSRenderContext* plsContext() const { return m_plsContext.get(); } @@ -24,13 +23,11 @@ class PLSThreadState : public EGLThreadState void makeCurrent(EGLSurface eglSurface) override; -protected: - void releaseContext() override; - private: std::unique_ptr m_plsContext; - bool m_ownsCurrentSurface = false; + // 1x1 Pbuffer surface that allows us to make the GL context current without a window surface. + EGLSurface m_backgroundSurface; }; } // namespace rive_android diff --git a/kotlin/src/main/cpp/include/helpers/thread_state_skia.hpp b/kotlin/src/main/cpp/include/helpers/thread_state_skia.hpp index 10d85a86..e5185721 100644 --- a/kotlin/src/main/cpp/include/helpers/thread_state_skia.hpp +++ b/kotlin/src/main/cpp/include/helpers/thread_state_skia.hpp @@ -17,8 +17,7 @@ class SkiaThreadState : public EGLThreadState { public: SkiaThreadState() = default; - - ~SkiaThreadState() { releaseContext(); } + ~SkiaThreadState(); sk_sp createSkiaSurface(EGLSurface, int width, int height); @@ -26,9 +25,6 @@ class SkiaThreadState : public EGLThreadState void makeCurrent(EGLSurface) override; -protected: - void releaseContext() override; - private: sk_sp m_skContext = nullptr; }; diff --git a/kotlin/src/main/cpp/include/helpers/worker_thread.hpp b/kotlin/src/main/cpp/include/helpers/worker_thread.hpp index 16a216bc..9ec3837e 100644 --- a/kotlin/src/main/cpp/include/helpers/worker_thread.hpp +++ b/kotlin/src/main/cpp/include/helpers/worker_thread.hpp @@ -44,7 +44,7 @@ class WorkerThread ~WorkerThread() { terminateThread(); } - WorkID run(Work work) + WorkID run(Work&& work) { assert(work != nullptr); // Clients can't push the null termination token. uint64_t pushedWorkID; @@ -71,7 +71,7 @@ class WorkerThread } } - void runAndWait(Work work) { waitUntilComplete(run(work)); } + void runAndWait(Work&& work) { waitUntilComplete(run(std::move(work))); } bool canScheduleWork(WorkID workID) const { return m_lastCompletedWorkID >= workID; } diff --git a/kotlin/src/main/cpp/src/helpers/android_factories.cpp b/kotlin/src/main/cpp/src/helpers/android_factories.cpp index fc71de48..ce9801e2 100644 --- a/kotlin/src/main/cpp/src/helpers/android_factories.cpp +++ b/kotlin/src/main/cpp/src/helpers/android_factories.cpp @@ -6,11 +6,17 @@ #include "helpers/egl_worker.hpp" #include "helpers/general.hpp" +#include "helpers/thread_state_pls.hpp" +#include "rive/math/math_types.hpp" #include "rive/pls/pls_image.hpp" +#include "rive/pls/gl/pls_render_context_gl_impl.hpp" #include -bool JNIDecodeImage(rive::Span encodedBytes, +using namespace rive; +using namespace rive::pls; + +bool JNIDecodeImage(Span encodedBytes, bool premultiply, uint32_t* width, uint32_t* height, @@ -108,8 +114,8 @@ bool JNIDecodeImage(rive::Span encodedBytes, return true; } -std::vector AndroidSkiaFactory::platformDecode(rive::Span encodedBytes, - rive::SkiaFactory::ImageInfo* info) +std::vector AndroidSkiaFactory::platformDecode(Span encodedBytes, + SkiaFactory::ImageInfo* info) { uint32_t width, height; std::vector pixels; @@ -127,29 +133,51 @@ std::vector AndroidSkiaFactory::platformDecode(rive::Span imageDataRGBA) : - PLSImage(width, height, std::move(imageDataRGBA)), + AndroidPLSImage(int width, int height, std::unique_ptr imageDataRGBAPtr) : + PLSImage(width, height), m_glWorker(rive_android::EGLWorker::Current(rive_android::RendererType::Rive)) - {} + { + // Create the texture on the worker thread where the GL context is current. + const uint8_t* imageDataRGBA = imageDataRGBAPtr.release(); + m_textureCreationWorkID = m_glWorker->run([this, imageDataRGBA]( + rive_android::EGLThreadState* threadState) { + auto plsState = static_cast(threadState); + uint32_t mipLevelCount = math::msb(m_Height | m_Width); + auto* glImpl = plsState->plsContext()->static_impl_cast(); + resetTexture(glImpl->makeImageTexture(m_Width, m_Height, mipLevelCount, imageDataRGBA)); + delete[] imageDataRGBA; + }); + } ~AndroidPLSImage() override { - rive::pls::PLSTexture* texture = releaseTexture(); - if (texture) + // Ensure we are done initializing the texture before we turn around and delete it. + m_glWorker->waitUntilComplete(m_textureCreationWorkID); + // Since this is the destructor, we know nobody else is using this object anymore and there + // is not a race condition from accessing the texture from any thread. + PLSTexture* texture = releaseTexture(); + if (texture != nullptr) { - m_glWorker->run([=](rive_android::EGLThreadState*) { texture->unref(); }); + // Delete the texture on the worker thread where the GL context is current. + m_glWorker->run([texture](rive_android::EGLThreadState*) { texture->unref(); }); } } private: - rive::rcp m_glWorker; + rcp m_glWorker; + rive_android::EGLWorker::WorkID m_textureCreationWorkID; }; -std::unique_ptr AndroidPLSFactory::decodeImage( - rive::Span encodedBytes) +rcp AndroidPLSFactory::makeBufferU16(Span data) { return nullptr; } + +rcp AndroidPLSFactory::makeBufferU32(Span data) { return nullptr; } + +rcp AndroidPLSFactory::makeBufferF32(Span data) { return nullptr; } + +std::unique_ptr AndroidPLSFactory::decodeImage(Span encodedBytes) { uint32_t width, height; std::vector pixels; diff --git a/kotlin/src/main/cpp/src/helpers/thread_state_pls.cpp b/kotlin/src/main/cpp/src/helpers/thread_state_pls.cpp index 942204f7..44477c78 100644 --- a/kotlin/src/main/cpp/src/helpers/thread_state_pls.cpp +++ b/kotlin/src/main/cpp/src/helpers/thread_state_pls.cpp @@ -5,6 +5,39 @@ namespace rive_android { +PLSThreadState::PLSThreadState() +{ + // Create a 1x1 Pbuffer surface that we can use to guarantee m_context is always current on this + // thread. + const EGLint PbufferAttrs[] = { + EGL_WIDTH, + 1, + EGL_HEIGHT, + 1, + EGL_NONE, + }; + m_backgroundSurface = eglCreatePbufferSurface(m_display, m_config, PbufferAttrs); + EGL_ERR_CHECK(); + if (m_backgroundSurface == EGL_NO_SURFACE) + { + LOGE("Failed to create a 1x1 background Pbuffer surface for PLS"); + } + + eglMakeCurrent(m_display, m_backgroundSurface, m_backgroundSurface, m_context); + m_currentSurface = m_backgroundSurface; + + m_plsContext = rive::pls::PLSRenderContextGLImpl::MakeContext(); +} + +PLSThreadState::~PLSThreadState() +{ + assert(m_currentSurface == m_backgroundSurface); + m_plsContext.reset(); + + eglDestroySurface(m_display, m_backgroundSurface); + EGL_ERR_CHECK(); +} + void PLSThreadState::destroySurface(EGLSurface eglSurface) { if (eglSurface == EGL_NO_SURFACE) @@ -12,11 +45,13 @@ void PLSThreadState::destroySurface(EGLSurface eglSurface) return; } + assert(eglSurface != m_backgroundSurface); if (m_currentSurface == eglSurface) { - m_ownsCurrentSurface = true; - return; + // Make sure m_context always stays current. + makeCurrent(m_backgroundSurface); } + eglDestroySurface(m_display, eglSurface); EGL_ERR_CHECK(); } @@ -35,34 +70,7 @@ void PLSThreadState::makeCurrent(EGLSurface eglSurface) } eglMakeCurrent(m_display, eglSurface, eglSurface, m_context); - if (m_ownsCurrentSurface) - { - eglDestroySurface(m_display, m_currentSurface); - EGL_ERR_CHECK(); - m_ownsCurrentSurface = false; - } m_currentSurface = eglSurface; EGL_ERR_CHECK(); - - if (m_plsContext == nullptr) - { - m_plsContext = rive::pls::PLSRenderContextGLImpl::MakeContext(); - } -} - -void PLSThreadState::releaseContext() -{ - if (m_plsContext.get()) - { - assert(m_currentSurface != EGL_NO_SURFACE); - m_plsContext.reset(); - } - - if (m_ownsCurrentSurface) - { - eglDestroySurface(m_display, m_currentSurface); - EGL_ERR_CHECK(); - } } - } // namespace rive_android diff --git a/kotlin/src/main/cpp/src/helpers/thread_state_skia.cpp b/kotlin/src/main/cpp/src/helpers/thread_state_skia.cpp index a002303f..37a24eb9 100644 --- a/kotlin/src/main/cpp/src/helpers/thread_state_skia.cpp +++ b/kotlin/src/main/cpp/src/helpers/thread_state_skia.cpp @@ -33,7 +33,7 @@ static sk_sp make_skia_context() return ctx; } -void SkiaThreadState::releaseContext() +SkiaThreadState::~SkiaThreadState() { // Release Skia Context if has been init'd. if (m_skContext.get())