+ 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<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
+ }
+ else if(mImpl->mCurrentPipeline)
+ {
+ program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
+ }
+
+ if(program)
+ {
+ const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
+ // Update program uniforms
+ program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
+ }
+}
+
+void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
+{
+ 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<uint32_t>(textureTypeId);
+ if(mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] != textureId)
+ {
+ mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] = textureId;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.BindTexture(target, textureId);
+ }
+}
+
+void Context::GenerateMipmap(GLenum target)
+{
+ auto& gl = *mImpl->mController.GetGL();
+ gl.GenerateMipmap(target);
+}
+
+void Context::BindBuffer(GLenum target, uint32_t bufferId)
+{
+ if(mImpl->mGlStateCache.mBoundArrayBufferId != bufferId)
+ {
+ mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.BindBuffer(target, bufferId);
+ }
+}
+
+void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
+{
+ mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
+ mImpl->mGlStateCache.DepthBufferWriteEnabled(),
+ mImpl->mGlStateCache.StencilBufferWriteEnabled());
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.DrawBuffers(count, buffers);
+}
+
+void Context::BindFrameBuffer(GLenum target, uint32_t bufferId)
+{
+ mImpl->mGlStateCache.mFrameBufferStateCache.SetCurrentFrameBuffer(bufferId);
+
+ auto& gl = *mImpl->mController.GetGL();
+ gl.BindFramebuffer(target, bufferId);
+}
+
+void Context::GenFramebuffers(uint32_t count, uint32_t* framebuffers)
+{
+ auto& gl = *mImpl->mController.GetGL();
+ gl.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<EGLConfig> 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<EGLint> attribs;
+ attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
+ attribs.push_back(version / 10);
+ attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
+ attribs.push_back(version % 10);
+ attribs.push_back(EGL_NONE);
+
+ mImpl->mNativeDrawContext = eglCreateContext(display, 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);