Skip to content

Commit

Permalink
Android: New OpenGL based rendering for windowed and offscreen surfac…
Browse files Browse the repository at this point in the history
…es (#1722)
  • Loading branch information
chrfalch authored Aug 11, 2023
1 parent 35e8311 commit 80b0970
Show file tree
Hide file tree
Showing 24 changed files with 931 additions and 625 deletions.
Binary file added docs/static/img/offscreen/multiple_circles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 5 additions & 6 deletions example/src/Examples/API/Icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ const Icon = ({ icon }: IconProps) => {
return <SkiaPictureView picture={icon} style={style} />;
};

const Screen = () => {
type Props = { color: string };
const Screen: React.FC<Props> = ({ color }) => {
const { github, octocat, stackExchange, overflow } = useSVGs();
return (
<View
Expand All @@ -96,10 +97,8 @@ const Screen = () => {
<Icon icon={overflow} />
<Text>React Native Skia Canvas</Text>
<Canvas style={{ width: 50, height: 50 }}>
<Rect x={0} y={0} width={50} height={50} color="red" />
<Rect x={0} y={0} width={50} height={50} color={color} />
</Canvas>
<Text>React Native View</Text>
<View style={{ backgroundColor: "orange", width: 50, height: 50 }} />
</View>
<View style={{ flex: 1, alignItems: "center" }}>
<Text>React Native SVG</Text>
Expand All @@ -114,9 +113,9 @@ const Screen = () => {
);
};

const HomeScreen = () => <Screen />;
const HomeScreen = () => <Screen color="red" />;

const SettingsScreen = () => <Screen />;
const SettingsScreen = () => <Screen color="green" />;

const Tab = createBottomTabNavigator();

Expand Down
2 changes: 1 addition & 1 deletion package/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ add_library(

"${PROJECT_SOURCE_DIR}/cpp/jni/JniPlatformContext.cpp"
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp"
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/SkiaOpenGLRenderer.cpp"
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp"

"${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiHostObject.cpp"
"${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiValue.cpp"
Expand Down
87 changes: 74 additions & 13 deletions package/android/cpp/jni/include/JniSkiaBaseView.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <JniSkiaManager.h>
#include <RNSkAndroidView.h>

#include <android/bitmap.h>

namespace RNSkia {

namespace jsi = facebook::jsi;
Expand All @@ -18,7 +20,7 @@ class JniSkiaBaseView {
public:
JniSkiaBaseView(jni::alias_ref<JniSkiaManager::javaobject> skiaManager,
std::shared_ptr<RNSkBaseAndroidView> skiaView)
: _manager(skiaManager->cthis()), _skiaView(skiaView) {}
: _manager(skiaManager->cthis()), _skiaAndroidView(skiaView) {}

~JniSkiaBaseView() {}

Expand All @@ -28,38 +30,97 @@ class JniSkiaBaseView {

protected:
virtual void updateTouchPoints(jni::JArrayDouble touches) {
_skiaView->updateTouchPoints(touches);
_skiaAndroidView->updateTouchPoints(touches);
}

virtual void surfaceAvailable(jobject surface, int width, int height) {
_skiaView->surfaceAvailable(surface, width, height);
_skiaAndroidView->surfaceAvailable(surface, width, height);
}

virtual void surfaceSizeChanged(int width, int height) {
_skiaView->surfaceSizeChanged(width, height);
_skiaAndroidView->surfaceSizeChanged(width, height);
}

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

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

virtual void setDebugMode(bool show) { _skiaView->setShowDebugInfo(show); }
virtual void setDebugMode(bool show) {
_skiaAndroidView->setShowDebugInfo(show);
}

virtual void registerView(int nativeId) {
getSkiaManager()->registerSkiaView(nativeId, _skiaView->getSkiaView());
getSkiaManager()->registerSkiaView(nativeId,
_skiaAndroidView->getSkiaView());
}

virtual void unregisterView() {
getSkiaManager()->setSkiaView(_skiaView->getSkiaView()->getNativeId(),
nullptr);
getSkiaManager()->setSkiaView(
_skiaAndroidView->getSkiaView()->getNativeId(), nullptr);
getSkiaManager()->unregisterSkiaView(
_skiaView->getSkiaView()->getNativeId());
_skiaView->viewDidUnmount();
_skiaAndroidView->getSkiaView()->getNativeId());
_skiaAndroidView->viewDidUnmount();
}

/**
* Android specific method for rendering an offscreen GPU buffer to an Android
* bitmap. The result can be used to render the first frame of the Skia render
* to avoid flickering on android.
*/
/*
// TODO: Remove if we find another solution for first frame rendering
// protected native Object renderToBitmap(Object bitmap, int width, int
height); virtual jobject renderToBitmap(jobject bitmapIn, int width, int
height) { auto platformContext = getSkiaManager()->getPlatformContext(); auto
provider = std::make_shared<RNSkOffscreenCanvasProvider>( platformContext,
[]() {}, width, height);
// Render into a gpu backed buffer
_skiaAndroidView->getSkiaView()->getRenderer()->renderImmediate(provider);
auto rect = SkRect::MakeXYWH(0, 0, width, height);
auto image = provider->makeSnapshot(&rect);
AndroidBitmapInfo infoIn;
auto env = facebook::jni::Environment::current();
void *pixels;
// Get image info
if (AndroidBitmap_getInfo(env, bitmapIn, &infoIn) !=
ANDROID_BITMAP_RESULT_SUCCESS) {
return env->NewStringUTF("failed");
}
// Check image
if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
infoIn.format != ANDROID_BITMAP_FORMAT_RGB_565) {
return env->NewStringUTF("Only support ANDROID_BITMAP_FORMAT_RGBA_8888 "
"and ANDROID_BITMAP_FORMAT_RGB_565");
}
auto imageInfo = SkImageInfo::Make(image->width(), image->height(),
image->colorType(), image->alphaType());
// Lock all images
if (AndroidBitmap_lockPixels(env, bitmapIn, &pixels) !=
ANDROID_BITMAP_RESULT_SUCCESS) {
return env->NewStringUTF("AndroidBitmap_lockPixels failed!");
}
// Set pixels from SkImage
image->readPixels(imageInfo, pixels, imageInfo.minRowBytes(), 0, 0);
// Unlocks everything
AndroidBitmap_unlockPixels(env, bitmapIn);
image = nullptr;
provider = nullptr;
return bitmapIn;
}*/

private:
JniSkiaManager *_manager;
std::shared_ptr<RNSkBaseAndroidView> _skiaView;
std::shared_ptr<RNSkBaseAndroidView> _skiaAndroidView;
};

} // namespace RNSkia
32 changes: 20 additions & 12 deletions package/android/cpp/jni/include/JniSkiaDomView.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,21 @@ class JniSkiaDomView : public jni::HybridClass<JniSkiaDomView>,
}

static void registerNatives() {
registerHybrid(
{makeNativeMethod("initHybrid", JniSkiaDomView::initHybrid),
makeNativeMethod("surfaceAvailable", JniSkiaDomView::surfaceAvailable),
makeNativeMethod("surfaceDestroyed", JniSkiaDomView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaDomView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaDomView::setMode),
makeNativeMethod("setDebugMode", JniSkiaDomView::setDebugMode),
makeNativeMethod("updateTouchPoints",
JniSkiaDomView::updateTouchPoints),
makeNativeMethod("registerView", JniSkiaDomView::registerView),
makeNativeMethod("unregisterView", JniSkiaDomView::unregisterView)});
registerHybrid({
makeNativeMethod("initHybrid", JniSkiaDomView::initHybrid),
makeNativeMethod("surfaceAvailable", JniSkiaDomView::surfaceAvailable),
makeNativeMethod("surfaceDestroyed", JniSkiaDomView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaDomView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaDomView::setMode),
makeNativeMethod("setDebugMode", JniSkiaDomView::setDebugMode),
makeNativeMethod("updateTouchPoints",
JniSkiaDomView::updateTouchPoints),
makeNativeMethod("registerView", JniSkiaDomView::registerView),
makeNativeMethod("unregisterView", JniSkiaDomView::unregisterView)
// TODO: Remove if we find another solution for first frame rendering
// makeNativeMethod("renderToBitmap", JniSkiaDomView::renderToBitmap)
});
}

protected:
Expand Down Expand Up @@ -73,6 +76,11 @@ class JniSkiaDomView : public jni::HybridClass<JniSkiaDomView>,

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

// TODO: Remove if we find another solution for first frame rendering
/*jobject renderToBitmap(jobject bitmap, int width, int height) override {
return JniSkiaBaseView::renderToBitmap(bitmap, width, height);
}*/

private:
friend HybridBase;

Expand Down
34 changes: 20 additions & 14 deletions package/android/cpp/jni/include/JniSkiaDrawView.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,21 @@ class JniSkiaDrawView : public jni::HybridClass<JniSkiaDrawView>,
}

static void registerNatives() {
registerHybrid(
{makeNativeMethod("initHybrid", JniSkiaDrawView::initHybrid),
makeNativeMethod("surfaceAvailable",
JniSkiaDrawView::surfaceAvailable),
makeNativeMethod("surfaceDestroyed",
JniSkiaDrawView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaDrawView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaDrawView::setMode),
makeNativeMethod("setDebugMode", JniSkiaDrawView::setDebugMode),
makeNativeMethod("updateTouchPoints",
JniSkiaDrawView::updateTouchPoints),
makeNativeMethod("registerView", JniSkiaDrawView::registerView),
makeNativeMethod("unregisterView", JniSkiaDrawView::unregisterView)});
registerHybrid({
makeNativeMethod("initHybrid", JniSkiaDrawView::initHybrid),
makeNativeMethod("surfaceAvailable", JniSkiaDrawView::surfaceAvailable),
makeNativeMethod("surfaceDestroyed", JniSkiaDrawView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaDrawView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaDrawView::setMode),
makeNativeMethod("setDebugMode", JniSkiaDrawView::setDebugMode),
makeNativeMethod("updateTouchPoints",
JniSkiaDrawView::updateTouchPoints),
makeNativeMethod("registerView", JniSkiaDrawView::registerView),
makeNativeMethod("unregisterView", JniSkiaDrawView::unregisterView),
// TODO: Remove if we find another solution for first frame rendering
// makeNativeMethod("renderToBitmap", JniSkiaDrawView::renderToBitmap)
});
}

protected:
Expand Down Expand Up @@ -74,6 +75,11 @@ class JniSkiaDrawView : public jni::HybridClass<JniSkiaDrawView>,

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

// TODO: Remove if we find another solution for first frame rendering
/*jobject renderToBitmap(jobject bitmap, int width, int height) override {
return JniSkiaBaseView::renderToBitmap(bitmap, width, height);
}*/

private:
friend HybridBase;

Expand Down
39 changes: 24 additions & 15 deletions package/android/cpp/jni/include/JniSkiaPictureView.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,24 @@ class JniSkiaPictureView : public jni::HybridClass<JniSkiaPictureView>,
}

static void registerNatives() {
registerHybrid(
{makeNativeMethod("initHybrid", JniSkiaPictureView::initHybrid),
makeNativeMethod("surfaceAvailable",
JniSkiaPictureView::surfaceAvailable),
makeNativeMethod("surfaceDestroyed",
JniSkiaPictureView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaPictureView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaPictureView::setMode),
makeNativeMethod("setDebugMode", JniSkiaPictureView::setDebugMode),
makeNativeMethod("updateTouchPoints",
JniSkiaPictureView::updateTouchPoints),
makeNativeMethod("registerView", JniSkiaPictureView::registerView),
makeNativeMethod("unregisterView",
JniSkiaPictureView::unregisterView)});
registerHybrid({
makeNativeMethod("initHybrid", JniSkiaPictureView::initHybrid),
makeNativeMethod("surfaceAvailable",
JniSkiaPictureView::surfaceAvailable),
makeNativeMethod("surfaceDestroyed",
JniSkiaPictureView::surfaceDestroyed),
makeNativeMethod("surfaceSizeChanged",
JniSkiaPictureView::surfaceSizeChanged),
makeNativeMethod("setMode", JniSkiaPictureView::setMode),
makeNativeMethod("setDebugMode", JniSkiaPictureView::setDebugMode),
makeNativeMethod("updateTouchPoints",
JniSkiaPictureView::updateTouchPoints),
makeNativeMethod("registerView", JniSkiaPictureView::registerView),
makeNativeMethod("unregisterView", JniSkiaPictureView::unregisterView),
// TODO: Remove if we find another solution for first frame rendering
// makeNativeMethod("renderToBitmap",
// JniSkiaPictureView::renderToBitmap)
});
}

protected:
Expand Down Expand Up @@ -75,6 +78,12 @@ class JniSkiaPictureView : public jni::HybridClass<JniSkiaPictureView>,

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

/*
TODO: Remove if we find another solution for first frame rendering
jobject renderToBitmap(jobject bitmap, int width, int height) override {
return JniSkiaBaseView::renderToBitmap(bitmap, width, height);
}*/

private:
friend HybridBase;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#include <JniPlatformContext.h>
#include <RNSkPlatformContext.h>
#include <SkiaOpenGLRenderer.h>
#include <SkiaOpenGLSurfaceFactory.h>

namespace RNSkia {
namespace jsi = facebook::jsi;
Expand Down Expand Up @@ -38,7 +38,7 @@ class RNSkAndroidPlatformContext : public RNSkPlatformContext {
}

sk_sp<SkSurface> makeOffscreenSurface(int width, int height) override {
return MakeOffscreenGLSurface(width, height);
return SkiaOpenGLSurfaceFactory::makeOffscreenSurface(width, height);
}

void runOnMainThread(std::function<void()> task) override {
Expand Down
Loading

0 comments on commit 80b0970

Please sign in to comment.