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