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