Fix occasional tc failure of glView
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / gl-view / drawable-view-native-renderer.cpp
1 /*
2  * Copyright (c) 2023 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 <EGL/egl.h>
28 #include <EGL/eglext.h>
29 #include <GLES3/gl3.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 // clang-format off
53 constexpr float QUAD_VERTS[] = {
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   1.0f, 1.0f,   // top right
73   1.0f, 0.0f,   // bottom right
74   0.0f, 0.0f,   // bottom left
75   0.0f, 1.0f    // top left
76 };
77 // clang-format on
78 } // namespace
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    * Constructor
253    */
254   explicit Impl(const NativeRendererCreateInfo& renderCreateInfo)
255   : mCreateInfo(renderCreateInfo)
256   {
257   }
258
259   /**
260    * Destructor
261    */
262   ~Impl()
263   {
264     Terminate();
265     if(mThread)
266     {
267       mThread->join();
268     }
269   }
270
271   /**
272    * Initializes renderer thread for offscreen rendering
273    */
274   void InitializeThread()
275   {
276     // Make mRunning true first
277     // Terminate() may be called before StartThread()
278     mRunning = true;
279
280     mThread = std::make_unique<std::thread>(&Impl::StartThread, this);
281   }
282
283   void PushRenderCallbackInputData(const Dali::RenderCallbackInput& renderCallbackInput)
284   {
285     std::scoped_lock<std::mutex> lock(mRenderCallbackInputDataMutex);
286     mRenderCallbackInputData = renderCallbackInput;
287   }
288
289   void PopRenderCallbackInputData(Dali::RenderCallbackInput& renderCallbackInput)
290   {
291     std::scoped_lock<std::mutex> lock(mRenderCallbackInputDataMutex);
292     renderCallbackInput = mRenderCallbackInputData;
293   }
294
295   void Terminate()
296   {
297     mRunning = false;
298   }
299
300   /**
301    * Function initializes thread for parallel rendering.
302    *
303    * The internal loop runs until the private EGL context has been
304    * initialized.
305    */
306   void StartThread()
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       // Invoke callback
388       if(mOnRenderCallback)
389       {
390         CallbackBase::ExecuteReturn<int>(*mOnRenderCallback);
391       }
392
393       // If the framebuffer is guarded with fence object then
394       // delete it as at this point it is no longer valid.
395       if(fb.fence)
396       {
397         // Make sure GPU finished
398         glDeleteSync(fb.fence);
399         fb.fence = nullptr;
400       }
401
402       // Inject sync object into the GL commands stream
403       fb.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
404       EnqueueTextureReadBuffer(index);
405       glBindFramebuffer(GL_FRAMEBUFFER, 0);
406     }
407   }
408
409   void Resize(uint32_t width, uint32_t height)
410   {
411     mWidth         = width;
412     mHeight        = height;
413     mResizeRequest = true;
414   }
415
416   uint32_t         mWidth{0u};
417   uint32_t         mHeight{0u};
418   std::atomic_bool mResizeRequest{false};
419
420   /**
421    * Clones current EGL context, this function must be called from the render callback
422    * and be executed on the DALi RenderThread
423    */
424   void CloneEglContext()
425   {
426     // extract shared context (void*)
427     auto context = eglGetCurrentContext();
428
429     // Obtain configs
430     EGLint configId{0u};
431     EGLint size{0u};
432     eglGetConfigs(mEglDisplay, nullptr, 0, &size);
433     std::vector<EGLConfig> configs;
434     configs.resize(size);
435     eglGetConfigs(mEglDisplay, configs.data(), EGLint(configs.size()), &size);
436
437     // Find out which config is used by current context
438     eglQueryContext(mEglDisplay, context, EGL_CONFIG_ID, &configId);
439
440     // Setup EGL version
441     auto                version = int(30); // TODO: get context version and select the same one
442     std::vector<EGLint> attribs;
443     attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
444     attribs.push_back(version / 10);
445     attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
446     attribs.push_back(version % 10);
447     attribs.push_back(EGL_NONE);
448
449     // Create cloned context with shared context
450     mEglContext = eglCreateContext(mEglDisplay, configs[configId], mEglSharedContext, attribs.data());
451   }
452
453   // Pre-, Post- functions are being called from the callbacks
454   void GlViewPreInit(const Dali::RenderCallbackInput& input)
455   {
456     // This runs on DALi RenderThread!!!
457
458     // Bind the shared context in case of threaded rendering
459     if(mThread && !mEglContextBound)
460     {
461       // Store the shared context just once
462       if(!mEglSharedContext)
463       {
464         // Store the shared context returned by the drawable callback
465         mEglSharedContext = std::any_cast<EGLContext>(input.eglContext);
466       }
467       // Setup the EGL context
468       mEglDisplay = eglGetCurrentDisplay();
469
470       // switch to shared context in order to create shared GL resources
471       auto currentContext = eglGetCurrentContext();
472
473       // Retrieve current surfaces (read and draw)
474       mDrawSurface = eglGetCurrentSurface(EGL_DRAW);
475       mReadSurface = eglGetCurrentSurface(EGL_READ);
476
477       eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglSharedContext);
478
479       [[maybe_unused]] auto eglError = eglGetError();
480
481       // Now clone it to create compatible context for our threaded rendering
482       CloneEglContext();
483
484       // Bring back current context
485       eglMakeCurrent(mEglDisplay, mDrawSurface, mReadSurface, currentContext);
486     }
487   }
488
489   GLuint CreateProgram(const char* vertexSource, const char* fragmentSource)
490   {
491     GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
492     if(!vertexShader)
493     {
494       return 0;
495     }
496     GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
497     if(!fragmentShader)
498     {
499       return 0;
500     }
501     GLuint program = glCreateProgram();
502     if(program)
503     {
504       GL(glAttachShader(program, vertexShader));
505       GL(glAttachShader(program, fragmentShader));
506       GL(glLinkProgram(program));
507       GLint linkStatus = GL_FALSE;
508       GL(glGetProgramiv(program, GL_LINK_STATUS, &linkStatus));
509       if(linkStatus != GL_TRUE)
510       {
511         GLint bufLength = 0;
512         glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
513         if(bufLength)
514         {
515           char* buf = (char*)malloc(bufLength);
516           if(buf)
517           {
518             glGetProgramInfoLog(program, bufLength, NULL, buf);
519             free(buf);
520           }
521         }
522         glDeleteProgram(program);
523         program = 0;
524       }
525     }
526     return program;
527   }
528
529   GLuint LoadShader(GLenum shaderType, const char* shaderSource)
530   {
531     GLuint shader = glCreateShader(shaderType);
532     if(shader != 0)
533     {
534       GL(glShaderSource(shader, 1, &shaderSource, NULL));
535       GL(glCompileShader(shader));
536       GLint compiled = 0;
537       glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
538       if(compiled != GL_TRUE)
539       {
540         GLint infoLen = 0;
541         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
542
543         if(infoLen > 0)
544         {
545           char* logBuffer = (char*)malloc(infoLen);
546
547           if(logBuffer != NULL)
548           {
549             glGetShaderInfoLog(shader, infoLen, NULL, logBuffer);
550
551             DALI_ASSERT_ALWAYS(true && logBuffer);
552
553             free(logBuffer);
554             logBuffer = NULL;
555           }
556
557           glDeleteShader(shader);
558           shader = 0;
559         }
560       }
561     }
562     return shader;
563   }
564
565   void PrepareBlitShader()
566   {
567     static const char glVertexShader[] =
568       "attribute vec2 vertexPosition;\n"
569       "attribute vec2 texCoords;\n"
570       "varying vec2 vTexCoords ;\n"
571       "void main()\n"
572       "{\n"
573       "    gl_Position = vec4(vertexPosition, 0.0, 1.0);\n"
574       "    vTexCoords = texCoords;\n"
575       "}\n";
576
577     static const char glFragmentShader[] =
578       "precision mediump float;\n"
579       "varying vec2 vTexCoords;\n"
580       "uniform sampler2D tex;\n"
581       "void main()\n"
582       "{\n"
583       "    gl_FragColor = texture2D(tex, vTexCoords);\n"
584       "}\n";
585
586     mBlitProgram        = CreateProgram(glVertexShader, glFragmentShader);
587     mBlitVertexLocation = glGetAttribLocation(mBlitProgram, "vertexPosition");
588     mBlitTexCoord       = glGetAttribLocation(mBlitProgram, "texCoords");
589   }
590
591   GLuint mBlitProgram{0u};
592   GLuint mBlitVertexLocation{0u};
593   GLuint mBlitTexCoord{0u};
594
595   /**
596    * Initializes FBO textures
597    */
598   void InitializeOffscreenFramebuffers()
599   {
600     for(auto i = 0u; i < mCreateInfo.maxOffscreenBuffers; ++i)
601     {
602       mFramebufferTexture.emplace_back();
603       mFramebufferTexture.back().textureId = CreateOffscreenTexture(mWidth, mHeight);
604
605       // Populate Draw queue entries
606       mTextureDrawQueue.push_back(i);
607
608       // Create framebuffers
609       CreateFramebuffer(i, mWidth, mHeight);
610     }
611   }
612
613   /**
614    * Creates an offscreen texture for threaded renderer
615    */
616   uint32_t CreateOffscreenTexture(uint32_t width, uint32_t height)
617   {
618     GLuint offscreenTexture{0u};
619     GL(glGenTextures(1, &offscreenTexture));
620     GL(glBindTexture(GL_TEXTURE_2D, offscreenTexture));
621     GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
622     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
623     GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
624     return offscreenTexture;
625   }
626
627   void GlViewInit(const Dali::RenderCallbackInput& input)
628   {
629     if(mOnInitCallback)
630     {
631       GlViewPreInit(input);
632       CallbackBase::Execute(*mOnInitCallback, input);
633     }
634   }
635
636   int GlViewRender(const Dali::RenderCallbackInput& input)
637   {
638     // Non-threaded solution invokes callback directly
639     int retval = 0;
640     if(!mCreateInfo.threadEnabled)
641     {
642       if(mOnRenderCallback)
643       {
644         retval = CallbackBase::ExecuteReturn<int>(*mOnRenderCallback, input);
645       }
646     }
647     else
648     {
649       BlitTexture();
650     }
651     return retval;
652   }
653
654   void GlViewTerminate(const Dali::RenderCallbackInput& input)
655   {
656     // Non-threaded solution invokes callback directly
657     if(!mCreateInfo.threadEnabled)
658     {
659       if(mOnTerminateCallback)
660       {
661         CallbackBase::Execute(*mOnTerminateCallback, input);
662       }
663     }
664   }
665
666   void BlitTexture()
667   {
668     // If no threaded mode, return
669     if(!mCreateInfo.threadEnabled)
670     {
671       return;
672     }
673
674     // Read input
675     auto x = 0;
676     auto y = 0;
677     auto w = mWidth;
678     auto h = mHeight;
679
680     // Deqeueue texture, there should be always something waiting to be drawn, if not, ignore
681     FrameBufferTexture fb;
682     auto               textureBufferIndex = DequeueTextureReadBuffer(fb);
683
684     // Do nothing if frame not ready
685     if(textureBufferIndex < 0)
686     {
687       if(mLastTextureBufferIndex >= 0)
688       {
689         textureBufferIndex = mLastTextureBufferIndex;
690       }
691       else
692       {
693         return;
694       }
695     }
696     else
697     {
698       // return last texture to the pull
699       if(mLastTextureBufferIndex >= 0)
700       {
701         // return it to the queue
702         EnqueueTextureDrawBuffer(mLastTextureBufferIndex);
703       }
704     }
705
706     GL(glViewport(x, y, w, h));
707     if(!mBlitStateDone)
708     {
709       mBlitStateDone = true;
710       GL(glUseProgram(mBlitProgram));
711       GL(glVertexAttribPointer(mBlitVertexLocation, 2, GL_FLOAT, GL_FALSE, 0, QUAD_VERTS));
712       GL(glEnableVertexAttribArray(mBlitVertexLocation));
713       GL(glVertexAttribPointer(mBlitTexCoord, 2, GL_FLOAT, GL_FALSE, 0, QUAD_UV));
714       GL(glEnableVertexAttribArray(mBlitTexCoord));
715       GL(glActiveTexture(GL_TEXTURE0));
716     }
717     GL(glBindTexture(GL_TEXTURE_2D, mFramebufferTexture[textureBufferIndex].textureId));
718
719     GL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, QUAD_INDICES))
720
721     mLastTextureBufferIndex = textureBufferIndex;
722   }
723
724   // List of offscreen framebuffers
725   std::vector<FrameBufferTexture> mFramebufferTexture{};
726
727   /**
728    * Rendering pipeline uses 3 queues:
729    * Draw - the producer queue (NativeRenderer thread writes to it)
730    * Read - the consumer queue (RenderThread reads from it)
731    * Stage - Already written but not ready to be read (not signaled)
732    *
733    * // Rendering offscreen
734    * 1. PRODUCER pops framebuffer from Draw queue
735    * 2. PRODUCER renders into the framebuffer
736    * 3. PRODUCER pushes framebuffer into Stage queue
737    *
738    * // Rendering onscreen
739    * 1. CONSUMER checks Stage queue for signaled (ready) framebuffers
740    * 2. If framebuffers are ready, pushes them into the Read queue
741    * 3. If MAILBOX mode, CONSUMER discards all 'outdated' framebuffers and displays the most recent
742    * 4. If FIFO mode, CONSUMER displays all the images in order of being produced.
743    */
744   std::deque<uint32_t> mTextureDrawQueue;
745   std::deque<uint32_t> mTextureStageQueue;
746   std::deque<uint32_t> mTextureReadQueue;
747
748   // Mutex guarding the queues reads/writes
749   std::recursive_mutex         mTextureQueueMutex;
750   std::unique_ptr<std::thread> mThread;              ///< Thread for parallel mode
751   bool                         mRunning{false};      ///< Thread running flag
752   EGLContext                   mEglContext{nullptr}; ///< EGL context associated with the thread
753   EGLContext                   mEglSharedContext{nullptr};
754
755   EGLDisplay       mEglDisplay{nullptr};         ///< Current EGL display
756   std::atomic_bool mEglContextBound{false};      ///< Flag indicating whether EGL context is bound
757   EGLSurface       mDrawSurface{EGL_NO_SURFACE}; ///< Current EGL draw surface
758   EGLSurface       mReadSurface{EGL_NO_SURFACE}; ///< Current EGL read surface
759
760   // Callbacks associated with GlView interface
761   std::unique_ptr<CallbackBase> mOnInitCallback{nullptr};
762   std::unique_ptr<CallbackBase> mOnRenderCallback{nullptr};
763   std::unique_ptr<CallbackBase> mOnTerminateCallback{nullptr};
764
765   int32_t mLastTextureBufferIndex{-1};
766   bool    mBlitStateDone{false};
767
768   std::mutex                mRenderCallbackInputDataMutex{};
769   Dali::RenderCallbackInput mRenderCallbackInputData{};
770
771   NativeRendererCreateInfo mCreateInfo{};
772 };
773
774 DrawableViewNativeRenderer::DrawableViewNativeRenderer(const NativeRendererCreateInfo& createInfo)
775 : mImpl(new Impl(createInfo))
776 {
777   if(createInfo.threadEnabled)
778   {
779     mImpl->InitializeThread();
780   }
781 }
782
783 DrawableViewNativeRenderer::~DrawableViewNativeRenderer() = default;
784
785 void DrawableViewNativeRenderer::RegisterGlCallbacks(Dali::CallbackBase* onInitCallback, Dali::CallbackBase* onRenderCallback, Dali::CallbackBase* onTerminateCallback)
786 {
787   mImpl->mOnInitCallback.reset(onInitCallback);
788   mImpl->mOnRenderCallback.reset(onRenderCallback);
789   mImpl->mOnTerminateCallback.reset(onTerminateCallback);
790 }
791
792 void DrawableViewNativeRenderer::InvokeGlInitCallback(const RenderCallbackInput& renderCallbackInput)
793 {
794   mImpl->GlViewInit(renderCallbackInput);
795 }
796
797 void DrawableViewNativeRenderer::InvokeGlRenderCallback(const RenderCallbackInput& renderCallbackInput)
798 {
799   mImpl->GlViewRender(renderCallbackInput);
800 }
801
802 void DrawableViewNativeRenderer::InvokeGlTerminateCallback(const RenderCallbackInput& renderCallbackInput)
803 {
804   mImpl->GlViewTerminate(renderCallbackInput);
805 }
806
807 void DrawableViewNativeRenderer::Resize(uint32_t width, uint32_t height)
808 {
809   mImpl->Resize(width, height);
810 }
811
812 void DrawableViewNativeRenderer::PushRenderCallbackInputData(const Dali::RenderCallbackInput& renderCallbackInput)
813 {
814   mImpl->PushRenderCallbackInputData(renderCallbackInput);
815 }
816
817 void DrawableViewNativeRenderer::Terminate()
818 {
819   mImpl->Terminate();
820 }
821
822 } // namespace Dali::Internal