Skip to content

Commit

Permalink
Implement image meshes in PLS
Browse files Browse the repository at this point in the history
Diffs=
ca1d2229b Implement image meshes in PLS (#5942)

Co-authored-by: Chris Dalton <[email protected]>
  • Loading branch information
csmartdalton and csmartdalton committed Sep 5, 2023
1 parent c4788bf commit 3281c19
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
99a7282e629feb48b6d7fb8d37a7bef9cf98386b
ca1d2229bb518848920c1ef0d275a8617eedb84f
2 changes: 1 addition & 1 deletion .rive_renderer
Original file line number Diff line number Diff line change
@@ -1 +1 @@
39965129acbc42977031f0eb2db4e5999e0906ad
bcd112a0c434f69b7dcfecf7a7ea3f8c25de67e2
21 changes: 17 additions & 4 deletions kotlin/src/main/cpp/include/helpers/worker_thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class WorkerThread
public:
using Work = std::function<void(EGLThreadState*)>;
using WorkID = uint64_t;
constexpr static WorkID kWorkIDAlwaysFinished = 0;

WorkerThread(const char* name, Affinity affinity, const RendererType rendererType) :
m_RendererType(rendererType), mName(name), mAffinity(affinity), mWorkMutex{}
Expand All @@ -44,6 +45,16 @@ class WorkerThread

~WorkerThread() { terminateThread(); }

const std::thread::id threadID() const { return mThread.get_id(); }

// Only accessible on the worker thread.
EGLThreadState* threadState() const
{
assert(std::this_thread::get_id() == threadID());
assert(m_threadState != nullptr);
return m_threadState.get();
}

WorkID run(Work&& work)
{
assert(work != nullptr); // Clients can't push the null termination token.
Expand Down Expand Up @@ -107,7 +118,7 @@ class WorkerThread
pthread_setname_np(pthread_self(), mName.c_str());

GetJNIEnv(); // Attach thread to JVM.
std::unique_ptr<EGLThreadState> threadState = MakeThreadState(m_RendererType);
m_threadState = MakeThreadState(m_RendererType);

std::unique_lock lock(mWorkMutex);
for (;;)
Expand All @@ -126,20 +137,21 @@ class WorkerThread
}

lock.unlock();
work(threadState.get());
work(m_threadState.get());
lock.lock();

++m_lastCompletedWorkID;
m_workedCompletedCondition.notify_all();
}
m_threadState.reset();
DetachThread();
}

const std::string mName;
const Affinity mAffinity;

WorkID m_lastPushedWorkID = 0;
std::atomic<WorkID> m_lastCompletedWorkID = 0;
WorkID m_lastPushedWorkID = kWorkIDAlwaysFinished;
std::atomic<WorkID> m_lastCompletedWorkID = kWorkIDAlwaysFinished;
bool mIsTerminated = false;

std::queue<std::function<void(EGLThreadState*)>> mWorkQueue;
Expand All @@ -148,5 +160,6 @@ class WorkerThread

std::mutex mWorkMutex;
std::thread mThread;
std::unique_ptr<EGLThreadState> m_threadState;
};
} // namespace rive_android
115 changes: 107 additions & 8 deletions kotlin/src/main/cpp/src/helpers/android_factories.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "helpers/thread_state_pls.hpp"
#include "rive/math/math_types.hpp"
#include "rive/pls/pls_image.hpp"
#include "rive/pls/gl/pls_render_buffer_gl_impl.hpp"
#include "rive/pls/gl/pls_render_context_gl_impl.hpp"

#include <vector>
Expand Down Expand Up @@ -133,6 +134,111 @@ std::vector<uint8_t> AndroidSkiaFactory::platformDecode(Span<const uint8_t> enco
return pixels;
}

