2 * Copyright (c) 2024 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 std::string GetShaderSource(Dali::Graphics::ShaderState shaderState)
123 std::vector<uint8_t> data;
124 auto* shader = static_cast<const Dali::Graphics::GLES::Shader*>(shaderState.shader);
125 auto& shaderCreateInfo = shader->GetCreateInfo();
126 data.resize(shaderCreateInfo.sourceSize + 1);
127 std::memcpy(&data[0], shaderCreateInfo.sourceData, shaderCreateInfo.sourceSize);
128 data[shaderCreateInfo.sourceSize] = 0;
130 return std::string(reinterpret_cast<char*>(&data[0]));
133 void ParseShaderSamplers(std::string shaderSource, std::vector<Dali::Graphics::UniformInfo>& uniformOpaques, int& samplerPosition, std::vector<int>& samplerPositions)
135 if(!shaderSource.empty())
137 char* shaderStr = strdup(shaderSource.c_str());
138 char* uniform = strstr(shaderStr, UNIFORM);
142 // From "uniform" to ";", not ignoring comments.
143 char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
145 char* nextPtr = nullptr;
146 char* token = strtok_r(outerToken, DELIMITERS, &nextPtr);
149 // Ignore any token up to "sampler"
150 if(SAMPLER_PREFIX == token)
152 token += SAMPLER_PREFIX.mLength;
153 if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
156 // We now are at next token after "samplerxxx" in outerToken token "stream"
158 // Does it use array notation?
159 int arraySize = 0; // 0 = No array
160 auto iter = std::string(token).find("[", 0);
161 if(iter != std::string::npos)
163 // Get Array size from source. (Warning, may be higher than GetActiveUniform suggests)
165 arraySize = int(strtol(token + iter, nullptr, 0));
168 token = strtok_r(nullptr, DELIMITERS_INC_INDEX, &nextPtr); // " ", "\t", "\n", "[", "]"
170 for(uint32_t i = 0; i < static_cast<uint32_t>(uniformOpaques.size()); ++i)
172 if(samplerPositions[i] == -1 &&
173 strncmp(token, uniformOpaques[i].name.c_str(), uniformOpaques[i].name.size()) == 0)
175 // We have found a matching name.
176 samplerPositions[i] = uniformOpaques[i].offset = samplerPosition;
183 samplerPosition += arraySize;
192 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Sampler uniform %s declared but not used in the shader\n", token);
198 token = strtok_r(nullptr, DELIMITERS, &nextPtr);
201 uniform = strstr(uniform, UNIFORM);
207 } // anonymous namespace
209 namespace Dali::Graphics::GLES
211 Reflection::Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller)
212 : Graphics::Reflection(),
213 mController(controller),
218 Reflection::~Reflection() = default;
220 void Reflection::BuildVertexAttributeReflection()
222 auto glProgram = mProgram.GetGlProgram();
224 int written, size, location, maxLength, nAttribs;
228 auto gl = mController.GetGL();
231 // Do nothing during shutdown
235 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build vertex attribute reflection for glProgram : %u\n", glProgram);
237 gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
238 gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
240 mVertexInputAttributes.clear();
241 mVertexInputAttributes.resize(nAttribs);
243 int maximumLocation = nAttribs - 1;
245 name = new GLchar[maxLength];
246 for(int i = 0; i < nAttribs; i++)
248 gl->GetActiveAttrib(glProgram, i, maxLength, &written, &size, &type, name);
249 location = gl->GetAttribLocation(glProgram, name);
253 if(maximumLocation < location)
255 maximumLocation = location;
256 // Increate continer size s.t. we can use maximumLocation as index.
257 mVertexInputAttributes.resize(maximumLocation + 1u);
260 AttributeInfo attributeInfo;
261 attributeInfo.location = location;
262 attributeInfo.name = name;
263 attributeInfo.format = GetVertexAttributeTypeFormat(type);
264 mVertexInputAttributes[location] = std::move(attributeInfo);
271 void Reflection::BuildUniformBlockReflection()
273 auto gl = mController.GetGL();
274 auto glProgram = mProgram.GetGlProgram();
276 int numUniformBlocks = 0;
280 // Do nothing during shutdown
284 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::General, "Build uniform block reflection for glProgram : %u\n", glProgram);
286 int maxUniformNameLength;
287 GLint activeUniformCount = 0;
288 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
289 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &activeUniformCount);
290 gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformNameLength);
292 numUniformBlocks++; // add block 0 for standalone UBO block
294 mUniformBlocks.resize(numUniformBlocks);
295 mUniformOpaques.clear();
297 mStandaloneUniformExtraInfos.clear();
299 std::vector<GLuint> uniformIndices;
300 uniformIndices.reserve(activeUniformCount);
301 for(auto i = 0; i < activeUniformCount; ++i)
303 uniformIndices.emplace_back(i);
306 // Obtain all parameters for active uniforms
307 auto getActiveUniformParams = [gl, glProgram, uniformIndices](GLenum param) {
308 std::vector<GLint> params;
309 params.resize(uniformIndices.size());
310 gl->GetActiveUniformsiv(glProgram, uniformIndices.size(), uniformIndices.data(), param, params.data());
314 auto activeUniformType = getActiveUniformParams(GL_UNIFORM_TYPE);
315 auto activeUniformSize = getActiveUniformParams(GL_UNIFORM_SIZE);
316 auto activeUniformNameLength = getActiveUniformParams(GL_UNIFORM_NAME_LENGTH);
317 auto activeUniformBlockIndex = getActiveUniformParams(GL_UNIFORM_BLOCK_INDEX);
318 auto activeUniformOffset = getActiveUniformParams(GL_UNIFORM_OFFSET);
320 // Extract only uniform blocks and collect data
321 // collect samplers into separate array
322 std::vector<UniformInfo> samplers;
324 name = new char[maxUniformNameLength + 1];
326 for(auto i = 0u; i < activeUniformBlockIndex.size(); ++i)
331 gl->GetActiveUniform(glProgram, i, maxUniformNameLength, &written, &elementCount, &type, name);
333 auto location = gl->GetUniformLocation(glProgram, name);
335 UniformInfo* uniformInfo{nullptr};
336 if(IsSampler(activeUniformType[i]))
338 samplers.emplace_back();
339 uniformInfo = &samplers.back();
340 uniformInfo->uniformClass = UniformClass::COMBINED_IMAGE_SAMPLER;
344 auto blockIndex = activeUniformBlockIndex[i] + 1;
345 auto& members = mUniformBlocks[blockIndex].members;
346 members.emplace_back();
347 uniformInfo = &members.back();
348 uniformInfo->uniformClass = UniformClass::UNIFORM;
349 uniformInfo->binding = 0;
350 uniformInfo->bufferIndex = blockIndex;
351 uniformInfo->binding = blockIndex == 0 ? i : 0; // this will be reset later
352 uniformInfo->offset = activeUniformOffset[i];
355 uniformInfo->location = location; // location must be set later and sorted by offset
356 uniformInfo->name = name;
358 // Strip off array index from name, use element count instead
361 std::string uniformName = name;
362 auto iter = uniformName.find('[', 0);
363 if(iter != std::string::npos)
365 uniformInfo->name = uniformName.substr(0, iter);
366 uniformInfo->elementCount = elementCount;
373 uint32_t blockIndex = 0;
374 for(auto& ubo : mUniformBlocks)
376 std::sort(ubo.members.begin(), ubo.members.end(), [](auto& lhs, auto& rhs) {
377 return lhs.offset < rhs.offset;
383 GLint blockNameLength;
384 gl->GetActiveUniformBlockiv(glProgram, blockIndex - 1, GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize);
385 gl->GetActiveUniformBlockiv(glProgram, blockIndex - 1, GL_UNIFORM_BLOCK_NAME_LENGTH, &blockNameLength);
386 char* blockName = new char[blockNameLength];
387 gl->GetActiveUniformBlockName(glProgram, blockIndex - 1, blockNameLength, nullptr, blockName);
388 ubo.name = blockName;
395 ubo.size = 0; // to compute later
398 ubo.descriptorSet = 0;
402 // count uniform size
403 auto& defaultUniformBlock = mUniformBlocks[0]; // Standalone block
404 defaultUniformBlock.size = 0;
406 mStandaloneUniformExtraInfos.reserve(defaultUniformBlock.members.size());
407 for(auto& member : defaultUniformBlock.members)
409 auto& type = activeUniformType[member.binding];
410 auto dataTypeSize = GetGLDataTypeSize(type);
411 member.offset = defaultUniformBlock.size;
412 defaultUniformBlock.size += (dataTypeSize * activeUniformSize[member.binding]);
414 UniformExtraInfo extraInfo{};
415 extraInfo.location = member.location;
416 extraInfo.size = (dataTypeSize);
417 extraInfo.offset = member.offset;
418 extraInfo.arraySize = activeUniformSize[member.binding];
419 extraInfo.type = activeUniformType[member.binding];
421 mStandaloneUniformExtraInfos.emplace_back(extraInfo);
424 mUniformOpaques = samplers;
428 uint32_t Reflection::GetVertexAttributeLocation(const std::string& name) const
430 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "name : %s\n", name.c_str());
431 for(auto&& attr : mVertexInputAttributes)
433 if(attr.name == name)
435 return attr.location;
438 return ERROR_ATTRIBUTE_NOT_FOUND;
441 Dali::Graphics::VertexInputAttributeFormat Reflection::GetVertexAttributeFormat(uint32_t location) const
443 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
444 if(location >= mVertexInputAttributes.size())
446 return Dali::Graphics::VertexInputAttributeFormat::UNDEFINED;
449 return mVertexInputAttributes[location].format;
452 std::string Reflection::GetVertexAttributeName(uint32_t location) const
454 DALI_LOG_INFO(gGraphicsReflectionLogFilter, Debug::Verbose, "location : %u\n", location);
455 if(location >= mVertexInputAttributes.size())
457 return std::string();
460 return mVertexInputAttributes[location].name;
463 std::vector<uint32_t> Reflection::GetVertexAttributeLocations() const
465 std::vector<uint32_t> locations;
466 for(auto&& attr : mVertexInputAttributes)
468 if(attr.format != Dali::Graphics::VertexInputAttributeFormat::UNDEFINED)
470 locations.push_back(attr.location);
477 uint32_t Reflection::GetUniformBlockCount() const
479 return mUniformBlocks.size();
482 uint32_t Reflection::GetUniformBlockBinding(uint32_t index) const
484 return index < mUniformBlocks.size() ? mUniformBlocks[index].binding : 0u;
487 uint32_t Reflection::GetUniformBlockSize(uint32_t index) const
489 return index < mUniformBlocks.size() ? mUniformBlocks[index].size : 0u;
492 bool Reflection::GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const
494 if(index >= mUniformBlocks.size())
499 const auto& block = mUniformBlocks[index];
501 out.name = block.name;
502 out.binding = block.binding;
503 out.descriptorSet = block.descriptorSet;
504 auto membersSize = block.members.size();
505 out.members.resize(membersSize);
506 out.size = block.size;
507 for(auto i = 0u; i < out.members.size(); ++i)
509 const auto& memberUniform = block.members[i];
510 out.members[i].name = memberUniform.name;
511 out.members[i].binding = block.binding;
512 out.members[i].uniformClass = Graphics::UniformClass::UNIFORM;
513 out.members[i].offset = memberUniform.offset;
514 out.members[i].location = memberUniform.location;
515 out.members[i].elementCount = memberUniform.elementCount;
521 std::vector<uint32_t> Reflection::GetUniformBlockLocations() const
523 std::vector<uint32_t> retval{};
524 for(auto&& ubo : mUniformBlocks)
526 retval.emplace_back(ubo.binding);
531 std::string Reflection::GetUniformBlockName(uint32_t blockIndex) const
533 if(blockIndex < mUniformBlocks.size())
535 return mUniformBlocks[blockIndex].name;
539 return std::string();
543 uint32_t Reflection::GetUniformBlockMemberCount(uint32_t blockIndex) const
545 if(blockIndex < mUniformBlocks.size())
547 return static_cast<uint32_t>(mUniformBlocks[blockIndex].members.size());
555 std::string Reflection::GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const
557 if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
559 return mUniformBlocks[blockIndex].members[memberLocation].name;
563 return std::string();
567 uint32_t Reflection::GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const
569 if(blockIndex < mUniformBlocks.size() && memberLocation < mUniformBlocks[blockIndex].members.size())
571 return mUniformBlocks[blockIndex].members[memberLocation].offset;
579 bool Reflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
582 for(auto&& ubo : mUniformBlocks)
584 for(auto&& member : ubo.members)
586 if(name == member.name || name == (ubo.name + "." + member.name))
589 out.location = member.location;
590 out.binding = ubo.binding;
591 out.bufferIndex = index;
592 out.offset = member.offset;
593 out.uniformClass = Graphics::UniformClass::UNIFORM;
602 for(auto&& uniform : mUniformOpaques)
604 if(uniform.name == name)
606 out.uniformClass = Graphics::UniformClass::COMBINED_IMAGE_SAMPLER;
609 out.offset = index; // lexical location in shader
610 out.location = uniform.location; // uniform location mapping
619 std::vector<GLenum> Reflection::GetStandaloneUniformTypes() const
621 std::vector<GLenum> retval{};
622 for(auto&& uniform : mStandaloneUniformExtraInfos)
624 retval.emplace_back(uniform.type);
630 const std::vector<Reflection::UniformExtraInfo>& Reflection::GetStandaloneUniformExtraInfo() const
632 return mStandaloneUniformExtraInfos;
635 const std::vector<Dali::Graphics::UniformInfo>& Reflection::GetSamplers() const
637 return mUniformOpaques;
640 Graphics::ShaderLanguage Reflection::GetLanguage() const
642 auto version = Graphics::ShaderLanguage::GLSL_3_2;
644 auto gl = mController.GetGL();
647 // Do nothing during shutdown
651 int majorVersion, minorVersion;
652 gl->GetIntegerv(GL_MAJOR_VERSION, &majorVersion);
653 gl->GetIntegerv(GL_MINOR_VERSION, &minorVersion);
654 DALI_LOG_RELEASE_INFO("GL Version (integer) : %d.%d\n", majorVersion, minorVersion);
655 DALI_LOG_RELEASE_INFO("GLSL Version : %s\n", gl->GetString(GL_SHADING_LANGUAGE_VERSION));
657 // TODO: the language version is hardcoded for now, but we may use what we get
658 // from GL_SHADING_LANGUAGE_VERSION?
662 void Reflection::SortOpaques()
664 //Determine declaration order of each sampler
665 auto& programCreateInfo = mProgram.GetCreateInfo();
667 std::vector<uint8_t> data;
668 std::string vertShader;
669 std::string fragShader;
671 for(auto& shaderState : *programCreateInfo.shaderState)
673 if(shaderState.pipelineStage == PipelineStage::VERTEX_SHADER)
675 vertShader = GetShaderSource(shaderState);
677 else if(shaderState.pipelineStage == PipelineStage::FRAGMENT_SHADER)
679 fragShader = GetShaderSource(shaderState);
683 int samplerPosition = 0;
684 std::vector<int> samplerPositions(mUniformOpaques.size(), -1);
686 ParseShaderSamplers(vertShader, mUniformOpaques, samplerPosition, samplerPositions);
687 ParseShaderSamplers(fragShader, mUniformOpaques, samplerPosition, samplerPositions);
689 std::sort(mUniformOpaques.begin(), mUniformOpaques.end(), [](const UniformInfo& a, const UniformInfo& b) { return a.offset < b.offset; });
692 } // namespace Dali::Graphics::GLES