void DiscardResource(GLES::Texture* texture)
{
mDiscardTextureSet.insert(texture);
+ if(texture->IsNativeTexture())
+ {
+ mTextureDependencyChecker.DiscardNativeTexture(texture);
+ }
}
/**
// Warning, this may cause glWaitSync to occur on the GPU, or glClientWaitSync to block the CPU.
dependencyChecker.CheckNeedsSync(this, texture, true);
texture->Bind(binding);
- texture->Prepare();
+
+ if(texture->IsNativeTexture())
+ {
+ texture->Prepare();
+ dependencyChecker.MarkNativeTexturePrepared(texture);
+ }
if(programChanged)
{
dependencyChecker.AddTextures(this, framebuffer);
}
}
+
+ if(dependencyChecker.GetNativeTextureCount() > 0)
+ {
+ dependencyChecker.CreateNativeTextureSync(this);
+ }
}
void Context::ClearState()
#define DALI_GRAPHICS_GLES_TEXTURE_H
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return mCreateInfo.nativeImagePtr ? BoundTextureType::TEXTURE_EXTERNAL_OES : static_cast<BoundTextureType>(mCreateInfo.textureType);
}
+ /**
+ * @brief Returns wether we are using a native texture or not.
+ * @return True if we are using a native texture. False otherwise.
+ */
+ [[nodiscard]] bool IsNativeTexture() const
+ {
+ return mCreateInfo.nativeImagePtr != nullptr;
+ }
+
/**
* @brief Called when initializing the resource
*
}
}
+bool AgingSyncObject::IsSynced()
+{
+ bool synced = false;
+ if(egl)
+ {
+ if(eglSyncObject)
+ {
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::IsSynced(); EGL::ClientWaitSync\n");
+ synced = eglSyncObject->IsSynced();
+ }
+ }
+ else
+ {
+ auto gl = controller.GetGL();
+ if(gl && glSyncObject)
+ {
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::IsSynced(); glClientWaitSync 0ms\n");
+ const GLuint64 TIMEOUT = 0; //0ms!
+ GLenum result = gl->ClientWaitSync(glSyncObject, GL_SYNC_FLUSH_COMMANDS_BIT, TIMEOUT);
+
+ synced = (result == GL_ALREADY_SIGNALED || result == GL_CONDITION_SATISFIED);
+ }
+ }
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgingSyncObject::IsSynced(); Result: %s\n", synced ? "Synced" : "NOT SYNCED");
+ return synced;
+}
+
bool AgingSyncObject::ClientWait()
{
bool synced = false;
AgingSyncObject* SyncPool::AllocateSyncObject(const Context* writeContext, SyncPool::SyncContext syncContext)
{
- std::unique_ptr<AgingSyncObject> syncObject = std::make_unique<AgingSyncObject>(mController, writeContext, (syncContext == SyncContext::EGL));
- mSyncObjects.push_back(std::move(syncObject));
- return mSyncObjects.back().get();
+ AgingSyncObject* agingSyncObject = new AgingSyncObject(mController, writeContext, (syncContext == SyncContext::EGL));
+
+ // Take ownership of sync object
+ mSyncObjects.PushBack(agingSyncObject);
+ return agingSyncObject;
}
-void SyncPool::Wait(AgingSyncObject* syncPoolObject)
+bool SyncPool::IsSynced(AgingSyncObject* agingSyncObject)
{
- if(syncPoolObject != nullptr)
+ if(DALI_LIKELY(agingSyncObject != nullptr))
{
- syncPoolObject->syncing = true;
- syncPoolObject->Wait();
+ return agingSyncObject->IsSynced();
}
+ return false;
}
-bool SyncPool::ClientWait(AgingSyncObject* syncPoolObject)
+void SyncPool::Wait(AgingSyncObject* agingSyncObject)
{
- if(syncPoolObject)
+ if(DALI_LIKELY(agingSyncObject != nullptr))
{
- return syncPoolObject->ClientWait();
+ agingSyncObject->syncing = true;
+ agingSyncObject->Wait();
+ }
+}
+
+bool SyncPool::ClientWait(AgingSyncObject* agingSyncObject)
+{
+ if(DALI_LIKELY(agingSyncObject != nullptr))
+ {
+ return agingSyncObject->ClientWait();
}
return false;
}
void SyncPool::FreeSyncObject(AgingSyncObject* agingSyncObject)
{
- auto iter = std::find_if(mSyncObjects.begin(), mSyncObjects.end(), [&agingSyncObject](AgingSyncPtrRef agingSyncPtr) { return agingSyncPtr.get() == agingSyncObject; });
- if(iter != mSyncObjects.end())
+ if(DALI_LIKELY(agingSyncObject != nullptr))
{
- iter->reset();
+ // Release memory of sync object
+ mSyncObjects.EraseObject(agingSyncObject);
}
}
*/
void SyncPool::AgeSyncObjects()
{
- DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count: %d\n", mSyncObjects.size());
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count: %d\n", mSyncObjects.Count());
- if(!mSyncObjects.empty())
+ if(!mSyncObjects.IsEmpty())
{
// Age the remaining sync objects.
- for(auto& agingSyncObject : mSyncObjects)
+ for(auto iter = mSyncObjects.Begin(); iter != mSyncObjects.End();)
{
- if(agingSyncObject != nullptr && (agingSyncObject->glSyncObject != 0 || agingSyncObject->eglSyncObject != nullptr))
+ auto* agingSyncObject = (*iter);
+ if(agingSyncObject != nullptr && (agingSyncObject->glSyncObject != 0 || agingSyncObject->eglSyncObject != nullptr) && agingSyncObject->age > 0)
+ {
+ --agingSyncObject->age;
+ ++iter;
+ }
+ else
{
- if(agingSyncObject->age > 0)
- {
- agingSyncObject->age--;
- }
- else
- {
- agingSyncObject.reset();
- }
+ // Release memory of sync object
+ iter = mSyncObjects.Erase(iter);
}
}
}
- // Move any old sync objects to the end of the list, and then remove them all.
- mSyncObjects.erase(std::remove_if(mSyncObjects.begin(), mSyncObjects.end(), [&](std::unique_ptr<AgingSyncObject>& agingSyncObject) { return agingSyncObject == nullptr; }),
- mSyncObjects.end());
- DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count after erase: %d\n", mSyncObjects.size());
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "AgeSyncObjects: count after erase: %d\n", mSyncObjects.Count());
}
} // namespace Dali::Graphics::GLES
#include <dali/graphics-api/graphics-types.h>
#include <dali/integration-api/gl-abstraction.h>
+#include <dali/integration-api/ordered-set.h>
#include <dali/public-api/common/vector-wrapper.h>
namespace Dali
bool syncing{false};
bool egl{false};
+ bool IsSynced();
void Wait();
bool ClientWait();
};
*/
AgingSyncObject* AllocateSyncObject(const Context* writeContext, SyncContext syncContext);
+ /**
+ * Check whether given object is synced in the CPU
+ * @param syncPoolObject The object to check synced.
+ * @return true if the sync object was signaled, false if it timed out
+ */
+ bool IsSynced(AgingSyncObject* syncPoolObject);
+
/**
* Wait on a sync object in any context in the GPU
* @param syncPoolObject The object to wait on.
void AgeSyncObjects();
private:
- std::vector<std::unique_ptr<AgingSyncObject>> mSyncObjects;
- EglGraphicsController& mController;
+ Dali::Integration::OrderedSet<AgingSyncObject> mSyncObjects; ///< The list of sync objects in this pool (owned)
+ EglGraphicsController& mController;
};
} // namespace GLES
namespace Dali::Graphics::GLES
{
+TextureDependencyChecker::~TextureDependencyChecker()
+{
+ if(DALI_LIKELY(!Dali::Graphics::EglGraphicsController::IsShuttingDown()))
+ {
+ for(auto& textureDependency : mFramebufferTextureDependencies)
+ {
+ for(auto texture : textureDependency.textures)
+ {
+ texture->SetDependencyIndex(0xffffffff);
+ }
+ mController.GetSyncPool().FreeSyncObject(textureDependency.agingSyncObject);
+ }
+ mFramebufferTextureDependencies.clear();
+
+ for(uint32_t nativeIndex = 0u; nativeIndex < 2u; ++nativeIndex)
+ {
+ for(auto& nativeTextureDependency : mNativeTextureDependencies[nativeIndex])
+ {
+ mController.GetSyncPool().FreeSyncObject(nativeTextureDependency.agingSyncObject);
+ }
+ mNativeTextureDependencies[nativeIndex].clear();
+ }
+ }
+}
void TextureDependencyChecker::Reset()
{
- for(auto& textureDependency : mTextureDependencies)
+ for(auto& textureDependency : mFramebufferTextureDependencies)
{
for(auto texture : textureDependency.textures)
{
mController.GetSyncPool().FreeSyncObject(textureDependency.agingSyncObject);
}
}
- mTextureDependencies.clear();
+ mFramebufferTextureDependencies.clear();
+
+ if(mNativeTextureDependencies[0].size() > 0 || mNativeTextureDependencies[1].size())
+ {
+ DALI_ASSERT_ALWAYS(mIsFirstPreparedNativeTextureDependency && "CreateNativeTextureSync should be called before PostRender!");
+
+ // Remove all infomations about previous native textures
+ for(auto& nativeTextureDependency : mNativeTextureDependencies[mPreviousNativeTextureDependencyIndex])
+ {
+ mController.GetSyncPool().FreeSyncObject(nativeTextureDependency.agingSyncObject);
+ }
+ mNativeTextureDependencies[mPreviousNativeTextureDependencyIndex].clear();
+
+ mCurrentNativeTextureDependencyIndex = __sync_fetch_and_xor(&mPreviousNativeTextureDependencyIndex, 1);
+ }
}
void TextureDependencyChecker::AddTextures(const GLES::Context* writeContext, const GLES::Framebuffer* framebuffer)
{
- uint32_t index = mTextureDependencies.size();
- mTextureDependencies.emplace_back();
- TextureDependency& textureDependency = mTextureDependencies.back();
+ uint32_t index = mFramebufferTextureDependencies.size();
+ mFramebufferTextureDependencies.emplace_back();
+ auto& textureDependency = mFramebufferTextureDependencies.back();
for(int i = 0; i < 3; ++i)
{
void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext, const GLES::Texture* texture, bool cpu)
{
uint32_t dependencyIndex = texture->GetDependencyIndex();
- if(dependencyIndex < mTextureDependencies.size())
+ if(dependencyIndex < mFramebufferTextureDependencies.size())
{
- auto& textureDependency = mTextureDependencies[dependencyIndex];
+ auto& textureDependency = mFramebufferTextureDependencies[dependencyIndex];
if(!textureDependency.syncing && textureDependency.writeContext != readContext)
{
// Needs syncing!
}
}
}
+
+ // Native dependency check
+ if(texture->IsNativeTexture())
+ {
+ // TODO : Optimize here. For now, we don't have too much EndPass call. So just keep this logic.
+ for(auto& nativeTextureDependency : mNativeTextureDependencies[mPreviousNativeTextureDependencyIndex])
+ {
+ if(nativeTextureDependency.synced)
+ {
+ // Fast-out if we know it is already synced
+ continue;
+ }
+
+ auto iter = nativeTextureDependency.textures.find(texture);
+ if(iter != nativeTextureDependency.textures.end())
+ {
+ if(nativeTextureDependency.agingSyncObject != nullptr)
+ {
+ if(cpu)
+ {
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CheckNeedsSync (for native) Insert CPU WAIT");
+ nativeTextureDependency.synced = mController.GetSyncPool().ClientWait(nativeTextureDependency.agingSyncObject);
+ }
+ else
+ {
+ // Wait on the sync object in GPU. This will ensure that the writeContext completes its tasks prior
+ // to the sync point.
+ // However, this may instead timeout, and we can't tell the difference (at least, for glFenceSync)
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CheckNeedsSync (for native) Insert GPU WAIT");
+ mController.GetSyncPool().Wait(nativeTextureDependency.agingSyncObject);
+ }
+ }
+
+ nativeTextureDependency.textures.erase(iter);
+ }
+ }
+ }
+}
+
+void TextureDependencyChecker::MarkNativeTexturePrepared(const GLES::Texture* texture)
+{
+ if(DALI_LIKELY(texture->IsNativeTexture()))
+ {
+ if(mIsFirstPreparedNativeTextureDependency)
+ {
+ mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].emplace_back();
+ mIsFirstPreparedNativeTextureDependency = false;
+ }
+
+ if(DALI_LIKELY(!mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].empty()))
+ {
+ auto& nativeTextureDependency = mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].back();
+ nativeTextureDependency.textures.insert(texture);
+ }
+ }
+}
+
+void TextureDependencyChecker::DiscardNativeTexture(const GLES::Texture* texture)
+{
+ if(DALI_LIKELY(texture->IsNativeTexture()))
+ {
+ for(uint32_t nativeIndex = 0u; nativeIndex < 2u; ++nativeIndex)
+ {
+ for(auto iter = mNativeTextureDependencies[nativeIndex].begin(); iter != mNativeTextureDependencies[nativeIndex].end();)
+ {
+ auto& nativeTextureDependency = *iter;
+
+ bool isErased = false;
+
+ auto jter = nativeTextureDependency.textures.find(texture);
+ if(jter != nativeTextureDependency.textures.end())
+ {
+ nativeTextureDependency.textures.erase(jter);
+ if(nativeTextureDependency.textures.empty())
+ {
+ mController.GetSyncPool().FreeSyncObject(nativeTextureDependency.agingSyncObject);
+ iter = mNativeTextureDependencies[nativeIndex].erase(iter);
+
+ isErased = true;
+ }
+ }
+
+ if(!isErased)
+ {
+ ++iter;
+ }
+ }
+ }
+ }
+}
+
+void TextureDependencyChecker::CreateNativeTextureSync(const GLES::Context* writeContext)
+{
+ if(mIsFirstPreparedNativeTextureDependency)
+ {
+ return;
+ }
+
+ // Reset flag
+ mIsFirstPreparedNativeTextureDependency = true;
+
+ if(DALI_LIKELY(!mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].empty()))
+ {
+ auto& nativeTextureDependency = mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].back();
+
+ DALI_LOG_INFO(gLogSyncFilter, Debug::Verbose, "TextureDependencyChecker::CreateNativeTextureSync() Allocating sync object\n");
+ nativeTextureDependency.agingSyncObject = mController.GetSyncPool().AllocateSyncObject(writeContext, SyncPool::SyncContext::EGL);
+ }
}
} // namespace Dali::Graphics::GLES
// EXTERNAL INCLUDES
#include <cstddef>
+#include <unordered_set>
// INTERNAL INCLUDES
#include <dali/public-api/common/vector-wrapper.h>
{
public:
explicit TextureDependencyChecker(EglGraphicsController& controller)
- : mController(controller)
+ : mController(controller),
+ mCurrentNativeTextureDependencyIndex(0u),
+ mPreviousNativeTextureDependencyIndex(1u),
+ mIsFirstPreparedNativeTextureDependency(true)
{
}
+ ~TextureDependencyChecker();
+
/**
* Clear all the textures. Call at the start of a frame
*/
/**
* Get the number of (offscreen) textures for dependency checking
*/
- size_t GetTextureCount() const
+ size_t GetFramebufferTextureCount() const
{
- return mTextureDependencies.size();
+ return mFramebufferTextureDependencies.size();
}
+ /**
+ * Get the number of (offscreen) textures for dependency checking
+ */
+ size_t GetNativeTextureCount() const
+ {
+ return mNativeTextureDependencies[mCurrentNativeTextureDependencyIndex].size();
+ }
+
+public: ///< For NativeTexture dependency checker
+ /**
+ * @brief Add prepared native image texture to dependency list
+ */
+ void MarkNativeTexturePrepared(const Texture* texture);
+
+ /**
+ * @brief Remove native image texture from dependency list.
+ * It will be called at discarding texture.
+ */
+ void DiscardNativeTexture(const Texture* texture);
+
+ /**
+ * @brief Create Sync object for native images.
+ * It will be called at EndRenderPass.
+ */
+ void CreateNativeTextureSync(const Context* writeContext);
+
private:
- struct TextureDependency
+ struct FramebufferTextureDependency
{
std::vector<Texture*> textures;
Context* writeContext{nullptr};
Framebuffer* framebuffer{nullptr};
- AgingSyncObject* agingSyncObject;
+ AgingSyncObject* agingSyncObject{nullptr};
bool syncing{false};
};
- std::vector<TextureDependency> mTextureDependencies;
- EglGraphicsController& mController;
+ std::vector<FramebufferTextureDependency> mFramebufferTextureDependencies;
+
+ struct NativeTextureDependency
+ {
+ std::unordered_set<const Texture*> textures;
+ AgingSyncObject* agingSyncObject{nullptr};
+ bool synced{false};
+ };
+ std::vector<NativeTextureDependency> mNativeTextureDependencies[2];
+
+ EglGraphicsController& mController;
+
+ uint32_t mCurrentNativeTextureDependencyIndex; // 0 or 1, toggled every frame
+ uint32_t mPreviousNativeTextureDependencyIndex; // 0 or 1, toggled every frame
+
+ bool mIsFirstPreparedNativeTextureDependency : 1;
};
} // namespace GLES