[dali_2.0.3] Merge branch 'devel/master' 30/248530/1
authorRichard Huang <r.huang@samsung.com>
Fri, 27 Nov 2020 11:57:40 +0000 (11:57 +0000)
committerRichard Huang <r.huang@samsung.com>
Fri, 27 Nov 2020 11:57:40 +0000 (11:57 +0000)
Change-Id: I6a02abaa41405a5e288843ec7578caa669fb7a3d

23 files changed:
automated-tests/src/dali/CMakeLists.txt
automated-tests/src/dali/dali-test-suite-utils/dali-test-suite-utils.h
automated-tests/src/dali/utc-Dali-Scene.cpp
automated-tests/src/dali/utc-Dali-Semaphore.cpp [new file with mode: 0644]
dali/devel-api/file.list
dali/devel-api/threading/semaphore.h [new file with mode: 0644]
dali/integration-api/scene.cpp
dali/integration-api/scene.h
dali/internal/event/actors/camera-actor-impl.cpp
dali/internal/event/actors/camera-actor-impl.h
dali/internal/event/common/scene-impl.cpp
dali/internal/event/common/scene-impl.h
dali/internal/render/common/render-instruction.h
dali/internal/render/common/render-manager.cpp
dali/internal/render/common/render-manager.h
dali/internal/render/gl-resources/context.cpp
dali/internal/render/gl-resources/context.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/internal/update/render-tasks/scene-graph-camera.cpp
dali/internal/update/render-tasks/scene-graph-camera.h
dali/public-api/dali-core-version.cpp
packaging/dali.spec

index bab6fb6..67aceac 100644 (file)
@@ -47,6 +47,7 @@ SET(TC_SOURCES
         utc-Dali-Matrix3.cpp
         utc-Dali-MeshMaterial.cpp
         utc-Dali-Mutex.cpp
+        utc-Dali-Semaphore.cpp
         utc-Dali-ObjectRegistry.cpp
         utc-Dali-PanGesture.cpp
         utc-Dali-PanGestureDetector.cpp
index 2c1c703..b8de978 100644 (file)
@@ -378,6 +378,34 @@ inline void DALI_TEST_PRINT_ASSERT(DaliException& e)
     DALI_TEST_ASSERT(e, assertstring, TEST_LOCATION);                                                       \
   }
 
