bfa9185671e95b88e3424d9bc7e4cb56809a76ad
[platform/core/uifw/dali-core.git] / dali / internal / render / gl-resources / texture-cache.cpp
1 /*
2  * Copyright (c) 2014 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 #include <dali/internal/render/gl-resources/texture-cache.h>
19
20 #include <dali/integration-api/bitmap.h>
21
22 #include <dali/internal/update/resources/resource-manager-declarations.h>
23 #include <dali/internal/render/common/post-process-resource-dispatcher.h>
24 #include <dali/internal/render/queue/render-queue.h>
25 #include <dali/internal/render/gl-resources/context.h>
26 #include <dali/internal/render/gl-resources/texture-factory.h>
27 #include <dali/internal/render/gl-resources/texture.h>
28 #include <dali/internal/render/gl-resources/texture-observer.h>
29 #include <dali/internal/render/gl-resources/bitmap-texture.h>
30 #include <dali/internal/render/gl-resources/native-texture.h>
31 #include <dali/internal/render/gl-resources/frame-buffer-texture.h>
32
33 #include <dali/integration-api/debug.h>
34
35 using Dali::Internal::Texture;
36 using Dali::Internal::FrameBufferTexture;
37 using Dali::Integration::Bitmap;
38
39 namespace
40 {
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gTextureCacheFilter = Debug::Filter::New(Debug::Concise, false, "LOG_TEXTURE_CACHE");
43 #endif
44 }
45
46
47 namespace Dali
48 {
49 namespace Internal
50 {
51
52 // value types used by messages
53 template <> struct ParameterType< Pixel::Format > : public BasicType< Pixel::Format > {};
54
55 namespace SceneGraph
56 {
57
58 TextureCache::TextureCache( RenderQueue& renderQueue,
59                             PostProcessResourceDispatcher& postProcessResourceDispatcher,
60                             Context& context)
61 : TextureCacheDispatcher(renderQueue),
62   mPostProcessResourceDispatcher(postProcessResourceDispatcher),
63   mContext(context),
64   mDiscardBitmapsPolicy(ResourcePolicy::DISCARD)
65 {
66 }
67
68 TextureCache::~TextureCache()
69 {
70 }
71
72 void TextureCache::CreateTexture( ResourceId        id,
73                                   unsigned int      width,
74                                   unsigned int      height,
75                                   Pixel::Format     pixelFormat,
76                                   bool              clearPixels )
77 {
78   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::CreateTexture(id=%i width:%u height:%u)\n", id, width, height);
79
80   Texture* texture = TextureFactory::NewBitmapTexture(width, height, pixelFormat, clearPixels, mContext, GetDiscardBitmapsPolicy() );
81   mTextures.insert(TexturePair(id, texture));
82 }
83
84 void TextureCache::AddBitmap(ResourceId id, Integration::BitmapPtr bitmap)
85 {
86   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::AddBitmap(id=%i Bitmap:%p)\n", id, bitmap.Get());
87
88   Texture* texture = TextureFactory::NewBitmapTexture(bitmap.Get(), mContext, GetDiscardBitmapsPolicy());
89   mTextures.insert(TexturePair(id, texture));
90 }
91
92 void TextureCache::AddNativeImage(ResourceId id, NativeImagePtr nativeImage)
93 {
94   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::AddNativeImage(id=%i NativeImg:%p)\n", id, nativeImage.Get());
95
96   /// TODO - currently a new Texture is created even if we reuse the same NativeImage
97   Texture* texture = TextureFactory::NewNativeImageTexture(*nativeImage, mContext);
98   mTextures.insert(TexturePair(id, texture));
99 }
100
101 void TextureCache::AddFrameBuffer( ResourceId id, unsigned int width, unsigned int height, Pixel::Format pixelFormat )
102 {
103   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::AddFrameBuffer(id=%i width:%u height:%u)\n", id, width, height);
104
105   // Note: Do not throttle framebuffer generation - a request for a framebuffer should always be honoured
106   // as soon as possible.
107   Texture* texture = TextureFactory::NewFrameBufferTexture( width, height, pixelFormat, mContext );
108   mFramebufferTextures.insert(TexturePair(id, texture));
109 }
110
111 void TextureCache::AddFrameBuffer( ResourceId id, NativeImagePtr nativeImage )
112 {
113   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::AddFrameBuffer(id=%i width:%u height:%u)\n", id, nativeImage->GetWidth(), nativeImage->GetHeight());
114
115   // Note: Do not throttle framebuffer generation - a request for a framebuffer should always be honoured
116   // as soon as possible.
117   Texture* texture = TextureFactory::NewFrameBufferTexture( nativeImage, mContext );
118   mFramebufferTextures.insert(TexturePair(id, texture));
119 }
120
121 void TextureCache::UpdateTexture( ResourceId id, Integration::BitmapPtr bitmap )
122 {
123   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::UpdateTexture(id=%i bitmap:%p )\n", id, bitmap.Get());
124
125   TextureIter textureIter = mTextures.find(id);
126   if( textureIter != mTextures.end() )
127   {
128     // we have reloaded the image from file, update texture
129     TexturePointer texturePtr = textureIter->second;
130     if( texturePtr )
131     {
132       texturePtr->Update( bitmap.Get() );
133
134       ResourcePostProcessRequest ppRequest( id, ResourcePostProcessRequest::UPLOADED );
135       mPostProcessResourceDispatcher.DispatchPostProcessRequest(ppRequest);
136     }
137   }
138 }
139
140 void TextureCache::UpdateTexture( ResourceId destId, ResourceId srcId, std::size_t xOffset, std::size_t yOffset )
141 {
142   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::UpdateTexture(destId=%i srcId=%i )\n", destId, srcId );
143
144   BitmapTexture* srcTexture = TextureCache::GetBitmapTexture( srcId );
145   Integration::Bitmap* srcBitmap = ( srcTexture != NULL ) ? srcTexture->GetBitmap() : NULL;
146
147   if( srcBitmap )
148   {
149     TextureIter textureIter = mTextures.find( destId );
150     if( textureIter != mTextures.end() )
151     {
152       TexturePointer texturePtr = textureIter->second;
153       if( texturePtr )
154       {
155         texturePtr->Update( srcBitmap, xOffset, yOffset );
156
157         ResourcePostProcessRequest ppRequest( srcId, ResourcePostProcessRequest::UPLOADED );
158         mPostProcessResourceDispatcher.DispatchPostProcessRequest(ppRequest);
159       }
160     }
161   }
162 }
163
164 void TextureCache::UpdateTextureArea( ResourceId id, const Dali::RectArea& area )
165 {
166   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::UpdateTextureArea(id=%i)\n", id );
167
168   TextureIter textureIter = mTextures.find(id);
169   if( textureIter != mTextures.end() )
170   {
171     TexturePointer texturePtr = textureIter->second;
172     if( texturePtr )
173     {
174       texturePtr->UpdateArea( area );
175
176       ResourcePostProcessRequest ppRequest( id, ResourcePostProcessRequest::UPLOADED );
177       mPostProcessResourceDispatcher.DispatchPostProcessRequest(ppRequest);
178     }
179   }
180 }
181
182 void TextureCache::AddBitmapUploadArray( ResourceId id, const BitmapUploadArray& uploadArray )
183 {
184   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::AddBitmapUploadArray(id=%i )\n", id);
185
186   TextureIter textureIter = mTextures.find(id);
187
188   DALI_ASSERT_DEBUG( textureIter != mTextures.end() );
189   if( textureIter != mTextures.end() )
190   {
191     TexturePointer texturePtr = textureIter->second;
192     if( texturePtr )
193     {
194       BitmapTexture* texture = static_cast< BitmapTexture* >( texturePtr.Get() );
195       texture->UploadBitmapArray( uploadArray );
196     }
197   }
198 }
199
200 void TextureCache::ClearAreas( ResourceId id,
201                                const BitmapClearArray& areaArray,
202                                std::size_t blockSize,
203                                uint32_t color )
204 {
205   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::ClearAreas(id: %d)\n", id);
206   TextureIter textureIter = mTextures.find(id);
207   DALI_ASSERT_DEBUG( textureIter != mTextures.end() );
208   if( textureIter != mTextures.end() )
209   {
210     TexturePointer texturePtr = textureIter->second;
211     if( texturePtr )
212     {
213       BitmapTexture* texture = static_cast< BitmapTexture* >( texturePtr.Get() );
214       texture->ClearAreas( areaArray, blockSize, color );
215     }
216   }
217 }
218
219 void TextureCache::DiscardTexture( ResourceId id )
220 {
221   bool deleted = false;
222
223   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::DiscardTexture(id:%u)\n", id);
224
225   if( mTextures.size() > 0)
226   {
227     TextureIter iter = mTextures.find(id);
228     if( iter != mTextures.end() )
229     {
230       TexturePointer texturePtr = iter->second;
231       if( texturePtr )
232       {
233         // if valid texture pointer, cleanup GL resources
234         texturePtr->GlCleanup();
235       }
236       mTextures.erase(iter);
237       deleted = true;
238     }
239   }
240
241   if( mFramebufferTextures.size() > 0)
242   {
243     TextureIter iter = mFramebufferTextures.find(id);
244     if( iter != mFramebufferTextures.end() )
245     {
246       TexturePointer texturePtr = iter->second;
247       if( texturePtr )
248       {
249         // if valid texture pointer, cleanup GL resources
250         texturePtr->GlCleanup();
251       }
252       mFramebufferTextures.erase(iter);
253       deleted = true;
254     }
255   }
256
257   if(deleted)
258   {
259     if( mObservers.size() > 0 )
260     {
261       TextureResourceObserversIter observersIter = mObservers.find(id);
262       if( observersIter != mObservers.end() )
263       {
264         TextureObservers observers = observersIter->second;
265         for( TextureObserversIter iter = observers.begin(); iter != observers.end(); ++iter )
266         {
267           TextureObserver* observer = *iter;
268           observer->TextureDiscarded( id );
269         }
270
271         mObservers.erase( observersIter );
272       }
273     }
274
275     // Tell resource manager
276     ResourcePostProcessRequest ppRequest( id, ResourcePostProcessRequest::DELETED );
277     mPostProcessResourceDispatcher.DispatchPostProcessRequest(ppRequest);
278   }
279 }
280
281 void TextureCache::BindTexture( Texture *texture, ResourceId id, GLenum target, TextureUnit textureunit )
282 {
283   bool created = texture->Bind(target, textureunit);
284   if( created && texture->UpdateOnCreate() ) // i.e. the pixel data was sent to GL
285   {
286     ResourcePostProcessRequest ppRequest( id, ResourcePostProcessRequest::UPLOADED );
287     mPostProcessResourceDispatcher.DispatchPostProcessRequest(ppRequest);
288   }
289 }
290
291 Texture* TextureCache::GetTexture(ResourceId id)
292 {
293   Texture* texture = NULL;
294   TextureIter iter = mTextures.find(id);
295
296   if( iter != mTextures.end() )
297   {
298     TexturePointer texturePtr = iter->second;
299     if( texturePtr )
300     {
301       texture = texturePtr.Get();
302     }
303   }
304
305   if( texture == NULL )
306   {
307     TextureIter iter = mFramebufferTextures.find(id);
308     if( iter != mFramebufferTextures.end() )
309     {
310       TexturePointer texturePtr = iter->second;
311       if( texturePtr )
312       {
313         texture = texturePtr.Get();
314       }
315     }
316   }
317
318   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::GetTexture(id:%u) : %p\n", id, texture);
319
320   return texture;
321 }
322
323 BitmapTexture* TextureCache::GetBitmapTexture(ResourceId id)
324 {
325   BitmapTexture* texture = NULL;
326   TextureIter iter = mTextures.find( id );
327
328   if( iter != mTextures.end() )
329   {
330     TexturePointer texturePtr = iter->second;
331     if( texturePtr )
332     {
333       texture = dynamic_cast< BitmapTexture* >( texturePtr.Get() );
334     }
335   }
336
337   return texture;
338 }
339
340 FrameBufferTexture* TextureCache::GetFramebuffer(ResourceId id)
341 {
342   FrameBufferTexture* offscreen = NULL;
343   TextureIter iter = mFramebufferTextures.find(id);
344
345   DALI_ASSERT_DEBUG( iter != mFramebufferTextures.end() );
346
347   if( iter != mFramebufferTextures.end() )
348   {
349     TexturePointer texturePtr = iter->second;
350     if( texturePtr )
351     {
352       offscreen = dynamic_cast< FrameBufferTexture* >( texturePtr.Get() );
353     }
354   }
355   DALI_ASSERT_DEBUG( offscreen );
356
357   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::GetFramebuffer(id:%u) : %p\n", id, offscreen);
358
359   return offscreen;
360 }
361
362 void TextureCache::AddObserver( ResourceId id, TextureObserver* observer )
363 {
364   TextureResourceObserversIter observersIter = mObservers.find(id);
365   if( observersIter != mObservers.end() )
366   {
367     TextureObservers& observers = observersIter->second;
368     bool foundObserver = false;
369     for( TextureObserversIter iter = observers.begin(); iter != observers.end(); ++iter )
370     {
371       if( *iter == observer )
372       {
373         foundObserver = true;
374         break;
375       }
376     }
377     if( ! foundObserver )
378     {
379       observers.push_back(observer);
380     }
381   }
382   else
383   {
384     TextureObservers observers;
385     observers.push_back(observer);
386     mObservers.insert(std::pair<ResourceId, TextureObservers>(id, observers));
387   }
388 }
389
390 void TextureCache::RemoveObserver( ResourceId id, TextureObserver* observer )
391 {
392   TextureResourceObserversIter observersIter = mObservers.find(id);
393   if( observersIter != mObservers.end() )
394   {
395     TextureObservers& observers = observersIter->second;
396     for( TextureObserversIter iter = observers.begin(); iter != observers.end(); ++iter )
397     {
398       if(*iter == observer)
399       {
400         observers.erase(iter);
401         break;
402       }
403     }
404   }
405 }
406
407 void TextureCache::GlContextDestroyed()
408 {
409   TextureIter end = mTextures.end();
410   TextureIter iter = mTextures.begin();
411   for( ; iter != end; ++iter )
412   {
413     (*iter->second).GlContextDestroyed(); // map holds intrusive pointers
414   }
415
416   end = mFramebufferTextures.end();
417   iter = mFramebufferTextures.begin();
418   for( ; iter != end; ++iter )
419   {
420     (*iter->second).GlContextDestroyed(); // map holds intrusive pointers
421   }
422 }
423
424 void TextureCache::SetDiscardBitmapsPolicy( ResourcePolicy::Discardable policy )
425 {
426   DALI_LOG_INFO( gTextureCacheFilter, Debug::General, "TextureCache::SetDiscardBitmapsPolicy(%s)\n",
427                  policy==ResourcePolicy::RETAIN?"RETAIN":"DISCARD" );
428   mDiscardBitmapsPolicy = policy;
429 }
430
431 ResourcePolicy::Discardable TextureCache::GetDiscardBitmapsPolicy()
432 {
433   return mDiscardBitmapsPolicy;
434 }
435
436
437 /********************************************************************************
438  **********************  Implements TextureCacheDispatcher  *********************
439  ********************************************************************************/
440
441 void TextureCache::DispatchCreateTexture( ResourceId        id,
442                                           unsigned int      width,
443                                           unsigned int      height,
444                                           Pixel::Format     pixelFormat,
445                                           bool              clearPixels )
446 {
447   // NULL, means being shutdown, so ignore msgs
448   if( mSceneGraphBuffers != NULL )
449   {
450     typedef MessageValue5< TextureCache, ResourceId, unsigned int, unsigned int, Pixel::Format, bool > DerivedType;
451
452     // Reserve some memory inside the render queue
453     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
454
455     // Construct message in the render queue memory; note that delete should not be called on the return value
456     new (slot) DerivedType( this, &TextureCache::CreateTexture, id, width, height, pixelFormat, clearPixels );
457   }
458 }
459
460 void TextureCache::DispatchCreateTextureForBitmap( ResourceId id, Bitmap* bitmap )
461 {
462   // NULL, means being shutdown, so ignore msgs
463   if( mSceneGraphBuffers != NULL )
464   {
465     typedef MessageValue2< TextureCache, ResourceId, Integration::BitmapPtr > DerivedType;
466
467     // Reserve some memory inside the render queue
468     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
469
470     // Construct message in the render queue memory; note that delete should not be called on the return value
471     new (slot) DerivedType( this, &TextureCache::AddBitmap, id, bitmap );
472   }
473 }
474
475 void TextureCache::DispatchCreateTextureForNativeImage( ResourceId id, NativeImagePtr nativeImage )
476 {
477   // NULL, means being shutdown, so ignore msgs
478   if( mSceneGraphBuffers != NULL )
479   {
480     typedef MessageValue2< TextureCache, ResourceId, NativeImagePtr > DerivedType;
481
482     // Reserve some memory inside the render queue
483     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
484
485     // Construct message in the render queue memory; note that delete should not be called on the return value
486     new (slot) DerivedType( this, &TextureCache::AddNativeImage, id, nativeImage );
487   }
488 }
489
490 void TextureCache::DispatchCreateTextureForFrameBuffer( ResourceId id, unsigned int width, unsigned int height, Pixel::Format pixelFormat )
491 {
492   // NULL, means being shutdown, so ignore msgs
493   if( mSceneGraphBuffers != NULL )
494   {
495     typedef MessageValue4< TextureCache, ResourceId, unsigned int, unsigned int, Pixel::Format > DerivedType;
496
497     // Reserve some memory inside the render queue
498     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
499
500     // Construct message in the render queue memory; note that delete should not be called on the return value
501     new (slot) DerivedType( this, &TextureCache::AddFrameBuffer, id, width, height, pixelFormat );
502   }
503 }
504
505 void TextureCache::DispatchCreateTextureForFrameBuffer( ResourceId id, NativeImagePtr nativeImage )
506 {
507   // NULL, means being shutdown, so ignore msgs
508   if( mSceneGraphBuffers != NULL )
509   {
510     typedef MessageValue2< TextureCache, ResourceId, NativeImagePtr > DerivedType;
511
512     // Reserve some memory inside the render queue
513     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
514
515     // Construct message in the render queue memory; note that delete should not be called on the return value
516     new (slot) DerivedType( this, &TextureCache::AddFrameBuffer, id, nativeImage );
517   }
518 }
519
520 void TextureCache::DispatchUpdateTexture( ResourceId id, Bitmap* bitmap )
521 {
522   // NULL, means being shutdown, so ignore msgs
523   if( mSceneGraphBuffers != NULL )
524   {
525     typedef MessageValue2< TextureCache, ResourceId, Integration::BitmapPtr > DerivedType;
526
527     // Reserve some memory inside the render queue
528     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
529
530     // Construct message in the render queue memory; note that delete should not be called on the return value
531     new (slot) DerivedType( this, &TextureCache::UpdateTexture, id, bitmap );
532   }
533 }
534
535 void TextureCache::DispatchUpdateTexture( ResourceId destId, ResourceId srcId, std::size_t xOffset, std::size_t yOffset )
536 {
537   // NULL, means being shutdown, so ignore msgs
538   if( mSceneGraphBuffers != NULL )
539   {
540     typedef MessageValue4< TextureCache, ResourceId, ResourceId, std::size_t, std::size_t > DerivedType;
541
542     // Reserve some memory inside the render queue
543     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
544
545     // Construct message in the render queue memory; note that delete should not be called on the return value
546     new (slot) DerivedType( this, &TextureCache::UpdateTexture, destId, srcId, xOffset, yOffset );
547   }
548 }
549
550 void TextureCache::DispatchUpdateTextureArea( ResourceId id, const Dali::RectArea& area )
551 {
552   // NULL, means being shutdown, so ignore msgs
553   if( mSceneGraphBuffers != NULL )
554   {
555     typedef MessageValue2< TextureCache, ResourceId, Dali::RectArea > DerivedType;
556
557     // Reserve some memory inside the render queue
558     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
559
560     // Construct message in the render queue memory; note that delete should not be called on the return value
561     new (slot) DerivedType( this, &TextureCache::UpdateTextureArea, id, area );
562   }
563 }
564
565 void TextureCache::DispatchUploadBitmapArrayToTexture( ResourceId id, const BitmapUploadArray& uploadArray )
566 {
567   // NULL, means being shutdown, so ignore msgs
568   if( mSceneGraphBuffers != NULL )
569   {
570     typedef MessageValue2< TextureCache, ResourceId, BitmapUploadArray > DerivedType;
571
572     // Reserve some memory inside the render queue
573     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
574
575     // Construct message in the render queue memory; note that delete should not be called on the return value
576     new (slot) DerivedType( this, &TextureCache::AddBitmapUploadArray, id, uploadArray );
577   }
578 }
579
580 void TextureCache::DispatchClearAreas( ResourceId id, const BitmapClearArray& areaArray, std::size_t blockSize, uint32_t color )
581 {
582   // NULL, means being shutdown, so ignore msgs
583   if( mSceneGraphBuffers != NULL )
584   {
585     typedef MessageValue4< TextureCache, ResourceId, BitmapClearArray, std::size_t, uint32_t > DerivedType;
586
587     // Reserve some memory inside the render queue
588     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
589
590     // Construct message in the render queue memory; note that delete should not be called on the return value
591     new (slot) DerivedType( this, &TextureCache::ClearAreas, id, areaArray, blockSize, color );
592   }
593 }
594
595 void TextureCache::DispatchDiscardTexture( ResourceId id )
596 {
597   // NULL, means being shutdown, so ignore msgs
598   if( mSceneGraphBuffers != NULL )
599   {
600     typedef MessageValue1< TextureCache, ResourceId > DerivedType;
601
602     // Reserve some memory inside the render queue
603     unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
604
605     // Construct message in the render queue memory; note that delete should not be called on the return value
606     new (slot) DerivedType( this, &TextureCache::DiscardTexture, id );
607   }
608 }
609
610 } // SceneGraph
611
612 } // Internal
613
614 } // Dali