Merge "Removed redundant resource loading & rendering code" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / render / common / render-manager.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/render/common/render-manager.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/actors/sampling.h>
23 #include <dali/public-api/common/dali-common.h>
24 #include <dali/public-api/common/stage.h>
25 #include <dali/public-api/render-tasks/render-task.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/core.h>
28 #include <dali/internal/common/owner-pointer.h>
29 #include <dali/internal/render/common/render-algorithms.h>
30 #include <dali/internal/render/common/render-debug.h>
31 #include <dali/internal/render/common/render-tracker.h>
32 #include <dali/internal/render/common/render-instruction-container.h>
33 #include <dali/internal/render/common/render-instruction.h>
34 #include <dali/internal/render/gl-resources/context.h>
35 #include <dali/internal/render/queue/render-queue.h>
36 #include <dali/internal/render/renderers/render-frame-buffer.h>
37 #include <dali/internal/render/renderers/render-geometry.h>
38 #include <dali/internal/render/renderers/render-renderer.h>
39 #include <dali/internal/render/renderers/render-sampler.h>
40 #include <dali/internal/render/shaders/program-controller.h>
41
42 namespace Dali
43 {
44
45 namespace Internal
46 {
47
48 namespace SceneGraph
49 {
50
51 typedef OwnerContainer< Render::Renderer* >    RendererOwnerContainer;
52 typedef RendererOwnerContainer::Iterator       RendererOwnerIter;
53
54 typedef OwnerContainer< Render::Geometry* >    GeometryOwnerContainer;
55 typedef GeometryOwnerContainer::Iterator       GeometryOwnerIter;
56
57 typedef OwnerContainer< Render::Sampler* >    SamplerOwnerContainer;
58 typedef SamplerOwnerContainer::Iterator       SamplerOwnerIter;
59
60 typedef OwnerContainer< Render::Texture* >   TextureOwnerContainer;
61 typedef TextureOwnerContainer::Iterator         TextureOwnerIter;
62
63 typedef OwnerContainer< Render::FrameBuffer* >  FrameBufferOwnerContainer;
64 typedef FrameBufferOwnerContainer::Iterator     FrameBufferOwnerIter;
65
66 typedef OwnerContainer< Render::PropertyBuffer* > PropertyBufferOwnerContainer;
67 typedef PropertyBufferOwnerContainer::Iterator    PropertyBufferOwnerIter;
68
69 typedef OwnerContainer< Render::RenderTracker* > RenderTrackerContainer;
70 typedef RenderTrackerContainer::Iterator         RenderTrackerIter;
71 typedef RenderTrackerContainer::ConstIterator    RenderTrackerConstIter;
72
73 /**
74  * Structure to contain internal data
75  */
76 struct RenderManager::Impl
77 {
78   Impl( Integration::GlAbstraction& glAbstraction,
79         Integration::GlSyncAbstraction& glSyncAbstraction )
80   : context( glAbstraction ),
81     glSyncAbstraction( glSyncAbstraction ),
82     renderQueue(),
83     instructions(),
84     backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ),
85     frameCount( 0 ),
86     renderBufferIndex( SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX ),
87     defaultSurfaceRect(),
88     rendererContainer(),
89     samplerContainer(),
90     textureContainer(),
91     frameBufferContainer(),
92     renderersAdded( false ),
93     firstRenderCompleted( false ),
94     defaultShader( NULL ),
95     programController( glAbstraction )
96   {
97   }
98
99   ~Impl()
100   {
101   }
102
103   void AddRenderTracker( Render::RenderTracker* renderTracker )
104   {
105     DALI_ASSERT_DEBUG( renderTracker != NULL );
106     mRenderTrackers.PushBack( renderTracker );
107   }
108
109   void RemoveRenderTracker( Render::RenderTracker* renderTracker )
110   {
111     DALI_ASSERT_DEBUG( renderTracker != NULL );
112     for(RenderTrackerIter iter = mRenderTrackers.Begin(), end = mRenderTrackers.End(); iter != end; ++iter)
113     {
114       if( *iter == renderTracker )
115       {
116         mRenderTrackers.Erase( iter );
117         break;
118       }
119     }
120   }
121
122   void UpdateTrackers()
123   {
124     for(RenderTrackerIter iter = mRenderTrackers.Begin(), end = mRenderTrackers.End(); iter != end; ++iter)
125     {
126       (*iter)->PollSyncObject();
127     }
128   }
129
130   // the order is important for destruction,
131   // programs are owned by context at the moment.
132   Context                       context;                  ///< holds the GL state
133   Integration::GlSyncAbstraction& glSyncAbstraction;      ///< GL sync abstraction
134   RenderQueue                   renderQueue;              ///< A message queue for receiving messages from the update-thread.
135
136   // Render instructions describe what should be rendered during RenderManager::Render()
137   // Owned by RenderManager. Update manager updates instructions for the next frame while we render the current one
138   RenderInstructionContainer    instructions;
139
140   Vector4                       backgroundColor;          ///< The glClear color used at the beginning of each frame.
141
142   unsigned int                  frameCount;               ///< The current frame count
143   BufferIndex                   renderBufferIndex;        ///< The index of the buffer to read from; this is opposite of the "update" buffer
144
145   Rect<int>                     defaultSurfaceRect;       ///< Rectangle for the default surface we are rendering to
146
147   RendererOwnerContainer        rendererContainer;        ///< List of owned renderers
148   SamplerOwnerContainer         samplerContainer;         ///< List of owned samplers
149   TextureOwnerContainer         textureContainer;         ///< List of owned textures
150   FrameBufferOwnerContainer     frameBufferContainer;     ///< List of owned framebuffers
151   PropertyBufferOwnerContainer  propertyBufferContainer;  ///< List of owned property buffers
152   GeometryOwnerContainer        geometryContainer;        ///< List of owned Geometries
153
154   bool                          renderersAdded;
155
156   RenderTrackerContainer        mRenderTrackers;          ///< List of render trackers
157
158   bool                          firstRenderCompleted;     ///< False until the first render is done
159   Shader*                       defaultShader;            ///< Default shader to use
160   ProgramController             programController;        ///< Owner of the GL programs
161
162 };
163
164 RenderManager* RenderManager::New( Integration::GlAbstraction& glAbstraction,
165                                    Integration::GlSyncAbstraction& glSyncAbstraction )
166 {
167   RenderManager* manager = new RenderManager;
168   manager->mImpl = new Impl( glAbstraction,
169                              glSyncAbstraction );
170   return manager;
171 }
172
173 RenderManager::RenderManager()
174 : mImpl(NULL)
175 {
176 }
177
178 RenderManager::~RenderManager()
179 {
180   delete mImpl;
181 }
182
183 RenderQueue& RenderManager::GetRenderQueue()
184 {
185   return mImpl->renderQueue;
186 }
187
188 void RenderManager::ContextCreated()
189 {
190   mImpl->context.GlContextCreated();
191   mImpl->programController.GlContextCreated();
192
193   // renderers, textures and gpu buffers cannot reinitialize themselves
194   // so they rely on someone reloading the data for them
195 }
196
197 void RenderManager::ContextDestroyed()
198 {
199   mImpl->context.GlContextDestroyed();
200   mImpl->programController.GlContextDestroyed();
201
202   //Inform textures
203   for( TextureOwnerIter iter = mImpl->textureContainer.Begin(); iter != mImpl->textureContainer.End(); ++iter )
204   {
205     (*iter)->GlContextDestroyed();
206   }
207
208   //Inform framebuffers
209   for( FrameBufferOwnerIter iter = mImpl->frameBufferContainer.Begin(); iter != mImpl->frameBufferContainer.End(); ++iter )
210   {
211     (*iter)->GlContextDestroyed();
212   }
213
214   // inform renderers
215   RendererOwnerContainer::Iterator end = mImpl->rendererContainer.End();
216   RendererOwnerContainer::Iterator iter = mImpl->rendererContainer.Begin();
217   for( ; iter != end; ++iter )
218   {
219     GlResourceOwner* renderer = *iter;
220     renderer->GlContextDestroyed(); // Clear up vertex buffers
221   }
222 }
223
224 void RenderManager::SetShaderSaver( ShaderSaver& upstream )
225 {
226   mImpl->programController.SetShaderSaver( upstream );
227 }
228
229 RenderInstructionContainer& RenderManager::GetRenderInstructionContainer()
230 {
231   return mImpl->instructions;
232 }
233
234 void RenderManager::SetBackgroundColor( const Vector4& color )
235 {
236   mImpl->backgroundColor = color;
237 }
238
239 void RenderManager::SetDefaultSurfaceRect(const Rect<int>& rect)
240 {
241   mImpl->defaultSurfaceRect = rect;
242 }
243
244 void RenderManager::AddRenderer( Render::Renderer* renderer )
245 {
246   // Initialize the renderer as we are now in render thread
247   renderer->Initialize( mImpl->context );
248
249   mImpl->rendererContainer.PushBack( renderer );
250
251   if( !mImpl->renderersAdded )
252   {
253     mImpl->renderersAdded = true;
254   }
255 }
256
257 void RenderManager::RemoveRenderer( Render::Renderer* renderer )
258 {
259   DALI_ASSERT_DEBUG( NULL != renderer );
260
261   RendererOwnerContainer& renderers = mImpl->rendererContainer;
262
263   // Find the renderer
264   for ( RendererOwnerIter iter = renderers.Begin(); iter != renderers.End(); ++iter )
265   {
266     if ( *iter == renderer )
267     {
268       renderers.Erase( iter ); // Renderer found; now destroy it
269       break;
270     }
271   }
272 }
273
274 void RenderManager::AddSampler( Render::Sampler* sampler )
275 {
276   mImpl->samplerContainer.PushBack( sampler );
277 }
278
279 void RenderManager::RemoveSampler( Render::Sampler* sampler )
280 {
281   DALI_ASSERT_DEBUG( NULL != sampler );
282
283   SamplerOwnerContainer& samplers = mImpl->samplerContainer;
284
285   // Find the sampler
286   for ( SamplerOwnerIter iter = samplers.Begin(); iter != samplers.End(); ++iter )
287   {
288     if ( *iter == sampler )
289     {
290       samplers.Erase( iter ); // Sampler found; now destroy it
291       break;
292     }
293   }
294 }
295
296 void RenderManager::AddTexture( Render::Texture* texture )
297 {
298   mImpl->textureContainer.PushBack( texture );
299   texture->Initialize(mImpl->context);
300 }
301
302 void RenderManager::RemoveTexture( Render::Texture* texture )
303 {
304   DALI_ASSERT_DEBUG( NULL != texture );
305
306   TextureOwnerContainer& textures = mImpl->textureContainer;
307
308   // Find the texture
309   for ( TextureOwnerIter iter = textures.Begin(); iter != textures.End(); ++iter )
310   {
311     if ( *iter == texture )
312     {
313       texture->Destroy( mImpl->context );
314       textures.Erase( iter ); // Texture found; now destroy it
315       break;
316     }
317   }
318 }
319
320 void RenderManager::UploadTexture( Render::Texture* texture, PixelDataPtr pixelData, const Texture::UploadParams& params )
321 {
322   texture->Upload( mImpl->context, pixelData, params );
323 }
324
325 void RenderManager::GenerateMipmaps( Render::Texture* texture )
326 {
327   texture->GenerateMipmaps( mImpl->context );
328 }
329
330 void RenderManager::SetFilterMode( Render::Sampler* sampler, unsigned int minFilterMode, unsigned int magFilterMode )
331 {
332   sampler->mMinificationFilter = static_cast<Dali::FilterMode::Type>(minFilterMode);
333   sampler->mMagnificationFilter = static_cast<Dali::FilterMode::Type>(magFilterMode );
334 }
335
336 void RenderManager::SetWrapMode( Render::Sampler* sampler, unsigned int rWrapMode, unsigned int sWrapMode, unsigned int tWrapMode )
337 {
338   sampler->mRWrapMode = static_cast<Dali::WrapMode::Type>(rWrapMode);
339   sampler->mSWrapMode = static_cast<Dali::WrapMode::Type>(sWrapMode);
340   sampler->mTWrapMode = static_cast<Dali::WrapMode::Type>(tWrapMode);
341 }
342
343 void RenderManager::AddFrameBuffer( Render::FrameBuffer* frameBuffer )
344 {
345   mImpl->frameBufferContainer.PushBack( frameBuffer );
346   frameBuffer->Initialize(mImpl->context);
347 }
348
349 void RenderManager::RemoveFrameBuffer( Render::FrameBuffer* frameBuffer )
350 {
351   DALI_ASSERT_DEBUG( NULL != frameBuffer );
352
353   FrameBufferOwnerContainer& framebuffers = mImpl->frameBufferContainer;
354
355   // Find the sampler
356   for ( FrameBufferOwnerIter iter = framebuffers.Begin(); iter != framebuffers.End(); ++iter )
357   {
358     if ( *iter == frameBuffer )
359     {
360       frameBuffer->Destroy( mImpl->context );
361       framebuffers.Erase( iter ); // frameBuffer found; now destroy it
362       break;
363     }
364   }
365 }
366
367 void RenderManager::AttachColorTextureToFrameBuffer( Render::FrameBuffer* frameBuffer, Render::Texture* texture, unsigned int mipmapLevel, unsigned int layer )
368 {
369   frameBuffer->AttachColorTexture( mImpl->context, texture, mipmapLevel, layer );
370 }
371
372 void RenderManager::AddPropertyBuffer( Render::PropertyBuffer* propertyBuffer )
373 {
374   mImpl->propertyBufferContainer.PushBack( propertyBuffer );
375 }
376
377 void RenderManager::RemovePropertyBuffer( Render::PropertyBuffer* propertyBuffer )
378 {
379   DALI_ASSERT_DEBUG( NULL != propertyBuffer );
380
381   PropertyBufferOwnerContainer& propertyBuffers = mImpl->propertyBufferContainer;
382
383   // Find the sampler
384   for ( PropertyBufferOwnerIter iter = propertyBuffers.Begin(); iter != propertyBuffers.End(); ++iter )
385   {
386     if ( *iter == propertyBuffer )
387     {
388       propertyBuffers.Erase( iter ); // Property buffer found; now destroy it
389       break;
390     }
391   }
392 }
393
394 void RenderManager::SetPropertyBufferFormat(Render::PropertyBuffer* propertyBuffer, Render::PropertyBuffer::Format* format )
395 {
396   propertyBuffer->SetFormat( format );
397 }
398
399 void RenderManager::SetPropertyBufferData( Render::PropertyBuffer* propertyBuffer, Dali::Vector<char>* data, size_t size )
400 {
401   propertyBuffer->SetData( data, size );
402 }
403
404 void RenderManager::SetIndexBuffer( Render::Geometry* geometry, Dali::Vector<unsigned short>& indices )
405 {
406   geometry->SetIndexBuffer( indices );
407 }
408
409 void RenderManager::AddGeometry( Render::Geometry* geometry )
410 {
411   mImpl->geometryContainer.PushBack( geometry );
412 }
413
414 void RenderManager::RemoveGeometry( Render::Geometry* geometry )
415 {
416   DALI_ASSERT_DEBUG( NULL != geometry );
417
418   GeometryOwnerContainer& geometries = mImpl->geometryContainer;
419
420   // Find the geometry
421   for ( GeometryOwnerIter iter = geometries.Begin(); iter != geometries.End(); ++iter )
422   {
423     if ( *iter == geometry )
424     {
425       geometries.Erase( iter ); // Geometry found; now destroy it
426       break;
427     }
428   }
429 }
430
431 void RenderManager::AddVertexBuffer( Render::Geometry* geometry, Render::PropertyBuffer* propertyBuffer )
432 {
433   DALI_ASSERT_DEBUG( NULL != geometry );
434
435   GeometryOwnerContainer& geometries = mImpl->geometryContainer;
436
437   // Find the renderer
438   for ( GeometryOwnerIter iter = geometries.Begin(); iter != geometries.End(); ++iter )
439   {
440     if ( *iter == geometry )
441     {
442       (*iter)->AddPropertyBuffer( propertyBuffer );
443       break;
444     }
445   }
446 }
447
448 void RenderManager::RemoveVertexBuffer( Render::Geometry* geometry, Render::PropertyBuffer* propertyBuffer )
449 {
450   DALI_ASSERT_DEBUG( NULL != geometry );
451
452   GeometryOwnerContainer& geometries = mImpl->geometryContainer;
453
454   // Find the renderer
455   for ( GeometryOwnerIter iter = geometries.Begin(); iter != geometries.End(); ++iter )
456   {
457     if ( *iter == geometry )
458     {
459       (*iter)->RemovePropertyBuffer( propertyBuffer );
460       break;
461     }
462   }
463 }
464
465 void RenderManager::SetGeometryType( Render::Geometry* geometry, unsigned int geometryType )
466 {
467   geometry->SetType( Render::Geometry::Type(geometryType) );
468 }
469
470 void RenderManager::AddRenderTracker( Render::RenderTracker* renderTracker )
471 {
472   mImpl->AddRenderTracker(renderTracker);
473 }
474
475 void RenderManager::RemoveRenderTracker( Render::RenderTracker* renderTracker )
476 {
477   mImpl->RemoveRenderTracker(renderTracker);
478 }
479
480 void RenderManager::SetDefaultShader( Shader* shader )
481 {
482   mImpl->defaultShader = shader;
483 }
484
485 ProgramCache* RenderManager::GetProgramCache()
486 {
487   return &(mImpl->programController);
488 }
489
490 bool RenderManager::Render( Integration::RenderStatus& status )
491 {
492   DALI_PRINT_RENDER_START( mImpl->renderBufferIndex );
493
494   // Core::Render documents that GL context must be current before calling Render
495   DALI_ASSERT_DEBUG( mImpl->context.IsGlContextCreated() );
496
497   // Increment the frame count at the beginning of each frame
498   ++(mImpl->frameCount);
499
500   // Process messages queued during previous update
501   mImpl->renderQueue.ProcessMessages( mImpl->renderBufferIndex );
502
503   // No need to make any gl calls if we've done 1st glClear & don't have any renderers to render during startup.
504   if( !mImpl->firstRenderCompleted || mImpl->renderersAdded )
505   {
506     // switch rendering to adaptor provided (default) buffer
507     mImpl->context.BindFramebuffer( GL_FRAMEBUFFER, 0 );
508
509     mImpl->context.Viewport( mImpl->defaultSurfaceRect.x,
510                              mImpl->defaultSurfaceRect.y,
511                              mImpl->defaultSurfaceRect.width,
512                              mImpl->defaultSurfaceRect.height );
513
514     mImpl->context.ClearColor( mImpl->backgroundColor.r,
515                                mImpl->backgroundColor.g,
516                                mImpl->backgroundColor.b,
517                                mImpl->backgroundColor.a );
518
519     mImpl->context.ClearStencil( 0 );
520
521     // Clear the entire color, depth and stencil buffers for the default framebuffer.
522     // It is important to clear all 3 buffers, for performance on deferred renderers like Mali
523     // e.g. previously when the depth & stencil buffers were NOT cleared, it caused the DDK to exceed a "vertex count limit",
524     // and then stall. That problem is only noticeable when rendering a large number of vertices per frame.
525     mImpl->context.SetScissorTest( false );
526     mImpl->context.ColorMask( true );
527     mImpl->context.DepthMask( true );
528     mImpl->context.StencilMask( 0xFF ); // 8 bit stencil mask, all 1's
529     mImpl->context.Clear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,  Context::FORCE_CLEAR );
530
531     // reset the program matrices for all programs once per frame
532     // this ensures we will set view and projection matrix once per program per camera
533     mImpl->programController.ResetProgramMatrices();
534
535     // if we don't have default shader, no point doing the render calls
536     if( mImpl->defaultShader )
537     {
538       size_t count = mImpl->instructions.Count( mImpl->renderBufferIndex );
539       for ( size_t i = 0; i < count; ++i )
540       {
541         RenderInstruction& instruction = mImpl->instructions.At( mImpl->renderBufferIndex, i );
542
543         DoRender( instruction, *mImpl->defaultShader );
544       }
545       GLenum attachments[] = { GL_DEPTH, GL_STENCIL };
546       mImpl->context.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
547
548       mImpl->UpdateTrackers();
549
550       mImpl->firstRenderCompleted = true;
551     }
552   }
553
554   //Notify RenderGeometries that rendering has finished
555   for ( GeometryOwnerIter iter = mImpl->geometryContainer.Begin(); iter != mImpl->geometryContainer.End(); ++iter )
556   {
557     (*iter)->OnRenderFinished();
558   }
559
560   /**
561    * The rendering has finished; swap to the next buffer.
562    * Ideally the update has just finished using this buffer; otherwise the render thread
563    * should block until the update has finished.
564    */
565   mImpl->renderBufferIndex = (0 != mImpl->renderBufferIndex) ? 0 : 1;
566
567   DALI_PRINT_RENDER_END();
568
569   return false;
570 }
571
572 void RenderManager::DoRender( RenderInstruction& instruction, Shader& defaultShader )
573 {
574   Rect<int> viewportRect;
575   Vector4   clearColor;
576
577   if ( instruction.mIsClearColorSet )
578   {
579     clearColor = instruction.mClearColor;
580   }
581   else
582   {
583     clearColor = Dali::RenderTask::DEFAULT_CLEAR_COLOR;
584   }
585
586   if( instruction.mFrameBuffer != 0 )
587   {
588     instruction.mFrameBuffer->Bind( mImpl->context );
589     if ( instruction.mIsViewportSet )
590     {
591       // For glViewport the lower-left corner is (0,0)
592       const int y = ( instruction.mFrameBuffer->GetHeight() - instruction.mViewport.height ) - instruction.mViewport.y;
593       viewportRect.Set( instruction.mViewport.x,  y, instruction.mViewport.width, instruction.mViewport.height );
594     }
595     else
596     {
597       viewportRect.Set( 0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight() );
598     }
599   }
600   else // !(instruction.mOffscreenTexture)
601   {
602     // switch rendering to adaptor provided (default) buffer
603     mImpl->context.BindFramebuffer( GL_FRAMEBUFFER, 0 );
604
605     // Check whether a viewport is specified, otherwise the full surface size is used
606     if ( instruction.mIsViewportSet )
607     {
608       // For glViewport the lower-left corner is (0,0)
609       const int y = ( mImpl->defaultSurfaceRect.height - instruction.mViewport.height ) - instruction.mViewport.y;
610       viewportRect.Set( instruction.mViewport.x,  y, instruction.mViewport.width, instruction.mViewport.height );
611     }
612     else
613     {
614       viewportRect = mImpl->defaultSurfaceRect;
615     }
616   }
617
618   mImpl->context.Viewport(viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height);
619
620   if ( instruction.mIsClearColorSet )
621   {
622     mImpl->context.ClearColor( clearColor.r,
623                                clearColor.g,
624                                clearColor.b,
625                                clearColor.a );
626
627     // Clear the viewport area only
628     mImpl->context.SetScissorTest( true );
629     mImpl->context.Scissor( viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height );
630     mImpl->context.ColorMask( true );
631     mImpl->context.Clear( GL_COLOR_BUFFER_BIT , Context::CHECK_CACHED_VALUES );
632     mImpl->context.SetScissorTest( false );
633   }
634
635   Render::ProcessRenderInstruction( instruction,
636                                     mImpl->context,
637                                     defaultShader,
638                                     mImpl->renderBufferIndex );
639
640   if( instruction.mRenderTracker && ( instruction.mFrameBuffer != NULL ) )
641   {
642     // This will create a sync object every frame this render tracker
643     // is alive (though it should be now be created only for
644     // render-once render tasks)
645     instruction.mRenderTracker->CreateSyncObject( mImpl->glSyncAbstraction );
646     instruction.mRenderTracker = NULL; // Only create once.
647   }
648 }
649
650 } // namespace SceneGraph
651
652 } // namespace Internal
653
654 } // namespace Dali