Add GlView 55/254755/43
authorDaekwang Ryu <dkdk.ryu@samsung.com>
Fri, 17 Sep 2021 09:01:42 +0000 (18:01 +0900)
committerDaekwang Ryu <dkdk.ryu@samsung.com>
Fri, 17 Sep 2021 09:01:42 +0000 (18:01 +0900)
GlView allows drawing with OpenGL.
It creates a context, a surface and a render thread.
The render thread invokes user's callbacks.

Change-Id: I936313d32f1ce6653e1b1ce0a45f16f216c3f665

16 files changed:
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-window-impl.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-window.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-window.h
automated-tests/src/dali-toolkit/utc-Dali-GlView.cpp [new file with mode: 0644]
dali-toolkit/dali-toolkit.h
dali-toolkit/internal/controls/gl-view/gl-view-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/gl-view/gl-view-impl.h [new file with mode: 0644]
dali-toolkit/internal/controls/gl-view/gl-view-render-thread.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h [new file with mode: 0644]
dali-toolkit/internal/file.list
dali-toolkit/internal/graphics/shaders/gl-view.frag [new file with mode: 0644]
dali-toolkit/internal/graphics/shaders/gl-view.vert [new file with mode: 0644]
dali-toolkit/public-api/controls/gl-view/gl-view.cpp [new file with mode: 0644]
dali-toolkit/public-api/controls/gl-view/gl-view.h [new file with mode: 0644]
dali-toolkit/public-api/file.list

index 8990a0f..b1aece3 100755 (executable)
@@ -78,6 +78,7 @@ SET(TC_SOURCES
   utc-Dali-ControlWrapper.cpp
   utc-Dali-DragAndDropDetector.cpp
   utc-Dali-NPatchUtilities.cpp
   utc-Dali-ControlWrapper.cpp
   utc-Dali-DragAndDropDetector.cpp
   utc-Dali-NPatchUtilities.cpp
+  utc-Dali-GlView.cpp
 )
 
 # List of test harness files (Won't get parsed for test cases)
 )
 
 # List of test harness files (Won't get parsed for test cases)
index dc7a206..9044964 100644 (file)
@@ -54,6 +54,7 @@ public:
   FocusChangeSignalType mFocusChangeSignal;
   ResizeSignalType      mResizeSignal;
   int                   mRotationAngle;
   FocusChangeSignalType mFocusChangeSignal;
   ResizeSignalType      mResizeSignal;
   int                   mRotationAngle;
+  bool                  mVisible;
   DevelWindow::VisibilityChangedSignalType mVisibilityChangedSignal;
 };
 
   DevelWindow::VisibilityChangedSignalType mVisibilityChangedSignal;
 };
 
index d0a3e4f..b8cc1bc 100644 (file)
@@ -48,6 +48,7 @@ Window::Window( const PositionSize& positionSize )
   mFocusChangeSignal(),
   mResizeSignal(),
   mRotationAngle(90), // dummy angle for test coverage
   mFocusChangeSignal(),
   mResizeSignal(),
   mRotationAngle(90), // dummy angle for test coverage
+  mVisible(true),
   mVisibilityChangedSignal()
 {
 }
   mVisibilityChangedSignal()
 {
 }
@@ -183,6 +184,12 @@ void Window::Raise()
 void Window::Hide()
 {
   GetImplementation( *this ).mVisibilityChangedSignal.Emit( *this, false );
 void Window::Hide()
 {
   GetImplementation( *this ).mVisibilityChangedSignal.Emit( *this, false );
+  GetImplementation( *this ).mVisible = false;
+}
+
+bool Window::IsVisible() const
+{
+  return GetImplementation( *this ).mVisible;
 }
 
 FocusChangeSignalType& Window::FocusChangeSignal()
 }
 
 FocusChangeSignalType& Window::FocusChangeSignal()
index a696684..d102f6a 100644 (file)
@@ -77,6 +77,7 @@ public:
   Vector4 GetBackgroundColor() const;
   void Raise();
   void Hide();
   Vector4 GetBackgroundColor() const;
   void Raise();
   void Hide();
+  bool IsVisible() const;
   FocusChangeSignalType& FocusChangeSignal();
   KeyEventSignalType& KeyEventSignal();
   TouchEventSignalType& TouchedSignal();
   FocusChangeSignalType& FocusChangeSignal();
   KeyEventSignalType& KeyEventSignal();
   TouchEventSignalType& TouchedSignal();