+/**
+ * Test that given piece of code triggers an exception
+ * Fails the test if the exception didn't occur.
+ * Turns off logging during the execution of the code to avoid excessive false positive log output from the assertions
+ * @param expressions code to execute
+ * @param except the exception expected in the assert
+ */
+#define DALI_TEST_THROWS(expressions, except)                                                               \
+  try                                                                                                       \
+  {                                                                                                         \
+    TestApplication::EnableLogging(false);                                                                  \
+    expressions;                                                                                            \
+    TestApplication::EnableLogging(true);                                                                   \
+    fprintf(stderr, "Test failed in %s, expected exception: '%s' didn't occur\n", __FILELINE__, #except);   \
+    tet_result(TET_FAIL);                                                                                   \
+    throw("TET_FAIL");                                                                                      \
+  }                                                                                                         \
+  catch(except &)                                                                                           \
+  {                                                                                                         \
+    tet_result(TET_PASS);                                                                                   \
+  }                                                                                                         \
+  catch(...)                                                                                                \
+  {                                                                                                         \
+    fprintf(stderr, "Test failed in %s, unexpected exception\n", __FILELINE__);                             \
+    tet_result(TET_FAIL);                                                                                   \
+    throw;                                                                                                  \
+  }
+
 // Functor to test whether an Applied signal is emitted
 struct ConstraintAppliedCheck
 {
index 2fb7d3c..688f7c3 100644 (file)
@@ -1050,6 +1050,232 @@ int UtcDaliSceneSurfaceResizedAdditionalScene(void)
   END_TEST;
 }
 
+#define CLIPPING_RECT_X          (16)
+#define CLIPPING_RECT_Y          (768)
+#define CLIPPING_RECT_WIDTH      (32)
+#define CLIPPING_RECT_HEIGHT     (32)
+
+int UtcDaliSceneSurfaceRotatedWithAngle0(void)
+{
+  tet_infoline("Ensure rotation of the surface is handled properly with Angle 0");
+
+  TestApplication application(
+    TestApplication::DEFAULT_SURFACE_WIDTH,
+    TestApplication::DEFAULT_SURFACE_HEIGHT,
+    TestApplication::DEFAULT_HORIZONTAL_DPI,
+    TestApplication::DEFAULT_VERTICAL_DPI,
+    true,
+    true);
+
+  const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams());
+
+  std::vector<Rect<int>> damagedRects;
+  Rect<int>              clippingRect;
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  Actor actor = CreateRenderableActor();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+
+  damagedRects.clear();
+  application.GetScene().SurfaceRotated(TestApplication::DEFAULT_SURFACE_WIDTH,
+                                        TestApplication::DEFAULT_SURFACE_HEIGHT, 0);
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  clippingRect = Rect<int>(CLIPPING_RECT_X, CLIPPING_RECT_Y, CLIPPING_RECT_WIDTH, CLIPPING_RECT_HEIGHT); // in screen coordinates, includes 3 last frames updates
+  DALI_TEST_EQUALS<Rect<int>>(clippingRect, damagedRects[0], TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  DALI_TEST_EQUALS(clippingRect.x, glScissorParams.x, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.y, glScissorParams.y, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.width, glScissorParams.width, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.height, glScissorParams.height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneSurfaceRotatedWithAngle90(void)
+{
+  tet_infoline("Ensure rotation of the surface is handled properly with Angle 90");
+
+  TestApplication application(
+    TestApplication::DEFAULT_SURFACE_WIDTH,
+    TestApplication::DEFAULT_SURFACE_HEIGHT,
+    TestApplication::DEFAULT_HORIZONTAL_DPI,
+    TestApplication::DEFAULT_VERTICAL_DPI,
+    true,
+    true);
+
+  const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams());
+
+  std::vector<Rect<int>> damagedRects;
+  Rect<int>              clippingRect;
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  Actor actor = CreateRenderableActor();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+
+  damagedRects.clear();
+  application.GetScene().SurfaceRotated(TestApplication::DEFAULT_SURFACE_WIDTH,
+                                        TestApplication::DEFAULT_SURFACE_HEIGHT, 90);
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  clippingRect = Rect<int>(CLIPPING_RECT_X, CLIPPING_RECT_Y, CLIPPING_RECT_WIDTH, CLIPPING_RECT_HEIGHT); // in screen coordinates, includes 3 last frames updates
+  DALI_TEST_EQUALS<Rect<int>>(clippingRect, damagedRects[0], TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // It is recalculation for glScissor.
+  // Because surface is rotated and glScissor is called with recalcurated value.
+  clippingRect.x = TestApplication::DEFAULT_SURFACE_HEIGHT - (CLIPPING_RECT_Y + CLIPPING_RECT_HEIGHT);
+  clippingRect.y = CLIPPING_RECT_X;
+  clippingRect.width = CLIPPING_RECT_HEIGHT;
+  clippingRect.height = CLIPPING_RECT_WIDTH;
+
+  DALI_TEST_EQUALS(clippingRect.x, glScissorParams.x, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.y, glScissorParams.y, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.width, glScissorParams.width, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.height, glScissorParams.height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneSurfaceRotatedWithAngle180(void)
+{
+  tet_infoline("Ensure rotation of the surface is handled properly with Angle 180");
+
+  TestApplication application(
+    TestApplication::DEFAULT_SURFACE_WIDTH,
+    TestApplication::DEFAULT_SURFACE_HEIGHT,
+    TestApplication::DEFAULT_HORIZONTAL_DPI,
+    TestApplication::DEFAULT_VERTICAL_DPI,
+    true,
+    true);
+
+  const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams());
+
+  std::vector<Rect<int>> damagedRects;
+  Rect<int>              clippingRect;
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  Actor actor = CreateRenderableActor();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+
+  damagedRects.clear();
+  application.GetScene().SurfaceRotated(TestApplication::DEFAULT_SURFACE_WIDTH,
+                                        TestApplication::DEFAULT_SURFACE_HEIGHT, 180);
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  clippingRect = Rect<int>(CLIPPING_RECT_X, CLIPPING_RECT_Y, CLIPPING_RECT_WIDTH, CLIPPING_RECT_HEIGHT); // in screen coordinates, includes 3 last frames updates
+  DALI_TEST_EQUALS<Rect<int>>(clippingRect, damagedRects[0], TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // It is recalculation for glScissor.
+  // Because surface is rotated and glScissor is called with recalcurated value.
+  clippingRect.x = TestApplication::DEFAULT_SURFACE_WIDTH - (CLIPPING_RECT_X + CLIPPING_RECT_WIDTH);
+  clippingRect.y = TestApplication::DEFAULT_SURFACE_HEIGHT - (CLIPPING_RECT_Y +CLIPPING_RECT_HEIGHT);
+  clippingRect.width = CLIPPING_RECT_WIDTH;
+  clippingRect.height = CLIPPING_RECT_HEIGHT;
+
+  DALI_TEST_EQUALS(clippingRect.x, glScissorParams.x, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.y, glScissorParams.y, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.width, glScissorParams.width, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.height, glScissorParams.height, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneSurfaceRotatedWithAngle270(void)
+{
+  tet_infoline("Ensure rotation of the surface is handled properly with Angle 270");
+
+  TestApplication application(
+    TestApplication::DEFAULT_SURFACE_WIDTH,
+    TestApplication::DEFAULT_SURFACE_HEIGHT,
+    TestApplication::DEFAULT_HORIZONTAL_DPI,
+    TestApplication::DEFAULT_VERTICAL_DPI,
+    true,
+    true);
+
+  const TestGlAbstraction::ScissorParams& glScissorParams(application.GetGlAbstraction().GetScissorParams());
+
+  std::vector<Rect<int>> damagedRects;
+  Rect<int>              clippingRect;
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+
+  DALI_TEST_EQUALS(damagedRects.size(), 0, TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  Actor actor = CreateRenderableActor();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3(16.0f, 16.0f, 0.0f));
+  actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+
+  damagedRects.clear();
+  application.GetScene().SurfaceRotated(TestApplication::DEFAULT_SURFACE_WIDTH,
+                                        TestApplication::DEFAULT_SURFACE_HEIGHT, 270);
+  application.SendNotification();
+  application.PreRenderWithPartialUpdate(TestApplication::RENDER_FRAME_INTERVAL, nullptr, damagedRects);
+  DALI_TEST_EQUALS(damagedRects.size(), 1, TEST_LOCATION);
+
+  clippingRect = Rect<int>(CLIPPING_RECT_X, CLIPPING_RECT_Y, CLIPPING_RECT_WIDTH, CLIPPING_RECT_HEIGHT); // in screen coordinates, includes 3 last frames updates
+  DALI_TEST_EQUALS<Rect<int>>(clippingRect, damagedRects[0], TEST_LOCATION);
+  application.RenderWithPartialUpdate(damagedRects, clippingRect);
+
+  // It is recalculation for glScissor.
+  // Because surface is rotated and glScissor is called with recalcurated value.
+  clippingRect.x = CLIPPING_RECT_Y;
+  clippingRect.y = TestApplication::DEFAULT_SURFACE_WIDTH - (CLIPPING_RECT_X + CLIPPING_RECT_WIDTH);
+  clippingRect.width = CLIPPING_RECT_HEIGHT;
+  clippingRect.height = CLIPPING_RECT_WIDTH;
+
+  DALI_TEST_EQUALS(clippingRect.x, glScissorParams.x, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.y, glScissorParams.y, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.width, glScissorParams.width, TEST_LOCATION);
+  DALI_TEST_EQUALS(clippingRect.height, glScissorParams.height, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliSceneKeyEventGeneratedSignalP(void)
 {
   TestApplication          application;
diff --git a/automated-tests/src/dali/utc-Dali-Semaphore.cpp b/automated-tests/src/dali/utc-Dali-Semaphore.cpp
new file mode 100644 (file)
index 0000000..4cda5e6
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 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 <dali-test-suite-utils.h>
+#include <dali/devel-api/threading/semaphore.h>
+#include <dali/public-api/dali-core.h>
+#include <algorithm>
+#include <chrono>
+#include <stdexcept>
+#include <thread>
+#include <future>
+
+int UtcDaliSemaphoreTryAcquire(void)
+{
+  using namespace std::chrono_literals;
+  constexpr auto waitTime{100ms};
+
+  tet_infoline("Testing Dali::Semaphore try acquire methods");
+  Dali::Semaphore<3> sem(0);
+
+  DALI_TEST_EQUALS(false, sem.TryAcquire(), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, sem.TryAcquireFor(waitTime), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, sem.TryAcquireUntil(std::chrono::system_clock::now() + waitTime), TEST_LOCATION);
+
+  sem.Release(3);
+
+  DALI_TEST_EQUALS(true, sem.TryAcquire(), TEST_LOCATION);
+  DALI_TEST_EQUALS(true, sem.TryAcquireFor(waitTime), TEST_LOCATION);
+  DALI_TEST_EQUALS(true, sem.TryAcquireUntil(std::chrono::system_clock::now() + waitTime), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(false, sem.TryAcquire(), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, sem.TryAcquireFor(waitTime), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, sem.TryAcquireUntil(std::chrono::system_clock::now() + waitTime), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSemaphoreInvalidArguments(void)
+{
+  tet_infoline("Testing Dali::Semaphore invalid arguments");
+
+  Dali::Semaphore<2> sem(0);
+
+  DALI_TEST_THROWS(sem.Release(3), std::invalid_argument);
+  DALI_TEST_THROWS(sem.Release(-1), std::invalid_argument);
+  sem.Release(1);
+  DALI_TEST_THROWS(sem.Release(2), std::invalid_argument);
+  sem.Release(1);
+  DALI_TEST_THROWS(sem.Release(1), std::invalid_argument);
+
+  DALI_TEST_THROWS(Dali::Semaphore<1>(2), std::invalid_argument);
+  DALI_TEST_THROWS(Dali::Semaphore<>(-1), std::invalid_argument);
+
+  END_TEST;
+}
+
+int UtcDaliSemaphoreAcquire(void)
+{
+  tet_infoline("Testing Dali::Semaphore multithread acquire");
+
+  using namespace std::chrono_literals;
+
+  constexpr std::ptrdiff_t numTasks{2};
+
+  auto f = [](Dali::Semaphore<numTasks> &sem, bool &flag)
+  {
+    sem.Acquire();
+    flag = true;
+  };
+
+  auto flag1{false}, flag2{false};
+  Dali::Semaphore<numTasks> sem(0);
+
+  auto fut1 = std::async(std::launch::async, f, std::ref(sem), std::ref(flag1));
+  auto fut2 = std::async(std::launch::async, f, std::ref(sem), std::ref(flag2));
+
+  DALI_TEST_EQUALS(std::future_status::timeout, fut1.wait_for(100ms), TEST_LOCATION);
+  DALI_TEST_EQUALS(std::future_status::timeout, fut2.wait_for(100ms), TEST_LOCATION);
+  DALI_TEST_EQUALS(false, flag1, TEST_LOCATION);
+  DALI_TEST_EQUALS(false, flag2, TEST_LOCATION);
+  sem.Release(numTasks);
+  fut1.wait();
+  DALI_TEST_EQUALS(true, flag1, TEST_LOCATION);
+  fut2.wait();
+  DALI_TEST_EQUALS(true, flag2, TEST_LOCATION);
+
+  END_TEST;
+}
index d65e823..1973073 100644 (file)
@@ -128,6 +128,7 @@ SET( devel_api_core_scripting_header_files
 SET( devel_api_core_threading_header_files
   ${devel_api_src_dir}/threading/conditional-wait.h
   ${devel_api_src_dir}/threading/mutex.h
+  ${devel_api_src_dir}/threading/semaphore.h
   ${devel_api_src_dir}/threading/thread.h
   ${devel_api_src_dir}/threading/thread-pool.h
 )
diff --git a/dali/devel-api/threading/semaphore.h b/dali/devel-api/threading/semaphore.h
new file mode 100644 (file)
index 0000000..9ecb41a
--- /dev/null
@@ -0,0 +1,180 @@
+#pragma once
+
+/*
+ * Copyright (c) 2020 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/public-api/common/dali-common.h>
+
+// EXTERNAL INCLUDES
+#include <mutex>
+#include <condition_variable>
+#include <stdexcept>
+#include <sstream>
+#include <limits>
+
+namespace Dali
+{
+/**
+ * @brief  Class that implements a C++20 counting_semaphore like interface
+ */
+template<std::ptrdiff_t LeastMaxValue = std::numeric_limits<std::ptrdiff_t>::max()>
+class Semaphore
+{
+public:
+  /**
+   * @brief Returns the internal counter's maximum possible value,
+   *        which is greater than or equal to LeastMaxValue.
+   *
+   * @return the maximum value of the semaphore
+   */
+  static constexpr std::ptrdiff_t Max() noexcept
+  {
+    return LeastMaxValue;
+  }
+
+  /**
+   * @brief class constructor
+   *
+   * @param[in] desired the desired initial value of the semaphore
+   */
+  explicit Semaphore(std::ptrdiff_t desired)
+    : mCount(desired)
+  {
+    if (mCount < 0 || mCount > Max())
+    {
+      ThrowInvalidParamException(desired);
+    }
+  }
+
+  /**
+   * @brief Atomically increments the internal counter by the value of update.
+   *
+   * Any thread waiting for the counter to be greater than 0 will subsequently
+   * be unlocked.
+   *
+   * @param[in] update value to increment the semaphore
+   */
+  void Release(std::ptrdiff_t update = 1)
+  {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (update < 0 || update > Max() - mCount)
+    {
+      ThrowInvalidParamException(update);
+    }
+
+    mCount += update;
+    while (update--)
+    {
+      mCondVar.notify_one();
+    }
+  }
+
+  /**
+   * @brief Atomically decrements the internal counter by one if it is greater
+   *        than zero; otherwise blocks until it is greater than zero and can
+   *        successfully decrement the internal counter.
+   */
+  void Acquire()
+  {
+    std::unique_lock<std::mutex> lock(mLock);
+    while (mCount == 0)
+    {
+      mCondVar.wait(lock);
+    }
+    --mCount;
+  }
+
+  /**
+   * @brief Tries to atomically decrement the internal counter by one if it is
+   *        greater than zero; no blocking occurs regardless.
+   *
+   * @return true if it decremented the counter, otherwise false.
+   */
+  bool TryAcquire()
+  {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mCount)
+    {
+      --mCount;
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * @brief Tries to atomically decrement the internal counter by one if it is greater
+   *        than zero; otherwise blocks until it is greater than zero can successfully
+   *        decrement the internal counter, or the relTime duration has been exceeded.
+   *
+   * @param[in] relTime the minimum duration the function must wait for to fail
+   *
+   * @return true if it decremented the internal counter, otherwise false
+   */
+  template<typename Rep, typename Period>
+  bool TryAcquireFor(const std::chrono::duration<Rep, Period> &relTime)
+  {
+    std::unique_lock<std::mutex> lock(mLock);
+    while (mCount == 0)
+    {
+      if (mCondVar.wait_for(lock, relTime) == std::cv_status::timeout)
+      {
+        return false;
+      }
+    }
+    --mCount;
+    return true;
+  }
+
+  /**
+   * @brief Tries to atomically decrement the internal counter by one if it is greater
+   *        than zero; otherwise blocks until it is greater than zero can successfully
+   *        decrement the internal counter, or the absTime duration point has been passed.
+   *
+   * @param[in] absTime the earliest time the function must wait until in order to fail
+   *
+   * @return true if it decremented the internal counter, otherwise false
+   */
+  template<typename Clock, typename Duration>
+  bool TryAcquireUntil(const std::chrono::time_point<Clock, Duration> &absTime)
+  {
+    std::unique_lock<std::mutex> lock(mLock);
+    while (mCount == 0)
+    {
+      if (mCondVar.wait_until(lock, absTime) == std::cv_status::timeout)
+      {
+        return false;
+      }
+    }
+    --mCount;
+    return true;
+  }
+
+private:
+  void ThrowInvalidParamException(std::ptrdiff_t param) const
+  {
+    std::stringstream ss("Invalid parameter value ");
+    ss << param;
+    throw std::invalid_argument(ss.str());
+  }
+
+  std::condition_variable mCondVar;
+  std::mutex mLock;
+  std::ptrdiff_t mCount;
+};
+}
index 9a6af8e..8650fea 100644 (file)
@@ -27,9 +27,9 @@ namespace Dali
 {
 namespace Integration
 {
-Scene Scene::New(Size size)
+Scene Scene::New(Size size, int orientation)
 {
-  Internal::ScenePtr internal = Internal::Scene::New(size);
+  Internal::ScenePtr internal = Internal::Scene::New(size, orientation);
   return Scene(internal.Get());
 }
 
@@ -156,6 +156,11 @@ void Scene::GetFramePresentedCallback(FrameCallbackContainer& callbacks)
   GetImplementation(*this).GetFramePresentedCallback(callbacks);
 }
 
+void Scene::SurfaceRotated(float width, float height, int orientation)
+{
+  GetImplementation(*this).SurfaceRotated(width, height, orientation);
+}
+
 Scene::EventProcessingFinishedSignalType& Scene::EventProcessingFinishedSignal()
 {
   return GetImplementation(*this).EventProcessingFinishedSignal();
index 7d97dd3..d5920da 100644 (file)
@@ -66,10 +66,11 @@ public:
    * @brief Create an initialized Scene handle.
    *
    * @param[in] size The size of the set surface for this scene
+   * @param[in] orientation The rotated angle of the set surface for this scene
    *
    * @return a handle to a newly allocated Dali resource.
    */
-  static Scene New(Size size);
+  static Scene New(Size size, int orientation = 0);
 
   /**
    * @brief Downcast an Object handle to Scene handle.
@@ -284,6 +285,15 @@ public:
   void GetFramePresentedCallback(FrameCallbackContainer& callbacks);
 
   /**
+   * @brief Informs the scene that the set surface has been rotated.
+   *
+   * @param[in] width The width of rotated surface
+   * @param[in] height The height of rotated surface
+   * @param[in] orientation The orientation of rotated surface
+   */
+  void SurfaceRotated(float width, float height, int orientation);
+
+  /**
    * @brief This signal is emitted just after the event processing is finished.
    *
    * @return The signal to connect to
index fecea3c..b81341a 100644 (file)
@@ -524,6 +524,12 @@ const SceneGraph::Camera* CameraActor::GetCamera() const
   return mSceneObject;
 }
 
+void CameraActor::RotateProjection(int rotationAngle)
+{
+  // sceneObject is being used in a separate thread; queue a message to set
+  RotateProjectionMessage(GetEventThreadServices(), *mSceneObject, rotationAngle);
+}
+
 void CameraActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
 {
   if(index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
index 4463ba1..97b62bd 100644 (file)
@@ -204,6 +204,13 @@ public:
    */
   const SceneGraph::Camera* GetCamera() const;
 
+  /**
+   * Rotate the projection.
+   * It is used in case that the target buffer direction is different from the window direction.
+   * @param [in] rotationAngle The rotation angle
+   */
+  void RotateProjection(int rotationAngle);
+
 public: // properties
 
   /**
index 496fba4..4ab5142 100755 (executable)
@@ -43,23 +43,24 @@ namespace Dali
 namespace Internal
 {
 
-ScenePtr Scene::New( Size size )
+ScenePtr Scene::New(Size size, int orientation)
 {
   ScenePtr scene = new Scene;
 
   // Second-phase construction
-  scene->Initialize( size );
+  scene->Initialize(size, orientation);
 
   return scene;
 }
 
 Scene::Scene()
-: mSceneObject( nullptr ),
+: mSceneObject(nullptr),
   mSize(), // Don't set the proper value here, this will be set when the surface is set later
   mDpi(),
-  mBackgroundColor( DEFAULT_BACKGROUND_COLOR ),
-  mDepthTreeDirty( false ),
-  mEventProcessor( *this, ThreadLocalStorage::GetInternal()->GetGestureEventProcessor() )
+  mBackgroundColor(DEFAULT_BACKGROUND_COLOR),
+  mDepthTreeDirty(false),
+  mEventProcessor(*this, ThreadLocalStorage::GetInternal()->GetGestureEventProcessor()),
+  mSurfaceOrientation(0)
 {
 }
 
@@ -94,7 +95,7 @@ Scene::~Scene()
   // When this destructor is called, the scene has either already been removed from Core or Core has already been destroyed
 }
 
-void Scene::Initialize( Size size )
+void Scene::Initialize(Size size, int orientation)
 {
   ThreadLocalStorage* tls = ThreadLocalStorage::GetInternal();
 
@@ -130,8 +131,9 @@ void Scene::Initialize( Size size )
   // Create the default render-task and ensure clear is enabled on it to show the background color
   RenderTaskPtr renderTask = mRenderTaskList->CreateTask( mRootLayer.Get(), mDefaultCamera.Get() );
   renderTask->SetClearEnabled(true);
+  mSurfaceOrientation = orientation;
 
-  SurfaceResized( size.width, size.height );
+  SurfaceRotated( size.width, size.height, mSurfaceOrientation );
 
   // Create scene graph object
   mSceneObject = new SceneGraph::Scene();
@@ -199,27 +201,11 @@ Actor& Scene::GetDefaultRootActor()
   return *mRootLayer;
 }
 
-void Scene::SurfaceResized( float width, float height )
+void Scene::SurfaceResized(float width, float height)
 {
-  if( ( fabsf( mSize.width - width ) > Math::MACHINE_EPSILON_1 ) || ( fabsf( mSize.height - height ) > Math::MACHINE_EPSILON_1 ) )
+  if((fabsf(mSize.width - width) > Math::MACHINE_EPSILON_1) || (fabsf(mSize.height - height) > Math::MACHINE_EPSILON_1))
   {
-    Rect< int32_t > newSize( 0, 0, static_cast< int32_t >( width ), static_cast< int32_t >( height ) ); // truncated
-
-    mSize.width = width;
-    mSize.height = height;
-
-    // Calculates the aspect ratio, near and far clipping planes, field of view and camera Z position.
-    mDefaultCamera->SetPerspectiveProjection( mSize );
-
-    mRootLayer->SetSize( mSize.width, mSize.height );
-
-    ThreadLocalStorage* tls = ThreadLocalStorage::GetInternal();
-    SceneGraph::UpdateManager& updateManager = tls->GetUpdateManager();
-    SetDefaultSurfaceRectMessage( updateManager, newSize );
-
-    // set default render-task viewport parameters
-    RenderTaskPtr defaultRenderTask = mRenderTaskList->GetTask( 0u );
-    defaultRenderTask->SetViewport( newSize );
+    ChangedSurface(width, height, mSurfaceOrientation);
   }
 }
 
@@ -294,6 +280,43 @@ void Scene::EmitKeyEventSignal(const Dali::KeyEvent& event)
   }
 }
 
+void Scene::SurfaceRotated(float width, float height, int orientation)
+{
+  mSurfaceOrientation = orientation;
+  ChangedSurface(width, height, orientation);
+}
+
+int Scene::GetSurfaceOrientation()
+{
+  return mSurfaceOrientation;
+}
+
+void Scene::ChangedSurface(float width, float height, int orientation)
+{
+  Rect<int32_t> newSize(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height)); // truncated
+
+  mSize.width  = width;
+  mSize.height = height;
+
+  // Calculates the aspect ratio, near and far clipping planes, field of view and camera Z position.
+  mDefaultCamera->SetPerspectiveProjection(mSize);
+  // Set the surface orientation to Default camera for window/screen rotation
+  mDefaultCamera->RotateProjection(orientation);
+
+  mRootLayer->SetSize(width, height);
+
+  ThreadLocalStorage*        tls           = ThreadLocalStorage::GetInternal();
+  SceneGraph::UpdateManager& updateManager = tls->GetUpdateManager();
+  SetDefaultSurfaceRectMessage(updateManager, newSize);
+
+  // Send the surface orientation to render manager for calculating glViewport/Scissor
+  SetDefaultSurfaceOrientationMessage(updateManager, orientation);
+
+  // set default render-task viewport parameters
+  RenderTaskPtr defaultRenderTask = mRenderTaskList->GetTask(0u);
+  defaultRenderTask->SetViewport(newSize);
+}
+
 bool Scene::EmitKeyEventGeneratedSignal(const Dali::KeyEvent& event)
 {
   // Emit the KeyEventGenerated signal when KeyEvent is generated
index 67d08d5..44a5e3b 100755 (executable)
@@ -114,7 +114,7 @@ public:
   /**
    * @copydoc Dali::Integration::Scene::New
    */
-  static ScenePtr New( Size size );
+  static ScenePtr New(Size size, int orientation = 0);
 
   /**
    * virtual destructor
@@ -232,6 +232,24 @@ public:
   SceneGraph::Scene* GetSceneObject() const;
 
   /**
+   * Notify the surface has been rotated.
+   * When the device is rotated or the rotation event is received by display manager,
+   * this function will be called by window implementation.
+   *
+   * @param[in] width The width of rotated surface
+   * @param[in] height The height of rotated surface
+   * @param[in] orientation The orientation of rotated surface
+   */
+  void SurfaceRotated(float width, float height, int orientation);
+
+  /**
+   * @brief Get surface's current orientation
+   *
+   * @return surface orientation
+   */
+  int GetSurfaceOrientation();
+
+  /**
    * Used by the EventProcessor to emit key event signals.
    * @param[in] event The key event.
    */
@@ -338,8 +356,9 @@ private:
    * Second-phase constructor.
    *
    * @param[in] size The size of the set surface
+   * @param[in] orientation The orientation of the set surface for this scene
    */
-  void Initialize( Size size );
+  void Initialize(Size size, int orientation);
 
   // Undefined
   Scene(const Scene&) = delete;
@@ -347,6 +366,15 @@ private:
   // Undefined
   Scene& operator=(const Scene& rhs) = delete;
 
+  /**
+   * Informs the scene that the set surface has been resized or rotated.
+   *
+   * @param[in] width The width of rotated surface
+   * @param[in] height The height of rotated surface
+   * @param[in] orientation The orientation of rotated surface
+   */
+  void ChangedSurface(float width, float height, int orientation);
+
 private:
   Internal::SceneGraph::Scene* mSceneObject;
 
@@ -370,6 +398,9 @@ private:
 
   EventProcessor mEventProcessor;
 
+  // The Surface's orientation
+  int mSurfaceOrientation;
+
   // The key event signal
   Integration::Scene::KeyEventSignalType mKeyEventSignal;
   Integration::Scene::KeyEventGeneratedSignalType   mKeyEventGeneratedSignal;
index 8f59b13..7ebecee 100644 (file)
@@ -122,7 +122,7 @@ public:
   const Matrix* GetProjectionMatrix( BufferIndex index ) const
   {
     // inlined as this is called once per frame per render instruction
-    return &mCamera->GetProjectionMatrix( index );
+    return &mCamera->GetFinalProjectionMatrix(index);
   }
   // for reflection effect
   const Camera* GetCamera() const
index e885c33..3f3d319 100755 (executable)
@@ -77,7 +77,8 @@ struct RenderManager::Impl
     programController( glAbstraction ),
     depthBufferAvailable( depthBufferAvailableParam ),
     stencilBufferAvailable( stencilBufferAvailableParam ),
-    partialUpdateAvailable( partialUpdateAvailableParam )
+    partialUpdateAvailable( partialUpdateAvailableParam ),
+    defaultSurfaceOrientation(0)
   {
      // Create thread pool with just one thread ( there may be a need to create more threads in the future ).
     threadPool = std::unique_ptr<Dali::ThreadPool>( new Dali::ThreadPool() );
@@ -174,6 +175,9 @@ struct RenderManager::Impl
   std::unique_ptr<Dali::ThreadPool>         threadPool;               ///< The thread pool
   Vector<GLuint>                            boundTextures;            ///< The textures bound for rendering
   Vector<GLuint>                            textureDependencyList;    ///< The dependency list of binded textures
+
+  int                                       defaultSurfaceOrientation; ///< defaultSurfaceOrientation for the default surface we are rendering to
+
 };
 
 RenderManager* RenderManager::New( Integration::GlAbstraction& glAbstraction,
@@ -257,6 +261,11 @@ void RenderManager::SetDefaultSurfaceRect(const Rect<int32_t>& rect)
   mImpl->defaultSurfaceRect = rect;
 }
 
+void RenderManager::SetDefaultSurfaceOrientation(int orientation)
+{
+  mImpl->defaultSurfaceOrientation = orientation;
+}
+
 void RenderManager::AddRenderer( OwnerPointer< Render::Renderer >& renderer )
 {
   // Initialize the renderer as we are now in render thread
@@ -822,6 +831,7 @@ void RenderManager::RenderScene( Integration::RenderStatus& status, Integration:
     Rect<int32_t> surfaceRect = mImpl->defaultSurfaceRect;
     Integration::DepthBufferAvailable depthBufferAvailable = mImpl->depthBufferAvailable;
     Integration::StencilBufferAvailable stencilBufferAvailable = mImpl->stencilBufferAvailable;
+    int surfaceOrientation = sceneInternal.GetSurfaceOrientation();
 
     if ( instruction.mFrameBuffer )
     {
@@ -921,6 +931,7 @@ void RenderManager::RenderScene( Integration::RenderStatus& status, Integration:
       {
         viewportRect.Set( 0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight() );
       }
+      surfaceOrientation = 0;
     }
     else // No Offscreen frame buffer rendering
     {
@@ -937,6 +948,9 @@ void RenderManager::RenderScene( Integration::RenderStatus& status, Integration:
       }
     }
 
+    // Set surface orientation
+    mImpl->currentContext->SetSurfaceOrientation(surfaceOrientation);
+
     bool clearFullFrameRect = true;
     if( instruction.mFrameBuffer != nullptr )
     {
@@ -1079,6 +1093,7 @@ void RenderManager::RenderScene( Integration::RenderStatus& status, Integration:
 
   GLenum attachments[] = { GL_DEPTH, GL_STENCIL };
   mImpl->currentContext->InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
+
 }
 
 void RenderManager::PostRender( bool uploadOnly )
index 2e3ea2d..8474a8a 100644 (file)
@@ -131,6 +131,12 @@ public:
   void SetDefaultSurfaceRect( const Rect<int>& rect );
 
   /**
+   * Returns the orintation for the default surface (probably the application window).
+   * @param[in] orientation the surface's orientation.
+   */
+  void SetDefaultSurfaceOrientation(int orientation);
+
+  /**
    * Add a Renderer to the render manager.
    * @param[in] renderer The renderer to add.
    * @post renderer is owned by RenderManager
index f92d0d0..279113f 100644 (file)
@@ -107,7 +107,8 @@ Context::Context( Integration::GlAbstraction& glAbstraction, OwnerContainer< Con
   mClearColor(Color::WHITE),    // initial color, never used until it's been set by the user
   mCullFaceMode( FaceCullingMode::NONE ),
   mViewPort( 0, 0, 0, 0 ),
-  mSceneContexts( contexts )
+  mSceneContexts( contexts ),
+  mSurfaceOrientation(0)
 {
 }
 
index fe4d2dd..a66cee1 100644 (file)
@@ -1580,8 +1580,42 @@ public:
    */
   void Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
   {
-    LOG_GL("Scissor %d %d %d %d\n", x, y, width, height);
-    CHECK_GL( mGlAbstraction, mGlAbstraction.Scissor(x, y, width, height) );
+    GLint cx, cy, cw, ch;
+
+    // scissor's value should be set based on the default system coordinates.
+    // when the surface is rotated, the input valus already were set with the rotated angle.
+    // So, re-calculation is needed.
+    if(mSurfaceOrientation == 90)
+    {
+      cx = mViewPort.height - (y + height);
+      cy = x;
+      cw = height;
+      ch = width;
+    }
+    else if(mSurfaceOrientation == 180)
+    {
+      cx = mViewPort.width - (x + width);
+      cy = mViewPort.height - (y + height);
+      cw = width;
+      ch = height;
+    }
+    else if(mSurfaceOrientation == 270)
+    {
+      cx = y;
+      cy = mViewPort.width - (x + width);
+      cw = height;
+      ch = width;
+    }
+    else
+    {
+      cx = x;
+      cy = y;
+      cw = width;
+      ch = height;
+    }
+
+    LOG_GL("Scissor %d %d %d %d\n", cx, cy, cw, ch);
+    CHECK_GL(mGlAbstraction, mGlAbstraction.Scissor(cx, cy, cw, ch));
   }
 
   /**
@@ -1750,16 +1784,33 @@ public:
   void Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
   {
     // check if its same as already set
-    Rect<int> newViewport( x, y, width, height );
+    GLsizei cw, ch;
+
+    // viewport's value shoud be set based on the default system size.
+    // when the surface is rotated, the input width and height already were swapped.
+    // So, re-swapping is needed.
+    if(mSurfaceOrientation == 90 || mSurfaceOrientation == 270)
+    {
+      cw = height;
+      ch = width;
+    }
+    else
+    {
+      cw = width;
+      ch = height;
+    }
+
+    // User uses the rotated viewport size.
+    Rect<int> newViewport(x, y, width, height);
 
     // Temporarily disable the viewport caching, as the implementation of GLES driver in Tizen platform
     // share a global viewport between multiple contexts, therefore glViewport has to be called every
     // time after glBindFramebuffer regardless of the same vewport size in the same context.
-//    if( mViewPort != newViewport )
+    //    if( mViewPort != newViewport )
     {
       // set new one
-      LOG_GL("Viewport %d %d %d %d\n", x, y, width, height);
-      CHECK_GL( mGlAbstraction, mGlAbstraction.Viewport(x, y, width, height) );
+      LOG_GL("Viewport %d %d %d %d\n", x, y, cw, ch);
+      CHECK_GL(mGlAbstraction, mGlAbstraction.Viewport(x, y, cw, ch));
       mViewPort = newViewport; // remember new one
     }
   }
@@ -1782,6 +1833,12 @@ public:
     return mMaxTextureSize;
   }
 
+  void SetSurfaceOrientation(int orientation)
+  {
+    LOG_GL( "SetSurfaceOrientation: orientation: %d\n", orientation );
+    mSurfaceOrientation = orientation;
+  }
+
   /**
    * Get the current viewport.
    * @return Viewport rectangle.
@@ -1893,6 +1950,8 @@ private: // Data
   FrameBufferStateCache mFrameBufferStateCache;   ///< frame buffer state cache
 
   OwnerContainer< Context* >* mSceneContexts;      ///< The pointer of the container of contexts for surface rendering
+
+  int mSurfaceOrientation;
 };
 
 } // namespace Internal
index a7ca327..3127eab 100644 (file)
@@ -1148,6 +1148,17 @@ void UpdateManager::SurfaceReplaced( Scene* scene )
   new (slot) DerivedType( &mImpl->renderManager,  &RenderManager::SurfaceReplaced, scene );
 }
 
+void UpdateManager::SetDefaultSurfaceOrientation(int orientation)
+{
+  using DerivedType = MessageValue1<RenderManager, int>;
+
+  // Reserve some memory inside the render queue
+  unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetDefaultSurfaceOrientation, orientation);
+}
+
 void UpdateManager::KeepRendering( float durationSeconds )
 {
   mImpl->keepRenderingSeconds = std::max( mImpl->keepRenderingSeconds, durationSeconds );
index 8f272d8..4f2214a 100644 (file)
@@ -634,6 +634,12 @@ public:
   void SetDefaultSurfaceRect( const Rect<int>& rect );
 
   /**
+   * Set the default surface orientation.
+   * @param[in] orientation The orientation value representing the surface.
+   */
+  void SetDefaultSurfaceOrientation(int orientation);
+
+  /**
    * @copydoc Dali::Stage::KeepRendering()
    */
   void KeepRendering( float durationSeconds );
@@ -1095,6 +1101,17 @@ inline void SurfaceReplacedMessage( UpdateManager& manager, const SceneGraph::Sc
   new (slot) LocalType( &manager, &UpdateManager::SurfaceReplaced, &scene );
 }
 
+inline void SetDefaultSurfaceOrientationMessage(UpdateManager& manager, int orientation)
+{
+  using LocalType = MessageValue1<UpdateManager, int>;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&manager, &UpdateManager::SetDefaultSurfaceOrientation, orientation);
+}
+
 inline void KeepRenderingMessage( UpdateManager& manager, float durationSeconds )
 {
   using LocalType = MessageValue1<UpdateManager, float>;
index e644bb3..d07c8c7 100644 (file)
@@ -161,6 +161,7 @@ const Vector3 Camera::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
 Camera::Camera()
 : mUpdateViewFlag( UPDATE_COUNT ),
   mUpdateProjectionFlag( UPDATE_COUNT ),
+  mProjectionRotation(0),
   mNode( nullptr ),
   mType( DEFAULT_TYPE ),
   mProjectionMode( DEFAULT_MODE ),
@@ -176,7 +177,8 @@ Camera::Camera()
   mTargetPosition( DEFAULT_TARGET_POSITION ),
   mViewMatrix(),
   mProjectionMatrix(),
-  mInverseViewProjection( Matrix::IDENTITY )
+  mInverseViewProjection( Matrix::IDENTITY ),
+  mFinalProjection(Matrix::IDENTITY)
 {
 }
 
@@ -268,8 +270,6 @@ void Camera::SetTargetPosition( const Vector3& targetPosition )
   mUpdateViewFlag = UPDATE_COUNT;
 }
 
-
-
 void VectorReflectedByPlane(Vector4 &out, Vector4 &in, Vector4 &plane)
 {
   float d = float(2.0) * plane.Dot(in);
@@ -331,6 +331,12 @@ void Camera::SetReflectByPlane( const Vector4& plane )
   mUpdateViewFlag = UPDATE_COUNT;
 }
 
+void Camera::RotateProjection(int rotationAngle)
+{
+  mProjectionRotation = rotationAngle;
+  mUpdateViewFlag     = UPDATE_COUNT;
+}
+
 const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
 {
   return mProjectionMatrix[ bufferIndex ];
@@ -346,6 +352,11 @@ const Matrix& Camera::GetInverseViewProjectionMatrix( BufferIndex bufferIndex )
   return mInverseViewProjection[ bufferIndex ];
 }
 
+const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
+{
+  return mFinalProjection[ bufferIndex ];
+}
+
 const PropertyInputImpl* Camera::GetProjectionMatrix() const
 {
   return &mProjectionMatrix;
@@ -654,7 +665,39 @@ uint32_t Camera::UpdateProjection( BufferIndex updateBufferIndex )
         }
       }
 
-      mProjectionMatrix.SetDirty( updateBufferIndex );
+      mProjectionMatrix.SetDirty(updateBufferIndex);
+
+      Matrix& finalProjection = mFinalProjection[updateBufferIndex];
+      finalProjection.SetIdentity();
+
+      Quaternion rotationAngle;
+      switch(mProjectionRotation)
+      {
+        case 90:
+        {
+          rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
+          break;
+        }
+        case 180:
+        {
+          rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
+          break;
+        }
+        case 270:
+        {
+          rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
+          break;
+        }
+        default:
+          rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
+          break;
+      }
+
+      Matrix rotation;
+      rotation.SetIdentity();
+      rotation.SetTransformComponents(Vector3(1.0f, 1.0f, 1.0f), rotationAngle, Vector3(0.0f, 0.0f, 0.0f));
+
+      Matrix::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotation);
     }
     --mUpdateProjectionFlag;
   }
index c5917a0..d752b5c 100644 (file)
@@ -161,6 +161,11 @@ public:
   void SetFarClippingPlane( float farClippingPlane );
 
   /**
+   * @copydoc Dali::Internal::CameraActor::RotateProjection
+   */
+  void RotateProjection(int rotationAngle);
+
+  /**
    * @copydoc Dali::Internal::CameraActor::SetTarget
    */
   void SetTargetPosition( const Vector3& targetPosition );
@@ -224,6 +229,13 @@ public:
   const Matrix& GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const;
 
   /**
+   * Retrieve the final projection-matrix; this is double buffered for input handling.
+   * @param[in] bufferIndex The buffer to read from.
+   * @return The projection-matrix that should be used to render.
+   */
+  const Matrix& GetFinalProjectionMatrix(BufferIndex bufferIndex) const;
+
+  /**
    * Retrieve the projection-matrix property querying interface.
    * @pre The camera is on-stage.
    * @return The projection-matrix property querying interface.
@@ -295,6 +307,7 @@ private:
 
   uint32_t                  mUpdateViewFlag;       ///< This is non-zero if the view matrix requires an update
   uint32_t                  mUpdateProjectionFlag; ///< This is non-zero if the projection matrix requires an update
+  int                       mProjectionRotation;   ///< The rotaion angle of the projection
   const Node*                   mNode;                 ///< The node this scene graph camera belongs to
 
 public:  // PROPERTIES
@@ -323,6 +336,7 @@ public:  // PROPERTIES
 
   DoubleBuffered< FrustumPlanes > mFrustum;               ///< Clipping frustum; double buffered for input handling
   DoubleBuffered< Matrix >        mInverseViewProjection; ///< Inverted viewprojection; double buffered for input handling
+  DoubleBuffered< Matrix >        mFinalProjection;       ///< Final projection matrix; double buffered for input handling
 
 };
 
@@ -460,6 +474,17 @@ inline void SetInvertYAxisMessage( EventThreadServices& eventThreadServices, con
   new (slot) LocalType( &camera, &Camera::SetInvertYAxis, parameter );
 }
 
+inline void RotateProjectionMessage( EventThreadServices& eventThreadServices, const Camera& camera, int parameter )
+{
+  typedef MessageValue1< Camera, int > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new (slot) LocalType( &camera, &Camera::RotateProjection, parameter );
+}
+
 } // namespace SceneGraph
 
 } // namespace Internal
index f77181b..371199a 100644 (file)
@@ -27,7 +27,7 @@ namespace Dali
 {
 const uint32_t    CORE_MAJOR_VERSION = 2;
 const uint32_t    CORE_MINOR_VERSION = 0;
-const uint32_t    CORE_MICRO_VERSION = 2;
+const uint32_t    CORE_MICRO_VERSION = 3;
 const char* const CORE_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 3a6a159..d060792 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2
 Summary:    DALi 3D Engine
-Version:    2.0.2
+Version:    2.0.3
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT