f3e15cb16c0fd515dbe7294a81caea43614904e5
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-reflection.cpp
1 /*
2  * Copyright (c) 2021 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   int maxLocationValue = nAttribs - 1;
230
231   name = new GLchar[maxLength];
232   for(int i = 0; i < nAttribs; i++)
233   {
234     gl->GetActiveAttrib(glProgram, i, maxLength, &written, &size, &type, name);
235     location = gl->GetAttribLocation(glProgram, name);
236
237     if(location >= 0)
238     {
239       if(maxLocationValue < location)
240       {
241         maxLocationValue = location;
242         mVertexInputAttributes.resize(maxLocationValue + 1u);
243       }
244
245       AttributeInfo attributeInfo;
246       attributeInfo.location = location;
247       attributeInfo.name     = name;
248       attributeInfo.format   = GetVertexAttributeTypeFormat(type);
249       mVertexInputAttributes[location] = std::move(attributeInfo);
250     }
251   }
252
253   delete[] name;
254 }
255
256 void Reflection::BuildUniformReflection()
257 {
258   auto glProgram = mProgram.GetGlProgram();
259
260   int   maxLen;
261   char* name;
262
263   int numUniforms = 0;
264
265   auto gl = mController.GetGL();
266   if(!gl)
267   {
268     // Do nothing during shutdown
269     return;
270   }
271
272   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform reflection for glProgram : %u\n", glProgram);
273
274   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
275   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
276
277   mUniformBlocks.clear();
278   mDefaultUniformBlock.members.clear();
279   mUniformOpaques.clear();
280
281   name = new char[maxLen];
282
283   mStandaloneUniformExtraInfos.clear();
284
285   for(int i = 0; i < numUniforms; ++i)
286   {
287     int    elementCount;
288     GLenum type;
289     int    written;
290     gl->GetActiveUniform(glProgram, i, maxLen, &written, &elementCount, &type, name);
291     int location = gl->GetUniformLocation(glProgram, name);
292
293     Dali::Graphics::UniformInfo uniformInfo;
294
295     uniformInfo.name = name;
296     if(elementCount > 1)
297     {
298       auto iter = std::string(uniformInfo.name).find("[", 0);
299       if(iter != std::string::npos)
300       {
301         uniformInfo.name = std::string(name).substr(0, iter);
302       }
303     }
304
305     uniformInfo.uniformClass = IsSampler(type) ? Dali::Graphics::UniformClass::COMBINED_IMAGE_SAMPLER : Dali::Graphics::UniformClass::UNIFORM;
306     uniformInfo.location     = location; //IsSampler(type) ? 0 : location;
307     uniformInfo.binding      = 0;        // IsSampler(type) ? location : 0;
308     uniformInfo.bufferIndex  = 0;
309     uniformInfo.offset       = 0;
310
311     if(IsSampler(type))
312     {
313       mUniformOpaques.push_back(uniformInfo);
314     }
315     else
316     {
317       mDefaultUniformBlock.members.push_back(uniformInfo);
318       mStandaloneUniformExtraInfos.emplace_back(location, GetGLDataTypeSize(type), uniformInfo.offset, elementCount, type);
319     }
320   }
321
322   // Re-order according to uniform locations.
323
324   if(mDefaultUniformBlock.members.size() > 1)
325   {
326     std::sort(mDefaultUniformBlock.members.begin(), mDefaultUniformBlock.members.end(), SortUniformInfoByLocation);
327     std::sort(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), SortUniformExtraInfoByLocation);
328   }
329
330   if(mUniformOpaques.size() > 1)
331   {
332     SortOpaques();
333   }
334
335   // Calculate the uniform offset
336   for(unsigned int i = 0; i < mDefaultUniformBlock.members.size(); ++i)
337   {
338     if(i == 0)
339     {
340       mDefaultUniformBlock.members[i].offset = 0;
341     }
342     else
343     {
344       uint32_t previousUniformLocation = mDefaultUniformBlock.members[i - 1].location;
345       auto     previousUniform         = std::find_if(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), [&previousUniformLocation](const UniformExtraInfo& iter) { return iter.location == previousUniformLocation; });
346       if(previousUniform != mStandaloneUniformExtraInfos.end())
347       {
348         mDefaultUniformBlock.members[i].offset = mDefaultUniformBlock.members[i - 1].offset + (previousUniform->size * previousUniform->arraySize);
349         mStandaloneUniformExtraInfos[i].offset = mDefaultUniformBlock.members[i].offset;
350       }
351     }
352   }
353
354   if(mDefaultUniformBlock.members.size() > 0)
355   {
356     uint32_t lastUniformLocation = mDefaultUniformBlock.members.back().location;
357     auto     lastUniform         = std::find_if(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), [&lastUniformLocation](const UniformExtraInfo& iter) { return iter.location == lastUniformLocation; });
358     if(lastUniform != mStandaloneUniformExtraInfos.end())
359     {
360       mDefaultUniformBlock.size = mDefaultUniformBlock.members.back().offset + (lastUniform->size * lastUniform->arraySize);
361       mUniformBlocks.push_back(mDefaultUniformBlock);
362     }
363   }
364   else
365   {
366     mDefaultUniformBlock.size = 0;
367   }
368
369   delete[] name;
370 }
371
372 // TODO: Maybe this is not needed if uniform block is not support by dali shaders?
373 void Reflection::BuildUniformBlockReflection()
374 {
375   auto gl               = mController.GetGL();
376   auto glProgram        = mProgram.GetGlProgram();
377   int  numUniformBlocks = 0;
378
379   if(!gl)
380   {
381     // Do nothing during shutdown
382     return;
383   }
384
385   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform block reflection for glProgram : %u\n", glProgram);
386
387   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
388
389   mUniformBlocks.clear();
390   mUniformBlocks.resize(numUniformBlocks);
391
392   int uniformBlockMaxLength = 0;
393   gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
394
395   char* uniformBlockName = new char[uniformBlockMaxLength];
396   for(int i = 0; i < numUniformBlocks; i++)
397   {
398     int length;
399     int blockBinding;
400     int blockDataSize;
401     gl->GetActiveUniformBlockName(glProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
402     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
403     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
404
405     Dali::Graphics::UniformBlockInfo uniformBlockInfo;
406     uniformBlockInfo.name    = uniformBlockName;
407     uniformBlockInfo.size    = blockDataSize;
408     uniformBlockInfo.binding = blockBinding;
409
410     int nUnis;
411     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
412     int* unifIndexes = new GLint[nUnis];
413     gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
414     char* uniformName{};
415     int   maxUniLen;
416     gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
417
418     for(int unif = 0; unif < nUnis; ++unif)
419     {
420       int    uniIndex = unifIndexes[unif];
421       int    size;
422       GLenum type;
423
424       gl->GetActiveUniform(glProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
425       int location = gl->GetUniformLocation(glProgram, uniformName);
426
427       Dali::Graphics::UniformInfo uniform;
428       uniform.name     = uniformName;
429       uniform.location = location;
430       uniformBlockInfo.members.push_back(uniform);
431     }
432
433     delete[] unifIndexes;
434
435     mUniformBlocks.push_back(uniformBlockInfo);
436   }
437   delete[] uniformBlockName;
438 }
439
440 uint32_t Reflection::GetVertexAttributeLocation(const std::string& name) const
441 {
442   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "name : %s\n", name.c_str());
443   for(auto&& attr : mVertexInputAttributes)
444   {
445     if(attr.name == name)
446     {
447       return attr.location;
448     }
449   }
450   return ERROR_ATTRIBUTE_NOT_FOUND;
451 }
452
453 Dali::Graphics::VertexInputAttributeFormat Reflection::GetVertexAttributeFormat(uint32_t location) const
454 {
455   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
456   if(location >= mVertexInputAttributes.size())
457   {
458     return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
459   }
460
461   return mVertexInputAttributes[location].format;
462 }
463
464 std::string Reflection::GetVertexAttributeName(uint32_t location) const
465 {
466   DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
467   if(location >= mVertexInputAttributes.size())
468   {
469     return std::string();
470   }
471
472   return mVertexInputAttributes[location].name;
473 }
474
475 std::vector<uint32_t> Reflection::GetVertexAttributeLocations() const
476 {
477   std::vector<uint32_t> locations;
478   for(auto&& attr : mVertexInputAttributes)
479   {
480     if(attr.format != Dali::Graphics::VertexInputAttributeFormat::UNDEFINED)
481     {
482       locations.push_back(attr.location);
483     }
484   }
485
486   return locations;
487 }
488
489 uint32_t Reflection::GetUniformBlockCount() const
490 {
491   return mUniformBlocks.size();
492 }
493
494 uint32_t Reflection::GetUniformBlockBinding(uint32_t index) const
495 {
496   return index < mUniformBlocks.size() ? mUniformBlocks[index].binding : 0u;
497 }
498
499 uint32_t Reflection::GetUniformBlockSize(uint32_t index) const
500 {
501   return index < mUniformBlocks.size() ? mUniformBlocks[index].size : 0u;
502 }
503
504 bool Reflection::GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const
505 {
506   if(index >= mUniformBlocks.size())
507   {
508     return false;
509   }
510
511   const auto& block = mUniformBlocks[index];
512
513   out.name          = block.name;
514   out.binding       = block.binding;
515   out.descriptorSet = block.descriptorSet;
516   auto membersSize  = block.members.size();
517   out.members.resize(membersSize);
518   out.size = block.size;
519   for(auto i = 0u; i < out.members.size(); ++i)
520   {
521     const auto& memberUniform   = block.members[i];
522     out.members[i].name         = memberUniform.name;
523     out.members[i].binding      = block.binding;
524     out.members[i].uniformClass = Graphics::UniformClass::UNIFORM;
525     out.members[i].offset       = memberUniform.offset;
526     out.members[i].location     = memberUniform.location;
527   }
528
529   return true;
530 }
531
532 std::vector<uint32_t> Reflection::GetUniformBlockLocations() const
533 {
534   std::vector<uint32_t> retval{};
535   for(auto&& ubo : mUniformBlocks)
536   {
537     retval.emplace_back(ubo.binding);
538   }
539   return retval;
540 }
541
542 std::string Reflection::GetUniformBlockName(uint32_t blockIndex) const
543 {
544   if(blockIndex < mUniformBlocks.size())
545   {
546     return mUniformBlocks[blockIndex].name;
547   }
548   else
549   {
550     return std::string();
551   }
552 }
553
554 uint32_t Reflection::GetUniformBlockMemberCount(uint32_t blockIndex) const
555 {
556   if(blockIndex < mUniformBlocks.size())
557   {
558     return static_cast<uint32_t>(mUniformBlocks[blockIndex].members.size());
559   }
560   else
561   {
562     return 0u;
563   }
564 }
565
566 std::string Reflection::GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const
567 {
568   if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
569   {
570     return mUniformBlocks[blockIndex].members[memberLocation].name;
571   }
572   else
573   {
574     return std::string();
575   }
576 }
577
578 uint32_t Reflection::GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const
579 {
580   if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
581   {
582     return mUniformBlocks[blockIndex].members[memberLocation].offset;
583   }
584   else
585   {
586     return 0u;
587   }
588 }
589
590 bool Reflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
591 {
592   auto index = 0u;
593   for(auto&& ubo : mUniformBlocks)
594   {
595     for(auto&& member : ubo.members)
596     {
597       if(name == member.name || name == (ubo.name + "." + member.name))
598       {
599         out.name         = name;
600         out.location     = member.location;
601         out.binding      = ubo.binding;
602         out.bufferIndex  = index;
603         out.offset       = member.offset;
604         out.uniformClass = Graphics::UniformClass::UNIFORM;
605         return true;
606       }
607     }
608     ++index;
609   }
610
611   // check samplers
612   index = 0u;
613   for(auto&& uniform : mUniformOpaques)
614   {
615     if(uniform.name == name)
616     {
617       out.uniformClass = Graphics::UniformClass::COMBINED_IMAGE_SAMPLER;
618       out.binding      = 0;
619       out.name         = name;
620       out.offset       = index;            // lexical location in shader
621       out.location     = uniform.location; // uniform location mapping
622       return true;
623     }
624     ++index;
625   }
626
627   return false;
628 }
629
630 std::vector<GLenum> Reflection::GetStandaloneUniformTypes() const
631 {
632   std::vector<GLenum> retval{};
633   for(auto&& uniform : mStandaloneUniformExtraInfos)
634   {
635     retval.emplace_back(uniform.type);
636   }
637
638   return retval;
639 }
640
641 const std::vector<Reflection::UniformExtraInfo>& Reflection::GetStandaloneUniformExtraInfo() const
642 {
643   return mStandaloneUniformExtraInfos;
644 }
645
646 const std::vector<Dali::Graphics::UniformInfo>& Reflection::GetSamplers() const
647 {
648   return mUniformOpaques;
649 }
650
651 Graphics::ShaderLanguage Reflection::GetLanguage() const
652 {
653   auto version = Graphics::ShaderLanguage::GLSL_3_2;
654
655   auto gl = mController.GetGL();
656   if(!gl)
657   {
658     // Do nothing during shutdown
659     return version;
660   }
661
662   int majorVersion, minorVersion;
663   gl->GetIntegerv(GL_MAJOR_VERSION, &majorVersion);
664   gl->GetIntegerv(GL_MINOR_VERSION, &minorVersion);
665   DALI_LOG_RELEASE_INFO("GL Version (integer) : %d.%d\n", majorVersion, minorVersion);
666   DALI_LOG_RELEASE_INFO("GLSL Version : %s\n", gl->GetString(GL_SHADING_LANGUAGE_VERSION));
667
668   // TODO: the language version is hardcoded for now, but we may use what we get
669   // from GL_SHADING_LANGUAGE_VERSION?
670   return version;
671 }
672
673 void Reflection::SortOpaques()
674 {
675   //Determine declaration order of each sampler
676   auto& programCreateInfo = mProgram.GetCreateInfo();
677
678   std::vector<uint8_t> data;
679   std::string          vertShader;
680   std::string          fragShader;
681
682   for(auto& shaderState : *programCreateInfo.shaderState)
683   {
684     if(shaderState.pipelineStage == PipelineStage::VERTEX_SHADER)
685     {
686       vertShader = GetShaderSource(shaderState);
687     }
688     else if(shaderState.pipelineStage == PipelineStage::FRAGMENT_SHADER)
689     {
690       fragShader = GetShaderSource(shaderState);
691     }
692   }
693
694   int              samplerPosition = 0;
695   std::vector<int> samplerPositions(mUniformOpaques.size(), -1);
696
697   ParseShaderSamplers(vertShader, mUniformOpaques, samplerPosition, samplerPositions);
698   ParseShaderSamplers(fragShader, mUniformOpaques, samplerPosition, samplerPositions);
699
700   std::sort(mUniformOpaques.begin(), mUniformOpaques.end(), [](const UniformInfo& a, const UniformInfo& b) { return a.offset < b.offset; });
701 }
702
703 } // namespace Dali::Graphics::GLES