2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/update/manager/update-manager.h>
22 #include <dali/public-api/common/stage.h>
23 #include <dali/devel-api/common/owner-container.h>
24 #include <dali/devel-api/threading/mutex.h>
26 #include <dali/integration-api/core.h>
27 #include <dali/integration-api/render-controller.h>
28 #include <dali/internal/common/shader-data.h>
29 #include <dali/integration-api/debug.h>
31 #include <dali/internal/common/core-impl.h>
32 #include <dali/internal/common/message.h>
34 #include <dali/internal/event/common/notification-manager.h>
35 #include <dali/internal/event/common/property-notification-impl.h>
36 #include <dali/internal/event/common/property-notifier.h>
37 #include <dali/internal/event/effects/shader-factory.h>
38 #include <dali/internal/event/animation/animation-playlist.h>
40 #include <dali/internal/update/animation/scene-graph-animator.h>
41 #include <dali/internal/update/animation/scene-graph-animation.h>
42 #include <dali/internal/update/common/discard-queue.h>
43 #include <dali/internal/update/common/scene-graph-buffers.h>
44 #include <dali/internal/update/controllers/render-message-dispatcher.h>
45 #include <dali/internal/update/controllers/scene-controller-impl.h>
46 #include <dali/internal/update/gestures/scene-graph-pan-gesture.h>
47 #include <dali/internal/update/manager/render-task-processor.h>
48 #include <dali/internal/update/manager/sorted-layers.h>
49 #include <dali/internal/update/manager/update-algorithms.h>
50 #include <dali/internal/update/manager/update-manager-debug.h>
51 #include <dali/internal/update/manager/transform-manager.h>
52 #include <dali/internal/update/nodes/node.h>
53 #include <dali/internal/update/nodes/scene-graph-layer.h>
54 #include <dali/internal/update/queue/update-message-queue.h>
55 #include <dali/internal/update/render-tasks/scene-graph-render-task.h>
56 #include <dali/internal/update/render-tasks/scene-graph-render-task-list.h>
57 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
59 #include <dali/internal/render/common/render-instruction-container.h>
60 #include <dali/internal/render/common/render-manager.h>
61 #include <dali/internal/render/queue/render-queue.h>
62 #include <dali/internal/render/shaders/scene-graph-shader.h>
64 // Un-comment to enable node tree debug logging
65 //#define NODE_TREE_LOGGING 1
67 #if ( defined( DEBUG_ENABLED ) && defined( NODE_TREE_LOGGING ) )
68 #define SNAPSHOT_NODE_LOGGING \
69 const int FRAME_COUNT_TRIGGER = 16;\
70 if( mImpl->frameCounter >= FRAME_COUNT_TRIGGER )\
72 if ( NULL != mImpl->root )\
74 mImpl->frameCounter = 0;\
75 PrintNodeTree( *mImpl->root, mSceneGraphBuffers.GetUpdateBufferIndex(), "" );\
78 mImpl->frameCounter++;
80 #define SNAPSHOT_NODE_LOGGING
83 #if defined(DEBUG_ENABLED)
84 extern Debug::Filter* gRenderTaskLogFilter;
88 using namespace Dali::Integration;
89 using Dali::Internal::Update::MessageQueue;
103 * Helper to reset animate-able objects to base values
104 * @param container to iterate over
105 * @param updateBufferIndex to use
108 inline void ResetToBaseValues( OwnerContainer<T*>& container, BufferIndex updateBufferIndex )
110 // Reset animatable properties to base values
111 // use reference to avoid extra copies of the iterator
112 for( auto&& iter : container )
114 iter->ResetToBaseValues( updateBufferIndex );
119 * Helper to Erase an object from OwnerContainer using discard queue
120 * @param container to remove from
121 * @param object to remove
122 * @param discardQueue to put the object to
123 * @param updateBufferIndex to use
126 inline void EraseUsingDiscardQueue( OwnerContainer<T*>& container, T* object, DiscardQueue& discardQueue, BufferIndex updateBufferIndex )
128 DALI_ASSERT_DEBUG( object && "NULL object not allowed" );
130 // need to use the reference version of auto as we need the pointer to the pointer for the Release call below
131 for( auto&& iter : container )
133 if ( iter == object )
135 // Transfer ownership to the discard queue, this keeps the object alive, until the render-thread has finished with it
136 discardQueue.Add( updateBufferIndex, container.Release( &iter ) ); // take the address of the reference to a pointer (iter)
137 return; // return as we only ever remove one object. Iterators to container are now invalidated as well so cannot continue
145 * Structure to contain UpdateManager internal data
147 struct UpdateManager::Impl
149 Impl( NotificationManager& notificationManager,
150 CompleteNotificationInterface& animationPlaylist,
151 PropertyNotifier& propertyNotifier,
152 DiscardQueue& discardQueue,
153 RenderController& renderController,
154 RenderManager& renderManager,
155 RenderQueue& renderQueue,
156 SceneGraphBuffers& sceneGraphBuffers,
157 RenderTaskProcessor& renderTaskProcessor )
158 : renderMessageDispatcher( renderManager, renderQueue, sceneGraphBuffers ),
159 notificationManager( notificationManager ),
161 animationPlaylist( animationPlaylist ),
162 propertyNotifier( propertyNotifier ),
164 discardQueue( discardQueue ),
165 renderController( renderController ),
166 sceneController( NULL ),
167 renderManager( renderManager ),
168 renderQueue( renderQueue ),
169 renderInstructions( renderManager.GetRenderInstructionContainer() ),
170 renderTaskProcessor( renderTaskProcessor ),
171 backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ),
172 taskList( renderMessageDispatcher ),
173 systemLevelTaskList( renderMessageDispatcher ),
175 systemLevelRoot( NULL ),
179 panGestureProcessor( NULL ),
180 messageQueue( renderController, sceneGraphBuffers ),
181 keepRenderingSeconds( 0.0f ),
182 nodeDirtyFlags( TransformFlag ), // set to TransformFlag to ensure full update the first time through Update()
184 animationFinishedDuringUpdate( false ),
185 previousUpdateScene( false ),
186 renderTaskWaiting( false ),
187 renderersAdded( false )
189 sceneController = new SceneControllerImpl( renderMessageDispatcher, renderQueue, discardQueue );
191 // create first 'dummy' node
197 // Disconnect render tasks from nodes, before destroying the nodes
198 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
199 for ( auto&& iter : tasks )
201 iter->SetSourceNode( NULL );
203 // ..repeat for system level RenderTasks
204 RenderTaskList::RenderTaskContainer& systemLevelTasks = systemLevelTaskList.GetTasks();
205 for ( auto&& iter : systemLevelTasks )
207 iter->SetSourceNode( NULL );
210 // UpdateManager owns the Nodes. Although Nodes are pool allocated they contain heap allocated parts
211 // like custom properties, which get released here
212 Vector<Node*>::Iterator iter = nodes.Begin()+1;
213 Vector<Node*>::Iterator endIter = nodes.End();
214 for(;iter!=endIter;++iter)
216 (*iter)->OnDestroy();
220 // If there is root, reset it, otherwise do nothing as rendering was never started
225 Node::Delete( root );
229 if( systemLevelRoot )
231 systemLevelRoot->OnDestroy();
233 Node::Delete( systemLevelRoot );
234 systemLevelRoot = NULL;
237 delete sceneController;
240 SceneGraphBuffers sceneGraphBuffers; ///< Used to keep track of which buffers are being written or read
241 RenderMessageDispatcher renderMessageDispatcher; ///< Used for passing messages to the render-thread
242 NotificationManager& notificationManager; ///< Queues notification messages for the event-thread.
243 TransformManager transformManager; ///< Used to update the transformation matrices of the nodes
244 CompleteNotificationInterface& animationPlaylist; ///< Holds handles to all the animations
245 PropertyNotifier& propertyNotifier; ///< Provides notification to applications when properties are modified.
246 ShaderSaver* shaderSaver; ///< Saves shader binaries.
247 DiscardQueue& discardQueue; ///< Nodes are added here when disconnected from the scene-graph.
248 RenderController& renderController; ///< render controller
249 SceneControllerImpl* sceneController; ///< scene controller
250 RenderManager& renderManager; ///< This is responsible for rendering the results of each "update"
251 RenderQueue& renderQueue; ///< Used to queue messages for the next render
252 RenderInstructionContainer& renderInstructions; ///< Used to prepare the render instructions
253 RenderTaskProcessor& renderTaskProcessor; ///< Handles RenderTasks and RenderInstrucitons
255 Vector4 backgroundColor; ///< The glClear color used at the beginning of each frame.
257 RenderTaskList taskList; ///< The list of scene graph render-tasks
258 RenderTaskList systemLevelTaskList; ///< Separate render-tasks for system-level content
260 Layer* root; ///< The root node (root is a layer)
261 Layer* systemLevelRoot; ///< A separate root-node for system-level content
263 Vector<Node*> nodes; ///< A container of all instantiated nodes
265 SortedLayerPointers sortedLayers; ///< A container of Layer pointers sorted by depth
266 SortedLayerPointers systemLevelSortedLayers; ///< A separate container of system-level Layers
268 OwnerContainer< Camera* > cameras; ///< A container of cameras
269 OwnerContainer< PropertyOwner* > customObjects; ///< A container of owned objects (with custom properties)
271 AnimationContainer animations; ///< A container of owned animations
272 PropertyNotificationContainer propertyNotifications; ///< A container of owner property notifications.
274 OwnerContainer< Renderer* > renderers; ///< A container of owned renderers
275 OwnerContainer< TextureSet* > textureSets; ///< A container of owned texture sets
276 OwnerContainer< Shader* > shaders; ///< A container of owned shaders
277 OwnerPointer< PanGesture > panGestureProcessor; ///< Owned pan gesture processor; it lives for the lifecycle of UpdateManager
279 MessageQueue messageQueue; ///< The messages queued from the event-thread
280 std::vector<Internal::ShaderDataPtr> renderCompiledShaders; ///< Shaders compiled on Render thread are inserted here for update thread to pass on to event thread.
281 std::vector<Internal::ShaderDataPtr> updateCompiledShaders; ///< Shaders to be sent from Update to Event
282 Mutex compiledShaderMutex; ///< lock to ensure no corruption on the renderCompiledShaders
284 float keepRenderingSeconds; ///< Set via Dali::Stage::KeepRendering
285 int nodeDirtyFlags; ///< cumulative node dirty flags from previous frame
286 int frameCounter; ///< Frame counter used in debugging to choose which frame to debug and which to ignore.
288 bool animationFinishedDuringUpdate; ///< Flag whether any animations finished during the Update()
289 bool previousUpdateScene; ///< True if the scene was updated in the previous frame (otherwise it was optimized out)
290 bool renderTaskWaiting; ///< A REFRESH_ONCE render task is waiting to be rendered
291 bool renderersAdded; ///< Flag to keep track when renderers have been added to avoid unnecessary processing
295 Impl( const Impl& ); ///< Undefined
296 Impl& operator=( const Impl& ); ///< Undefined
299 UpdateManager::UpdateManager( NotificationManager& notificationManager,
300 CompleteNotificationInterface& animationFinishedNotifier,
301 PropertyNotifier& propertyNotifier,
302 DiscardQueue& discardQueue,
303 RenderController& controller,
304 RenderManager& renderManager,
305 RenderQueue& renderQueue,
306 RenderTaskProcessor& renderTaskProcessor )
309 mImpl = new Impl( notificationManager,
310 animationFinishedNotifier,
317 renderTaskProcessor );
321 UpdateManager::~UpdateManager()
326 void UpdateManager::InstallRoot( OwnerPointer<Layer>& layer, bool systemLevel )
328 DALI_ASSERT_DEBUG( layer->IsLayer() );
329 DALI_ASSERT_DEBUG( layer->GetParent() == NULL);
333 DALI_ASSERT_DEBUG( mImpl->root == NULL && "Root Node already installed" );
334 mImpl->root = layer.Release();
335 mImpl->root->CreateTransform( &mImpl->transformManager );
336 mImpl->root->SetRoot(true);
340 DALI_ASSERT_DEBUG( mImpl->systemLevelRoot == NULL && "System-level Root Node already installed" );
341 mImpl->systemLevelRoot = layer.Release();
342 mImpl->systemLevelRoot->CreateTransform( &mImpl->transformManager );
343 mImpl->systemLevelRoot->SetRoot(true);
348 void UpdateManager::AddNode( OwnerPointer<Node>& node )
350 DALI_ASSERT_ALWAYS( NULL == node->GetParent() ); // Should not have a parent yet
352 // Nodes must be sorted by pointer
353 Node* rawNode = node.Release();
354 Vector<Node*>::Iterator begin = mImpl->nodes.Begin();
355 for( Vector<Node*>::Iterator iter = mImpl->nodes.End()-1; iter >= begin; --iter )
357 if( rawNode > (*iter) )
359 mImpl->nodes.Insert((iter+1), rawNode );
360 rawNode->CreateTransform( &mImpl->transformManager );
366 void UpdateManager::ConnectNode( Node* parent, Node* node )
368 DALI_ASSERT_ALWAYS( NULL != parent );
369 DALI_ASSERT_ALWAYS( NULL != node );
370 DALI_ASSERT_ALWAYS( NULL == node->GetParent() ); // Should not have a parent yet
372 parent->ConnectChild( node );
375 void UpdateManager::DisconnectNode( Node* node )
377 Node* parent = node->GetParent();
378 DALI_ASSERT_ALWAYS( NULL != parent );
379 parent->SetDirtyFlag( ChildDeletedFlag ); // make parent dirty so that render items dont get reused
381 parent->DisconnectChild( mSceneGraphBuffers.GetUpdateBufferIndex(), *node );
384 void UpdateManager::DestroyNode( Node* node )
386 DALI_ASSERT_ALWAYS( NULL != node );
387 DALI_ASSERT_ALWAYS( NULL == node->GetParent() ); // Should have been disconnected
389 Vector<Node*>::Iterator iter = mImpl->nodes.Begin()+1;
390 Vector<Node*>::Iterator endIter = mImpl->nodes.End();
391 for(;iter!=endIter;++iter)
395 mImpl->nodes.Erase(iter);
400 mImpl->discardQueue.Add( mSceneGraphBuffers.GetUpdateBufferIndex(), node );
402 // Notify the Node about impending destruction
406 void UpdateManager::AddCamera( OwnerPointer< Camera >& camera )
408 mImpl->cameras.PushBack( camera.Release() ); // takes ownership
411 void UpdateManager::RemoveCamera( const Camera* camera )
413 // Find the camera and destroy it
414 EraseUsingDiscardQueue( mImpl->cameras, const_cast<Camera*>( camera ), mImpl->discardQueue, mSceneGraphBuffers.GetUpdateBufferIndex() );
417 void UpdateManager::AddObject( OwnerPointer<PropertyOwner>& object )
419 mImpl->customObjects.PushBack( object.Release() );
422 void UpdateManager::RemoveObject( PropertyOwner* object )
424 mImpl->customObjects.EraseObject( object );
427 void UpdateManager::AddAnimation( OwnerPointer< SceneGraph::Animation >& animation )
429 mImpl->animations.PushBack( animation.Release() );
432 void UpdateManager::StopAnimation( Animation* animation )
434 DALI_ASSERT_DEBUG( animation && "NULL animation called to stop" );
436 bool animationFinished = animation->Stop( mSceneGraphBuffers.GetUpdateBufferIndex() );
438 mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || animationFinished;
441 void UpdateManager::RemoveAnimation( Animation* animation )
443 DALI_ASSERT_DEBUG( animation && "NULL animation called to remove" );
445 animation->OnDestroy( mSceneGraphBuffers.GetUpdateBufferIndex() );
447 DALI_ASSERT_DEBUG( animation->GetState() == Animation::Destroyed );
450 bool UpdateManager::IsAnimationRunning() const
452 // Find any animation that isn't stopped or paused
453 for ( auto&& iter : mImpl->animations )
455 const Animation::State state = iter->GetState();
457 if (state != Animation::Stopped &&
458 state != Animation::Paused)
460 return true; // stop iteration as soon as first one is found
467 void UpdateManager::AddPropertyNotification( OwnerPointer< PropertyNotification >& propertyNotification )
469 mImpl->propertyNotifications.PushBack( propertyNotification.Release() );
472 void UpdateManager::RemovePropertyNotification( PropertyNotification* propertyNotification )
474 mImpl->propertyNotifications.EraseObject( propertyNotification );
477 void UpdateManager::PropertyNotificationSetNotify( PropertyNotification* propertyNotification, PropertyNotification::NotifyMode notifyMode )
479 DALI_ASSERT_DEBUG( propertyNotification && "propertyNotification scene graph object missing" );
480 propertyNotification->SetNotifyMode( notifyMode );
483 void UpdateManager::AddShader( OwnerPointer< Shader >& shader )
485 mImpl->shaders.PushBack( shader.Release() );
488 void UpdateManager::RemoveShader( Shader* shader )
490 // Find the shader and destroy it
491 EraseUsingDiscardQueue( mImpl->shaders, shader, mImpl->discardQueue, mSceneGraphBuffers.GetUpdateBufferIndex() );
494 void UpdateManager::SetShaderProgram( Shader* shader,
495 Internal::ShaderDataPtr shaderData, bool modifiesGeometry )
500 typedef MessageValue3< Shader, Internal::ShaderDataPtr, ProgramCache*, bool> DerivedType;
502 // Reserve some memory inside the render queue
503 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
505 // Construct message in the render queue memory; note that delete should not be called on the return value
506 new (slot) DerivedType( shader, &Shader::SetProgram, shaderData, mImpl->renderManager.GetProgramCache(), modifiesGeometry );
510 void UpdateManager::SaveBinary( Internal::ShaderDataPtr shaderData )
512 DALI_ASSERT_DEBUG( shaderData && "No NULL shader data pointers please." );
513 DALI_ASSERT_DEBUG( shaderData->GetBufferSize() > 0 && "Shader binary empty so nothing to save." );
515 // lock as update might be sending previously compiled shaders to event thread
516 Mutex::ScopedLock lock( mImpl->compiledShaderMutex );
517 mImpl->renderCompiledShaders.push_back( shaderData );
521 void UpdateManager::SetShaderSaver( ShaderSaver& upstream )
523 mImpl->shaderSaver = &upstream;
526 void UpdateManager::AddRenderer( OwnerPointer< Renderer >& renderer )
528 renderer->ConnectToSceneGraph( *mImpl->sceneController, mSceneGraphBuffers.GetUpdateBufferIndex() );
529 mImpl->renderers.PushBack( renderer.Release() );
530 mImpl->renderersAdded = true;
533 void UpdateManager::RemoveRenderer( Renderer* renderer )
535 // Find the renderer and destroy it
536 EraseUsingDiscardQueue( mImpl->renderers, renderer, mImpl->discardQueue, mSceneGraphBuffers.GetUpdateBufferIndex() );
537 // Need to remove the render object as well
538 renderer->DisconnectFromSceneGraph( *mImpl->sceneController, mSceneGraphBuffers.GetUpdateBufferIndex() );
541 void UpdateManager::SetPanGestureProcessor( PanGesture* panGestureProcessor )
543 DALI_ASSERT_DEBUG( NULL != panGestureProcessor );
545 mImpl->panGestureProcessor = panGestureProcessor;
548 void UpdateManager::AddTextureSet( OwnerPointer< TextureSet >& textureSet )
550 mImpl->textureSets.PushBack( textureSet.Release() );
553 void UpdateManager::RemoveTextureSet( TextureSet* textureSet )
555 mImpl->textureSets.EraseObject( textureSet );
558 RenderTaskList* UpdateManager::GetRenderTaskList( bool systemLevel )
562 // copy the list, this is only likely to happen once in application life cycle
563 return &(mImpl->taskList);
567 // copy the list, this is only likely to happen once in application life cycle
568 return &(mImpl->systemLevelTaskList);
572 unsigned int* UpdateManager::ReserveMessageSlot( std::size_t size, bool updateScene )
574 return mImpl->messageQueue.ReserveMessageSlot( size, updateScene );
577 void UpdateManager::EventProcessingStarted()
579 mImpl->messageQueue.EventProcessingStarted();
582 bool UpdateManager::FlushQueue()
584 return mImpl->messageQueue.FlushQueue();
587 void UpdateManager::ResetProperties( BufferIndex bufferIndex )
589 // Clear the "animations finished" flag; This should be set if any (previously playing) animation is stopped
590 mImpl->animationFinishedDuringUpdate = false;
592 // Animated properties have to be reset to their original value each frame
594 // Reset root properties
597 mImpl->root->ResetToBaseValues( bufferIndex );
599 if ( mImpl->systemLevelRoot )
601 mImpl->systemLevelRoot->ResetToBaseValues( bufferIndex );
604 // Reset all the nodes
605 Vector<Node*>::Iterator iter = mImpl->nodes.Begin()+1;
606 Vector<Node*>::Iterator endIter = mImpl->nodes.End();
607 for( ;iter != endIter; ++iter )
609 (*iter)->ResetToBaseValues( bufferIndex );
612 // Reset system-level render-task list properties to base values
613 ResetToBaseValues( mImpl->systemLevelTaskList.GetTasks(), bufferIndex );
615 // Reset render-task list properties to base values.
616 ResetToBaseValues( mImpl->taskList.GetTasks(), bufferIndex );
618 // Reset custom object properties to base values
619 ResetToBaseValues( mImpl->customObjects, bufferIndex );
621 // Reset animatable renderer properties to base values
622 ResetToBaseValues( mImpl->renderers, bufferIndex );
624 // Reset animatable shader properties to base values
625 ResetToBaseValues( mImpl->shaders, bufferIndex );
628 bool UpdateManager::ProcessGestures( BufferIndex bufferIndex, unsigned int lastVSyncTimeMilliseconds, unsigned int nextVSyncTimeMilliseconds )
630 bool gestureUpdated( false );
632 if( mImpl->panGestureProcessor )
634 // gesture processor only supports default properties
635 mImpl->panGestureProcessor->ResetDefaultProperties( bufferIndex ); // Needs to be done every time as gesture data is written directly to an update-buffer rather than via a message
636 gestureUpdated |= mImpl->panGestureProcessor->UpdateProperties( lastVSyncTimeMilliseconds, nextVSyncTimeMilliseconds );
639 return gestureUpdated;
642 void UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
644 AnimationContainer &animations = mImpl->animations;
645 AnimationIter iter = animations.Begin();
646 bool animationLooped = false;
648 while ( iter != animations.End() )
650 Animation* animation = *iter;
651 bool finished = false;
653 bool progressMarkerReached = false;
654 animation->Update( bufferIndex, elapsedSeconds, looped, finished, progressMarkerReached );
656 if ( progressMarkerReached )
658 mImpl->notificationManager.QueueMessage( Internal::NotifyProgressReachedMessage( mImpl->animationPlaylist, animation ) );
661 mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || finished;
662 animationLooped = animationLooped || looped;
664 // Remove animations that had been destroyed but were still waiting for an update
665 if (animation->GetState() == Animation::Destroyed)
667 iter = animations.Erase(iter);
675 // queue the notification on finished or looped (to update loop count)
676 if ( mImpl->animationFinishedDuringUpdate || animationLooped )
678 // The application should be notified by NotificationManager, in another thread
679 mImpl->notificationManager.QueueCompleteNotification( &mImpl->animationPlaylist );
683 void UpdateManager::ConstrainCustomObjects( BufferIndex bufferIndex )
685 //Constrain custom objects (in construction order)
686 for ( auto&& object : mImpl->customObjects )
688 ConstrainPropertyOwner( *object, bufferIndex );
692 void UpdateManager::ConstrainRenderTasks( BufferIndex bufferIndex )
694 // Constrain system-level render-tasks
695 const RenderTaskList::RenderTaskContainer& systemLevelTasks = mImpl->systemLevelTaskList.GetTasks();
696 for ( auto&& task : systemLevelTasks )
698 ConstrainPropertyOwner( *task, bufferIndex );
701 // Constrain render-tasks
702 const RenderTaskList::RenderTaskContainer& tasks = mImpl->taskList.GetTasks();
703 for ( auto&& task : tasks )
705 ConstrainPropertyOwner( *task, bufferIndex );
709 void UpdateManager::ConstrainShaders( BufferIndex bufferIndex )
711 // constrain shaders... (in construction order)
712 for ( auto&& shader : mImpl->shaders )
714 ConstrainPropertyOwner( *shader, bufferIndex );
718 void UpdateManager::ProcessPropertyNotifications( BufferIndex bufferIndex )
720 for( auto&& notification : mImpl->propertyNotifications )
722 bool valid = notification->Check( bufferIndex );
725 mImpl->notificationManager.QueueMessage( PropertyChangedMessage( mImpl->propertyNotifier, notification, notification->GetValidity() ) );
730 void UpdateManager::ForwardCompiledShadersToEventThread()
732 DALI_ASSERT_DEBUG( (mImpl->shaderSaver != 0) && "shaderSaver should be wired-up during startup." );
733 if( mImpl->shaderSaver )
735 // lock and swap the queues
737 // render might be attempting to send us more binaries at the same time
738 Mutex::ScopedLock lock( mImpl->compiledShaderMutex );
739 mImpl->renderCompiledShaders.swap( mImpl->updateCompiledShaders );
742 if( mImpl->updateCompiledShaders.size() > 0 )
744 ShaderSaver& factory = *mImpl->shaderSaver;
745 for( auto&& shader : mImpl->updateCompiledShaders )
747 mImpl->notificationManager.QueueMessage( ShaderCompiledMessage( factory, shader ) );
749 // we don't need them in update anymore
750 mImpl->updateCompiledShaders.clear();
755 void UpdateManager::UpdateRenderers( BufferIndex bufferIndex )
757 const unsigned int rendererCount = mImpl->renderers.Count();
758 for( unsigned int i = 0; i < rendererCount; ++i )
761 ConstrainPropertyOwner( *mImpl->renderers[i], bufferIndex );
763 mImpl->renderers[i]->PrepareRender( bufferIndex );
767 void UpdateManager::UpdateNodes( BufferIndex bufferIndex )
769 mImpl->nodeDirtyFlags = NothingFlag;
776 // Prepare resources, update shaders, for each node
777 // And add the renderers to the sorted layers. Start from root, which is also a layer
778 mImpl->nodeDirtyFlags = UpdateNodeTree( *( mImpl->root ),
780 mImpl->renderQueue );
782 if ( mImpl->systemLevelRoot )
784 mImpl->nodeDirtyFlags |= UpdateNodeTree( *( mImpl->systemLevelRoot ),
786 mImpl->renderQueue );
790 unsigned int UpdateManager::Update( float elapsedSeconds,
791 unsigned int lastVSyncTimeMilliseconds,
792 unsigned int nextVSyncTimeMilliseconds )
794 const BufferIndex bufferIndex = mSceneGraphBuffers.GetUpdateBufferIndex();
796 //Clear nodes/resources which were previously discarded
797 mImpl->discardQueue.Clear( bufferIndex );
799 //Process Touches & Gestures
800 const bool gestureUpdated = ProcessGestures( bufferIndex, lastVSyncTimeMilliseconds, nextVSyncTimeMilliseconds );
802 bool updateScene = // The scene-graph requires an update if..
803 (mImpl->nodeDirtyFlags & RenderableUpdateFlags) || // ..nodes were dirty in previous frame OR
804 IsAnimationRunning() || // ..at least one animation is running OR
805 mImpl->messageQueue.IsSceneUpdateRequired() || // ..a message that modifies the scene graph node tree is queued OR
806 gestureUpdated; // ..a gesture property was updated
809 // Although the scene-graph may not require an update, we still need to synchronize double-buffered
810 // values if the scene was updated in the previous frame.
811 if( updateScene || mImpl->previousUpdateScene )
813 //Reset properties from the previous update
814 ResetProperties( bufferIndex );
815 mImpl->transformManager.ResetToBaseValue();
818 // Process the queued scene messages. Note, MessageQueue::FlushQueue may be called
819 // between calling IsSceneUpdateRequired() above and here, so updateScene should
821 updateScene |= mImpl->messageQueue.ProcessMessages( bufferIndex );
823 //Forward compiled shader programs to event thread for saving
824 ForwardCompiledShadersToEventThread();
826 // Although the scene-graph may not require an update, we still need to synchronize double-buffered
827 // renderer lists if the scene was updated in the previous frame.
828 // We should not start skipping update steps or reusing lists until there has been two frames where nothing changes
829 if( updateScene || mImpl->previousUpdateScene )
832 Animate( bufferIndex, elapsedSeconds );
834 //Constraint custom objects
835 ConstrainCustomObjects( bufferIndex );
837 //Clear the lists of renderers from the previous update
838 for( size_t i(0); i<mImpl->sortedLayers.size(); ++i )
840 mImpl->sortedLayers[i]->ClearRenderables();
843 for( size_t i(0); i<mImpl->systemLevelSortedLayers.size(); ++i )
845 mImpl->systemLevelSortedLayers[i]->ClearRenderables();
848 //Update node hierarchy, apply constraints and perform sorting / culling.
849 //This will populate each Layer with a list of renderers which are ready.
850 UpdateNodes( bufferIndex );
852 //Apply constraints to RenderTasks, shaders
853 ConstrainRenderTasks( bufferIndex );
854 ConstrainShaders( bufferIndex );
856 //Update renderers and apply constraints
857 UpdateRenderers( bufferIndex );
859 //Update the trnasformations of all the nodes
860 mImpl->transformManager.Update();
862 //Process Property Notifications
863 ProcessPropertyNotifications( bufferIndex );
866 for( auto&& cameraIterator : mImpl->cameras )
868 cameraIterator->Update( bufferIndex );
871 //Process the RenderTasks if renderers exist. This creates the instructions for rendering the next frame.
872 //reset the update buffer index and make sure there is enough room in the instruction container
873 if( mImpl->renderersAdded )
875 mImpl->renderInstructions.ResetAndReserve( bufferIndex,
876 mImpl->taskList.GetTasks().Count() + mImpl->systemLevelTaskList.GetTasks().Count() );
878 if ( NULL != mImpl->root )
880 mImpl->renderTaskProcessor.Process( bufferIndex,
884 mImpl->renderInstructions );
886 // Process the system-level RenderTasks last
887 if ( NULL != mImpl->systemLevelRoot )
889 mImpl->renderTaskProcessor.Process( bufferIndex,
890 mImpl->systemLevelTaskList,
891 *mImpl->systemLevelRoot,
892 mImpl->systemLevelSortedLayers,
893 mImpl->renderInstructions );
899 // check the countdown and notify (note, at the moment this is only done for normal tasks, not for systemlevel tasks)
900 bool doRenderOnceNotify = false;
901 mImpl->renderTaskWaiting = false;
902 for ( auto&& renderTask : mImpl->taskList.GetTasks() )
904 renderTask->UpdateState();
906 if( renderTask->IsWaitingToRender() &&
907 renderTask->ReadyToRender( bufferIndex ) /*avoid updating forever when source actor is off-stage*/ )
909 mImpl->renderTaskWaiting = true; // keep update/render threads alive
912 if( renderTask->HasRendered() )
914 doRenderOnceNotify = true;
918 if( doRenderOnceNotify )
920 DALI_LOG_INFO(gRenderTaskLogFilter, Debug::General, "Notify a render task has finished\n");
921 mImpl->notificationManager.QueueCompleteNotification( mImpl->taskList.GetCompleteNotificationInterface() );
924 // Macro is undefined in release build.
925 SNAPSHOT_NODE_LOGGING;
927 // A ResetProperties() may be required in the next frame
928 mImpl->previousUpdateScene = updateScene;
930 // Check whether further updates are required
931 unsigned int keepUpdating = KeepUpdatingCheck( elapsedSeconds );
933 // tell the update manager that we're done so the queue can be given to event thread
934 mImpl->notificationManager.UpdateCompleted();
936 // The update has finished; swap the double-buffering indices
937 mSceneGraphBuffers.Swap();
942 unsigned int UpdateManager::KeepUpdatingCheck( float elapsedSeconds ) const
944 // Update the duration set via Stage::KeepRendering()
945 if ( mImpl->keepRenderingSeconds > 0.0f )
947 mImpl->keepRenderingSeconds -= elapsedSeconds;
950 unsigned int keepUpdatingRequest = KeepUpdating::NOT_REQUESTED;
952 // If Stage::KeepRendering() has been called, then continue until the duration has elapsed.
953 // Keep updating until no messages are received and no animations are running.
954 // If an animation has just finished, update at least once more for Discard end-actions.
955 // No need to check for renderQueue as there is always a render after update and if that
956 // render needs another update it will tell the adaptor to call update again
958 if ( mImpl->keepRenderingSeconds > 0.0f )
960 keepUpdatingRequest |= KeepUpdating::STAGE_KEEP_RENDERING;
963 if ( IsAnimationRunning() ||
964 mImpl->animationFinishedDuringUpdate )
966 keepUpdatingRequest |= KeepUpdating::ANIMATIONS_RUNNING;
969 if ( mImpl->renderTaskWaiting )
971 keepUpdatingRequest |= KeepUpdating::RENDER_TASK_SYNC;
974 return keepUpdatingRequest;
977 void UpdateManager::SetBackgroundColor( const Vector4& color )
979 typedef MessageValue1< RenderManager, Vector4 > DerivedType;
981 // Reserve some memory inside the render queue
982 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
984 // Construct message in the render queue memory; note that delete should not be called on the return value
985 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetBackgroundColor, color );
988 void UpdateManager::SetDefaultSurfaceRect( const Rect<int>& rect )
990 typedef MessageValue1< RenderManager, Rect<int> > DerivedType;
992 // Reserve some memory inside the render queue
993 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
995 // Construct message in the render queue memory; note that delete should not be called on the return value
996 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetDefaultSurfaceRect, rect );
999 void UpdateManager::KeepRendering( float durationSeconds )
1001 mImpl->keepRenderingSeconds = std::max( mImpl->keepRenderingSeconds, durationSeconds );
1004 void UpdateManager::SetLayerDepths( const SortedLayerPointers& layers, bool systemLevel )
1008 // just copy the vector of pointers
1009 mImpl->sortedLayers = layers;
1013 mImpl->systemLevelSortedLayers = layers;
1017 void UpdateManager::SetDepthIndices( OwnerPointer< NodeDepths >& nodeDepths )
1019 // note,this vector is already in depth order. It could be used as-is to
1020 // remove sorting in update algorithm. However, it lacks layer boundary markers.
1021 for( auto&& iter : nodeDepths->nodeDepths )
1023 iter.node->SetDepthIndex( iter.sortedDepth );
1027 void UpdateManager::AddSampler( OwnerPointer< Render::Sampler >& sampler )
1029 // Message has ownership of Sampler while in transit from update to render
1030 typedef MessageValue1< RenderManager, OwnerPointer< Render::Sampler > > DerivedType;
1032 // Reserve some memory inside the render queue
1033 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1035 // Construct message in the render queue memory; note that delete should not be called on the return value
1036 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AddSampler, sampler );
1039 void UpdateManager::RemoveSampler( Render::Sampler* sampler )
1041 typedef MessageValue1< RenderManager, Render::Sampler* > DerivedType;
1043 // Reserve some memory inside the render queue
1044 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1046 // Construct message in the render queue memory; note that delete should not be called on the return value
1047 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::RemoveSampler, sampler );
1050 void UpdateManager::SetFilterMode( Render::Sampler* sampler, unsigned int minFilterMode, unsigned int magFilterMode )
1052 typedef MessageValue3< RenderManager, Render::Sampler*, unsigned int, unsigned int > DerivedType;
1054 // Reserve some memory inside the render queue
1055 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1057 // Construct message in the render queue memory; note that delete should not be called on the return value
1058 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetFilterMode, sampler, minFilterMode, magFilterMode );
1061 void UpdateManager::SetWrapMode( Render::Sampler* sampler, unsigned int rWrapMode, unsigned int sWrapMode, unsigned int tWrapMode )
1063 typedef MessageValue4< RenderManager, Render::Sampler*, unsigned int, unsigned int, unsigned int > DerivedType;
1065 // Reserve some memory inside the render queue
1066 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1068 // Construct message in the render queue memory; note that delete should not be called on the return value
1069 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetWrapMode, sampler, rWrapMode, sWrapMode, tWrapMode );
1072 void UpdateManager::AddPropertyBuffer( OwnerPointer< Render::PropertyBuffer >& propertyBuffer )
1074 // Message has ownership of format while in transit from update -> render
1075 typedef MessageValue1< RenderManager, OwnerPointer< Render::PropertyBuffer > > DerivedType;
1077 // Reserve some memory inside the render queue
1078 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1080 // Construct message in the render queue memory; note that delete should not be called on the return value
1081 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AddPropertyBuffer, propertyBuffer );
1084 void UpdateManager::RemovePropertyBuffer( Render::PropertyBuffer* propertyBuffer )
1086 typedef MessageValue1< RenderManager, Render::PropertyBuffer* > DerivedType;
1088 // Reserve some memory inside the render queue
1089 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1091 // Construct message in the render queue memory; note that delete should not be called on the return value
1092 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::RemovePropertyBuffer, propertyBuffer );
1095 void UpdateManager::SetPropertyBufferFormat( Render::PropertyBuffer* propertyBuffer, OwnerPointer< Render::PropertyBuffer::Format>& format )
1097 // Message has ownership of format while in transit from update -> render
1098 typedef MessageValue2< RenderManager, Render::PropertyBuffer*, OwnerPointer< Render::PropertyBuffer::Format > > DerivedType;
1100 // Reserve some memory inside the render queue
1101 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1103 // Construct message in the render queue memory; note that delete should not be called on the return value
1104 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetPropertyBufferFormat, propertyBuffer, format );
1107 void UpdateManager::SetPropertyBufferData( Render::PropertyBuffer* propertyBuffer, OwnerPointer< Vector<char> >& data, size_t size )
1109 // Message has ownership of format while in transit from update -> render
1110 typedef MessageValue3< RenderManager, Render::PropertyBuffer*, OwnerPointer< Dali::Vector<char> >, size_t > DerivedType;
1112 // Reserve some memory inside the render queue
1113 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1115 // Construct message in the render queue memory; note that delete should not be called on the return value
1116 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetPropertyBufferData, propertyBuffer, data, size );
1119 void UpdateManager::AddGeometry( OwnerPointer< Render::Geometry >& geometry )
1121 // Message has ownership of format while in transit from update -> render
1122 typedef MessageValue1< RenderManager, OwnerPointer< Render::Geometry > > DerivedType;
1124 // Reserve some memory inside the render queue
1125 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1127 // Construct message in the render queue memory; note that delete should not be called on the return value
1128 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AddGeometry, geometry );
1131 void UpdateManager::RemoveGeometry( Render::Geometry* geometry )
1133 typedef MessageValue1< RenderManager, Render::Geometry* > DerivedType;
1135 // Reserve some memory inside the render queue
1136 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1138 // Construct message in the render queue memory; note that delete should not be called on the return value
1139 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::RemoveGeometry, geometry );
1142 void UpdateManager::SetGeometryType( Render::Geometry* geometry, unsigned int geometryType )
1144 typedef MessageValue2< RenderManager, Render::Geometry*, unsigned int > DerivedType;
1146 // Reserve some memory inside the render queue
1147 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1149 // Construct message in the render queue memory; note that delete should not be called on the return value
1150 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::SetGeometryType, geometry, geometryType );
1153 void UpdateManager::SetIndexBuffer( Render::Geometry* geometry, Dali::Vector<unsigned short>& indices )
1155 typedef IndexBufferMessage< RenderManager > DerivedType;
1157 // Reserve some memory inside the render queue
1158 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1160 // Construct message in the render queue memory; note that delete should not be called on the return value
1161 new (slot) DerivedType( &mImpl->renderManager, geometry, indices );
1164 void UpdateManager::RemoveVertexBuffer( Render::Geometry* geometry, Render::PropertyBuffer* propertyBuffer )
1166 typedef MessageValue2< RenderManager, Render::Geometry*, Render::PropertyBuffer* > DerivedType;
1168 // Reserve some memory inside the render queue
1169 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1171 // Construct message in the render queue memory; note that delete should not be called on the return value
1172 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::RemoveVertexBuffer, geometry, propertyBuffer );
1175 void UpdateManager::AttachVertexBuffer( Render::Geometry* geometry, Render::PropertyBuffer* propertyBuffer )
1177 typedef MessageValue2< RenderManager, Render::Geometry*, Render::PropertyBuffer* > DerivedType;
1179 // Reserve some memory inside the render queue
1180 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1182 // Construct message in the render queue memory; note that delete should not be called on the return value
1183 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AttachVertexBuffer, geometry, propertyBuffer );
1186 void UpdateManager::AddTexture( OwnerPointer< Render::Texture >& texture )
1188 // Message has ownership of Texture while in transit from update -> render
1189 typedef MessageValue1< RenderManager, OwnerPointer< Render::Texture > > DerivedType;
1191 // Reserve some memory inside the render queue
1192 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1194 // Construct message in the render queue memory; note that delete should not be called on the return value
1195 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AddTexture, texture );
1198 void UpdateManager::RemoveTexture( Render::Texture* texture)
1200 typedef MessageValue1< RenderManager, Render::Texture* > DerivedType;
1202 // Reserve some memory inside the render queue
1203 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1205 // Construct message in the render queue memory; note that delete should not be called on the return value
1206 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::RemoveTexture, texture );
1209 void UpdateManager::UploadTexture( Render::Texture* texture, PixelDataPtr pixelData, const Texture::UploadParams& params )
1211 typedef MessageValue3< RenderManager, Render::Texture*, PixelDataPtr, Texture::UploadParams > DerivedType;
1213 // Reserve some memory inside the message queue
1214 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1216 // Construct message in the message queue memory; note that delete should not be called on the return value
1217 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::UploadTexture, texture, pixelData, params );
1220 void UpdateManager::GenerateMipmaps( Render::Texture* texture )
1222 typedef MessageValue1< RenderManager, Render::Texture* > DerivedType;
1224 // Reserve some memory inside the render queue
1225 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1227 // Construct message in the render queue memory; note that delete should not be called on the return value
1228 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::GenerateMipmaps, texture );
1231 void UpdateManager::AddFrameBuffer( Render::FrameBuffer* frameBuffer )
1233 typedef MessageValue1< RenderManager, Render::FrameBuffer* > DerivedType;
1235 // Reserve some memory inside the render queue
1236 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1238 // Construct message in the render queue memory; note that delete should not be called on the return value
1239 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AddFrameBuffer, frameBuffer );
1242 void UpdateManager::RemoveFrameBuffer( Render::FrameBuffer* frameBuffer)
1244 typedef MessageValue1< RenderManager, Render::FrameBuffer* > DerivedType;
1246 // Reserve some memory inside the render queue
1247 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1249 // Construct message in the render queue memory; note that delete should not be called on the return value
1250 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::RemoveFrameBuffer, frameBuffer );
1253 void UpdateManager::AttachColorTextureToFrameBuffer( Render::FrameBuffer* frameBuffer, Render::Texture* texture, unsigned int mipmapLevel, unsigned int layer )
1255 typedef MessageValue4< RenderManager, Render::FrameBuffer*, Render::Texture*, unsigned int, unsigned int > DerivedType;
1257 // Reserve some memory inside the render queue
1258 unsigned int* slot = mImpl->renderQueue.ReserveMessageSlot( mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof( DerivedType ) );
1260 // Construct message in the render queue memory; note that delete should not be called on the return value
1261 new (slot) DerivedType( &mImpl->renderManager, &RenderManager::AttachColorTextureToFrameBuffer, frameBuffer, texture, mipmapLevel, layer );
1264 } // namespace SceneGraph
1266 } // namespace Internal