Remove annoying log message
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-reflection.cpp
1 /*
2  * Copyright (c) 2022 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-graphics-reflection.h"
19
20 #include <dali/integration-api/debug.h>
21 #include <dali/integration-api/gl-abstraction.h>
22 #include <dali/integration-api/gl-defines.h>
23
24 #include <vector>
25 #include "egl-graphics-controller.h"
26
27 #include <GLES3/gl3.h>
28 #include <GLES3/gl31.h>
29
30 #include "gles-graphics-program.h"
31
32 #include <iostream>
33
34 namespace
35 {
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gGraphicsReflectionLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GRAPHICS_REFLECTION");
38 #endif
39
40 struct StringSize
41 {
42   const char* const mString;
43   const uint32_t    mLength;
44
45   template<uint32_t kLength>
46   constexpr StringSize(const char (&string)[kLength])
47   : mString(string),
48     mLength(kLength - 1) // remove terminating null; N.B. there should be no other null.
49   {
50   }
51
52   operator const char*() const
53   {
54     return mString;
55   }
56 };
57
58 bool operator==(const StringSize& lhs, const char* rhs)
59 {
60   return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
61 }
62
63 const char* const    DELIMITERS = " \t\n";
64 constexpr StringSize UNIFORM{"uniform"};
65 constexpr StringSize SAMPLER_PREFIX{"sampler"};
66 constexpr StringSize SAMPLER_TYPES[]   = {"2D", "Cube", "ExternalOES"};
67 constexpr auto       END_SAMPLER_TYPES = SAMPLER_TYPES + std::extent<decltype(SAMPLER_TYPES)>::value;
68
69 Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeTypeFormat(GLenum type)
70 {
71   switch(type)
72   {
73     case GL_FLOAT:
74       return Dali::Graphics::VertexInputAttributeFormat::FLOAT;
75     case GL_FLOAT_VEC2:
76       return Dali::Graphics::VertexInputAttributeFormat::VEC2;
77     case GL_FLOAT_VEC3:
78       return Dali::Graphics::VertexInputAttributeFormat::VEC3;
79     case GL_FLOAT_VEC4:
80       return Dali::Graphics::VertexInputAttributeFormat::VEC4;
81     case GL_INT:
82       return Dali::Graphics::VertexInputAttributeFormat::INTEGER;
83     default:
84       return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
85   }
86 }
87
88 uint32_t GetGLDataTypeSize(GLenum type)
89 {
90   // There are many more types than what are covered here, but
91   // they are not supported in dali.
92   switch(type)
93   {
94     case GL_FLOAT: // "float", 1 float, 4 bytes
95       return 4;
96     case GL_FLOAT_VEC2: // "vec2", 2 floats, 8 bytes
97       return 8;
98     case GL_FLOAT_VEC3: // "vec3", 3 floats, 12 bytes
99       return 12;
100     case GL_FLOAT_VEC4: // "vec4", 4 floats, 16 bytes
101       return 16;
102     case GL_INT: // "int", 1 integer, 4 bytes
103       return 4;
104     case GL_FLOAT_MAT2: // "mat2", 4 floats, 16 bytes
105       return 16;
106     case GL_FLOAT_MAT3: // "mat3", 3 vec3, 36 bytes
107       return 36;
108     case GL_FLOAT_MAT4: // "mat4", 4 vec4, 64 bytes
109       return 64;
110     default:
111       return 0;
112   }
113 }
114
115 bool IsSampler(GLenum type)
116 {
117   return type == GL_SAMPLER_2D || type == GL_SAMPLER_3D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES;
118 }
119
120 bool SortUniformInfoByLocation(Dali::Graphics::UniformInfo a, Dali::Graphics::UniformInfo b)
121 {
122   return a.location < b.location;
123 }
124
125 bool SortUniformExtraInfoByLocation(Dali::Graphics::GLES::Reflection::UniformExtraInfo a, Dali::Graphics::GLES::Reflection::UniformExtraInfo b)
126 {
127   return a.location < b.location;
128 }
129
130 std::string GetShaderSource(Dali::Graphics::ShaderState shaderState)
131 {
132   std::vector<uint8_t> data;
133   auto*                shader           = static_cast<const Dali::Graphics::GLES::Shader*>(shaderState.shader);
134   auto&                shaderCreateInfo = shader->GetCreateInfo();
135   data.resize(shaderCreateInfo.sourceSize + 1);
136   std::memcpy(&data[0], shaderCreateInfo.sourceData, shaderCreateInfo.sourceSize);
137   data[shaderCreateInfo.sourceSize] = 0;
138
139   return std::string(reinterpret_cast<char*>(&data[0]));
140 }
141
142 void ParseShaderSamplers(std::string shaderSource, std::vector<Dali::Graphics::UniformInfo>& uniformOpaques, int& samplerPosition, std::vector<int>& samplerPositions)
143 {
144   if(!shaderSource.empty())
145   {
146     char* shaderStr = strdup(shaderSource.c_str());
147     char* uniform   = strstr(shaderStr, UNIFORM);
148
149     while(uniform)
150     {
151       char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
152
153       char* nextPtr = nullptr;
154       char* token   = strtok_r(outerToken, DELIMITERS, &nextPtr);
155       while(token)
156       {
157         if(SAMPLER_PREFIX == token)
158         {
159           token += SAMPLER_PREFIX.mLength;
160           if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
161           {
162             bool found(false);
163             token = strtok_r(nullptr, DELIMITERS, &nextPtr);
164
165             for(uint32_t i = 0; i < static_cast<uint32_t>(uniformOpaques.size()); ++i)
166             {
167               if(samplerPositions[i] == -1 &&
168                  strncmp(token, uniformOpaques[i].name.c_str(), uniformOpaques[i].name.size()) == 0)
169               {
170                 samplerPositions[i] = uniformOpaques[i].offset = samplerPosition++;
171                 found                                          = true;
172                 break;
173               }
174             }
175
176             if(!found)
177             {
178               DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Sampler uniform %s declared but not used in the shader\n", token);
179             }
180             break;
181           }
182         }
183
184         token = strtok_r(nullptr, DELIMITERS, &nextPtr);
185       }
186
187       uniform = strstr(uniform, UNIFORM);
188     }
189     free(shaderStr);
190   }
191 }
192
193 } // anonymous namespace
194
195 namespace Dali::Graphics::GLES
196 {
197 Reflection::Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller)
198 : Graphics::Reflection(),
199   mController(controller),
200   mProgram(program)
201 {
202 }
203
204 Reflection::~Reflection() = default;
205
206 void Reflection::BuildVertexAttributeReflection()
207 {
208   auto glProgram = mProgram.GetGlProgram();
209
210   int    written, size, location, maxLength, nAttribs;
211   GLenum type;
212   char*  name;
213
214   auto gl = mController.GetGL();
215   if(!gl)
216   {
217     // Do nothing during shutdown
218     return;
219   }
220
221   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build vertex attribute reflection for glProgram : %u\n", glProgram);
222
223   gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
224   gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
225
226   mVertexInputAttributes.clear();
227   mVertexInputAttributes.resize(nAttribs);
228
229   name = new GLchar[maxLength];
230   for(int i = 0; i < nAttribs; i++)
231   {
232     gl->GetActiveAttrib(glProgram, i, maxLength, &written, &size, &type, name);
233     location = gl->GetAttribLocation(glProgram, name);
234
235     if(location >= 0)
236     {
237       AttributeInfo attributeInfo;
238       attributeInfo.location = location;
239       attributeInfo.name     = name;
240       attributeInfo.format   = GetVertexAttributeTypeFormat(type);
241       mVertexInputAttributes.insert(mVertexInputAttributes.begin() + location, attributeInfo);
242     }
243   }
244   delete[] name;
245 }
246
247 void Reflection::BuildUniformReflection()
248 {
249   auto glProgram = mProgram.GetGlProgram();
250
251   int   maxLen;
252   char* name;
253
254   int numUniforms = 0;
255
256   auto gl = mController.GetGL();
257   if(!gl)
258   {
259     // Do nothing during shutdown
260     return;
261   }
262
263   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform reflection for glProgram : %u\n", glProgram);
264
265   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
266   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
267
268   mUniformBlocks.clear();
269   mDefaultUniformBlock.members.clear();
270   mUniformOpaques.clear();
271
272   name = new char[maxLen];
273
274   mStandaloneUniformExtraInfos.clear();
275
276   for(int i = 0; i < numUniforms; ++i)
277   {
278     int    elementCount;
279     GLenum type;
280     int    written;
281     gl->GetActiveUniform(glProgram, i, maxLen, &written, &elementCount, &type, name);
282     int location = gl->GetUniformLocation(glProgram, name);
283
284     Dali::Graphics::UniformInfo uniformInfo;
285
286     uniformInfo.name = name;
287     if(elementCount > 1)
288     {
289       auto iter = std::string(uniformInfo.name).find("[", 0);
290       if(iter != std::string::npos)
291       {
292         uniformInfo.name = std::string(name).substr(0, iter);
293       }
294     }
295
296     uniformInfo.uniformClass = IsSampler(type) ? Dali::Graphics::UniformClass::COMBINED_IMAGE_SAMPLER : Dali::Graphics::UniformClass::UNIFORM;
297     uniformInfo.location     = location; //IsSampler(type) ? 0 : location;
298     uniformInfo.binding      = 0;        // IsSampler(type) ? location : 0;
299     uniformInfo.bufferIndex  = 0;
300     uniformInfo.offset       = 0;
301
302     if(IsSampler(type))
303     {
304       mUniformOpaques.push_back(uniformInfo);
305     }
306     else
307     {
308       mDefaultUniformBlock.members.push_back(uniformInfo);
309       mStandaloneUniformExtraInfos.emplace_back(location, GetGLDataTypeSize(type), uniformInfo.offset, elementCount, type);
310     }
311   }
312
313   // Re-order according to uniform locations.
314
315   if(mDefaultUniformBlock.members.size() > 1)
316   {
317     std::sort(mDefaultUniformBlock.members.begin(), mDefaultUniformBlock.members.end(), SortUniformInfoByLocation);
318     std::sort(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), SortUniformExtraInfoByLocation);
319   }
320
321   if(mUniformOpaques.size() > 1)
322   {
323     SortOpaques();
324   }
325
326   // Calculate the uniform offset
327   for(unsigned int i = 0; i < mDefaultUniformBlock.members.size(); ++i)
328   {
329     if(i == 0)
330     {
331       mDefaultUniformBlock.members[i].offset = 0;
332     }
333     else
334     {
335       uint32_t previousUniformLocation = mDefaultUniformBlock.members[i - 1].location;
336       auto     previousUniform         = std::find_if(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), [&previousUniformLocation](const UniformExtraInfo& iter) { return iter.location == previousUniformLocation; });
337       if(previousUniform != mStandaloneUniformExtraInfos.end())
338       {
339         mDefaultUniformBlock.members[i].offset = mDefaultUniformBlock.members[i - 1].offset + (previousUniform->size * previousUniform->arraySize);
340         mStandaloneUniformExtraInfos[i].offset = mDefaultUniformBlock.members[i].offset;
341       }
342     }
343   }
344
345   if(mDefaultUniformBlock.members.size() > 0)
346   {
347     uint32_t lastUniformLocation = mDefaultUniformBlock.members.back().location;
348     auto     lastUniform         = std::find_if(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), [&lastUniformLocation](const UniformExtraInfo& iter) { return iter.location == lastUniformLocation; });
349     if(lastUniform != mStandaloneUniformExtraInfos.end())
350     {
351       mDefaultUniformBlock.size = mDefaultUniformBlock.members.back().offset + (lastUniform->size * lastUniform->arraySize);
352       mUniformBlocks.push_back(mDefaultUniformBlock);
353     }
354   }
355   else
356   {
357     mDefaultUniformBlock.size = 0;
358   }
359
360   delete[] name;
361 }
362
363 // TODO: Maybe this is not needed if uniform block is not support by dali shaders?
364 void Reflection::BuildUniformBlockReflection()
365 {
366   auto gl               = mController.GetGL();
367   auto glProgram        = mProgram.GetGlProgram();
368   int  numUniformBlocks = 0;
369
370   if(!gl)
371   {
372     // Do nothing during shutdown
373     return;
374   }
375
376   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform block reflection for glProgram : %u\n", glProgram);
377
378   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
379
380   mUniformBlocks.clear();
381   mUniformBlocks.resize(numUniformBlocks);
382
383   int uniformBlockMaxLength = 0;
384   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
385
386   char* uniformBlockName = new char[uniformBlockMaxLength];
387   for(int i = 0; i < numUniformBlocks; i++)
388   {
389     int length;
390     int blockBinding;
391     int blockDataSize;
392     gl->GetActiveUniformBlockName(glProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
393     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
394     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
395
396     Dali::Graphics::UniformBlockInfo uniformBlockInfo;
397     uniformBlockInfo.name    = uniformBlockName;
398     uniformBlockInfo.size    = blockDataSize;
399     uniformBlockInfo.binding = blockBinding;
400
401     int nUnis;
402     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
403     int* unifIndexes = new GLint[nUnis];
404     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
405     char* uniformName{};
406     int   maxUniLen;
407     gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
408
409     for(int unif = 0; unif < nUnis; ++unif)
410     {
411       int    uniIndex = unifIndexes[unif];
412       int    size;
413       GLenum type;
414
415       gl->GetActiveUniform(glProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
416       int location = gl->GetUniformLocation(glProgram, uniformName);
417
418       Dali::Graphics::UniformInfo uniform;
419       uniform.name     = uniformName;
420       uniform.location = location;
421       uniformBlockInfo.members.push_back(uniform);
422     }
423
424     delete[] unifIndexes;
425
426     mUniformBlocks.push_back(uniformBlockInfo);
427   }
428   delete[] uniformBlockName;
429 }
430
431 uint32_t Reflection::GetVertexAttributeLocation(const std::string& name) const
432 {
433   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "name : %s\n", name.c_str());
434   for(auto&& attr : mVertexInputAttributes)
435   {
436     if(attr.name == name)
437     {
438       return attr.location;
439     }
440   }
441   return ERROR_ATTRIBUTE_NOT_FOUND;
442 }
443
444 Dali::Graphics::VertexInputAttributeFormat Reflection::GetVertexAttributeFormat(uint32_t location) const
445 {
446   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
447   if(location >= mVertexInputAttributes.size())
448   {
449     return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
450   }
451
452   return mVertexInputAttributes[location].format;
453 }
454
455 std::string Reflection::GetVertexAttributeName(uint32_t location) const
456 {
457   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
458   if(location >= mVertexInputAttributes.size())
459   {
460     return std::string();
461   }
462
463   return mVertexInputAttributes[location].name;
464 }
465
466 std::vector<uint32_t> Reflection::GetVertexAttributeLocations() const
467 {
468   std::vector<uint32_t> locations;
469   for(auto&& attr : mVertexInputAttributes)
470   {
471     if(attr.format != Dali::Graphics::VertexInputAttributeFormat::UNDEFINED)
472     {
473       locations.push_back(attr.location);
474     }
475   }
476
477   return locations;
478 }
479
480 uint32_t Reflection::GetUniformBlockCount() const
481 {
482   return mUniformBlocks.size();
483 }
484
485 uint32_t Reflection::GetUniformBlockBinding(uint32_t index) const
486 {
487   return index < mUniformBlocks.size() ? mUniformBlocks[index].binding : 0u;
488 }
489
490 uint32_t Reflection::GetUniformBlockSize(uint32_t index) const
491 {
492   return index < mUniformBlocks.size() ? mUniformBlocks[index].size : 0u;
493 }
494
495 bool Reflection::GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const
496 {
497   if(index >= mUniformBlocks.size())
498   {
499     return false;
500   }
501
502   const auto& block = mUniformBlocks[index];
503
504   out.name          = block.name;
505   out.binding       = block.binding;
506   out.descriptorSet = block.descriptorSet;
507   auto membersSize  = block.members.size();
508   out.members.resize(membersSize);
509   out.size = block.size;
510   for(auto i = 0u; i < out.members.size(); ++i)
511   {
512     const auto& memberUniform   = block.members[i];
513     out.members[i].name         = memberUniform.name;
514     out.members[i].binding      = block.binding;
515     out.members[i].uniformClass = Graphics::UniformClass::UNIFORM;
516     out.members[i].offset       = memberUniform.offset;
517     out.members[i].location     = memberUniform.location;
518   }
519
520   return true;
521 }
522
523 std::vector<uint32_t> Reflection::GetUniformBlockLocations() const
524 {
525   std::vector<uint32_t> retval{};
526   for(auto&& ubo : mUniformBlocks)
527   {
528     retval.emplace_back(ubo.binding);
529   }
530   return retval;
531 }
532
533 std::string Reflection::GetUniformBlockName(uint32_t blockIndex) const
534 {
535   if(blockIndex < mUniformBlocks.size())
536   {
537     return mUniformBlocks[blockIndex].name;
538   }
539   else
540   {
541     return std::string();
542   }
543 }
544
545 uint32_t Reflection::GetUniformBlockMemberCount(uint32_t blockIndex) const
546 {
547   if(blockIndex < mUniformBlocks.size())
548   {
549     return static_cast<uint32_t>(mUniformBlocks[blockIndex].members.size());
550   }
551   else
552   {
553     return 0u;
554   }
555 }
556
557 std::string Reflection::GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const
558 {
559   if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
560   {
561     return mUniformBlocks[blockIndex].members[memberLocation].name;
562   }
563   else
564   {
565     return std::string();
566   }
567 }
568
569 uint32_t Reflection::GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const
570 {
571   if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
572   {
573     return mUniformBlocks[blockIndex].members[memberLocation].offset;
574   }
575   else
576   {
577     return 0u;
578   }
579 }
580
581 bool Reflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
582 {
583   auto index = 0u;
584   for(auto&& ubo : mUniformBlocks)
585   {
586     for(auto&& member : ubo.members)
587     {
588       if(name == member.name || name == (ubo.name + "." + member.name))
589       {
590         out.name         = name;
591         out.location     = member.location;
592         out.binding      = ubo.binding;
593         out.bufferIndex  = index;
594         out.offset       = member.offset;
595         out.uniformClass = Graphics::UniformClass::UNIFORM;
596         return true;
597       }
598     }
599     ++index;
600   }
601
602   // check samplers
603   index = 0u;
604   for(auto&& uniform : mUniformOpaques)
605   {
606     if(uniform.name == name)
607     {
608       out.uniformClass = Graphics::UniformClass::COMBINED_IMAGE_SAMPLER;
609       out.binding      = 0;
610       out.name         = name;
611       out.offset       = index;            // lexical location in shader
612       out.location     = uniform.location; // uniform location mapping
613       return true;
614     }
615     ++index;
616   }
617
618   return false;
619 }
620
621 std::vector<GLenum> Reflection::GetStandaloneUniformTypes() const
622 {
623   std::vector<GLenum> retval{};
624   for(auto&& uniform : mStandaloneUniformExtraInfos)
625   {
626     retval.emplace_back(uniform.type);
627   }
628
629   return retval;
630 }
631
632 const std::vector<Reflection::UniformExtraInfo>& Reflection::GetStandaloneUniformExtraInfo() const
633 {
634   return mStandaloneUniformExtraInfos;
635 }
636
637 const std::vector<Dali::Graphics::UniformInfo>& Reflection::GetSamplers() const
638 {
639   return mUniformOpaques;
640 }
641
642 Graphics::ShaderLanguage Reflection::GetLanguage() const
643 {
644   auto version = Graphics::ShaderLanguage::GLSL_3_2;
645
646   auto gl = mController.GetGL();
647   if(!gl)
648   {
649     // Do nothing during shutdown
650     return version;
651   }
652
653   int majorVersion, minorVersion;
654   gl->GetIntegerv(GL_MAJOR_VERSION, &majorVersion);
655   gl->GetIntegerv(GL_MINOR_VERSION, &minorVersion);
656   DALI_LOG_RELEASE_INFO("GL Version (integer) : %d.%d\n", majorVersion, minorVersion);
657   DALI_LOG_RELEASE_INFO("GLSL Version : %s\n", gl->GetString(GL_SHADING_LANGUAGE_VERSION));
658
659   // TODO: the language version is hardcoded for now, but we may use what we get
660   // from GL_SHADING_LANGUAGE_VERSION?
661   return version;
662 }
663
664 void Reflection::SortOpaques()
665 {
666   //Determine declaration order of each sampler
667   auto& programCreateInfo = mProgram.GetCreateInfo();
668
669   std::vector<uint8_t> data;
670   std::string          vertShader;
671   std::string          fragShader;
672
673   for(auto& shaderState : *programCreateInfo.shaderState)
674   {
675     if(shaderState.pipelineStage == PipelineStage::VERTEX_SHADER)
676     {
677       vertShader = GetShaderSource(shaderState);
678     }
679     else if(shaderState.pipelineStage == PipelineStage::FRAGMENT_SHADER)
680     {
681       fragShader = GetShaderSource(shaderState);
682     }
683   }
684
685   int              samplerPosition = 0;
686   std::vector<int> samplerPositions(mUniformOpaques.size(), -1);
687
688   ParseShaderSamplers(vertShader, mUniformOpaques, samplerPosition, samplerPositions);
689   ParseShaderSamplers(fragShader, mUniformOpaques, samplerPosition, samplerPositions);
690
691   std::sort(mUniformOpaques.begin(), mUniformOpaques.end(), [](const UniformInfo& a, const UniformInfo& b) { return a.offset < b.offset; });
692 }
693
694 } // namespace Dali::Graphics::GLES