API for eager GLTexture Creation in NativeImage 97/37197/8
authorAndrew Cox <andrew.cox@partner.samsung.com>
Tue, 17 Mar 2015 15:38:31 +0000 (15:38 +0000)
committerAndrew Cox <andrew.cox@partner.samsung.com>
Wed, 13 May 2015 13:41:53 +0000 (14:41 +0100)
Change-Id: I975c87cfdb59921695ecfb8ceedd741df238297e
Signed-off-by: Andrew Cox <andrew.cox@partner.samsung.com>
16 files changed:
automated-tests/src/dali/dali-test-suite-utils/test-native-image.cpp
automated-tests/src/dali/dali-test-suite-utils/test-native-image.h
automated-tests/src/dali/utc-Dali-NativeImage.cpp
dali/internal/event/images/native-image-impl.cpp
dali/internal/event/images/native-image-impl.h
dali/internal/event/resources/resource-client.cpp
dali/internal/event/resources/resource-client.h
dali/internal/render/common/texture-cache-dispatcher.h
dali/internal/render/gl-resources/native-texture.cpp
dali/internal/render/gl-resources/texture-cache.cpp
dali/internal/render/gl-resources/texture-cache.h
dali/internal/render/gl-resources/texture.h
dali/internal/update/resources/resource-manager.cpp
dali/internal/update/resources/resource-manager.h
dali/public-api/images/native-image.cpp
dali/public-api/images/native-image.h

index b15dfbc..d143d35 100644 (file)
@@ -29,7 +29,7 @@ TestNativeImagePointer TestNativeImage::New(int width, int height)
 }
 
 TestNativeImage::TestNativeImage(int width, int height)
-: mWidth(width), mHeight(height)
+: mWidth(width), mHeight(height), mExtensionCreateCalls(0), mExtensionDestroyCalls(0), mTargetTextureCalls(0)
 {
 }
 
index 720b429..4a39cb1 100644 (file)
@@ -31,9 +31,9 @@ class DALI_IMPORT_API TestNativeImage : public Dali::NativeImageInterface
 public:
   static TestNativeImagePointer New(int width, int height);
 
-  inline virtual bool GlExtensionCreate() {return true;};
-  inline virtual void GlExtensionDestroy() {};
-  inline virtual GLenum TargetTexture() {return 1;};
+  inline virtual bool GlExtensionCreate() { ++mExtensionCreateCalls; return true;};
+  inline virtual void GlExtensionDestroy() { ++mExtensionDestroyCalls; };
+  inline virtual GLenum TargetTexture() { ++mTargetTextureCalls; return 1;};
   inline virtual void PrepareTexture() {};
   inline virtual unsigned int GetWidth() const {return mWidth;};
   inline virtual unsigned int GetHeight() const {return mHeight;};
@@ -45,6 +45,10 @@ private:
 
   int mWidth;
   int mHeight;
+public:
+  int mExtensionCreateCalls;
+  int mExtensionDestroyCalls;
+  int mTargetTextureCalls;
 };
 
 } // Dali
index 9a36354..bf7b98b 100644 (file)
@@ -81,3 +81,87 @@ int UtcDaliNativeImageDownCast(void)
   DALI_TEST_CHECK(image7);
   END_TEST;
 }
