Skip to content

Commit

Permalink
fix(🤖): Remove internal draw loop (#2763)
Browse files Browse the repository at this point in the history
On Android it removes the need to use JNI back and forth for each frame.
  • Loading branch information
wcandillon authored Nov 23, 2024
1 parent 1352eca commit ba1db84
Show file tree
Hide file tree
Showing 41 changed files with 256 additions and 766 deletions.
130 changes: 65 additions & 65 deletions apps/paper/ios/paper.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion packages/skia/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ add_library(

"${PROJECT_SOURCE_DIR}/../cpp/rnskia/RNSkManager.cpp"
"${PROJECT_SOURCE_DIR}/../cpp/rnskia/RNSkDomView.cpp"
"${PROJECT_SOURCE_DIR}/../cpp/rnskia/RNSkDispatchQueue.cpp"

"${PROJECT_SOURCE_DIR}/../cpp/rnskia/dom/base/DrawingContext.cpp"
"${PROJECT_SOURCE_DIR}/../cpp/rnskia/dom/base/ConcatablePaint.cpp"
Expand Down
49 changes: 0 additions & 49 deletions packages/skia/android/cpp/jni/JniPlatformContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ using TSelf = jni::local_ref<JniPlatformContext::jhybriddata>;
void JniPlatformContext::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", JniPlatformContext::initHybrid),
makeNativeMethod("notifyDrawLoop",
JniPlatformContext::notifyDrawLoopExternal),
makeNativeMethod("notifyTaskReady",
JniPlatformContext::notifyTaskReadyExternal),
});
}

Expand Down Expand Up @@ -134,51 +130,6 @@ sk_sp<SkImage> JniPlatformContext::takeScreenshotFromViewTag(size_t tag) {
return skImage;
}

void JniPlatformContext::startDrawLoop() {
jni::ThreadScope ts;
// Start drawing loop
static auto method =
javaPart_->getClass()->getMethod<void(void)>("beginDrawLoop");
method(javaPart_.get());
}

void JniPlatformContext::stopDrawLoop() {
jni::ThreadScope ts;
// Stop drawing loop
static auto method =
javaPart_->getClass()->getMethod<void(void)>("endDrawLoop");
method(javaPart_.get());
}

void JniPlatformContext::notifyDrawLoopExternal() {
jni::ThreadScope ts;
_onNotifyDrawLoop();
}

void JniPlatformContext::runTaskOnMainThread(std::function<void()> task) {
_taskMutex->lock();
_taskCallbacks.push(task);
_taskMutex->unlock();

// Notify Java that task is ready
static auto method = javaPart_->getClass()->getMethod<void(void)>(
"notifyTaskReadyOnMainThread");
method(javaPart_.get());
}

void JniPlatformContext::notifyTaskReadyExternal() {
jni::ThreadScope ts;
_taskMutex->lock();
auto task = _taskCallbacks.front();
if (task != nullptr) {
_taskCallbacks.pop();
_taskMutex->unlock();
task();
} else {
_taskMutex->unlock();
}
}

