Changed DiscardQueue to templated class
[platform/core/uifw/dali-core.git] / dali / internal / common / core-impl.cpp
index 74549ac..44f193b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/internal/common/core-impl.h>
 
 // INTERNAL INCLUDES
-#include <dali/integration-api/system-overlay.h>
+#include <dali/graphics-api/graphics-controller.h>
 #include <dali/integration-api/core.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/events/event.h>
-#include <dali/integration-api/gl-sync-abstraction.h>
+#include <dali/integration-api/gl-context-helper-abstraction.h>
 #include <dali/integration-api/platform-abstraction.h>
+#include <dali/integration-api/processor-interface.h>
 #include <dali/integration-api/render-controller.h>
+#include <dali/integration-api/trace.h>
 
 #include <dali/internal/event/actors/actor-impl.h>
 #include <dali/internal/event/animation/animation-playlist.h>
+#include <dali/internal/event/common/event-thread-services.h>
 #include <dali/internal/event/common/notification-manager.h>
 #include <dali/internal/event/common/property-notification-manager.h>
 #include <dali/internal/event/common/stage-impl.h>
 #include <dali/internal/event/size-negotiation/relayout-controller-impl.h>
 
 #include <dali/internal/update/common/discard-queue.h>
-#include <dali/internal/update/manager/update-manager.h>
 #include <dali/internal/update/manager/render-task-processor.h>
+#include <dali/internal/update/manager/update-manager.h>
 
 #include <dali/internal/render/common/performance-monitor.h>
 #include <dali/internal/render/common/render-manager.h>
-#include <dali/internal/render/gl-resources/context.h>
 
-using Dali::Internal::SceneGraph::UpdateManager;
 using Dali::Internal::SceneGraph::RenderManager;
-using Dali::Internal::SceneGraph::DiscardQueue;
 using Dali::Internal::SceneGraph::RenderQueue;
