Fix attribute cache bug
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-context.cpp
index 31e0713..02d32ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include "gles-context.h"
 #include <dali/integration-api/adaptor-framework/render-surface-interface.h>
+#include <dali/integration-api/debug.h>
 #include <dali/integration-api/gl-abstraction.h>
 #include <dali/integration-api/gl-defines.h>
 #include <dali/internal/graphics/common/graphics-interface.h>
+#include <dali/public-api/math/math-utils.h>
 
 #include "egl-graphics-controller.h"
 #include "gles-graphics-buffer.h"
 #include "gles-graphics-program.h"
 #include "gles-graphics-render-pass.h"
 #include "gles-graphics-render-target.h"
+#include "gles-texture-dependency-checker.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <map>
 
 namespace Dali::Graphics::GLES
 {
 struct Context::Impl
 {
-  Impl(EglGraphicsController& controller)
+  explicit Impl(EglGraphicsController& controller)
   : mController(controller)
   {
   }
@@ -40,6 +47,49 @@ struct Context::Impl
   ~Impl() = default;
 
   /**
+   * Binds (and creates) VAO
+   *
+   * VAO is fixed per program so it has to be created only once assuming
+   * that VertexInputState has been set correctly for the pipeline.
+   *
+   */
+  void BindProgramVAO(GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
+  {
+    auto& gl   = *mController.GetGL();
+    auto  iter = mProgramVAOMap.find(program);
+    if(iter != mProgramVAOMap.end())
+    {
+      if(mProgramVAOCurrentState != iter->second)
+      {
+        mProgramVAOCurrentState = iter->second;
+        gl.BindVertexArray(iter->second);
+      }
+
+      // We should re-check enable attribute usage because geometry might be changed.
+      // @todo : We can remove this loop if we enable vertex attrib by shader's information.
+      for(const auto& attr : vertexInputState.attributes)
+      {
+        gl.EnableVertexAttribArray(attr.location);
+      }
+      return;
+    }
+
+    uint32_t vao;
+    gl.GenVertexArrays(1, &vao);
+    gl.BindVertexArray(vao);
+    mProgramVAOMap[program] = vao;
+    
+    // @todo : Enable vertex attrib only by shader's information, not with Geometry.
+    // Currently, vertexInputState.attributes depend on Geometry's VertexBuffer.
+    for(const auto& attr : vertexInputState.attributes)
+    {
+      gl.EnableVertexAttribArray(attr.location);
+    }
+
+    mProgramVAOCurrentState = vao;
+  }
+
+  /**
    * Sets the initial GL state.
    */
   void InitializeGlState()
@@ -165,9 +215,18 @@ struct Context::Impl
   const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
   const GLES::RenderPass*   mCurrentRenderPass{nullptr};
 
-  GLStateCache mGlStateCache{}; ///< GL status cache
+  // Each context must have own VAOs as they cannot be shared
+  std::map<GLES::ProgramImpl*, uint32_t> mProgramVAOMap;              ///< GL program-VAO map
+  uint32_t                               mProgramVAOCurrentState{0u}; ///< Currently bound VAO
+  GLStateCache                           mGlStateCache{};             ///< GL status cache
 
   bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
+
+  EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context
+
+  EGLSurface mCacheDrawReadSurface{0u};    ///< cached 'read' surface
+  EGLSurface mCacheDrawWriteSurface{0u};   ///< cached 'write' surface
+  EGLContext mCacheEGLGraphicsContext{0u}; ///< cached window context
 };
 
 Context::Context(EglGraphicsController& controller)
@@ -175,12 +234,22 @@ Context::Context(EglGraphicsController& controller)
   mImpl = std::make_unique<Impl>(controller);
 }
 
-Context::~Context() = default;
+Context::~Context()
+{
+  // Destroy native rendering context if one exists
+  if(mImpl->mNativeDrawContext)
+  {
+    eglDestroyContext(eglGetCurrentDisplay(), mImpl->mNativeDrawContext);
+    mImpl->mNativeDrawContext = EGL_NO_CONTEXT;
+  }
+}
 
-void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
+void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker)
 {
   auto& gl = *mImpl->mController.GetGL();
 
+  static const bool hasGLES3(mImpl->mController.GetGLESVersion() >= GLESVersion::GLES_30);
+
   // early out if neither current nor new pipelines are set
   // this behaviour may be valid so no assert
   if(!mImpl->mCurrentPipeline && !mImpl->mNewPipeline)
@@ -199,6 +268,13 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
     newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
   }
 
