From: Richard Huang Date: Tue, 25 May 2021 16:50:28 +0000 (+0100) Subject: Multi-level context caching X-Git-Tag: dali_2.0.29~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5a309b7db8eade971a38b0728cd4dc5cfdccaa8a;hp=f730545fafee7a4f65d966a9b28bb80fba9ba93a;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git Multi-level context caching Change-Id: I31f298c50e584df98f722cf01fd6eb6d4517d90b --- diff --git a/dali/internal/adaptor/common/combined-update-render-controller.cpp b/dali/internal/adaptor/common/combined-update-render-controller.cpp index a8a1401..e26e549 100644 --- a/dali/internal/adaptor/common/combined-update-render-controller.cpp +++ b/dali/internal/adaptor/common/combined-update-render-controller.cpp @@ -690,14 +690,14 @@ void CombinedUpdateRenderController::UpdateRenderThread() 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); diff --git a/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp b/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp index 772d4ef..545c257 100644 --- a/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp +++ b/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp @@ -288,6 +288,7 @@ void EglGraphicsController::DeleteSurfaceContext(Dali::RenderSurfaceInterface* s void EglGraphicsController::ActivateResourceContext() { mCurrentContext = mContext.get(); + mCurrentContext->GlContextCreated(); } void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface) @@ -301,6 +302,7 @@ void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface* if(iter != mSurfaceContexts.end()) { mCurrentContext = iter->second.get(); + mCurrentContext->GlContextCreated(); } } } @@ -335,7 +337,7 @@ void EglGraphicsController::ProcessDiscardQueues() ProcessDiscardQueue(mDiscardFramebufferQueue); // Process pipelines - ProcessDiscardQueue(mDiscardPipelineQueue); + ProcessDiscardQueue(mDiscardPipelineQueue); // Process programs ProcessDiscardQueue(mDiscardProgramQueue); @@ -428,14 +430,7 @@ void EglGraphicsController::ProcessCommandBuffer(const GLES::CommandBuffer& comm } 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? @@ -627,7 +622,7 @@ void EglGraphicsController::ProcessTextureUpdateQueue() } mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1); - mGlAbstraction->BindTexture(bindTarget, texture->GetGLTexture()); + mCurrentContext->BindTexture(bindTarget, texture->GetTextureTypeId(), texture->GetGLTexture()); if(!isSubImage) { @@ -751,8 +746,8 @@ void EglGraphicsController::ProcessTextureMipmapGenerationQueue() { 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(); } diff --git a/dali/internal/graphics/gles-impl/egl-graphics-controller.h b/dali/internal/graphics/gles-impl/egl-graphics-controller.h index 673e5a9..be883bf 100644 --- a/dali/internal/graphics/gles-impl/egl-graphics-controller.h +++ b/dali/internal/graphics/gles-impl/egl-graphics-controller.h @@ -135,6 +135,19 @@ public: { // Final flush Flush(); + + if(mContext) + { + mContext->GlContextDestroyed(); + } + + for(auto&& context : mSurfaceContexts) + { + if(context.second) + { + context.second->GlContextDestroyed(); + } + } } /** @@ -448,6 +461,12 @@ public: // 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(); @@ -483,9 +502,9 @@ public: } /** - * @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 void ProcessDiscardQueue(T& queue) @@ -516,6 +535,53 @@ public: } /** + * @brief Processes a discard queue for pipeline + * + * @param[in,out] queue Reference to the create queue + */ + void ProcessDiscardQueue(std::queue& queue) + { + while(!queue.empty()) + { + auto* object = const_cast(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(); @@ -580,6 +646,44 @@ public: 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 @@ -611,6 +715,16 @@ public: */ 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}; diff --git a/dali/internal/graphics/gles-impl/file.list b/dali/internal/graphics/gles-impl/file.list index 84481fd..1b0af65 100644 --- a/dali/internal/graphics/gles-impl/file.list +++ b/dali/internal/graphics/gles-impl/file.list @@ -21,4 +21,5 @@ SET( adaptor_graphics_gles_src_files ${adaptor_graphics_gles_src_files} ${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 ) diff --git a/dali/internal/graphics/gles-impl/gles-context-state-cache.h b/dali/internal/graphics/gles-impl/gles-context-state-cache.h new file mode 100644 index 0000000..07f1512 --- /dev/null +++ b/dali/internal/graphics/gles-impl/gles-context-state-cache.h @@ -0,0 +1,142 @@ +#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 +#include +#include + +// 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 diff --git a/dali/internal/graphics/gles-impl/gles-context.cpp b/dali/internal/graphics/gles-impl/gles-context.cpp index 974869a..fb32249 100644 --- a/dali/internal/graphics/gles-impl/gles-context.cpp +++ b/dali/internal/graphics/gles-impl/gles-context.cpp @@ -39,10 +39,110 @@ struct Context::Impl ~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 mCurrentTextureBindings{}; std::vector mCurrentSamplerBindings{}; @@ -64,6 +164,10 @@ struct Context::Impl // 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) @@ -77,29 +181,30 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) { auto& gl = *mImpl->mController.GetGL(); - // Change pipeline - if(mImpl->mNewPipeline) + // Execute states if pipeline is changed + const auto& currentProgram = mImpl->mCurrentPipeline ? static_cast(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr; + const auto& newProgram = static_cast(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(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) { @@ -115,6 +220,7 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) } 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) @@ -126,19 +232,21 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) } // 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, @@ -148,7 +256,7 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) } // Resolve topology - const auto& ia = mImpl->mCurrentPipeline->GetCreateInfo().inputAssemblyState; + const auto& ia = mImpl->mNewPipeline->GetCreateInfo().inputAssemblyState; // Bind uniforms @@ -157,6 +265,11 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) { 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); @@ -164,9 +277,14 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) } 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, @@ -181,6 +299,13 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) } ClearState(); + + // Change pipeline + if(mImpl->mNewPipeline) + { + mImpl->mCurrentPipeline = mImpl->mNewPipeline; + mImpl->mNewPipeline = nullptr; + } } void Context::BindTextures(const std::vector& bindings) @@ -217,7 +342,8 @@ void Context::BindIndexBuffer(const IndexBufferBindingDescriptor& indexBufferBin 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& uboBindings, @@ -245,68 +371,115 @@ void Context::BindUniformBuffers(const std::vectormCurrentPipeline->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()) } @@ -325,7 +498,7 @@ void Context::ResolveStandaloneUniforms() auto& gl = *mImpl->mController.GetGL(); // Find reflection for program - const auto program = static_cast(mImpl->mCurrentPipeline->GetCreateInfo().programState->program); + const auto program = static_cast(mImpl->mNewPipeline->GetCreateInfo().programState->program); const auto& reflection = program->GetReflection(); @@ -436,7 +609,7 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin) if(targetInfo.surface) { // Bind surface FB - gl.BindFramebuffer(GL_FRAMEBUFFER, 0); + BindFrameBuffer(GL_FRAMEBUFFER, 0); } else if(targetInfo.framebuffer) { @@ -457,13 +630,27 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin) { 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 @@ -472,20 +659,28 @@ void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin) 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; @@ -510,80 +705,257 @@ void Context::ClearState() 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(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 diff --git a/dali/internal/graphics/gles-impl/gles-context.h b/dali/internal/graphics/gles-impl/gles-context.h index c9df90f..2bba73f 100644 --- a/dali/internal/graphics/gles-impl/gles-context.h +++ b/dali/internal/graphics/gles-impl/gles-context.h @@ -19,6 +19,7 @@ */ #include +#include "gles-context-state-cache.h" #include "gles-graphics-types.h" namespace Dali::Graphics @@ -29,6 +30,8 @@ namespace GLES class Pipeline; class RenderPass; class RenderTarget; +class Texture; + /** * @brief Context represents single GLES context */ @@ -137,9 +140,43 @@ public: */ 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, diff --git a/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.cpp b/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.cpp new file mode 100644 index 0000000..2fbb32e --- /dev/null +++ b/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.cpp @@ -0,0 +1,218 @@ +/* + * 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 +#include + +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 diff --git a/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.h b/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.h new file mode 100644 index 0000000..02442b8 --- /dev/null +++ b/dali/internal/graphics/gles-impl/gles-framebuffer-state-cache.h @@ -0,0 +1,153 @@ +#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 +#include + +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; + + /** + * @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 diff --git a/dali/internal/graphics/gles-impl/gles-graphics-buffer.cpp b/dali/internal/graphics/gles-impl/gles-graphics-buffer.cpp index 3e4805a..b8dafab 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-buffer.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-buffer.cpp @@ -83,9 +83,15 @@ void Buffer::InitializeCPUBuffer() 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); } @@ -123,7 +129,12 @@ void Buffer::DiscardResource() 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 @@ -142,12 +153,12 @@ void Buffer::Bind(Graphics::BufferUsage bindingTarget) const { 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: diff --git a/dali/internal/graphics/gles-impl/gles-graphics-framebuffer.cpp b/dali/internal/graphics/gles-impl/gles-graphics-framebuffer.cpp index 2e52934..d909146 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-framebuffer.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-framebuffer.cpp @@ -92,13 +92,14 @@ Framebuffer::~Framebuffer() = 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) { @@ -106,7 +107,7 @@ bool Framebuffer::InitializeResource() } // @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) { @@ -135,7 +136,8 @@ bool Framebuffer::InitializeResource() AttachTexture(stencilTexture, attachmentId, 0, mCreateInfo.depthStencilAttachment.stencilLevel); } - gl->BindFramebuffer(GL_FRAMEBUFFER, 0); + + context->BindFrameBuffer(GL_FRAMEBUFFER, 0); } return mInitialized; @@ -143,8 +145,9 @@ bool Framebuffer::InitializeResource() void Framebuffer::DestroyResource() { - auto gl = mController.GetGL(); - if(gl && mInitialized) + auto context = mController.GetCurrentContext(); + auto gl = mController.GetGL(); + if(gl && context && mInitialized) { if(mDepthBufferId) { @@ -155,7 +158,8 @@ void Framebuffer::DestroyResource() gl->DeleteRenderbuffers(1, &mStencilBufferId); } - gl->DeleteFramebuffers(1, &mFramebufferId); + context->DeleteFramebuffers(1, &mFramebufferId); + mFramebufferId = 0u; mInitialized = false; } @@ -168,8 +172,13 @@ void Framebuffer::DiscardResource() 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) diff --git a/dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp b/dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp index 603039b..a39260e 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-pipeline.cpp @@ -90,10 +90,9 @@ auto& PipelineImpl::GetController() const return mController; } -void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline) +void PipelineImpl::Bind(const uint32_t glProgram) const { - auto& gl = *GetController().GetGL(); - auto glProgram = static_cast(GetCreateInfo().programState->program)->GetImplementation()->GetGlProgram(); + auto& gl = *GetController().GetGL(); gl.UseProgram(glProgram); } diff --git a/dali/internal/graphics/gles-impl/gles-graphics-pipeline.h b/dali/internal/graphics/gles-impl/gles-graphics-pipeline.h index fca74fa..57c490e 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-pipeline.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-pipeline.h @@ -30,6 +30,7 @@ namespace Dali::Graphics::GLES { class PipelineCache; +class Program; /** * @brief PipelineImpl is the implementation of Pipeline @@ -60,12 +61,9 @@ public: * * 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 diff --git a/dali/internal/graphics/gles-impl/gles-graphics-texture.cpp b/dali/internal/graphics/gles-impl/gles-graphics-texture.cpp index a2c9617..672e56f 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-texture.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-texture.cpp @@ -113,10 +113,11 @@ bool Texture::InitializeResource() 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; @@ -128,7 +129,7 @@ bool Texture::InitializeNativeImage() 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 @@ -161,8 +162,9 @@ bool Texture::InitializeNativeImage() 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; @@ -185,7 +187,7 @@ bool Texture::InitializeTexture() { // 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) @@ -233,7 +235,7 @@ bool Texture::InitializeTexture() { // 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)); @@ -312,22 +314,21 @@ void Texture::DiscardResource() 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(binding.sampler); - const auto& samplerCreateInfo = sampler->GetCreateInfo(); + const auto& samplerCreateInfo = static_cast(binding.sampler)->GetCreateInfo(); auto mipMapMode = samplerCreateInfo.mipMapMode; diff --git a/dali/internal/graphics/gles-impl/gles-graphics-texture.h b/dali/internal/graphics/gles-impl/gles-graphics-texture.h index dc7d921..19619e4 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-texture.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-texture.h @@ -26,10 +26,12 @@ // INTERNAL INCLUDES #include "gles-graphics-resource.h" +#include "gles-graphics-types.h" namespace Dali::Graphics::GLES { using TextureResource = Resource; +class Sampler; /** * The Texture class represents a GPU texture object. It's slightly @@ -65,6 +67,15 @@ public: } /** + * @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(mCreateInfo.textureType); + } + + /** * @brief Called when initializing the resource * * @return True on success diff --git a/dali/internal/graphics/gles-impl/gles-graphics-types.h b/dali/internal/graphics/gles-impl/gles-graphics-types.h index 35893c8..e90f2bc 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-types.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-types.h @@ -2018,6 +2018,17 @@ enum class GLESVersion }; /** + * @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 diff --git a/dali/internal/graphics/gles/egl-graphics.cpp b/dali/internal/graphics/gles/egl-graphics.cpp index d19f824..3f00195 100644 --- a/dali/internal/graphics/gles/egl-graphics.cpp +++ b/dali/internal/graphics/gles/egl-graphics.cpp @@ -73,24 +73,24 @@ void EglGraphics::SetIsSurfacelessContextSupported(const bool isSupported) 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() @@ -153,15 +153,14 @@ void EglGraphics::ConfigureSurface(Dali::RenderSurfaceInterface* surface) { // 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); } }