+using Dali::Internal::SceneGraph::UpdateManager;
 
 namespace
 {
 // The Update for frame N+1 may be processed whilst frame N is being rendered.
-const unsigned int MAXIMUM_UPDATE_COUNT = 2u;
+const uint32_t MAXIMUM_UPDATE_COUNT = 2u;
+
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gCoreFilter = Debug::Filter::New(Debug::Concise, false, "LOG_CORE");
 #endif
-}
+} // namespace
 
 namespace Dali
 {
-
 namespace Internal
 {
-
-using Integration::RenderController;
-using Integration::PlatformAbstraction;
-using Integration::GlSyncAbstraction;
-using Integration::GestureManager;
-using Integration::GlAbstraction;
 using Integration::Event;
-using Integration::UpdateStatus;
+using Integration::GlAbstraction;
+using Integration::GlContextHelperAbstraction;
+using Integration::PlatformAbstraction;
+using Integration::RenderController;
 using Integration::RenderStatus;
+using Integration::UpdateStatus;
 
-Core::Core( RenderController& renderController,
-            PlatformAbstraction& platform,
-            GlAbstraction& glAbstraction,
-            GlSyncAbstraction& glSyncAbstraction,
-            GestureManager& gestureManager,
-            ResourcePolicy::DataRetention dataRetentionPolicy,
-            Integration::RenderToFrameBuffer renderToFboEnabled,
-            Integration::DepthBufferAvailable depthBufferAvailable,
-            Integration::StencilBufferAvailable stencilBufferAvailable )
-: mRenderController( renderController ),
+Core::Core(RenderController&                   renderController,
+           PlatformAbstraction&                platform,
+           Graphics::Controller&               graphicsController,
+           Integration::RenderToFrameBuffer    renderToFboEnabled,
+           Integration::DepthBufferAvailable   depthBufferAvailable,
+           Integration::StencilBufferAvailable stencilBufferAvailable,
+           Integration::PartialUpdateAvailable partialUpdateAvailable)
+: mRenderController(renderController),
   mPlatform(platform),
-  mProcessingEvent(false)
+  mGraphicsController(graphicsController),
+  mProcessingEvent(false),
+  mForceNextUpdate(false),
+  mProcessorUnregistered(false),
+  mPostProcessorUnregistered(false)
 {
   // Create the thread local storage
   CreateThreadLocalStorage();
 
   // This does nothing until Core is built with --enable-performance-monitor
-  PERFORMANCE_MONITOR_INIT( platform );
+  PERFORMANCE_MONITOR_INIT(platform);
 
   mNotificationManager = new NotificationManager();
 
@@ -105,37 +107,37 @@ Core::Core( RenderController& renderController,
 
   mRenderTaskProcessor = new SceneGraph::RenderTaskProcessor();
 
-  mRenderManager = RenderManager::New( glAbstraction, glSyncAbstraction, depthBufferAvailable, stencilBufferAvailable );
+  mRenderManager = RenderManager::New(graphicsController, depthBufferAvailable, stencilBufferAvailable, partialUpdateAvailable);
 
   RenderQueue& renderQueue = mRenderManager->GetRenderQueue();
 
-  mDiscardQueue = new DiscardQueue( renderQueue );
+  mUpdateManager = new UpdateManager(*mNotificationManager,
+                                     *mAnimationPlaylist,
+                                     *mPropertyNotificationManager,
+                                     renderController,
+                                     *mRenderManager,
+                                     renderQueue,
+                                     *mRenderTaskProcessor);
 
-  mUpdateManager = new UpdateManager( *mNotificationManager,
-                                      *mAnimationPlaylist,
-                                      *mPropertyNotificationManager,
-                                      *mDiscardQueue,
-                                       renderController,
-                                      *mRenderManager,
-                                       renderQueue,
-                                      *mRenderTaskProcessor );
+  mRenderManager->SetShaderSaver(*mUpdateManager);
 
-  mRenderManager->SetShaderSaver( *mUpdateManager );
+  mObjectRegistry = ObjectRegistry::New();
 
-  mStage = IntrusivePtr<Stage>( Stage::New( *mAnimationPlaylist, *mPropertyNotificationManager, *mUpdateManager, *mNotificationManager, mRenderController ) );
+  mStage = IntrusivePtr<Stage>(Stage::New(*mUpdateManager));
 
   // This must be called after stage is created but before stage initialization
-  mRelayoutController = IntrusivePtr< RelayoutController >( new RelayoutController( mRenderController ) );
+  mRelayoutController = IntrusivePtr<RelayoutController>(new RelayoutController(mRenderController));
 
-  mStage->Initialize( renderToFboEnabled == Integration::RenderToFrameBuffer::TRUE );
-
-  mGestureEventProcessor = new GestureEventProcessor( *mStage, *mUpdateManager, gestureManager, mRenderController );
-  mEventProcessor = new EventProcessor( *mStage, *mNotificationManager, *mGestureEventProcessor );
+  mGestureEventProcessor = new GestureEventProcessor(*mUpdateManager, mRenderController);
 
   mShaderFactory = new ShaderFactory();
-  mUpdateManager->SetShaderSaver( *mShaderFactory );
+  mUpdateManager->SetShaderSaver(*mShaderFactory);
 
   GetImplementation(Dali::TypeRegistry::Get()).CallInitFunctions();
+
+  DALI_LOG_RELEASE_INFO("Node size: %lu\n", sizeof(Dali::Internal::SceneGraph::Node));
+  DALI_LOG_RELEASE_INFO("Renderer size: %lu\n", sizeof(Dali::Internal::SceneGraph::Renderer));
+  DALI_LOG_RELEASE_INFO("RenderItem size: %lu\n", sizeof(Dali::Internal::SceneGraph::RenderItem));
 }
 
 Core::~Core()
@@ -148,21 +150,24 @@ Core::~Core()
   // allows core to be created / deleted many times in the same thread (how TET cases work).
   // Do this before mStage.Reset() so Stage::IsInstalled() returns false
   ThreadLocalStorage* tls = ThreadLocalStorage::GetInternal();
-  if( tls )
+  if(tls)
   {
     tls->Remove();
-    delete tls;
+    tls->Unreference();
   }
 
+  mObjectRegistry.Reset();
+
   // Stop relayout requests being raised on stage destruction
   mRelayoutController.Reset();
 
-  // Clean-up stage - remove default camera and root layer
-  mStage->Uninitialize();
-
   // remove (last?) reference to stage
   mStage.Reset();
+}
 
+void Core::Initialize()
+{
+  mStage->Initialize(*mScenes[0]);
 }
 
 Integration::ContextNotifierInterface* Core::GetContextNotifier()
@@ -179,38 +184,13 @@ void Core::RecoverFromContextLoss()
 
 void Core::ContextCreated()
 {
-  mRenderManager->ContextCreated();
 }
 
 void Core::ContextDestroyed()
 {
-  mRenderManager->ContextDestroyed();
-}
-
-void Core::SurfaceResized( unsigned int width, unsigned int height )
-{
-  mStage->SurfaceResized( width, height );
-
-  // The stage-size may be less than surface-size (reduced by top-margin)
-  Vector2 size = mStage->GetSize();
-  mRelayoutController->SetStageSize( size.width, size.height );
-}
-
-void Core::SetTopMargin( unsigned int margin )
-{
-  mStage->SetTopMargin( margin );
-
-  // The stage-size may be less than surface-size (reduced by top-margin)
-  Vector2 size = mStage->GetSize();
-  mRelayoutController->SetStageSize( size.width, size.height );
 }
 
-void Core::SetDpi( unsigned int dpiHorizontal, unsigned int dpiVertical )
-{
-  mStage->SetDpi( Vector2( dpiHorizontal , dpiVertical) );
-}
-
-void Core::Update( float elapsedSeconds, unsigned int lastVSyncTimeMilliseconds, unsigned int nextVSyncTimeMilliseconds, Integration::UpdateStatus& status, bool renderToFboEnabled, bool isRenderingToFbo )
+void Core::Update(float elapsedSeconds, uint32_t lastVSyncTimeMilliseconds, uint32_t nextVSyncTimeMilliseconds, Integration::UpdateStatus& status, bool renderToFboEnabled, bool isRenderingToFbo, bool uploadOnly)
 {
   // set the time delta so adaptor can easily print FPS with a release build with 0 as
   // it is cached by frametime
@@ -218,11 +198,12 @@ void Core::Update( float elapsedSeconds, unsigned int lastVSyncTimeMilliseconds,
 
   // Render returns true when there are updates on the stage or one or more animations are completed.
   // Use the estimated time diff till we render as the elapsed time.
-  status.keepUpdating = mUpdateManager->Update( elapsedSeconds,
-                                                lastVSyncTimeMilliseconds,
-                                                nextVSyncTimeMilliseconds,
-                                                renderToFboEnabled,
-                                                isRenderingToFbo );
+  status.keepUpdating = mUpdateManager->Update(elapsedSeconds,
+                                               lastVSyncTimeMilliseconds,
+                                               nextVSyncTimeMilliseconds,
+                                               renderToFboEnabled,
+                                               isRenderingToFbo,
+                                               uploadOnly);
 
   // Check the Notification Manager message queue to set needsNotification
   status.needsNotification = mNotificationManager->MessagesToProcess();
@@ -231,9 +212,30 @@ void Core::Update( float elapsedSeconds, unsigned int lastVSyncTimeMilliseconds,
   // Any message to update will wake it up anyways
 }
 
-void Core::Render( RenderStatus& status, bool forceClear )
+void Core::PreRender(RenderStatus& status, bool forceClear)
+{
+  mRenderManager->PreRender(status, forceClear);
+}
+
+void Core::PreRender(Integration::Scene& scene, std::vector<Rect<int>>& damagedRects)
 {
-  mRenderManager->Render( status, forceClear );
+  mRenderManager->PreRender(scene, damagedRects);
+}
+
+void Core::RenderScene(RenderStatus& status, Integration::Scene& scene, bool renderToFbo)
+{
+  mRenderManager->RenderScene(status, scene, renderToFbo);
+}
+
+void Core::RenderScene(RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect<int>& clippingRect)
+{
+  mRenderManager->RenderScene(status, scene, renderToFbo, clippingRect);
+}
+
+void Core::PostRender()
+{
+  mUpdateManager->PostRender();
+  mRenderManager->PostRender();
 }
 
 void Core::SceneCreated()
@@ -241,44 +243,70 @@ void Core::SceneCreated()
   mStage->EmitSceneCreatedSignal();
 
   mRelayoutController->OnApplicationSceneCreated();
+
+  for(const auto& scene : mScenes)
+  {
+    Dali::Actor sceneRootLayer = scene->GetRootLayer();
+    mRelayoutController->RequestRelayoutTree(sceneRootLayer);
+  }
 }
 
-void Core::QueueEvent( const Integration::Event& event )
+void Core::QueueEvent(const Integration::Event& event)
 {
-  mEventProcessor->QueueEvent( event );
+  if(mScenes.size() != 0)
+  {
+    mScenes.front()->QueueEvent(event);
+  }
 }
 
 void Core::ProcessEvents()
 {
   // Guard against calls to ProcessEvents() during ProcessEvents()
-  if( mProcessingEvent )
+  if(mProcessingEvent)
   {
-    DALI_LOG_ERROR( "ProcessEvents should not be called from within ProcessEvents!\n" );
-    mRenderController.RequestProcessEventsOnIdle( false );
+    DALI_LOG_ERROR("ProcessEvents should not be called from within ProcessEvents!\n");
+    mRenderController.RequestProcessEventsOnIdle(false);
     return;
   }
 
   mProcessingEvent = true;
-  mRelayoutController->SetProcessingCoreEvents( true );
+  mRelayoutController->SetProcessingCoreEvents(true);
 
   // Signal that any messages received will be flushed soon
   mUpdateManager->EventProcessingStarted();
 
-  mEventProcessor->ProcessEvents();
+  // Scene could be added or removed while processing the events
+  // Copy the Scene container locally to avoid possibly invalid iterator
+  SceneContainer scenes = mScenes;
+
+  // process events in all scenes
+  for(auto scene : scenes)
+  {
+    scene->ProcessEvents();
+  }
 
   mNotificationManager->ProcessMessages();
 
   // Emit signal here to inform listeners that event processing has finished.
-  mStage->EmitEventProcessingFinishedSignal();
+  for(auto scene : scenes)
+  {
+    scene->EmitEventProcessingFinishedSignal();
+  }
+
+  // Run any registered processors
+  RunProcessors();
 
   // Run the size negotiation after event processing finished signal
   mRelayoutController->Relayout();
 
-  // Run any registered processors
-  RunProcessors();
+  // Run any registered post processors
+  RunPostProcessors();
 
   // Rebuild depth tree after event processing has finished
-  mStage->RebuildDepthTree();
+  for(auto scene : scenes)
+  {
+    scene->RebuildDepthTree();
+  }
 
   // Flush any queued messages for the update-thread
   const bool messagesToProcess = mUpdateManager->FlushQueue();
@@ -286,78 +314,129 @@ void Core::ProcessEvents()
   // Check if the touch or gestures require updates.
   const bool gestureNeedsUpdate = mGestureEventProcessor->NeedsUpdate();
   // Check if the next update is forced.
-  const bool forceUpdate = mStage->IsNextUpdateForced();
+  const bool forceUpdate = IsNextUpdateForced();
 
-  if( messagesToProcess || gestureNeedsUpdate || forceUpdate )
+  if(messagesToProcess || gestureNeedsUpdate || forceUpdate)
   {
     // tell the render controller to keep update thread running
-    mRenderController.RequestUpdate( forceUpdate );
+    mRenderController.RequestUpdate(forceUpdate);
   }
 
-  mRelayoutController->SetProcessingCoreEvents( false );
+  mRelayoutController->SetProcessingCoreEvents(false);
 
   // ProcessEvents() may now be called again
   mProcessingEvent = false;
 }
 
-unsigned int Core::GetMaximumUpdateCount() const
+uint32_t Core::GetMaximumUpdateCount() const
 {
   return MAXIMUM_UPDATE_COUNT;
 }
 
-Integration::SystemOverlay& Core::GetSystemOverlay()
+void Core::RegisterProcessor(Integration::Processor& processor, bool postProcessor)
 {
-  return mStage->GetSystemOverlay();
+  if(postProcessor)
+  {
+    mPostProcessors.PushBack(&processor);
+  }
+  else
+  {
+    mProcessors.PushBack(&processor);
+  }
 }
 
-void Core::SetViewMode( ViewMode viewMode )
+void Core::UnregisterProcessor(Integration::Processor& processor, bool postProcessor)
 {
-  mStage->SetViewMode( viewMode );
+  if(postProcessor)
+  {
+    auto iter = std::find(mPostProcessors.Begin(), mPostProcessors.End(), &processor);
+    if(iter != mPostProcessors.End())
+    {
+      mPostProcessors.Erase(iter);
+      mPostProcessorUnregistered = true;
+    }
+  }
+  else
+  {
+    auto iter = std::find(mProcessors.Begin(), mProcessors.End(), &processor);
+    if(iter != mProcessors.End())
+    {
+      mProcessors.Erase(iter);
+      mProcessorUnregistered = true;
+    }
+  }
 }
 
-ViewMode Core::GetViewMode() const
+void Core::RunProcessors()
 {
-  return mStage->GetViewMode();
-}
+  if(mProcessors.Count() != 0)
+  {
+    DALI_TRACE_SCOPE(gTraceFilter, "DALI_CORE_RUN_PROCESSORS");
 
-void Core::SetStereoBase( float stereoBase )
-{
-  mStage->SetStereoBase( stereoBase );
-}
+    // Copy processor pointers to prevent changes to vector affecting loop iterator.
+    Dali::Vector<Integration::Processor*> processors(mProcessors);
 
-void Core::RegisterProcessor( Integration::Processor& processor )
-{
-  mProcessors.PushBack(&processor);
-}
+    // To prevent accessing processor unregistered during the loop
+    mProcessorUnregistered = false;
 
-void Core::UnregisterProcessor( Integration::Processor& processor )
-{
-  auto iter = std::find( mProcessors.Begin(), mProcessors.End(), &processor );
-  if( iter != mProcessors.End() )
-  {
-    mProcessors.Erase( iter );
+    for(auto processor : processors)
+    {
+      if(processor)
+      {
+        if(!mProcessorUnregistered)
+        {
+          processor->Process(false);
+        }
+        else
+        {
+          // Run processor if the processor is still in the list.
+          // It may be removed during the loop.
+          auto iter = std::find(mProcessors.Begin(), mProcessors.End(), processor);
+          if(iter != mProcessors.End())
+          {
+            processor->Process(false);
+          }
+        }
+      }
+    }
   }
 }
 
-void Core::RunProcessors()
+void Core::RunPostProcessors()
 {
-  // Copy processor pointers to prevent changes to vector affecting loop iterator.
-  Dali::Vector<Integration::Processor*> processors( mProcessors );
-
-  for( auto processor : processors )
+  if(mPostProcessors.Count() != 0)
   {
-    if( processor )
+    DALI_TRACE_SCOPE(gTraceFilter, "DALI_CORE_RUN_POST_PROCESSORS");
+
+    // Copy processor pointers to prevent changes to vector affecting loop iterator.
+    Dali::Vector<Integration::Processor*> processors(mPostProcessors);
+
+    // To prevent accessing processor unregistered during the loop
+    mPostProcessorUnregistered = false;
+
+    for(auto processor : processors)
     {
-      processor->Process();
+      if(processor)
+      {
+        if(!mPostProcessorUnregistered)
+        {
+          processor->Process(true);
+        }
+        else
+        {
+          // Run processor if the processor is still in the list.
+          // It may be removed during the loop.
+          auto iter = std::find(mPostProcessors.Begin(), mPostProcessors.End(), processor);
+          if(iter != mPostProcessors.End())
+          {
+            processor->Process(true);
+          }
+        }
+      }
     }
   }
 }
 
-float Core::GetStereoBase() const
-{
-  return mStage->GetStereoBase();
-}
-
 StagePtr Core::GetCurrentStage()
 {
   return mStage.Get();
@@ -398,11 +477,131 @@ RelayoutController& Core::GetRelayoutController()
   return *(mRelayoutController.Get());
 }
 
+ObjectRegistry& Core::GetObjectRegistry() const
+{
+  return *(mObjectRegistry.Get());
+}
+
+void Core::LogMemoryPools() const
+{
+  uint32_t animationPoolCapacity    = SceneGraph::Animation::GetMemoryPoolCapacity();
+  uint32_t renderItemPoolCapacity   = SceneGraph::RenderItem::GetMemoryPoolCapacity();
+  uint32_t relayoutItemPoolCapacity = mRelayoutController->GetMemoryPoolCapacity();
+  uint32_t rendererPoolCapacity     = SceneGraph::Renderer::GetMemoryPoolCapacity();
+  uint32_t textureSetPoolCapacity   = SceneGraph::TextureSet::GetMemoryPoolCapacity();
+  uint32_t renderTaskPoolCapacity   = SceneGraph::RenderTaskList::GetMemoryPoolCapacity();
+  uint32_t nodePoolCapacity         = SceneGraph::Node::GetMemoryPoolCapacity();
+
+  DALI_LOG_RELEASE_INFO(
+    "\nMemory Pool capacities:\n"
+    "  Animations:    %lu\n"
+    "  RenderItems:   %lu\n"
+    "  RelayoutItems: %lu\n"
+    "  Renderers:     %lu\n"
+    "  TextureSets:   %lu\n"
+    "  RenderTasks:   %lu\n"
+    "  Nodes:         %lu\n",
+    animationPoolCapacity,
+    renderItemPoolCapacity,
+    relayoutItemPoolCapacity,
+    rendererPoolCapacity,
+    textureSetPoolCapacity,
+    renderTaskPoolCapacity,
+    nodePoolCapacity);
+
+  uint32_t updateQCapacity = mUpdateManager->GetUpdateMessageQueueCapacity();
+  uint32_t renderQCapacity = mUpdateManager->GetRenderMessageQueueCapacity();
+
+  DALI_LOG_RELEASE_INFO(
+    "\nMessage Queue capacities:\n"
+    "  UpdateQueue: %lu\n"
+    "  RenderQueue: %lu\n",
+    updateQCapacity,
+    renderQCapacity);
+
+  size_t renderInstructionCapacity = mUpdateManager->GetRenderInstructionCapacity();
+  DALI_LOG_RELEASE_INFO("\nRenderInstruction capacity: %lu\n", renderInstructionCapacity);
+}
+
+EventThreadServices& Core::GetEventThreadServices()
+{
+  return *this;
+}
+
+PropertyNotificationManager& Core::GetPropertyNotificationManager() const
+{
+  return *(mPropertyNotificationManager);
+}
+
+AnimationPlaylist& Core::GetAnimationPlaylist() const
+{
+  return *(mAnimationPlaylist);
+}
+
+Integration::GlAbstraction& Core::GetGlAbstraction() const
+{
+  return mGraphicsController.GetGlAbstraction();
+}
+
+void Core::AddScene(Scene* scene)
+{
+  mScenes.push_back(scene);
+}
+
+void Core::RemoveScene(Scene* scene)
+{
+  auto iter = std::find(mScenes.begin(), mScenes.end(), scene);
+  if(iter != mScenes.end())
+  {
+    mScenes.erase(iter);
+  }
+}
+
 void Core::CreateThreadLocalStorage()
 {
   // a pointer to the ThreadLocalStorage object will be stored in TLS
   // The ThreadLocalStorage object should be deleted by the Core destructor
-  new ThreadLocalStorage(this);
+  ThreadLocalStorage* tls = new ThreadLocalStorage(this);
+  tls->Reference();
+}
+
+void Core::RegisterObject(Dali::BaseObject* object)
+{
+  mObjectRegistry = &ThreadLocalStorage::Get().GetObjectRegistry();
+  mObjectRegistry->RegisterObject(object);
+}
+
+void Core::UnregisterObject(Dali::BaseObject* object)
+{
+  mObjectRegistry = &ThreadLocalStorage::Get().GetObjectRegistry();
+  mObjectRegistry->UnregisterObject(object);
+}
+
+Integration::RenderController& Core::GetRenderController()
+{
+  return mRenderController;
+}
+
+uint32_t* Core::ReserveMessageSlot(uint32_t size, bool updateScene)
+{
+  return mUpdateManager->ReserveMessageSlot(size, updateScene);
+}
+
+BufferIndex Core::GetEventBufferIndex() const
+{
+  return mUpdateManager->GetEventBufferIndex();
+}
+
+void Core::ForceNextUpdate()
+{
+  mForceNextUpdate = true;
+}
+
+bool Core::IsNextUpdateForced()
+{
+  bool nextUpdateForced = mForceNextUpdate;
+  mForceNextUpdate      = false;
+  return nextUpdateForced;
 }
 
 } // namespace Internal