2 * Copyright (c) 2023 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 "gles-graphics-reflection.h"
20 #include <dali/integration-api/debug.h>
21 #include <dali/integration-api/gl-abstraction.h>
22 #include <dali/integration-api/gl-defines.h>
25 #include "egl-graphics-controller.h"
27 #include <GLES3/gl3.h>
28 #include <GLES3/gl31.h>
30 #include "gles-graphics-program.h"
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gGraphicsReflectionLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GRAPHICS_REFLECTION");
42 const char* const mString;
43 const uint32_t mLength;
45 template<uint32_t kLength>
46 constexpr StringSize(const char (&string)[kLength])
48 mLength(kLength - 1) // remove terminating null; N.B. there should be no other null.
52 operator const char*() const
58 bool operator==(const StringSize& lhs, const char* rhs)
60 return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
63 const char* const DELIMITERS = " \t\n";
64 const char* const DELIMITERS_INC_INDEX = " \t\n[]";
65 constexpr StringSize UNIFORM{"uniform"};
66 constexpr StringSize SAMPLER_PREFIX{"sampler"};
67 constexpr StringSize SAMPLER_TYPES[] = {"2D", "Cube", "ExternalOES"};
68 constexpr auto END_SAMPLER_TYPES = SAMPLER_TYPES + std::extent<decltype(SAMPLER_TYPES)>::value;
70 Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeTypeFormat(GLenum type)
75 return Dali::Graphics::VertexInputAttributeFormat::FLOAT;
77 return Dali::Graphics::VertexInputAttributeFormat::VEC2;
79 return Dali::Graphics::VertexInputAttributeFormat::VEC3;
81 return Dali::Graphics::VertexInputAttributeFormat::VEC4;
83 return Dali::Graphics::VertexInputAttributeFormat::INTEGER;
85 return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
89 uint32_t GetGLDataTypeSize(GLenum type)
91 // There are many more types than what are covered here, but
92 // they are not supported in dali.
95 case GL_FLOAT: // "float", 1 float, 4 bytes
97 case GL_FLOAT_VEC2: // "vec2", 2 floats, 8 bytes
99 case GL_FLOAT_VEC3: // "vec3", 3 floats, 12 bytes
101 case GL_FLOAT_VEC4: // "vec4", 4 floats, 16 bytes
103 case GL_INT: // "int", 1 integer, 4 bytes
105 case GL_FLOAT_MAT2: // "mat2", 4 floats, 16 bytes
107 case GL_FLOAT_MAT3: // "mat3", 3 vec3, 36 bytes
109 case GL_FLOAT_MAT4: // "mat4", 4 vec4, 64 bytes
116 bool IsSampler(GLenum type)
118 return type == GL_SAMPLER_2D || type == GL_SAMPLER_3D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES;
121 bool SortUniformInfoByLocation(Dali::Graphics::UniformInfo a, Dali::Graphics::UniformInfo b)
123 return a.location < b.location;
126 bool SortUniformExtraInfoByLocation(Dali::Graphics::GLES::Reflection::UniformExtraInfo a, Dali::Graphics::GLES::Reflection::UniformExtraInfo b)
128 return a.location < b.location;
131 std::string GetShaderSource(Dali::Graphics::ShaderState shaderState)
133 std::vector<uint8_t> data;
134 auto* shader = static_cast<const Dali::Graphics::GLES::Shader*>(shaderState.shader);
135 auto& shaderCreateInfo = shader->GetCreateInfo();
136 data.resize(shaderCreateInfo.sourceSize + 1);
137 std::memcpy(&data[0], shaderCreateInfo.sourceData, shaderCreateInfo.sourceSize);
138 data[shaderCreateInfo.sourceSize] = 0;
140 return std::string(reinterpret_cast<char*>(&data[0]));
143 void ParseShaderSamplers(std::string shaderSource, std::vector<Dali::Graphics::UniformInfo>& uniformOpaques, int& samplerPosition, std::vector<int>& samplerPositions)
145 if(!shaderSource.empty())
147 char* shaderStr = strdup(shaderSource.c_str());
148 char* uniform = strstr(shaderStr, UNIFORM);
152 // From "uniform" to ";", not ignoring comments.
153 char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
155 char* nextPtr = nullptr;
156 char* token = strtok_r(outerToken, DELIMITERS, &nextPtr);
159 // Ignore any token up to "sampler"
160 if(SAMPLER_PREFIX == token)
162 token += SAMPLER_PREFIX.mLength;
163 if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
166 // We now are at next token after "samplerxxx" in outerToken token "stream"
168 // Does it use array notation?
169 int arraySize = 0; // 0 = No array
170 auto iter = std::string(token).find("[", 0);
171 if(iter != std::string::npos)
173 // Get Array size from source. (Warning, may be higher than GetActiveUniform suggests)
175 arraySize = int(strtol(token + iter, nullptr, 0));
178 token = strtok_r(nullptr, DELIMITERS_INC_INDEX, &nextPtr); // " ", "\t", "\n", "[", "]"
180 for(uint32_t i = 0; i < static_cast<uint32_t>(uniformOpaques.size()); ++i)
182 if(samplerPositions[i] == -1 &&
183 strncmp(token, uniformOpaques[i].name.c_str(), uniformOpaques[i].name.size()) == 0)
185 // We have found a matching name.
186 samplerPositions[i] = uniformOpaques[i].offset = samplerPosition;
193 samplerPosition += arraySize;
202 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Sampler uniform %s declared but not used in the shader\n", token);
208 token = strtok_r(nullptr, DELIMITERS, &nextPtr);
211 uniform = strstr(uniform, UNIFORM);
217 } // anonymous namespace
219 namespace Dali::Graphics::GLES
221 Reflection::Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller)
222 : Graphics::Reflection(),
223 mController(controller),
228 Reflection::~Reflection() = default;
230 void Reflection::BuildVertexAttributeReflection()
232 auto glProgram = mProgram.GetGlProgram();
234 int written, size, location, maxLength, nAttribs;
238 auto gl = mController.GetGL();
241 // Do nothing during shutdown
245 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build vertex attribute reflection for glProgram : %u\n", glProgram);
247 gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
248 gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
250 mVertexInputAttributes.clear();
251 mVertexInputAttributes.resize(nAttribs);
253 int maxLocationValue = nAttribs - 1;
255 name = new GLchar[maxLength];
256 for(int i = 0; i < nAttribs; i++)
258 gl->GetActiveAttrib(glProgram, i, maxLength, &written, &size, &type, name);
259 location = gl->GetAttribLocation(glProgram, name);
263 if(maxLocationValue < location)
265 maxLocationValue = location;
266 mVertexInputAttributes.resize(maxLocationValue + 1u);
269 AttributeInfo attributeInfo;
270 attributeInfo.location = location;
271 attributeInfo.name = name;
272 attributeInfo.format = GetVertexAttributeTypeFormat(type);
273 mVertexInputAttributes[location] = std::move(attributeInfo);
280 void Reflection::BuildUniformReflection()
282 auto glProgram = mProgram.GetGlProgram();
289 auto gl = mController.GetGL();
292 // Do nothing during shutdown
296 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform reflection for glProgram : %u\n", glProgram);
298 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
299 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
301 mUniformBlocks.clear();
302 mDefaultUniformBlock.members.clear();
303 mUniformOpaques.clear();
305 name = new char[maxLen];
307 mStandaloneUniformExtraInfos.clear();
309 for(int i = 0; i < numUniforms; ++i)
314 gl->GetActiveUniform(glProgram, i, maxLen, &written, &elementCount, &type, name);
316 int location = gl->GetUniformLocation(glProgram, name);
318 Dali::Graphics::UniformInfo uniformInfo;
320 uniformInfo.name = name;
323 // If we have an active uniform that refers to an array, only the first element
324 // is present in this list, and is referenced as "uniform[0]", but the element
325 // count is non-zero to indicate how many uniforms there are in the array.
327 // Strip off the array, but store the element count
328 auto iter = std::string(uniformInfo.name).find("[", 0);
329 if(iter != std::string::npos)
331 uniformInfo.name = std::string(name).substr(0, iter);
332 uniformInfo.elementCount = elementCount;
335 uniformInfo.uniformClass = IsSampler(type) ? Dali::Graphics::UniformClass::COMBINED_IMAGE_SAMPLER : Dali::Graphics::UniformClass::UNIFORM;
336 uniformInfo.location = location; // GL doesn't guarantee that consecutive array elements have sequential locations. But, we only store location of first element.
337 uniformInfo.binding = 0;
338 uniformInfo.bufferIndex = 0;
339 uniformInfo.offset = 0;
343 mUniformOpaques.push_back(uniformInfo);
347 mDefaultUniformBlock.members.push_back(uniformInfo);
348 mStandaloneUniformExtraInfos.emplace_back(location, GetGLDataTypeSize(type), uniformInfo.offset, elementCount, type);
352 // Re-order according to uniform locations.
354 if(mDefaultUniformBlock.members.size() > 1)
356 std::sort(mDefaultUniformBlock.members.begin(), mDefaultUniformBlock.members.end(), SortUniformInfoByLocation);
357 std::sort(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), SortUniformExtraInfoByLocation);
360 if(mUniformOpaques.size() > 1)
365 // Calculate the uniform offset
366 for(unsigned int i = 0; i < mDefaultUniformBlock.members.size(); ++i)
370 mDefaultUniformBlock.members[i].offset = 0;
374 uint32_t previousUniformLocation = mDefaultUniformBlock.members[i - 1].location;
375 auto previousUniform = std::find_if(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), [&previousUniformLocation](const UniformExtraInfo& iter) { return iter.location == previousUniformLocation; });
376 if(previousUniform != mStandaloneUniformExtraInfos.end())
378 mDefaultUniformBlock.members[i].offset = mDefaultUniformBlock.members[i - 1].offset + (previousUniform->size * previousUniform->arraySize);
379 mStandaloneUniformExtraInfos[i].offset = mDefaultUniformBlock.members[i].offset;
384 if(mDefaultUniformBlock.members.size() > 0)
386 uint32_t lastUniformLocation = mDefaultUniformBlock.members.back().location;
387 auto lastUniform = std::find_if(mStandaloneUniformExtraInfos.begin(), mStandaloneUniformExtraInfos.end(), [&lastUniformLocation](const UniformExtraInfo& iter) { return iter.location == lastUniformLocation; });
388 if(lastUniform != mStandaloneUniformExtraInfos.end())
390 mDefaultUniformBlock.size = mDefaultUniformBlock.members.back().offset + (lastUniform->size * lastUniform->arraySize);
391 mUniformBlocks.push_back(mDefaultUniformBlock);
396 mDefaultUniformBlock.size = 0;
402 // TODO: Maybe this is not needed if uniform block is not support by dali shaders?
403 void Reflection::BuildUniformBlockReflection()
405 auto gl = mController.GetGL();
406 auto glProgram = mProgram.GetGlProgram();
407 int numUniformBlocks = 0;
411 // Do nothing during shutdown
415 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform block reflection for glProgram : %u\n", glProgram);
417 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
419 mUniformBlocks.clear();
420 mUniformBlocks.resize(numUniformBlocks);
422 int uniformBlockMaxLength = 0;
423 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
425 char* uniformBlockName = new char[uniformBlockMaxLength];
426 for(int i = 0; i < numUniformBlocks; i++)
431 gl->GetActiveUniformBlockName(glProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
432 gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
433 gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
435 Dali::Graphics::UniformBlockInfo uniformBlockInfo;
436 uniformBlockInfo.name = uniformBlockName;
437 uniformBlockInfo.size = blockDataSize;
438 uniformBlockInfo.binding = blockBinding;
441 gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
442 int* unifIndexes = new GLint[nUnis];
443 gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
446 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
448 for(int unif = 0; unif < nUnis; ++unif)
450 int uniIndex = unifIndexes[unif];
454 gl->GetActiveUniform(glProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
455 int location = gl->GetUniformLocation(glProgram, uniformName);
457 Dali::Graphics::UniformInfo uniform;
458 uniform.name = uniformName;
459 uniform.location = location;
460 uniformBlockInfo.members.push_back(uniform);
463 delete[] unifIndexes;
465 mUniformBlocks.push_back(uniformBlockInfo);
467 delete[] uniformBlockName;
470 uint32_t Reflection::GetVertexAttributeLocation(const std::string& name) const
472 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "name : %s\n", name.c_str());
473 for(auto&& attr : mVertexInputAttributes)
475 if(attr.name == name)
477 return attr.location;
480 return ERROR_ATTRIBUTE_NOT_FOUND;
483 Dali::Graphics::VertexInputAttributeFormat Reflection::GetVertexAttributeFormat(uint32_t location) const
485 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
486 if(location >= mVertexInputAttributes.size())
488 return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
491 return mVertexInputAttributes[location].format;
494 std::string Reflection::GetVertexAttributeName(uint32_t location) const
496 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
497 if(location >= mVertexInputAttributes.size())
499 return std::string();
502 return mVertexInputAttributes[location].name;
505 std::vector<uint32_t> Reflection::GetVertexAttributeLocations() const
507 std::vector<uint32_t> locations;
508 for(auto&& attr : mVertexInputAttributes)
510 if(attr.format != Dali::Graphics::VertexInputAttributeFormat::UNDEFINED)
512 locations.push_back(attr.location);
519 uint32_t Reflection::GetUniformBlockCount() const
521 return mUniformBlocks.size();
524 uint32_t Reflection::GetUniformBlockBinding(uint32_t index) const
526 return index < mUniformBlocks.size() ? mUniformBlocks[index].binding : 0u;
529 uint32_t Reflection::GetUniformBlockSize(uint32_t index) const
531 return index < mUniformBlocks.size() ? mUniformBlocks[index].size : 0u;
534 bool Reflection::GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const
536 if(index >= mUniformBlocks.size())
541 const auto& block = mUniformBlocks[index];
543 out.name = block.name;
544 out.binding = block.binding;
545 out.descriptorSet = block.descriptorSet;
546 auto membersSize = block.members.size();
547 out.members.resize(membersSize);
548 out.size = block.size;
549 for(auto i = 0u; i < out.members.size(); ++i)
551 const auto& memberUniform = block.members[i];
552 out.members[i].name = memberUniform.name;
553 out.members[i].binding = block.binding;
554 out.members[i].uniformClass = Graphics::UniformClass::UNIFORM;
555 out.members[i].offset = memberUniform.offset;
556 out.members[i].location = memberUniform.location;
562 std::vector<uint32_t> Reflection::GetUniformBlockLocations() const
564 std::vector<uint32_t> retval{};
565 for(auto&& ubo : mUniformBlocks)
567 retval.emplace_back(ubo.binding);
572 std::string Reflection::GetUniformBlockName(uint32_t blockIndex) const
574 if(blockIndex < mUniformBlocks.size())
576 return mUniformBlocks[blockIndex].name;
580 return std::string();
584 uint32_t Reflection::GetUniformBlockMemberCount(uint32_t blockIndex) const
586 if(blockIndex < mUniformBlocks.size())
588 return static_cast<uint32_t>(mUniformBlocks[blockIndex].members.size());
596 std::string Reflection::GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const
598 if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
600 return mUniformBlocks[blockIndex].members[memberLocation].name;
604 return std::string();
608 uint32_t Reflection::GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const
610 if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
612 return mUniformBlocks[blockIndex].members[memberLocation].offset;
620 bool Reflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
623 for(auto&& ubo : mUniformBlocks)
625 for(auto&& member : ubo.members)
627 if(name == member.name || name == (ubo.name + "." + member.name))
630 out.location = member.location;
631 out.binding = ubo.binding;
632 out.bufferIndex = index;
633 out.offset = member.offset;
634 out.uniformClass = Graphics::UniformClass::UNIFORM;
643 for(auto&& uniform : mUniformOpaques)
645 if(uniform.name == name)
647 out.uniformClass = Graphics::UniformClass::COMBINED_IMAGE_SAMPLER;
650 out.offset = index; // lexical location in shader
651 out.location = uniform.location; // uniform location mapping
660 std::vector<GLenum> Reflection::GetStandaloneUniformTypes() const
662 std::vector<GLenum> retval{};
663 for(auto&& uniform : mStandaloneUniformExtraInfos)
665 retval.emplace_back(uniform.type);
671 const std::vector<Reflection::UniformExtraInfo>& Reflection::GetStandaloneUniformExtraInfo() const
673 return mStandaloneUniformExtraInfos;
676 const std::vector<Dali::Graphics::UniformInfo>& Reflection::GetSamplers() const
678 return mUniformOpaques;
681 Graphics::ShaderLanguage Reflection::GetLanguage() const
683 auto version = Graphics::ShaderLanguage::GLSL_3_2;
685 auto gl = mController.GetGL();
688 // Do nothing during shutdown
692 int majorVersion, minorVersion;
693 gl->GetIntegerv(GL_MAJOR_VERSION, &majorVersion);
694 gl->GetIntegerv(GL_MINOR_VERSION, &minorVersion);
695 DALI_LOG_RELEASE_INFO("GL Version (integer) : %d.%d\n", majorVersion, minorVersion);
696 DALI_LOG_RELEASE_INFO("GLSL Version : %s\n", gl->GetString(GL_SHADING_LANGUAGE_VERSION));
698 // TODO: the language version is hardcoded for now, but we may use what we get
699 // from GL_SHADING_LANGUAGE_VERSION?
703 void Reflection::SortOpaques()
705 //Determine declaration order of each sampler
706 auto& programCreateInfo = mProgram.GetCreateInfo();
708 std::vector<uint8_t> data;
709 std::string vertShader;
710 std::string fragShader;
712 for(auto& shaderState : *programCreateInfo.shaderState)
714 if(shaderState.pipelineStage == PipelineStage::VERTEX_SHADER)
716 vertShader = GetShaderSource(shaderState);
718 else if(shaderState.pipelineStage == PipelineStage::FRAGMENT_SHADER)
720 fragShader = GetShaderSource(shaderState);
724 int samplerPosition = 0;
725 std::vector<int> samplerPositions(mUniformOpaques.size(), -1);
727 ParseShaderSamplers(vertShader, mUniformOpaques, samplerPosition, samplerPositions);
728 ParseShaderSamplers(fragShader, mUniformOpaques, samplerPosition, samplerPositions);
730 std::sort(mUniformOpaques.begin(), mUniformOpaques.end(), [](const UniformInfo& a, const UniformInfo& b) { return a.offset < b.offset; });
733 } // namespace Dali::Graphics::GLES