} // namespace
+EglGraphicsController::EglGraphicsController()
+: mTextureDependencyChecker(*this),
+ mSyncPool(*this)
+{
+}
+
EglGraphicsController::~EglGraphicsController()
{
while(!mPresentationCommandBuffers.empty())
}
}
+void EglGraphicsController::PostRender()
+{
+ mTextureDependencyChecker.Reset();
+ mSyncPool.AgeSyncObjects();
+}
+
Integration::GlAbstraction& EglGraphicsController::GetGlAbstraction()
{
DALI_ASSERT_DEBUG(mGlAbstraction && "Graphics controller not initialized");
}
case GLES::CommandType::DRAW:
{
- mCurrentContext->Flush(false, cmd.draw);
+ mCurrentContext->Flush(false, cmd.draw, mTextureDependencyChecker);
break;
}
case GLES::CommandType::DRAW_INDEXED:
{
- mCurrentContext->Flush(false, cmd.draw);
+ mCurrentContext->Flush(false, cmd.draw, mTextureDependencyChecker);
break;
}
case GLES::CommandType::DRAW_INDEXED_INDIRECT:
{
- mCurrentContext->Flush(false, cmd.draw);
+ mCurrentContext->Flush(false, cmd.draw, mTextureDependencyChecker);
break;
}
case GLES::CommandType::SET_SCISSOR: // @todo Consider correcting for orientation here?
}
mCurrentContext->BeginRenderPass(cmd.beginRenderPass);
+
break;
}
case GLES::CommandType::END_RENDERPASS:
{
- mCurrentContext->EndRenderPass();
+ mCurrentContext->EndRenderPass(mTextureDependencyChecker);
+ // This sync object is to enable cpu to wait for rendering to complete, not gpu.
+ // It's only needed for reading the framebuffer texture in the client.
auto syncObject = const_cast<GLES::SyncObject*>(static_cast<const GLES::SyncObject*>(cmd.endRenderPass.syncObject));
if(syncObject)
{
#include <dali/internal/graphics/gles-impl/gles-graphics-shader.h>
#include <dali/internal/graphics/gles-impl/gles-graphics-texture.h>
#include <dali/internal/graphics/gles-impl/gles-graphics-types.h>
+#include <dali/internal/graphics/gles-impl/gles-sync-pool.h>
+#include <dali/internal/graphics/gles-impl/gles-texture-dependency-checker.h>
#include <dali/internal/graphics/gles-impl/gles2-graphics-memory.h>
namespace Dali
{
class CommandBuffer;
class PipelineCache;
+class SyncPool;
+class TextureDependencyChecker;
} // namespace GLES
/**
{
public:
/**
- * @brief Deault constructor
+ * @brief Constructor
*/
- EglGraphicsController() = default;
+ EglGraphicsController();
/**
* @brief Destructor
void ResolvePresentRenderTarget(GLES::RenderTarget* renderTarget);
/**
+ * Invoked after all rendering has finished. Used to clean up sync resources
+ */
+ void PostRender();
+
+ /**
* Creates a GLES context for the given render surface
*
* @param[in] surface The surface whose GLES context to be created.
return mSharedContext;
}
+ GLES::SyncPool& GetSyncPool()
+ {
+ return mSyncPool;
+ }
+
private:
Integration::GlAbstraction* mGlAbstraction{nullptr};
Integration::GlContextHelperAbstraction* mGlContextHelperAbstraction{nullptr};
std::queue<const GLES::CommandBuffer*> mPresentationCommandBuffers{}; ///< Queue of reusable command buffers used by presentation engine
void* mSharedContext{nullptr}; ///< Shared EGL context
+
+ GLES::TextureDependencyChecker mTextureDependencyChecker; // Checks if FBO textures need syncing
+ GLES::SyncPool mSyncPool;
};
} // namespace Graphics
${adaptor_graphics_dir}/gles-impl/gles-graphics-texture.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-pipeline-cache.cpp
${adaptor_graphics_dir}/gles-impl/gles-context.cpp
+ ${adaptor_graphics_dir}/gles-impl/gles-sync-pool.cpp
${adaptor_graphics_dir}/gles-impl/gles-sync-object.cpp
${adaptor_graphics_dir}/gles-impl/gles-framebuffer-state-cache.cpp
+ ${adaptor_graphics_dir}/gles-impl/gles-texture-dependency-checker.cpp
)
#include "gles-graphics-program.h"
#include "gles-graphics-render-pass.h"
#include "gles-graphics-render-target.h"
+#include "gles-texture-dependency-checker.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
}
}
-void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall)
+void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker)
{
auto& gl = *mImpl->mController.GetGL();
{
// Attempt to reinitialize
// @todo need to put this somewhere else where it isn't const.
- // Maybe post it back on end of initialize queue if initialization fails?
+ // Maybe post it bac/k on end of initialize queue if initialization fails?
texture->InitializeResource();
}
+ // Warning, this may cause glWaitSync to occur on the GPU.
+ dependencyChecker.CheckNeedsSync(this, texture);
+
texture->Bind(binding);
texture->Prepare(); // @todo also non-const.
mImpl->FlushVertexAttributeLocations();
}
+ //@todo Wait if textures need syncing
gl.DrawArrays(GLESTopology(ia->topology),
drawCall.draw.firstVertex,
drawCall.draw.vertexCount);
else if(targetInfo.framebuffer)
{
// bind framebuffer and swap.
- renderTarget.GetFramebuffer()->Bind();
+ auto framebuffer = renderTarget.GetFramebuffer();
+ framebuffer->Bind();
}
// clear (ideally cache the setup)
const auto& attachments = *renderPass.GetCreateInfo().attachments;
const auto& color0 = attachments[0];
GLuint mask = 0;
+
if(color0.loadOp == AttachmentLoadOp::CLEAR)
{
mask |= GL_COLOR_BUFFER_BIT;
mImpl->mCurrentRenderTarget = &renderTarget;
}
-void Context::EndRenderPass()
+void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
{
if(mImpl->mCurrentRenderTarget)
{
- if(mImpl->mCurrentRenderTarget->GetFramebuffer())
+ GLES::Framebuffer* framebuffer = mImpl->mCurrentRenderTarget->GetFramebuffer();
+ if(framebuffer)
{
auto& gl = *mImpl->mController.GetGL();
gl.Flush();
+
+ /* @todo Full dependency checking would need to store textures in Begin, and create
+ * fence objects here; but we're going to draw all fbos on shared context in serial,
+ * so no real need (yet). Might want to consider ensuring order of render passes,
+ * but that needs doing in the controller, and would need doing before ProcessCommandQueues.
+ *
+ * Currently up to the client to create render tasks in the right order.
+ */
+
+ /* Create fence sync objects. Other contexts can then wait on these fences before reading
+ * textures.
+ */
+ dependencyChecker.AddTextures(this, framebuffer);
}
}
}
class RenderPass;
class RenderTarget;
class Texture;
+class TextureDependencyChecker;
/**
* @brief Context represents single GLES context
* @brief Flushes the context
*
* Flushes the context by issuing GL calls to set the required
- * state.
+ * state. Causes a glWaitSync if any drawn textures are dependent
+ * on another context.
*
* @param[in] reset If true then state is reset unconditionally
+ * @param[in] drawCall the draws that need flushing
+ * @param[in] dependencyChecker The texture dependecy checker
*/
- void Flush(bool reset, const GLES::DrawCallDescriptor& drawCall);
+ void Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker);
/**
* @brief Returns context Id
void ResolveStandaloneUniforms();
/**
- * @brief Begins render pass for sepcified render target
+ * @brief Begins render pass for specified render target
*
* @param[in] renderPass render pass object to begin
- * @param[in] renderTarget render target to be drawn onto
*/
void BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin);
* @brief Ends render pass
*
* Ending render pass is necessary in order to ensure
- * proper implicit synchronization is in place
+ * proper explicit synchronization is in place
*/
- void EndRenderPass();
+ void EndRenderPass(TextureDependencyChecker& checker);
/**
* @brief Returns the cache of GL state in the context
void SetSamplerParameter(uint32_t param, uint32_t& cacheValue, uint32_t value) const;
+ uint32_t GetDependencyIndex() const
+ {
+ return mDependencyIndex;
+ }
+ void SetDependencyIndex(uint32_t dependencyIndex)
+ {
+ mDependencyIndex = dependencyIndex;
+ }
+
private:
mutable struct SamplerStateCache
{
uint32_t mTextureId{0u};
GLenum mGlTarget{0u};
uint32_t mMaxMipMapLevel{0u};
+ uint32_t mDependencyIndex{0xFFFFFFFF};
void* mGLOwnerContext{nullptr};
bool mIsCompressed{false};
};
--- /dev/null
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Class header
+#include <dali/internal/graphics/gles-impl/gles-sync-pool.h>
+
+// External Headers
+#include <dali/graphics-api/graphics-sync-object-create-info.h>
+
+// Internal Headers
+#include <dali/internal/graphics/gles-impl/egl-graphics-controller.h>
+
+namespace Dali::Graphics::GLES
+{
+AgingSyncObject::AgingSyncObject(Graphics::EglGraphicsController& controller, const Context* writeContext)
+: controller(controller),
+ writeContext(writeContext)
+{
+ auto gl = controller.GetGL();
+ if(gl)
+ {
+ glSyncObject = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ }
+}
+
+AgingSyncObject::~AgingSyncObject()
+{
+ auto gl = controller.GetGL();
+ if(gl && glSyncObject != nullptr)
+ {
+ gl->DeleteSync(glSyncObject);
+ }
+}
+
+SyncPool::~SyncPool() = default;
+
+AgingSyncObject* SyncPool::AllocateSyncObject(const Context* writeContext)
+{
+ std::unique_ptr<AgingSyncObject> syncObject = std::make_unique<AgingSyncObject>(mController, writeContext);
+ mSyncObjects.push_back(std::move(syncObject));
+ return mSyncObjects.back().get();
+}
+
+void SyncPool::Wait(AgingSyncObject* syncPoolObject)
+{
+ auto gl = mController.GetGL();
+ if(gl && syncPoolObject->glSyncObject != nullptr)
+ {
+ syncPoolObject->syncing = true;
+ gl->WaitSync(syncPoolObject->glSyncObject, 0, GL_TIMEOUT_IGNORED);
+ }
+}
+
+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())
+ {
+ iter->reset();
+ }
+}
+
+/**
+ * Age sync objects. Call at the end of each frame.
+ * When a sync object is older than 2 frames, delete it.
+ */
+void SyncPool::AgeSyncObjects()
+{
+ if(!mSyncObjects.empty())
+ {
+ // Age the remaining sync objects.
+ for(auto& agingSyncObject : mSyncObjects)
+ {
+ if(agingSyncObject != nullptr && agingSyncObject->glSyncObject != 0)
+ {
+ if(agingSyncObject->age > 0)
+ {
+ agingSyncObject->age--;
+ }
+ else
+ {
+ agingSyncObject.reset();
+ }
+ }
+ }
+ }
+ // 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());
+}
+
+} // namespace Dali::Graphics::GLES
--- /dev/null
+#ifndef DALI_GRAPHICS_GLES_SYNC_POOL_H
+#define DALI_GRAPHICS_GLES_SYNC_POOL_H
+
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/graphics-api/graphics-types.h>
+#include <dali/integration-api/gl-abstraction.h>
+#include <dali/public-api/common/vector-wrapper.h>
+
+namespace Dali::Graphics
+{
+class EglGraphicsController;
+
+namespace GLES
+{
+class Context;
+
+struct AgingSyncObject
+{
+ AgingSyncObject(Graphics::EglGraphicsController& controller, const Context* writeContext);
+ ~AgingSyncObject();
+
+ EglGraphicsController& controller;
+ const Context* writeContext;
+ GLsync glSyncObject{0};
+ uint8_t age{2};
+ bool syncing{false};
+};
+using AgingSyncPtrRef = std::unique_ptr<AgingSyncObject>&;
+
+/**
+ * A vector of current fence syncs. They only age if glWaitSync is called on them in the
+ * same frame they are created, otherwise they are deleted.
+ * They must be created in the writeContext, but can be synced from a readContext.
+ * (Pool per context? - probably only ever used in resource context!)
+ */
+class SyncPool
+{
+public:
+ explicit SyncPool(Graphics::EglGraphicsController& graphicsController)
+ : mController(graphicsController)
+ {
+ }
+
+ ~SyncPool();
+
+ /**
+ * Allocate a sync object in the writeContext
+ * @param writeContext
+ * @return An owned ptr to a sync object
+ */
+ AgingSyncObject* AllocateSyncObject(const Context* writeContext);
+
+ /**
+ * Wait on a sync object in any context
+ * @param syncPoolObject The object to wait on.
+ */
+ void Wait(AgingSyncObject* syncPoolObject);
+
+ /**
+ * Delete the sync object if it's not needed.
+ *
+ */
+ void FreeSyncObject(AgingSyncObject* agingSyncObject);
+
+ /**
+ * Age outstanding sync objects. Call at the end of each frame.
+ * When a sync object is older than 2 frames, delete it.
+ */
+ void AgeSyncObjects();
+
+private:
+ std::vector<std::unique_ptr<AgingSyncObject>> mSyncObjects;
+ EglGraphicsController& mController;
+};
+
+} // namespace GLES
+} // namespace Dali::Graphics
+
+#endif //DALI_GRAPHICS_GLES_SYNC_POOL_H
--- /dev/null
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include "gles-texture-dependency-checker.h"
+
+// EXTERNAL INCLUDES
+#include <dali/internal/graphics/gles-impl/egl-graphics-controller.h>
+
+namespace Dali::Graphics::GLES
+{
+void TextureDependencyChecker::Reset()
+{
+ for(auto& textureDependency : mTextureDependencies)
+ {
+ for(auto texture : textureDependency.textures)
+ {
+ texture->SetDependencyIndex(0xffffffff);
+ }
+ if(!textureDependency.syncing)
+ {
+ mController.GetSyncPool().FreeSyncObject(textureDependency.agingSyncObject);
+ }
+ }
+ mTextureDependencies.clear();
+}
+
+void TextureDependencyChecker::AddTextures(const GLES::Context* writeContext, const GLES::Framebuffer* framebuffer)
+{
+ uint32_t index = mTextureDependencies.size();
+ mTextureDependencies.emplace_back();
+ TextureDependency& textureDependency = mTextureDependencies.back();
+
+ for(int i = 0; i < 3; ++i)
+ {
+ GLES::Texture* texture{nullptr};
+ switch(i)
+ {
+ case 0:
+ texture = static_cast<GLES::Texture*>(framebuffer->GetCreateInfo().colorAttachments[0].texture);
+ break;
+ case 1:
+ texture = static_cast<GLES::Texture*>(framebuffer->GetCreateInfo().depthStencilAttachment.depthTexture);
+ break;
+ case 2:
+ texture = static_cast<GLES::Texture*>(framebuffer->GetCreateInfo().depthStencilAttachment.stencilTexture);
+ break;
+ }
+ if(texture != nullptr)
+ {
+ textureDependency.textures.push_back(texture);
+ texture->SetDependencyIndex(index);
+ }
+ }
+ textureDependency.writeContext = const_cast<GLES::Context*>(writeContext);
+ textureDependency.framebuffer = const_cast<GLES::Framebuffer*>(framebuffer);
+ textureDependency.agingSyncObject = mController.GetSyncPool().AllocateSyncObject(writeContext);
+}
+
+void TextureDependencyChecker::CheckNeedsSync(const GLES::Context* readContext, const GLES::Texture* texture)
+{
+ uint32_t dependencyIndex = texture->GetDependencyIndex();
+ if(dependencyIndex < mTextureDependencies.size())
+ {
+ auto& textureDependency = mTextureDependencies[dependencyIndex];
+ if(!textureDependency.syncing && textureDependency.writeContext != readContext)
+ {
+ // Needs syncing!
+ textureDependency.syncing = true;
+
+ // Wait on the sync object in GPU. This will ensure that the writeContext completes its tasks prior
+ // to the sync point.
+ mController.GetSyncPool().Wait(textureDependency.agingSyncObject);
+ }
+ }
+}
+
+} // namespace Dali::Graphics::GLES
--- /dev/null
+#ifndef DALI_GLES_TEXTURE_DEPENDENCY_CHECKER_H
+#define DALI_GLES_TEXTURE_DEPENDENCY_CHECKER_H
+
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/public-api/common/vector-wrapper.h>
+
+namespace Dali::Graphics
+{
+class EglGraphicsController;
+
+namespace GLES
+{
+class Context;
+class Framebuffer;
+class Texture;
+class AgingSyncObject;
+
+/**
+ * Class to handle dependency checks between textures on different
+ * GL Contexts.
+ *
+ * We have a shared (resource) context for writing to offscreen framebuffers,
+ * and separate contexts for each window/scene.
+ * If a framebuffer attachment is used in a scene, then it needs a sync point
+ * in the GPU in order to ensure that the first context finishes writing to the
+ * texture before it is read in the scene context.
+ */
+class TextureDependencyChecker
+{
+public:
+ explicit TextureDependencyChecker(EglGraphicsController& controller)
+ : mController(controller)
+ {
+ }
+
+ /**
+ * Clear all the textures. Call at the start of a frame
+ */
+ void Reset();
+
+ /**
+ * Add Texture dependencies
+ *
+ * @param[in] writeContext The context of the framebuffer's render pass
+ * @param[in] framebuffer The framebuffer to collect textures from
+ */
+ void AddTextures(const Context* writeContext, const Framebuffer* framebuffer);
+
+ /**
+ * Check if the given texture needs syncing before being read. This
+ * will perform a glWaitSync() (GPU side semaphore) if the texture
+ * needs syncing.
+ * @param[in] readContext The context that the texture is being read (drawn with)
+ * @param[in] texture The texture being read
+ */
+ void CheckNeedsSync(const Context* readContext, const Texture* texture);
+
+private:
+ struct TextureDependency
+ {
+ std::vector<Texture*> textures;
+ Context* writeContext{nullptr};
+ Framebuffer* framebuffer{nullptr};
+ AgingSyncObject* agingSyncObject;
+ bool syncing{false};
+ };
+ std::vector<TextureDependency> mTextureDependencies;
+ EglGraphicsController& mController;
+};
+
+} // namespace GLES
+} // namespace Dali::Graphics
+
+#endif //DALI_GLES_TEXTURE_DEPENDENCY_CHECKER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
{
mGraphicsController.GetCurrentContext()->InvalidateDepthStencilBuffers();
}
+
+ mGraphicsController.PostRender();
}
void EglGraphics::SetFirstFrameAfterResume()