3f15dae3c5c80a57fa6923ab7d9d74408bb875ee
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-context.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
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>
25
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"
33
34 #include <EGL/egl.h>
35 #include <EGL/eglext.h>
36 #include <map>
37 #include <unordered_map>
38
39 namespace Dali::Graphics::GLES
40 {
41 struct Context::Impl
42 {
43   explicit Impl(EglGraphicsController& controller)
44   : mController(controller)
45   {
46   }
47
48   ~Impl() = default;
49
50   /**
51    * Binds (and creates) VAO
52    *
53    * VAO is fixed per program so it has to be created only once assuming
54    * that VertexInputState has been set correctly for the pipeline.
55    *
56    */
57   void BindProgramVAO(const GLES::ProgramImpl* program, const VertexInputState& vertexInputState)
58   {
59     // Calculate attributes location hash unordered.
60     std::size_t hash = 0;
61     for(const auto& attr : vertexInputState.attributes)
62     {
63       hash ^= std::hash<uint32_t>{}(attr.location);
64     }
65
66     auto& gl   = *mController.GetGL();
67     auto  iter = mProgramVAOMap.find(program);
68     if(iter != mProgramVAOMap.end())
69     {
70       auto attributeIter = iter->second.find(hash);
71       if(attributeIter != iter->second.end())
72       {
73         if(mProgramVAOCurrentState != attributeIter->second)
74         {
75           mProgramVAOCurrentState = attributeIter->second;
76           gl.BindVertexArray(attributeIter->second);
77
78           // Binding VAO seems to reset the index buffer binding so the cache must be reset
79           mGlStateCache.mBoundElementArrayBufferId = 0;
80         }
81         return;
82       }
83     }
84
85     uint32_t vao;
86     gl.GenVertexArrays(1, &vao);
87     gl.BindVertexArray(vao);
88
89     // Binding VAO seems to reset the index buffer binding so the cache must be reset
90     mGlStateCache.mBoundElementArrayBufferId = 0;
91
92     mProgramVAOMap[program][hash] = vao;
93     for(const auto& attr : vertexInputState.attributes)
94     {
95       gl.EnableVertexAttribArray(attr.location);
96     }
97
98     mProgramVAOCurrentState = vao;
99   }
100
101   /**
102    * Sets the initial GL state.
103    */
104   void InitializeGlState()
105   {
106     auto& gl = *mController.GetGL();
107
108     mGlStateCache.mClearColorSet        = false;
109     mGlStateCache.mColorMask            = true;
110     mGlStateCache.mStencilMask          = 0xFF;
111     mGlStateCache.mBlendEnabled         = false;
112     mGlStateCache.mDepthBufferEnabled   = false;
113     mGlStateCache.mDepthMaskEnabled     = false;
114     mGlStateCache.mScissorTestEnabled   = false;
115     mGlStateCache.mStencilBufferEnabled = false;
116
117     gl.Disable(GL_DITHER);
118
119     mGlStateCache.mBoundArrayBufferId        = 0;
120     mGlStateCache.mBoundElementArrayBufferId = 0;
121     mGlStateCache.mActiveTextureUnit         = 0;
122
123     mGlStateCache.mBlendFuncSeparateSrcRGB   = BlendFactor::ONE;
124     mGlStateCache.mBlendFuncSeparateDstRGB   = BlendFactor::ZERO;
125     mGlStateCache.mBlendFuncSeparateSrcAlpha = BlendFactor::ONE;
126     mGlStateCache.mBlendFuncSeparateDstAlpha = BlendFactor::ZERO;
127
128     // initial state is GL_FUNC_ADD for both RGB and Alpha blend modes
129     mGlStateCache.mBlendEquationSeparateModeRGB   = BlendOp::ADD;
130     mGlStateCache.mBlendEquationSeparateModeAlpha = BlendOp::ADD;
131
132     mGlStateCache.mCullFaceMode = CullMode::NONE; //By default cullface is disabled, front face is set to CCW and cull face is set to back
133
134     //Initialze vertex attribute cache
135     memset(&mGlStateCache.mVertexAttributeCachedState, 0, sizeof(mGlStateCache.mVertexAttributeCachedState));
136     memset(&mGlStateCache.mVertexAttributeCurrentState, 0, sizeof(mGlStateCache.mVertexAttributeCurrentState));
137
138     //Initialize bound 2d texture cache
139     memset(&mGlStateCache.mBoundTextureId, 0, sizeof(mGlStateCache.mBoundTextureId));
140
141     mGlStateCache.mFrameBufferStateCache.Reset();
142
143     GLint maxTextures;
144     gl.GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextures);
145     DALI_LOG_RELEASE_INFO("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d\n", maxTextures);
146   }
147
148   /**
149    * Flushes vertex attribute location changes to the driver
150    */
151   void FlushVertexAttributeLocations()
152   {
153     auto& gl = *mController.GetGL();
154
155     for(unsigned int i = 0; i < MAX_ATTRIBUTE_CACHE_SIZE; ++i)
156     {
157       // see if the cached state is different to the actual state
158       if(mGlStateCache.mVertexAttributeCurrentState[i] != mGlStateCache.mVertexAttributeCachedState[i])
159       {
160         // it's different so make the change to the driver and update the cached state
161         mGlStateCache.mVertexAttributeCurrentState[i] = mGlStateCache.mVertexAttributeCachedState[i];
162
163         if(mGlStateCache.mVertexAttributeCurrentState[i])
164         {
165           gl.EnableVertexAttribArray(i);
166         }
167         else
168         {
169           gl.DisableVertexAttribArray(i);
170         }
171       }
172     }
173   }
174
175   /**
176    * Either enables or disables a vertex attribute location in the cache
177    * The cahnges won't take affect until FlushVertexAttributeLocations is called
178    * @param location attribute location
179    * @param state attribute state
180    */
181   void SetVertexAttributeLocation(unsigned int location, bool state)
182   {
183     auto& gl = *mController.GetGL();
184
185     if(location >= MAX_ATTRIBUTE_CACHE_SIZE)
186     {
187       // not cached, make the gl call through context
188       if(state)
189       {
190         gl.EnableVertexAttribArray(location);
191       }
192       else
193       {
194         gl.DisableVertexAttribArray(location);
195       }
196     }
197     else
198     {
199       // set the cached state, it will be set at the next draw call
200       // if it's different from the current driver state
201       mGlStateCache.mVertexAttributeCachedState[location] = state;
202     }
203   }
204
205   EglGraphicsController& mController;
206
207   const GLES::PipelineImpl* mCurrentPipeline{nullptr}; ///< Currently bound pipeline
208   const GLES::PipelineImpl* mNewPipeline{nullptr};     ///< New pipeline to be set on flush
209
210   std::vector<Graphics::TextureBinding> mCurrentTextureBindings{};
211   std::vector<Graphics::SamplerBinding> mCurrentSamplerBindings{};
212   GLES::IndexBufferBindingDescriptor    mCurrentIndexBufferBinding{};
213
214   struct VertexBufferBinding
215   {
216     GLES::Buffer* buffer{nullptr};
217     uint32_t      offset{0u};
218   };
219
220   // Currently bound buffers
221   std::vector<VertexBufferBindingDescriptor> mCurrentVertexBufferBindings{};
222
223   // Currently bound UBOs (check if it's needed per program!)
224   std::vector<UniformBufferBindingDescriptor> mCurrentUBOBindings{};
225   UniformBufferBindingDescriptor              mCurrentStandaloneUBOBinding{};
226
227   // Current render pass and render target
228   const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
229   const GLES::RenderPass*   mCurrentRenderPass{nullptr};
230
231   // Each context must have own VAOs as they cannot be shared
232   std::unordered_map<const GLES::ProgramImpl*, std::map<std::size_t, uint32_t>> mProgramVAOMap;              ///< GL program-VAO map
233   uint32_t                                                                      mProgramVAOCurrentState{0u}; ///< Currently bound VAO
234   GLStateCache                                                                  mGlStateCache{};             ///< GL status cache
235
236   bool mGlContextCreated{false};    ///< True if the OpenGL context has been created
237   bool mVertexBuffersChanged{true}; ///< True if BindVertexBuffers changed any buffer bindings
238
239   EGLContext mNativeDrawContext{0u}; ///< Native rendering EGL context compatible with window context
240
241   EGLSurface mCacheDrawReadSurface{0u};    ///< cached 'read' surface
242   EGLSurface mCacheDrawWriteSurface{0u};   ///< cached 'write' surface
243   EGLContext mCacheEGLGraphicsContext{0u}; ///< cached window context
244 };
245
246 Context::Context(EglGraphicsController& controller)
247 {
248   mImpl = std::make_unique<Impl>(controller);
249 }
250
251 Context::~Context()
252 {
253   // Destroy native rendering context if one exists
254   if(mImpl->mNativeDrawContext)
255   {
256     eglDestroyContext(eglGetCurrentDisplay(), mImpl->mNativeDrawContext);
257     mImpl->mNativeDrawContext = EGL_NO_CONTEXT;
258   }
259 }
260
261 void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker)
262 {
263   auto& gl = *mImpl->mController.GetGL();
264
265   static const bool hasGLES3(mImpl->mController.GetGLESVersion() >= GLESVersion::GLES_30);
266
267   // early out if neither current nor new pipelines are set
268   // this behaviour may be valid so no assert
269   if(!mImpl->mCurrentPipeline && !mImpl->mNewPipeline)
270   {
271     return;
272   }
273
274   // Execute states if pipeline is changed
275   const auto currentProgram = mImpl->mCurrentPipeline ? static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program) : nullptr;
276
277   // case when new pipeline has been set
278   const GLES::Program* newProgram = nullptr;
279
280   if(mImpl->mNewPipeline)
281   {
282     newProgram = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
283   }
284
285   if(!currentProgram && !newProgram)
286   {
287     // Early out if we have no program for this pipeline.
288     DALI_LOG_ERROR("No program defined for pipeline\n");
289     return;
290   }
291
292   // If this draw uses a different pipeline _AND_ the pipeline has a different GL Program,
293   // Then bind the new program. Ensure vertex atrributes are set.
294
295   bool programChanged = false;
296   if(mImpl->mNewPipeline && mImpl->mCurrentPipeline != mImpl->mNewPipeline)
297   {
298     if(!currentProgram || currentProgram->GetImplementation()->GetGlProgram() != newProgram->GetImplementation()->GetGlProgram())
299     {
300       mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
301       programChanged = true;
302     }
303
304     // Blend state
305     ResolveBlendState();
306
307     // Resolve rasterization state
308     ResolveRasterizationState();
309   }
310
311   // Resolve uniform buffers
312   ResolveUniformBuffers();
313
314   // Bind textures
315   // Map binding# to sampler location
316   const auto& reflection = !newProgram ? currentProgram->GetReflection() : newProgram->GetReflection();
317   const auto& samplers   = reflection.GetSamplers();
318
319   uint32_t currentSampler = 0;
320   uint32_t currentElement = 0;
321
322   // @warning Assume that binding.binding is strictly linear in the same order as mCurrentTextureBindings
323   // elements. This avoids having to sort the bindings.
324   for(const auto& binding : mImpl->mCurrentTextureBindings)
325   {
326     if(currentSampler >= samplers.size())
327     {
328       // Don't bind more textures than there are active samplers.
329       break;
330     }
331
332     auto texture = const_cast<GLES::Texture*>(static_cast<const GLES::Texture*>(binding.texture));
333
334     // Texture may not have been initialized yet...(tbm_surface timing issue?)
335     if(!texture->GetGLTexture())
336     {
337       texture->InitializeResource();
338     }
339
340     // Warning, this may cause glWaitSync to occur on the GPU.
341     dependencyChecker.CheckNeedsSync(this, texture);
342     texture->Bind(binding);
343     texture->Prepare();
344
345     if(programChanged)
346     {
347       // @warning Assume that location of array elements is sequential.
348       // @warning GL does not guarantee this, but in practice, it is.
349       gl.Uniform1i(samplers[currentSampler].location + currentElement,
350                    samplers[currentSampler].offset + currentElement);
351       ++currentElement;
352       if(currentElement >= samplers[currentSampler].elementCount)
353       {
354         ++currentSampler;
355         currentElement = 0;
356       }
357     }
358   }
359
360   const auto& pipelineState    = mImpl->mNewPipeline ? mImpl->mNewPipeline->GetCreateInfo() : mImpl->mCurrentPipeline->GetCreateInfo();
361   const auto& vertexInputState = pipelineState.vertexInputState;
362
363   // for each attribute bind vertices, unless the pipeline+buffer is the same
364   if(programChanged || mImpl->mVertexBuffersChanged)
365   {
366     if(hasGLES3)
367     {
368       mImpl->BindProgramVAO(static_cast<const GLES::Program*>(pipelineState.programState->program)->GetImplementation(), *vertexInputState);
369     }
370
371     for(const auto& attr : vertexInputState->attributes)
372     {
373       // Enable location
374       if(!hasGLES3)
375       {
376         mImpl->SetVertexAttributeLocation(attr.location, true);
377       }
378
379       const auto& bufferSlot    = mImpl->mCurrentVertexBufferBindings[attr.binding];
380       const auto& bufferBinding = vertexInputState->bufferBindings[attr.binding];
381
382       auto glesBuffer = bufferSlot.buffer->GetGLBuffer();
383
384       BindBuffer(GL_ARRAY_BUFFER, glesBuffer); // Cached
385
386       if(attr.format == VertexInputFormat::FLOAT ||
387          attr.format == VertexInputFormat::FVECTOR2 ||
388          attr.format == VertexInputFormat::FVECTOR3 ||
389          attr.format == VertexInputFormat::FVECTOR4)
390       {
391         gl.VertexAttribPointer(attr.location, // Not cached...
392                                GLVertexFormat(attr.format).size,
393                                GLVertexFormat(attr.format).format,
394                                GL_FALSE,
395                                bufferBinding.stride,
396                                reinterpret_cast<void*>(attr.offset));
397       }
398       else
399       {
400         gl.VertexAttribIPointer(attr.location,
401                                 GLVertexFormat(attr.format).size,
402                                 GLVertexFormat(attr.format).format,
403                                 bufferBinding.stride,
404                                 reinterpret_cast<void*>(attr.offset));
405       }
406
407       if(hasGLES3)
408       {
409         switch(bufferBinding.inputRate)
410         {
411           case Graphics::VertexInputRate::PER_VERTEX:
412           {
413             gl.VertexAttribDivisor(attr.location, 0);
414             break;
415           }
416           case Graphics::VertexInputRate::PER_INSTANCE:
417           {
418             //@todo Get actual instance rate...
419             gl.VertexAttribDivisor(attr.location, 1);
420             break;
421           }
422         }
423       }
424     }
425   }
426
427   // Resolve topology
428   const auto& ia = pipelineState.inputAssemblyState;
429
430   // Resolve draw call
431   switch(drawCall.type)
432   {
433     case DrawCallDescriptor::Type::DRAW:
434     {
435       mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
436                                                                 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
437                                                                 mImpl->mGlStateCache.StencilBufferWriteEnabled());
438       // For GLES3+ we use VAO, for GLES2 internal cache
439       if(!hasGLES3)
440       {
441         mImpl->FlushVertexAttributeLocations();
442       }
443
444       if(drawCall.draw.instanceCount == 0)
445       {
446         gl.DrawArrays(GLESTopology(ia->topology),
447                       drawCall.draw.firstVertex,
448                       drawCall.draw.vertexCount);
449       }
450       else
451       {
452         gl.DrawArraysInstanced(GLESTopology(ia->topology),
453                                drawCall.draw.firstVertex,
454                                drawCall.draw.vertexCount,
455                                drawCall.draw.instanceCount);
456       }
457       break;
458     }
459     case DrawCallDescriptor::Type::DRAW_INDEXED:
460     {
461       const auto& binding = mImpl->mCurrentIndexBufferBinding;
462       BindBuffer(GL_ELEMENT_ARRAY_BUFFER, binding.buffer->GetGLBuffer());
463
464       mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
465                                                                 mImpl->mGlStateCache.DepthBufferWriteEnabled(),
466                                                                 mImpl->mGlStateCache.StencilBufferWriteEnabled());
467
468       // For GLES3+ we use VAO, for GLES2 internal cache
469       if(!hasGLES3)
470       {
471         mImpl->FlushVertexAttributeLocations();
472       }
473
474       auto indexBufferFormat = GLIndexFormat(binding.format).format;
475       if(drawCall.drawIndexed.instanceCount == 0)
476       {
477         gl.DrawElements(GLESTopology(ia->topology),
478                         drawCall.drawIndexed.indexCount,
479                         indexBufferFormat,
480                         reinterpret_cast<void*>(binding.offset));
481       }
482       else
483       {
484         gl.DrawElementsInstanced(GLESTopology(ia->topology),
485                                  drawCall.drawIndexed.indexCount,
486                                  indexBufferFormat,
487                                  reinterpret_cast<void*>(binding.offset),
488                                  drawCall.drawIndexed.instanceCount);
489       }
490       break;
491     }
492     case DrawCallDescriptor::Type::DRAW_INDEXED_INDIRECT:
493     {
494       break;
495     }
496   }
497
498   ClearState();
499
500   // Change pipeline
501   if(mImpl->mNewPipeline)
502   {
503     mImpl->mCurrentPipeline = mImpl->mNewPipeline;
504     mImpl->mNewPipeline     = nullptr;
505   }
506 }
507
508 void Context::BindTextures(const Graphics::TextureBinding* bindings, uint32_t count)
509 {
510   // for each texture allocate slot
511   for(auto i = 0u; i < count; ++i)
512   {
513     auto& binding = bindings[i];
514
515     // Resize binding array if needed
516     if(mImpl->mCurrentTextureBindings.size() <= binding.binding)
517     {
518       mImpl->mCurrentTextureBindings.resize(binding.binding + 1);
519     }
520     // Store the binding details
521     mImpl->mCurrentTextureBindings[binding.binding] = binding;
522   }
523 }
524
525 void Context::BindVertexBuffers(const GLES::VertexBufferBindingDescriptor* bindings, uint32_t count)
526 {
527   if(count > mImpl->mCurrentVertexBufferBindings.size())
528   {
529     mImpl->mCurrentVertexBufferBindings.resize(count);
530   }
531   // Copy only set slots
532   mImpl->mVertexBuffersChanged = false;
533   auto toIter                  = mImpl->mCurrentVertexBufferBindings.begin();
534   for(auto fromIter = bindings, end = bindings + count; fromIter != end; ++fromIter)
535   {
536     if(fromIter->buffer != nullptr)
537     {
538       if(toIter->buffer != fromIter->buffer || toIter->offset != fromIter->offset)
539       {
540         mImpl->mVertexBuffersChanged = true;
541       }
542       *toIter++ = *fromIter;
543     }
544   }
545 }
546
547 void Context::BindIndexBuffer(const IndexBufferBindingDescriptor& indexBufferBinding)
548 {
549   mImpl->mCurrentIndexBufferBinding = indexBufferBinding;
550 }
551
552 void Context::BindPipeline(const GLES::Pipeline* newPipeline)
553 {
554   DALI_ASSERT_ALWAYS(newPipeline && "Invalid pipeline");
555   mImpl->mNewPipeline = &newPipeline->GetPipeline();
556 }
557
558 void Context::BindUniformBuffers(const UniformBufferBindingDescriptor* uboBindings,
559                                  uint32_t                              uboCount,
560                                  const UniformBufferBindingDescriptor& standaloneBindings)
561 {
562   if(standaloneBindings.buffer)
563   {
564     mImpl->mCurrentStandaloneUBOBinding = standaloneBindings;
565   }
566
567   if(uboCount && uboCount > mImpl->mCurrentUBOBindings.size())
568   {
569     mImpl->mCurrentUBOBindings.resize(uboCount);
570   }
571
572   auto it = uboBindings;
573   for(auto i = 0u; i < uboCount; ++i)
574   {
575     if(it->buffer)
576     {
577       mImpl->mCurrentUBOBindings[i] = *it;
578     }
579     ++it;
580   }
581 }
582
583 void Context::ResolveBlendState()
584 {
585   const auto& currentBlendState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().colorBlendState : nullptr;
586   const auto& newBlendState     = mImpl->mNewPipeline->GetCreateInfo().colorBlendState;
587
588   // TODO: prevent leaking the state
589   if(!newBlendState)
590   {
591     return;
592   }
593
594   auto& gl = *mImpl->mController.GetGL();
595
596   if(!currentBlendState || currentBlendState->blendEnable != newBlendState->blendEnable)
597   {
598     if(newBlendState->blendEnable != mImpl->mGlStateCache.mBlendEnabled)
599     {
600       mImpl->mGlStateCache.mBlendEnabled = newBlendState->blendEnable;
601       newBlendState->blendEnable ? gl.Enable(GL_BLEND) : gl.Disable(GL_BLEND);
602     }
603   }
604
605   if(!newBlendState->blendEnable)
606   {
607     return;
608   }
609
610   BlendFactor newSrcRGB(newBlendState->srcColorBlendFactor);
611   BlendFactor newDstRGB(newBlendState->dstColorBlendFactor);
612   BlendFactor newSrcAlpha(newBlendState->srcAlphaBlendFactor);
613   BlendFactor newDstAlpha(newBlendState->dstAlphaBlendFactor);
614
615   if(!currentBlendState ||
616      currentBlendState->srcColorBlendFactor != newSrcRGB ||
617      currentBlendState->dstColorBlendFactor != newDstRGB ||
618      currentBlendState->srcAlphaBlendFactor != newSrcAlpha ||
619      currentBlendState->dstAlphaBlendFactor != newDstAlpha)
620   {
621     if((mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB != newSrcRGB) ||
622        (mImpl->mGlStateCache.mBlendFuncSeparateDstRGB != newDstRGB) ||
623        (mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha != newSrcAlpha) ||
624        (mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha != newDstAlpha))
625     {
626       mImpl->mGlStateCache.mBlendFuncSeparateSrcRGB   = newSrcRGB;
627       mImpl->mGlStateCache.mBlendFuncSeparateDstRGB   = newDstRGB;
628       mImpl->mGlStateCache.mBlendFuncSeparateSrcAlpha = newSrcAlpha;
629       mImpl->mGlStateCache.mBlendFuncSeparateDstAlpha = newDstAlpha;
630
631       if(newSrcRGB == newSrcAlpha && newDstRGB == newDstAlpha)
632       {
633         gl.BlendFunc(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB));
634       }
635       else
636       {
637         gl.BlendFuncSeparate(GLBlendFunc(newSrcRGB), GLBlendFunc(newDstRGB), GLBlendFunc(newSrcAlpha), GLBlendFunc(newDstAlpha));
638       }
639     }
640   }
641
642   if(!currentBlendState ||
643      currentBlendState->colorBlendOp != newBlendState->colorBlendOp ||
644      currentBlendState->alphaBlendOp != newBlendState->alphaBlendOp)
645   {
646     if(mImpl->mGlStateCache.mBlendEquationSeparateModeRGB != newBlendState->colorBlendOp ||
647        mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha != newBlendState->alphaBlendOp)
648     {
649       mImpl->mGlStateCache.mBlendEquationSeparateModeRGB   = newBlendState->colorBlendOp;
650       mImpl->mGlStateCache.mBlendEquationSeparateModeAlpha = newBlendState->alphaBlendOp;
651
652       if(newBlendState->colorBlendOp == newBlendState->alphaBlendOp)
653       {
654         gl.BlendEquation(GLBlendOp(newBlendState->colorBlendOp));
655         if(newBlendState->colorBlendOp >= Graphics::ADVANCED_BLEND_OPTIONS_START)
656         {
657           gl.BlendBarrier();
658         }
659       }
660       else
661       {
662         gl.BlendEquationSeparate(GLBlendOp(newBlendState->colorBlendOp), GLBlendOp(newBlendState->alphaBlendOp));
663       }
664     }
665   }
666 }
667
668 void Context::ResolveRasterizationState()
669 {
670   const auto& currentRasterizationState = mImpl->mCurrentPipeline ? mImpl->mCurrentPipeline->GetCreateInfo().rasterizationState : nullptr;
671   const auto& newRasterizationState     = mImpl->mNewPipeline->GetCreateInfo().rasterizationState;
672
673   // TODO: prevent leaking the state
674   if(!newRasterizationState)
675   {
676     return;
677   }
678
679   auto& gl = *mImpl->mController.GetGL();
680
681   if(!currentRasterizationState ||
682      currentRasterizationState->cullMode != newRasterizationState->cullMode)
683   {
684     if(mImpl->mGlStateCache.mCullFaceMode != newRasterizationState->cullMode)
685     {
686       mImpl->mGlStateCache.mCullFaceMode = newRasterizationState->cullMode;
687       if(newRasterizationState->cullMode == CullMode::NONE)
688       {
689         gl.Disable(GL_CULL_FACE);
690       }
691       else
692       {
693         gl.Enable(GL_CULL_FACE);
694         gl.CullFace(GLCullMode(newRasterizationState->cullMode));
695       }
696     }
697   }
698   // TODO: implement polygon mode (fill, line, points)
699   //       seems like we don't support it (no glPolygonMode())
700 }
701
702 void Context::ResolveUniformBuffers()
703 {
704   // Resolve standalone uniforms if we have binding
705   if(mImpl->mCurrentStandaloneUBOBinding.buffer)
706   {
707     ResolveStandaloneUniforms();
708   }
709   if(!mImpl->mCurrentUBOBindings.empty())
710   {
711     ResolveGpuUniformBuffers();
712   }
713 }
714
715 void Context::ResolveGpuUniformBuffers()
716 {
717   auto& gl = *mImpl->mController.GetGL();
718   auto  i  = 0u;
719   for(auto& binding : mImpl->mCurrentUBOBindings)
720   {
721     gl.BindBufferRange(GL_UNIFORM_BUFFER, i++, binding.buffer->GetGLBuffer(), binding.offset, binding.dataSize);
722   }
723 }
724
725 void Context::ResolveStandaloneUniforms()
726 {
727   // Find reflection for program
728   const GLES::Program* program{nullptr};
729
730   if(mImpl->mNewPipeline)
731   {
732     program = static_cast<const GLES::Program*>(mImpl->mNewPipeline->GetCreateInfo().programState->program);
733   }
734   else if(mImpl->mCurrentPipeline)
735   {
736     program = static_cast<const GLES::Program*>(mImpl->mCurrentPipeline->GetCreateInfo().programState->program);
737   }
738
739   if(program)
740   {
741     const auto ptr = reinterpret_cast<const char*>(mImpl->mCurrentStandaloneUBOBinding.buffer->GetCPUAllocatedAddress()) + mImpl->mCurrentStandaloneUBOBinding.offset;
742     // Update program uniforms
743     program->GetImplementation()->UpdateStandaloneUniformBlock(ptr);
744   }
745 }
746
747 void Context::BeginRenderPass(const BeginRenderPassDescriptor& renderPassBegin)
748 {
749   auto& renderPass   = *renderPassBegin.renderPass;
750   auto& renderTarget = *renderPassBegin.renderTarget;
751
752   const auto& targetInfo = renderTarget.GetCreateInfo();
753
754   auto& gl = *mImpl->mController.GetGL();
755
756   if(targetInfo.surface)
757   {
758     // Bind surface FB
759     BindFrameBuffer(GL_FRAMEBUFFER, 0);
760   }
761   else if(targetInfo.framebuffer)
762   {
763     // bind framebuffer and swap.
764     auto framebuffer = renderTarget.GetFramebuffer();
765     framebuffer->Bind();
766   }
767
768   // clear (ideally cache the setup)
769
770   // In GL we assume that the last attachment is depth/stencil (we may need
771   // to cache extra information inside GLES RenderTarget if we want to be
772   // more specific in case of MRT)
773
774   const auto& attachments = *renderPass.GetCreateInfo().attachments;
775   const auto& color0      = attachments[0];
776   GLuint      mask        = 0;
777
778   if(color0.loadOp == AttachmentLoadOp::CLEAR)
779   {
780     mask |= GL_COLOR_BUFFER_BIT;
781
782     // Set clear color
783     // Something goes wrong here if Alpha mask is GL_TRUE
784     ColorMask(true);
785
786     const auto clearValues = renderPassBegin.clearValues.Ptr();
787
788     if(!Dali::Equals(mImpl->mGlStateCache.mClearColor.r, clearValues[0].color.r) ||
789        !Dali::Equals(mImpl->mGlStateCache.mClearColor.g, clearValues[0].color.g) ||
790        !Dali::Equals(mImpl->mGlStateCache.mClearColor.b, clearValues[0].color.b) ||
791        !Dali::Equals(mImpl->mGlStateCache.mClearColor.a, clearValues[0].color.a) ||
792        !mImpl->mGlStateCache.mClearColorSet)
793     {
794       gl.ClearColor(clearValues[0].color.r,
795                     clearValues[0].color.g,
796                     clearValues[0].color.b,
797                     clearValues[0].color.a);
798
799       mImpl->mGlStateCache.mClearColorSet = true;
800       mImpl->mGlStateCache.mClearColor    = Vector4(clearValues[0].color.r,
801                                                  clearValues[0].color.g,
802                                                  clearValues[0].color.b,
803                                                  clearValues[0].color.a);
804     }
805   }
806
807   // check for depth stencil
808   if(attachments.size() > 1)
809   {
810     const auto& depthStencil = attachments.back();
811     if(depthStencil.loadOp == AttachmentLoadOp::CLEAR)
812     {
813       if(!mImpl->mGlStateCache.mDepthMaskEnabled)
814       {
815         mImpl->mGlStateCache.mDepthMaskEnabled = true;
816         gl.DepthMask(true);
817       }
818       mask |= GL_DEPTH_BUFFER_BIT;
819     }
820     if(depthStencil.stencilLoadOp == AttachmentLoadOp::CLEAR)
821     {
822       if(mImpl->mGlStateCache.mStencilMask != 0xFF)
823       {
824         mImpl->mGlStateCache.mStencilMask = 0xFF;
825         gl.StencilMask(0xFF);
826       }
827       mask |= GL_STENCIL_BUFFER_BIT;
828     }
829   }
830
831   SetScissorTestEnabled(true);
832   gl.Scissor(renderPassBegin.renderArea.x, renderPassBegin.renderArea.y, renderPassBegin.renderArea.width, renderPassBegin.renderArea.height);
833   ClearBuffer(mask, true);
834   SetScissorTestEnabled(false);
835
836   mImpl->mCurrentRenderPass   = &renderPass;
837   mImpl->mCurrentRenderTarget = &renderTarget;
838 }
839
840 void Context::EndRenderPass(GLES::TextureDependencyChecker& dependencyChecker)
841 {
842   if(mImpl->mCurrentRenderTarget)
843   {
844     GLES::Framebuffer* framebuffer = mImpl->mCurrentRenderTarget->GetFramebuffer();
845     if(framebuffer)
846     {
847       auto& gl = *mImpl->mController.GetGL();
848       gl.Flush();
849
850       /* @todo Full dependency checking would need to store textures in Begin, and create
851        * fence objects here; but we're going to draw all fbos on shared context in serial,
852        * so no real need (yet). Might want to consider ensuring order of render passes,
853        * but that needs doing in the controller, and would need doing before ProcessCommandQueues.
854        *
855        * Currently up to the client to create render tasks in the right order.
856        */
857
858       /* Create fence sync objects. Other contexts can then wait on these fences before reading
859        * textures.
860        */
861       dependencyChecker.AddTextures(this, framebuffer);
862     }
863   }
864 }
865
866 void Context::ClearState()
867 {
868   mImpl->mCurrentTextureBindings.clear();
869   mImpl->mCurrentUBOBindings.clear();
870 }
871
872 void Context::ColorMask(bool enabled)
873 {
874   if(enabled != mImpl->mGlStateCache.mColorMask)
875   {
876     mImpl->mGlStateCache.mColorMask = enabled;
877
878     auto& gl = *mImpl->mController.GetGL();
879     gl.ColorMask(enabled, enabled, enabled, enabled);
880   }
881 }
882
883 void Context::ClearStencilBuffer()
884 {
885   ClearBuffer(GL_STENCIL_BUFFER_BIT, false);
886 }
887
888 void Context::ClearDepthBuffer()
889 {
890   ClearBuffer(GL_DEPTH_BUFFER_BIT, false);
891 }
892
893 void Context::ClearBuffer(uint32_t mask, bool forceClear)
894 {
895   mask = mImpl->mGlStateCache.mFrameBufferStateCache.GetClearMask(mask, forceClear, mImpl->mGlStateCache.mScissorTestEnabled);
896   if(mask > 0)
897   {
898     auto& gl = *mImpl->mController.GetGL();
899     gl.Clear(mask);
900   }
901 }
902
903 void Context::InvalidateDepthStencilBuffers()
904 {
905   auto& gl = *mImpl->mController.GetGL();
906
907   GLenum attachments[] = {GL_DEPTH, GL_STENCIL};
908   gl.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments);
909 }
910
911 void Context::SetScissorTestEnabled(bool scissorEnabled)
912 {
913   if(mImpl->mGlStateCache.mScissorTestEnabled != scissorEnabled)
914   {
915     mImpl->mGlStateCache.mScissorTestEnabled = scissorEnabled;
916
917     auto& gl = *mImpl->mController.GetGL();
918     if(scissorEnabled)
919     {
920       gl.Enable(GL_SCISSOR_TEST);
921     }
922     else
923     {
924       gl.Disable(GL_SCISSOR_TEST);
925     }
926   }
927 }
928
929 void Context::SetStencilTestEnable(bool stencilEnable)
930 {
931   if(stencilEnable != mImpl->mGlStateCache.mStencilBufferEnabled)
932   {
933     mImpl->mGlStateCache.mStencilBufferEnabled = stencilEnable;
934
935     auto& gl = *mImpl->mController.GetGL();
936     if(stencilEnable)
937     {
938       gl.Enable(GL_STENCIL_TEST);
939     }
940     else
941     {
942       gl.Disable(GL_STENCIL_TEST);
943     }
944   }
945 }
946
947 void Context::StencilMask(uint32_t writeMask)
948 {
949   if(writeMask != mImpl->mGlStateCache.mStencilMask)
950   {
951     mImpl->mGlStateCache.mStencilMask = writeMask;
952
953     auto& gl = *mImpl->mController.GetGL();
954     gl.StencilMask(writeMask);
955   }
956 }
957
958 void Context::StencilFunc(Graphics::CompareOp compareOp,
959                           uint32_t            reference,
960                           uint32_t            compareMask)
961 {
962   if(compareOp != mImpl->mGlStateCache.mStencilFunc ||
963      reference != mImpl->mGlStateCache.mStencilFuncRef ||
964      compareMask != mImpl->mGlStateCache.mStencilFuncMask)
965   {
966     mImpl->mGlStateCache.mStencilFunc     = compareOp;
967     mImpl->mGlStateCache.mStencilFuncRef  = reference;
968     mImpl->mGlStateCache.mStencilFuncMask = compareMask;
969
970     auto& gl = *mImpl->mController.GetGL();
971     gl.StencilFunc(GLCompareOp(compareOp).op, reference, compareMask);
972   }
973 }
974
975 void Context::StencilOp(Graphics::StencilOp failOp,
976                         Graphics::StencilOp depthFailOp,
977                         Graphics::StencilOp passOp)
978 {
979   if(failOp != mImpl->mGlStateCache.mStencilOpFail ||
980      depthFailOp != mImpl->mGlStateCache.mStencilOpDepthFail ||
981      passOp != mImpl->mGlStateCache.mStencilOpDepthPass)
982   {
983     mImpl->mGlStateCache.mStencilOpFail      = failOp;
984     mImpl->mGlStateCache.mStencilOpDepthFail = depthFailOp;
985     mImpl->mGlStateCache.mStencilOpDepthPass = passOp;
986
987     auto& gl = *mImpl->mController.GetGL();
988     gl.StencilOp(GLStencilOp(failOp).op, GLStencilOp(depthFailOp).op, GLStencilOp(passOp).op);
989   }
990 }
991
992 void Context::SetDepthCompareOp(Graphics::CompareOp compareOp)
993 {
994   if(compareOp != mImpl->mGlStateCache.mDepthFunction)
995   {
996     mImpl->mGlStateCache.mDepthFunction = compareOp;
997     auto& gl                            = *mImpl->mController.GetGL();
998     gl.DepthFunc(GLCompareOp(compareOp).op);
999   }
1000 }
1001
1002 void Context::SetDepthTestEnable(bool depthTestEnable)
1003 {
1004   if(depthTestEnable != mImpl->mGlStateCache.mDepthBufferEnabled)
1005   {
1006     mImpl->mGlStateCache.mDepthBufferEnabled = depthTestEnable;
1007
1008     auto& gl = *mImpl->mController.GetGL();
1009     if(depthTestEnable)
1010     {
1011       gl.Enable(GL_DEPTH_TEST);
1012     }
1013     else
1014     {
1015       gl.Disable(GL_DEPTH_TEST);
1016     }
1017   }
1018 }
1019
1020 void Context::SetDepthWriteEnable(bool depthWriteEnable)
1021 {
1022   if(depthWriteEnable != mImpl->mGlStateCache.mDepthMaskEnabled)
1023   {
1024     mImpl->mGlStateCache.mDepthMaskEnabled = depthWriteEnable;
1025
1026     auto& gl = *mImpl->mController.GetGL();
1027     gl.DepthMask(depthWriteEnable);
1028   }
1029 }
1030
1031 void Context::ActiveTexture(uint32_t textureBindingIndex)
1032 {
1033   if(mImpl->mGlStateCache.mActiveTextureUnit != textureBindingIndex)
1034   {
1035     mImpl->mGlStateCache.mActiveTextureUnit = textureBindingIndex;
1036
1037     auto& gl = *mImpl->mController.GetGL();
1038     gl.ActiveTexture(GL_TEXTURE0 + textureBindingIndex);
1039   }
1040 }
1041
1042 void Context::BindTexture(GLenum target, BoundTextureType textureTypeId, uint32_t textureId)
1043 {
1044   uint32_t typeId = static_cast<uint32_t>(textureTypeId);
1045   if(mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] != textureId)
1046   {
1047     mImpl->mGlStateCache.mBoundTextureId[mImpl->mGlStateCache.mActiveTextureUnit][typeId] = textureId;
1048
1049     auto& gl = *mImpl->mController.GetGL();
1050     gl.BindTexture(target, textureId);
1051   }
1052 }
1053
1054 void Context::GenerateMipmap(GLenum target)
1055 {
1056   auto& gl = *mImpl->mController.GetGL();
1057   gl.GenerateMipmap(target);
1058 }
1059
1060 bool Context::BindBuffer(GLenum target, uint32_t bufferId)
1061 {
1062   switch(target)
1063   {
1064     case GL_ARRAY_BUFFER:
1065     {
1066       if(mImpl->mGlStateCache.mBoundArrayBufferId == bufferId)
1067       {
1068         return false;
1069       }
1070       mImpl->mGlStateCache.mBoundArrayBufferId = bufferId;
1071       break;
1072     }
1073     case GL_ELEMENT_ARRAY_BUFFER:
1074     {
1075       if(mImpl->mGlStateCache.mBoundElementArrayBufferId == bufferId)
1076       {
1077         return false;
1078       }
1079       mImpl->mGlStateCache.mBoundElementArrayBufferId = bufferId;
1080       break;
1081     }
1082   }
1083
1084   // Cache miss. Bind buffer.
1085   auto& gl = *mImpl->mController.GetGL();
1086   gl.BindBuffer(target, bufferId);
1087   return true;
1088 }
1089
1090 void Context::DrawBuffers(uint32_t count, const GLenum* buffers)
1091 {
1092   mImpl->mGlStateCache.mFrameBufferStateCache.DrawOperation(mImpl->mGlStateCache.mColorMask,
1093                                                             mImpl->mGlStateCache.DepthBufferWriteEnabled(),
1094                                                             mImpl->mGlStateCache.StencilBufferWriteEnabled());
1095
1096   auto& gl = *mImpl->mController.GetGL();
1097   gl.DrawBuffers(count, buffers);
1098 }
1099
1100 void Context::BindFrameBuffer(GLenum target, uint32_t bufferId)
1101 {
1102   mImpl->mGlStateCache.mFrameBufferStateCache.SetCurrentFrameBuffer(bufferId);
1103
1104   auto& gl = *mImpl->mController.GetGL();
1105   gl.BindFramebuffer(target, bufferId);
1106 }
1107
1108 void Context::GenFramebuffers(uint32_t count, uint32_t* framebuffers)
1109 {
1110   auto& gl = *mImpl->mController.GetGL();
1111   gl.GenFramebuffers(count, framebuffers);
1112
1113   mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersCreated(count, framebuffers);
1114 }
1115
1116 void Context::DeleteFramebuffers(uint32_t count, uint32_t* framebuffers)
1117 {
1118   mImpl->mGlStateCache.mFrameBufferStateCache.FrameBuffersDeleted(count, framebuffers);
1119
1120   auto& gl = *mImpl->mController.GetGL();
1121   gl.DeleteFramebuffers(count, framebuffers);
1122 }
1123
1124 GLStateCache& Context::GetGLStateCache()
1125 {
1126   return mImpl->mGlStateCache;
1127 }
1128
1129 void Context::GlContextCreated()
1130 {
1131   if(!mImpl->mGlContextCreated)
1132   {
1133     mImpl->mGlContextCreated = true;
1134
1135     // Set the initial GL state
1136     mImpl->InitializeGlState();
1137   }
1138 }
1139
1140 void Context::GlContextDestroyed()
1141 {
1142   mImpl->mGlContextCreated = false;
1143 }
1144
1145 void Context::InvalidateCachedPipeline(GLES::Pipeline* pipeline)
1146 {
1147   // Since the pipeline is deleted, invalidate the cached pipeline.
1148   if(mImpl->mCurrentPipeline == &pipeline->GetPipeline())
1149   {
1150     mImpl->mCurrentPipeline = nullptr;
1151   }
1152
1153   // Remove cached VAO map
1154   auto* gl = mImpl->mController.GetGL();
1155   if(gl)
1156   {
1157     const auto* program = pipeline->GetCreateInfo().programState->program;
1158     if(program)
1159     {
1160       const auto* programImpl = static_cast<const GLES::Program*>(program)->GetImplementation();
1161       if(programImpl)
1162       {
1163         auto iter = mImpl->mProgramVAOMap.find(programImpl);
1164         if(iter != mImpl->mProgramVAOMap.end())
1165         {
1166           for(auto& attributeHashPair : iter->second)
1167           {
1168             auto vao = attributeHashPair.second;
1169             gl->DeleteVertexArrays(1, &vao);
1170             if(mImpl->mProgramVAOCurrentState == vao)
1171             {
1172               mImpl->mProgramVAOCurrentState = 0u;
1173             }
1174           }
1175           mImpl->mProgramVAOMap.erase(iter);
1176         }
1177       }
1178     }
1179   }
1180 }
1181
1182 void Context::PrepareForNativeRendering()
1183 {
1184   // this should be pretty much constant
1185   auto display     = eglGetCurrentDisplay();
1186   auto drawSurface = eglGetCurrentSurface(EGL_DRAW);
1187   auto readSurface = eglGetCurrentSurface(EGL_READ);
1188   auto context     = eglGetCurrentContext();
1189
1190   // push the surface and context data to the impl
1191   // It's needed to restore context
1192   if(!mImpl->mCacheEGLGraphicsContext)
1193   {
1194     mImpl->mCacheDrawWriteSurface   = drawSurface;
1195     mImpl->mCacheDrawReadSurface    = readSurface;
1196     mImpl->mCacheEGLGraphicsContext = context;
1197   }
1198
1199   if(!mImpl->mNativeDrawContext)
1200   {
1201     EGLint configId{0u};
1202     eglQueryContext(display, mImpl->mController.GetSharedContext(), EGL_CONFIG_ID, &configId);
1203
1204     EGLint configAttribs[3];
1205     configAttribs[0] = EGL_CONFIG_ID;
1206     configAttribs[1] = configId;
1207     configAttribs[2] = EGL_NONE;
1208
1209     EGLConfig config;
1210     EGLint    numConfigs;
1211     if(eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) != EGL_TRUE)
1212     {
1213       DALI_LOG_ERROR("eglChooseConfig failed!\n");
1214       return;
1215     }
1216
1217     auto version = int(mImpl->mController.GetGLESVersion());
1218
1219     std::vector<EGLint> attribs;
1220     attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
1221     attribs.push_back(version / 10);
1222     attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
1223     attribs.push_back(version % 10);
1224     attribs.push_back(EGL_NONE);
1225
1226     mImpl->mNativeDrawContext = eglCreateContext(display, config, mImpl->mController.GetSharedContext(), attribs.data());
1227     if(mImpl->mNativeDrawContext == EGL_NO_CONTEXT)
1228     {
1229       DALI_LOG_ERROR("eglCreateContext failed!\n");
1230       return;
1231     }
1232   }
1233
1234   eglMakeCurrent(display, drawSurface, readSurface, mImpl->mNativeDrawContext);
1235 }
1236
1237 void Context::RestoreFromNativeRendering()
1238 {
1239   auto display = eglGetCurrentDisplay();
1240
1241   // bring back original context
1242   eglMakeCurrent(display, mImpl->mCacheDrawWriteSurface, mImpl->mCacheDrawReadSurface, mImpl->mCacheEGLGraphicsContext);
1243 }
1244
1245 } // namespace Dali::Graphics::GLES