// PLSRenderBufferGLImpl specialization that can take advantage EGLWorker and be used on or off the
// GL thread.
class AndroidPLSRenderBuffer : public PLSRenderBufferGLImpl
{
public:
AndroidPLSRenderBuffer(RenderBufferType type, RenderBufferFlags flags, size_t sizeInBytes) :
PLSRenderBufferGLImpl(type, flags, sizeInBytes),
m_glWorker(rive_android::EGLWorker::Current(rive_android::RendererType::Rive))
{
if (std::this_thread::get_id() != m_glWorker->threadID())
{
// We aren't on the GL thread. Init this object on the GL thread.
// Keep this class alive until the worker thread finishes initializing it.
rcp<AndroidPLSRenderBuffer> thisRef = ref_rcp(this);
m_bufferCreationWorkID =
m_glWorker->run([thisRef](rive_android::EGLThreadState* threadState) {
auto plsState = static_cast<rive_android::PLSThreadState*>(threadState);
auto* glImpl =
plsState->plsContext()->static_impl_cast<PLSRenderContextGLImpl>();
thisRef->init(ref_rcp(glImpl->state()));
});
}
else
{
auto plsState = static_cast<rive_android::PLSThreadState*>(m_glWorker->threadState());
auto* glImpl = plsState->plsContext()->static_impl_cast<PLSRenderContextGLImpl>();
init(ref_rcp(glImpl->state()));
m_bufferCreationWorkID = rive_android::WorkerThread::kWorkIDAlwaysFinished;
}
}

~AndroidPLSRenderBuffer()
{
if (std::this_thread::get_id() != m_glWorker->threadID())
{
// Ensure we are done initializing the buffers before we turn around and delete them.
m_glWorker->waitUntilComplete(m_bufferCreationWorkID);
// We aren't on the GL thread. Intercept the buffers before ~PLSRenderBufferGLImpl(),
// and then marshal them off to the GL thread for deletion.
std::array<GLuint, pls::kBufferRingSize> buffersToDelete = detachBuffers();
rcp<GLState> glState = ref_rcp(state());
m_glWorker->run([buffersToDelete, glState](rive_android::EGLThreadState* threadState) {
for (GLuint bufferID : buffersToDelete)
{
if (bufferID != 0)
{
glState->deleteBuffer(bufferID);
}
}
});
}
}

void* onMap() override
{
if (std::this_thread::get_id() != m_glWorker->threadID())
{
// We aren't on the GL thread. Allocate a side buffer to fill.
assert(m_offThreadBufferDataMirror == nullptr);
m_offThreadBufferDataMirror.reset(new uint8_t[sizeInBytes()]);
return m_offThreadBufferDataMirror.get();
}
else
{
return PLSRenderBufferGLImpl::onMap();
}
}

void onUnmap() override
{
if (std::this_thread::get_id() != m_glWorker->threadID())
{
// We aren't on the GL thread. Marshal our side buffer to the GL thread to update the
// buffer.
const uint8_t* sideBufferData = m_offThreadBufferDataMirror.release();
assert(sideBufferData != nullptr);
// Keep this class alive until the worker thread finishes updating the buffer.
rcp<AndroidPLSRenderBuffer> thisRef = ref_rcp(this);
m_glWorker->run([sideBufferData, thisRef](rive_android::EGLThreadState* threadState) {
void* ptr = thisRef->PLSRenderBufferGLImpl::onMap();
memcpy(ptr, sideBufferData, thisRef->sizeInBytes());
thisRef->PLSRenderBufferGLImpl::onUnmap();
delete[] sideBufferData;
});
}
else
{
assert(!m_offThreadBufferDataMirror);
PLSRenderBufferGLImpl::onUnmap();
}
}

protected:
const rcp<rive_android::EGLWorker> m_glWorker;
std::unique_ptr<uint8_t[]> m_offThreadBufferDataMirror;
rive_android::EGLWorker::WorkID m_bufferCreationWorkID;
};

rcp<RenderBuffer> AndroidPLSFactory::makeRenderBuffer(RenderBufferType type,
RenderBufferFlags flags,
size_t sizeInBytes)
{
return make_rcp<AndroidPLSRenderBuffer>(type, flags, sizeInBytes);
}

class AndroidPLSImage : public PLSImage
{
public:
Expand Down Expand Up @@ -167,17 +273,10 @@ class AndroidPLSImage : public PLSImage
}

private:
rcp<rive_android::EGLWorker> m_glWorker;
const rcp<rive_android::EGLWorker> m_glWorker;
rive_android::EGLWorker::WorkID m_textureCreationWorkID;
};

rcp<RenderBuffer> AndroidPLSFactory::makeRenderBuffer(RenderBufferType type,
RenderBufferFlags flags,
size_t sizeInBytes)
{
return nullptr;
}

std::unique_ptr<RenderImage> AndroidPLSFactory::decodeImage(Span<const uint8_t> encodedBytes)
{
uint32_t width, height;
Expand Down

0 comments on commit 3281c19

Please sign in to comment.