2 * Copyright (c) 2021 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 <dali/internal/graphics/gles-impl/egl-graphics-controller.h>
21 #include <dali/integration-api/adaptor-framework/render-surface-interface.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/gl-abstraction.h>
24 #include <dali/integration-api/gl-defines.h>
25 #include <dali/internal/graphics/gles-impl/gles-graphics-command-buffer.h>
26 #include <dali/internal/graphics/gles-impl/gles-graphics-pipeline.h>
27 #include <dali/internal/graphics/gles-impl/gles-graphics-render-pass.h>
28 #include <dali/internal/graphics/gles-impl/gles-graphics-render-target.h>
29 #include <dali/internal/graphics/gles-impl/gles-graphics-shader.h>
30 #include <dali/internal/graphics/gles-impl/gles-graphics-texture.h>
31 #include <dali/internal/graphics/gles-impl/gles-graphics-types.h>
32 #include <dali/internal/graphics/gles-impl/gles3-graphics-memory.h>
33 #include <dali/public-api/common/dali-common.h>
34 #include "gles-graphics-program.h"
36 // Uncomment the following define to turn on frame dumping
37 //#define ENABLE_COMMAND_BUFFER_FRAME_DUMP 1
38 #include <dali/internal/graphics/gles-impl/egl-graphics-controller-debug.h>
41 namespace Dali::Graphics
46 * @brief Custom deleter for all Graphics objects created
47 * with use of the Controller.
49 * When Graphics object dies the unique pointer (Graphics::UniquePtr)
50 * doesn't destroy it directly but passes the ownership back
51 * to the Controller. The GLESDeleter is responsible for passing
52 * the object to the discard queue (by calling Resource::DiscardResource()).
57 GLESDeleter() = default;
59 void operator()(T* object)
61 // Discard resource (add it to discard queue)
62 object->DiscardResource();
67 * @brief Helper function allocating graphics object
69 * @param[in] info Create info structure
70 * @param[in] controller Controller object
71 * @param[out] out Unique pointer to the return object
73 template<class GLESType, class GfxCreateInfo, class T>
74 auto NewObject(const GfxCreateInfo& info, EglGraphicsController& controller, T&& oldObject)
77 using Type = typename T::element_type;
78 using UPtr = Graphics::UniquePtr<Type>;
79 if(info.allocationCallbacks)
81 auto* memory = info.allocationCallbacks->allocCallback(
84 info.allocationCallbacks->userData);
85 return UPtr(new(memory) GLESType(info, controller), GLESDeleter<GLESType>());
87 else // Use standard allocator
89 return UPtr(new GLESType(info, controller), GLESDeleter<GLESType>());
93 template<class T0, class T1>
94 T0* CastObject(T1* apiObject)
96 return static_cast<T0*>(apiObject);
99 // Maximum size of texture upload buffer.
100 const uint32_t TEXTURE_UPLOAD_MAX_BUFER_SIZE_MB = 1;
104 EglGraphicsController::~EglGraphicsController() = default;
106 void EglGraphicsController::InitializeGLES(Integration::GlAbstraction& glAbstraction)
108 DALI_LOG_RELEASE_INFO("Initializing New Graphics Controller #1\n");
109 mGlAbstraction = &glAbstraction;
110 mContext = std::make_unique<GLES::Context>(*this);
111 mCurrentContext = mContext.get();
114 void EglGraphicsController::Initialize(Integration::GlSyncAbstraction& glSyncAbstraction,
115 Integration::GlContextHelperAbstraction& glContextHelperAbstraction,
116 Internal::Adaptor::GraphicsInterface& graphicsInterface)
118 DALI_LOG_RELEASE_INFO("Initializing New Graphics Controller #2\n");
119 mGlSyncAbstraction = &glSyncAbstraction;
120 mGlContextHelperAbstraction = &glContextHelperAbstraction;
121 mGraphics = &graphicsInterface;
124 void EglGraphicsController::SubmitCommandBuffers(const SubmitInfo& submitInfo)
126 for(auto& cmdbuf : submitInfo.cmdBuffer)
128 // Push command buffers
129 mCommandQueue.push(static_cast<GLES::CommandBuffer*>(cmdbuf));
132 // If flush bit set, flush all pending tasks
133 if(submitInfo.flags & (0 | SubmitFlagBits::FLUSH))
139 void EglGraphicsController::PresentRenderTarget(RenderTarget* renderTarget)
141 // Use command buffer to execute presentation (we should pool it)
142 CommandBufferCreateInfo info;
143 info.SetLevel(CommandBufferLevel::PRIMARY);
144 info.fixedCapacity = 1; // only one command
145 auto presentCommandBuffer = new GLES::CommandBuffer(info, *this);
146 presentCommandBuffer->PresentRenderTarget(static_cast<GLES::RenderTarget*>(renderTarget));
147 SubmitInfo submitInfo;
148 submitInfo.cmdBuffer = {presentCommandBuffer};
149 submitInfo.flags = 0 | SubmitFlagBits::FLUSH;
150 SubmitCommandBuffers(submitInfo);
153 void EglGraphicsController::ResolvePresentRenderTarget(GLES::RenderTarget* renderTarget)
155 auto* rt = static_cast<GLES::RenderTarget*>(renderTarget);
156 if(rt->GetCreateInfo().surface)
158 auto* surfaceInterface = reinterpret_cast<Dali::RenderSurfaceInterface*>(rt->GetCreateInfo().surface);
159 surfaceInterface->MakeContextCurrent();
160 surfaceInterface->PostRender();
164 Integration::GlAbstraction& EglGraphicsController::GetGlAbstraction()
166 DALI_ASSERT_DEBUG(mGlAbstraction && "Graphics controller not initialized");
167 return *mGlAbstraction;
170 Integration::GlSyncAbstraction& EglGraphicsController::GetGlSyncAbstraction()
172 DALI_ASSERT_DEBUG(mGlSyncAbstraction && "Graphics controller not initialized");
173 return *mGlSyncAbstraction;
176 Integration::GlContextHelperAbstraction& EglGraphicsController::GetGlContextHelperAbstraction()
178 DALI_ASSERT_DEBUG(mGlContextHelperAbstraction && "Graphics controller not initialized");
179 return *mGlContextHelperAbstraction;
182 Graphics::UniquePtr<CommandBuffer> EglGraphicsController::CreateCommandBuffer(
183 const CommandBufferCreateInfo& commandBufferCreateInfo,
184 Graphics::UniquePtr<CommandBuffer>&& oldCommandBuffer)
186 return NewObject<GLES::CommandBuffer>(commandBufferCreateInfo, *this, std::move(oldCommandBuffer));
189 Graphics::UniquePtr<RenderPass> EglGraphicsController::CreateRenderPass(const RenderPassCreateInfo& renderPassCreateInfo, Graphics::UniquePtr<RenderPass>&& oldRenderPass)
191 return NewObject<GLES::RenderPass>(renderPassCreateInfo, *this, std::move(oldRenderPass));
194 Graphics::UniquePtr<Texture>
195 EglGraphicsController::CreateTexture(const TextureCreateInfo& textureCreateInfo, Graphics::UniquePtr<Texture>&& oldTexture)
197 return NewObject<GLES::Texture>(textureCreateInfo, *this, std::move(oldTexture));
200 Graphics::UniquePtr<Buffer> EglGraphicsController::CreateBuffer(
201 const BufferCreateInfo& bufferCreateInfo, Graphics::UniquePtr<Buffer>&& oldBuffer)
203 return NewObject<GLES::Buffer>(bufferCreateInfo, *this, std::move(oldBuffer));
206 Graphics::UniquePtr<Framebuffer> EglGraphicsController::CreateFramebuffer(
207 const FramebufferCreateInfo& framebufferCreateInfo, Graphics::UniquePtr<Framebuffer>&& oldFramebuffer)
209 return NewObject<GLES::Framebuffer>(framebufferCreateInfo, *this, std::move(oldFramebuffer));
212 Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(
213 const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
215 // Create pipeline cache if needed
218 mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
221 return mPipelineCache->GetPipeline(pipelineCreateInfo, std::move(oldPipeline));
224 Graphics::UniquePtr<Program> EglGraphicsController::CreateProgram(
225 const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram)
227 // Create program cache if needed
230 mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
233 return mPipelineCache->GetProgram(programCreateInfo, std::move(oldProgram));
236 Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
238 return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
241 Graphics::UniquePtr<Sampler> EglGraphicsController::CreateSampler(const SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr<Sampler>&& oldSampler)
243 return NewObject<GLES::Sampler>(samplerCreateInfo, *this, std::move(oldSampler));
246 Graphics::UniquePtr<RenderTarget> EglGraphicsController::CreateRenderTarget(const RenderTargetCreateInfo& renderTargetCreateInfo, Graphics::UniquePtr<RenderTarget>&& oldRenderTarget)
248 return NewObject<GLES::RenderTarget>(renderTargetCreateInfo, *this, std::move(oldRenderTarget));
251 const Graphics::Reflection& EglGraphicsController::GetProgramReflection(const Graphics::Program& program)
253 return static_cast<const Graphics::GLES::Program*>(&program)->GetReflection();
256 void EglGraphicsController::CreateSurfaceContext(Dali::RenderSurfaceInterface* surface)
258 std::unique_ptr<GLES::Context> context = std::make_unique<GLES::Context>(*this);
259 mSurfaceContexts.push_back(std::move(std::make_pair(surface, std::move(context))));
262 void EglGraphicsController::DeleteSurfaceContext(Dali::RenderSurfaceInterface* surface)
264 mSurfaceContexts.erase(std::remove_if(
265 mSurfaceContexts.begin(), mSurfaceContexts.end(), [surface](SurfaceContextPair& iter) { return surface == iter.first; }),
266 mSurfaceContexts.end());
269 void EglGraphicsController::ActivateResourceContext()
271 mCurrentContext = mContext.get();
274 void EglGraphicsController::ActivateSurfaceContext(Dali::RenderSurfaceInterface* surface)
276 if(surface && mGraphics->IsResourceContextSupported())
278 auto iter = std::find_if(mSurfaceContexts.begin(), mSurfaceContexts.end(), [surface](SurfaceContextPair& iter) {
279 return (iter.first == surface);
282 if(iter != mSurfaceContexts.end())
284 mCurrentContext = iter->second.get();
289 void EglGraphicsController::AddTexture(GLES::Texture& texture)
291 // Assuming we are on the correct context
292 mCreateTextureQueue.push(&texture);
295 void EglGraphicsController::AddBuffer(GLES::Buffer& buffer)
297 // Assuming we are on the correct context
298 mCreateBufferQueue.push(&buffer);
301 void EglGraphicsController::AddFramebuffer(GLES::Framebuffer& framebuffer)
303 // Assuming we are on the correct context
304 mCreateFramebufferQueue.push(&framebuffer);
307 void EglGraphicsController::ProcessDiscardQueues()
310 ProcessDiscardQueue<GLES::Texture>(mDiscardTextureQueue);
313 ProcessDiscardQueue<GLES::Buffer>(mDiscardBufferQueue);
315 // Process Framebuffers
316 ProcessDiscardQueue<GLES::Framebuffer>(mDiscardFramebufferQueue);
319 ProcessDiscardQueue<GLES::Pipeline>(mDiscardPipelineQueue);
322 ProcessDiscardQueue<GLES::Program>(mDiscardProgramQueue);
325 ProcessDiscardQueue<GLES::Shader>(mDiscardShaderQueue);
328 ProcessDiscardQueue<GLES::Sampler>(mDiscardSamplerQueue);
330 // Process command buffers
331 ProcessDiscardQueue<GLES::CommandBuffer>(mDiscardCommandBufferQueue);
334 void EglGraphicsController::ProcessCreateQueues()
337 ProcessCreateQueue(mCreateTextureQueue);
340 ProcessCreateQueue(mCreateBufferQueue);
342 // Process framebuffers
343 ProcessCreateQueue(mCreateFramebufferQueue);
346 void EglGraphicsController::ProcessCommandBuffer(const GLES::CommandBuffer& commandBuffer)
348 for(auto& cmd : commandBuffer.GetCommands())
353 case GLES::CommandType::FLUSH:
355 // Nothing to do here
358 case GLES::CommandType::BIND_TEXTURES:
360 mCurrentContext->BindTextures(cmd.bindTextures.textureBindings);
363 case GLES::CommandType::BIND_VERTEX_BUFFERS:
365 auto& bindings = cmd.bindVertexBuffers.vertexBufferBindings;
366 mCurrentContext->BindVertexBuffers(bindings);
369 case GLES::CommandType::BIND_UNIFORM_BUFFER:
371 auto& bindings = cmd.bindUniformBuffers;
372 mCurrentContext->BindUniformBuffers(bindings.uniformBufferBindings, bindings.standaloneUniformsBufferBinding);
375 case GLES::CommandType::BIND_INDEX_BUFFER:
377 mCurrentContext->BindIndexBuffer(cmd.bindIndexBuffer);
380 case GLES::CommandType::BIND_SAMPLERS:
384 case GLES::CommandType::BIND_PIPELINE:
386 auto pipeline = static_cast<const GLES::Pipeline*>(cmd.bindPipeline.pipeline);
387 mCurrentContext->BindPipeline(pipeline);
390 case GLES::CommandType::DRAW:
392 mCurrentContext->Flush(false, cmd.draw);
395 case GLES::CommandType::DRAW_INDEXED:
397 mCurrentContext->Flush(false, cmd.draw);
400 case GLES::CommandType::DRAW_INDEXED_INDIRECT:
402 mCurrentContext->Flush(false, cmd.draw);
405 case GLES::CommandType::SET_SCISSOR: // @todo Consider correcting for orientation here?
407 mGlAbstraction->Scissor(cmd.scissor.region.x, cmd.scissor.region.y, cmd.scissor.region.width, cmd.scissor.region.height);
410 case GLES::CommandType::SET_SCISSOR_TEST:
412 if(cmd.scissorTest.enable)
414 mGlAbstraction->Enable(GL_SCISSOR_TEST);
418 mGlAbstraction->Disable(GL_SCISSOR_TEST);
422 case GLES::CommandType::SET_VIEWPORT: // @todo Consider correcting for orientation here?
424 mGlAbstraction->Viewport(cmd.viewport.region.x, cmd.viewport.region.y, cmd.viewport.region.width, cmd.viewport.region.height);
428 case GLES::CommandType::SET_COLOR_MASK:
430 mCurrentContext->ColorMask(cmd.colorMask.enabled);
433 case GLES::CommandType::CLEAR_STENCIL_BUFFER:
435 mCurrentContext->ClearStencilBuffer();
438 case GLES::CommandType::CLEAR_DEPTH_BUFFER:
440 mCurrentContext->ClearDepthBuffer();
444 case GLES::CommandType::SET_STENCIL_TEST_ENABLE:
446 mCurrentContext->SetStencilTestEnable(cmd.stencilTest.enabled);
450 case GLES::CommandType::SET_STENCIL_FUNC:
452 mCurrentContext->StencilFunc(cmd.stencilFunc.compareOp,
453 cmd.stencilFunc.reference,
454 cmd.stencilFunc.compareMask);
458 case GLES::CommandType::SET_STENCIL_WRITE_MASK:
460 mCurrentContext->StencilMask(cmd.stencilWriteMask.mask);
464 case GLES::CommandType::SET_STENCIL_OP:
466 mCurrentContext->StencilOp(cmd.stencilOp.failOp,
467 cmd.stencilOp.depthFailOp,
468 cmd.stencilOp.passOp);
472 case GLES::CommandType::SET_DEPTH_COMPARE_OP:
474 mCurrentContext->SetDepthCompareOp(cmd.depth.compareOp);
477 case GLES::CommandType::SET_DEPTH_TEST_ENABLE:
479 mCurrentContext->SetDepthTestEnable(cmd.depth.testEnabled);
482 case GLES::CommandType::SET_DEPTH_WRITE_ENABLE:
484 mCurrentContext->SetDepthWriteEnable(cmd.depth.writeEnabled);
488 case GLES::CommandType::BEGIN_RENDERPASS:
490 auto& renderTarget = *cmd.beginRenderPass.renderTarget;
491 const auto& targetInfo = renderTarget.GetCreateInfo();
493 if(targetInfo.surface)
495 // switch to surface context
496 mGraphics->ActivateSurfaceContext(static_cast<Dali::RenderSurfaceInterface*>(targetInfo.surface));
498 else if(targetInfo.framebuffer)
500 // switch to resource context
501 mGraphics->ActivateResourceContext();
504 mCurrentContext->BeginRenderPass(cmd.beginRenderPass);
507 case GLES::CommandType::END_RENDERPASS:
509 mCurrentContext->EndRenderPass();
512 case GLES::CommandType::PRESENT_RENDER_TARGET:
514 ResolvePresentRenderTarget(cmd.presentRenderTarget.targetToPresent);
516 // push this command buffer to the discard queue
517 mDiscardCommandBufferQueue.push(const_cast<GLES::CommandBuffer*>(&commandBuffer));
520 case GLES::CommandType::EXECUTE_COMMAND_BUFFERS:
522 // Process secondary command buffers
523 // todo: check validity of the secondaries
524 // there are operations which are illigal to be done
525 // within secondaries.
526 for(auto& buf : cmd.executeCommandBuffers.buffers)
528 ProcessCommandBuffer(*static_cast<const GLES::CommandBuffer*>(buf));
536 void EglGraphicsController::ProcessCommandQueues()
538 // TODO: command queue per context, sync between queues should be
540 currentFramebuffer = nullptr;
544 while(!mCommandQueue.empty())
546 auto cmdBuf = mCommandQueue.front();
548 DUMP_FRAME_COMMAND_BUFFER(cmdBuf);
549 ProcessCommandBuffer(*cmdBuf);
555 void EglGraphicsController::ProcessTextureUpdateQueue()
557 while(!mTextureUpdateRequests.empty())
559 TextureUpdateRequest& request = mTextureUpdateRequests.front();
561 auto& info = request.first;
562 auto& source = request.second;
564 if(source.sourceType == Graphics::TextureUpdateSourceInfo::Type::MEMORY)
566 // GPU memory must be already allocated.
568 // Check if it needs conversion
569 auto* texture = static_cast<GLES::Texture*>(info.dstTexture);
570 const auto& createInfo = texture->GetCreateInfo();
571 auto srcFormat = GLES::GLTextureFormatType(info.srcFormat).format;
572 auto srcType = GLES::GLTextureFormatType(info.srcFormat).type;
573 auto destInternalFormat = GLES::GLTextureFormatType(createInfo.format).internalFormat;
574 auto destFormat = GLES::GLTextureFormatType(createInfo.format).format;
576 // From render-texture.cpp
577 const bool isSubImage(info.dstOffset2D.x != 0 || info.dstOffset2D.y != 0 ||
578 info.srcExtent2D.width != (createInfo.size.width / (1 << info.level)) ||
579 info.srcExtent2D.height != (createInfo.size.height / (1 << info.level)));
581 auto* sourceBuffer = reinterpret_cast<uint8_t*>(source.memorySource.memory);
582 std::vector<uint8_t> tempBuffer;
583 if(mGlAbstraction->TextureRequiresConverting(srcFormat, destFormat, isSubImage))
585 // Convert RGB to RGBA if necessary.
586 texture->TryConvertPixelData(source.memorySource.memory, info.srcFormat, createInfo.format, info.srcSize, info.srcExtent2D.width, info.srcExtent2D.height, tempBuffer);
587 sourceBuffer = &tempBuffer[0];
588 srcFormat = destFormat;
589 srcType = GLES::GLTextureFormatType(createInfo.format).type;
592 // Calculate the maximum mipmap level for the texture
593 texture->SetMaxMipMapLevel(std::max(texture->GetMaxMipMapLevel(), info.level));
595 GLenum bindTarget{GL_TEXTURE_2D};
596 GLenum target{GL_TEXTURE_2D};
598 if(createInfo.textureType == Graphics::TextureType::TEXTURE_CUBEMAP)
600 bindTarget = GL_TEXTURE_CUBE_MAP;
601 target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + info.layer;
604 mGlAbstraction->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
605 mGlAbstraction->BindTexture(bindTarget, texture->GetGLTexture());
609 mGlAbstraction->TexImage2D(target,
612 info.srcExtent2D.width,
613 info.srcExtent2D.height,
621 mGlAbstraction->TexSubImage2D(target,
625 info.srcExtent2D.width,
626 info.srcExtent2D.height,
631 // free staging memory
632 free(source.memorySource.memory);
636 // TODO: other sources
639 mTextureUpdateRequests.pop();
643 void EglGraphicsController::UpdateTextures(const std::vector<TextureUpdateInfo>& updateInfoList,
644 const std::vector<TextureUpdateSourceInfo>& sourceList)
647 for(auto& info : updateInfoList)
649 mTextureUpdateRequests.push(std::make_pair(info, sourceList[info.srcReference]));
650 auto& pair = mTextureUpdateRequests.back();
651 switch(pair.second.sourceType)
653 case Graphics::TextureUpdateSourceInfo::Type::MEMORY:
655 auto& info = pair.first;
656 auto& source = pair.second;
658 // allocate staging memory and copy the data
659 // TODO: using PBO with GLES3, this is just naive
662 char* stagingBuffer = reinterpret_cast<char*>(malloc(info.srcSize));
663 std::copy(&reinterpret_cast<char*>(source.memorySource.memory)[info.srcOffset],
664 reinterpret_cast<char*>(source.memorySource.memory) + info.srcSize,
667 mTextureUploadTotalCPUMemoryUsed += info.srcSize;
669 // store staging buffer
670 source.memorySource.memory = stagingBuffer;
673 case Graphics::TextureUpdateSourceInfo::Type::BUFFER:
675 // TODO, with PBO support
678 case Graphics::TextureUpdateSourceInfo::Type::TEXTURE:
680 // TODO texture 2 texture in-GPU copy
686 // If upload buffer exceeds maximum size, flush.
687 if(mTextureUploadTotalCPUMemoryUsed > TEXTURE_UPLOAD_MAX_BUFER_SIZE_MB * 1024)
690 mTextureUploadTotalCPUMemoryUsed = 0;
694 void EglGraphicsController::ProcessTextureMipmapGenerationQueue()
696 while(!mTextureMipmapGenerationRequests.empty())
698 auto* texture = mTextureMipmapGenerationRequests.front();
700 mGlAbstraction->BindTexture(texture->GetGlTarget(), texture->GetGLTexture());
701 mGlAbstraction->GenerateMipmap(texture->GetGlTarget());
703 mTextureMipmapGenerationRequests.pop();
707 void EglGraphicsController::GenerateTextureMipmaps(const Graphics::Texture& texture)
709 mTextureMipmapGenerationRequests.push(static_cast<const GLES::Texture*>(&texture));
712 Graphics::UniquePtr<Memory> EglGraphicsController::MapBufferRange(const MapBufferInfo& mapInfo)
714 mGraphics->ActivateResourceContext();
716 // Mapping buffer requires the object to be created NOW
717 // Workaround - flush now, otherwise there will be given a staging buffer
718 // in case when the buffer is not there yet
719 ProcessCreateQueues();
721 if(GetGLESVersion() < GLES::GLESVersion::GLES_30)
723 return Graphics::UniquePtr<Memory>(new GLES::Memory2(mapInfo, *this));
727 return Graphics::UniquePtr<Memory>(new GLES::Memory3(mapInfo, *this));
731 bool EglGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
733 return static_cast<GLES::Program*>(&program)->GetImplementation()->GetParameter(parameterId, outData);
736 GLES::PipelineCache& EglGraphicsController::GetPipelineCache() const
738 return *mPipelineCache;
741 } // namespace Dali::Graphics