+  if(!currentProgram && !newProgram)
+  {
+    // Early out if we have no program for this pipeline.
+    DALI_LOG_ERROR("No program defined for pipeline\n");
+    return;
+  }
+
   if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
   {
     if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
@@ -233,6 +309,9 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
       texture->InitializeResource();
     }
 
+    // Warning, this may cause glWaitSync to occur on the GPU.
+    dependencyChecker.CheckNeedsSync(this, texture);
+
     texture->Bind(binding);
 
     texture->Prepare(); // @todo also non-const.
@@ -246,15 +325,25 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   }
 
   // for each attribute bind vertices
-  const auto& pipelineState = mImpl->mNewPipeline->GetCreateInfo();
-  const auto& vi            = pipelineState.vertexInputState;
-  for(const auto& attr : vi->attributes)
+
+  const auto& pipelineState    = mImpl->mNewPipeline ? mImpl->mNewPipeline->GetCreateInfo() : mImpl->mCurrentPipeline->GetCreateInfo();
+  const auto& vertexInputState = pipelineState.vertexInputState;
+
+  if(hasGLES3)
+  {
+    mImpl->BindProgramVAO(static_cast<const GLES::Program*>(pipelineState.programState->program)->GetImplementation(), *vertexInputState);
+  }
+
+  for(const auto& attr : vertexInputState->attributes)
   {
     // Enable location
-    mImpl->SetVertexAttributeLocation(attr.location, true);
+    if(!hasGLES3)
+    {
+      mImpl->SetVertexAttributeLocation(attr.location, true);
+    }
 
     const auto& bufferSlot    = mImpl->mCurrentVertexBufferBindings[attr.binding];
-    const auto& bufferBinding = vi->bufferBindings[attr.binding];
+    const auto& bufferBinding = vertexInputState->bufferBindings[attr.binding];
 
     auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
 
@@ -270,7 +359,7 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   }
 
   // Resolve topology
-  const auto& ia = mImpl->mNewPipeline->GetCreateInfo().inputAssemblyState;
+  const auto& ia = pipelineState.inputAssemblyState;
 
   // Bind uniforms
 
@@ -282,7 +371,11 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
       mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
                                                                 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
                                                                 mImpl->mGlStateCache.StencilBufferWriteEnabled());
-      mImpl->FlushVertexAttributeLocations();
+      // For GLES3+ we use VAO, for GLES2 internal cache
+      if(!hasGLES3)
+      {
+        mImpl->FlushVertexAttributeLocations();
+      }
 
       gl.DrawArrays(GLESTopology(ia->topology),
                     drawCall.draw.firstVertex,
@@ -297,7 +390,12 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
       mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
                                                                 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
                                                                 mImpl->mGlStateCache.StencilBufferWriteEnabled());
-      mImpl->FlushVertexAttributeLocations();
+
+      // For GLES3+ we use VAO, for GLES2 internal cache
+      if(!hasGLES3)
+      {
+        mImpl->FlushVertexAttributeLocations();
+      }
 
       auto indexBufferFormat = GLIndexFormat(binding.format).format;
       gl.DrawElements(GLESTopology(ia->topology),
@@ -322,11 +420,13 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
   }
 }
 