+
+int UtcDaliNativeImageCreateGlTextureN(void)
+{
+  TestApplication application;
+  tet_infoline( "Testing Dali::NativeImage::GenerateGlTexture()" );
+
+  NativeImage image;
+  try
+  {
+    image.CreateGlTexture();
+    tet_printf( "Assertion test failed - no Exception\n" );
+    tet_result( TET_FAIL );
+  }
+  catch( Dali::DaliException& e )
+  {
+    DALI_TEST_PRINT_ASSERT( e );
+    DALI_TEST_ASSERT( e, "image &&", TEST_LOCATION );
+  }
+  END_TEST;
+}
+
+int UtcDaliNativeImageCreateGlTextureP(void)
+{
+  TestApplication application;
+  tet_infoline( "Testing Dali::NativeImage::GenerateGlTexture()" );
+
+  TestNativeImagePointer imageInterface = TestNativeImage::New( 16, 16 );
+  NativeImage image = NativeImage::New( *(imageInterface.Get()) );
+  DALI_TEST_CHECK( image );
+
+  image.CreateGlTexture();
+
+  application.SendNotification();
+  application.Render(16);
+
+  DALI_TEST_EQUALS( imageInterface->mExtensionCreateCalls, 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( imageInterface->mTargetTextureCalls, 1, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliNativeImageContextLoss(void)
+{
+  TestApplication application;
+  tet_infoline( "Testing Dali::NativeImage behaviour through a context lost/regained cycle." );
+
+  // Build an image that is expected to have a GL texture created for it and
+  // recreated in a GL context recovery:
+  TestNativeImagePointer eagerImageInterface = TestNativeImage::New( 16, 16 );
+  NativeImage eagerImage = NativeImage::New( *(eagerImageInterface.Get()) );
+  DALI_TEST_CHECK( eagerImage );
+
+  // Build a regular lazy-allocated image for comparison:
+  TestNativeImagePointer lazyImageInterface = TestNativeImage::New( 16, 16 );
+  NativeImage lazyImage = NativeImage::New( *(lazyImageInterface.Get()) );
+  DALI_TEST_CHECK( lazyImage );
+
+  eagerImage.CreateGlTexture();
+
+  application.SendNotification();
+  application.Render(16);
+
+  // Cycle through a context loss and regain, asserting that the texture is
+  // not reallocated if it already existed before the cycle and is never allocated
+  // throughout the cycle if of the regular lazy kind:
+
+  // Call render thread context destroyed / created functions:
+  application.ResetContext();
+
+  // Call event thread function:
+  application.GetCore().RecoverFromContextLoss();
+
+  // Run update/render loop
+  application.SendNotification();
+  application.Render(16);
+
+  DALI_TEST_EQUALS( eagerImageInterface->mExtensionCreateCalls, 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( eagerImageInterface->mTargetTextureCalls, 1, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( lazyImageInterface->mExtensionCreateCalls, 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( lazyImageInterface->mTargetTextureCalls, 0, TEST_LOCATION );
+
+  END_TEST;
+}
index 46211cb..4043f91 100644 (file)
@@ -67,6 +67,12 @@ NativeImage::~NativeImage()
 {
 }
 
+void NativeImage::CreateGlTexture()
+{
+  ResourceClient& resourceClient = ThreadLocalStorage::Get().GetResourceClient();
+  resourceClient.CreateGlTexture( GetResourceId() );
+}
+
 } // namespace Internal
 
 } // namespace Dali
index 7ea947b..0f1dfe4 100644 (file)
@@ -42,10 +42,15 @@ public:
    * Creates object by using native resources
    * the maximum size of the image is limited by GL_MAX_TEXTURE_SIZE
    * @param [in] nativeImageInterface An reference to the object of the interface implementation.
-    * @return a pointer to a newly created object.
+   * @return a pointer to a newly created object.
    */
   static NativeImagePtr New( NativeImageInterface& nativeImageInterface );
 
+  /**
+   * @copydoc Dali::NativeImage::CreateGlTexture
+   */
+  void CreateGlTexture();
+
 protected:
 
   /**
index 5fe830a..3fe6bd8 100644 (file)
@@ -474,6 +474,12 @@ Bitmap* ResourceClient::GetBitmap(ResourceTicketPtr ticket)
   return bitmap;
 }
 
+void ResourceClient::CreateGlTexture( ResourceId id )
+{
+  RequestCreateGlTextureMessage( mEventThreadServices, mResourceManager, id );
+}
+
+
 /********************************************************************************
  ********************   ResourceTicketLifetimeObserver methods   ****************
  ********************************************************************************/
index 13aa385..9db1da1 100644 (file)
@@ -266,6 +266,11 @@ public:
    * @return The bitmap, or NULL if the ticket did not reference a bitmap
    */
   Integration::Bitmap* GetBitmap(ResourceTicketPtr ticket);
+   /**
+    * @brief Trigger asynchronous creation of GL texture to back resource immediately.
+    * @param[in] id The resource ID to allocate a GL texture for.
+    */
+   void CreateGlTexture( ResourceId id );
 
 public: // From ResourceTicketLifetimeObserver.
 
index 06b2a29..5e46c11 100644 (file)
@@ -119,6 +119,12 @@ public:
   virtual void DispatchCreateTextureForFrameBuffer( ResourceId id, NativeImageInterfacePtr nativeImage ) = 0;
 
   /**
+   * @brief Create GL texture for native image resource.
+   * @param[in] id The resource id.
+   */
+  virtual void DispatchCreateGlTexture( ResourceId id ) = 0;
+
+  /**
    * Dispatch a message to update the texture.
    * May be called from Update thread
    * @param[in] id Resource Id of the texture
index 8ac99c6..9fc84a9 100644 (file)
@@ -80,6 +80,12 @@ bool NativeTexture::HasAlphaChannel() const
 
 bool NativeTexture::CreateGlTexture()
 {
+  if ( mId != 0 )
+  {
+    DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "GL texture creation duplicate for GL id: %d\n", &mId );
+    return true;
+  }
+
   if( mNativeImage->GlExtensionCreate() )
   {
     mContext.GenTextures( 1, &mId );
@@ -106,10 +112,9 @@ void NativeTexture::GlCleanup()
 {
   Texture::GlCleanup();
 
-  DALI_ASSERT_DEBUG(mNativeImage);
+  DALI_ASSERT_DEBUG( mNativeImage );
 
   mNativeImage->GlExtensionDestroy();
-
   mNativeImage.Reset();
 }
 
index d3d1fa3..c44ea26 100644 (file)
@@ -55,6 +55,24 @@ template <> struct ParameterType< Pixel::Format > : public BasicType< Pixel::For
 namespace SceneGraph
 {
 
+namespace
+{
+
+/**
+ * @brief Forward to all textures in container the news that the GL Context is down.
+ */
+void GlContextDestroyed( TextureContainer& textures )
+{
+  TextureIter end = textures.end();
+  TextureIter iter = textures.begin();
+  for( ; iter != end; ++iter )
+  {
+    (*iter->second).GlContextDestroyed();
+  }
+}
+
+}
+
 TextureCache::TextureCache( RenderQueue& renderQueue,
                             PostProcessResourceDispatcher& postProcessResourceDispatcher,
                             Context& context)
@@ -118,6 +136,21 @@ void TextureCache::AddFrameBuffer( ResourceId id, NativeImageInterfacePtr native
   mFramebufferTextures.insert(TexturePair(id, texture));
 }
 
+void TextureCache::CreateGlTexture( ResourceId id )
+{
+  TextureIter textureIter = mTextures.find(id);
+  // If we found a non-null texture object pointer for the resource id, force it
+  // to eagerly allocate a backing GL texture:
+  if( textureIter != mTextures.end() )
+  {
+    TexturePointer texturePtr = textureIter->second;
+    if( texturePtr )
+    {
+      texturePtr->CreateGlTexture();
+    }
+  }
+}
+
 void TextureCache::UpdateTexture( ResourceId id, Integration::BitmapPtr bitmap )
 {
   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "TextureCache::UpdateTexture(id=%i bitmap:%p )\n", id, bitmap.Get());
@@ -376,19 +409,8 @@ void TextureCache::RemoveObserver( ResourceId id, TextureObserver* observer )
 
 void TextureCache::GlContextDestroyed()
 {
-  TextureIter end = mTextures.end();
-  TextureIter iter = mTextures.begin();
-  for( ; iter != end; ++iter )
-  {
-    (*iter->second).GlContextDestroyed(); // map holds intrusive pointers
-  }
-
-  end = mFramebufferTextures.end();
-  iter = mFramebufferTextures.begin();
-  for( ; iter != end; ++iter )
-  {
-    (*iter->second).GlContextDestroyed(); // map holds intrusive pointers
-  }
+  SceneGraph::GlContextDestroyed( mTextures );
+  SceneGraph::GlContextDestroyed( mFramebufferTextures );
 }
 
 void TextureCache::SetDiscardBitmapsPolicy( ResourcePolicy::Discardable policy )
@@ -457,6 +479,22 @@ void TextureCache::DispatchCreateTextureForNativeImage( ResourceId id, NativeIma
   }
 }
 
+void TextureCache::DispatchCreateGlTexture( ResourceId id )
+{
+  // NULL, means being shutdown, so ignore msgs
+  if( mSceneGraphBuffers != NULL )
+  {
+    typedef MessageValue1< TextureCache, ResourceId > DerivedType;
+
+    // Reserve some memory inside the render queue
+    unsigned int* slot = mRenderQueue.ReserveMessageSlot( mSceneGraphBuffers->GetUpdateBufferIndex(), sizeof( DerivedType ) );
+
+    // Construct message in the render queue memory; note that delete should not be called on the return value
+    new (slot) DerivedType( this, &TextureCache::CreateGlTexture, id );
+  }
+}
+
+
 void TextureCache::DispatchCreateTextureForFrameBuffer( ResourceId id, unsigned int width, unsigned int height, Pixel::Format pixelFormat )
 {
   // NULL, means being shutdown, so ignore msgs
index 2cdf5c7..8187b80 100644 (file)
@@ -131,6 +131,12 @@ public:
   void AddFrameBuffer( ResourceId id, NativeImageInterfacePtr nativeImage );
 
   /**
+   * Create GL texture eagerly right now instead of waiting for first use.
+   * @param[in] id The resource id corresponding to the texture.
+   */
+  void CreateGlTexture( ResourceId id );
+
+  /**
    * Update the texture with a newly loaded bitmap
    * @param[in] id Resource Id of the bitmap
    * @param[in] bitmap The bitmap
@@ -260,6 +266,11 @@ protected: // Implements TextureCacheDispatcher
   virtual void DispatchCreateTextureForNativeImage( ResourceId id, NativeImageInterfacePtr nativeImage );
 
   /**
+   * @copydoc TextureCacheDispatcher::DispatchCreateGlTexture()
+   */
+  virtual void DispatchCreateGlTexture( ResourceId id );
+
+  /**
    * @copydoc TextureCacheDispatcher::DispatchCreateTextureForFramebuffer()
    */
   virtual void DispatchCreateTextureForFrameBuffer( ResourceId id, unsigned int width, unsigned int height, Pixel::Format pixelFormat );
index c43ddbf..8fc163b 100644 (file)
@@ -206,14 +206,13 @@ protected:
   Texture( Context&      context,
            unsigned int  width,
            unsigned int  height );
-
+public:
   /**
    * Initialize texture for rendering.
    * @return true on success
    */
   virtual bool CreateGlTexture() = 0;
 
-public:
   /**
    * Destructor.
    * Delete the GL texture associated with it.
index 9e15a1c..3d123e5 100644 (file)
@@ -599,6 +599,11 @@ void ResourceManager::HandleDiscardResourceRequest( ResourceId deadId, ResourceT
   }
 }
 
+void ResourceManager::HandleCreateGlTextureRequest(ResourceId id)
+{
+  mImpl->mTextureCacheDispatcher.DispatchCreateGlTexture( id );
+}
+
 /********************************************************************************
  ******************** Update thread object direct interface  ********************
  ********************************************************************************/
index 2fae192..e0aea0b 100644 (file)
@@ -306,6 +306,12 @@ public: // Used by ResourceClient
    */
   void HandleDiscardResourceRequest( ResourceId id, Integration::ResourceTypeId typeId );
 
+   /**
+    * @brief Create GL texture for resource.
+    * @param[in] id The resource id.
+    */
+   void HandleCreateGlTextureRequest( ResourceId id );
+
   /********************************************************************************
    ******************** Update thread object direct interface  ********************
    ********************************************************************************/
@@ -650,6 +656,19 @@ inline void RequestDiscardResourceMessage( EventThreadServices& eventThreadServi
   new (slot) LocalType( &manager, &ResourceManager::HandleDiscardResourceRequest, id, typeId );
 }
 
+inline void RequestCreateGlTextureMessage( EventThreadServices& eventThreadServices,
+                                           ResourceManager& manager,
+                                           ResourceId id )
+{
+  typedef MessageValue1< ResourceManager, ResourceId > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new (slot) LocalType( &manager, &ResourceManager::HandleCreateGlTextureRequest, id );
+}
+
 } // namespace Internal
 
 } // namespace Dali
index bb097b9..fd40713 100644 (file)
@@ -48,6 +48,11 @@ NativeImage& NativeImage::operator=( const NativeImage& rhs )
   return *this;
 }
 
+void NativeImage::CreateGlTexture()
+{
+  GetImplementation(*this).CreateGlTexture();
+}
+
 NativeImage NativeImage::New( NativeImageInterface& resourceData )
 {
   Internal::NativeImagePtr internal = Internal::NativeImage::New( resourceData );
index 69b5536..926069e 100644 (file)
@@ -68,6 +68,19 @@ public:
   NativeImage& operator=( const NativeImage& rhs );
 
   /**
+   * @brief Trigger asynchronous creation of backing GL texture immediately.
+   *
+   * The normal policy is for a GL texture to created lazily when needed.
+   * This function forces the allocation of a texture to happen at the earliest
+   * opportunity.
+   *
+   * @note If the application loses its GL context, native images may lose their
+   * GL textures. This function can be called again after context regain to force
+   * the creation of the GL texture if still needed.
+   */
+  void CreateGlTexture();
+
+  /**
    * @brief Create a new NativeImage, which used native resources.
    *
    * The maximum size of the image is limited by GL_MAX_TEXTURE_SIZE