}
TestNativeImage::TestNativeImage(int width, int height)
-: mWidth(width), mHeight(height)
+: mWidth(width), mHeight(height), mExtensionCreateCalls(0), mExtensionDestroyCalls(0), mTargetTextureCalls(0)
{
}
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;};
int mWidth;
int mHeight;
+public:
+ int mExtensionCreateCalls;
+ int mExtensionDestroyCalls;
+ int mTargetTextureCalls;
};
} // Dali
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;
+}
{
}
+void NativeImage::CreateGlTexture()
+{
+ ResourceClient& resourceClient = ThreadLocalStorage::Get().GetResourceClient();
+ resourceClient.CreateGlTexture( GetResourceId() );
+}
+
} // namespace Internal
} // namespace Dali
* 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:
/**
return bitmap;
}
+void ResourceClient::CreateGlTexture( ResourceId id )
+{
+ RequestCreateGlTextureMessage( mEventThreadServices, mResourceManager, id );
+}
+
+
/********************************************************************************
******************** ResourceTicketLifetimeObserver methods ****************
********************************************************************************/
* @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.
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
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 );
{
Texture::GlCleanup();
- DALI_ASSERT_DEBUG(mNativeImage);
+ DALI_ASSERT_DEBUG( mNativeImage );
mNativeImage->GlExtensionDestroy();
-
mNativeImage.Reset();
}
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)
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());
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 )
}
}
+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
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
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 );
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.
}
}
+void ResourceManager::HandleCreateGlTextureRequest(ResourceId id)
+{
+ mImpl->mTextureCacheDispatcher.DispatchCreateGlTexture( id );
+}
+
/********************************************************************************
******************** Update thread object direct interface ********************
********************************************************************************/
*/
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 ********************
********************************************************************************/
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
return *this;
}
+void NativeImage::CreateGlTexture()
+{
+ GetImplementation(*this).CreateGlTexture();
+}
+
NativeImage NativeImage::New( NativeImageInterface& resourceData )
{
Internal::NativeImagePtr internal = Internal::NativeImage::New( resourceData );
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