-void Context::BindTextures(const std::vector<Graphics::TextureBinding>& bindings)
+void Context::BindTextures(const Graphics::TextureBinding* bindings, uint32_t count)
 {
   // for each texture allocate slot
-  for(const auto& binding : bindings)
+  for(auto i = 0u; i < count; ++i)
   {
+    auto& binding = bindings[i];
+
     // Resize binding array if needed
     if(mImpl->mCurrentTextureBindings.size() <= binding.binding)
     {
@@ -337,14 +437,14 @@ void Context::BindTextures(const std::vector<Graphics::TextureBinding>& bindings
   }
 }
 
-void Context::BindVertexBuffers(const std::vector<GLES::VertexBufferBindingDescriptor>& bindings)
+void Context::BindVertexBuffers(const GLES::VertexBufferBindingDescriptor* bindings, uint32_t count)
 {
-  if(bindings.size() > mImpl->mCurrentVertexBufferBindings.size())
+  if(count > mImpl->mCurrentVertexBufferBindings.size())
   {
-    mImpl->mCurrentVertexBufferBindings.resize(bindings.size());
+    mImpl->mCurrentVertexBufferBindings.resize(count);
   }
   // Copy only set slots
-  std::copy_if(bindings.begin(), bindings.end(), mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) {
+  std::copy_if(bindings, bindings + count, mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) {
     return (nullptr != item.buffer);
   });
 }
@@ -360,21 +460,22 @@ void Context::BindPipeline(const GLES::Pipeline* newPipeline)
   mImpl->mNewPipeline = &newPipeline->GetPipeline();
 }
 
-void Context::BindUniformBuffers(const std::vector<UniformBufferBindingDescriptor>& uboBindings,
-                                 const UniformBufferBindingDescriptor&              standaloneBindings)
+void Context::BindUniformBuffers(const UniformBufferBindingDescriptor* uboBindings,
+                                 uint32_t                              uboCount,
+                                 const UniformBufferBindingDescriptor& standaloneBindings)
 {
   if(standaloneBindings.buffer)
   {
     mImpl->mCurrentStandaloneUBOBinding = standaloneBindings;
   }
 
-  if(uboBindings.size() >= mImpl->mCurrentUBOBindings.size())
+  if(uboCount >= mImpl->mCurrentUBOBindings.size())
   {
-    mImpl->mCurrentUBOBindings.resize(uboBindings.size() + 1);
+    mImpl->mCurrentUBOBindings.resize(uboCount + 1);
   }
 
-  auto it = uboBindings.begin();
-  for(auto i = 0u; i < uboBindings.size(); ++i)
+  auto it = uboBindings;
+  for(auto i = 0u; i < uboCount; ++i)
   {
     if(it->buffer)
     {
@@ -455,6 +556,10 @@ void Context::ResolveBlendState()
       if(newBlendState->colorBlendOp == newBlendState->alphaBlendOp)
       {
         gl.BlendEquation(GLBlendOp(newBlendState->colorBlendOp));
+        if(newBlendState->colorBlendOp >= Graphics::ADVANCED_BLEND_OPTIONS_START)
+        {
+          gl.BlendBarrier();
+        }
       }
       else
       {
@@ -509,105 +614,23 @@ void Context::ResolveUniformBuffers()
 
 void Context::ResolveStandaloneUniforms()
 {
-  auto& gl = *mImpl->mController.GetGL();
-
   // Find reflection for program
-  const auto program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
-
-  const auto& reflection = program->GetReflection();
+  const GLES::Program* program{nullptr};
 
-  auto extraInfos = reflection.GetStandaloneUniformExtraInfo();
-
-  const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
+  if(mImpl->mNewPipeline)
+  {
+    program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+  }
+  else if(mImpl->mCurrentPipeline)
+  {
+    program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
+  }
 
-  for(const auto& info : extraInfos)
+  if(program)
   {
-    auto type   = GLTypeConversion(info.type).type;
-    auto offset = info.offset;
-    switch(type)
-    {
-      case GLType::FLOAT_VEC2:
-      {
-        gl.Uniform2fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::FLOAT_VEC3:
-      {
-        gl.Uniform3fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::FLOAT_VEC4:
-      {
-        gl.Uniform4fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::INT_VEC2:
-      {
-        gl.Uniform2iv(info.location, info.arraySize, reinterpret_cast<const GLint*>(&ptr[offset]));
-        break;
-      }
-      case GLType::INT_VEC3:
-      {
-        gl.Uniform3iv(info.location, info.arraySize, reinterpret_cast<const GLint*>(&ptr[offset]));
-        break;
-      }
-      case GLType::INT_VEC4:
-      {
-        gl.Uniform4iv(info.location, info.arraySize, reinterpret_cast<const GLint*>(&ptr[offset]));
-        break;
-      }
-      case GLType::BOOL:
-      {
-        // not supported by DALi
-        break;
-      }
-      case GLType::BOOL_VEC2:
-      {
-        // not supported by DALi
-        break;
-      }
-      case GLType::BOOL_VEC3:
-      {
-        // not supported by DALi
-        break;
-      }
-      case GLType::BOOL_VEC4:
-      {
-        // not supported by DALi
-        break;
-      }
-      case GLType::FLOAT:
-      {
-        gl.Uniform1fv(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::FLOAT_MAT2:
-      {
-        gl.UniformMatrix2fv(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::FLOAT_MAT3:
-      {
-        gl.UniformMatrix3fv(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::FLOAT_MAT4:
-      {
-        gl.UniformMatrix4fv(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
-        break;
-      }
-      case GLType::SAMPLER_2D:
-      {
-        break;
-      }
-      case GLType::SAMPLER_CUBE:
-      {
-        break;
-      }
-      default:
-      {
-      }
-    }
+    const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
+    // Update program uniforms
+    program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
   }
 }
 
@@ -628,7 +651,8 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
   else if(targetInfo.framebuffer)
   {
     // bind framebuffer and swap.
-    renderTarget.GetFramebuffer()->Bind();
+    auto framebuffer = renderTarget.GetFramebuffer();
+    framebuffer->Bind();
   }
 
   // clear (ideally cache the setup)
@@ -640,6 +664,7 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
   const auto& attachments = *renderPass.GetCreateInfo().attachments;
   const auto& color0      = attachments[0];
   GLuint      mask        = 0;
+
   if(color0.loadOp == AttachmentLoadOp::CLEAR)
   {
     mask |= GL_COLOR_BUFFER_BIT;
@@ -648,22 +673,24 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
     // Something goes wrong here if Alpha mask is GL_TRUE
     ColorMask(true);
 
-    if(!mImpl->mGlStateCache.mClearColorSet ||
-       mImpl->mGlStateCache.mClearColor.r != renderPassBegin.clearValues[0].color.r ||
-       mImpl->mGlStateCache.mClearColor.g != renderPassBegin.clearValues[0].color.g ||
-       mImpl->mGlStateCache.mClearColor.b != renderPassBegin.clearValues[0].color.b ||
-       mImpl->mGlStateCache.mClearColor.a != renderPassBegin.clearValues[0].color.a)
+    const auto clearValues = renderPassBegin.clearValues.Ptr();
+
+    if(!Dali::Equals(mImpl->mGlStateCache.mClearColor.r, clearValues[0].color.r) ||
+       !Dali::Equals(mImpl->mGlStateCache.mClearColor.g, clearValues[0].color.g) ||
+       !Dali::Equals(mImpl->mGlStateCache.mClearColor.b, clearValues[0].color.b) ||
+       !Dali::Equals(mImpl->mGlStateCache.mClearColor.a, clearValues[0].color.a) ||
+       !mImpl->mGlStateCache.mClearColorSet)
     {
-      gl.ClearColor(renderPassBegin.clearValues[0].color.r,
-                    renderPassBegin.clearValues[0].color.g,
-                    renderPassBegin.clearValues[0].color.b,
-                    renderPassBegin.clearValues[0].color.a);
+      gl.ClearColor(clearValues[0].color.r,
+                    clearValues[0].color.g,
+                    clearValues[0].color.b,
+                    clearValues[0].color.a);
 
       mImpl->mGlStateCache.mClearColorSet = true;
-      mImpl->mGlStateCache.mClearColor    = Vector4(renderPassBegin.clearValues[0].color.r,
-                                                 renderPassBegin.clearValues[0].color.g,
-                                                 renderPassBegin.clearValues[0].color.b,
-                                                 renderPassBegin.clearValues[0].color.a);
+      mImpl->mGlStateCache.mClearColor    = Vector4(clearValues[0].color.r,
+                                                 clearValues[0].color.g,
+                                                 clearValues[0].color.b,
+                                                 clearValues[0].color.a);
     }
   }
 
@@ -700,14 +727,28 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
   mImpl->mCurrentRenderTarget = &renderTarget;
 }
 
-void Context::EndRenderPass()
+void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
 {
   if(mImpl->mCurrentRenderTarget)
   {
-    if(mImpl->mCurrentRenderTarget->GetFramebuffer())
+    GLES::Framebuffer* framebuffer = mImpl->mCurrentRenderTarget->GetFramebuffer();
+    if(framebuffer)
     {
       auto& gl = *mImpl->mController.GetGL();
       gl.Flush();
+
+      /* @todo Full dependency checking would need to store textures in Begin, and create
+       * fence objects here; but we're going to draw all fbos on shared context in serial,
+       * so no real need (yet). Might want to consider ensuring order of render passes,
+       * but that needs doing in the controller, and would need doing before ProcessCommandQueues.
+       *
+       * Currently up to the client to create render tasks in the right order.
+       */
+
+      /* Create fence sync objects. Other contexts can then wait on these fences before reading
+       * textures.
+       */
+      dependencyChecker.AddTextures(this, framebuffer);
     }
   }
 }
@@ -748,6 +789,14 @@ void Context::ClearBuffer(uint32_t mask, bool forceClear)
   }
 }
 
+void Context::InvalidateDepthStencilBuffers()
+{
+  auto& gl = *mImpl->mController.GetGL();
+
+  GLenum attachments[] = {GL_DEPTH, GL_STENCIL};
+  gl.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
+}
+
 void Context::SetScissorTestEnabled(bool scissorEnabled)
 {
   if(mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled)
@@ -972,4 +1021,67 @@ void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
   }
 }
 
+void Context::PrepareForNativeRendering()
+{
+  // this should be pretty much constant
+  auto display     = eglGetCurrentDisplay();
+  auto drawSurface = eglGetCurrentSurface(EGL_DRAW);
+  auto readSurface = eglGetCurrentSurface(EGL_READ);
+  auto context     = eglGetCurrentContext();
+
+  // push the surface and context data to the impl
+  // It's needed to restore context
+  if(!mImpl->mCacheEGLGraphicsContext)
+  {
+    mImpl->mCacheDrawWriteSurface   = drawSurface;
+    mImpl->mCacheDrawReadSurface    = readSurface;
+    mImpl->mCacheEGLGraphicsContext = context;
+  }
+
+  if(!mImpl->mNativeDrawContext)
+  {
+    EGLint configId{0u};
+    eglQueryContext(display, mImpl->mController.GetSharedContext(), EGL_CONFIG_ID, &configId);
+
+    EGLint configAttribs[3];
+    configAttribs[0] = EGL_CONFIG_ID;
+    configAttribs[1] = configId;
+    configAttribs[2] = EGL_NONE;
+
+    EGLConfig config;
+    EGLint    numConfigs;
+    if(eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) != EGL_TRUE)
+    {
+      DALI_LOG_ERROR("eglChooseConfig failed!\n");
+      return;
+    }
+
+    auto version = int(mImpl->mController.GetGLESVersion());
+
+    std::vector<EGLint> attribs;
+    attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
+    attribs.push_back(version / 10);
+    attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
+    attribs.push_back(version % 10);
+    attribs.push_back(EGL_NONE);
+
+    mImpl->mNativeDrawContext = eglCreateContext(display, config, mImpl->mController.GetSharedContext(), attribs.data());
+    if(mImpl->mNativeDrawContext == EGL_NO_CONTEXT)
+    {
+      DALI_LOG_ERROR("eglCreateContext failed!\n");
+      return;
+    }
+  }
+
+  eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext);
+}
+
+void Context::RestoreFromNativeRendering()
+{
+  auto display = eglGetCurrentDisplay();
+
+  // bring back original context
+  eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext);
+}
+
 } // namespace Dali::Graphics::GLES