Skip to content

Commit

Permalink
Merge PLSFactory into PLSRenderContext
Browse files Browse the repository at this point in the history
Once we start creating GPU buffers for image meshes, it won't make sense for the PLSFactory to be separate from PLSRenderContext anymore. It already didn't make sense with image textures, but we worked around that by awkwardly and temporarily storing the image data on PLSImage. This change also removes that awkwardness.

Diffs=
f2f5b297f Merge PLSFactory into PLSRenderContext (#5896)

Co-authored-by: Chris Dalton <[email protected]>
Co-authored-by: Luigi Rosso <[email protected]>
  • Loading branch information
3 people committed Aug 28, 2023
1 parent 6d01a95 commit 5294bb6
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16cf8082fd9a66014305994c736d98cc12d96c73
f2f5b297f4c00c96c99e2c0101dc25faee9217c9
2 changes: 1 addition & 1 deletion .rive_renderer
Original file line number Diff line number Diff line change
@@ -1 +1 @@
aebf0cd5e3e2fa712b6838022d9e9c8642d9002d
9101f852f8483bd703fc6b3bbcf43f831cf038a5
2 changes: 1 addition & 1 deletion kotlin/src/main/cpp/build.rive.for.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions kotlin/src/main/cpp/include/helpers/android_factories.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class AndroidSkiaFactory : public rive::SkiaFactory
class AndroidPLSFactory : public rive::pls::PLSFactory
{
public:
rive::rcp<rive::RenderBuffer> makeBufferU16(rive::Span<const uint16_t>) override;
rive::rcp<rive::RenderBuffer> makeBufferU32(rive::Span<const uint32_t>) override;
rive::rcp<rive::RenderBuffer> makeBufferF32(rive::Span<const float>) override;
std::unique_ptr<rive::RenderImage> decodeImage(rive::Span<const uint8_t>) override;
};

Expand Down
6 changes: 0 additions & 6 deletions kotlin/src/main/cpp/include/helpers/thread_state_egl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<EGLConfig>(0);
};
} // namespace rive_android
Expand Down
11 changes: 4 additions & 7 deletions kotlin/src/main/cpp/include/helpers/thread_state_pls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,20 @@ namespace rive_android
class PLSThreadState : public EGLThreadState
{
public:
PLSThreadState() = default;

~PLSThreadState() { releaseContext(); }
PLSThreadState();
~PLSThreadState();

rive::pls::PLSRenderContext* plsContext() const { return m_plsContext.get(); }

void destroySurface(EGLSurface eglSurface) override;

void makeCurrent(EGLSurface eglSurface) override;

protected:
void releaseContext() override;

private:
std::unique_ptr<rive::pls::PLSRenderContext> 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

Expand Down
6 changes: 1 addition & 5 deletions kotlin/src/main/cpp/include/helpers/thread_state_skia.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,14 @@ class SkiaThreadState : public EGLThreadState
{
public:
SkiaThreadState() = default;

~SkiaThreadState() { releaseContext(); }
~SkiaThreadState();

sk_sp<SkSurface> createSkiaSurface(EGLSurface, int width, int height);

void destroySurface(EGLSurface) override;

void makeCurrent(EGLSurface) override;

protected:
void releaseContext() override;

private:
sk_sp<GrDirectContext> m_skContext = nullptr;
};
Expand Down
4 changes: 2 additions & 2 deletions kotlin/src/main/cpp/include/helpers/worker_thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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; }

Expand Down
54 changes: 41 additions & 13 deletions kotlin/src/main/cpp/src/helpers/android_factories.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <vector>

bool JNIDecodeImage(rive::Span<const uint8_t> encodedBytes,
using namespace rive;
using namespace rive::pls;

bool JNIDecodeImage(Span<const uint8_t> encodedBytes,
bool premultiply,
uint32_t* width,
uint32_t* height,
Expand Down Expand Up @@ -108,8 +114,8 @@ bool JNIDecodeImage(rive::Span<const uint8_t> encodedBytes,
return true;
}

std::vector<uint8_t> AndroidSkiaFactory::platformDecode(rive::Span<const uint8_t> encodedBytes,
rive::SkiaFactory::ImageInfo* info)
std::vector<uint8_t> AndroidSkiaFactory::platformDecode(Span<const uint8_t> encodedBytes,
SkiaFactory::ImageInfo* info)
{
uint32_t width, height;
std::vector<uint8_t> pixels;
Expand All @@ -127,29 +133,51 @@ std::vector<uint8_t> AndroidSkiaFactory::platformDecode(rive::Span<const uint8_t
return pixels;
}

class AndroidPLSImage : public rive::pls::PLSImage
class AndroidPLSImage : public PLSImage
{
public:
AndroidPLSImage(int width, int height, std::unique_ptr<const uint8_t[]> imageDataRGBA) :
PLSImage(width, height, std::move(imageDataRGBA)),
AndroidPLSImage(int width, int height, std::unique_ptr<const uint8_t[]> 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<rive_android::PLSThreadState*>(threadState);
uint32_t mipLevelCount = math::msb(m_Height | m_Width);
auto* glImpl = plsState->plsContext()->static_impl_cast<PLSRenderContextGLImpl>();
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<rive_android::EGLWorker> m_glWorker;
rcp<rive_android::EGLWorker> m_glWorker;
rive_android::EGLWorker::WorkID m_textureCreationWorkID;
};

std::unique_ptr<rive::RenderImage> AndroidPLSFactory::decodeImage(
rive::Span<const uint8_t> encodedBytes)
rcp<RenderBuffer> AndroidPLSFactory::makeBufferU16(Span<const uint16_t> data) { return nullptr; }

rcp<RenderBuffer> AndroidPLSFactory::makeBufferU32(Span<const uint32_t> data) { return nullptr; }

rcp<RenderBuffer> AndroidPLSFactory::makeBufferF32(Span<const float> data) { return nullptr; }

std::unique_ptr<RenderImage> AndroidPLSFactory::decodeImage(Span<const uint8_t> encodedBytes)
{
uint32_t width, height;
std::vector<uint8_t> pixels;
Expand Down
66 changes: 37 additions & 29 deletions kotlin/src/main/cpp/src/helpers/thread_state_pls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,53 @@

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)
{
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();
}
Expand All @@ -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
2 changes: 1 addition & 1 deletion kotlin/src/main/cpp/src/helpers/thread_state_skia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static sk_sp<GrDirectContext> make_skia_context()
return ctx;
}

void SkiaThreadState::releaseContext()
SkiaThreadState::~SkiaThreadState()
{
// Release Skia Context if has been init'd.
if (m_skContext.get())
Expand Down

0 comments on commit 5294bb6

Please sign in to comment.