Skip to content

Commit

Permalink
Added immediate rendering of Skia Views (#1614)
Browse files Browse the repository at this point in the history
* Added immediate rendering of Skia Views

Instead of waiting till the draw loop calls us back, the view will now perform a direct render after adding itself to the draw loop to ensure that the view is rendered as quickly as possible.

Fixes Skia mount startup time #610 (again)

* Added support for direct rendering on first frame

- Added renderImmediate() method that can be called on the main UI thread to render outside the drawing loop
- Added return value to render method in providers to enable propagating the result value
- Implemented drawRect on iOS to render immediate

* Removed unused method

* Added #pragma

* Added reset of redraw counter when renderImmediate is called

* Added SVG Comparison example

- Added RN SVG
- Added navigation tabbar
- Added comparison example

* Simplified OpenGL C++ renderer a bit

- Moved connecting Skia / OpenGL to render method - it doesn't need to be done only once.

* Added immediate rendering to the Android C++ Base View class

When surface is available, the Android view  will now try to perform an immediate rendering like on iOS.

* Lint

* Lint

* Fixed CI

* Fixed inconcisency in performDraw in SkPictureView Renderer

Added test to see if picture is set inside of the renderer, not outside.

* Remove redudant example

* Remove redudant example

* 💚

* Remove log

* 💚

---------

Co-authored-by: William Candillon <[email protected]>
  • Loading branch information
chrfalch and wcandillon authored Jun 16, 2023
1 parent c7be20b commit 8c80884
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 99 deletions.
1 change: 0 additions & 1 deletion example/src/Examples/API/Icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const useSVGPicture = (module: number) => {
const recorder = Skia.PictureRecorder();
const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, 48, 48));
canvas.drawSvg(svg);
console.log("finishRecordingAsPicture");
return recorder.finishRecordingAsPicture();
}, [svg]);
};
Expand Down
1 change: 0 additions & 1 deletion example/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ export type StackParamList = {
Animation: undefined;
Reanimated: undefined;
Performance: undefined;
SVGComparison: undefined;
};
4 changes: 4 additions & 0 deletions package/android/cpp/rnskia-android/RNSkAndroidView.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class RNSkAndroidView : public T, public RNSkBaseAndroidView {
void surfaceAvailable(jobject surface, int width, int height) override {
std::static_pointer_cast<RNSkOpenGLCanvasProvider>(T::getCanvasProvider())
->surfaceAvailable(surface, width, height);

// 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();
}

void surfaceDestroyed() override {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ float RNSkOpenGLCanvasProvider::getScaledWidth() { return _width; }

float RNSkOpenGLCanvasProvider::getScaledHeight() { return _height; }

void RNSkOpenGLCanvasProvider::renderToCanvas(
bool RNSkOpenGLCanvasProvider::renderToCanvas(
const std::function<void(SkCanvas *)> &cb) {
if (_renderer != nullptr) {
_renderer->run(cb, _width, _height);
return _renderer->run(cb, _width, _height);
}
return false;
}

void RNSkOpenGLCanvasProvider::surfaceAvailable(jobject surface, int width,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class RNSkOpenGLCanvasProvider

float getScaledHeight() override;

void renderToCanvas(const std::function<void(SkCanvas *)> &cb) override;
bool renderToCanvas(const std::function<void(SkCanvas *)> &cb) override;

void surfaceAvailable(jobject surface, int width, int height);

Expand Down
104 changes: 35 additions & 69 deletions package/android/cpp/rnskia-android/SkiaOpenGLRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <android/native_window.h>
#include <android/native_window_jni.h>

#define STENCIL_BUFFER_SIZE 8

namespace RNSkia {
/** Static members */
sk_sp<SkSurface> MakeOffscreenGLSurface(int width, int height) {
Expand Down Expand Up @@ -137,7 +139,7 @@ SkiaOpenGLRenderer::~SkiaOpenGLRenderer() {
_nativeWindow = nullptr;
}

void SkiaOpenGLRenderer::run(const std::function<void(SkCanvas *)> &cb,
bool SkiaOpenGLRenderer::run(const std::function<void(SkCanvas *)> &cb,
int width, int height) {
switch (_renderState) {
case RenderState::Initializing: {
Expand All @@ -148,31 +150,48 @@ void SkiaOpenGLRenderer::run(const std::function<void(SkCanvas *)> &cb,
case RenderState::Rendering: {
// Make sure to initialize the rendering pipeline
if (!ensureInitialised()) {
break;
}

// Ensure we have the Skia surface to draw on. We need to
// pass width and height since the surface will be recreated
// when the view is resized.
if (!ensureSkiaSurface(width, height)) {
return;
return false;
}

if (cb != nullptr) {
// Reset Skia Context since it might be modified by another Skia View
// during rendering.
// RNSkLogger::logToConsole("SKIARENDER - Render begin");

getThreadDrawingContext()->skContext->resetContext();

SkColorType colorType;
// setup surface for fbo0
GrGLFramebufferInfo fboInfo;
fboInfo.fFBOID = 0;
fboInfo.fFormat = 0x8058;
colorType = kN32_SkColorType;

GrBackendRenderTarget backendRT(width, height, 0, STENCIL_BUFFER_SIZE,
fboInfo);

SkSurfaceProps props(0, kUnknown_SkPixelGeometry);

sk_sp<SkSurface> renderTarget(SkSurface::MakeFromBackendRenderTarget(
getThreadDrawingContext()->skContext.get(), backendRT,
kBottomLeft_GrSurfaceOrigin, colorType, nullptr, &props));

auto canvas = renderTarget->getCanvas();

// Draw picture into surface
cb(_skSurface->getCanvas());
cb(canvas);

// Flush
_skSurface->flushAndSubmit();
canvas->flush();

if (!eglSwapBuffers(getThreadDrawingContext()->glDisplay, _glSurface)) {
RNSkLogger::logToConsole("eglSwapBuffers failed: %d\n", eglGetError());
return false;
}

// RNSkLogger::logToConsole("SKIARENDER - render done");
return true;
}
break;

return false;
}
case RenderState::Finishing: {
_renderState = RenderState::Done;
Expand All @@ -184,14 +203,11 @@ void SkiaOpenGLRenderer::run(const std::function<void(SkCanvas *)> &cb,
_glSurface = EGL_NO_SURFACE;
}

// Release Skia Surface
_skSurface = nullptr;

break;
return true;
}
case RenderState::Done: {
// Do nothing. We're done.
break;
return true;
}
}
}
Expand Down Expand Up @@ -326,54 +342,4 @@ bool SkiaOpenGLRenderer::initGLSurface() {

return true;
}

bool SkiaOpenGLRenderer::ensureSkiaSurface(int width, int height) {
if (getThreadDrawingContext()->skContext == nullptr) {
return false;
}

if (_skSurface == nullptr || !_skRenderTarget.isValid() ||
_prevWidth != width || _prevHeight != height) {
glViewport(0, 0, width, height);

_prevWidth = width;
_prevHeight = height;

GLint buffer;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);

GLint stencil;
glGetIntegerv(GL_STENCIL_BITS, &stencil);

GLint samples;
glGetIntegerv(GL_SAMPLES, &samples);

auto maxSamples =
getThreadDrawingContext()->skContext->maxSurfaceSampleCountForColorType(
kRGBA_8888_SkColorType);

if (samples > maxSamples)
samples = maxSamples;

GrGLFramebufferInfo fbInfo;
fbInfo.fFBOID = buffer;
fbInfo.fFormat = 0x8058;

_skRenderTarget =
GrBackendRenderTarget(width, height, samples, stencil, fbInfo);

_skSurface = SkSurface::MakeFromBackendRenderTarget(
getThreadDrawingContext()->skContext.get(), _skRenderTarget,
kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, nullptr, nullptr);

if (!_skSurface) {
RNSkLogger::logToConsole(
"JniSkiaDrawView::setupSurface - skSurface could not be created!");
return false;
}

return true;
}
return true;
}
} // namespace RNSkia
13 changes: 1 addition & 12 deletions package/android/cpp/rnskia-android/SkiaOpenGLRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class SkiaOpenGLRenderer {
* @param width Width of surface to render if there is a picture
* @param height Height of surface to render if there is a picture
*/
void run(const std::function<void(SkCanvas *)> &cb, int width, int height);
bool run(const std::function<void(SkCanvas *)> &cb, int width, int height);

/**
* Sets the state to finishing. Next time the renderer will be called it
Expand Down Expand Up @@ -102,15 +102,6 @@ class SkiaOpenGLRenderer {
*/
bool initGLSurface();

/**
* Ensures that we have a valid Skia surface to draw to. The surface will
* be recreated if the width/height change.
* @param width Width of the underlying view
* @param height Height of the underlying view
* @return True if initialization went well
*/
bool ensureSkiaSurface(int width, int height);

/**
* To be able to use static contexts (and avoid reloading the skia context for
* each new view, we track the OpenGL and Skia drawing context per thread.
Expand All @@ -121,8 +112,6 @@ class SkiaOpenGLRenderer {
EGLSurface _glSurface = EGL_NO_SURFACE;

ANativeWindow *_nativeWindow = nullptr;
GrBackendRenderTarget _skRenderTarget;
sk_sp<SkSurface> _skSurface;

int _prevWidth = 0;
int _prevHeight = 0;
Expand Down
5 changes: 3 additions & 2 deletions package/cpp/rnskia/RNSkDomView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,16 @@ bool RNSkDomRenderer::tryRender(

// We render on the main thread
if (_renderLock->try_lock()) {
bool result = false;
// If we have a Dom Node we can render directly on the main thread
if (_root != nullptr) {
canvasProvider->renderToCanvas(std::bind(
result = canvasProvider->renderToCanvas(std::bind(
&RNSkDomRenderer::renderCanvas, this, std::placeholders::_1,
canvasProvider->getScaledWidth(), canvasProvider->getScaledHeight()));
}

_renderLock->unlock();
return true;
return result;
} else {
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions package/cpp/rnskia/RNSkPictureView.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ class RNSkPictureRenderer
: RNSkRenderer(requestRedraw), _platformContext(context) {}

bool tryRender(std::shared_ptr<RNSkCanvasProvider> canvasProvider) override {
performDraw(canvasProvider);
return true;
return performDraw(canvasProvider);
}

void
Expand All @@ -64,7 +63,7 @@ class RNSkPictureRenderer
}

private:
void performDraw(std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
bool performDraw(std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
canvasProvider->renderToCanvas([=](SkCanvas *canvas) {
// Make sure to scale correctly
auto pd = _platformContext->getPixelDensity();
Expand All @@ -78,6 +77,7 @@ class RNSkPictureRenderer

canvas->restore();
});
return true;
}

std::shared_ptr<RNSkPlatformContext> _platformContext;
Expand Down
15 changes: 13 additions & 2 deletions package/cpp/rnskia/RNSkView.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class RNSkCanvasProvider {
/**
Render to a canvas
*/
virtual void renderToCanvas(const std::function<void(SkCanvas *)> &) = 0;
virtual bool renderToCanvas(const std::function<void(SkCanvas *)> &) = 0;

protected:
std::function<void()> _requestRedraw;
Expand Down Expand Up @@ -123,8 +123,9 @@ class RNSkImageCanvasProvider : public RNSkCanvasProvider {
/**
Render to a canvas
*/
void renderToCanvas(const std::function<void(SkCanvas *)> &cb) override {
bool renderToCanvas(const std::function<void(SkCanvas *)> &cb) override {
cb(_surface->getCanvas());
return true;
};

private:
Expand Down Expand Up @@ -224,6 +225,15 @@ class RNSkView : public std::enable_shared_from_this<RNSkView> {
*/
void requestRedraw() { _redrawRequestCounter++; }

/**
Renders immediate. Be carefull to not call this method from another thread
than the UI thread
*/
void renderImmediate() {
_renderer->renderImmediate(_canvasProvider);
_redrawRequestCounter = 0;
}

/**
Sets the native id of the view
*/
Expand Down Expand Up @@ -409,6 +419,7 @@ class RNSkView : public std::enable_shared_from_this<RNSkView> {

size_t _drawingLoopId = 0;
std::atomic<int> _redrawRequestCounter = {1};
bool _initialDrawingDone = false;
};

} // namespace RNSkia
2 changes: 1 addition & 1 deletion package/ios/RNSkia-iOS/RNSkMetalCanvasProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class RNSkMetalCanvasProvider : public RNSkia::RNSkCanvasProvider {
float getScaledWidth() override;
float getScaledHeight() override;

void renderToCanvas(const std::function<void(SkCanvas *)> &cb) override;
bool renderToCanvas(const std::function<void(SkCanvas *)> &cb) override;

void setSize(int width, int height);

Expand Down
12 changes: 7 additions & 5 deletions package/ios/RNSkia-iOS/RNSkMetalCanvasProvider.mm
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@
/**
Render to a canvas
*/
void RNSkMetalCanvasProvider::renderToCanvas(
bool RNSkMetalCanvasProvider::renderToCanvas(
const std::function<void(SkCanvas *)> &cb) {
if (_width <= 0 || _height <= 0) {
return;
return false;
}

// Make sure to NOT render or try any render operations while we're in the
Expand All @@ -82,7 +82,7 @@
_requestRedraw();
// and don't draw now since it might cause errors in the metal renderer if
// we try to render while in the background. (see above issue)
return;
return false;
}
}

Expand Down Expand Up @@ -113,7 +113,7 @@
*/
id<CAMetalDrawable> currentDrawable = [_layer nextDrawable];
if (currentDrawable == nullptr) {
return;
return false;
}

GrMtlTextureInfo fbInfo;
Expand All @@ -129,7 +129,7 @@ GrBackendRenderTarget backendRT(_layer.drawableSize.width,
if (skSurface == nullptr || skSurface->getCanvas() == nullptr) {
RNSkia::RNSkLogger::logToConsole(
"Skia surface could not be created from parameters.");
return;
return false;
}

SkCanvas *canvas = skSurface->getCanvas();
Expand All @@ -142,6 +142,8 @@ GrBackendRenderTarget backendRT(_layer.drawableSize.width,
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
}

return true;
};

void RNSkMetalCanvasProvider::setSize(int width, int height) {
Expand Down
Loading

0 comments on commit 8c80884

Please sign in to comment.