diff options
author | Adam Bialogonski <adam.b@samsung.com> | 2024-07-03 14:44:09 +0100 |
---|---|---|
committer | Adam Bialogonski <adam.b@samsung.com> | 2024-07-03 18:07:01 +0100 |
commit | f27d4f47f4680c79278cf73acdaf1fee03df5147 (patch) | |
tree | dc338f874bfdee243de71fe800019372e666f3b8 /dali | |
parent | 063faff1bee7cd88ce4e4c3e8a9ceb79a52a5f54 (diff) | |
download | dali-adaptor-f27d4f47f4680c79278cf73acdaf1fee03df5147.tar.gz dali-adaptor-f27d4f47f4680c79278cf73acdaf1fee03df5147.tar.bz2 dali-adaptor-f27d4f47f4680c79278cf73acdaf1fee03df5147.zip |
Shader processor
Shader process that uses DALi own shader syntax and
translates it into GLES2, GLES3 or Vulkan compatible
shaders.
Change-Id: I83f7370e5ced6f85f50fa3937f196f10ecdb3323
Diffstat (limited to 'dali')
-rw-r--r-- | dali/internal/graphics/common/shader-parser.cpp | 539 | ||||
-rw-r--r-- | dali/internal/graphics/common/shader-parser.h | 94 | ||||
-rw-r--r-- | dali/internal/graphics/file.list | 1 | ||||
-rw-r--r-- | dali/internal/graphics/gles-impl/gles-graphics-program.cpp | 83 | ||||
-rw-r--r-- | dali/internal/graphics/gles-impl/gles-graphics-program.h | 7 | ||||
-rw-r--r-- | dali/internal/graphics/gles-impl/gles-graphics-shader.cpp | 65 | ||||
-rw-r--r-- | dali/internal/graphics/gles-impl/gles-graphics-shader.h | 46 |
7 files changed, 812 insertions, 23 deletions
diff --git a/dali/internal/graphics/common/shader-parser.cpp b/dali/internal/graphics/common/shader-parser.cpp new file mode 100644 index 000000000..0447c8e86 --- /dev/null +++ b/dali/internal/graphics/common/shader-parser.cpp @@ -0,0 +1,539 @@ +/* +* Copyright (c) 2024 Samsung Electronics Co., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +#include <dali/internal/graphics/common/shader-parser.h> +#include <sstream> + +namespace Dali::Internal::ShaderParser +{ +CodeLine TokenizeLine(std::string line) +{ + std::regex word_regex("(\\w+)"); + auto words_begin = + std::sregex_iterator(line.begin(), line.end(), word_regex); + auto words_end = std::sregex_iterator(); + + CodeLine lineOfCode; + lineOfCode.line = line; + + for(auto it = words_begin; it != words_end; ++it) + { + const std::smatch& match = *it; + lineOfCode.tokens.emplace_back(match.position(), match.length()); + } + return lineOfCode; +} + +std::string GetToken(CodeLine& line, int i) +{ + // Function allows retrieving a token from start and from the + // end of line. Negative 'i' retrieves token from the end. For example: + // GetToken( line, -1 ) - retrieves last token + // GetToken( line, 0 ) - retrieves first token + // GetToken( line, 1 ) - retrieves second (counting from 0) token + if(abs(i) >= line.tokens.size()) + { + return ""; + } + if(i < 0) + { + i = int(line.tokens.size()) + i; + } + return std::string(std::string_view(&line.line[line.tokens[i].first], line.tokens[i].second)); +} + +void TokenizeSource(Program& program, ShaderStage stage, std::istream& ss) +{ + Shader* output{nullptr}; + if(stage == ShaderStage::VERTEX) + { + output = &program.vertexShader; + } + else if(stage == ShaderStage::FRAGMENT) + { + output = &program.fragmentShader; + } + + // Invalid shader stage + if(output == nullptr) + { + return; + } + + std::string line; + bool ignoreLines = false; + int lineNumber = 0; + output->customOutputLineIndex = -1; // Assume using gl_FragColor in fragment shader, no index for custom output + output->mainLine = -1; + while(std::getline(ss, line)) + { + // turn ignoring on + if(line.substr(0, 12) == "//@ignore:on") + { + ignoreLines = true; + continue; + } + + // turn ignoring off + if(ignoreLines) + { + if(line.substr(0, 13) == "//@ignore:off") + { + ignoreLines = false; + } + continue; + } + + CodeLine lineOfCode = TokenizeLine(line); + + // find out whether fragment shader contains OUTPUT + if(!lineOfCode.tokens.empty() && stage == ShaderStage::FRAGMENT) + { + // Look for at least one OUTPUT int the fragment shader if written + // for GLSL3. If there is no OUTPUT we assume programmers used GLSL2 + // gl_FragColor. + if(output->customOutputLineIndex < 0) + { + if(GetToken(lineOfCode, 0) == "OUTPUT") + { + output->customOutputLineIndex = output->codeLines.size(); + } + } + // find main function + if(output->mainLine < 0 && GetToken(lineOfCode, 0) == "void" && GetToken(lineOfCode, 1) == "main") + { + output->mainLine = lineNumber; + } + } + + output->codeLines.emplace_back(std::move(lineOfCode)); + lineNumber++; + } +} + +void TokenizeSource(Program& program, ShaderStage stage, const std::string& sourceCodeString) +{ + std::stringstream ss(sourceCodeString); + TokenizeSource(program, stage, ss); +} + +template<class IT> +bool ProcessTokenINPUT(IT& it, Program& program, OutputLanguage lang, ShaderStage stage) +{ + int attributeLocation = 0; + auto& l = *it; + std::string& outString = ((stage == ShaderStage::VERTEX) ? program.vertexShader.output : program.fragmentShader.output); + std::stringstream ss; + if(l.tokens.size()) + { + auto token = GetToken(l, 0); + if(token == "INPUT") + { + auto varName = GetToken(l, -1); + if(lang == OutputLanguage::SPIRV_GLSL) + { + // For vertex stage input locations are incremental + int location = 0; + if(stage == ShaderStage::VERTEX) + { + location = attributeLocation++; + } + else + { + auto iter = program.varyings.find(varName); + if(iter != program.varyings.end()) + { + location = (*iter).second; + } + } + + ss << "layout(location = " << location << ") in" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + outString += ss.str(); + return true; + } + else if(lang == OutputLanguage::GLSL3) + { + ss << "in" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + outString += ss.str(); + return true; + } + else if(lang == OutputLanguage::GLSL2) + { + if(stage == ShaderStage::VERTEX) + { + ss << "attribute" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + } + else + { + ss << "varying" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + } + outString += ss.str(); + return true; + } + } + } + return false; +} + +template<class IT> +bool ProcessTokenOUTPUT(IT& it, Program& program, OutputLanguage lang, ShaderStage stage) +{ + std::string& outString = ((stage == ShaderStage::VERTEX) ? program.vertexShader.output : program.fragmentShader.output); + auto& l = *it; + if(l.tokens.size()) + { + auto token = GetToken(l, 0); + if(token == "OUTPUT") + { + if(lang == OutputLanguage::SPIRV_GLSL) + { + int location = -1; // invalid location + std::stringstream ss; + if(stage == ShaderStage::VERTEX) + { + auto varName = GetToken(l, -1); + // compare varyings map + auto iter = program.varyings.find(varName); + if(iter != program.varyings.end()) + { + location = (*iter).second; + } + // SPIRV requires storing locations + ss << "layout(location=" << location << ") out" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + outString += ss.str(); + } + else + { + // for fragment shader the gl_FragColor is our output + // we will use OUT_COLOR + auto varName = GetToken(l, -1); + ss << "out" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + outString += ss.str(); + } + return true; + } + else if(lang == OutputLanguage::GLSL3) + { + std::stringstream ss; + ss << "out" + << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + outString += ss.str(); + return true; + } + else if(lang == OutputLanguage::GLSL2) + { + std::stringstream ss; + if(stage == ShaderStage::VERTEX) + { + ss << "varying" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + outString += ss.str(); + } + else + { + // get output variable name + auto& cl = program.fragmentShader.codeLines[program.fragmentShader.customOutputLineIndex]; + auto varname = GetToken(cl, -1); + ss << "#define " << varname << " gl_FragColor\n"; + outString += ss.str(); + } + return true; + } + } + } + return false; +} + +template<class IT> +bool ProcessTokenUNIFORM_BLOCK(IT& it, Program& program, OutputLanguage lang, ShaderStage stage) +{ + auto& l = *it; + int& binding = program.uboBinding; + std::string& outStr = (stage == ShaderStage::VERTEX) ? program.vertexShader.output : program.fragmentShader.output; + std::stringstream ss; + if(l.tokens.size()) + { + auto token = GetToken(l, 0); + if(token == "UNIFORM_BLOCK") + { + bool gles3plus = false; + if(lang == OutputLanguage::SPIRV_GLSL) + { + ss << "layout(set=0, binding=" << binding << ", std140) uniform" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + binding++; + gles3plus = true; + } + else if(lang == OutputLanguage::GLSL3) + { + ss << "layout(std140) uniform" << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + gles3plus = true; + } + if(gles3plus) // remove word UNIFORM for gles3+/spirv + { + // iterate block + l = (*++it); + while(l.line.find('}') == std::string::npos) + { + auto isUniform = (GetToken(l, 0) == "UNIFORM"); + if(isUniform) + { + ss << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + } + else + { + ss << l.line << "\n"; + } + l = *(++it); + } + ss << "};\n"; + } + else if(lang == OutputLanguage::GLSL2) + { + while(l.line.find('{') == std::string::npos) + { + l = *(++it); + } + l = *(++it); + while(l.line.find('}') == std::string::npos) + { + auto isUniform = (GetToken(l, 0) == "UNIFORM"); + if(isUniform) + { + ss << "uniform " << l.line.substr(l.tokens[0].first + l.tokens[0].second).c_str() << "\n"; + } + else + { + ss << l.line << "\n"; + } + l = *(++it); + } + } + } + auto str = ss.str(); + if(str.empty()) + { + return false; + } + outStr += str; + return true; + } + return false; +} + +// Links inputs and outputs of two stages and assigns +// location +void LinkProgram(Program& program) +{ + int location = 0; + for(auto& line : program.vertexShader.codeLines) + { + auto token = GetToken(line, 0); + if(token == std::string("OUTPUT")) + { + auto varname = GetToken(line, -1); + program.varyings[varname] = location++; + } + } + // Verify + for(auto& line : program.fragmentShader.codeLines) + { + auto token = GetToken(line, 0); + if(token == std::string("INPUT")) + { + auto varname = GetToken(line, -1); + } + } +} + +void ProcessStage(Program& program, ShaderStage stage, OutputLanguage language) +{ + auto& codeLines = ((stage == ShaderStage::VERTEX) ? program.vertexShader.codeLines : program.fragmentShader.codeLines); + auto& outString = ((stage == ShaderStage::VERTEX) ? program.vertexShader.output : program.fragmentShader.output); + + int lineNum = 0; + bool textureDone = false; + + // add OUTPUT to the fragment shader if it's not defined (then we assume gl_FragColor has been used) + if(stage == ShaderStage::FRAGMENT && + program.fragmentShader.customOutputLineIndex < 0 && + program.fragmentShader.mainLine >= 0 && + language != OutputLanguage::GLSL2) + { + // Push tokenized extra line into the code that defines the output + // we add output as _glFragColor and define + std::string line1 = "OUTPUT mediump vec4 _glFragColor;"; + program.fragmentShader.codeLines.insert(program.fragmentShader.codeLines.begin() + program.fragmentShader.mainLine, TokenizeLine(line1)); + line1 = "#define gl_FragColor _glFragColor"; + program.fragmentShader.codeLines.insert(program.fragmentShader.codeLines.begin() + program.fragmentShader.mainLine, TokenizeLine(line1)); + } + + for(auto it = codeLines.begin(); it != codeLines.end(); ++it) + { + auto& line = *it; + if(lineNum > 0 && !textureDone) + { + textureDone = true; + // Add texture macro + if(language == OutputLanguage::GLSL2) + { + outString += "\n#define TEXTURE texture2D\n"; + } + else + { + outString += "\n#define TEXTURE texture\n"; + } + } + lineNum++; + // no tokens (shouldn't happen?) + if(line.tokens.empty()) + { + outString += line.line + "\n"; + continue; + } + + auto res = ProcessTokenINPUT(it, program, language, stage); + if(!res) + { + res = ProcessTokenOUTPUT(it, program, language, stage); + } + if(!res) + { + res = ProcessTokenUNIFORM_BLOCK(it, program, language, stage); + } + if(!res) + { + outString += line.line + "\n"; + } + } +} + +void Parse(const ShaderParserInfo& parseInfo, std::vector<std::string>& output) +{ + auto vs = std::istringstream(*parseInfo.vertexShaderCode); + auto fs = std::istringstream(*parseInfo.fragmentShaderCode); + + output.resize(2); + + // Create program + Program program; + + if(parseInfo.vertexShaderLegacyVersion) + { + output[0] = *parseInfo.vertexShaderCode; + } + else + { + TokenizeSource(program, ShaderStage::VERTEX, vs); + } + + if(parseInfo.fragmentShaderLegacyVersion) + { + output[1] = *parseInfo.fragmentShaderCode; + } + else + { + TokenizeSource(program, ShaderStage::FRAGMENT, fs); + } + + // Pick the right GLSL dialect and version based on provided shaders + if(parseInfo.vertexShaderLegacyVersion && parseInfo.fragmentShaderLegacyVersion) + { + // Not touching any shaders, return current code as output + return; + } + else if(!parseInfo.vertexShaderLegacyVersion && !parseInfo.fragmentShaderLegacyVersion) + { + // Both shaders need processing and linking + // Assign the shader version. Since both stages are being converted + // the version can be assumed. + if(parseInfo.language == OutputLanguage::GLSL3) + { + program.vertexShader.output += "#version 320 es\n"; + program.fragmentShader.output += "#version 320 es\n"; + } + else if(parseInfo.language == OutputLanguage::GLSL2) + { + program.vertexShader.output += "#version 100\n"; + program.fragmentShader.output += "#version 100\n"; + } + else if(parseInfo.language == OutputLanguage::SPIRV_GLSL) + { + program.vertexShader.output += "#version 430\n"; + program.fragmentShader.output += "#version 430\n"; + } + + // link inputs and outputs between vertex and fragment shader + LinkProgram(program); + + ProcessStage(program, ShaderStage::VERTEX, parseInfo.language); + ProcessStage(program, ShaderStage::FRAGMENT, parseInfo.language); + + output[0] = std::move(program.vertexShader.output); + output[1] = std::move(program.fragmentShader.output); + } + else + { + // Case: only vertex shader is modern + if(!parseInfo.vertexShaderLegacyVersion) + { + // update #version + std::string suffix(parseInfo.outputVersion < 200 ? std::string("\n") : std::string(" es\n")); + program.vertexShader.output += std::string("#version ") + std::to_string(parseInfo.outputVersion) + suffix; + program.fragmentShader.output += std::string("#version ") + std::to_string(parseInfo.outputVersion) + suffix; + + auto language = parseInfo.language; + if(parseInfo.language != OutputLanguage::SPIRV_GLSL) + { + if(parseInfo.outputVersion < 200) + { + language = OutputLanguage::GLSL2; + } + else + { + language = OutputLanguage::GLSL3; + } + } + ProcessStage(program, ShaderStage::VERTEX, language); + output[0] = std::move(program.vertexShader.output); + } + // Case: only fragment shader is modern + else + { + // update #version + std::string suffix(parseInfo.outputVersion < 200 ? std::string("\n") : std::string(" es\n")); + program.vertexShader.output += std::string("#version ") + std::to_string(parseInfo.outputVersion) + suffix; + program.fragmentShader.output += std::string("#version ") + std::to_string(parseInfo.outputVersion) + suffix; + + auto language = parseInfo.language; + if(parseInfo.language != OutputLanguage::SPIRV_GLSL) + { + if(parseInfo.outputVersion < 200) + { + language = OutputLanguage::GLSL2; + } + else + { + language = OutputLanguage::GLSL3; + } + } + + ProcessStage(program, ShaderStage::FRAGMENT, language); + output[1] = std::move(program.fragmentShader.output); + } + } +} + +} // namespace Dali::Internal::ShaderParser
\ No newline at end of file diff --git a/dali/internal/graphics/common/shader-parser.h b/dali/internal/graphics/common/shader-parser.h new file mode 100644 index 000000000..376fb7165 --- /dev/null +++ b/dali/internal/graphics/common/shader-parser.h @@ -0,0 +1,94 @@ +#ifndef DALI_INTERNAL_GRAPHICS_SHADER_PARSER_H +#define DALI_INTERNAL_GRAPHICS_SHADER_PARSER_H + +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include <map> +#include <regex> +#include <string> +#include <utility> +#include <vector> + +namespace Dali::Internal::ShaderParser +{ +/** + * Defines parser shader stages + */ +enum class ShaderStage +{ + VERTEX, + FRAGMENT +}; + +/** + * Defines output shader GLSL dialects + */ +enum class OutputLanguage +{ + GLSL2, + GLSL3, + SPIRV_GLSL +}; + +using CodeTokenPair = std::pair<int, int>; +struct CodeLine +{ + std::vector<CodeTokenPair> tokens; + std::vector<std::string> replacement; + std::string line; +}; + +struct Shader +{ + std::vector<CodeLine> codeLines; + std::string output; + int customOutputLineIndex; + int mainLine; +}; + +struct Program +{ + Shader vertexShader; + Shader fragmentShader; + std::map<std::string, int> varyings; + int uboBinding{0}; +}; + +struct ShaderParserInfo +{ + const std::string* vertexShaderCode; + const std::string* fragmentShaderCode; + + uint32_t vertexShaderLegacyVersion; + uint32_t fragmentShaderLegacyVersion; + + OutputLanguage language; + uint32_t outputVersion; +}; + +/** + * Parses given source code and returns requested variant of shader + * @param[in] parseInfo Valid ShaderParserInfo structure + * @param[out] output Output strings + */ +void Parse(const ShaderParserInfo& parseInfo, std::vector<std::string>& output); + +} // namespace Dali::Internal::ShaderParser + +#endif
\ No newline at end of file diff --git a/dali/internal/graphics/file.list b/dali/internal/graphics/file.list index 3f8b5dd45..006e744f4 100644 --- a/dali/internal/graphics/file.list +++ b/dali/internal/graphics/file.list @@ -9,6 +9,7 @@ SET( adaptor_graphics_gles_src_files ${adaptor_graphics_dir}/gles/gl-proxy-implementation.cpp ${adaptor_graphics_dir}/gles/egl-graphics-factory.cpp ${adaptor_graphics_dir}/gles/egl-graphics.cpp + ${adaptor_graphics_dir}/common/shader-parser.cpp ) INCLUDE( ${adaptor_graphics_dir}/gles-impl/file.list ) diff --git a/dali/internal/graphics/gles-impl/gles-graphics-program.cpp b/dali/internal/graphics/gles-impl/gles-graphics-program.cpp index dcd09669f..d0636fc76 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-program.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-program.cpp @@ -18,13 +18,13 @@ #include "gles-graphics-program.h" // INTERNAL HEADERS +#include <dali/internal/graphics/common/shader-parser.h> #include "egl-graphics-controller.h" #include "gles-graphics-reflection.h" #include "gles-graphics-shader.h" // EXTERNAL HEADERS -#include <dali/integration-api/gl-abstraction.h> -#include <dali/integration-api/gl-defines.h> +#include <iostream> #if defined(DEBUG_ENABLED) Debug::Filter* gGraphicsProgramLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GRAPHICS_PROGRAM"); @@ -135,6 +135,80 @@ bool ProgramImpl::Destroy() return false; } +void ProgramImpl::Preprocess() +{ + // For now only Vertex and Fragment shader stages supported + // and one per stage + std::string vertexString; + std::string fragmentString; + std::string* currentString = nullptr; + + const GLES::Shader* vsh = nullptr; + const GLES::Shader* fsh = nullptr; + + const auto& info = mImpl->createInfo; + + for(const auto& state : *info.shaderState) + { + const auto* shader = static_cast<const GLES::Shader*>(state.shader); + if(state.pipelineStage == PipelineStage::VERTEX_SHADER) + { + // Only TEXT source mode can be processed + currentString = &vertexString; + vsh = shader; + } + else if(state.pipelineStage == PipelineStage::FRAGMENT_SHADER) + { + // Only TEXT source mode can be processed + currentString = &fragmentString; + fsh = shader; + } + else + { + // no valid stream to push + currentString = nullptr; + DALI_LOG_ERROR("Shader state contains invalid shader source (most likely binary)! Can't process!"); + } + + // Check if stream valid + if(currentString && currentString->empty() && shader->GetCreateInfo().sourceMode == ShaderSourceMode::TEXT) + { + *currentString = std::string(reinterpret_cast<const char*>(shader->GetCreateInfo().sourceData), + shader->GetCreateInfo().sourceSize); + } + else + { + DALI_LOG_ERROR("Preprocessing of binary shaders isn't allowed!"); + } + } + + // if we have both streams ready + if(!vertexString.empty() && !fragmentString.empty()) + { + // In case we have one modern shader and one legacy counterpart we need to enforce + // output language. + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexString; + parseInfo.fragmentShaderCode = &fragmentString; + parseInfo.vertexShaderLegacyVersion = vsh->GetGLSLVersion(); + parseInfo.fragmentShaderLegacyVersion = fsh->GetGLSLVersion(); + parseInfo.language = Internal::ShaderParser::OutputLanguage::GLSL3; // We default to GLSL3 + parseInfo.outputVersion = std::max(vsh->GetGLSLVersion(), fsh->GetGLSLVersion()); + + std::vector<std::string> newShaders; + + Internal::ShaderParser::Parse(parseInfo, newShaders); + + // substitute shader code + vsh->GetImplementation()->SetPreprocessedCode(newShaders[0].data(), newShaders[0].size()); + fsh->GetImplementation()->SetPreprocessedCode(newShaders[1].data(), newShaders[1].size()); + } + else + { + DALI_LOG_ERROR("Preprocessing shader code failed!"); + } +} + bool ProgramImpl::Create() { // Create and link new program @@ -150,6 +224,9 @@ bool ProgramImpl::Create() DALI_LOG_DEBUG_INFO("Program[%s] create program id : %u\n", mImpl->name.c_str(), program); const auto& info = mImpl->createInfo; + + Preprocess(); + for(const auto& state : *info.shaderState) { const auto* shader = static_cast<const GLES::Shader*>(state.shader); @@ -175,7 +252,7 @@ bool ProgramImpl::Create() gl->GetProgramInfoLog(program, 4096, &size, output); // log on error - DALI_LOG_ERROR("glLinkProgam[%s] failed:\n%s\n", mImpl->name.c_str(), output); + DALI_LOG_ERROR("glLinkProgram[%s] failed:\n%s\n", mImpl->name.c_str(), output); gl->DeleteProgram(program); return false; } diff --git a/dali/internal/graphics/gles-impl/gles-graphics-program.h b/dali/internal/graphics/gles-impl/gles-graphics-program.h index ee8a888b9..cc89b71b0 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-program.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-program.h @@ -2,7 +2,7 @@ #define DALI_GRAPHICS_GLES_PROGRAM_H /* - * Copyright (c) 2023 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,6 +66,11 @@ public: bool Create(); /** + * @brief Preprocesses shaders + */ + void Preprocess(); + + /** * @brief Returns GL program id * * @return GL program id diff --git a/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp b/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp index 36ffd27ce..2ee3020fd 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp @@ -39,7 +39,7 @@ struct ShaderImpl::Impl size_t dataStartIndex = 0; size_t dataSize; - ShaderImpl::StripLegacyCodeIfNeeded( _createInfo, dataStartIndex, dataSize ); + ShaderImpl::StripLegacyCodeIfNeeded(_createInfo, dataStartIndex, glslVersion, dataSize); source.resize(dataSize); std::copy(reinterpret_cast<const uint8_t*>(_createInfo.sourceData) + dataStartIndex, @@ -106,8 +106,8 @@ struct ShaderImpl::Impl if(pipelineStage) { auto shader = gl->CreateShader(pipelineStage); - const auto src = reinterpret_cast<const char*>(createInfo.sourceData); - GLint size = createInfo.sourceSize; + const auto src = !sourcePreprocessed.empty() ? reinterpret_cast<const char*>(sourcePreprocessed.data()) : reinterpret_cast<const char*>(createInfo.sourceData); + GLint size = !sourcePreprocessed.empty() ? GLint(sourcePreprocessed.size()) : createInfo.sourceSize; gl->ShaderSource(shader, 1, const_cast<const char**>(&src), &size); gl->CompileShader(shader); @@ -118,7 +118,7 @@ struct ShaderImpl::Impl char output[4096]; GLsizei outputSize{0u}; gl->GetShaderInfoLog(shader, 4096, &outputSize, output); - DALI_LOG_ERROR("Code: %.*s\n", size, reinterpret_cast<const char*>(createInfo.sourceData)); + DALI_LOG_ERROR("Code: %.*s\n", size, reinterpret_cast<const char*>(src)); DALI_LOG_ERROR("glCompileShader() failed: \n%s\n", output); gl->DeleteShader(shader); return false; @@ -141,13 +141,24 @@ struct ShaderImpl::Impl } } + void SetPreprocessedCode(void* data, uint32_t size) + { + sourcePreprocessed.resize(size); + + std::copy(reinterpret_cast<const uint8_t*>(data), + reinterpret_cast<const uint8_t*>(data) + size, + sourcePreprocessed.data()); + } + EglGraphicsController& controller; ShaderCreateInfo createInfo; std::vector<uint8_t> source{}; + std::vector<uint8_t> sourcePreprocessed{}; uint32_t glShader{}; uint32_t refCount{0u}; - uint32_t flushCount{0u}; ///< Number of frames at refCount=0 + uint32_t flushCount{0u}; ///< Number of frames at refCount=0 + uint32_t glslVersion{0u}; ///< 0 - unknown, otherwise valid #version like 130, 300, etc. }; ShaderImpl::ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller) @@ -191,6 +202,11 @@ uint32_t ShaderImpl::Release() return mImpl->flushCount; } +[[nodiscard]] uint32_t ShaderImpl::GetGLSLVersion() const +{ + return mImpl->glslVersion; +} + /** * @brief Compiles shader * @@ -216,12 +232,13 @@ const ShaderCreateInfo& ShaderImpl::GetCreateInfo() const return mImpl->controller; } -void ShaderImpl::StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, size_t& finalDataSize) +void ShaderImpl::StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, uint32_t& glslVersion, size_t& finalDataSize) { // Make a copy of source code. if code is meant to be used // by modern parser, skip the prefix part - auto text = reinterpret_cast<const char*>(info.sourceData); + auto text = reinterpret_cast<const char*>(info.sourceData); auto result = std::string_view(text).find("//@legacy-prefix-end"); + glslVersion = 0u; if(info.shaderVersion != 0) { if(result != 0 && result != std::string::npos) @@ -238,23 +255,30 @@ void ShaderImpl::StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& s { // For legacy shaders we need to make sure that the #version is a very first line // so need to strip //@legacy-prefix-end tag - if(result != std::string::npos) + auto versionPos = std::string_view(text).find("#version", 0); + if(versionPos == std::string::npos) { - auto versionPos = std::string_view(text).find("#version", result); - if(versionPos == std::string::npos) - { - DALI_LOG_ERROR("Shader processing: new-line missing after @legacy-prefix-end!\n"); - startIndex = 0; // not trimming anything - } - else - { - startIndex = versionPos; - } + startIndex = 0; // not trimming anything + + // if there's no version yet it's a legacy shader we assign 100 + glslVersion = 100; + } + else + { + // save version of legacy shader + char* end; + glslVersion = uint32_t(std::strtol(std::string_view(text).data() + versionPos + 9, &end, 10)); + startIndex = versionPos; } } finalDataSize = info.sourceSize - startIndex; } +void ShaderImpl::SetPreprocessedCode(void* data, uint32_t size) +{ + mImpl->SetPreprocessedCode(data, size); +} + Shader::~Shader() { if(!mShader->Release()) @@ -277,4 +301,9 @@ void Shader::DiscardResource() } } +uint32_t Shader::GetGLSLVersion() const +{ + return GetImplementation()->GetGLSLVersion(); +} + } // namespace Dali::Graphics::GLES diff --git a/dali/internal/graphics/gles-impl/gles-graphics-shader.h b/dali/internal/graphics/gles-impl/gles-graphics-shader.h index 45fce49c5..ac85822e0 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-shader.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-shader.h @@ -36,12 +36,28 @@ public: * @param[in] controller Reference to the controller */ ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller); + + /** + * @brief destructor + */ ~ShaderImpl(); + /** + * @brief Increases ref count + * @return ref count after increment + */ uint32_t Retain(); + /** + * @brief Decreases refcount + * @return ref count after decrement + */ uint32_t Release(); + /** + * @brief returns current ref count + * @return current ref count + */ [[nodiscard]] uint32_t GetRefCount() const; /** @@ -70,19 +86,45 @@ public: */ void Destroy(); + /** + * @brief Returns GL resource + * @return Valid GL shader resource + */ uint32_t GetGLShader() const; + /** + * @brief Returns create info structure + * @return Returns valid create info structure + */ [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const; + /** + * @brief Returns reference to the graphics controller + * @return Valid reference to the graphics controller + */ [[nodiscard]] EglGraphicsController& GetController() const; /** * Strips legacy prefix fromt he GLSL source code if necessary * @param info valid ShaderCreateInfo strucutre * @param[out] startIndex Start index of the source code + * @param[out] glslVersion Detected GLSL version of legacy shader * @param[out] finalDataSize Size of trimmed data */ - static void StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, size_t& finalDataSize); + static void StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, uint32_t& glslVersion, size_t& finalDataSize); + + /** + * @brief Sets preprocess code + * @param[in] data Valid pointer to the new source code + * @param[in] size Size of the source code + */ + void SetPreprocessedCode(void* data, uint32_t size); + + /** + * @brief Returns GLSL version + * @return Returns valid GLSL version or 0 if undefined + */ + [[nodiscard]] uint32_t GetGLSLVersion() const; private: friend class Shader; @@ -147,6 +189,8 @@ public: // nothing to do here } + [[nodiscard]] uint32_t GetGLSLVersion() const; + private: ShaderImpl* mShader{nullptr}; }; |