sceneSurfaceResized = scene.IsSurfaceRectChanged();
windowSurface->SetIsResizing(sceneSurfaceResized);
- windowSurface->InitializeGraphics();
-
// clear previous frame damaged render items rects, buffer history is tracked on surface level
mDamagedRects.clear();
// Collect damage rects
mCore.PreRender(scene, mDamagedRects);
+ graphics.ActivateSurfaceContext(windowSurface);
+
// Render off-screen frame buffers first if any
mCore.RenderScene(windowRenderStatus, scene, true);
void EglGraphicsController::ActivateResourceContext()
{
mCurrentContext = mContext.get();
+ mCurrentContext->GlContextCreated();
}
void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface)
if(iter != mSurfaceContexts.end())
{
mCurrentContext = iter->second.get();
+ mCurrentContext->GlContextCreated();
}
}
}
ProcessDiscardQueue<GLES::Framebuffer>(mDiscardFramebufferQueue);
// Process pipelines
- ProcessDiscardQueue<GLES::Pipeline>(mDiscardPipelineQueue);
+ ProcessDiscardQueue(mDiscardPipelineQueue);
// Process programs
ProcessDiscardQueue<GLES::Program>(mDiscardProgramQueue);
}
case GLES::CommandType::SET_SCISSOR_TEST:
{
- if(cmd.scissorTest.enable)
- {
- mGlAbstraction->Enable(GL_SCISSOR_TEST);
- }
- else
- {
- mGlAbstraction->Disable(GL_SCISSOR_TEST);
- }
+ mCurrentContext->SetScissorTestEnabled(cmd.scissorTest.enable);
break;
}
case GLES::CommandType::SET_VIEWPORT: // @todo Consider correcting for orientation here?
}
mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
- mGlAbstraction->BindTexture(bindTarget, texture->GetGLTexture());
+ mCurrentContext->BindTexture(bindTarget, texture->GetTextureTypeId(), texture->GetGLTexture());
if(!isSubImage)
{
{
auto* texture = mTextureMipmapGenerationRequests.front();
- mGlAbstraction->BindTexture(texture->GetGlTarget(), texture->GetGLTexture());
- mGlAbstraction->GenerateMipmap(texture->GetGlTarget());
+ mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture());
+ mCurrentContext->GenerateMipmap(texture->GetGlTarget());
mTextureMipmapGenerationRequests.pop();
}
{
// Final flush
Flush();
+
+ if(mContext)
+ {
+ mContext->GlContextDestroyed();
+ }
+
+ for(auto&& context : mSurfaceContexts)
+ {
+ if(context.second)
+ {
+ context.second->GlContextDestroyed();
+ }
+ }
}
/**
// Process main command queue
ProcessCommandQueues();
+ // Reset texture cache in the contexts while destroying textures
+ ResetTextureCache();
+
+ // Reset buffer cache in the contexts while destroying buffers
+ ResetBufferCache();
+
// Process discards
ProcessDiscardQueues();
}
/**
- * @brief Processes a create queue for type specified
+ * @brief Processes a discard queue for type specified
*
- * @param[in,out] queue Reference to the create queue
+ * @param[in,out] queue Reference to the discard queue
*/
template<class U, class T>
void ProcessDiscardQueue(T& queue)
}
/**
+ * @brief Processes a discard queue for pipeline
+ *
+ * @param[in,out] queue Reference to the create queue
+ */
+ void ProcessDiscardQueue(std::queue<GLES::Pipeline*>& queue)
+ {
+ while(!queue.empty())
+ {
+ auto* object = const_cast<GLES::Pipeline*>(queue.front());
+
+ // Inform the contexts to invalidate the pipeline if cached
+ if(mContext)
+ {
+ mContext->InvalidateCachedPipeline(object);
+ }
+
+ for(auto&& context : mSurfaceContexts)
+ {
+ if(context.second)
+ {
+ context.second->InvalidateCachedPipeline(object);
+ }
+ }
+
+ // Destroy
+ object->DestroyResource();
+
+ // Free
+ auto* clbk = object->GetCreateInfo().allocationCallbacks;
+ if(clbk)
+ {
+ // Call destructor
+ using GLESPipeline = GLES::Pipeline;
+ object->~GLESPipeline();
+
+ // Free memory
+ clbk->freeCallback(object, clbk->userData);
+ }
+ else
+ {
+ delete object;
+ }
+ queue.pop();
+ }
+ }
+
+ /**
* @brief Processes all resource create queues
*/
void ProcessCreateQueues();
return mIsShuttingDown;
}
+ /**
+ * @brief Reset texture cache in the contexts
+ */
+ void ResetTextureCache()
+ {
+ if(mContext)
+ {
+ mContext->GetGLStateCache().ResetTextureCache();
+ }
+
+ for(auto& context : mSurfaceContexts)
+ {
+ if(context.second)
+ {
+ context.second->GetGLStateCache().ResetTextureCache();
+ }
+ }
+ }
+
+ /**
+ * @brief Reset buffer cache in the contexts
+ */
+ void ResetBufferCache()
+ {
+ if(mContext)
+ {
+ mContext->GetGLStateCache().ResetBufferCache();
+ }
+
+ for(auto& context : mSurfaceContexts)
+ {
+ if(context.second)
+ {
+ context.second->GetGLStateCache().ResetBufferCache();
+ }
+ }
+ }
+
void ProcessCommandBuffer(const GLES::CommandBuffer& commandBuffer);
// Resolves presentation
*/
void ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface);
+ /**
+ * @brief Returns the current context
+ *
+ * @return the current context
+ */
+ GLES::Context* GetCurrentContext() const
+ {
+ return mCurrentContext;
+ }
+
private:
Integration::GlAbstraction* mGlAbstraction{nullptr};
Integration::GlContextHelperAbstraction* mGlContextHelperAbstraction{nullptr};
${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-object.cpp
+ ${adaptor_graphics_dir}/gles-impl/gles-framebuffer-state-cache.cpp
)
--- /dev/null
+#ifndef DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
+#define DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
+
+/*
+ * Copyright (c) 2021 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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/graphics-api/graphics-types.h>
+#include <dali/integration-api/gl-abstraction.h>
+#include <dali/integration-api/gl-defines.h>
+
+// INTERNAL INCLUDES
+#include "gles-framebuffer-state-cache.h"
+
+namespace Dali::Graphics
+{
+namespace GLES
+{
+namespace
+{
+static constexpr unsigned int MAX_TEXTURE_UNITS = 32; // As what is defined in gl-defines.h, which is more than DALi uses anyways
+static constexpr unsigned int MAX_TEXTURE_TARGET = 4; // We only support GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP and GL_TEXTURE_EXTERNAL_OES
+static constexpr unsigned int MAX_ATTRIBUTE_CACHE_SIZE = 8; // Size of the VertexAttributeArray enables
+} // namespace
+
+/**
+ * @brief Cache of GL State per context
+ */
+struct GLStateCache
+{
+ /**
+ * Reset the cached texture ids.
+ */
+ void ResetTextureCache()
+ {
+ // reset the cached texture id's in case the driver re-uses them
+ // when creating new textures
+ for(unsigned int i = 0; i < MAX_TEXTURE_UNITS; ++i)
+ {
+ for(unsigned int j = 0; j < MAX_TEXTURE_TARGET; ++j)
+ {
+ mBoundTextureId[i][j] = 0;
+ }
+ }
+ }
+
+ /**
+ * Reset the cached buffer ids.
+ */
+ void ResetBufferCache()
+ {
+ // reset the cached buffer id's
+ // fixes problem where some drivers will a generate a buffer with the
+ // same id, as the last deleted buffer id.
+ mBoundArrayBufferId = 0;
+ mBoundElementArrayBufferId = 0;
+ }
+
+ /**
+ * @return true if next draw operation will write to depth buffer
+ */
+ bool DepthBufferWriteEnabled() const
+ {
+ return mDepthBufferEnabled && mDepthMaskEnabled;
+ }
+
+ /**
+ * @return true if next draw operation will write to stencil buffer
+ */
+ bool StencilBufferWriteEnabled() const
+ {
+ return mStencilBufferEnabled && (mStencilMask > 0);
+ }
+
+ // glEnable/glDisable states
+ bool mColorMask{true};
+ GLuint mStencilMask{0xFF};
+ bool mBlendEnabled{false};
+ bool mDepthBufferEnabled{false};
+ bool mDepthMaskEnabled{false};
+ bool mScissorTestEnabled{false};
+ bool mStencilBufferEnabled{false};
+ bool mClearColorSet{false};
+
+ // glBindBuffer() state
+ GLuint mBoundArrayBufferId{0}; ///< The ID passed to glBindBuffer(GL_ARRAY_BUFFER)
+ GLuint mBoundElementArrayBufferId{0}; ///< The ID passed to glBindBuffer(GL_ELEMENT_ARRAY_BUFFER)
+
+ // glBindTexture() state
+ GLenum mActiveTextureUnit{MAX_TEXTURE_UNITS};
+ GLuint mBoundTextureId[MAX_TEXTURE_UNITS][MAX_TEXTURE_TARGET]; ///< The ID passed to glBindTexture()
+
+ // glBlendFuncSeparate() state
+ BlendFactor mBlendFuncSeparateSrcRGB{BlendFactor::ONE}; ///< The srcRGB parameter passed to glBlendFuncSeparate()
+ BlendFactor mBlendFuncSeparateDstRGB{BlendFactor::ZERO}; ///< The dstRGB parameter passed to glBlendFuncSeparate()
+ BlendFactor mBlendFuncSeparateSrcAlpha{BlendFactor::ONE}; ///< The srcAlpha parameter passed to glBlendFuncSeparate()
+ BlendFactor mBlendFuncSeparateDstAlpha{BlendFactor::ZERO}; ///< The dstAlpha parameter passed to glBlendFuncSeparate()
+
+ // glBlendEquationSeparate state
+ BlendOp mBlendEquationSeparateModeRGB{BlendOp::ADD}; ///< Controls RGB blend mode
+ BlendOp mBlendEquationSeparateModeAlpha{BlendOp::ADD}; ///< Controls Alpha blend mode
+
+ // glStencilFunc() and glStencilOp() state.
+ CompareOp mStencilFunc{CompareOp::ALWAYS};
+ GLuint mStencilFuncRef{0};
+ GLuint mStencilFuncMask{0xFFFFFFFF};
+ StencilOp mStencilOpFail{StencilOp::KEEP};
+ StencilOp mStencilOpDepthFail{StencilOp::KEEP};
+ StencilOp mStencilOpDepthPass{StencilOp::KEEP};
+
+ CompareOp mDepthFunction{CompareOp::LESS}; ///The depth function
+
+ Vector4 mClearColor{Color::WHITE}; ///< clear color. never used until it's been set by the user
+
+ CullMode mCullFaceMode{CullMode::NONE}; ///< Face culling mode
+
+ // Vertex Attribute Buffer enable caching
+ bool mVertexAttributeCachedState[MAX_ATTRIBUTE_CACHE_SIZE]; ///< Value cache for Enable Vertex Attribute
+ bool mVertexAttributeCurrentState[MAX_ATTRIBUTE_CACHE_SIZE]; ///< Current state on the driver for Enable Vertex Attribute
+
+ FrameBufferStateCache mFrameBufferStateCache{}; ///< frame buffer state cache
+};
+
+} // namespace GLES
+
+} // namespace Dali::Graphics
+
+#endif // DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
~Impl() = default;
+ /**
+ * Sets the initial GL state.
+ */
+ void InitializeGlState()
+ {
+ auto& gl = *mController.GetGL();
+
+ mGlStateCache.mClearColorSet = false;
+ mGlStateCache.mColorMask = true;
+ mGlStateCache.mStencilMask = 0xFF;
+ mGlStateCache.mBlendEnabled = false;
+ mGlStateCache.mDepthBufferEnabled = false;
+ mGlStateCache.mDepthMaskEnabled = false;
+ mGlStateCache.mScissorTestEnabled = false;
+ mGlStateCache.mStencilBufferEnabled = false;
+
+ gl.Disable(GL_DITHER);
+
+ mGlStateCache.mBoundArrayBufferId = 0;
+ mGlStateCache.mBoundElementArrayBufferId = 0;
+ mGlStateCache.mActiveTextureUnit = 0;
+
+ mGlStateCache.mBlendFuncSeparateSrcRGB = BlendFactor::ONE;
+ mGlStateCache.mBlendFuncSeparateDstRGB = BlendFactor::ZERO;
+ mGlStateCache.mBlendFuncSeparateSrcAlpha = BlendFactor::ONE;
+ mGlStateCache.mBlendFuncSeparateDstAlpha = BlendFactor::ZERO;
+
+ // initial state is GL_FUNC_ADD for both RGB and Alpha blend modes
+ mGlStateCache.mBlendEquationSeparateModeRGB = BlendOp::ADD;
+ mGlStateCache.mBlendEquationSeparateModeAlpha = BlendOp::ADD;
+
+ mGlStateCache.mCullFaceMode = CullMode::NONE; //By default cullface is disabled, front face is set to CCW and cull face is set to back
+
+ //Initialze vertex attribute cache
+ memset(&mGlStateCache.mVertexAttributeCachedState, 0, sizeof(mGlStateCache.mVertexAttributeCachedState));
+ memset(&mGlStateCache.mVertexAttributeCurrentState, 0, sizeof(mGlStateCache.mVertexAttributeCurrentState));
+
+ //Initialize bound 2d texture cache
+ memset(&mGlStateCache.mBoundTextureId, 0, sizeof(mGlStateCache.mBoundTextureId));
+
+ mGlStateCache.mFrameBufferStateCache.Reset();
+ }
+
+ /**
+ * Flushes vertex attribute location changes to the driver
+ */
+ void FlushVertexAttributeLocations()
+ {
+ auto& gl = *mController.GetGL();
+
+ for(unsigned int i = 0; i < MAX_ATTRIBUTE_CACHE_SIZE; ++i)
+ {
+ // see if the cached state is different to the actual state
+ if(mGlStateCache.mVertexAttributeCurrentState[i] != mGlStateCache.mVertexAttributeCachedState[i])
+ {
+ // it's different so make the change to the driver and update the cached state
+ mGlStateCache.mVertexAttributeCurrentState[i] = mGlStateCache.mVertexAttributeCachedState[i];
+
+ if(mGlStateCache.mVertexAttributeCurrentState[i])
+ {
+ gl.EnableVertexAttribArray(i);
+ }
+ else
+ {
+ gl.DisableVertexAttribArray(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * Either enables or disables a vertex attribute location in the cache
+ * The cahnges won't take affect until FlushVertexAttributeLocations is called
+ * @param location attribute location
+ * @param state attribute state
+ */
+ void SetVertexAttributeLocation(unsigned int location, bool state)
+ {
+ auto& gl = *mController.GetGL();
+
+ if(location >= MAX_ATTRIBUTE_CACHE_SIZE)
+ {
+ // not cached, make the gl call through context
+ if(state)
+ {
+ gl.EnableVertexAttribArray(location);
+ }
+ else
+ {
+ gl.DisableVertexAttribArray(location);
+ }
+ }
+ else
+ {
+ // set the cached state, it will be set at the next draw call
+ // if it's different from the current driver state
+ mGlStateCache.mVertexAttributeCachedState[location] = state;
+ }
+ }
+
EglGraphicsController& mController;
- const GLES::Pipeline* mCurrentPipeline{nullptr}; ///< Currently bound pipeline
- const GLES::Pipeline* mNewPipeline{nullptr}; ///< New pipeline to be set on flush
+ const GLES::PipelineImpl* mCurrentPipeline{nullptr}; ///< Currently bound pipeline
+ const GLES::PipelineImpl* mNewPipeline{nullptr}; ///< New pipeline to be set on flush
std::vector<Graphics::TextureBinding> mCurrentTextureBindings{};
std::vector<Graphics::SamplerBinding> mCurrentSamplerBindings{};
// Current render pass and render target
const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
const GLES::RenderPass* mCurrentRenderPass{nullptr};
+
+ GLStateCache mGlStateCache{}; ///< GL status cache
+
+ bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
};
Context::Context(EglGraphicsController& controller)
{
auto& gl = *mImpl->mController.GetGL();
- // Change pipeline
- if(mImpl->mNewPipeline)
+ // Execute states if pipeline is changed
+ const auto& currentProgram = mImpl->mCurrentPipeline ? static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr;
+ const auto& newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+
+ if(mImpl->mCurrentPipeline != mImpl->mNewPipeline)
{
- // Execute states if different
- mImpl->mCurrentPipeline = mImpl->mNewPipeline;
- mImpl->mNewPipeline = nullptr;
- }
- mImpl->mCurrentPipeline->GetPipeline().Bind(nullptr);
+ if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
+ {
+ mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
+ }
- // Blend state
- ResolveBlendState();
+ // Blend state
+ ResolveBlendState();
- // Resolve rasterization state
- ResolveRasterizationState();
+ // Resolve rasterization state
+ ResolveRasterizationState();
+ }
// Resolve uniform buffers
ResolveUniformBuffers();
// Bind textures
// Map binding# to sampler location
- const auto program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
-
- const auto& reflection = program->GetReflection();
+ const auto& reflection = newProgram->GetReflection();
const auto& samplers = reflection.GetSamplers();
for(const auto& binding : mImpl->mCurrentTextureBindings)
{
}
texture->Bind(binding);
+
texture->Prepare(); // @todo also non-const.
if(binding.binding < samplers.size()) // binding maps to texture unit. (texture bindings should also be in binding order)
}
// for each attribute bind vertices
- const auto& pipelineState = mImpl->mCurrentPipeline->GetCreateInfo();
+ const auto& pipelineState = mImpl->mNewPipeline->GetCreateInfo();
const auto& vi = pipelineState.vertexInputState;
for(const auto& attr : vi->attributes)
{
// Enable location
- gl.EnableVertexAttribArray(attr.location);
+ mImpl->SetVertexAttributeLocation(attr.location, true);
+
const auto& bufferSlot = mImpl->mCurrentVertexBufferBindings[attr.binding];
const auto& bufferBinding = vi->bufferBindings[attr.binding];
auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
// Bind buffer
- gl.BindBuffer(GL_ARRAY_BUFFER, glesBuffer);
+ BindBuffer(GL_ARRAY_BUFFER, glesBuffer);
+
gl.VertexAttribPointer(attr.location,
GLVertexFormat(attr.format).size,
GLVertexFormat(attr.format).format,
}
// Resolve topology
- const auto& ia = mImpl->mCurrentPipeline->GetCreateInfo().inputAssemblyState;
+ const auto& ia = mImpl->mNewPipeline->GetCreateInfo().inputAssemblyState;
// Bind uniforms
{
case DrawCallDescriptor::Type::DRAW:
{
+ mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+ mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+ mImpl->mGlStateCache.StencilBufferWriteEnabled());
+ mImpl->FlushVertexAttributeLocations();
+
gl.DrawArrays(GLESTopology(ia->topology),
drawCall.draw.firstVertex,
drawCall.draw.vertexCount);
}
case DrawCallDescriptor::Type::DRAW_INDEXED:
{
- const auto& binding = mImpl->mCurrentIndexBufferBinding;
- const auto* glesBuffer = binding.buffer;
- gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, glesBuffer->GetGLBuffer());
+ const auto& binding = mImpl->mCurrentIndexBufferBinding;
+ BindBuffer(GL_ELEMENT_ARRAY_BUFFER, binding.buffer->GetGLBuffer());
+
+ mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+ mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+ mImpl->mGlStateCache.StencilBufferWriteEnabled());
+ mImpl->FlushVertexAttributeLocations();
+
auto indexBufferFormat = GLIndexFormat(binding.format).format;
gl.DrawElements(GLESTopology(ia->topology),
drawCall.drawIndexed.indexCount,
}
ClearState();
+
+ // Change pipeline
+ if(mImpl->mNewPipeline)
+ {
+ mImpl->mCurrentPipeline = mImpl->mNewPipeline;
+ mImpl->mNewPipeline = nullptr;
+ }
}
void Context::BindTextures(const std::vector<Graphics::TextureBinding>& bindings)
void Context::BindPipeline(const GLES::Pipeline* newPipeline)
{
- mImpl->mNewPipeline = newPipeline;
+ DALI_ASSERT_ALWAYS(newPipeline && "Invalid pipeline");
+ mImpl->mNewPipeline = &newPipeline->GetPipeline();
}
void Context::BindUniformBuffers(const std::vector<UniformBufferBindingDescriptor>& uboBindings,
void Context::ResolveBlendState()
{
- const auto& state = mImpl->mCurrentPipeline->GetCreateInfo();
- const auto& bs = state.colorBlendState;
- auto& gl = *mImpl->mController.GetGL();
+ const auto& currentBlendState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().colorBlendState : nullptr;
+ const auto& newBlendState = mImpl->mNewPipeline->GetCreateInfo().colorBlendState;
// TODO: prevent leaking the state
- if(!bs)
- {
- return;
- }
-
- bs->blendEnable ? gl.Enable(GL_BLEND) : gl.Disable(GL_BLEND);
- if(!bs->blendEnable)
+ if(!newBlendState)
{
return;
}
- gl.BlendFunc(GLBlendFunc(bs->srcColorBlendFactor), GLBlendFunc(bs->dstColorBlendFactor));
+ auto& gl = *mImpl->mController.GetGL();
- if((GLBlendFunc(bs->srcColorBlendFactor) == GLBlendFunc(bs->srcAlphaBlendFactor)) &&
- (GLBlendFunc(bs->dstColorBlendFactor) == GLBlendFunc(bs->dstAlphaBlendFactor)))
+ if(!currentBlendState || currentBlendState->blendEnable != newBlendState->blendEnable)
{
- gl.BlendFunc(GLBlendFunc(bs->srcColorBlendFactor), GLBlendFunc(bs->dstColorBlendFactor));
+ if(newBlendState->blendEnable != mImpl->mGlStateCache.mBlendEnabled)
+ {
+ mImpl->mGlStateCache.mBlendEnabled = newBlendState->blendEnable;
+ newBlendState->blendEnable ? gl.Enable(GL_BLEND) : gl.Disable(GL_BLEND);
+ }
}
- else
+
+ if(!newBlendState->blendEnable)
{
- gl.BlendFuncSeparate(GLBlendFunc(bs->srcColorBlendFactor),
- GLBlendFunc(bs->dstColorBlendFactor),
- GLBlendFunc(bs->srcAlphaBlendFactor),
- GLBlendFunc(bs->dstAlphaBlendFactor));
+ return;
}
- if(GLBlendOp(bs->colorBlendOp) == GLBlendOp(bs->alphaBlendOp))
+
+ BlendFactor newSrcRGB(newBlendState->srcColorBlendFactor);
+ BlendFactor newDstRGB(newBlendState->dstColorBlendFactor);
+ BlendFactor newSrcAlpha(newBlendState->srcAlphaBlendFactor);
+ BlendFactor newDstAlpha(newBlendState->dstAlphaBlendFactor);
+
+ if(!currentBlendState ||
+ currentBlendState->srcColorBlendFactor != newSrcRGB ||
+ currentBlendState->dstColorBlendFactor != newDstRGB ||
+ currentBlendState->srcAlphaBlendFactor != newSrcAlpha ||
+ currentBlendState->dstAlphaBlendFactor != newDstAlpha)
{
- gl.BlendEquation(GLBlendOp(bs->colorBlendOp));
+ if((mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB != newSrcRGB) ||
+ (mImpl->mGlStateCache.mBlendFuncSeparateDstRGB != newDstRGB) ||
+ (mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha != newSrcAlpha) ||
+ (mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha != newDstAlpha))
+ {
+ mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB = newSrcRGB;
+ mImpl->mGlStateCache.mBlendFuncSeparateDstRGB = newDstRGB;
+ mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha = newSrcAlpha;
+ mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha = newDstAlpha;
+
+ if(newSrcRGB == newSrcAlpha && newDstRGB == newDstAlpha)
+ {
+ gl.BlendFunc(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB));
+ }
+ else
+ {
+ gl.BlendFuncSeparate(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB), GLBlendFunc(newSrcAlpha), GLBlendFunc(newDstAlpha));
+ }
+ }
}
- else
+
+ if(!currentBlendState ||
+ currentBlendState->colorBlendOp != newBlendState->colorBlendOp ||
+ currentBlendState->alphaBlendOp != newBlendState->alphaBlendOp)
{
- gl.BlendEquationSeparate(GLBlendOp(bs->colorBlendOp), GLBlendOp(bs->alphaBlendOp));
+ if(mImpl->mGlStateCache.mBlendEquationSeparateModeRGB != newBlendState->colorBlendOp ||
+ mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha != newBlendState->alphaBlendOp)
+ {
+ mImpl->mGlStateCache.mBlendEquationSeparateModeRGB = newBlendState->colorBlendOp;
+ mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha = newBlendState->alphaBlendOp;
+
+ if(newBlendState->colorBlendOp == newBlendState->alphaBlendOp)
+ {
+ gl.BlendEquation(GLBlendOp(newBlendState->colorBlendOp));
+ }
+ else
+ {
+ gl.BlendEquationSeparate(GLBlendOp(newBlendState->colorBlendOp), GLBlendOp(newBlendState->alphaBlendOp));
+ }
+ }
}
}
void Context::ResolveRasterizationState()
{
- const auto& state = mImpl->mCurrentPipeline->GetCreateInfo();
- const auto& rs = state.rasterizationState;
- auto& gl = *mImpl->mController.GetGL();
+ const auto& currentRasterizationState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().rasterizationState : nullptr;
+ const auto& newRasterizationState = mImpl->mNewPipeline->GetCreateInfo().rasterizationState;
// TODO: prevent leaking the state
- if(!rs)
+ if(!newRasterizationState)
{
return;
}
- if(rs->cullMode == CullMode::NONE)
- {
- gl.Disable(GL_CULL_FACE);
- }
- else
+ auto& gl = *mImpl->mController.GetGL();
+
+ if(!currentRasterizationState ||
+ currentRasterizationState->cullMode != newRasterizationState->cullMode)
{
- gl.Enable(GL_CULL_FACE);
- gl.CullFace(GLCullMode(rs->cullMode));
+ if(mImpl->mGlStateCache.mCullFaceMode != newRasterizationState->cullMode)
+ {
+ mImpl->mGlStateCache.mCullFaceMode = newRasterizationState->cullMode;
+ if(newRasterizationState->cullMode == CullMode::NONE)
+ {
+ gl.Disable(GL_CULL_FACE);
+ }
+ else
+ {
+ gl.Enable(GL_CULL_FACE);
+ gl.CullFace(GLCullMode(newRasterizationState->cullMode));
+ }
+ }
}
-
// TODO: implement polygon mode (fill, line, points)
// seems like we don't support it (no glPolygonMode())
}
auto& gl = *mImpl->mController.GetGL();
// Find reflection for program
- const auto program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
+ const auto program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
const auto& reflection = program->GetReflection();
if(targetInfo.surface)
{
// Bind surface FB
- gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
+ BindFrameBuffer(GL_FRAMEBUFFER, 0);
}
else if(targetInfo.framebuffer)
{
{
mask |= GL_COLOR_BUFFER_BIT;
- // Set clear color (todo: cache it!)
+ // Set clear color
// Something goes wrong here if Alpha mask is GL_TRUE
- gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- gl.ClearColor(renderPassBegin.clearValues[0].color.r,
- renderPassBegin.clearValues[0].color.g,
- renderPassBegin.clearValues[0].color.b,
- renderPassBegin.clearValues[0].color.a);
+ ColorMask(true);
+
+ if(!mImpl->mGlStateCache.mClearColorSet ||
+ mImpl->mGlStateCache.mClearColor.r != renderPassBegin.clearValues[0].color.r ||
+ mImpl->mGlStateCache.mClearColor.g != renderPassBegin.clearValues[0].color.g ||
+ mImpl->mGlStateCache.mClearColor.b != renderPassBegin.clearValues[0].color.b ||
+ mImpl->mGlStateCache.mClearColor.a != renderPassBegin.clearValues[0].color.a)
+ {
+ gl.ClearColor(renderPassBegin.clearValues[0].color.r,
+ renderPassBegin.clearValues[0].color.g,
+ renderPassBegin.clearValues[0].color.b,
+ renderPassBegin.clearValues[0].color.a);
+
+ mImpl->mGlStateCache.mClearColorSet = true;
+ mImpl->mGlStateCache.mClearColor = Vector4(renderPassBegin.clearValues[0].color.r,
+ renderPassBegin.clearValues[0].color.g,
+ renderPassBegin.clearValues[0].color.b,
+ renderPassBegin.clearValues[0].color.a);
+ }
}
// check for depth stencil
const auto& depthStencil = attachments.back();
if(depthStencil.loadOp == AttachmentLoadOp::CLEAR)
{
- gl.DepthMask(true);
+ if(!mImpl->mGlStateCache.mDepthMaskEnabled)
+ {
+ mImpl->mGlStateCache.mDepthMaskEnabled = true;
+ gl.DepthMask(true);
+ }
mask |= GL_DEPTH_BUFFER_BIT;
}
if(depthStencil.stencilLoadOp == AttachmentLoadOp::CLEAR)
{
- gl.StencilMask(0xFF);
+ if(mImpl->mGlStateCache.mStencilMask != 0xFF)
+ {
+ mImpl->mGlStateCache.mStencilMask = 0xFF;
+ gl.StencilMask(0xFF);
+ }
mask |= GL_STENCIL_BUFFER_BIT;
}
}
- gl.Enable(GL_SCISSOR_TEST);
+ SetScissorTestEnabled(true);
gl.Scissor(renderPassBegin.renderArea.x, renderPassBegin.renderArea.y, renderPassBegin.renderArea.width, renderPassBegin.renderArea.height);
- gl.Clear(mask);
- gl.Disable(GL_SCISSOR_TEST);
+ ClearBuffer(mask, true);
+ SetScissorTestEnabled(false);
mImpl->mCurrentRenderPass = &renderPass;
mImpl->mCurrentRenderTarget = &renderTarget;
void Context::ColorMask(bool enabled)
{
- auto& gl = *mImpl->mController.GetGL();
- gl.ColorMask(enabled, enabled, enabled, enabled);
+ if(enabled != mImpl->mGlStateCache.mColorMask)
+ {
+ mImpl->mGlStateCache.mColorMask = enabled;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.ColorMask(enabled, enabled, enabled, enabled);
+ }
}
void Context::ClearStencilBuffer()
{
- auto& gl = *mImpl->mController.GetGL();
- gl.Clear(GL_STENCIL_BUFFER_BIT);
+ ClearBuffer(GL_STENCIL_BUFFER_BIT, false);
}
void Context::ClearDepthBuffer()
{
- auto& gl = *mImpl->mController.GetGL();
- gl.Clear(GL_DEPTH_BUFFER_BIT);
+ ClearBuffer(GL_DEPTH_BUFFER_BIT, false);
}
-void Context::SetStencilTestEnable(bool stencilEnable)
+void Context::ClearBuffer(uint32_t mask, bool forceClear)
{
- auto& gl = *mImpl->mController.GetGL();
- if(stencilEnable)
+ mask = mImpl->mGlStateCache.mFrameBufferStateCache.GetClearMask(mask, forceClear, mImpl->mGlStateCache.mScissorTestEnabled);
+ if(mask > 0)
{
- gl.Enable(GL_STENCIL_TEST);
+ auto& gl = *mImpl->mController.GetGL();
+ gl.Clear(mask);
}
- else
+}
+
+void Context::SetScissorTestEnabled(bool scissorEnabled)
+{
+ if(mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled)
{
- gl.Disable(GL_STENCIL_TEST);
+ mImpl->mGlStateCache.mScissorTestEnabled = scissorEnabled;
+
+ auto& gl = *mImpl->mController.GetGL();
+ if(scissorEnabled)
+ {
+ gl.Enable(GL_SCISSOR_TEST);
+ }
+ else
+ {
+ gl.Disable(GL_SCISSOR_TEST);
+ }
+ }
+}
+
+void Context::SetStencilTestEnable(bool stencilEnable)
+{
+ if(stencilEnable != mImpl->mGlStateCache.mStencilBufferEnabled)
+ {
+ mImpl->mGlStateCache.mStencilBufferEnabled = stencilEnable;
+
+ auto& gl = *mImpl->mController.GetGL();
+ if(stencilEnable)
+ {
+ gl.Enable(GL_STENCIL_TEST);
+ }
+ else
+ {
+ gl.Disable(GL_STENCIL_TEST);
+ }
}
}
void Context::StencilMask(uint32_t writeMask)
{
- auto& gl = *mImpl->mController.GetGL();
- gl.StencilMask(writeMask);
+ if(writeMask != mImpl->mGlStateCache.mStencilMask)
+ {
+ mImpl->mGlStateCache.mStencilMask = writeMask;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.StencilMask(writeMask);
+ }
}
void Context::StencilFunc(Graphics::CompareOp compareOp,
uint32_t reference,
uint32_t compareMask)
{
- auto& gl = *mImpl->mController.GetGL();
- gl.StencilFunc(GLCompareOp(compareOp).op, reference, compareMask);
+ if(compareOp != mImpl->mGlStateCache.mStencilFunc ||
+ reference != mImpl->mGlStateCache.mStencilFuncRef ||
+ compareMask != mImpl->mGlStateCache.mStencilFuncMask)
+ {
+ mImpl->mGlStateCache.mStencilFunc = compareOp;
+ mImpl->mGlStateCache.mStencilFuncRef = reference;
+ mImpl->mGlStateCache.mStencilFuncMask = compareMask;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.StencilFunc(GLCompareOp(compareOp).op, reference, compareMask);
+ }
}
void Context::StencilOp(Graphics::StencilOp failOp,
Graphics::StencilOp depthFailOp,
Graphics::StencilOp passOp)
{
- auto& gl = *mImpl->mController.GetGL();
- gl.StencilOp(GLStencilOp(failOp).op, GLStencilOp(depthFailOp).op, GLStencilOp(passOp).op);
+ if(failOp != mImpl->mGlStateCache.mStencilOpFail ||
+ depthFailOp != mImpl->mGlStateCache.mStencilOpDepthFail ||
+ passOp != mImpl->mGlStateCache.mStencilOpDepthPass)
+ {
+ mImpl->mGlStateCache.mStencilOpFail = failOp;
+ mImpl->mGlStateCache.mStencilOpDepthFail = depthFailOp;
+ mImpl->mGlStateCache.mStencilOpDepthPass = passOp;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.StencilOp(GLStencilOp(failOp).op, GLStencilOp(depthFailOp).op, GLStencilOp(passOp).op);
+ }
}
void Context::SetDepthCompareOp(Graphics::CompareOp compareOp)
{
- auto& gl = *mImpl->mController.GetGL();
- gl.DepthFunc(GLCompareOp(compareOp).op);
+ if(compareOp != mImpl->mGlStateCache.mDepthFunction)
+ {
+ mImpl->mGlStateCache.mDepthFunction = compareOp;
+ auto& gl = *mImpl->mController.GetGL();
+ gl.DepthFunc(GLCompareOp(compareOp).op);
+ }
}
void Context::SetDepthTestEnable(bool depthTestEnable)
{
- auto& gl = *mImpl->mController.GetGL();
- if(depthTestEnable)
+ if(depthTestEnable != mImpl->mGlStateCache.mDepthBufferEnabled)
{
- gl.Enable(GL_DEPTH_TEST);
+ mImpl->mGlStateCache.mDepthBufferEnabled = depthTestEnable;
+
+ auto& gl = *mImpl->mController.GetGL();
+ if(depthTestEnable)
+ {
+ gl.Enable(GL_DEPTH_TEST);
+ }
+ else
+ {
+ gl.Disable(GL_DEPTH_TEST);
+ }
}
- else
+}
+
+void Context::SetDepthWriteEnable(bool depthWriteEnable)
+{
+ if(depthWriteEnable != mImpl->mGlStateCache.mDepthMaskEnabled)
{
- gl.Disable(GL_DEPTH_TEST);
+ mImpl->mGlStateCache.mDepthMaskEnabled = depthWriteEnable;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.DepthMask(depthWriteEnable);
}
}
-void Context::SetDepthWriteEnable(bool depthWriteEnable)
+void Context::ActiveTexture(uint32_t textureBindingIndex)
+{
+ if(mImpl->mGlStateCache.mActiveTextureUnit != textureBindingIndex)
+ {
+ mImpl->mGlStateCache.mActiveTextureUnit = textureBindingIndex;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.ActiveTexture(GL_TEXTURE0 + textureBindingIndex);
+ }
+}
+
+void Context::BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId)
+{
+ uint32_t typeId = static_cast<uint32_t>(textureTypeId);
+ if(mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] != textureId)
+ {
+ mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] = textureId;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.BindTexture(target, textureId);
+ }
+}
+
+void Context::GenerateMipmap(GLenum target)
+{
+ auto& gl = *mImpl->mController.GetGL();
+ gl.GenerateMipmap(target);
+}
+
+void Context::BindBuffer(GLenum target, uint32_t bufferId)
+{
+ if(mImpl->mGlStateCache.mBoundArrayBufferId != bufferId)
+ {
+ mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.BindBuffer(target, bufferId);
+ }
+}
+
+void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
+{
+ mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+ mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+ mImpl->mGlStateCache.StencilBufferWriteEnabled());
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.DrawBuffers(count, buffers);
+}
+
+void Context::BindFrameBuffer(GLenum target, uint32_t bufferId)
+{
+ mImpl->mGlStateCache.mFrameBufferStateCache.SetCurrentFrameBuffer(bufferId);
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.BindFramebuffer(target, bufferId);
+}
+
+void Context::GenFramebuffers(uint32_t count, uint32_t* framebuffers)
{
auto& gl = *mImpl->mController.GetGL();
- gl.DepthMask(depthWriteEnable);
+ gl.GenFramebuffers(count, framebuffers);
+
+ mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersCreated(count, framebuffers);
+}
+
+void Context::DeleteFramebuffers(uint32_t count, uint32_t* framebuffers)
+{
+ mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersDeleted(count, framebuffers);
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.DeleteFramebuffers(count, framebuffers);
+}
+
+GLStateCache& Context::GetGLStateCache()
+{
+ return mImpl->mGlStateCache;
+}
+
+void Context::GlContextCreated()
+{
+ if(!mImpl->mGlContextCreated)
+ {
+ mImpl->mGlContextCreated = true;
+
+ // Set the initial GL state
+ mImpl->InitializeGlState();
+ }
+}
+
+void Context::GlContextDestroyed()
+{
+ mImpl->mGlContextCreated = false;
+}
+
+void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
+{
+ // Since the pipeline is deleted, invalidate the cached pipeline.
+ if(mImpl->mCurrentPipeline == &pipeline->GetPipeline())
+ {
+ mImpl->mCurrentPipeline = nullptr;
+ }
}
} // namespace Dali::Graphics::GLES
*/
#include <dali/graphics-api/graphics-command-buffer.h>
+#include "gles-context-state-cache.h"
#include "gles-graphics-types.h"
namespace Dali::Graphics
class Pipeline;
class RenderPass;
class RenderTarget;
+class Texture;
+
/**
* @brief Context represents single GLES context
*/
*/
void EndRenderPass();
+ /**
+ * @brief Returns the cache of GL state in the context
+ * @return the reference of GL state cache (which can be modified)
+ */
+ GLStateCache& GetGLStateCache();
+
+ /**
+ * @brief Called when the GL context has been created.
+ */
+ void GlContextCreated();
+
+ /**
+ * @brief Called when the GL context has been destroyed.
+ */
+ void GlContextDestroyed();
+
+ /**
+ * @brief Invalidates the cached pipeline object in the context if it matches
+ * This is called before the pipeline is deleted
+ *
+ * @param[in] pipeline The pipeline
+ */
+ void InvalidateCachedPipeline(GLES::Pipeline* pipeline);
+
+ void ActiveTexture(uint32_t textureBindingIndex);
+ void BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId);
+ void GenerateMipmap(GLenum target);
+ void BindBuffer(GLenum target, uint32_t bufferId);
+ void DrawBuffers(uint32_t count, const GLenum* buffers);
+ void BindFrameBuffer(GLenum target, uint32_t bufferId);
+ void GenFramebuffers(uint32_t count, uint32_t* framebuffers);
+ void DeleteFramebuffers(uint32_t count, uint32_t* framebuffers);
void ColorMask(bool enabled);
void ClearStencilBuffer();
void ClearDepthBuffer();
+ void ClearBuffer(uint32_t mask, bool forceClear);
+ void SetScissorTestEnabled(bool scissorEnabled);
void SetStencilTestEnable(bool stencilEnable);
void StencilMask(uint32_t writeMask);
void StencilFunc(Graphics::CompareOp compareOp,
--- /dev/null
+/*
+ * Copyright (c) 2021 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-framebuffer-state-cache.h"
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/gl-defines.h>
+
+namespace Dali::Graphics
+{
+namespace GLES
+{
+namespace
+{
+const uint32_t INITIAL_FRAMEBUFFER_STATE = 0u;
+}
+
+FrameBufferStateCache::FrameBufferStateCache()
+{
+}
+
+FrameBufferStateCache::~FrameBufferStateCache() = default;
+
+GLbitfield FrameBufferStateCache::GetClearMask(GLbitfield mask, bool forceClear, bool scissorTestEnabled)
+{
+ if(scissorTestEnabled)
+ {
+ // don't do anything if scissor test is enabled, in the future we could
+ // potentially keep track of frame buffer size vs scissor test size to see if the entire
+ // buffer is cleared or not.
+ return mask;
+ }
+
+ FrameBufferState* state = GetFrameBufferState(mCurrentFrameBufferId);
+ if(!state)
+ {
+ DALI_LOG_ERROR("FrameBuffer not found %d \n", mCurrentFrameBufferId);
+ return mask;
+ }
+
+ // if we are forcing the clear operation, then just update the internal cached values
+ if(forceClear)
+ {
+ SetClearState(state, mask);
+ return mask;
+ }
+
+ // use the cached values
+ if(mask & GL_COLOR_BUFFER_BIT)
+ {
+ // check if color buffer is currently clean
+ if(state->mState & COLOR_BUFFER_CLEAN)
+ {
+ // remove clear color buffer flag from bitmask, no need to clear twice
+ mask &= ~GL_COLOR_BUFFER_BIT;
+ }
+ }
+ if(mask & GL_DEPTH_BUFFER_BIT)
+ {
+ // check if depth buffer is currently clean
+ if(state->mState & DEPTH_BUFFER_CLEAN)
+ {
+ // remove clear depth buffer flag from bitmask, no need to clear twice
+ mask &= ~GL_DEPTH_BUFFER_BIT;
+ }
+ }
+ if(mask & GL_STENCIL_BUFFER_BIT)
+ {
+ // check if stencil buffer is currently clean
+ if(state->mState & STENCIL_BUFFER_CLEAN)
+ {
+ // remove clear stencil buffer flag from bitmask, no need to clear twice
+
+ mask &= ~GL_STENCIL_BUFFER_BIT;
+ }
+ }
+
+ // set the clear state based, what's about to be cleared
+ SetClearState(state, mask);
+
+ return mask;
+}
+
+void FrameBufferStateCache::SetCurrentFrameBuffer(GLuint frameBufferId)
+{
+ mCurrentFrameBufferId = frameBufferId;
+}
+
+void FrameBufferStateCache::FrameBuffersDeleted(GLsizei count, const GLuint* const frameBuffers)
+{
+ for(GLsizei i = 0; i < count; ++i)
+ {
+ DeleteFrameBuffer(frameBuffers[i]);
+ }
+}
+void FrameBufferStateCache::FrameBuffersCreated(GLsizei count, const GLuint* const frameBuffers)
+{
+ for(GLsizei i = 0; i < count; ++i)
+ {
+ // check the frame buffer doesn't exist already
+ GLuint id = frameBuffers[i];
+
+ FrameBufferState* state = GetFrameBufferState(id);
+ if(state)
+ {
+ DALI_LOG_ERROR("FrameBuffer already exists%d \n", id);
+ // reset its state
+ state->mState = INITIAL_FRAMEBUFFER_STATE;
+ continue;
+ }
+
+ FrameBufferState newFrameBuffer(frameBuffers[i]);
+ mFrameBufferStates.PushBack(newFrameBuffer);
+ }
+}
+
+void FrameBufferStateCache::DrawOperation(bool colorBuffer, bool depthBuffer, bool stencilBuffer)
+{
+ FrameBufferState* state = GetFrameBufferState(mCurrentFrameBufferId);
+ if(!state)
+ {
+ // an error will have already been logged by the clear operation
+ return;
+ }
+
+ if(colorBuffer)
+ {
+ // un-set the clean bit
+ state->mState &= ~COLOR_BUFFER_CLEAN;
+ }
+ if(depthBuffer)
+ {
+ // un-set the clean bit
+ state->mState &= ~DEPTH_BUFFER_CLEAN;
+ }
+ if(stencilBuffer)
+ {
+ // un-set the clean bit
+ state->mState &= ~STENCIL_BUFFER_CLEAN;
+ }
+}
+
+void FrameBufferStateCache::Reset()
+{
+ mFrameBufferStates.Clear();
+
+ // create the default frame buffer
+ GLuint id = 0; // 0 == default frame buffer id
+ FrameBuffersCreated(1, &id);
+}
+
+void FrameBufferStateCache::SetClearState(FrameBufferState* state, GLbitfield mask)
+{
+ if(mask & GL_COLOR_BUFFER_BIT)
+ {
+ // set the color buffer to clean
+ state->mState |= COLOR_BUFFER_CLEAN;
+ }
+ if(mask & GL_DEPTH_BUFFER_BIT)
+ {
+ // set the depth buffer to clean
+ state->mState |= DEPTH_BUFFER_CLEAN;
+ }
+ if(mask & GL_STENCIL_BUFFER_BIT)
+ {
+ // set the stencil buffer to clean
+ state->mState |= STENCIL_BUFFER_CLEAN;
+ }
+}
+
+FrameBufferStateCache::FrameBufferState* FrameBufferStateCache::GetFrameBufferState(GLuint frameBufferId)
+{
+ for(FrameBufferStateVector::SizeType i = 0; i < mFrameBufferStates.Count(); ++i)
+ {
+ FrameBufferState& state = mFrameBufferStates[i];
+ if(state.mId == frameBufferId)
+ {
+ return &state;
+ }
+ }
+ return nullptr;
+}
+
+void FrameBufferStateCache::DeleteFrameBuffer(GLuint frameBufferId)
+{
+ FrameBufferStateVector::Iterator iter = mFrameBufferStates.Begin();
+ FrameBufferStateVector::Iterator endIter = mFrameBufferStates.End();
+
+ for(; iter != endIter; ++iter)
+ {
+ if((*iter).mId == frameBufferId)
+ {
+ mFrameBufferStates.Erase(iter);
+ return;
+ }
+ }
+ DALI_LOG_ERROR("FrameBuffer not found %d \n", frameBufferId);
+}
+
+} // namespace GLES
+
+} // namespace Dali::Graphics
--- /dev/null
+#ifndef DALI_GRAPHICS_GLES_CONTEXT_FRAMEBUFFER_STATE_CACHE_H
+#define DALI_GRAPHICS_GLES_CONTEXT_FRAMEBUFFER_STATE_CACHE_H
+
+/*
+ * Copyright (c) 2021 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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/gl-abstraction.h>
+#include <dali/public-api/common/dali-vector.h>
+
+namespace Dali::Graphics
+{
+namespace GLES
+{
+/**
+ * @brief Keeps track of color, depth and stencil buffer state within each frame buffer.
+ * Used to avoid redundant glClear calls.
+ *
+ */
+class FrameBufferStateCache
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ FrameBufferStateCache();
+
+ /**
+ * @brief non-virtual destructor
+ */
+ ~FrameBufferStateCache();
+
+ /**
+ * @brief Get the bitmask to pass to glClear based on the mask requested
+ * and the current state of the frame buffer
+ * @param[in] mask glClear bit mask
+ * @param[in] forceClear whether to force the clear ( ignore cached state)
+ * @param[in] scissorTestEnabled whether scissor test is enabled
+ * @return new bitmask to pass to glClear
+ */
+ GLbitfield GetClearMask(GLbitfield mask, bool forceClear, bool scissorTestEnabled);
+
+ /**
+ * @brief Set the current bound frame buffer
+ * @param[in] frameBufferId frame buffer id
+ */
+ void SetCurrentFrameBuffer(GLuint frameBufferId);
+
+ /**
+ * @brief Called when frame buffers are deleted
+ * @param[in] count number of frame buffers
+ * @param[in] framebuffers array of frame buffer ids
+ */
+ void FrameBuffersDeleted(GLsizei count, const GLuint* const frameBuffers);
+
+ /**
+ * @brief Called when frame buffers are created
+ * @param[in] count number of frame buffers
+ * @param[in] framebuffers array of frame buffer ids
+ */
+ void FrameBuffersCreated(GLsizei count, const GLuint* const frameBuffers);
+
+ /**
+ * @brief Draw operation performed on the current frame buffer
+ * @param[in] colorBufferUsed whether the color buffer is being written to (glColorMask )
+ * @param[in] depthBufferUsed whether the depth buffer is being written to (glDepthMask )
+ * @param[in] stencilBufferUsed whether the stencil buffer is being written to (glStencilMask )
+ */
+ void DrawOperation(bool colorBufferUsed, bool depthBufferUsed, bool stencilBufferUsed);
+
+ /**
+ * Reset the cache
+ */
+ void Reset();
+
+private:
+ /**
+ * Current status of the frame buffer
+ */
+ enum FrameBufferStatus
+ {
+ COLOR_BUFFER_CLEAN = 1 << 0, ///< color buffer clean
+ DEPTH_BUFFER_CLEAN = 1 << 1, ///< depth buffer clean
+ STENCIL_BUFFER_CLEAN = 1 << 2, ///< stencil buffer clean
+ };
+
+ /**
+ * POD to store the status of frame buffer regarding color,depth and stencil buffers
+ */
+ struct FrameBufferState
+ {
+ /**
+ * Constructor
+ */
+ FrameBufferState(GLuint id)
+ : mId(id),
+ mState(0)
+ {
+ }
+ GLuint mId; ///< Frame buffer id
+ unsigned int mState; ///< State, bitmask of FrameBufferStatus flags
+ };
+
+ using FrameBufferStateVector = Dali::Vector<FrameBufferState>;
+
+ /**
+ * @brief Set the clear state
+ * @param[in] pointer to frame buffer state object
+ * @param[in] mask clear mask
+ */
+ void SetClearState(FrameBufferState* state, GLbitfield mask);
+
+ /**
+ * @brief Helper
+ * @param[in] frameBufferId frame buffer id
+ * @return pointer to frame buffer state object ( NULL if it doesn't exist)
+ */
+ FrameBufferState* GetFrameBufferState(GLuint frameBufferId);
+
+ /**
+ * @brief Helper to delete a frame buffer state object
+ * @param[in] frameBufferId frame buffer id
+ */
+ void DeleteFrameBuffer(GLuint frameBufferId);
+
+ FrameBufferStateCache(const FrameBufferStateCache&); ///< undefined copy constructor
+
+ FrameBufferStateCache& operator=(const FrameBufferStateCache&); ///< undefined assignment operator
+
+private: // data
+ FrameBufferStateVector mFrameBufferStates{}; ///< state of the frame buffers
+ GLuint mCurrentFrameBufferId{0}; ///< currently bound frame buffer
+};
+
+} // namespace GLES
+
+} // namespace Dali::Graphics
+
+#endif // DALI_GRAPHICS_GLES_CONTEXT_FRAMEBUFFER_STATE_CACHE_H
void Buffer::InitializeGPUBuffer()
{
- auto gl = mController.GetGL();
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(!gl || !context)
+ {
+ return;
+ }
+
gl->GenBuffers(1, &mBufferId);
- gl->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
+ context->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
gl->BufferData(GL_ARRAY_BUFFER, mCreateInfo.size, nullptr, GL_STATIC_DRAW);
}
void Buffer::Bind(Graphics::BufferUsage bindingTarget) const
{
- auto gl = mController.GetGL();
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(!gl || !context)
+ {
+ return;
+ }
// CPU allocated buffer may be bound only as Uniform Buffer
// on special binding point
{
case Graphics::BufferUsage::VERTEX_BUFFER:
{
- gl->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
+ context->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
break;
}
case Graphics::BufferUsage::INDEX_BUFFER:
{
- gl->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferId);
+ context->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferId);
break;
}
default:
bool Framebuffer::InitializeResource()
{
- auto gl = mController.GetGL();
- if(gl && !mInitialized)
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(gl && context && !mInitialized)
{
mInitialized = true;
- gl->GenFramebuffers(1, &mFramebufferId);
- gl->BindFramebuffer(GL_FRAMEBUFFER, mFramebufferId);
+ context->GenFramebuffers(1, &mFramebufferId);
+ context->BindFrameBuffer(GL_FRAMEBUFFER, mFramebufferId);
for(Graphics::ColorAttachment& attachment : mCreateInfo.colorAttachments)
{
}
// @todo is this per framebuffer, or more immediate state that needs setting when framebuffer changed?
- gl->DrawBuffers(mCreateInfo.colorAttachments.size(), COLOR_ATTACHMENTS);
+ context->DrawBuffers(mCreateInfo.colorAttachments.size(), COLOR_ATTACHMENTS);
if(mCreateInfo.depthStencilAttachment.depthTexture)
{
AttachTexture(stencilTexture, attachmentId, 0, mCreateInfo.depthStencilAttachment.stencilLevel);
}
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ context->BindFrameBuffer(GL_FRAMEBUFFER, 0);
}
return mInitialized;
void Framebuffer::DestroyResource()
{
- auto gl = mController.GetGL();
- if(gl && mInitialized)
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(gl && context && mInitialized)
{
if(mDepthBufferId)
{
gl->DeleteRenderbuffers(1, &mStencilBufferId);
}
- gl->DeleteFramebuffers(1, &mFramebufferId);
+ context->DeleteFramebuffers(1, &mFramebufferId);
+
mFramebufferId = 0u;
mInitialized = false;
}
void Framebuffer::Bind() const
{
- auto gl = mController.GetGL();
- gl->BindFramebuffer(GL_FRAMEBUFFER, mFramebufferId);
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+
+ if(gl && context)
+ {
+ context->BindFrameBuffer(GL_FRAMEBUFFER, mFramebufferId);
+ }
}
void Framebuffer::AttachTexture(const Graphics::Texture* texture, uint32_t attachmentId, uint32_t layerId, uint32_t levelId)
return mController;
}
-void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline)
+void PipelineImpl::Bind(const uint32_t glProgram) const
{
- auto& gl = *GetController().GetGL();
- auto glProgram = static_cast<const GLES::Program*>(GetCreateInfo().programState->program)->GetImplementation()->GetGlProgram();
+ auto& gl = *GetController().GetGL();
gl.UseProgram(glProgram);
}
namespace Dali::Graphics::GLES
{
class PipelineCache;
+class Program;
/**
* @brief PipelineImpl is the implementation of Pipeline
*
* Binds Pipeline by binding GL program and flushing state.
*
- * If previous pipeline specified, it will be used in order to
- * avoid redundant state swiches.
- *
- * @param[in] prevPipeline previous pipeline
+ * @param[in] glProgram The GL program to be bound
*/
- void Bind(GLES::PipelineImpl* prevPipeline);
+ void Bind(const uint32_t glProgram) const;
/**
* @brief Increases ref count
bool Texture::InitializeNativeImage()
{
- auto gl = mController.GetGL();
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
GLuint texture{0};
- if(!gl)
+ if(!gl || !context)
{
// Do nothing during shutdown
return false;
if(created)
{
gl->GenTextures(1, &texture);
- gl->BindTexture(mGlTarget, texture);
+ context->BindTexture(mGlTarget, GetTextureTypeId(), texture);
gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
bool Texture::InitializeTexture()
{
- auto gl = mController.GetGL();
- if(!gl)
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(!gl || !context)
{
// Do nothing during shutdown
return false;
{
// Bind texture
gl->GenTextures(1, &texture);
- gl->BindTexture(GL_TEXTURE_2D, texture);
+ context->BindTexture(GL_TEXTURE_2D, GetTextureTypeId(), texture);
// Allocate memory for the texture
if(!mIsCompressed)
{
// Bind texture
gl->GenTextures(1, &texture);
- gl->BindTexture(GL_TEXTURE_CUBE_MAP, texture);
+ context->BindTexture(GL_TEXTURE_CUBE_MAP, GetTextureTypeId(), texture);
gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
gl->TexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, Graphics::GLES::GLSamplerFilterAndMipMapMode(Graphics::SamplerFilter::LINEAR, SamplerMipmapMode::NONE));
void Texture::Bind(const TextureBinding& binding) const
{
- auto gl = mController.GetGL();
- if(!gl)
+ auto context = mController.GetCurrentContext();
+ auto gl = mController.GetGL();
+ if(!gl || !context)
{
// Do nothing during shutdown
return;
}
- gl->ActiveTexture(GL_TEXTURE0 + binding.binding);
- gl->BindTexture(mGlTarget, mTextureId);
+ context->ActiveTexture(binding.binding);
+ context->BindTexture(mGlTarget, GetTextureTypeId(), mTextureId);
// For GLES2 if there is a sampler set in the binding
if(binding.sampler)
{
- // Non-default.
- auto* sampler = static_cast<const GLES::Sampler*>(binding.sampler);
- const auto& samplerCreateInfo = sampler->GetCreateInfo();
+ const auto& samplerCreateInfo = static_cast<const GLES::Sampler*>(binding.sampler)->GetCreateInfo();
auto mipMapMode = samplerCreateInfo.mipMapMode;
// INTERNAL INCLUDES
#include "gles-graphics-resource.h"
+#include "gles-graphics-types.h"
namespace Dali::Graphics::GLES
{
using TextureResource = Resource<Graphics::Texture, Graphics::TextureCreateInfo>;
+class Sampler;
/**
* The Texture class represents a GPU texture object. It's slightly
}
/**
+ * @brief Returns the type of the bound Gl texture
+ * @return The type of the bound Gl texture
+ */
+ [[nodiscard]] BoundTextureType GetTextureTypeId() const
+ {
+ return mCreateInfo.nativeImagePtr ? BoundTextureType::TEXTURE_EXTERNAL_OES : static_cast<BoundTextureType>(mCreateInfo.textureType);
+ }
+
+ /**
* @brief Called when initializing the resource
*
* @return True on success
};
/**
+ * @brief Types of bond texture cached in the context
+ */
+enum class BoundTextureType
+{
+ TEXTURE_2D,
+ TEXTURE_3D,
+ TEXTURE_CUBEMAP,
+ TEXTURE_EXTERNAL_OES
+};
+
+/**
* The descriptor of BeginRenderPass command
*/
struct BeginRenderPassDescriptor
void EglGraphics::ActivateResourceContext()
{
- mGraphicsController.ActivateResourceContext();
-
if(mEglImplementation && mEglImplementation->IsSurfacelessContextSupported())
{
// Make the shared surfaceless context as current before rendering
mEglImplementation->MakeContextCurrent(EGL_NO_SURFACE, mEglImplementation->GetContext());
}
+
+ mGraphicsController.ActivateResourceContext();
}
void EglGraphics::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface)
{
- mGraphicsController.ActivateSurfaceContext(surface);
-
if(surface)
{
surface->InitializeGraphics();
surface->MakeContextCurrent();
}
+
+ mGraphicsController.ActivateSurfaceContext(surface);
}
void EglGraphics::SetFirstFrameAfterResume()
{
// Create a surfaceless OpenGL context for shared resources
mEglImplementation->CreateContext();
- mEglImplementation->MakeContextCurrent(EGL_NO_SURFACE, mEglImplementation->GetContext());
+ ActivateResourceContext();
}
else
{
currentSurface = surface;
if(currentSurface)
{
- currentSurface->InitializeGraphics();
- currentSurface->MakeContextCurrent();
+ ActivateSurfaceContext(currentSurface);
}
}