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-program.h"
21 #include <dali/internal/graphics/common/shader-parser.h>
22 #include "egl-graphics-controller.h"
23 #include "gles-graphics-reflection.h"
24 #include "gles-graphics-shader.h"
29 #if defined(DEBUG_ENABLED)
30 Debug::Filter* gGraphicsProgramLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GRAPHICS_PROGRAM");
33 namespace Dali::Graphics::GLES
35 using Integration::GlAbstraction;
38 * Memory compare working on 4-byte types. Since all types used in shaders are
39 * size of 4*N then no need for size and alignment checks.
41 template<class A, class B>
42 inline bool memcmp4(A* a, B* b, uint32_t size)
44 auto* pa = reinterpret_cast<const uint32_t*>(a);
45 auto* pb = reinterpret_cast<const uint32_t*>(b);
47 while(size-- && *pa++ == *pb++)
53 * Structure stores pointer to the function
54 * which will set the uniform of particular type
60 void (GlAbstraction::*uniformfProc)(GLint, GLsizei, const float*);
61 void (GlAbstraction::*uniformiProc)(GLint, GLsizei, const int*);
62 void (GlAbstraction::*uniformMatrixProc)(GLint, GLsizei, GLboolean, const float*);
76 struct ProgramImpl::Impl
78 explicit Impl(EglGraphicsController& _controller, const ProgramCreateInfo& info)
79 : controller(_controller)
84 createInfo.shaderState = new std::vector<ShaderState>(*info.shaderState);
87 // Create new reference of std::string_view.
88 name = std::string(info.name);
89 createInfo.name = name;
94 delete createInfo.shaderState;
97 EglGraphicsController& controller;
98 ProgramCreateInfo createInfo;
100 uint32_t glProgram{};
101 uint32_t refCount{0u};
103 std::unique_ptr<GLES::Reflection> reflection{nullptr};
106 std::vector<uint8_t> uniformData;
108 // List of standalone uniform setters
109 std::vector<UniformSetter> uniformSetters;
112 ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
114 // Create implementation
115 mImpl = std::make_unique<Impl>(controller, createInfo);
118 mImpl->reflection = std::make_unique<GLES::Reflection>(*this, controller);
121 ProgramImpl::~ProgramImpl() = default;
123 bool ProgramImpl::Destroy()
127 auto gl = mImpl->controller.GetGL();
132 gl->DeleteProgram(mImpl->glProgram);
138 void ProgramImpl::Preprocess()
140 // For now only Vertex and Fragment shader stages supported
142 std::string vertexString;
143 std::string fragmentString;
144 std::string* currentString = nullptr;
146 const GLES::Shader* vsh = nullptr;
147 const GLES::Shader* fsh = nullptr;
149 const auto& info = mImpl->createInfo;
151 for(const auto& state : *info.shaderState)
153 const auto* shader = static_cast<const GLES::Shader*>(state.shader);
154 if(state.pipelineStage == PipelineStage::VERTEX_SHADER)
156 // Only TEXT source mode can be processed
157 currentString = &vertexString;
160 else if(state.pipelineStage == PipelineStage::FRAGMENT_SHADER)
162 // Only TEXT source mode can be processed
163 currentString = &fragmentString;
168 // no valid stream to push
169 currentString = nullptr;
170 DALI_LOG_ERROR("Shader state contains invalid shader source (most likely binary)! Can't process!");
173 // Check if stream valid
174 if(currentString && currentString->empty() && shader->GetCreateInfo().sourceMode == ShaderSourceMode::TEXT)
176 *currentString = std::string(reinterpret_cast<const char*>(shader->GetCreateInfo().sourceData),
177 shader->GetCreateInfo().sourceSize);
181 DALI_LOG_ERROR("Preprocessing of binary shaders isn't allowed!");
185 // if we have both streams ready
186 if(!vertexString.empty() && !fragmentString.empty())
188 // In case we have one modern shader and one legacy counterpart we need to enforce
190 Internal::ShaderParser::ShaderParserInfo parseInfo{};
191 parseInfo.vertexShaderCode = &vertexString;
192 parseInfo.fragmentShaderCode = &fragmentString;
193 parseInfo.vertexShaderLegacyVersion = vsh->GetGLSLVersion();
194 parseInfo.fragmentShaderLegacyVersion = fsh->GetGLSLVersion();
195 parseInfo.language = Internal::ShaderParser::OutputLanguage::GLSL3; // We default to GLSL3
196 parseInfo.outputVersion = std::max(vsh->GetGLSLVersion(), fsh->GetGLSLVersion());
198 std::vector<std::string> newShaders;
200 Internal::ShaderParser::Parse(parseInfo, newShaders);
202 // substitute shader code
203 vsh->GetImplementation()->SetPreprocessedCode(newShaders[0].data(), newShaders[0].size());
204 fsh->GetImplementation()->SetPreprocessedCode(newShaders[1].data(), newShaders[1].size());
208 DALI_LOG_ERROR("Preprocessing shader code failed!");
212 bool ProgramImpl::Create()
214 // Create and link new program
215 auto gl = mImpl->controller.GetGL();
218 // Do nothing during shutdown
222 auto program = gl->CreateProgram();
224 DALI_LOG_DEBUG_INFO("Program[%s] create program id : %u\n", mImpl->name.c_str(), program);
226 const auto& info = mImpl->createInfo;
230 for(const auto& state : *info.shaderState)
232 const auto* shader = static_cast<const GLES::Shader*>(state.shader);
234 // Compile shader first (ignored when compiled)
235 if(shader->GetImplementation()->Compile())
237 auto shaderId = shader->GetImplementation()->GetGLShader();
238 DALI_LOG_DEBUG_INFO("Program[%s] attach shader : %u\n", mImpl->name.c_str(), shaderId);
239 gl->AttachShader(program, shaderId);
243 DALI_LOG_DEBUG_INFO("Program[%s] call glLinkProgram\n", mImpl->name.c_str());
244 gl->LinkProgram(program);
247 gl->GetProgramiv(program, GL_LINK_STATUS, &status);
248 if(status != GL_TRUE)
252 gl->GetProgramInfoLog(program, 4096, &size, output);
255 DALI_LOG_ERROR("glLinkProgram[%s] failed:\n%s\n", mImpl->name.c_str(), output);
256 gl->DeleteProgram(program);
260 mImpl->glProgram = program;
262 // Initialize reflection
263 mImpl->reflection->BuildVertexAttributeReflection();
264 mImpl->reflection->BuildUniformBlockReflection();
266 // populate uniform cache memory for standalone uniforms (it's not needed
267 // for real UBOs as real UBOs work with whole memory blocks)
268 auto& reflection = mImpl->reflection;
269 if(!reflection->GetStandaloneUniformExtraInfo().empty())
271 UniformBlockInfo blockInfo;
272 reflection->GetUniformBlock(0, blockInfo);
273 auto uniformCacheSize = blockInfo.size;
274 mImpl->uniformData.resize(uniformCacheSize);
276 std::fill(mImpl->uniformData.begin(), mImpl->uniformData.end(), 0);
278 BuildStandaloneUniformCache();
281 // Set up uniform block bindings
283 auto blockCount = reflection->GetUniformBlockCount();
284 for(uint32_t i = 1; i < blockCount; ++i) // Ignore emulated block at #0
286 UniformBlockInfo uboInfo{};
287 reflection->GetUniformBlock(i, uboInfo);
289 // make binding point
290 auto blockIndex = gl->GetUniformBlockIndex(program, uboInfo.name.c_str());
291 gl->UniformBlockBinding(program, blockIndex, binding++);
297 uint32_t ProgramImpl::GetGlProgram() const
299 return mImpl->glProgram;
302 uint32_t ProgramImpl::Retain()
304 return ++mImpl->refCount;
307 uint32_t ProgramImpl::Release()
309 return --mImpl->refCount;
312 uint32_t ProgramImpl::GetRefCount() const
314 return mImpl->refCount;
317 const GLES::Reflection& ProgramImpl::GetReflection() const
319 return *mImpl->reflection;
322 bool ProgramImpl::GetParameter(uint32_t parameterId, void* out)
324 if(parameterId == 1) // a magic number to access program id
326 *reinterpret_cast<decltype(&mImpl->glProgram)>(out) = mImpl->glProgram;
332 EglGraphicsController& ProgramImpl::GetController() const
334 return mImpl->controller;
337 const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
339 return mImpl->createInfo;
342 void ProgramImpl::UpdateStandaloneUniformBlock(const char* ptr)
344 const auto& reflection = GetReflection();
346 const auto& extraInfos = reflection.GetStandaloneUniformExtraInfo();
348 auto* gl = GetController().GetGL();
351 return; // Early out if no GL found
356 auto cachePtr = reinterpret_cast<char*>(mImpl->uniformData.data());
357 for(const auto& info : extraInfos)
359 auto& setter = mImpl->uniformSetters[index++];
360 auto offset = info.offset;
361 if(!memcmp4(&cachePtr[offset], &ptr[offset], info.size * info.arraySize))
365 case UniformSetter::Type::FLOAT:
367 (gl->*(setter.uniformfProc))(info.location, info.arraySize, reinterpret_cast<const float*>(&ptr[offset]));
370 case UniformSetter::Type::INT:
372 (gl->*(setter.uniformiProc))(info.location, info.arraySize, reinterpret_cast<const int*>(&ptr[offset]));
375 case UniformSetter::Type::MATRIX:
377 (gl->*(setter.uniformMatrixProc))(info.location, info.arraySize, GL_FALSE, reinterpret_cast<const float*>(&ptr[offset]));
380 case UniformSetter::Type::UNDEFINED:
387 memmove(mImpl->uniformData.data(), ptr, mImpl->uniformData.size());
390 void ProgramImpl::BuildStandaloneUniformCache()
392 const auto& reflection = GetReflection();
393 const auto& extraInfos = reflection.GetStandaloneUniformExtraInfo();
395 // Prepare pointers to the uniform setter calls
396 mImpl->uniformSetters.resize(extraInfos.size());
398 for(const auto& info : extraInfos)
400 auto type = GLTypeConversion(info.type).type;
401 mImpl->uniformSetters[index].type = UniformSetter::Type::UNDEFINED;
404 case GLType::FLOAT_VEC2:
406 mImpl->uniformSetters[index].uniformfProc = &GlAbstraction::Uniform2fv;
407 mImpl->uniformSetters[index].type = UniformSetter::Type::FLOAT;
410 case GLType::FLOAT_VEC3:
412 mImpl->uniformSetters[index].uniformfProc = &GlAbstraction::Uniform3fv;
413 mImpl->uniformSetters[index].type = UniformSetter::Type::FLOAT;
416 case GLType::FLOAT_VEC4:
418 mImpl->uniformSetters[index].uniformfProc = &GlAbstraction::Uniform4fv;
419 mImpl->uniformSetters[index].type = UniformSetter::Type::FLOAT;
422 case GLType::INT_VEC2:
424 mImpl->uniformSetters[index].uniformiProc = &GlAbstraction::Uniform2iv;
425 mImpl->uniformSetters[index].type = UniformSetter::Type::INT;
428 case GLType::INT_VEC3:
430 mImpl->uniformSetters[index].uniformiProc = &GlAbstraction::Uniform3iv;
431 mImpl->uniformSetters[index].type = UniformSetter::Type::INT;
434 case GLType::INT_VEC4:
436 mImpl->uniformSetters[index].uniformiProc = &GlAbstraction::Uniform4iv;
437 mImpl->uniformSetters[index].type = UniformSetter::Type::INT;
442 mImpl->uniformSetters[index].uniformiProc = &GlAbstraction::Uniform1iv;
443 mImpl->uniformSetters[index].type = UniformSetter::Type::INT;
447 case GLType::BOOL_VEC2:
448 case GLType::BOOL_VEC3:
449 case GLType::BOOL_VEC4:
452 mImpl->uniformSetters[index].uniformfProc = &GlAbstraction::Uniform1fv;
453 mImpl->uniformSetters[index].type = UniformSetter::Type::FLOAT;
456 case GLType::FLOAT_MAT2:
458 mImpl->uniformSetters[index].uniformMatrixProc = &GlAbstraction::UniformMatrix2fv;
459 mImpl->uniformSetters[index].type = UniformSetter::Type::MATRIX;
462 case GLType::FLOAT_MAT3:
464 mImpl->uniformSetters[index].uniformMatrixProc = &GlAbstraction::UniformMatrix3fv;
465 mImpl->uniformSetters[index].type = UniformSetter::Type::MATRIX;
468 case GLType::FLOAT_MAT4:
470 mImpl->uniformSetters[index].uniformMatrixProc = &GlAbstraction::UniformMatrix4fv;
471 mImpl->uniformSetters[index].type = UniformSetter::Type::MATRIX;
474 case GLType::SAMPLER_2D:
475 case GLType::SAMPLER_CUBE:
486 // Destroy GL resources of implementation. This should happen
487 // only if there's no more pipelines using this program so
488 // it is safe to do it in the destructor
489 if(!mProgram->Release())
495 const GLES::Reflection& Program::GetReflection() const
497 return mProgram->GetReflection();
500 EglGraphicsController& Program::GetController() const
502 return GetImplementation()->GetController();
505 const ProgramCreateInfo& Program::GetCreateInfo() const
507 return GetImplementation()->GetCreateInfo();
510 void Program::DiscardResource()
512 GetController().DiscardResource(this);
515 }; // namespace Dali::Graphics::GLES