[dali_2.1.30] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / gl-view / drawable-view-native-renderer.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include "drawable-view-native-renderer.h"
19 #include <dali/public-api/signals/render-callback.h>
20 #include <atomic>
21 #include <deque>
22 #include <mutex>
23 #include <thread>
24 #include <vector>
25
26 // GLES3+ is required for this to work!
27 #include <GLES3/gl3.h>
28 #include <EGL/egl.h>
29 #include <EGL/eglext.h>
30
31 #include <unistd.h>
32 #include <any>
33
34 #define GL(x)                                                   \
35   {                                                             \
36     glGetError();                                               \
37     {                                                           \
38       x;                                                        \
39     };                                                          \
40     auto err = glGetError();                                    \
41     if(err)                                                     \
42     {                                                           \
43       printf("%p:%d: ERROR: 0x%X\n", this, __LINE__, int(err)); \
44     }                                                           \
45   }
46
47 namespace
48 {
49 /**
50  * Vertices of quad to display when using offscreen rendering
51  */
52 constexpr float QUAD_VERTS[] = {
53   // positions          // colors           // texture coords
54   1.0f,  1.0f,
55   1.0f, -1.0f,
56   -1.0f, -1.0f,
57   -1.0f,  1.0f,
58 };
59
60 /**
61  * Indices of quad for offscreen rendering
62  */
63 constexpr unsigned short QUAD_INDICES[] = {
64   0, 1, 2,
65   3, 0, 2
66 };
67
68 /**
69  * UV coords of quad for offscreen rendering
70  */
71 constexpr float QUAD_UV[] = {
72   // positions          // colors           // texture coords
73   1.0f, 1.0f,   // top right
74   1.0f, 0.0f,   // bottom right
75   0.0f, 0.0f,   // bottom left
76   0.0f, 1.0f    // top left
77 };
78 }
79
80 namespace Dali::Internal
81 {
82 struct DrawableViewNativeRenderer::Impl
83 {
84   /**
85    * This structure associates framebuffer with texture and fence object
86    */
87   struct FrameBufferTexture
88   {
89     uint32_t textureId{0u};
90     uint32_t framebufferId{0u};
91     GLsync   fence{nullptr};
92   };
93
94   // Queues management
95   bool DequeueTextureDrawBuffer( uint32_t& outIndex )
96   {
97     std::scoped_lock<std::recursive_mutex> lock(mTextureQueueMutex);
98     if(mTextureDrawQueue.empty())
99     {
100       // TODO: probably add textures if necessary
101       return false;
102     }
103
104     auto retval = mTextureDrawQueue.front();
105     mTextureDrawQueue.pop_front();
106     outIndex = retval;
107     return true;
108   }
109
110   /**
111    * Enqueues framebuffer for the Read queue to be used by
112    * the CONSUMER.
113    */
114   void EnqueueTextureReadBuffer(uint32_t fbId)
115   {
116     // push ready texture to front of 'read' queue
117     std::scoped_lock<std::recursive_mutex> lock(mTextureQueueMutex);
118
119     auto& fb = mFramebufferTexture[fbId];
120
121     // Check state of fence whether the texture can be passed to the CONSUMER
122     if(fb.fence)
123     {
124       auto checkFenceState = glClientWaitSync(fb.fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
125       if(checkFenceState == GL_ALREADY_SIGNALED || checkFenceState == GL_CONDITION_SATISFIED)
126       {
127         // Ready so push directly to Read queue
128         mTextureReadQueue.push_back(fbId);
129       }
130       else
131       {
132         // Still busy so push to Stage queue
133         mTextureStageQueue.push_back(fbId);
134       }
135     }
136   }
137
138   void EnqueueTextureDrawBuffer(uint32_t fbId)
139   {
140     // push ready texture to front of 'read' queue
141     std::scoped_lock<std::recursive_mutex> lock(mTextureQueueMutex);
142     mTextureDrawQueue.push_back(fbId);
143   }
144
145   int32_t DequeueTextureReadBuffer(FrameBufferTexture& framebufferTexture)
146   {
147     // executed by DALi RenderThread!
148     std::deque<uint32_t>                   backTextures;
149     std::scoped_lock<std::recursive_mutex> lock(mTextureQueueMutex);
150
151     if(mTextureReadQueue.empty())
152     {
153       EnqueueStagedTexture();
154     }
155     else
156     {
157       while(!mTextureStageQueue.empty())
158       {
159         // we have something to render, so discard
160         auto stagedId = mTextureStageQueue.back();
161         EnqueueTextureDrawBuffer(stagedId);
162         mTextureStageQueue.pop_back();
163       }
164     }
165
166     if(mTextureReadQueue.empty())
167     {
168       return -1;
169     }
170
171     auto retval = mTextureReadQueue.back();
172     mTextureReadQueue.pop_back();
173
174     // drain all back images and return them to the 'draw' queue
175     // and remove old images
176     while(!mTextureReadQueue.empty())
177     {
178       auto texId = mTextureReadQueue.back();
179       if(framebufferTexture.fence)
180       {
181         glDeleteSync(framebufferTexture.fence);
182         framebufferTexture.fence = nullptr;
183       }
184       mTextureDrawQueue.push_front(texId);
185       mTextureReadQueue.pop_back();
186     }
187
188     return int32_t(retval);
189   }
190
191   /**
192    * Enqueues previously staged texture
193    */
194   uint32_t EnqueueStagedTexture()
195   {
196     // test stage queue
197     std::deque<uint32_t> stagedQueue;
198     bool                 found  = false;
199     uint32_t             retval = 0;
200     while(!mTextureStageQueue.empty())
201     {
202       auto  stagedId = mTextureStageQueue.front();
203       auto& fb       = mFramebufferTexture[stagedId];
204       if(!found)
205       {
206         auto syncResult = glClientWaitSync(fb.fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
207         if(syncResult == GL_CONDITION_SATISFIED || syncResult == GL_ALREADY_SIGNALED)
208         {
209           // push texture into the queue
210           mTextureReadQueue.push_back(stagedId);
211           retval = stagedId;
212           found  = true;
213         }
214         else
215         {
216           stagedQueue.push_back(stagedId);
217         }
218       }
219       else
220       {
221         stagedQueue.push_back(stagedId);
222       }
223       mTextureStageQueue.pop_front();
224     }
225     mTextureStageQueue = std::move(stagedQueue);
226     return retval;
227   }
228
229   uint32_t CreateFramebuffer(uint32_t index, uint32_t width, uint32_t height)
230   {
231     auto& fb = mFramebufferTexture[index];
232     if(!fb.framebufferId)
233     {
234       GLuint offscreenFramebuffer, renderBuffer;
235       GL(glGenFramebuffers(1, &offscreenFramebuffer))
236       GL(glBindFramebuffer(GL_FRAMEBUFFER, offscreenFramebuffer));
237       GL(glGenRenderbuffers(1, &renderBuffer));
238       GL(glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer));
239       GL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.textureId, 0));
240       GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height))
241       GL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer));
242       fb.framebufferId = offscreenFramebuffer;
243
244       [[maybe_unused]] auto result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
245       DALI_ASSERT_ALWAYS( result == GL_FRAMEBUFFER_COMPLETE && "Framebuffer incomplete!");
246       return offscreenFramebuffer;
247     }
248     return 0u;
249   }
250
251
252   /**
253    * Constructor
254    */
255   explicit Impl(const NativeRendererCreateInfo& renderCreateInfo)
256   : mCreateInfo(renderCreateInfo)
257   {
258   }
259
260   /**
261    * Destructor
262    */
263   ~Impl()
264   {
265     Terminate();
266     if(mThread)
267     {
268       mThread->join();
269     }
270   }
271
272   /**
273    * Initializes renderer thread for offscreen rendering
274    */
275   void InitializeThread()
276   {
277     mThread = std::make_unique<std::thread>(&Impl::StartThread, this);
278   }
279
280   void PushRenderCallbackInputData( const Dali::RenderCallbackInput& renderCallbackInput )
281   {
282
283     std::scoped_lock<std::mutex> lock(mRenderCallbackInputDataMutex);
284     mRenderCallbackInputData = renderCallbackInput;
285   }
286
287   void PopRenderCallbackInputData( Dali::RenderCallbackInput& renderCallbackInput )
288   {
289     std::scoped_lock<std::mutex> lock(mRenderCallbackInputDataMutex);
290     renderCallbackInput = mRenderCallbackInputData;
291   }
292
293   void Terminate()
294   {
295     mRunning = false;
296   }
297
298   /**
299    * Function initializes thread for parallel rendering.
300    *
301    * The internal loop runs until the private EGL context has been
302    * initialized.
303    */
304   void StartThread()
305   {
306     mRunning = true;
307
308     // We need to acquire shared context, while this is not done
309     // it's necessary to wait for context to be bound.
310     while(mRunning && !mEglContextBound)
311     {
312       // Wait for context to be given
313       if(!mEglContext)
314       {
315         continue;
316       }
317       if(!eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglContext))
318       {
319         [[maybe_unused]] auto err = eglGetError();
320         printf("%d\n", int(err));
321       }
322       mEglContextBound = true;
323     }
324
325     InitializeOffscreenFramebuffers();
326
327     PrepareBlitShader();
328
329     ThreadRunRender();
330   }
331
332   /**
333    * Reinitializes offscreen framebuffers and textures in case
334    * the resize has been requested.
335    */
336   void ReinitializeFramebuffers()
337   {
338     int index = 0;
339     for(auto& fb : mFramebufferTexture)
340     {
341       if(fb.fence)
342       {
343         GL(glDeleteSync(fb.fence));
344       }
345       if(fb.framebufferId)
346       {
347         GL(glDeleteFramebuffers(1, &fb.framebufferId))
348         fb.framebufferId = 0u;
349       }
350       if(fb.textureId)
351       {
352         GL(glDeleteTextures(1, &fb.textureId))
353         fb.textureId = 0u;
354       }
355       fb.textureId = CreateOffscreenTexture( mWidth, mHeight );
356       fb.framebufferId = CreateFramebuffer( index, mWidth, mHeight );
357       index++;
358     }
359   }
360
361   void ThreadRunRender()
362   {
363     while(mRunning)
364     {
365       // If there is a resize request waiting, then recreate all framebuffers
366       if(mResizeRequest)
367       {
368         ReinitializeFramebuffers();
369         mResizeRequest = false;
370       }
371
372       Dali::RenderCallbackInput input;
373
374       PopRenderCallbackInputData( input );
375
376       uint32_t index{0u};
377       auto result = DequeueTextureDrawBuffer(index);
378       if(!result)
379       {
380         continue;
381       }
382
383       auto& fb = mFramebufferTexture[index];
384       GL(glBindFramebuffer(GL_FRAMEBUFFER, fb.framebufferId))
385       GL(glClear(0));
386
387
388       // Invoke callback
389       if(mOnRenderCallback)
390       {
391         CallbackBase::ExecuteReturn<int>(*mOnRenderCallback);
392       }
393
394       // If the framebuffer is guarded with fence object then
395       // delete it as at this point it is no longer valid.
396       if(fb.fence)
397       {
398         // Make sure GPU finished
399         glDeleteSync(fb.fence);
400         fb.fence = nullptr;
401       }
402
403       // Inject sync object into the GL commands stream
404       fb.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
405       EnqueueTextureReadBuffer(index);
406       glBindFramebuffer(GL_FRAMEBUFFER, 0);
407     }
408   }
409
410   void Resize(uint32_t width, uint32_t height )
411   {
412     mWidth = width;
413     mHeight = height;
414     mResizeRequest = true;
415   }
416
417   uint32_t mWidth {0u};
418   uint32_t mHeight {0u};
419   std::atomic_bool mResizeRequest { false };
420
421   /**
422    * Clones current EGL context, this function must be called from the render callback
423    * and be executed on the DALi RenderThread
424    */
425   void CloneEglContext()
426   {
427     // extract shared context (void*)
428     auto context = eglGetCurrentContext();
429
430     // Obtain configs
431     EGLint configId{0u};
432     EGLint size{0u};
433     eglGetConfigs(mEglDisplay, nullptr, 0, &size);
434     std::vector<EGLConfig> configs;
435     configs.resize(size);
436     eglGetConfigs(mEglDisplay, configs.data(), EGLint(configs.size()), &size);
437
438     // Find out which config is used by current context
439     eglQueryContext(mEglDisplay, context, EGL_CONFIG_ID, &configId);
440
441     // Setup EGL version
442     auto                version = int(30); // TODO: get context version and select the same one
443     std::vector<EGLint> attribs;
444     attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
445     attribs.push_back(version / 10);
446     attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
447     attribs.push_back(version % 10);
448     attribs.push_back(EGL_NONE);
449
450     // Create cloned context with shared context
451     mEglContext = eglCreateContext(mEglDisplay, configs[configId], mEglSharedContext, attribs.data());
452   }
453
454   // Pre-, Post- functions are being called from the callbacks
455   void GlViewPreInit( const Dali::RenderCallbackInput& input )
456   {
457     // This runs on DALi RenderThread!!!
458
459     // Bind the shared context in case of threaded rendering
460     if(mThread && !mEglContextBound)
461     {
462       // Store the shared context just once
463       if(!mEglSharedContext )
464       {
465         // Store the shared context returned by the drawable callback
466         mEglSharedContext = std::any_cast<EGLContext>(input.eglContext);
467       }
468       // Setup the EGL context
469       mEglDisplay = eglGetCurrentDisplay();
470
471       // switch to shared context in order to create shared GL resources
472       auto currentContext = eglGetCurrentContext();
473
474       // Retrieve current surfaces (read and draw)
475       mDrawSurface = eglGetCurrentSurface(EGL_DRAW);
476       mReadSurface = eglGetCurrentSurface(EGL_READ);
477
478       eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglSharedContext);
479
480       [[maybe_unused]] auto eglError = eglGetError();
481
482       // Now clone it to create compatible context for our threaded rendering
483       CloneEglContext();
484
485       // Bring back current context
486       eglMakeCurrent(mEglDisplay, mDrawSurface, mReadSurface, currentContext);
487     }
488   }
489
490   GLuint CreateProgram(const char* vertexSource, const char* fragmentSource)
491   {
492     GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
493     if(!vertexShader)
494     {
495       return 0;
496     }
497     GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
498     if(!fragmentShader)
499     {
500       return 0;
501     }
502     GLuint program = glCreateProgram();
503     if(program)
504     {
505       GL(glAttachShader(program, vertexShader));
506       GL(glAttachShader(program, fragmentShader));
507       GL(glLinkProgram(program));
508       GLint linkStatus = GL_FALSE;
509       GL(glGetProgramiv(program, GL_LINK_STATUS, &linkStatus));
510       if(linkStatus != GL_TRUE)
511       {
512         GLint bufLength = 0;
513         glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
514         if(bufLength)
515         {
516           char* buf = (char*)malloc(bufLength);
517           if(buf)
518           {
519             glGetProgramInfoLog(program, bufLength, NULL, buf);
520             free(buf);
521           }
522         }
523         glDeleteProgram(program);
524         program = 0;
525       }
526     }
527     return program;
528   }
529
530   GLuint LoadShader(GLenum shaderType, const char* shaderSource)
531   {
532     GLuint shader = glCreateShader(shaderType);
533     if(shader != 0)
534     {
535       GL(glShaderSource(shader, 1, &shaderSource, NULL));
536       GL(glCompileShader(shader));
537       GLint compiled = 0;
538       glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
539       if(compiled != GL_TRUE)
540       {
541         GLint infoLen = 0;
542         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
543
544         if(infoLen > 0)
545         {
546           char* logBuffer = (char*)malloc(infoLen);
547
548           if(logBuffer != NULL)
549           {
550             glGetShaderInfoLog(shader, infoLen, NULL, logBuffer);
551
552             DALI_ASSERT_ALWAYS( true && logBuffer);
553
554             free(logBuffer);
555             logBuffer = NULL;
556           }
557
558           glDeleteShader(shader);
559           shader = 0;
560         }
561       }
562     }
563     return shader;
564   }
565
566   void PrepareBlitShader()
567   {
568     static const char glVertexShader[] =
569       "attribute vec2 vertexPosition;\n"
570       "attribute vec2 texCoords;\n"
571       "varying vec2 vTexCoords ;\n"
572       "void main()\n"
573       "{\n"
574       "    gl_Position = vec4(vertexPosition, 0.0, 1.0);\n"
575       "    vTexCoords = texCoords;\n"
576       "}\n";
577
578     static const char glFragmentShader[] =
579       "precision mediump float;\n"
580       "varying vec2 vTexCoords;\n"
581       "uniform sampler2D tex;\n"
582       "void main()\n"
583       "{\n"
584       "    gl_FragColor = texture2D(tex, vTexCoords);\n"
585       "}\n";
586
587     mBlitProgram = CreateProgram(glVertexShader, glFragmentShader);
588     mBlitVertexLocation       = glGetAttribLocation(mBlitProgram, "vertexPosition");
589     mBlitTexCoord = glGetAttribLocation(mBlitProgram, "texCoords");
590   }
591
592   GLuint mBlitProgram{0u};
593   GLuint mBlitVertexLocation{0u};
594   GLuint mBlitTexCoord{0u};
595
596   /**
597    * Initializes FBO textures
598    */
599   void InitializeOffscreenFramebuffers()
600   {
601     for(auto i = 0u; i < mCreateInfo.maxOffscreenBuffers; ++i)
602     {
603       mFramebufferTexture.emplace_back();
604       mFramebufferTexture.back().textureId = CreateOffscreenTexture(mWidth, mHeight);
605
606       // Populate Draw queue entries
607       mTextureDrawQueue.push_back(i);
608
609       // Create framebuffers
610       CreateFramebuffer( i, mWidth, mHeight);
611     }
612   }
613
614   /**
615    * Creates an offscreen texture for threaded renderer
616    */
617   uint32_t CreateOffscreenTexture(uint32_t width, uint32_t height)
618   {
619     GLuint offscreenTexture{0u};
620     GL(glGenTextures(1, &offscreenTexture));
621     GL(glBindTexture(GL_TEXTURE_2D, offscreenTexture));
622     GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
623     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
624     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
625     return offscreenTexture;
626   }
627
628   void GlViewInit(const Dali::RenderCallbackInput& input)
629   {
630     if(mOnInitCallback)
631     {
632       GlViewPreInit(input);
633       CallbackBase::Execute(*mOnInitCallback, input);
634     }
635   }
636
637   int GlViewRender(const Dali::RenderCallbackInput& input)
638   {
639     // Non-threaded solution invokes callback directly
640     int retval = 0;
641     if(!mCreateInfo.threadEnabled)
642     {
643       if(mOnRenderCallback)
644       {
645         retval = CallbackBase::ExecuteReturn<int>(*mOnRenderCallback, input);
646       }
647     }
648     else
649     {
650       BlitTexture();
651     }
652     return retval;
653   }
654
655   void GlViewTerminate(const Dali::RenderCallbackInput& input)
656   {
657     // Non-threaded solution invokes callback directly
658     if(!mCreateInfo.threadEnabled)
659     {
660       if(mOnTerminateCallback)
661       {
662         CallbackBase::Execute(*mOnTerminateCallback, input);
663       }
664     }
665   }
666
667   void BlitTexture()
668   {
669     // If no threaded mode, return
670     if(!mCreateInfo.threadEnabled)
671     {
672       return;
673     }
674
675     // Read input
676     auto x = 0;
677     auto y = 0;
678     auto w = mWidth;
679     auto h = mHeight;
680
681     // Deqeueue texture, there should be always something waiting to be drawn, if not, ignore
682     FrameBufferTexture fb;
683     auto textureBufferIndex = DequeueTextureReadBuffer(fb);
684
685     // Do nothing if frame not ready
686     if(textureBufferIndex < 0)
687     {
688       if(mLastTextureBufferIndex >= 0)
689       {
690         textureBufferIndex = mLastTextureBufferIndex;
691       }
692       else
693       {
694         return;
695       }
696     }
697     else
698     {
699       // return last texture to the pull
700       if(mLastTextureBufferIndex >= 0)
701       {
702         // return it to the queue
703         EnqueueTextureDrawBuffer( mLastTextureBufferIndex );
704       }
705     }
706
707     GL(glViewport(x, y, w, h));
708     if(!mBlitStateDone)
709     {
710       mBlitStateDone = true;
711       GL(glUseProgram(mBlitProgram));
712       GL(glVertexAttribPointer(mBlitVertexLocation, 2, GL_FLOAT, GL_FALSE, 0, QUAD_VERTS));
713       GL(glEnableVertexAttribArray(mBlitVertexLocation));
714       GL(glVertexAttribPointer(mBlitTexCoord, 2, GL_FLOAT, GL_FALSE, 0, QUAD_UV));
715       GL(glEnableVertexAttribArray(mBlitTexCoord));
716       GL(glActiveTexture(GL_TEXTURE0));
717     }
718     GL(glBindTexture(GL_TEXTURE_2D, mFramebufferTexture[textureBufferIndex].textureId));
719
720     GL(glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, QUAD_INDICES))
721
722     mLastTextureBufferIndex = textureBufferIndex;
723   }
724
725   // List of offscreen framebuffers
726   std::vector<FrameBufferTexture> mFramebufferTexture{};
727
728   /**
729    * Rendering pipeline uses 3 queues:
730    * Draw - the producer queue (NativeRenderer thread writes to it)
731    * Read - the consumer queue (RenderThread reads from it)
732    * Stage - Already written but not ready to be read (not signaled)
733    *
734    * // Rendering offscreen
735    * 1. PRODUCER pops framebuffer from Draw queue
736    * 2. PRODUCER renders into the framebuffer
737    * 3. PRODUCER pushes framebuffer into Stage queue
738    *
739    * // Rendering onscreen
740    * 1. CONSUMER checks Stage queue for signaled (ready) framebuffers
741    * 2. If framebuffers are ready, pushes them into the Read queue
742    * 3. If MAILBOX mode, CONSUMER discards all 'outdated' framebuffers and displays the most recent
743    * 4. If FIFO mode, CONSUMER displays all the images in order of being produced.
744    */
745   std::deque<uint32_t> mTextureDrawQueue;
746   std::deque<uint32_t> mTextureStageQueue;
747   std::deque<uint32_t> mTextureReadQueue;
748
749   // Mutex guarding the queues reads/writes
750   std::recursive_mutex mTextureQueueMutex;
751   std::unique_ptr<std::thread> mThread; ///< Thread for parallel mode
752   bool                         mRunning{false}; ///< Thread running flag
753   EGLContext                   mEglContext{nullptr}; ///< EGL context associated with the thread
754   EGLContext                   mEglSharedContext{nullptr};
755
756   EGLDisplay       mEglDisplay{nullptr}; ///< Current EGL display
757   std::atomic_bool mEglContextBound{false}; ///< Flag indicating whether EGL context is bound
758   EGLSurface       mDrawSurface{EGL_NO_SURFACE}; ///< Current EGL draw surface
759   EGLSurface       mReadSurface{EGL_NO_SURFACE}; ///< Current EGL read surface
760
761   // Callbacks associated with GlView interface
762   std::unique_ptr<CallbackBase> mOnInitCallback{nullptr};
763   std::unique_ptr<CallbackBase> mOnRenderCallback{nullptr};
764   std::unique_ptr<CallbackBase> mOnTerminateCallback{nullptr};
765
766   int32_t mLastTextureBufferIndex{-1};
767   bool mBlitStateDone{false};
768
769   std::mutex mRenderCallbackInputDataMutex{};
770   Dali::RenderCallbackInput mRenderCallbackInputData{};
771
772   NativeRendererCreateInfo mCreateInfo{};
773 };
774
775 DrawableViewNativeRenderer::DrawableViewNativeRenderer(const NativeRendererCreateInfo& createInfo)
776 : mImpl(new Impl(createInfo))
777 {
778   if(createInfo.threadEnabled)
779   {
780     mImpl->InitializeThread();
781   }
782 }
783
784 DrawableViewNativeRenderer::~DrawableViewNativeRenderer() = default;
785
786 void DrawableViewNativeRenderer::RegisterGlCallbacks(Dali::CallbackBase* onInitCallback, Dali::CallbackBase* onRenderCallback, Dali::CallbackBase* onTerminateCallback)
787 {
788   mImpl->mOnInitCallback.reset(onInitCallback);
789   mImpl->mOnRenderCallback.reset(onRenderCallback);
790   mImpl->mOnTerminateCallback.reset(onTerminateCallback);
791 }
792
793 void DrawableViewNativeRenderer::InvokeGlInitCallback(const RenderCallbackInput& renderCallbackInput)
794 {
795   mImpl->GlViewInit(renderCallbackInput);
796 }
797
798 void DrawableViewNativeRenderer::InvokeGlRenderCallback(const RenderCallbackInput& renderCallbackInput)
799 {
800   mImpl->GlViewRender(renderCallbackInput);
801 }
802
803 void DrawableViewNativeRenderer::InvokeGlTerminateCallback(const RenderCallbackInput& renderCallbackInput)
804 {
805   mImpl->GlViewTerminate(renderCallbackInput);
806 }
807
808 void DrawableViewNativeRenderer::Resize(uint32_t width, uint32_t height)
809 {
810   mImpl->Resize(width, height);
811 }
812
813 void DrawableViewNativeRenderer::PushRenderCallbackInputData( const Dali::RenderCallbackInput& renderCallbackInput )
814 {
815   mImpl->PushRenderCallbackInputData(renderCallbackInput);
816 }
817
818 void DrawableViewNativeRenderer::Terminate()
819 {
820   mImpl->Terminate();
821 }
822
823 } // namespace Dali::Internal