void JniPlatformContext::performStreamOperation(
const std::string &sourceUri,
const std::function<void(std::unique_ptr<SkStreamAsset>)> &op) {
Expand Down
18 changes: 1 addition & 17 deletions packages/skia/android/cpp/jni/include/JniPlatformContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ class JniPlatformContext : public jni::HybridClass<JniPlatformContext> {

void raiseError(const std::exception &err);

void startDrawLoop();
void stopDrawLoop();

void notifyDrawLoopExternal();

void notifyTaskReadyExternal();

void runTaskOnMainThread(std::function<void()> task);
Expand All @@ -46,10 +41,6 @@ class JniPlatformContext : public jni::HybridClass<JniPlatformContext> {

sk_sp<SkImage> takeScreenshotFromViewTag(size_t tag);

void setOnNotifyDrawLoop(const std::function<void(void)> &callback) {
_onNotifyDrawLoop = callback;
}

jni::global_ref<jobject> createVideo(const std::string &url);

private:
Expand All @@ -58,16 +49,9 @@ class JniPlatformContext : public jni::HybridClass<JniPlatformContext> {

float _pixelDensity;

std::function<void(void)> _onNotifyDrawLoop;

std::queue<std::function<void()>> _taskCallbacks;

std::shared_ptr<std::mutex> _taskMutex;

explicit JniPlatformContext(
jni::alias_ref<JniPlatformContext::jhybridobject> jThis,
const float pixelDensity)
: _taskMutex(std::make_shared<std::mutex>()),
javaPart_(jni::make_global(jThis)), _pixelDensity(pixelDensity) {}
: javaPart_(jni::make_global(jThis)), _pixelDensity(pixelDensity) {}
};
} // namespace RNSkia
2 changes: 0 additions & 2 deletions packages/skia/android/cpp/jni/include/JniSkiaBaseView.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ class JniSkiaBaseView {

virtual void surfaceDestroyed() { _skiaAndroidView->surfaceDestroyed(); }

virtual void setMode(std::string mode) { _skiaAndroidView->setMode(mode); }

virtual void setDebugMode(bool show) {
_skiaAndroidView->setShowDebugInfo(show);
}
Expand Down
3 changes: 0 additions & 3 deletions packages/skia/android/cpp/jni/include/JniSkiaDomView.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ class JniSkiaDomView : public jni::HybridClass<JniSkiaDomView>,
makeNativeMethod("surfaceDestroyed", JniSkiaDomView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaDomView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaDomView::setMode),
makeNativeMethod("setDebugMode", JniSkiaDomView::setDebugMode),
makeNativeMethod("registerView", JniSkiaDomView::registerView),
makeNativeMethod("unregisterView", JniSkiaDomView::unregisterView)});
Expand All @@ -57,8 +56,6 @@ class JniSkiaDomView : public jni::HybridClass<JniSkiaDomView>,

void surfaceDestroyed() override { JniSkiaBaseView::surfaceDestroyed(); }

void setMode(std::string mode) override { JniSkiaBaseView::setMode(mode); }

void setDebugMode(bool show) override { JniSkiaBaseView::setDebugMode(show); }

void registerView(int nativeId) override {
Expand Down
2 changes: 0 additions & 2 deletions packages/skia/android/cpp/jni/include/JniSkiaManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ class JniSkiaManager : public jni::HybridClass<JniSkiaManager> {
std::shared_ptr<RNSkManager> getSkiaManager() { return _skManager; }

void invalidate() {
_context->stopDrawLoop();
_context->notifyDrawLoop(true);
_skManager = nullptr;
_context = nullptr;
}
Expand Down
3 changes: 0 additions & 3 deletions packages/skia/android/cpp/jni/include/JniSkiaPictureView.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class JniSkiaPictureView : public jni::HybridClass<JniSkiaPictureView>,
JniSkiaPictureView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaPictureView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaPictureView::setMode),
makeNativeMethod("setDebugMode", JniSkiaPictureView::setDebugMode),
makeNativeMethod("registerView", JniSkiaPictureView::registerView),
makeNativeMethod("unregisterView",
Expand All @@ -59,8 +58,6 @@ class JniSkiaPictureView : public jni::HybridClass<JniSkiaPictureView>,

void surfaceDestroyed() override { JniSkiaBaseView::surfaceDestroyed(); }

void setMode(std::string mode) override { JniSkiaBaseView::setMode(mode); }

void setDebugMode(bool show) override { JniSkiaBaseView::setDebugMode(show); }

void registerView(int nativeId) override {
Expand Down
69 changes: 69 additions & 0 deletions packages/skia/android/cpp/rnskia-android/MainThreadDispatcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#include <android/looper.h>
#include <unistd.h>

class MainThreadDispatcher {
private:
ALooper *mainLooper;
int messagePipe[2];
std::queue<std::function<void()>> taskQueue;
std::mutex queueMutex;

static constexpr int LOOPER_ID_MAIN = 1;

void processMessages() {
std::lock_guard<std::mutex> lock(queueMutex);
while (!taskQueue.empty()) {
auto task = taskQueue.front();
taskQueue.pop();
task();
}
}

public:
static MainThreadDispatcher &getInstance() {
static MainThreadDispatcher instance;
return instance;
}

void post(std::function<void()> task) {
// TODO: this is disabled for now but we can clean this up
// if (ALooper_forThread() == mainLooper) {
// task();
// } else {
{
std::lock_guard<std::mutex> lock(queueMutex);
taskQueue.push(std::move(task));
}
char wake = 1;
write(messagePipe[1], &wake, 1);
// }
}

~MainThreadDispatcher() {
close(messagePipe[0]);
close(messagePipe[1]);
}

private:
MainThreadDispatcher() {
mainLooper = ALooper_forThread();
if (!mainLooper) {
mainLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
}

pipe(messagePipe);

ALooper_addFd(
mainLooper, messagePipe[0], LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT,
[](int fd, int events, void *data) -> int {
char buf[1];
read(fd, buf, 1);
auto dispatcher = static_cast<MainThreadDispatcher *>(data);
dispatcher->processMessages();
return 1;
},
this);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "AHardwareBufferUtils.h"
#include "JniPlatformContext.h"
#include "MainThreadDispatcher.h"
#include "RNSkAndroidVideo.h"
#include "RNSkPlatformContext.h"

Expand All @@ -37,13 +38,9 @@ class RNSkAndroidPlatformContext : public RNSkPlatformContext {
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker)
: RNSkPlatformContext(runtime, jsCallInvoker,
jniPlatformContext->getPixelDensity()),
_jniPlatformContext(jniPlatformContext) {
// Hook onto the notify draw loop callback in the platform context
jniPlatformContext->setOnNotifyDrawLoop(
[this]() { notifyDrawLoop(false); });
}
_jniPlatformContext(jniPlatformContext) {}

~RNSkAndroidPlatformContext() { stopDrawLoop(); }
~RNSkAndroidPlatformContext() {}

void performStreamOperation(
const std::string &sourceUri,
Expand Down Expand Up @@ -163,17 +160,13 @@ class RNSkAndroidPlatformContext : public RNSkPlatformContext {
}

void runOnMainThread(std::function<void()> task) override {
_jniPlatformContext->runTaskOnMainThread(task);
MainThreadDispatcher::getInstance().post(std::move(task));
}

sk_sp<SkImage> takeScreenshotFromViewTag(size_t tag) override {
return _jniPlatformContext->takeScreenshotFromViewTag(tag);
}

void startDrawLoop() override { _jniPlatformContext->startDrawLoop(); }

void stopDrawLoop() override { _jniPlatformContext->stopDrawLoop(); }

private:
JniPlatformContext *_jniPlatformContext;
};
Expand Down
16 changes: 3 additions & 13 deletions packages/skia/android/cpp/rnskia-android/RNSkAndroidView.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class RNSkBaseAndroidView {

virtual float getPixelDensity() = 0;

virtual void setMode(std::string mode) = 0;

virtual void setShowDebugInfo(bool show) = 0;

virtual void viewDidUnmount() = 0;
Expand All @@ -42,7 +40,7 @@ class RNSkAndroidView : public T, public RNSkBaseAndroidView {

// Try to render directly when the surface has been set so that
// we don't have to wait until the draw loop returns.
RNSkView::renderImmediate();
RNSkView::redraw();
}

void surfaceDestroyed() override {
Expand All @@ -55,24 +53,16 @@ class RNSkAndroidView : public T, public RNSkBaseAndroidView {
->surfaceSizeChanged(surface, width, height);
// This is only need for the first time to frame, this renderImmediate call
// will invoke updateTexImage for the previous frame
RNSkView::renderImmediate();
RNSkView::redraw();
}

float getPixelDensity() override {
return T::getPlatformContext()->getPixelDensity();
}

void setMode(std::string mode) override {
if (mode.compare("continuous") == 0) {
T::setDrawingMode(RNSkDrawingMode::Continuous);
} else {
T::setDrawingMode(RNSkDrawingMode::Default);
}
}

void setShowDebugInfo(bool show) override { T::setShowDebugOverlays(show); }

void viewDidUnmount() override { T::endDrawingLoop(); }
void viewDidUnmount() override {}

std::shared_ptr<RNSkView> getSkiaView() override {
return T::shared_from_this();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ bool RNSkOpenGLCanvasProvider::renderToCanvas(

// Swap buffers and show on screen
_surfaceHolder->present();

return true;
} else {
// the render context did not provide a surface
Expand Down
Loading

0 comments on commit ba1db84

Please sign in to comment.