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