/*
- * 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.
#include "gles-context.h"
#include <dali/integration-api/adaptor-framework/render-surface-interface.h>
+#include <dali/integration-api/debug.h>
#include <dali/integration-api/gl-abstraction.h>
#include <dali/integration-api/gl-defines.h>
#include <dali/internal/graphics/common/graphics-interface.h>
+#include <dali/public-api/math/math-utils.h>
#include "egl-graphics-controller.h"
#include "gles-graphics-buffer.h"
#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>
#include <map>
+#include <unordered_map>
namespace Dali::Graphics::GLES
{
struct Context::Impl
{
- Impl(EglGraphicsController& controller)
+ explicit Impl(EglGraphicsController& controller)
: mController(controller)
{
}
* that VertexInputState has been set correctly for the pipeline.
*
*/
- void BindProgramVAO(GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
+ void BindProgramVAO(const GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
{
+ // Calculate attributes location hash unordered.
+ std::size_t hash = 0;
+ for(const auto& attr : vertexInputState.attributes)
+ {
+ hash ^= std::hash<uint32_t>{}(attr.location);
+ }
+
auto& gl = *mController.GetGL();
auto iter = mProgramVAOMap.find(program);
if(iter != mProgramVAOMap.end())
{
- if(mProgramVAOCurrentState != iter->second)
+ auto attributeIter = iter->second.find(hash);
+ if(attributeIter != iter->second.end())
{
- mProgramVAOCurrentState = iter->second;
- gl.BindVertexArray(iter->second);
+ if(mProgramVAOCurrentState != attributeIter->second)
+ {
+ mProgramVAOCurrentState = attributeIter->second;
+ gl.BindVertexArray(attributeIter->second);
+ }
+ return;
}
- return;
}
uint32_t vao;
gl.GenVertexArrays(1, &vao);
gl.BindVertexArray(vao);
- mProgramVAOMap[program] = vao;
+ mProgramVAOMap[program][hash] = vao;
for(const auto& attr : vertexInputState.attributes)
{
gl.EnableVertexAttribArray(attr.location);
const GLES::RenderPass* mCurrentRenderPass{nullptr};
// Each context must have own VAOs as they cannot be shared
- std::map<GLES::ProgramImpl*, uint32_t> mProgramVAOMap; ///< GL program-VAO map
- uint32_t mProgramVAOCurrentState{0u}; ///< Currently bound VAO
- GLStateCache mGlStateCache{}; ///< GL status cache
+ std::unordered_map<const GLES::ProgramImpl*, std::map<std::size_t, uint32_t>> mProgramVAOMap; ///< GL program-VAO map
+ uint32_t mProgramVAOCurrentState{0u}; ///< Currently bound VAO
+ GLStateCache mGlStateCache{}; ///< GL status cache
bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
+
+ EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context
+
+ EGLSurface mCacheDrawReadSurface{0u}; ///< cached 'read' surface
+ EGLSurface mCacheDrawWriteSurface{0u}; ///< cached 'write' surface
+ EGLContext mCacheEGLGraphicsContext{0u}; ///< cached window context
};
Context::Context(EglGraphicsController& controller)
mImpl = std::make_unique<Impl>(controller);
}
-Context::~Context() = default;
+Context::~Context()
+{
+ // Destroy native rendering context if one exists
+ if(mImpl->mNativeDrawContext)
+ {
+ eglDestroyContext(eglGetCurrentDisplay(), mImpl->mNativeDrawContext);
+ mImpl->mNativeDrawContext = EGL_NO_CONTEXT;
+ }
+}
-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();
newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
}
+ if(!currentProgram && !newProgram)
+ {
+ // Early out if we have no program for this pipeline.
+ DALI_LOG_ERROR("No program defined for pipeline\n");
+ return;
+ }
+
if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
{
if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
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.
void Context::ResolveStandaloneUniforms()
{
// Find reflection for program
- const auto program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
- const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
+ const GLES::Program* program{nullptr};
+
+ if(mImpl->mNewPipeline)
+ {
+ program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+ }
+ else if(mImpl->mCurrentPipeline)
+ {
+ program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
+ }
- // Update program uniforms
- program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
+ if(program)
+ {
+ const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
+ // Update program uniforms
+ program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
+ }
}
void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
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;
const auto clearValues = renderPassBegin.clearValues.Ptr();
- if(!mImpl->mGlStateCache.mClearColorSet ||
- mImpl->mGlStateCache.mClearColor.r != clearValues[0].color.r ||
- mImpl->mGlStateCache.mClearColor.g != clearValues[0].color.g ||
- mImpl->mGlStateCache.mClearColor.b != clearValues[0].color.b ||
- mImpl->mGlStateCache.mClearColor.a != clearValues[0].color.a)
+ if(!Dali::Equals(mImpl->mGlStateCache.mClearColor.r, clearValues[0].color.r) ||
+ !Dali::Equals(mImpl->mGlStateCache.mClearColor.g, clearValues[0].color.g) ||
+ !Dali::Equals(mImpl->mGlStateCache.mClearColor.b, clearValues[0].color.b) ||
+ !Dali::Equals(mImpl->mGlStateCache.mClearColor.a, clearValues[0].color.a) ||
+ !mImpl->mGlStateCache.mClearColorSet)
{
gl.ClearColor(clearValues[0].color.r,
clearValues[0].color.g,
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);
}
}
}
{
mImpl->mCurrentPipeline = nullptr;
}
+
+ // Remove cached VAO map
+ auto* gl = mImpl->mController.GetGL();
+ if(gl)
+ {
+ const auto* program = pipeline->GetCreateInfo().programState->program;
+ if(program)
+ {
+ const auto* programImpl = static_cast<const GLES::Program*>(program)->GetImplementation();
+ if(programImpl)
+ {
+ auto iter = mImpl->mProgramVAOMap.find(programImpl);
+ if(iter != mImpl->mProgramVAOMap.end())
+ {
+ for(auto& attributeHashPair : iter->second)
+ {
+ auto vao = attributeHashPair.second;
+ gl->DeleteVertexArrays(1, &vao);
+ if(mImpl->mProgramVAOCurrentState == vao)
+ {
+ mImpl->mProgramVAOCurrentState = 0u;
+ }
+ }
+ mImpl->mProgramVAOMap.erase(iter);
+ }
+ }
+ }
+ }
+}
+
+void Context::PrepareForNativeRendering()
+{
+ // this should be pretty much constant
+ auto display = eglGetCurrentDisplay();
+ auto drawSurface = eglGetCurrentSurface(EGL_DRAW);
+ auto readSurface = eglGetCurrentSurface(EGL_READ);
+ auto context = eglGetCurrentContext();
+
+ // push the surface and context data to the impl
+ // It's needed to restore context
+ if(!mImpl->mCacheEGLGraphicsContext)
+ {
+ mImpl->mCacheDrawWriteSurface = drawSurface;
+ mImpl->mCacheDrawReadSurface = readSurface;
+ mImpl->mCacheEGLGraphicsContext = context;
+ }
+
+ if(!mImpl->mNativeDrawContext)
+ {
+ EGLint configId{0u};
+ eglQueryContext(display, mImpl->mController.GetSharedContext(), EGL_CONFIG_ID, &configId);
+
+ EGLint configAttribs[3];
+ configAttribs[0] = EGL_CONFIG_ID;
+ configAttribs[1] = configId;
+ configAttribs[2] = EGL_NONE;
+
+ EGLConfig config;
+ EGLint numConfigs;
+ if(eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) != EGL_TRUE)
+ {
+ DALI_LOG_ERROR("eglChooseConfig failed!\n");
+ return;
+ }
+
+ auto version = int(mImpl->mController.GetGLESVersion());
+
+ std::vector<EGLint> attribs;
+ attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
+ attribs.push_back(version / 10);
+ attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
+ attribs.push_back(version % 10);
+ attribs.push_back(EGL_NONE);
+
+ mImpl->mNativeDrawContext = eglCreateContext(display, config, mImpl->mController.GetSharedContext(), attribs.data());
+ if(mImpl->mNativeDrawContext == EGL_NO_CONTEXT)
+ {
+ DALI_LOG_ERROR("eglCreateContext failed!\n");
+ return;
+ }
+ }
+
+ eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext);
+}
+
+void Context::RestoreFromNativeRendering()
+{
+ auto display = eglGetCurrentDisplay();
+
+ // bring back original context
+ eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext);
}
} // namespace Dali::Graphics::GLES