X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Fgraphics%2Fgles-impl%2Fgles-context.cpp;h=f2962a07812006327aa0ed87349f3406cdbb7580;hb=ffee1abdac31e9756f02edfbded64f6d9b3d30b1;hp=152271979509f67219a6d1ebc075d032d11665e2;hpb=c645c37b2a83b769b6f488732ed9ceb6d9e858c2;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/graphics/gles-impl/gles-context.cpp b/dali/internal/graphics/gles-impl/gles-context.cpp index 1522719..f2962a0 100644 --- a/dali/internal/graphics/gles-impl/gles-context.cpp +++ b/dali/internal/graphics/gles-impl/gles-context.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,27 +16,172 @@ */ #include "gles-context.h" +#include #include #include +#include +#include + +#include "egl-graphics-controller.h" #include "gles-graphics-buffer.h" -#include "gles-graphics-command-buffer.h" #include "gles-graphics-pipeline.h" +#include "gles-graphics-program.h" +#include "gles-graphics-render-pass.h" +#include "gles-graphics-render-target.h" +#include "gles-texture-dependency-checker.h" + +#include +#include +#include namespace Dali::Graphics::GLES { struct Context::Impl { - Impl(EglGraphicsController& controller) + explicit Impl(EglGraphicsController& controller) : mController(controller) { } ~Impl() = default; + /** + * Binds (and creates) VAO + * + * VAO is fixed per program so it has to be created only once assuming + * that VertexInputState has been set correctly for the pipeline. + * + */ + void BindProgramVAO(GLES::ProgramImpl* program, const VertexInputState& vertexInputState) + { + auto& gl = *mController.GetGL(); + auto iter = mProgramVAOMap.find(program); + if(iter != mProgramVAOMap.end()) + { + if(mProgramVAOCurrentState != iter->second) + { + mProgramVAOCurrentState = iter->second; + gl.BindVertexArray(iter->second); + } + return; + } + + uint32_t vao; + gl.GenVertexArrays(1, &vao); + gl.BindVertexArray(vao); + mProgramVAOMap[program] = vao; + for(const auto& attr : vertexInputState.attributes) + { + gl.EnableVertexAttribArray(attr.location); + } + + mProgramVAOCurrentState = vao; + } + + /** + * 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{}; @@ -50,6 +195,27 @@ struct Context::Impl // Currently bound buffers std::vector mCurrentVertexBufferBindings{}; + + // Currently bound UBOs (check if it's needed per program!) + std::vector mCurrentUBOBindings{}; + UniformBufferBindingDescriptor mCurrentStandaloneUBOBinding{}; + + // Current render pass and render target + const GLES::RenderTarget* mCurrentRenderTarget{nullptr}; + const GLES::RenderPass* mCurrentRenderPass{nullptr}; + + // Each context must have own VAOs as they cannot be shared + std::map mProgramVAOMap; ///< GL program-VAO map + uint32_t mProgramVAOCurrentState{0u}; ///< Currently bound VAO + GLStateCache mGlStateCache{}; ///< GL status cache + + bool mGlContextCreated{false}; ///< True if the OpenGL context has been created + + EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context + + EGLSurface mCacheDrawReadSurface{0u}; ///< cached 'read' surface + EGLSurface mCacheDrawWriteSurface{0u}; ///< cached 'write' surface + EGLContext mCacheEGLGraphicsContext{0u}; ///< cached window context }; Context::Context(EglGraphicsController& controller) @@ -57,43 +223,115 @@ Context::Context(EglGraphicsController& controller) mImpl = std::make_unique(controller); } -Context::~Context() = default; +Context::~Context() +{ + // Destroy native rendering context if one exists + if(mImpl->mNativeDrawContext) + { + eglDestroyContext(eglGetCurrentDisplay(), mImpl->mNativeDrawContext); + mImpl->mNativeDrawContext = EGL_NO_CONTEXT; + } +} -void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) +void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker) { auto& gl = *mImpl->mController.GetGL(); - // Change pipeline + static const bool hasGLES3(mImpl->mController.GetGLESVersion() >= GLESVersion::GLES_30); + + // early out if neither current nor new pipelines are set + // this behaviour may be valid so no assert + if(!mImpl->mCurrentPipeline && !mImpl->mNewPipeline) + { + return; + } + + // Execute states if pipeline is changed + const auto currentProgram = mImpl->mCurrentPipeline ? static_cast(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr; + + // case when new pipeline has been set + const GLES::Program* newProgram = nullptr; + if(mImpl->mNewPipeline) { - // Execute states if different - mImpl->mCurrentPipeline = mImpl->mNewPipeline; - mImpl->mNewPipeline = nullptr; + newProgram = static_cast(mImpl->mNewPipeline->GetCreateInfo().programState->program); } + if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline) + { + if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram()) + { + mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram()); + } + + // Blend state + ResolveBlendState(); + + // Resolve rasterization state + ResolveRasterizationState(); + } + + // Resolve uniform buffers + ResolveUniformBuffers(); + // Bind textures + // Map binding# to sampler location + const auto& reflection = !newProgram ? currentProgram->GetReflection() : newProgram->GetReflection(); + const auto& samplers = reflection.GetSamplers(); for(const auto& binding : mImpl->mCurrentTextureBindings) { - gl.ActiveTexture(GL_TEXTURE0 + binding.binding); - gl.BindTexture(GL_TEXTURE_2D, - static_cast(binding.texture) - ->GetGLTexture()); + auto texture = const_cast(static_cast(binding.texture)); + + // Texture may not have been initialized yet...(tbm_surface timing issue?) + if(!texture->GetGLTexture()) + { + // Attempt to reinitialize + // @todo need to put this somewhere else where it isn't const. + // Maybe post it bac/k on end of initialize queue if initialization fails? + texture->InitializeResource(); + } + + // Warning, this may cause glWaitSync to occur on the GPU. + dependencyChecker.CheckNeedsSync(this, texture); + + texture->Bind(binding); + + texture->Prepare(); // @todo also non-const. + + if(binding.binding < samplers.size()) // binding maps to texture unit. (texture bindings should also be in binding order) + { + // Offset is set to the lexical offset within the frag shader, map it to the texture unit + // @todo Explicitly set the texture unit through the graphics interface + gl.Uniform1i(samplers[binding.binding].location, samplers[binding.binding].offset); + } } // for each attribute bind vertices - const auto& pipelineState = mImpl->mCurrentPipeline->GetCreateInfo(); - const auto& vi = pipelineState.vertexInputState; - for(const auto& attr : vi->attributes) + + const auto& pipelineState = mImpl->mNewPipeline ? mImpl->mNewPipeline->GetCreateInfo() : mImpl->mCurrentPipeline->GetCreateInfo(); + const auto& vertexInputState = pipelineState.vertexInputState; + + if(hasGLES3) + { + mImpl->BindProgramVAO(static_cast(pipelineState.programState->program)->GetImplementation(), *vertexInputState); + } + + for(const auto& attr : vertexInputState->attributes) { // Enable location - gl.EnableVertexAttribArray(attr.location); + if(!hasGLES3) + { + mImpl->SetVertexAttributeLocation(attr.location, true); + } + const auto& bufferSlot = mImpl->mCurrentVertexBufferBindings[attr.binding]; - const auto& bufferBinding = vi->bufferBindings[attr.binding]; + const auto& bufferBinding = vertexInputState->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, @@ -103,13 +341,25 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) } // Resolve topology - const auto& ia = mImpl->mCurrentPipeline->GetCreateInfo().inputAssemblyState; - // Resolve drawcall + const auto& ia = pipelineState.inputAssemblyState; + + // Bind uniforms + // Resolve draw call switch(drawCall.type) { case DrawCallDescriptor::Type::DRAW: { + mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask, + mImpl->mGlStateCache.DepthBufferWriteEnabled(), + mImpl->mGlStateCache.StencilBufferWriteEnabled()); + // For GLES3+ we use VAO, for GLES2 internal cache + if(!hasGLES3) + { + mImpl->FlushVertexAttributeLocations(); + } + + //@todo Wait if textures need syncing gl.DrawArrays(GLESTopology(ia->topology), drawCall.draw.firstVertex, drawCall.draw.vertexCount); @@ -117,9 +367,19 @@ 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()); + + // For GLES3+ we use VAO, for GLES2 internal cache + if(!hasGLES3) + { + mImpl->FlushVertexAttributeLocations(); + } + auto indexBufferFormat = GLIndexFormat(binding.format).format; gl.DrawElements(GLESTopology(ia->topology), drawCall.drawIndexed.indexCount, @@ -132,13 +392,24 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall) break; } } + + ClearState(); + + // Change pipeline + if(mImpl->mNewPipeline) + { + mImpl->mCurrentPipeline = mImpl->mNewPipeline; + mImpl->mNewPipeline = nullptr; + } } -void Context::BindTextures(const std::vector& bindings) +void Context::BindTextures(const Graphics::TextureBinding* bindings, uint32_t count) { // for each texture allocate slot - for(const auto& binding : bindings) + for(auto i = 0u; i < count; ++i) { + auto& binding = bindings[i]; + // Resize binding array if needed if(mImpl->mCurrentTextureBindings.size() <= binding.binding) { @@ -149,14 +420,14 @@ void Context::BindTextures(const std::vector& bindings } } -void Context::BindVertexBuffers(const std::vector& bindings) +void Context::BindVertexBuffers(const GLES::VertexBufferBindingDescriptor* bindings, uint32_t count) { - if(bindings.size() > mImpl->mCurrentVertexBufferBindings.size()) + if(count > mImpl->mCurrentVertexBufferBindings.size()) { - mImpl->mCurrentVertexBufferBindings.resize(bindings.size()); + mImpl->mCurrentVertexBufferBindings.resize(count); } // Copy only set slots - std::copy_if(bindings.begin(), bindings.end(), mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) { + std::copy_if(bindings, bindings + count, mImpl->mCurrentVertexBufferBindings.begin(), [](auto& item) { return (nullptr != item.buffer); }); } @@ -168,7 +439,620 @@ 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 UniformBufferBindingDescriptor* uboBindings, + uint32_t uboCount, + const UniformBufferBindingDescriptor& standaloneBindings) +{ + if(standaloneBindings.buffer) + { + mImpl->mCurrentStandaloneUBOBinding = standaloneBindings; + } + + if(uboCount >= mImpl->mCurrentUBOBindings.size()) + { + mImpl->mCurrentUBOBindings.resize(uboCount + 1); + } + + auto it = uboBindings; + for(auto i = 0u; i < uboCount; ++i) + { + if(it->buffer) + { + mImpl->mCurrentUBOBindings[i] = *it; + } + } +} + +void Context::ResolveBlendState() +{ + const auto& currentBlendState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().colorBlendState : nullptr; + const auto& newBlendState = mImpl->mNewPipeline->GetCreateInfo().colorBlendState; + + // TODO: prevent leaking the state + if(!newBlendState) + { + return; + } + + auto& gl = *mImpl->mController.GetGL(); + + if(!currentBlendState || currentBlendState->blendEnable != newBlendState->blendEnable) + { + if(newBlendState->blendEnable != mImpl->mGlStateCache.mBlendEnabled) + { + mImpl->mGlStateCache.mBlendEnabled = newBlendState->blendEnable; + newBlendState->blendEnable ? gl.Enable(GL_BLEND) : gl.Disable(GL_BLEND); + } + } + + if(!newBlendState->blendEnable) + { + return; + } + + 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) + { + 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)); + } + } + } + + if(!currentBlendState || + currentBlendState->colorBlendOp != newBlendState->colorBlendOp || + currentBlendState->alphaBlendOp != newBlendState->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)); + if(newBlendState->colorBlendOp >= Graphics::ADVANCED_BLEND_OPTIONS_START) + { + gl.BlendBarrier(); + } + } + else + { + gl.BlendEquationSeparate(GLBlendOp(newBlendState->colorBlendOp), GLBlendOp(newBlendState->alphaBlendOp)); + } + } + } +} + +void Context::ResolveRasterizationState() +{ + const auto& currentRasterizationState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().rasterizationState : nullptr; + const auto& newRasterizationState = mImpl->mNewPipeline->GetCreateInfo().rasterizationState; + + // TODO: prevent leaking the state + if(!newRasterizationState) + { + return; + } + + auto& gl = *mImpl->mController.GetGL(); + + if(!currentRasterizationState || + currentRasterizationState->cullMode != newRasterizationState->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()) +} + +void Context::ResolveUniformBuffers() +{ + // Resolve standalone uniforms if we have binding + if(mImpl->mCurrentStandaloneUBOBinding.buffer) + { + ResolveStandaloneUniforms(); + } +} + +void Context::ResolveStandaloneUniforms() +{ + // Find reflection for program + const GLES::Program* program{nullptr}; + + if(mImpl->mNewPipeline) + { + program = static_cast(mImpl->mNewPipeline->GetCreateInfo().programState->program); + } + else if(mImpl->mCurrentPipeline) + { + program = static_cast(mImpl->mCurrentPipeline->GetCreateInfo().programState->program); + } + + if(program) + { + const auto ptr = reinterpret_cast(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset; + // Update program uniforms + program->GetImplementation()->UpdateStandaloneUniformBlock(ptr); + } +} + +void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin) +{ + auto& renderPass = *renderPassBegin.renderPass; + auto& renderTarget = *renderPassBegin.renderTarget; + + const auto& targetInfo = renderTarget.GetCreateInfo(); + + auto& gl = *mImpl->mController.GetGL(); + + if(targetInfo.surface) + { + // Bind surface FB + BindFrameBuffer(GL_FRAMEBUFFER, 0); + } + else if(targetInfo.framebuffer) + { + // bind framebuffer and swap. + auto framebuffer = renderTarget.GetFramebuffer(); + framebuffer->Bind(); + } + + // clear (ideally cache the setup) + + // In GL we assume that the last attachment is depth/stencil (we may need + // to cache extra information inside GLES RenderTarget if we want to be + // more specific in case of MRT) + + const auto& attachments = *renderPass.GetCreateInfo().attachments; + const auto& color0 = attachments[0]; + GLuint mask = 0; + + if(color0.loadOp == AttachmentLoadOp::CLEAR) + { + mask |= GL_COLOR_BUFFER_BIT; + + // Set clear color + // Something goes wrong here if Alpha mask is GL_TRUE + ColorMask(true); + + const auto clearValues = renderPassBegin.clearValues.Ptr(); + + if(!Dali::Equals(mImpl->mGlStateCache.mClearColor.r, clearValues[0].color.r) || + !Dali::Equals(mImpl->mGlStateCache.mClearColor.g, clearValues[0].color.g) || + !Dali::Equals(mImpl->mGlStateCache.mClearColor.b, clearValues[0].color.b) || + !Dali::Equals(mImpl->mGlStateCache.mClearColor.a, clearValues[0].color.a) || + !mImpl->mGlStateCache.mClearColorSet) + { + gl.ClearColor(clearValues[0].color.r, + clearValues[0].color.g, + clearValues[0].color.b, + clearValues[0].color.a); + + mImpl->mGlStateCache.mClearColorSet = true; + mImpl->mGlStateCache.mClearColor = Vector4(clearValues[0].color.r, + clearValues[0].color.g, + clearValues[0].color.b, + clearValues[0].color.a); + } + } + + // check for depth stencil + if(attachments.size() > 1) + { + const auto& depthStencil = attachments.back(); + if(depthStencil.loadOp == AttachmentLoadOp::CLEAR) + { + if(!mImpl->mGlStateCache.mDepthMaskEnabled) + { + mImpl->mGlStateCache.mDepthMaskEnabled = true; + gl.DepthMask(true); + } + mask |= GL_DEPTH_BUFFER_BIT; + } + if(depthStencil.stencilLoadOp == AttachmentLoadOp::CLEAR) + { + if(mImpl->mGlStateCache.mStencilMask != 0xFF) + { + mImpl->mGlStateCache.mStencilMask = 0xFF; + gl.StencilMask(0xFF); + } + mask |= GL_STENCIL_BUFFER_BIT; + } + } + + SetScissorTestEnabled(true); + gl.Scissor(renderPassBegin.renderArea.x, renderPassBegin.renderArea.y, renderPassBegin.renderArea.width, renderPassBegin.renderArea.height); + ClearBuffer(mask, true); + SetScissorTestEnabled(false); + + mImpl->mCurrentRenderPass = &renderPass; + mImpl->mCurrentRenderTarget = &renderTarget; +} + +void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker) +{ + if(mImpl->mCurrentRenderTarget) + { + GLES::Framebuffer* framebuffer = mImpl->mCurrentRenderTarget->GetFramebuffer(); + if(framebuffer) + { + auto& gl = *mImpl->mController.GetGL(); + gl.Flush(); + + /* @todo Full dependency checking would need to store textures in Begin, and create + * fence objects here; but we're going to draw all fbos on shared context in serial, + * so no real need (yet). Might want to consider ensuring order of render passes, + * but that needs doing in the controller, and would need doing before ProcessCommandQueues. + * + * Currently up to the client to create render tasks in the right order. + */ + + /* Create fence sync objects. Other contexts can then wait on these fences before reading + * textures. + */ + dependencyChecker.AddTextures(this, framebuffer); + } + } +} + +void Context::ClearState() +{ + mImpl->mCurrentTextureBindings.clear(); +} + +void Context::ColorMask(bool enabled) +{ + if(enabled != mImpl->mGlStateCache.mColorMask) + { + mImpl->mGlStateCache.mColorMask = enabled; + + auto& gl = *mImpl->mController.GetGL(); + gl.ColorMask(enabled, enabled, enabled, enabled); + } +} + +void Context::ClearStencilBuffer() +{ + ClearBuffer(GL_STENCIL_BUFFER_BIT, false); +} + +void Context::ClearDepthBuffer() +{ + ClearBuffer(GL_DEPTH_BUFFER_BIT, false); +} + +void Context::ClearBuffer(uint32_t mask, bool forceClear) +{ + mask = mImpl->mGlStateCache.mFrameBufferStateCache.GetClearMask(mask, forceClear, mImpl->mGlStateCache.mScissorTestEnabled); + if(mask > 0) + { + auto& gl = *mImpl->mController.GetGL(); + gl.Clear(mask); + } +} + +void Context::InvalidateDepthStencilBuffers() +{ + auto& gl = *mImpl->mController.GetGL(); + + GLenum attachments[] = {GL_DEPTH, GL_STENCIL}; + gl.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +} + +void Context::SetScissorTestEnabled(bool scissorEnabled) +{ + if(mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled) + { + 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) +{ + 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) +{ + 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) +{ + 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) +{ + if(compareOp != mImpl->mGlStateCache.mDepthFunction) + { + mImpl->mGlStateCache.mDepthFunction = compareOp; + auto& gl = *mImpl->mController.GetGL(); + gl.DepthFunc(GLCompareOp(compareOp).op); + } +} + +void Context::SetDepthTestEnable(bool depthTestEnable) +{ + if(depthTestEnable != mImpl->mGlStateCache.mDepthBufferEnabled) + { + mImpl->mGlStateCache.mDepthBufferEnabled = depthTestEnable; + + auto& gl = *mImpl->mController.GetGL(); + if(depthTestEnable) + { + gl.Enable(GL_DEPTH_TEST); + } + else + { + gl.Disable(GL_DEPTH_TEST); + } + } +} + +void Context::SetDepthWriteEnable(bool depthWriteEnable) +{ + if(depthWriteEnable != mImpl->mGlStateCache.mDepthMaskEnabled) + { + mImpl->mGlStateCache.mDepthMaskEnabled = depthWriteEnable; + + auto& gl = *mImpl->mController.GetGL(); + gl.DepthMask(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.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; + } +} + +void Context::PrepareForNativeRendering() +{ + // this should be pretty much constant + auto display = eglGetCurrentDisplay(); + auto drawSurface = eglGetCurrentSurface(EGL_DRAW); + auto readSurface = eglGetCurrentSurface(EGL_READ); + auto context = eglGetCurrentContext(); + + // push the surface and context data to the impl + // It's needed to restore context + if(!mImpl->mCacheEGLGraphicsContext) + { + mImpl->mCacheDrawWriteSurface = drawSurface; + mImpl->mCacheDrawReadSurface = readSurface; + mImpl->mCacheEGLGraphicsContext = context; + } + + if(!mImpl->mNativeDrawContext) + { + EGLint configId{0u}; + EGLint size{0u}; + eglGetConfigs(display, nullptr, 0, &size); + std::vector configs; + configs.resize(size); + eglGetConfigs(display, configs.data(), configs.size(), &size); + + eglQueryContext(display, context, EGL_CONFIG_ID, &configId); + + auto version = int(mImpl->mController.GetGLESVersion()); + + std::vector attribs; + attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR); + attribs.push_back(version / 10); + attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR); + attribs.push_back(version % 10); + attribs.push_back(EGL_NONE); + + mImpl->mNativeDrawContext = eglCreateContext(display, configs[configId], mImpl->mController.GetSharedContext(), attribs.data()); + } + + eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext); +} + +void Context::RestoreFromNativeRendering() +{ + auto display = eglGetCurrentDisplay(); + + // bring back original context + eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext); } } // namespace Dali::Graphics::GLES