1 #include <utility>
2
3 #include "RNSkJsView.h"
4
5 namespace RNSkia {
6
RNSkJsRenderer(std::function<void ()> requestRedraw,std::shared_ptr<RNSkPlatformContext> context)7 RNSkJsRenderer::RNSkJsRenderer(std::function<void()> requestRedraw,
8 std::shared_ptr<RNSkPlatformContext> context)
9 : RNSkRenderer(requestRedraw),
10 _jsiCanvas(std::make_shared<JsiSkCanvas>(context)),
11 _platformContext(context),
12 _infoObject(std::make_shared<RNSkInfoObject>()),
13 _jsDrawingLock(std::make_shared<std::timed_mutex>()),
14 _gpuDrawingLock(std::make_shared<std::timed_mutex>()),
15 _jsTimingInfo("SKIA/JS"), _gpuTimingInfo("SKIA/GPU") {}
16
tryRender(std::shared_ptr<RNSkCanvasProvider> canvasProvider)17 bool RNSkJsRenderer::tryRender(
18 std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
19 // We render on the javascript thread.
20 if (_jsDrawingLock->try_lock()) {
21 _platformContext->runOnJavascriptThread(
22 [weakSelf = weak_from_this(), canvasProvider]() {
23 auto self = weakSelf.lock();
24 if (self) {
25 self->performDraw(canvasProvider);
26 }
27 });
28 return true;
29 } else {
30 #ifdef DEBUG
31 _jsTimingInfo.markSkipped();
32 #endif
33 return false;
34 }
35 }
36
renderImmediate(std::shared_ptr<RNSkCanvasProvider> canvasProvider)37 void RNSkJsRenderer::renderImmediate(
38 std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
39 std::chrono::milliseconds ms =
40 std::chrono::duration_cast<std::chrono::milliseconds>(
41 std::chrono::system_clock::now().time_since_epoch());
42 canvasProvider->renderToCanvas([&](SkCanvas *canvas) {
43 // Create jsi canvas
44 auto jsiCanvas = std::make_shared<JsiSkCanvas>(_platformContext);
45 jsiCanvas->setCanvas(canvas);
46
47 drawInJsiCanvas(std::move(jsiCanvas), canvasProvider->getScaledWidth(),
48 canvasProvider->getScaledHeight(), ms.count() / 1000);
49 });
50 }
51
setDrawCallback(std::shared_ptr<jsi::Function> drawCallback)52 void RNSkJsRenderer::setDrawCallback(
53 std::shared_ptr<jsi::Function> drawCallback) {
54 _drawCallback = drawCallback;
55 }
56
getInfoObject()57 std::shared_ptr<RNSkInfoObject> RNSkJsRenderer::getInfoObject() {
58 return _infoObject;
59 }
60
performDraw(std::shared_ptr<RNSkCanvasProvider> canvasProvider)61 void RNSkJsRenderer::performDraw(
62 std::shared_ptr<RNSkCanvasProvider> canvasProvider) {
63 // Start timing
64 _jsTimingInfo.beginTiming();
65
66 // Record the drawing operations on the JS thread so that we can
67 // move the actual drawing onto the render thread later
68 SkPictureRecorder recorder;
69 SkRTreeFactory factory;
70 SkCanvas *canvas =
71 recorder.beginRecording(canvasProvider->getScaledWidth(),
72 canvasProvider->getScaledHeight(), &factory);
73
74 _jsiCanvas->setCanvas(canvas);
75
76 // Get current milliseconds
77 std::chrono::milliseconds ms =
78 std::chrono::duration_cast<std::chrono::milliseconds>(
79 std::chrono::system_clock::now().time_since_epoch());
80
81 try {
82 // Perform the javascript drawing
83 drawInJsiCanvas(_jsiCanvas, canvasProvider->getScaledWidth(),
84 canvasProvider->getScaledHeight(), ms.count() / 1000.0);
85
86 } catch (...) {
87 _jsTimingInfo.stopTiming();
88 _jsDrawingLock->unlock();
89 throw;
90 }
91
92 // Finish drawing operations
93 auto p = recorder.finishRecordingAsPicture();
94
95 _jsiCanvas->setCanvas(nullptr);
96
97 // Calculate duration
98 _jsTimingInfo.stopTiming();
99
100 if (_gpuDrawingLock->try_lock()) {
101
102 // Post drawing message to the render thread where the picture recorded
103 // will be sent to the GPU/backend for rendering to screen.
104 auto gpuLock = _gpuDrawingLock;
105 _platformContext->runOnRenderThread([weakSelf = weak_from_this(),
106 p = std::move(p), gpuLock,
107 canvasProvider]() {
108 auto self = weakSelf.lock();
109 if (self) {
110 // Draw the picture recorded on the real GPU canvas
111 self->_gpuTimingInfo.beginTiming();
112
113 canvasProvider->renderToCanvas(
114 [p = std::move(p)](SkCanvas *canvas) { canvas->drawPicture(p); });
115
116 self->_gpuTimingInfo.stopTiming();
117 }
118 // Unlock GPU drawing
119 gpuLock->unlock();
120 });
121 } else {
122 #ifdef DEBUG
123 _gpuTimingInfo.markSkipped();
124 #endif
125 // Request a new redraw since the last frame was skipped.
126 _requestRedraw();
127 }
128
129 // Unlock JS drawing
130 _jsDrawingLock->unlock();
131 }
132
callJsDrawCallback(std::shared_ptr<JsiSkCanvas> jsiCanvas,int width,int height,double timestamp)133 void RNSkJsRenderer::callJsDrawCallback(std::shared_ptr<JsiSkCanvas> jsiCanvas,
134 int width, int height,
135 double timestamp) {
136
137 if (_drawCallback == nullptr) {
138 return;
139 }
140
141 // Reset timing info
142 _jsTimingInfo.reset();
143 _gpuTimingInfo.reset();
144
145 auto runtime = _platformContext->getJsRuntime();
146
147 // Update info parameter
148 _infoObject->beginDrawOperation(width, height, timestamp);
149
150 // Set up arguments array
151 std::vector<jsi::Value> args(2);
152 args[0] = jsi::Object::createFromHostObject(*runtime, jsiCanvas);
153 args[1] = jsi::Object::createFromHostObject(*runtime, _infoObject);
154
155 // To be able to call the drawing function we'll wrap it once again
156 _drawCallback->call(*runtime, static_cast<const jsi::Value *>(args.data()),
157 static_cast<size_t>(2));
158
159 // Reset touches
160 _infoObject->endDrawOperation();
161
162 // Draw debug overlays
163 if (getShowDebugOverlays()) {
164
165 // Display average rendering timer
166 auto jsAvg = _jsTimingInfo.getAverage();
167 // auto jsFps = _jsTimingInfo.getFps();
168
169 auto gpuAvg = _gpuTimingInfo.getAverage();
170 // auto gpuFps = _gpuTimingInfo.getFps();
171
172 auto total = jsAvg + gpuAvg;
173
174 // Build string
175 std::ostringstream stream;
176 stream << "js: " << jsAvg << "ms gpu: " << gpuAvg << "ms "
177 << " total: " << total << "ms";
178
179 std::string debugString = stream.str();
180
181 // Set up debug font/paints
182 auto font = SkFont();
183 font.setSize(14);
184 auto paint = SkPaint();
185 paint.setColor(SkColors::kRed);
186 jsiCanvas->getCanvas()->drawSimpleText(
187 debugString.c_str(), debugString.size(), SkTextEncoding::kUTF8, 8, 18,
188 font, paint);
189 }
190 }
191
drawInJsiCanvas(std::shared_ptr<JsiSkCanvas> jsiCanvas,int width,int height,double time)192 void RNSkJsRenderer::drawInJsiCanvas(std::shared_ptr<JsiSkCanvas> jsiCanvas,
193 int width, int height, double time) {
194
195 // Call the draw drawCallback and perform js based drawing
196 auto skCanvas = jsiCanvas->getCanvas();
197 if (_drawCallback != nullptr && skCanvas != nullptr) {
198 // Make sure to scale correctly
199 auto pd = _platformContext->getPixelDensity();
200 skCanvas->clear(SK_ColorTRANSPARENT);
201 skCanvas->save();
202 skCanvas->scale(pd, pd);
203
204 // Call draw function.
205 callJsDrawCallback(jsiCanvas, width / pd, height / pd, time);
206
207 skCanvas->restore();
208 }
209 }
210
211 } // namespace RNSkia
212