2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "gles-context.h"
19 #include <dali/integration-api/adaptor-framework/render-surface-interface.h>
20 #include <dali/integration-api/debug.h>
21 #include <dali/integration-api/gl-abstraction.h>
22 #include <dali/integration-api/gl-defines.h>
23 #include <dali/internal/graphics/common/graphics-interface.h>
24 #include <dali/public-api/math/math-utils.h>
26 #include "egl-graphics-controller.h"
27 #include "gles-graphics-buffer.h"
28 #include "gles-graphics-pipeline.h"
29 #include "gles-graphics-program.h"
30 #include "gles-graphics-render-pass.h"
31 #include "gles-graphics-render-target.h"
32 #include "gles-texture-dependency-checker.h"
35 #include <EGL/eglext.h>
37 #include <unordered_map>
39 namespace Dali::Graphics::GLES
43 explicit Impl(EglGraphicsController& controller, Integration::GlAbstraction* gl)
44 : mController(controller),
52 * Binds (and creates) VAO
54 * VAO is fixed per program so it has to be created only once assuming
55 * that VertexInputState has been set correctly for the pipeline.
58 void BindProgramVAO(const GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
60 // Calculate attributes location hash unordered.
62 for(const auto& attr : vertexInputState.attributes)
64 // Make unordered hash value by location.
65 // Note : This hash function varified for locations only under < 20.
66 std::size_t salt = attr.location + 1;
67 hash += salt << (sizeof(std::size_t) * 6);
69 salt ^= attr.location;
70 hash += salt << (sizeof(std::size_t) * 4);
76 if(!gl) // early out if no gl
81 if(DALI_UNLIKELY(!mDiscardedVAOList.empty()))
83 gl->DeleteVertexArrays(static_cast<Dali::GLsizei>(mDiscardedVAOList.size()), mDiscardedVAOList.data());
84 mDiscardedVAOList.clear();
87 auto iter = mProgramVAOMap.find(program);
88 if(iter != mProgramVAOMap.end())
90 auto attributeIter = iter->second.find(hash);
91 if(attributeIter != iter->second.end())
93 if(mProgramVAOCurrentState != attributeIter->second)
95 mProgramVAOCurrentState = attributeIter->second;
96 gl->BindVertexArray(attributeIter->second);
98 // Binding VAO seems to reset the index buffer binding so the cache must be reset
99 mGlStateCache.mBoundElementArrayBufferId = 0;
106 gl->GenVertexArrays(1, &vao);
107 gl->BindVertexArray(vao);
109 // Binding VAO seems to reset the index buffer binding so the cache must be reset
110 mGlStateCache.mBoundElementArrayBufferId = 0;
112 mProgramVAOMap[program][hash] = vao;
113 for(const auto& attr : vertexInputState.attributes)
115 gl->EnableVertexAttribArray(attr.location);
118 mProgramVAOCurrentState = vao;
122 * Sets the initial GL state.
124 void InitializeGlState()
129 mGlStateCache.mClearColorSet = false;
130 mGlStateCache.mColorMask = true;
131 mGlStateCache.mStencilMask = 0xFF;
132 mGlStateCache.mBlendEnabled = false;
133 mGlStateCache.mDepthBufferEnabled = false;
134 mGlStateCache.mDepthMaskEnabled = false;
135 mGlStateCache.mScissorTestEnabled = false;
136 mGlStateCache.mStencilBufferEnabled = false;
138 gl->Disable(GL_DITHER);
140 mGlStateCache.mBoundArrayBufferId = 0;
141 mGlStateCache.mBoundElementArrayBufferId = 0;
142 mGlStateCache.mActiveTextureUnit = 0;
144 mGlStateCache.mBlendFuncSeparateSrcRGB = BlendFactor::ONE;
145 mGlStateCache.mBlendFuncSeparateDstRGB = BlendFactor::ZERO;
146 mGlStateCache.mBlendFuncSeparateSrcAlpha = BlendFactor::ONE;
147 mGlStateCache.mBlendFuncSeparateDstAlpha = BlendFactor::ZERO;
149 // initial state is GL_FUNC_ADD for both RGB and Alpha blend modes
150 mGlStateCache.mBlendEquationSeparateModeRGB = BlendOp::ADD;
151 mGlStateCache.mBlendEquationSeparateModeAlpha = BlendOp::ADD;
153 mGlStateCache.mCullFaceMode = CullMode::NONE; // By default cullface is disabled, front face is set to CCW and cull face is set to back
155 // Initialze vertex attribute cache
156 memset(&mGlStateCache.mVertexAttributeCachedState, 0, sizeof(mGlStateCache.mVertexAttributeCachedState));
157 memset(&mGlStateCache.mVertexAttributeCurrentState, 0, sizeof(mGlStateCache.mVertexAttributeCurrentState));
159 // Initialize bound 2d texture cache
160 memset(&mGlStateCache.mBoundTextureId, 0, sizeof(mGlStateCache.mBoundTextureId));
162 mGlStateCache.mFrameBufferStateCache.Reset();
165 gl->GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextures);
166 DALI_LOG_RELEASE_INFO("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d\n", maxTextures);
171 * Flushes vertex attribute location changes to the driver
173 void FlushVertexAttributeLocations()
178 for(unsigned int i = 0; i < MAX_ATTRIBUTE_CACHE_SIZE; ++i)
180 // see if the cached state is different to the actual state
181 if(mGlStateCache.mVertexAttributeCurrentState[i] != mGlStateCache.mVertexAttributeCachedState[i])
183 // it's different so make the change to the driver and update the cached state
184 mGlStateCache.mVertexAttributeCurrentState[i] = mGlStateCache.mVertexAttributeCachedState[i];
186 if(mGlStateCache.mVertexAttributeCurrentState[i])
188 gl->EnableVertexAttribArray(i);
192 gl->DisableVertexAttribArray(i);
200 * Either enables or disables a vertex attribute location in the cache
201 * The changes won't take affect until FlushVertexAttributeLocations is called
202 * @param location attribute location
203 * @param state attribute state
205 void SetVertexAttributeLocation(unsigned int location, bool state)
210 if(location >= MAX_ATTRIBUTE_CACHE_SIZE)
212 // not cached, make the gl call through context
215 gl->EnableVertexAttribArray(location);
219 gl->DisableVertexAttribArray(location);
224 // set the cached state, it will be set at the next draw call
225 // if it's different from the current driver state
226 mGlStateCache.mVertexAttributeCachedState[location] = state;
232 * Get the pointer to the GL implementation
233 * @return The GL implementation, nullptr if the context has not been created or shutting down
235 [[nodiscard]] inline Integration::GlAbstraction* GetGL() const
237 return mGlContextCreated ? mGL : nullptr;
240 EglGraphicsController& mController;
241 Integration::GlAbstraction* mGL{nullptr};
243 const GLES::PipelineImpl* mCurrentPipeline{nullptr}; ///< Currently bound pipeline
244 const GLES::PipelineImpl* mNewPipeline{nullptr}; ///< New pipeline to be set on flush
246 std::vector<Graphics::TextureBinding> mCurrentTextureBindings{};
247 std::vector<Graphics::SamplerBinding> mCurrentSamplerBindings{};
248 GLES::IndexBufferBindingDescriptor mCurrentIndexBufferBinding{};
250 struct VertexBufferBinding
252 GLES::Buffer* buffer{nullptr};
256 // Currently bound buffers
257 std::vector<VertexBufferBindingDescriptor> mCurrentVertexBufferBindings{};
259 // Currently bound UBOs (check if it's needed per program!)
260 std::vector<UniformBufferBindingDescriptor> mCurrentUBOBindings{};
261 UniformBufferBindingDescriptor mCurrentStandaloneUBOBinding{};
263 // Current render pass and render target
264 const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
265 const GLES::RenderPass* mCurrentRenderPass{nullptr};
267 // Each context must have own VAOs as they cannot be shared
268 std::unordered_map<const GLES::ProgramImpl*, std::map<std::size_t, uint32_t>> mProgramVAOMap; ///< GL program-VAO map
269 uint32_t mProgramVAOCurrentState{0u}; ///< Currently bound VAO
270 GLStateCache mGlStateCache{}; ///< GL status cache
271 std::vector<Dali::GLuint> mDiscardedVAOList{};
273 bool mGlContextCreated{false}; ///< True if the OpenGL context has been created
274 bool mVertexBuffersChanged{true}; ///< True if BindVertexBuffers changed any buffer bindings
276 EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context
278 EGLSurface mCacheDrawReadSurface{0u}; ///< cached 'read' surface
279 EGLSurface mCacheDrawWriteSurface{0u}; ///< cached 'write' surface
280 EGLContext mCacheEGLGraphicsContext{0u}; ///< cached window context
283 Context::Context(EglGraphicsController& controller, Integration::GlAbstraction* glAbstraction)
285 mImpl = std::make_unique<Impl>(controller, glAbstraction);
290 // Destroy native rendering context if one exists
291 if(mImpl->mNativeDrawContext)
293 eglDestroyContext(eglGetCurrentDisplay(), mImpl->mNativeDrawContext);
294 mImpl->mNativeDrawContext = EGL_NO_CONTEXT;
298 void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker)
300 auto* gl = mImpl->GetGL();
301 if(!gl) // Early out if no gl
306 static const bool hasGLES3(mImpl->mController.GetGLESVersion() >= GLESVersion::GLES_30);
308 // early out if neither current nor new pipelines are set
309 // this behaviour may be valid so no assert
310 if(!mImpl->mCurrentPipeline && !mImpl->mNewPipeline)
315 // Execute states if pipeline is changed
316 const auto currentProgram = mImpl->mCurrentPipeline ? static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr;
318 // case when new pipeline has been set
319 const GLES::Program* newProgram = nullptr;
321 if(mImpl->mNewPipeline)
323 newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
326 if(!currentProgram && !newProgram)
328 // Early out if we have no program for this pipeline.
329 DALI_LOG_ERROR("No program defined for pipeline\n");
333 // If this draw uses a different pipeline _AND_ the pipeline has a different GL Program,
334 // Then bind the new program. Ensure vertex atrributes are set.
336 bool programChanged = false;
337 if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
339 if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
341 mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
342 programChanged = true;
348 // Resolve rasterization state
349 ResolveRasterizationState();
352 // Resolve uniform buffers
353 ResolveUniformBuffers();
356 // Map binding# to sampler location
357 const auto& reflection = !newProgram ? currentProgram->GetReflection() : newProgram->GetReflection();
358 const auto& samplers = reflection.GetSamplers();
360 uint32_t currentSampler = 0;
361 uint32_t currentElement = 0;
363 // @warning Assume that binding.binding is strictly linear in the same order as mCurrentTextureBindings
364 // elements. This avoids having to sort the bindings.
365 for(const auto& binding : mImpl->mCurrentTextureBindings)
367 if(currentSampler >= samplers.size())
369 // Don't bind more textures than there are active samplers.
373 auto texture = const_cast<GLES::Texture*>(static_cast<const GLES::Texture*>(binding.texture));
375 // Texture may not have been initialized yet...(tbm_surface timing issue?)
376 if(!texture->GetGLTexture())
378 texture->InitializeResource();
381 // Warning, this may cause glWaitSync to occur on the GPU, or glClientWaitSync to block the CPU.
382 dependencyChecker.CheckNeedsSync(this, texture, true);
383 texture->Bind(binding);
388 // @warning Assume that location of array elements is sequential.
389 // @warning GL does not guarantee this, but in practice, it is.
390 gl->Uniform1i(samplers[currentSampler].location + currentElement,
391 samplers[currentSampler].offset + currentElement);
393 if(currentElement >= samplers[currentSampler].elementCount)
401 const auto& pipelineState = mImpl->mNewPipeline ? mImpl->mNewPipeline->GetCreateInfo() : mImpl->mCurrentPipeline->GetCreateInfo();
402 const auto& vertexInputState = pipelineState.vertexInputState;
404 // for each attribute bind vertices, unless the pipeline+buffer is the same
405 if(programChanged || mImpl->mVertexBuffersChanged)
409 mImpl->BindProgramVAO(static_cast<const GLES::Program*>(pipelineState.programState->program)->GetImplementation(), *vertexInputState);
412 for(const auto& attr : vertexInputState->attributes)
417 mImpl->SetVertexAttributeLocation(attr.location, true);
420 const auto& bufferSlot = mImpl->mCurrentVertexBufferBindings[attr.binding];
421 const auto& bufferBinding = vertexInputState->bufferBindings[attr.binding];
423 auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
425 BindBuffer(GL_ARRAY_BUFFER, glesBuffer); // Cached
427 if(attr.format == VertexInputFormat::FLOAT ||
428 attr.format == VertexInputFormat::FVECTOR2 ||
429 attr.format == VertexInputFormat::FVECTOR3 ||
430 attr.format == VertexInputFormat::FVECTOR4)
432 gl->VertexAttribPointer(attr.location, // Not cached...
433 GLVertexFormat(attr.format).size,
434 GLVertexFormat(attr.format).format,
436 bufferBinding.stride,
437 reinterpret_cast<void*>(attr.offset));
441 gl->VertexAttribIPointer(attr.location,
442 GLVertexFormat(attr.format).size,
443 GLVertexFormat(attr.format).format,
444 bufferBinding.stride,
445 reinterpret_cast<void*>(attr.offset));
450 switch(bufferBinding.inputRate)
452 case Graphics::VertexInputRate::PER_VERTEX:
454 gl->VertexAttribDivisor(attr.location, 0);
457 case Graphics::VertexInputRate::PER_INSTANCE:
459 //@todo Get actual instance rate...
460 gl->VertexAttribDivisor(attr.location, 1);
469 const auto& ia = pipelineState.inputAssemblyState;
472 switch(drawCall.type)
474 case DrawCallDescriptor::Type::DRAW:
476 mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
477 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
478 mImpl->mGlStateCache.StencilBufferWriteEnabled());
479 // For GLES3+ we use VAO, for GLES2 internal cache
482 mImpl->FlushVertexAttributeLocations();
485 if(drawCall.draw.instanceCount == 0)
487 gl->DrawArrays(GLESTopology(ia->topology),
488 drawCall.draw.firstVertex,
489 drawCall.draw.vertexCount);
493 gl->DrawArraysInstanced(GLESTopology(ia->topology),
494 drawCall.draw.firstVertex,
495 drawCall.draw.vertexCount,
496 drawCall.draw.instanceCount);
500 case DrawCallDescriptor::Type::DRAW_INDEXED:
502 const auto& binding = mImpl->mCurrentIndexBufferBinding;
503 BindBuffer(GL_ELEMENT_ARRAY_BUFFER, binding.buffer->GetGLBuffer());
505 mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
506 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
507 mImpl->mGlStateCache.StencilBufferWriteEnabled());
509 // For GLES3+ we use VAO, for GLES2 internal cache
512 mImpl->FlushVertexAttributeLocations();
515 auto indexBufferFormat = GLIndexFormat(binding.format).format;
516 if(drawCall.drawIndexed.instanceCount == 0)
518 gl->DrawElements(GLESTopology(ia->topology),
519 drawCall.drawIndexed.indexCount,
521 reinterpret_cast<void*>(binding.offset));
525 gl->DrawElementsInstanced(GLESTopology(ia->topology),
526 drawCall.drawIndexed.indexCount,
528 reinterpret_cast<void*>(binding.offset),
529 drawCall.drawIndexed.instanceCount);
533 case DrawCallDescriptor::Type::DRAW_INDEXED_INDIRECT:
542 if(mImpl->mNewPipeline)
544 mImpl->mCurrentPipeline = mImpl->mNewPipeline;
545 mImpl->mNewPipeline = nullptr;
549 void Context::BindTextures(const Graphics::TextureBinding* bindings, uint32_t count)
551 // for each texture allocate slot
552 for(auto i = 0u; i < count; ++i)
554 auto& binding = bindings[i];
556 // Resize binding array if needed
557 if(mImpl->mCurrentTextureBindings.size() <= binding.binding)
559 mImpl->mCurrentTextureBindings.resize(binding.binding + 1);
561 // Store the binding details
562 mImpl->mCurrentTextureBindings[binding.binding] = binding;
566 void Context::BindVertexBuffers(const GLES::VertexBufferBindingDescriptor* bindings, uint32_t count)
568 if(count > mImpl->mCurrentVertexBufferBindings.size())
570 mImpl->mCurrentVertexBufferBindings.resize(count);
572 // Copy only set slots
573 mImpl->mVertexBuffersChanged = false;
574 auto toIter = mImpl->mCurrentVertexBufferBindings.begin();
575 for(auto fromIter = bindings, end = bindings + count; fromIter != end; ++fromIter)
577 if(fromIter->buffer != nullptr)
579 if(toIter->buffer != fromIter->buffer || toIter->offset != fromIter->offset)
581 mImpl->mVertexBuffersChanged = true;
583 *toIter++ = *fromIter;
588 void Context::BindIndexBuffer(const IndexBufferBindingDescriptor& indexBufferBinding)
590 mImpl->mCurrentIndexBufferBinding = indexBufferBinding;
593 void Context::BindPipeline(const GLES::Pipeline* newPipeline)
595 DALI_ASSERT_ALWAYS(newPipeline && "Invalid pipeline");
596 mImpl->mNewPipeline = &newPipeline->GetPipeline();
599 void Context::BindUniformBuffers(const UniformBufferBindingDescriptor* uboBindings,
601 const UniformBufferBindingDescriptor& standaloneBindings)
603 if(standaloneBindings.buffer)
605 mImpl->mCurrentStandaloneUBOBinding = standaloneBindings;
608 if(uboCount && uboCount > mImpl->mCurrentUBOBindings.size())
610 mImpl->mCurrentUBOBindings.resize(uboCount);
613 auto it = uboBindings;
614 for(auto i = 0u; i < uboCount; ++i)
618 mImpl->mCurrentUBOBindings[i] = *it;
624 void Context::ResolveBlendState()
626 const auto& currentBlendState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().colorBlendState : nullptr;
627 const auto& newBlendState = mImpl->mNewPipeline->GetCreateInfo().colorBlendState;
629 // TODO: prevent leaking the state
635 auto* gl = mImpl->GetGL();
636 if(!gl) // Early out if no gl
641 if(!currentBlendState || currentBlendState->blendEnable != newBlendState->blendEnable)
643 if(newBlendState->blendEnable != mImpl->mGlStateCache.mBlendEnabled)
645 mImpl->mGlStateCache.mBlendEnabled = newBlendState->blendEnable;
646 newBlendState->blendEnable ? gl->Enable(GL_BLEND) : gl->Disable(GL_BLEND);
650 if(!newBlendState->blendEnable)
655 BlendFactor newSrcRGB(newBlendState->srcColorBlendFactor);
656 BlendFactor newDstRGB(newBlendState->dstColorBlendFactor);
657 BlendFactor newSrcAlpha(newBlendState->srcAlphaBlendFactor);
658 BlendFactor newDstAlpha(newBlendState->dstAlphaBlendFactor);
660 if(!currentBlendState ||
661 currentBlendState->srcColorBlendFactor != newSrcRGB ||
662 currentBlendState->dstColorBlendFactor != newDstRGB ||
663 currentBlendState->srcAlphaBlendFactor != newSrcAlpha ||
664 currentBlendState->dstAlphaBlendFactor != newDstAlpha)
666 if((mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB != newSrcRGB) ||
667 (mImpl->mGlStateCache.mBlendFuncSeparateDstRGB != newDstRGB) ||
668 (mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha != newSrcAlpha) ||
669 (mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha != newDstAlpha))
671 mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB = newSrcRGB;
672 mImpl->mGlStateCache.mBlendFuncSeparateDstRGB = newDstRGB;
673 mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha = newSrcAlpha;
674 mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha = newDstAlpha;
676 if(newSrcRGB == newSrcAlpha && newDstRGB == newDstAlpha)
678 gl->BlendFunc(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB));
682 gl->BlendFuncSeparate(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB), GLBlendFunc(newSrcAlpha), GLBlendFunc(newDstAlpha));
687 if(!currentBlendState ||
688 currentBlendState->colorBlendOp != newBlendState->colorBlendOp ||
689 currentBlendState->alphaBlendOp != newBlendState->alphaBlendOp)
691 if(mImpl->mGlStateCache.mBlendEquationSeparateModeRGB != newBlendState->colorBlendOp ||
692 mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha != newBlendState->alphaBlendOp)
694 mImpl->mGlStateCache.mBlendEquationSeparateModeRGB = newBlendState->colorBlendOp;
695 mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha = newBlendState->alphaBlendOp;
697 if(newBlendState->colorBlendOp == newBlendState->alphaBlendOp)
699 gl->BlendEquation(GLBlendOp(newBlendState->colorBlendOp));
700 if(newBlendState->colorBlendOp >= Graphics::ADVANCED_BLEND_OPTIONS_START)
707 gl->BlendEquationSeparate(GLBlendOp(newBlendState->colorBlendOp), GLBlendOp(newBlendState->alphaBlendOp));
713 void Context::ResolveRasterizationState()
715 const auto& currentRasterizationState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().rasterizationState : nullptr;
716 const auto& newRasterizationState = mImpl->mNewPipeline->GetCreateInfo().rasterizationState;
718 // TODO: prevent leaking the state
719 if(!newRasterizationState)
724 auto* gl = mImpl->GetGL();
725 if(!gl) // Early out if no gl
730 if(!currentRasterizationState ||
731 currentRasterizationState->cullMode != newRasterizationState->cullMode)
733 if(mImpl->mGlStateCache.mCullFaceMode != newRasterizationState->cullMode)
735 mImpl->mGlStateCache.mCullFaceMode = newRasterizationState->cullMode;
736 if(newRasterizationState->cullMode == CullMode::NONE)
738 gl->Disable(GL_CULL_FACE);
742 gl->Enable(GL_CULL_FACE);
743 gl->CullFace(GLCullMode(newRasterizationState->cullMode));
747 // TODO: implement polygon mode (fill, line, points)
748 // seems like we don't support it (no glPolygonMode())
751 void Context::ResolveUniformBuffers()
753 // Resolve standalone uniforms if we have binding
754 if(mImpl->mCurrentStandaloneUBOBinding.buffer)
756 ResolveStandaloneUniforms();
758 if(!mImpl->mCurrentUBOBindings.empty())
760 ResolveGpuUniformBuffers();
764 void Context::ResolveGpuUniformBuffers()
766 if(auto* gl = mImpl->GetGL())
769 for(auto& binding : mImpl->mCurrentUBOBindings)
771 gl->BindBufferRange(GL_UNIFORM_BUFFER, i++, binding.buffer->GetGLBuffer(), GLintptr(binding.offset), GLintptr(binding.dataSize));
776 void Context::ResolveStandaloneUniforms()
778 // Find reflection for program
779 const GLES::Program* program{nullptr};
781 if(mImpl->mNewPipeline)
783 program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
785 else if(mImpl->mCurrentPipeline)
787 program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
792 const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
793 // Update program uniforms
794 program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
798 void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
800 auto& renderPass = *renderPassBegin.renderPass;
801 auto& renderTarget = *renderPassBegin.renderTarget;
803 const auto& targetInfo = renderTarget.GetCreateInfo();
805 auto* gl = mImpl->GetGL();
806 if(!gl) // Early out if no gl
811 if(targetInfo.surface)
814 BindFrameBuffer(GL_FRAMEBUFFER, 0);
816 else if(targetInfo.framebuffer)
818 // bind framebuffer and swap.
819 auto framebuffer = renderTarget.GetFramebuffer();
823 // clear (ideally cache the setup)
825 // In GL we assume that the last attachment is depth/stencil (we may need
826 // to cache extra information inside GLES RenderTarget if we want to be
827 // more specific in case of MRT)
829 const auto& attachments = *renderPass.GetCreateInfo().attachments;
830 const auto& color0 = attachments[0];
833 if(color0.loadOp == AttachmentLoadOp::CLEAR)
835 mask |= GL_COLOR_BUFFER_BIT;
838 // Something goes wrong here if Alpha mask is GL_TRUE
841 const auto clearValues = renderPassBegin.clearValues.Ptr();
843 if(!Dali::Equals(mImpl->mGlStateCache.mClearColor.r, clearValues[0].color.r) ||
844 !Dali::Equals(mImpl->mGlStateCache.mClearColor.g, clearValues[0].color.g) ||
845 !Dali::Equals(mImpl->mGlStateCache.mClearColor.b, clearValues[0].color.b) ||
846 !Dali::Equals(mImpl->mGlStateCache.mClearColor.a, clearValues[0].color.a) ||
847 !mImpl->mGlStateCache.mClearColorSet)
849 gl->ClearColor(clearValues[0].color.r,
850 clearValues[0].color.g,
851 clearValues[0].color.b,
852 clearValues[0].color.a);
854 mImpl->mGlStateCache.mClearColorSet = true;
855 mImpl->mGlStateCache.mClearColor = Vector4(clearValues[0].color.r,
856 clearValues[0].color.g,
857 clearValues[0].color.b,
858 clearValues[0].color.a);
862 // check for depth stencil
863 if(attachments.size() > 1)
865 const auto& depthStencil = attachments.back();
866 if(depthStencil.loadOp == AttachmentLoadOp::CLEAR)
868 if(!mImpl->mGlStateCache.mDepthMaskEnabled)
870 mImpl->mGlStateCache.mDepthMaskEnabled = true;
873 mask |= GL_DEPTH_BUFFER_BIT;
875 if(depthStencil.stencilLoadOp == AttachmentLoadOp::CLEAR)
877 if(mImpl->mGlStateCache.mStencilMask != 0xFF)
879 mImpl->mGlStateCache.mStencilMask = 0xFF;
880 gl->StencilMask(0xFF);
882 mask |= GL_STENCIL_BUFFER_BIT;
886 SetScissorTestEnabled(true);
887 gl->Scissor(renderPassBegin.renderArea.x, renderPassBegin.renderArea.y, renderPassBegin.renderArea.width, renderPassBegin.renderArea.height);
888 ClearBuffer(mask, true);
889 SetScissorTestEnabled(false);
891 mImpl->mCurrentRenderPass = &renderPass;
892 mImpl->mCurrentRenderTarget = &renderTarget;
895 void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
897 if(mImpl->mCurrentRenderTarget)
899 GLES::Framebuffer* framebuffer = mImpl->mCurrentRenderTarget->GetFramebuffer();
900 auto* gl = mImpl->GetGL();
901 if(framebuffer && gl)
905 /* @todo Full dependency checking would need to store textures in Begin, and create
906 * fence objects here; but we're going to draw all fbos on shared context in serial,
907 * so no real need (yet). Might want to consider ensuring order of render passes,
908 * but that needs doing in the controller, and would need doing before ProcessCommandQueues.
910 * Currently up to the client to create render tasks in the right order.
913 /* Create fence sync objects. Other contexts can then wait on these fences before reading
916 dependencyChecker.AddTextures(this, framebuffer);
921 void Context::ClearState()
923 mImpl->mCurrentTextureBindings.clear();
924 mImpl->mCurrentUBOBindings.clear();
927 void Context::ColorMask(bool enabled)
929 auto* gl = mImpl->GetGL();
930 if(gl && enabled != mImpl->mGlStateCache.mColorMask)
932 mImpl->mGlStateCache.mColorMask = enabled;
933 gl->ColorMask(enabled, enabled, enabled, enabled);
937 void Context::ClearStencilBuffer()
939 ClearBuffer(GL_STENCIL_BUFFER_BIT, false);
942 void Context::ClearDepthBuffer()
944 ClearBuffer(GL_DEPTH_BUFFER_BIT, false);
947 void Context::ClearBuffer(uint32_t mask, bool forceClear)
949 mask = mImpl->mGlStateCache.mFrameBufferStateCache.GetClearMask(mask, forceClear, mImpl->mGlStateCache.mScissorTestEnabled);
950 auto* gl = mImpl->GetGL();
957 void Context::InvalidateDepthStencilBuffers()
959 if(auto* gl = mImpl->GetGL())
961 GLenum attachments[] = {GL_DEPTH, GL_STENCIL};
962 gl->InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
966 void Context::SetScissorTestEnabled(bool scissorEnabled)
968 auto* gl = mImpl->GetGL();
969 if(gl && mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled)
971 mImpl->mGlStateCache.mScissorTestEnabled = scissorEnabled;
975 gl->Enable(GL_SCISSOR_TEST);
979 gl->Disable(GL_SCISSOR_TEST);
984 void Context::SetStencilTestEnable(bool stencilEnable)
986 auto* gl = mImpl->GetGL();
987 if(gl && stencilEnable != mImpl->mGlStateCache.mStencilBufferEnabled)
989 mImpl->mGlStateCache.mStencilBufferEnabled = stencilEnable;
993 gl->Enable(GL_STENCIL_TEST);
997 gl->Disable(GL_STENCIL_TEST);
1002 void Context::StencilMask(uint32_t writeMask)
1004 auto* gl = mImpl->GetGL();
1005 if(gl && writeMask != mImpl->mGlStateCache.mStencilMask)
1007 mImpl->mGlStateCache.mStencilMask = writeMask;
1009 gl->StencilMask(writeMask);
1013 void Context::StencilFunc(Graphics::CompareOp compareOp,
1015 uint32_t compareMask)
1017 auto* gl = mImpl->GetGL();
1019 (compareOp != mImpl->mGlStateCache.mStencilFunc ||
1020 reference != mImpl->mGlStateCache.mStencilFuncRef ||
1021 compareMask != mImpl->mGlStateCache.mStencilFuncMask))
1023 mImpl->mGlStateCache.mStencilFunc = compareOp;
1024 mImpl->mGlStateCache.mStencilFuncRef = reference;
1025 mImpl->mGlStateCache.mStencilFuncMask = compareMask;
1027 gl->StencilFunc(GLCompareOp(compareOp).op, reference, compareMask);
1031 void Context::StencilOp(Graphics::StencilOp failOp,
1032 Graphics::StencilOp depthFailOp,
1033 Graphics::StencilOp passOp)
1035 auto* gl = mImpl->GetGL();
1037 (failOp != mImpl->mGlStateCache.mStencilOpFail ||
1038 depthFailOp != mImpl->mGlStateCache.mStencilOpDepthFail ||
1039 passOp != mImpl->mGlStateCache.mStencilOpDepthPass))
1041 mImpl->mGlStateCache.mStencilOpFail = failOp;
1042 mImpl->mGlStateCache.mStencilOpDepthFail = depthFailOp;
1043 mImpl->mGlStateCache.mStencilOpDepthPass = passOp;
1045 gl->StencilOp(GLStencilOp(failOp).op, GLStencilOp(depthFailOp).op, GLStencilOp(passOp).op);
1049 void Context::SetDepthCompareOp(Graphics::CompareOp compareOp)
1051 auto* gl = mImpl->GetGL();
1052 if(gl && compareOp != mImpl->mGlStateCache.mDepthFunction)
1054 mImpl->mGlStateCache.mDepthFunction = compareOp;
1056 gl->DepthFunc(GLCompareOp(compareOp).op);
1060 void Context::SetDepthTestEnable(bool depthTestEnable)
1062 auto* gl = mImpl->GetGL();
1063 if(gl && depthTestEnable != mImpl->mGlStateCache.mDepthBufferEnabled)
1065 mImpl->mGlStateCache.mDepthBufferEnabled = depthTestEnable;
1069 gl->Enable(GL_DEPTH_TEST);
1073 gl->Disable(GL_DEPTH_TEST);
1078 void Context::SetDepthWriteEnable(bool depthWriteEnable)
1080 auto* gl = mImpl->GetGL();
1081 if(gl && depthWriteEnable != mImpl->mGlStateCache.mDepthMaskEnabled)
1083 mImpl->mGlStateCache.mDepthMaskEnabled = depthWriteEnable;
1085 gl->DepthMask(depthWriteEnable);
1089 void Context::ActiveTexture(uint32_t textureBindingIndex)
1091 auto* gl = mImpl->GetGL();
1092 if(gl && mImpl->mGlStateCache.mActiveTextureUnit != textureBindingIndex)
1094 mImpl->mGlStateCache.mActiveTextureUnit = textureBindingIndex;
1096 gl->ActiveTexture(GL_TEXTURE0 + textureBindingIndex);
1100 void Context::BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId)
1102 uint32_t typeId = static_cast<uint32_t>(textureTypeId);
1103 auto* gl = mImpl->GetGL();
1104 if(gl && mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] != textureId)
1106 mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] = textureId;
1108 gl->BindTexture(target, textureId);
1112 void Context::GenerateMipmap(GLenum target)
1114 if(auto* gl = mImpl->GetGL())
1116 gl->GenerateMipmap(target);
1120 bool Context::BindBuffer(GLenum target, uint32_t bufferId)
1122 if(auto* gl = mImpl->GetGL())
1126 case GL_ARRAY_BUFFER:
1128 if(mImpl->mGlStateCache.mBoundArrayBufferId == bufferId)
1132 mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
1135 case GL_ELEMENT_ARRAY_BUFFER:
1137 if(mImpl->mGlStateCache.mBoundElementArrayBufferId == bufferId)
1141 mImpl->mGlStateCache.mBoundElementArrayBufferId = bufferId;
1146 // Cache miss. Bind buffer.
1147 gl->BindBuffer(target, bufferId);
1153 void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
1155 if(auto* gl = mImpl->GetGL())
1157 mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
1158 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
1159 mImpl->mGlStateCache.StencilBufferWriteEnabled());
1161 gl->DrawBuffers(count, buffers);
1165 void Context::BindFrameBuffer(GLenum target, uint32_t bufferId)
1167 if(auto* gl = mImpl->GetGL())
1169 mImpl->mGlStateCache.mFrameBufferStateCache.SetCurrentFrameBuffer(bufferId);
1171 gl->BindFramebuffer(target, bufferId);
1175 void Context::GenFramebuffers(uint32_t count, uint32_t* framebuffers)
1177 if(auto* gl = mImpl->GetGL())
1179 gl->GenFramebuffers(count, framebuffers);
1180 mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersCreated(count, framebuffers);
1184 void Context::DeleteFramebuffers(uint32_t count, uint32_t* framebuffers)
1186 if(auto* gl = mImpl->GetGL())
1188 mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersDeleted(count, framebuffers);
1190 gl->DeleteFramebuffers(count, framebuffers);
1194 GLStateCache& Context::GetGLStateCache()
1196 return mImpl->mGlStateCache;
1199 void Context::GlContextCreated()
1201 if(!mImpl->mGlContextCreated)
1203 mImpl->mGlContextCreated = true;
1205 // Set the initial GL state
1206 mImpl->InitializeGlState();
1210 void Context::GlContextDestroyed()
1212 mImpl->mGlContextCreated = false;
1215 void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
1217 // Since the pipeline is deleted, invalidate the cached pipeline.
1218 if(mImpl->mCurrentPipeline == &pipeline->GetPipeline())
1220 mImpl->mCurrentPipeline = nullptr;
1223 // Remove cached VAO map
1224 auto* gl = mImpl->GetGL();
1227 const auto* program = pipeline->GetCreateInfo().programState->program;
1230 const auto* programImpl = static_cast<const GLES::Program*>(program)->GetImplementation();
1233 auto iter = mImpl->mProgramVAOMap.find(programImpl);
1234 if(iter != mImpl->mProgramVAOMap.end())
1236 for(auto& attributeHashPair : iter->second)
1238 auto vao = attributeHashPair.second;
1240 // Do not delete vao now. (Since Context might not be current.)
1241 mImpl->mDiscardedVAOList.emplace_back(vao);
1242 if(mImpl->mProgramVAOCurrentState == vao)
1244 mImpl->mProgramVAOCurrentState = 0u;
1248 // Clear cached Vertex buffer.
1249 mImpl->mCurrentVertexBufferBindings.clear();
1251 mImpl->mProgramVAOMap.erase(iter);
1258 void Context::PrepareForNativeRendering()
1260 // this should be pretty much constant
1261 auto display = eglGetCurrentDisplay();
1262 auto drawSurface = eglGetCurrentSurface(EGL_DRAW);
1263 auto readSurface = eglGetCurrentSurface(EGL_READ);
1264 auto context = eglGetCurrentContext();
1266 // push the surface and context data to the impl
1267 // It's needed to restore context
1268 if(!mImpl->mCacheEGLGraphicsContext)
1270 mImpl->mCacheDrawWriteSurface = drawSurface;
1271 mImpl->mCacheDrawReadSurface = readSurface;
1272 mImpl->mCacheEGLGraphicsContext = context;
1275 if(!mImpl->mNativeDrawContext)
1277 EGLint configId{0u};
1278 eglQueryContext(display, mImpl->mController.GetSharedContext(), EGL_CONFIG_ID, &configId);
1280 EGLint configAttribs[3];
1281 configAttribs[0] = EGL_CONFIG_ID;
1282 configAttribs[1] = configId;
1283 configAttribs[2] = EGL_NONE;
1287 if(eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) != EGL_TRUE)
1289 DALI_LOG_ERROR("eglChooseConfig failed!\n");
1293 auto version = int(mImpl->mController.GetGLESVersion());
1295 std::vector<EGLint> attribs;
1296 attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
1297 attribs.push_back(version / 10);
1298 attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
1299 attribs.push_back(version % 10);
1300 attribs.push_back(EGL_NONE);
1302 mImpl->mNativeDrawContext = eglCreateContext(display, config, mImpl->mController.GetSharedContext(), attribs.data());
1303 if(mImpl->mNativeDrawContext == EGL_NO_CONTEXT)
1305 DALI_LOG_ERROR("eglCreateContext failed!\n");
1310 eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext);
1311 // make sure it's current window context
1312 eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext);
1315 void Context::ResetGLESState()
1317 mImpl->mGlStateCache.ResetBufferCache();
1318 mImpl->mGlStateCache.ResetTextureCache();
1319 mImpl->mCurrentPipeline = nullptr;
1320 mImpl->mCurrentUBOBindings.clear();
1321 mImpl->mCurrentTextureBindings.clear();
1322 mImpl->mCurrentVertexBufferBindings.clear();
1323 mImpl->mCurrentRenderTarget = nullptr;
1324 mImpl->mCurrentRenderPass = nullptr;
1325 mImpl->mVertexBuffersChanged = true;
1326 mImpl->mCurrentIndexBufferBinding = {};
1327 mImpl->mCurrentSamplerBindings = {};
1328 mImpl->mProgramVAOCurrentState = 0;
1331 mImpl->InitializeGlState();
1334 void Context::RestoreFromNativeRendering()
1336 auto display = eglGetCurrentDisplay();
1338 // bring back original context
1339 eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext);
1342 } // namespace Dali::Graphics::GLES