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