diff --git a/automated-tests/src/dali-toolkit/utc-Dali-GlView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-GlView.cpp
new file mode 100644 (file)
index 0000000..9f74b72
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <unistd.h>
+#include <thread>
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+// Positive test case for a method
+int UtcDaliGlViewNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliGlViewNew");
+  GlView view = GlView::New(GlView::ColorFormat::RGBA8888);
+  DALI_TEST_CHECK( view );
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliGlViewDownCast(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliGlViewDownCast");
+
+  GlView view = GlView::New(GlView::ColorFormat::RGB888);
+  BaseHandle handle(view);
+
+  Toolkit::GlView view2 = Toolkit::GlView::DownCast( handle );
+  DALI_TEST_CHECK( view );
+  DALI_TEST_CHECK( view2 );
+  DALI_TEST_CHECK( view == view2 );
+  END_TEST;
+}
+
+int UtcDaliGlViewCopyAndAssignment(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewCopyAndAssignment");
+
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+  DALI_TEST_CHECK( view );
+
+  GlView copy( view );
+  DALI_TEST_CHECK( view == copy );
+
+  GlView assign;
+  DALI_TEST_CHECK( !assign );
+
+  assign = copy;
+  DALI_TEST_CHECK( assign == view );
+
+  END_TEST;
+}
+
+int UtcDaliGlViewMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewMoveAssignment");
+
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+  DALI_TEST_EQUALS( 1, view.GetBaseObject().ReferenceCount(), TEST_LOCATION );
+
+  GlView moved;
+  moved = std::move( view );
+  DALI_TEST_CHECK( moved );
+  DALI_TEST_EQUALS( 1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION );
+  DALI_TEST_CHECK( !view );
+
+  END_TEST;
+}
+
+int UtcDaliGlViewSetGraphicsConfigGles20N(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewSetGraphicsConfigGles20");
+  GlView view;
+  try
+  {
+    view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+    DALI_TEST_CHECK(false);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewSetGraphicsConfigGles30(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewSetGraphicsConfigGles30");
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+
+  try
+  {
+    view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_3_0);
+    DALI_TEST_CHECK(true);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(false);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewRenderingMode(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewRenderingMode");
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+
+  view.SetRenderingMode(GlView::RenderingMode::ON_DEMAND);
+
+  GlView::RenderingMode mode = view.GetRenderingMode();
+
+  DALI_TEST_EQUALS( GlView::RenderingMode::ON_DEMAND, mode, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliGlViewOnSizeSet(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewOnSizeSet");
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+
+
+  application.GetScene().Add( view );
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 size( 200.0f, 300.0f, 0.0f );
+  view.SetProperty( Actor::Property::SIZE, size );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( view.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ), size, TEST_LOCATION );
+
+  END_TEST;
+}
+
+
+// Internal callback function
+void glInit(void)
+{
+}
+
+int glRenderFrame(void)
+{
+  static unsigned int retFlag = 0;
+  return retFlag++;
+}
+
+void glTerminate(void)
+{
+}
+
+void resizeCB(Vector2 size)
+{
+}
+
+int UtcDaliGlViewRegisterGlCallbackN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewRegisterGlCallback");
+  GlView view;
+
+  try
+  {
+    view.RegisterGlCallback(Dali::MakeCallback(glInit), Dali::MakeCallback(glRenderFrame), Dali::MakeCallback(glTerminate));
+    DALI_TEST_CHECK(false);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewSetResizeCallbackN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewSetResizeCallback");
+  GlView view;
+
+  try
+  {
+    view.SetResizeCallback(Dali::MakeCallback(resizeCB));
+    DALI_TEST_CHECK(false);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(true);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewRenderOnce(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewRenderOnce");
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+
+  try
+  {
+    view.RenderOnce();
+    DALI_TEST_CHECK(true);
+  }
+  catch(...)
+  {
+    DALI_TEST_CHECK(false);
+  }
+  END_TEST;
+}
+
+int UtcDaliGlViewWindowVisibilityChanged(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewWindowVisibilityChanged");
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+  application.GetScene().Add( view );
+  view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS);
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallback(Dali::MakeCallback(glInit), Dali::MakeCallback(glRenderFrame), Dali::MakeCallback(glTerminate));
+  view.SetResizeCallback(Dali::MakeCallback(resizeCB));
+
+  application.SendNotification();
+  application.Render();
+
+  Window window = DevelWindow::Get( view );
+  window.Hide();
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
+
+int UtcDaliGlViewOnScene(void)
+{
+  ToolkitTestApplication application;
+
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+
+  //Onscene
+  application.GetScene().Add( view );
+  view.SetRenderingMode(GlView::RenderingMode::CONTINUOUS);
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallback(Dali::MakeCallback(glInit), Dali::MakeCallback(glRenderFrame), Dali::MakeCallback(glTerminate));
+
+  application.SendNotification();
+  application.Render();
+
+  //Offscene
+  application.GetScene().Remove(view);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
+
+int UtcDaliGlViewControlVisibilityChanged(void)
+{
+  ToolkitTestApplication application;
+
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+  application.GetScene().Add( view );
+
+  application.SendNotification();
+  application.Render();
+
+  view.SetProperty( Actor::Property::VISIBLE, false );
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(view.GetCurrentProperty<bool>(Actor::Property::VISIBLE) == false);
+
+  view.SetProperty( Actor::Property::VISIBLE, true );
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK(view.GetCurrentProperty<bool>(Actor::Property::VISIBLE) == true);
+
+  END_TEST;
+}
+
+int UtcDaliGlViewResize(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliGlViewResize");
+  GlView view = Toolkit::GlView::New(GlView::ColorFormat::RGB888);
+
+  application.GetScene().Add( view );
+  view.SetGraphicsConfig(true, true, 0, GlView::GraphicsApiVersion::GLES_VERSION_2_0);
+  view.RegisterGlCallback(Dali::MakeCallback(glInit), Dali::MakeCallback(glRenderFrame), Dali::MakeCallback(glTerminate));
+  view.SetResizeCallback(Dali::MakeCallback(resizeCB));
+  view.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  view.SetProperty(Actor::Property::SIZE, Vector2(360.0f, 360.0f));
+
+  application.SendNotification();
+  application.Render();
+
+  //To GlViewRenderThread can recognize Resize signal the main thread have to sleep.
+  std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK(true);
+  END_TEST;
+}
index 1ec7fa3..99621c8 100644 (file)
@@ -28,6 +28,7 @@
 #include <dali-toolkit/public-api/controls/control-impl.h>
 #include <dali-toolkit/public-api/controls/control.h>
 #include <dali-toolkit/public-api/controls/flex-container/flex-container.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
 #include <dali-toolkit/public-api/controls/control.h>
 #include <dali-toolkit/public-api/controls/flex-container/flex-container.h>
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
 #include <dali-toolkit/public-api/controls/model3d-view/model3d-view.h>
 #include <dali-toolkit/public-api/controls/progress-bar/progress-bar.h>
 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
 #include <dali-toolkit/public-api/controls/model3d-view/model3d-view.h>
 #include <dali-toolkit/public-api/controls/progress-bar/progress-bar.h>
diff --git a/dali-toolkit/internal/controls/gl-view/gl-view-impl.cpp b/dali-toolkit/internal/controls/gl-view/gl-view-impl.cpp
new file mode 100644 (file)
index 0000000..64e31d6
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/gl-view/gl-view-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/lifecycle-controller.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/object/any.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <dali/public-api/rendering/texture-set.h>
+#include <dali/public-api/rendering/texture.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+Dali::Toolkit::GlView GlView::New(Dali::Toolkit::GlView::ColorFormat colorFormat)
+{
+  GlView*               impl   = new GlView(colorFormat);
+  Dali::Toolkit::GlView handle = Dali::Toolkit::GlView(*impl);
+  impl->Initialize();
+  return handle;
+}
+
+GlView::GlView(Dali::Toolkit::GlView::ColorFormat colorFormat)
+: Control(ControlBehaviour(ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS)),
+  mRenderThread(nullptr),
+  mNativeImageQueue(nullptr),
+  mRenderingMode(Toolkit::GlView::RenderingMode::CONTINUOUS),
+  mColorFormat(colorFormat),
+  mDepth(false),
+  mStencil(false),
+  mMSAA(0)
+{
+}
+
+GlView::~GlView()
+{
+  if(mRenderThread)
+  {
+    mRenderThread->Stop();
+    mRenderThread->Join();
+  }
+}
+
+void GlView::RegisterGlCallback(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback)
+{
+  if(mRenderThread)
+  {
+    mRenderThread->RegisterGlCallback(initCallback, renderFrameCallback, terminateCallback);
+  }
+}
+
+void GlView::SetResizeCallback(CallbackBase* resizeCallback)
+{
+  if(mRenderThread)
+  {
+    mRenderThread->SetResizeCallback(resizeCallback);
+  }
+}
+
+bool GlView::SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version)
+{
+  // Init Graphics
+  mDepth   = depth;
+  mStencil = stencil;
+  mMSAA    = msaa;
+
+  int rVersion;
+
+  if(version == Dali::Toolkit::GlView::GraphicsApiVersion::GLES_VERSION_2_0)
+  {
+    rVersion = 20;
+  }
+  else
+  {
+    rVersion = 30;
+  }
+
+  if(mRenderThread)
+  {
+    return mRenderThread->SetGraphicsConfig(depth, stencil, msaa, rVersion);
+  }
+
+  return false;
+}
+
+void GlView::SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode)
+{
+  mRenderingMode    = mode;
+  Renderer renderer = Self().GetRendererAt(0);
+
+  if(mRenderingMode == Dali::Toolkit::GlView::RenderingMode::ON_DEMAND)
+  {
+    renderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::IF_REQUIRED);
+
+    if(mRenderThread)
+    {
+      mRenderThread->SetOnDemandRenderMode(true);
+    }
+  }
+  else
+  {
+    renderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY);
+
+    if(mRenderThread)
+    {
+      mRenderThread->SetOnDemandRenderMode(false);
+    }
+  }
+}
+
+Dali::Toolkit::GlView::RenderingMode GlView::GetRenderingMode() const
+{
+  return mRenderingMode;
+}
+
+void GlView::RenderOnce()
+{
+  if(mRenderThread)
+  {
+    mRenderThread->RenderOnce();
+  }
+}
+
+void GlView::OnInitialize()
+{
+  //Create NativeImageSourceQueue with the size of 1,1
+  mNativeImageQueue = Dali::NativeImageSourceQueue::New(1, 1, GetColorFormat(mColorFormat));
+
+  if(!mNativeImageQueue)
+  {
+    DALI_LOG_ERROR("NativeImageSourceQueue is NULL");
+    return;
+  }
+
+  AddRenderer();
+
+  Actor self = Self();
+
+  //Create a RenderThread
+  mRenderThread = std::unique_ptr<GlViewRenderThread>(new GlViewRenderThread(mNativeImageQueue));
+  if(!mRenderThread)
+  {
+    DALI_LOG_ERROR("Fail to create GlView Render Thread!!!!\n");
+    return;
+  }
+
+  //Adding VisibilityChange Signal.
+  Dali::DevelActor::VisibilityChangedSignal(self).Connect(this, &GlView::OnControlVisibilityChanged);
+}
+
+void GlView::OnSizeSet(const Vector3& targetSize)
+{
+  Control::OnSizeSet(targetSize);
+
+  if(mRenderThread)
+  {
+    if(mNativeImageQueue)
+    {
+      mRenderThread->AcquireSurface();
+      mNativeImageQueue->SetSize(static_cast<uint32_t>(targetSize.x), static_cast<uint32_t>(targetSize.y));
+      mRenderThread->SetSurfaceSize(Vector2(targetSize.x, targetSize.y));
+      mRenderThread->ReleaseSurface();
+    }
+  }
+}
+
+Shader GlView::CreateShader()
+{
+  std::string fragmentShader = std::string(SHADER_GL_VIEW_FRAG);
+
+  if(mNativeImageQueue)
+  {
+    mNativeImageQueue->ApplyNativeFragmentShader(fragmentShader);
+  }
+
+  return Shader::New(SHADER_GL_VIEW_VERT, fragmentShader);
+}
+
+void GlView::OnControlVisibilityChanged(Dali::Actor actor, bool visible, Dali::DevelActor::VisibilityChange::Type type)
+{
+  Actor self = Self();
+  if(self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+  {
+    if(mRenderThread)
+    {
+      if(visible && DevelWindow::Get(self).IsVisible())
+      {
+        mRenderThread->Resume();
+      }
+      else
+      {
+        mRenderThread->Pause();
+      }
+    }
+  }
+}
+
+void GlView::OnWindowVisibilityChanged(Window window, bool visible)
+{
+  if(mRenderThread)
+  {
+    if(visible && Self().GetProperty<bool>(Actor::Property::VISIBLE))
+    {
+      mRenderThread->Resume();
+    }
+    else
+    {
+      mRenderThread->Pause();
+    }
+  }
+}
+
+void GlView::OnSceneConnection(int depth)
+{
+  Control::OnSceneConnection(depth);
+
+  Actor  self   = Self();
+  Window window = DevelWindow::Get(self);
+
+  if(window)
+  {
+    DevelWindow::VisibilityChangedSignal(window).Connect(this, &GlView::OnWindowVisibilityChanged);
+  }
+
+  if(mRenderThread)
+  {
+    if(self.GetProperty<bool>(Actor::Property::VISIBLE) && window.IsVisible())
+    {
+      mRenderThread->Resume();
+    }
+  }
+}
+
+void GlView::OnSceneDisconnection()
+{
+  Control::OnSceneDisconnection();
+  if(mRenderThread)
+  {
+    mRenderThread->Pause();
+  }
+}
+
+Dali::Geometry GlView::CreateTexturedQuad()
+{
+  struct Vertex
+  {
+    Dali::Vector2 position;
+  };
+
+  static const Vertex data[] = {{Dali::Vector2(-0.5f, -0.5f)},
+                                {Dali::Vector2(0.5f, -0.5f)},
+                                {Dali::Vector2(-0.5f, 0.5f)},
+                                {Dali::Vector2(0.5f, 0.5f)}};
+
+  uint32_t numberOfVertices = sizeof(data) / sizeof(Vertex);
+
+  Dali::VertexBuffer  vertexBuffer;
+  Dali::Property::Map vertexFormat;
+  vertexFormat["aPosition"] = Dali::Property::VECTOR2;
+
+  //Create a vertex buffer for vertex positions and texture coordinates
+  vertexBuffer = Dali::VertexBuffer::New(vertexFormat);
+  vertexBuffer.SetData(data, numberOfVertices);
+
+  //Create the geometry
+  Dali::Geometry geometry = Dali::Geometry::New();
+  geometry.AddVertexBuffer(vertexBuffer);
+  geometry.SetType(Dali::Geometry::TRIANGLE_STRIP);
+
+  return geometry;
+}
+
+void GlView::AddRenderer()
+{
+  if(!mNativeImageQueue)
+  {
+    DALI_LOG_ERROR("Target Surface is NULL");
+    return;
+  }
+
+  Actor    self     = Self();
+  Geometry geometry = CreateTexturedQuad();
+  Shader   shader   = CreateShader();
+  Renderer renderer = Renderer::New(geometry, shader);
+
+  Texture    nativeTexture = Texture::New(*mNativeImageQueue);
+  TextureSet textureSet    = TextureSet::New();
+  textureSet.SetTexture(0u, nativeTexture);
+
+  renderer.SetTextures(textureSet);
+
+  self.AddRenderer(renderer);
+}
+
+Dali::NativeImageSourceQueue::ColorFormat GlView::GetColorFormat(Dali::Toolkit::GlView::ColorFormat format)
+{
+  switch(format)
+  {
+    case Toolkit::GlView::ColorFormat::RGBA8888:
+    {
+      return Dali::NativeImageSourceQueue::ColorFormat::RGBA8888;
+    }
+
+    case Toolkit::GlView::ColorFormat::RGB888:
+    default:
+    {
+      return Dali::NativeImageSourceQueue::ColorFormat::RGBX8888;
+    }
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/gl-view/gl-view-impl.h b/dali-toolkit/internal/controls/gl-view/gl-view-impl.h
new file mode 100644 (file)
index 0000000..c0114ca
--- /dev/null
@@ -0,0 +1,176 @@
+#ifndef DALI_TOOLKIT_INTERNAL_GL_VIEW_H
+#define DALI_TOOLKIT_INTERNAL_GL_VIEW_H
+
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
+#include <dali/public-api/adaptor-framework/window.h>
+#include <dali/public-api/rendering/geometry.h>
+#include <dali/public-api/rendering/shader.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+class GlView;
+
+namespace Internal
+{
+class GlView : public Dali::Toolkit::Internal::Control
+{
+protected:
+  virtual ~GlView();
+
+public:
+  /**
+   * @copydoc Dali::Toolkit::GlView::New()
+   */
+  static Dali::Toolkit::GlView New(Dali::Toolkit::GlView::ColorFormat colorFormat);
+
+  /**
+   * Construct a new GlView.
+   */
+  GlView(Dali::Toolkit::GlView::ColorFormat colorFormat);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RegisterGlCallback()
+   */
+  void RegisterGlCallback(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetResizeCallback()
+   */
+  void SetResizeCallback(CallbackBase* resizeCallback);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetGraphisConfig()
+   */
+  bool SetGraphicsConfig(bool depth, bool stencil, int msaa, Dali::Toolkit::GlView::GraphicsApiVersion version);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetRenderingMode()
+   */
+  void SetRenderingMode(Dali::Toolkit::GlView::RenderingMode mode);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::GetRenderingMode()
+   */
+  Dali::Toolkit::GlView::RenderingMode GetRenderingMode() const;
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RenderOnce()
+   */
+  void RenderOnce();
+
+private: // From Control
+  /**
+   * @copydoc Toolkit::Control::OnInitialize()
+   */
+  virtual void OnInitialize() override;
+
+  /**
+   * @copydoc Toolkit::Control::OnSceneConnection()
+   */
+  void OnSceneConnection(int depth) override;
+
+  /**
+   * @copydoc Toolkit::Control::OnSceneDisconnection()
+   */
+  void OnSceneDisconnection() override;
+
+  /**
+   * @copydoc Toolkit::Control::OnSizeSet()
+   */
+  void OnSizeSet(const Vector3& targetSize) override;
+
+private:
+  // Undefined copy constructor and assignment operators
+  GlView(const GlView& GlView);
+  GlView& operator=(const GlView& GlView);
+
+  /**
+   * Callback when the visibility of the GlView is changed
+   */
+  void OnControlVisibilityChanged(Dali::Actor actor, bool visible, Dali::DevelActor::VisibilityChange::Type type);
+
+  /**
+   * Callback when the visibility of the window is changed
+   */
+  void OnWindowVisibilityChanged(Dali::Window window, bool visible);
+
+  /**
+   * Creates the geometry for texturing.
+   */
+  Dali::Geometry CreateTexturedQuad();
+
+  /**
+   * Adds renderer to Actor.
+   */
+  void AddRenderer();
+
+  /**
+   * Creates shader for rendering.
+   */
+  Dali::Shader CreateShader();
+
+  /**
+   * @brief Gets the NativeImageSourceQueue's ColorFormat with the GlView's ColorFormat.
+   * @param[in] colorFormat the color format of the GlView.
+   * @return The color format of NativeImageSourceQueue
+   */
+  Dali::NativeImageSourceQueue::ColorFormat GetColorFormat(Dali::Toolkit::GlView::ColorFormat format);
+
+private:
+  std::unique_ptr<GlViewRenderThread>  mRenderThread;
+  Dali::NativeImageSourceQueuePtr      mNativeImageQueue;
+  Dali::Toolkit::GlView::RenderingMode mRenderingMode;
+  Dali::Toolkit::GlView::ColorFormat   mColorFormat;
+
+  bool mDepth;
+  bool mStencil;
+  int  mMSAA;
+};
+
+} // namespace Internal
+
+inline Dali::Toolkit::Internal::GlView& GetImpl(Dali::Toolkit::GlView& handle)
+{
+  DALI_ASSERT_ALWAYS(handle);
+  Dali::RefObject& impl = handle.GetImplementation();
+  return static_cast<Dali::Toolkit::Internal::GlView&>(impl);
+}
+
+inline const Dali::Toolkit::Internal::GlView& GetImpl(const Dali::Toolkit::GlView& handle)
+{
+  DALI_ASSERT_ALWAYS(handle);
+  const Dali::RefObject& impl = handle.GetImplementation();
+  return static_cast<const Dali::Toolkit::Internal::GlView&>(impl);
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_GL_VIEW_H
diff --git a/dali-toolkit/internal/controls/gl-view/gl-view-render-thread.cpp b/dali-toolkit/internal/controls/gl-view/gl-view-render-thread.cpp
new file mode 100644 (file)
index 0000000..db2e98c
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h>
+
+//EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/thread-settings.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/integration-api/debug.h>
+#include <chrono>
+#include <thread>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+constexpr unsigned int NANOSECONDS_PER_SECOND(1e+9);
+
+// The following values will get calculated at compile time
+constexpr float    DEFAULT_FRAME_DURATION_IN_SECONDS(1.0f / 60.0f);
+constexpr uint64_t DEFAULT_FRAME_DURATION_IN_NANOSECONDS(DEFAULT_FRAME_DURATION_IN_SECONDS* NANOSECONDS_PER_SECOND);
+
+} // namespace
+
+GlViewRenderThread::GlViewRenderThread(Dali::NativeImageSourceQueuePtr queue)
+: mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
+  mSurfaceSize(1, 1),
+  mNativeImageSurface(),
+  mNativeImageQueue(queue),
+  mSurfaceSemaphore(1),
+  mGlInitCallback(nullptr),
+  mGlRenderFrameCallback(nullptr),
+  mGlTerminateCallback(nullptr),
+  mResizeCallback(nullptr),
+  mDepth(false),
+  mStencil(false),
+  mMSAA(0),
+  mGraphicsApiVersion(20),
+  mConditionalWait(),
+  mIsThreadStarted(0),
+  mIsThreadStopped(0),
+  mIsThreadPaused(0),
+  mIsRenderRequested(0),
+  mRenderingMode(0),
+  mIsSurfaceResized(0),
+  mDefaultFrameDurationNanoseconds(DEFAULT_FRAME_DURATION_IN_NANOSECONDS)
+{
+  mNativeImageSurface = Dali::NativeImageSurface::New(mNativeImageQueue);
+
+  if(!mNativeImageSurface)
+  {
+    DALI_LOG_ERROR("Creating NativeImageSurface Failed, Could not start GlView Render Thread");
+  }
+}
+
+void GlViewRenderThread::RegisterGlCallback(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback)
+{
+  if(!mGlInitCallback && !mGlRenderFrameCallback && !mGlTerminateCallback)
+  {
+    mGlInitCallback        = std::unique_ptr<CallbackBase>(initCallback);
+    mGlRenderFrameCallback = std::unique_ptr<CallbackBase>(renderFrameCallback);
+    mGlTerminateCallback   = std::unique_ptr<CallbackBase>(terminateCallback);
+  }
+}
+
+void GlViewRenderThread::SetResizeCallback(CallbackBase* resizeCallback)
+{
+  if(!mResizeCallback)
+  {
+    mResizeCallback = std::unique_ptr<CallbackBase>(resizeCallback);
+  }
+}
+
+bool GlViewRenderThread::SetGraphicsConfig(bool depth, bool stencil, int msaa, int version)
+{
+  mDepth              = depth;
+  mStencil            = stencil;
+  mMSAA               = msaa;
+  mGraphicsApiVersion = version;
+
+  if(mNativeImageSurface)
+  {
+    return mNativeImageSurface->SetGraphicsConfig(mDepth, mStencil, mMSAA, mGraphicsApiVersion);
+  }
+
+  return false;
+}
+
+void GlViewRenderThread::SetOnDemandRenderMode(bool onDemand)
+{
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+  mRenderingMode = static_cast<unsigned int>(onDemand);
+  DALI_LOG_RELEASE_INFO("GlViewRenderThread::SetOnDemandRenderMode(): mRenderingMode: %d\n", mRenderingMode);
+  if(!onDemand && !mIsThreadPaused)
+  {
+    mConditionalWait.Notify(lock);
+  }
+}
+
+void GlViewRenderThread::SetSurfaceSize(Dali::Vector2 size)
+{
+  //GlViewRenderThread::Run was already blocked in Internal::GlView::OnSizeSet
+  mSurfaceSize      = size;
+  mIsSurfaceResized = 1;
+}
+
+void GlViewRenderThread::RenderOnce()
+{
+  //Notify GLRender thread.
+  Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
+  mIsRenderRequested = 1;
+  mConditionalWait.Notify(lock);
+}
+
+void GlViewRenderThread::GetNanoSeconds(uint64_t& timeInNanoseconds)
+{
+  // Get the time of a monotonic clock since its epoch.
+  auto epoch        = std::chrono::steady_clock::now().time_since_epoch();
+  auto duration     = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
+  timeInNanoseconds = static_cast<uint64_t>(duration.count());
+}
+
+void GlViewRenderThread::SleepUntil(uint64_t timeInNanoseconds)
+{
+  using Clock     = std::chrono::steady_clock;
+  using TimePoint = std::chrono::time_point<Clock>;
+
+  const Clock::duration duration = std::chrono::nanoseconds(timeInNanoseconds);
+  const TimePoint       timePoint(duration);
+
+  std::this_thread::sleep_until(timePoint);
+}
+
+void GlViewRenderThread::Run()
+{
+  Dali::SetThreadName("GlViewRenderer");
+  mLogFactory.InstallLogFunction();
+
+  int renderFrameResult = 0;
+
+  if(!mNativeImageSurface)
+  {
+    DALI_LOG_ERROR("NativeImageSurface is null, Could not start GlView Render Thread");
+    return;
+  }
+
+  AcquireSurface();
+  mNativeImageSurface->InitializeGraphics();
+  ReleaseSurface();
+
+  mNativeImageSurface->PreRender();
+  if(mGlInitCallback)
+  {
+    CallbackBase::Execute(*mGlInitCallback);
+  }
+
+  uint64_t timeToSleepUntil = 0;
+
+  while(RenderReady(timeToSleepUntil))
+  {
+    uint64_t currentFrameStartTime = 0;
+    GetNanoSeconds(currentFrameStartTime);
+
+    AcquireSurface();
+    mNativeImageSurface->PreRender();
+    if(mIsSurfaceResized)
+    {
+      if(mResizeCallback)
+      {
+        CallbackBase::Execute(*mResizeCallback, static_cast<int>(mSurfaceSize.x), static_cast<int>(mSurfaceSize.y));
+      }
+      mIsSurfaceResized = 0;
+    }
+
+    if(mNativeImageSurface->CanRender())
+    {
+      if(mGlRenderFrameCallback)
+      {
+        renderFrameResult = CallbackBase::ExecuteReturn<int>(*mGlRenderFrameCallback);
+        if(renderFrameResult)
+        {
+          mNativeImageSurface->PostRender();
+        }
+      }
+    }
+
+    ReleaseSurface();
+
+    if(timeToSleepUntil == 0)
+    {
+      timeToSleepUntil = currentFrameStartTime + mDefaultFrameDurationNanoseconds;
+    }
+    else
+    {
+      timeToSleepUntil += mDefaultFrameDurationNanoseconds;
+      uint64_t currentFrameEndTime = 0;
+      GetNanoSeconds(currentFrameEndTime);
+      while(currentFrameEndTime > timeToSleepUntil + mDefaultFrameDurationNanoseconds)
+      {
+        timeToSleepUntil += mDefaultFrameDurationNanoseconds;
+      }
+    }
+
+    SleepUntil(timeToSleepUntil);
+  }
+
+  if(mGlTerminateCallback)
+  {
+    CallbackBase::Execute(*mGlTerminateCallback);
+  }
+
+  mNativeImageSurface->TerminateGraphics();
+
+  return;
+}
+
+void GlViewRenderThread::Stop()
+{
+  // Set to come out Render Thread out of waiting condition.
+  Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
+  mIsThreadStopped = 1;
+  mIsThreadPaused  = 0;
+  mConditionalWait.Notify(lock);
+}
+
+void GlViewRenderThread::Pause()
+{
+  //Notify GLRender thread, If actor visibility is change
+  Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
+  mIsThreadPaused = 1;
+  mConditionalWait.Notify(lock);
+}
+
+void GlViewRenderThread::Resume()
+{
+  Dali::ConditionalWait::ScopedLock lock(mConditionalWait);
+  if(!mIsThreadStarted)
+  {
+    Start();
+    mIsThreadStarted = 1;
+  }
+  mIsThreadPaused = 0;
+  mConditionalWait.Notify(lock);
+}
+
+bool GlViewRenderThread::RenderReady(uint64_t& timeToSleepUntil)
+{
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+  while((!mIsThreadStopped && mRenderingMode && !mIsRenderRequested) || mIsThreadPaused)
+  {
+    timeToSleepUntil = 0;
+    mConditionalWait.Wait(lock);
+  }
+
+  mIsRenderRequested = 0;
+  // Keep the update-render thread alive if this thread is NOT to be destroyed
+  return !mIsThreadStopped;
+}
+
+void GlViewRenderThread::AcquireSurface()
+{
+  mSurfaceSemaphore.Acquire();
+}
+
+void GlViewRenderThread::ReleaseSurface()
+{
+  mSurfaceSemaphore.Release(1);
+}
+
+GlViewRenderThread::~GlViewRenderThread()
+{
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h b/dali-toolkit/internal/controls/gl-view/gl-view-render-thread.h
new file mode 100644 (file)
index 0000000..c27c832
--- /dev/null
@@ -0,0 +1,179 @@
+#ifndef DALI_TOOLKIT_INTERNAL_GL_VIEW_THREAD_H
+#define DALI_TOOLKIT_INTERNAL_GL_VIEW_THREAD_H
+
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/semaphore.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/integration-api/adaptor-framework/log-factory-interface.h>
+#include <dali/integration-api/adaptor-framework/native-image-surface.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/public-api/signals/callback.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+/**
+ * @brief GlViewRenderThread is a render thread for GlView.
+ * This invokes user's callbacks.
+ */
+class GlViewRenderThread : public Dali::Thread
+{
+public:
+  /**
+   * Constructor
+   *
+   * @param[in] queue The NativeImageSourceQueue that GL renders onto
+   */
+  GlViewRenderThread(Dali::NativeImageSourceQueuePtr queue);
+
+  /**
+   * destructor.
+   */
+  virtual ~GlViewRenderThread();
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::RegisterGlCallback()
+   */
+  void RegisterGlCallback(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetResizeCallback()
+   */
+  void SetResizeCallback(CallbackBase* resizeCallback);
+
+  /**
+   * @copydoc Dali::Toolkit::GlView::SetGraphicsConfig()
+   */
+  bool SetGraphicsConfig(bool depth, bool stencil, int msaa, int version);
+
+  /**
+   * Enable OnDemand Rendering Mode
+   *
+   * @param[in] onDemand the flag of OnDemand Rendering Mode. If the flag is true, the rendering mode is set OnDemand,
+   * otherwise the flag is false, rendering mode is set continuous mode.
+   */
+  void SetOnDemandRenderMode(bool onDemand);
+
+  /**
+   * Sets the surface size
+   *
+   * @param[in] size the size of the NaitveImageSurface
+   */
+  void SetSurfaceSize(Dali::Vector2 size);
+
+  /**
+   * @copydoc Dali::Toolkit::RenderOnce()
+   */
+  void RenderOnce();
+
+  /**
+   * Pauses the render thread.
+   */
+  void Pause();
+
+  /**
+   * Resumes the render thread.
+   */
+  void Resume();
+
+  /**
+   * Stops the render thread.
+   * @note Should only be called in Stop as calling this will kill the render thread.
+   */
+  void Stop();
+
+  /**
+   * Acquires the surface resource
+   */
+  void AcquireSurface();
+
+  /**
+   * Releases the surface resource
+   */
+  void ReleaseSurface();
+
+protected:
+  /**
+   * The routine that the thread will execute once it is started.
+   */
+  void Run() override;
+
+private:
+  GlViewRenderThread(const GlViewRenderThread& obj) = delete;
+  GlViewRenderThread operator=(const GlViewRenderThread& obj) = delete;
+
+  /**
+   * Called by the Render Thread which ensures a wait if required.
+   *
+   * @param[out] timeToSleepUntil  The time remaining in nanoseconds to keep the thread sleeping before resuming.
+   * @return false, if the thread should stop.
+   */
+  bool RenderReady(uint64_t& timeToSleepUntil);
+
+  /**
+   * @brief Get the monotonic time since the clock's epoch.
+   * @param[out]  timeInNanoseconds  The time in nanoseconds since the reference point.
+   */
+  void GetNanoSeconds(uint64_t& timeInNanoseconds);
+
+  /**
+   * Blocks the execution of the current thread until specified sleep_time
+   * @param[in] timeInNanoseconds  The time blocking for
+   */
+  void SleepUntil(uint64_t timeInNanoseconds);
+
+private:
+  const Dali::LogFactoryInterface& mLogFactory;
+  Dali::Vector2                    mSurfaceSize; ///< The size of mNativeImageQueue
+  Dali::NativeImageSurfacePtr      mNativeImageSurface;
+  Dali::NativeImageSourceQueuePtr  mNativeImageQueue;
+  Semaphore<>                      mSurfaceSemaphore; ///< The semaphore to avoid race condition to the render target
+
+  std::unique_ptr<CallbackBase> mGlInitCallback;
+  std::unique_ptr<CallbackBase> mGlRenderFrameCallback;
+  std::unique_ptr<CallbackBase> mGlTerminateCallback;
+  std::unique_ptr<CallbackBase> mResizeCallback;
+
+  bool mDepth : 1;
+  bool mStencil : 1;
+  int  mMSAA;
+  int  mGraphicsApiVersion;
+
+  Dali::ConditionalWait mConditionalWait;
+  volatile unsigned int mIsThreadStarted;   ///< Whether this thread has been started.
+  volatile unsigned int mIsThreadStopped;   ///< Stop render thread. It means this render thread will be destroyed.
+  volatile unsigned int mIsThreadPaused;    ///< Sleep render thread by pause.
+  volatile unsigned int mIsRenderRequested; ///< Request rendering once
+  volatile unsigned int mRenderingMode;     ///< Rendering Mode, 0: Continuous, 1:OnDemand
+  volatile unsigned int mIsSurfaceResized;  ///< Invoke ResizeCallback when NativeImageSurface is resized.
+
+  uint64_t mDefaultFrameDurationNanoseconds; ///< Default duration of a frame (used for sleeping if not enough time elapsed). Not protected by lock, but written to rarely so not worth adding a lock when reading.
+};
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_GL_SURFACE_VIEW_THREAD_H
index 7f02340..f1aa866 100644 (file)
@@ -109,6 +109,8 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/controls/video-view/video-view-impl.cpp
    ${toolkit_src_dir}/controls/web-view/web-view-impl.cpp
    ${toolkit_src_dir}/controls/camera-view/camera-view-impl.cpp
    ${toolkit_src_dir}/controls/video-view/video-view-impl.cpp
    ${toolkit_src_dir}/controls/web-view/web-view-impl.cpp
    ${toolkit_src_dir}/controls/camera-view/camera-view-impl.cpp
+   ${toolkit_src_dir}/controls/gl-view/gl-view-impl.cpp
+   ${toolkit_src_dir}/controls/gl-view/gl-view-render-thread.cpp
    ${toolkit_src_dir}/accessibility-manager/accessibility-manager-impl.cpp
 
    ${toolkit_src_dir}/feedback/feedback-style.cpp
    ${toolkit_src_dir}/accessibility-manager/accessibility-manager-impl.cpp
 
    ${toolkit_src_dir}/feedback/feedback-style.cpp
diff --git a/dali-toolkit/internal/graphics/shaders/gl-view.frag b/dali-toolkit/internal/graphics/shaders/gl-view.frag
new file mode 100644 (file)
index 0000000..9b0b917
--- /dev/null
@@ -0,0 +1,8 @@
+uniform lowp vec4          uColor;
+varying mediump vec2       vTexCoord;
+uniform samplerExternalOES sTexture;
+
+void main()
+{
+  gl_FragColor = texture2D(sTexture, vTexCoord) * uColor;
+}
\ No newline at end of file
diff --git a/dali-toolkit/internal/graphics/shaders/gl-view.vert b/dali-toolkit/internal/graphics/shaders/gl-view.vert
new file mode 100644 (file)
index 0000000..eeec383
--- /dev/null
@@ -0,0 +1,11 @@
+attribute mediump vec2 aPosition;
+uniform mediump mat4   uMvpMatrix;
+uniform mediump vec3   uSize;
+varying mediump vec2   vTexCoord;
+
+void main()
+{
+  vec4 position = vec4(aPosition, 0.0, 1.0) * vec4(uSize, 1.0);
+  vTexCoord     = aPosition + vec2(0.5);
+  gl_Position   = uMvpMatrix * position;
+}
\ No newline at end of file
diff --git a/dali-toolkit/public-api/controls/gl-view/gl-view.cpp b/dali-toolkit/public-api/controls/gl-view/gl-view.cpp
new file mode 100644 (file)
index 0000000..b5f6a8c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/public-api/controls/gl-view/gl-view.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/gl-view/gl-view-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+GlView::GlView()
+{
+}
+
+GlView::GlView(const GlView& GlView) = default;
+
+GlView::GlView(GlView&& rhs) = default;
+
+GlView& GlView::operator=(const GlView& GlView) = default;
+
+GlView& GlView::operator=(GlView&& rhs) = default;
+
+GlView::~GlView()
+{
+}
+
+GlView GlView::New(ColorFormat colorFormat)
+{
+  return Internal::GlView::New(colorFormat);
+}
+
+GlView GlView::DownCast(BaseHandle handle)
+{
+  return Control::DownCast<GlView, Internal::GlView>(handle);
+}
+
+void GlView::RegisterGlCallback(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback)
+{
+  Dali::Toolkit::GetImpl(*this).RegisterGlCallback(initCallback, renderFrameCallback, terminateCallback);
+}
+
+void GlView::SetResizeCallback(CallbackBase* resizeCallback)
+{
+  Dali::Toolkit::GetImpl(*this).SetResizeCallback(resizeCallback);
+}
+
+bool GlView::SetGraphicsConfig(bool depth, bool stencil, int msaa, GraphicsApiVersion version)
+{
+  return Dali::Toolkit::GetImpl(*this).SetGraphicsConfig(depth, stencil, msaa, version);
+}
+
+void GlView::SetRenderingMode(RenderingMode mode)
+{
+  Dali::Toolkit::GetImpl(*this).SetRenderingMode(mode);
+}
+
+Dali::Toolkit::GlView::RenderingMode GlView::GetRenderingMode() const
+{
+  return Dali::Toolkit::GetImpl(*this).GetRenderingMode();
+}
+
+void GlView::RenderOnce()
+{
+  Dali::Toolkit::GetImpl(*this).RenderOnce();
+}
+
+GlView::GlView(Internal::GlView& implementation)
+: Control(implementation)
+{
+}
+
+GlView::GlView(Dali::Internal::CustomActor* internal)
+: Control(internal)
+{
+  VerifyCustomActorPointer<Internal::GlView>(internal);
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/public-api/controls/gl-view/gl-view.h b/dali-toolkit/public-api/controls/gl-view/gl-view.h
new file mode 100644 (file)
index 0000000..be13db9
--- /dev/null
@@ -0,0 +1,240 @@
+#ifndef DALI_TOOLKIT_GL_VIEW_H
+#define DALI_TOOLKIT_GL_VIEW_H
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal DALI_INTERNAL
+{
+class GlView;
+}
+
+/**
+ * @brief GlView is a class for rendering with GL
+ *
+ * GlView allows drawing with OpenGL.
+ * GlView creates a GL context, a GL surface and a render thread.
+ * The render thread invokes user's callbacks.
+ *
+ */
+class DALI_TOOLKIT_API GlView : public Dali::Toolkit::Control
+{
+public:
+  /**
+   * @brief Enumeration for rendering mode
+   *
+   * This Enumeration is used to choose the rendering mode.
+   * It has two options.
+   * One of them is continuous mode. It is rendered continuously.
+   * The other is on demand mode. It is rendered by application.
+   */
+  enum class RenderingMode
+  {
+    CONTINUOUS, ///< continuous mode
+    ON_DEMAND   ///< on demand by application
+  };
+
+  /**
+   * @brief Enumeration for Graphics API version
+   *
+   * This Enumeration is used to set a GLES version for EGL configuration.
+   */
+  enum class GraphicsApiVersion
+  {
+    GLES_VERSION_2_0 = 0, ///< GLES version 2.0
+    GLES_VERSION_3_0,     ///< GLES version 3.0
+  };
+
+  /**
+   * @brief Enumeration for color buffer format
+   *
+   * This Enumeration is used to set a color buffer format of GlView
+   */
+  enum class ColorFormat
+  {
+    RGB888,  ///< 8 red bits, 8 green bits, 8 blue bits
+    RGBA8888 ///< 8 red bits, 8 green bits, 8 blue bits, alpha 8 bits
+  };
+
+  /**
+   * @brief Creates a GlView control.
+   * @param[in] colorFormat the format of the color buffer.
+   * @return A handle to a GlView control
+   */
+  static GlView New(ColorFormat colorFormat);
+
+  /**
+   * @brief Creates an uninitialized GlView.
+   */
+  GlView();
+
+  /**
+   * @brief Destructor.
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~GlView();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @param[in] GlView GlView to copy. The copied GlView will point at the same implementation
+   */
+  GlView(const GlView& GlView);
+
+  /**
+   * @brief Move constructor
+   *
+   * @param[in] rhs A reference to the moved handle
+   */
+  GlView(GlView&& rhs);
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @param[in] GlView The GlView to assign from
+   * @return A reference to this
+   */
+  GlView& operator=(const GlView& GlView);
+
+  /**
+   * @brief Move assignment
+   *
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this
+   */
+  GlView& operator=(GlView&& rhs);
+
+  /**
+   * @brief Downcasts a handle to GlView handle.
+   *
+   * If handle points to a GlView, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @param[in] handle Handle to an object
+   * @return Handle to a GlView or an uninitialized handle
+   */
+  static GlView DownCast(BaseHandle handle);
+
+  /**
+   * @brief Registers GL callback functions for GlView.
+   *
+   * @param[in] initCallback  the callback function to create GL resources.
+   * @param[in] renderFrameCallback the callback function to render for the frame.
+   * @param[in] terminateCallback the callback function to clean-up GL resources.
+   *
+   * A initCallback of the following type have to be used:
+   * @code
+   *   void intializeGL();
+   * @endcode
+   * This callback will be called before renderFrame callback is called once.
+   *
+   * A renderFrameCallback of the following type have to be used:
+   * @code
+   *   int renderFrameGL();
+   * @endcode
+   * If the return value of this callback is not 0, the eglSwapBuffers() will be called.
+   *
+   * A terminateCallback of the following type have to be used:
+   * @code
+   *   void terminateGL();
+   * @endcode
+   * This callback is called when GlView is deleted.
+   *
+   * @note Ownership of the callbacks is passed onto this class.
+   * <b>You can't call Dali APIs in your callbacks because it is invoked in GlView's own render thread.</b>
+   * And this must be called before adding GlView to the scene.
+   */
+  void RegisterGlCallback(CallbackBase* initCallback, CallbackBase* renderFrameCallback, CallbackBase* terminateCallback);
+
+  /**
+   * @brief Sets the ResizeCallback of the GlView.
+   * When GlView is resized, ResizeCallback would be invoked.
+   * You can get the resized width and height of the GlView.
+   *
+   * @param[in] resizeCallback The ResizeCallback function
+   *
+   * A resizeCallback of the following type have to be used:
+   * @code
+   *   void resizeCallback(int width, int height);
+   * @endcode
+   *
+   * @note Ownership of the callback is passed onto this class.
+   * <b>You can't call Dali APIs in your callback because it is invoked in GlView's own render thread.</b>
+   * And this must be called before adding GlView to the scene.
+   */
+  void SetResizeCallback(CallbackBase* resizeCallback);
+
+  /**
+   * @brief Sets the rendering mode.
+   *
+   * @param[in] mode the rendering mode for GlView
+   *
+   * @note The default Rendering mode is CONTINUOUS.
+   * If ON_DEMAND mode is set, it is rendered by RenderOnce()
+   */
+  void SetRenderingMode(RenderingMode mode);
+
+  /**
+   * @brief Gets the rendering mode.
+   */
+  RenderingMode GetRenderingMode() const;
+
+  /**
+   * @brief Sets egl configuration for GlView
+   *
+   * @param[in] depth the flag of depth buffer. If the value is true, 24bit depth buffer is enabled.
+   * @param[in] stencil the flag of stencil. If the value is true, 8bit stencil buffer is enabled.
+   * @param[in] msaa the expected sampling number per pixel.
+   * @param[in] version the graphics API version
+   * @return True if the config exists, false otherwise.
+   */
+  bool SetGraphicsConfig(bool depth, bool stencil, int msaa, GraphicsApiVersion version);
+
+  /**
+   * @brief Renders once more even if GL render functions are not added to idler.
+   * @note Will not work if the window is hidden or GL render functions are added to idler
+   */
+  void RenderOnce();
+
+public: // Not intended for application developers
+  /// @cond internal
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   * @param[in] implementation The GlView implementation
+   */
+  DALI_INTERNAL GlView(Internal::GlView& implementation);
+
+  /**
+   * @brief Allows the creation of this GlView from an Internal::CustomActor pointer.
+   * @param[in] internal A pointer to the internal CustomActor
+   */
+  DALI_INTERNAL GlView(Dali::Internal::CustomActor* internal);
+  /// @endcond
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_GL_VIEW_H
index 7b72745..ffd306b 100644 (file)
@@ -27,6 +27,7 @@ SET( public_api_src_files
   ${public_api_src_dir}/controls/text-controls/text-field.cpp
   ${public_api_src_dir}/controls/video-view/video-view.cpp
   ${public_api_src_dir}/controls/camera-view/camera-view.cpp
   ${public_api_src_dir}/controls/text-controls/text-field.cpp
   ${public_api_src_dir}/controls/video-view/video-view.cpp
   ${public_api_src_dir}/controls/camera-view/camera-view.cpp
+  ${public_api_src_dir}/controls/gl-view/gl-view.cpp
   ${public_api_src_dir}/image-loader/image.cpp
   ${public_api_src_dir}/image-loader/image-url.cpp
   ${public_api_src_dir}/image-loader/async-image-loader.cpp
   ${public_api_src_dir}/image-loader/image.cpp
   ${public_api_src_dir}/image-loader/image-url.cpp
   ${public_api_src_dir}/image-loader/async-image-loader.cpp
@@ -141,6 +142,10 @@ SET( public_api_camera_view_header_files
   ${public_api_src_dir}/controls/camera-view/camera-view.h
 )
 
   ${public_api_src_dir}/controls/camera-view/camera-view.h
 )
 
+SET( public_api_gl_view_header_files
+  ${public_api_src_dir}/controls/gl-view/gl-view.h
+)
+
 SET( public_api_visuals_header_files
   ${public_api_src_dir}/visuals/border-visual-properties.h
   ${public_api_src_dir}/visuals/color-visual-properties.h
 SET( public_api_visuals_header_files
   ${public_api_src_dir}/visuals/border-visual-properties.h
   ${public_api_src_dir}/visuals/color-visual-properties.h
@@ -186,4 +191,5 @@ SET( PUBLIC_API_HEADERS ${PUBLIC_API_HEADERS}
   ${public_api_video_view_header_files}
   ${public_api_visuals_header_files}
   ${public_api_camera_view_header_files}
   ${public_api_video_view_header_files}
   ${public_api_visuals_header_files}
   ${public_api_camera_view_header_files}
+  ${public_api_gl_view_header_files}
 )
 )