From f27d4f47f4680c79278cf73acdaf1fee03df5147 Mon Sep 17 00:00:00 2001 From: Adam Bialogonski Date: Wed, 3 Jul 2024 14:44:09 +0100 Subject: [PATCH] Shader processor Shader process that uses DALi own shader syntax and translates it into GLES2, GLES3 or Vulkan compatible shaders. Change-Id: I83f7370e5ced6f85f50fa3937f196f10ecdb3323 --- .../resources/shaders/canvas-view-with-output.frag | 22 + .../shaders/canvas-view-with-output.frag.gles2 | 15 + .../shaders/canvas-view-with-output.frag.gles3 | 18 + .../canvas-view-with-output.frag.glsl-spirv | 18 + automated-tests/resources/shaders/canvas-view.frag | 20 + .../resources/shaders/canvas-view.frag.gles2 | 13 + .../resources/shaders/canvas-view.frag.gles3 | 18 + .../resources/shaders/canvas-view.frag.glsl-spirv | 18 + automated-tests/resources/shaders/canvas-view.vert | 20 + .../resources/shaders/canvas-view.vert.gles2 | 13 + .../resources/shaders/canvas-view.vert.gles3 | 16 + .../resources/shaders/canvas-view.vert.glsl-spirv | 16 + automated-tests/src/dali-graphics/CMakeLists.txt | 1 + .../src/dali-graphics/utc-Dali-GraphicsShader.cpp | 28 +- .../utc-Dali-GraphicsShaderParser.cpp | 271 +++++++++++ dali/internal/graphics/common/shader-parser.cpp | 539 +++++++++++++++++++++ dali/internal/graphics/common/shader-parser.h | 94 ++++ dali/internal/graphics/file.list | 1 + .../graphics/gles-impl/gles-graphics-program.cpp | 83 +++- .../graphics/gles-impl/gles-graphics-program.h | 7 +- .../graphics/gles-impl/gles-graphics-shader.cpp | 65 ++- .../graphics/gles-impl/gles-graphics-shader.h | 46 +- 22 files changed, 1307 insertions(+), 35 deletions(-) create mode 100644 automated-tests/resources/shaders/canvas-view-with-output.frag create mode 100644 automated-tests/resources/shaders/canvas-view-with-output.frag.gles2 create mode 100644 automated-tests/resources/shaders/canvas-view-with-output.frag.gles3 create mode 100644 automated-tests/resources/shaders/canvas-view-with-output.frag.glsl-spirv create mode 100644 automated-tests/resources/shaders/canvas-view.frag create mode 100644 automated-tests/resources/shaders/canvas-view.frag.gles2 create mode 100644 automated-tests/resources/shaders/canvas-view.frag.gles3 create mode 100644 automated-tests/resources/shaders/canvas-view.frag.glsl-spirv create mode 100644 automated-tests/resources/shaders/canvas-view.vert create mode 100644 automated-tests/resources/shaders/canvas-view.vert.gles2 create mode 100644 automated-tests/resources/shaders/canvas-view.vert.gles3 create mode 100644 automated-tests/resources/shaders/canvas-view.vert.glsl-spirv create mode 100644 automated-tests/src/dali-graphics/utc-Dali-GraphicsShaderParser.cpp create mode 100644 dali/internal/graphics/common/shader-parser.cpp create mode 100644 dali/internal/graphics/common/shader-parser.h diff --git a/automated-tests/resources/shaders/canvas-view-with-output.frag b/automated-tests/resources/shaders/canvas-view-with-output.frag new file mode 100644 index 0000000..d7b51ec --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view-with-output.frag @@ -0,0 +1,22 @@ +//@ignore:on +#define UNIFORM_BLOCK uniform +#define UNIFORM uniform +#define INPUT in +#define OUTPUT out +#define OUT_COLOR gl_FragColor +//@ignore:off + +UNIFORM_BLOCK FragBlock +{ + UNIFORM lowp vec4 uColor; +}; + +INPUT mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +OUTPUT mediump vec4 fragColor; + +void main() +{ + fragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view-with-output.frag.gles2 b/automated-tests/resources/shaders/canvas-view-with-output.frag.gles2 new file mode 100644 index 0000000..9e471ea --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view-with-output.frag.gles2 @@ -0,0 +1,15 @@ +#version 100 + + +#define TEXTURE texture2D +uniform lowp vec4 uColor; + +varying mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +#define fragColor gl_FragColor + +void main() +{ + fragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view-with-output.frag.gles3 b/automated-tests/resources/shaders/canvas-view-with-output.frag.gles3 new file mode 100644 index 0000000..91b5774 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view-with-output.frag.gles3 @@ -0,0 +1,18 @@ +#version 320 es + + +#define TEXTURE texture +layout(std140) uniform FragBlock +{ + lowp vec4 uColor; +}; + +in mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +out mediump vec4 fragColor; + +void main() +{ + fragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view-with-output.frag.glsl-spirv b/automated-tests/resources/shaders/canvas-view-with-output.frag.glsl-spirv new file mode 100644 index 0000000..ffa5d20 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view-with-output.frag.glsl-spirv @@ -0,0 +1,18 @@ +#version 430 + + +#define TEXTURE texture +layout(set=0, binding=1, std140) uniform FragBlock +{ + lowp vec4 uColor; +}; + +layout(location = 0) in mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +out mediump vec4 fragColor; + +void main() +{ + fragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view.frag b/automated-tests/resources/shaders/canvas-view.frag new file mode 100644 index 0000000..b3a4d06 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.frag @@ -0,0 +1,20 @@ +//@ignore:on +#define UNIFORM_BLOCK uniform +#define UNIFORM uniform +#define INPUT in +#define OUTPUT out +#define OUT_COLOR gl_FragColor +//@ignore:off + +UNIFORM_BLOCK FragBlock +{ + UNIFORM lowp vec4 uColor; +}; + +INPUT mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +void main() +{ + gl_FragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view.frag.gles2 b/automated-tests/resources/shaders/canvas-view.frag.gles2 new file mode 100644 index 0000000..a53d664 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.frag.gles2 @@ -0,0 +1,13 @@ +#version 100 + + +#define TEXTURE texture2D +uniform lowp vec4 uColor; + +varying mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +void main() +{ + gl_FragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view.frag.gles3 b/automated-tests/resources/shaders/canvas-view.frag.gles3 new file mode 100644 index 0000000..0f9435f --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.frag.gles3 @@ -0,0 +1,18 @@ +#version 320 es + + +#define TEXTURE texture +layout(std140) uniform FragBlock +{ + lowp vec4 uColor; +}; + +in mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +#define gl_FragColor _glFragColor +out mediump vec4 _glFragColor; +void main() +{ + gl_FragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view.frag.glsl-spirv b/automated-tests/resources/shaders/canvas-view.frag.glsl-spirv new file mode 100644 index 0000000..57f5256 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.frag.glsl-spirv @@ -0,0 +1,18 @@ +#version 430 + + +#define TEXTURE texture +layout(set=0, binding=1, std140) uniform FragBlock +{ + lowp vec4 uColor; +}; + +layout(location = 0) in mediump vec2 vTexCoord; +uniform sampler2D sTexture; + +#define gl_FragColor _glFragColor +out mediump vec4 _glFragColor; +void main() +{ + gl_FragColor = TEXTURE(sTexture, vTexCoord) * uColor; +} diff --git a/automated-tests/resources/shaders/canvas-view.vert b/automated-tests/resources/shaders/canvas-view.vert new file mode 100644 index 0000000..c663ed5 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.vert @@ -0,0 +1,20 @@ +//@ignore:on +#define UNIFORM_BLOCK uniform +#define UNIFORM uniform +#define INPUT in +#define OUTPUT out +#define OUT_COLOR gl_FragColor +//@ignore:off + +INPUT mediump vec2 aPosition; +OUTPUT mediump vec2 vTexCoord; +UNIFORM_BLOCK VertBlock +{ + UNIFORM highp mat4 uMvpMatrix; + UNIFORM highp vec3 uSize; +}; +void main() +{ + gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0); + vTexCoord = aPosition + vec2(0.5); +} diff --git a/automated-tests/resources/shaders/canvas-view.vert.gles2 b/automated-tests/resources/shaders/canvas-view.vert.gles2 new file mode 100644 index 0000000..563f2c0 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.vert.gles2 @@ -0,0 +1,13 @@ +#version 100 + + +#define TEXTURE texture2D +attribute mediump vec2 aPosition; +varying mediump vec2 vTexCoord; +uniform highp mat4 uMvpMatrix; +uniform highp vec3 uSize; +void main() +{ + gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0); + vTexCoord = aPosition + vec2(0.5); +} diff --git a/automated-tests/resources/shaders/canvas-view.vert.gles3 b/automated-tests/resources/shaders/canvas-view.vert.gles3 new file mode 100644 index 0000000..9566ded --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.vert.gles3 @@ -0,0 +1,16 @@ +#version 320 es + + +#define TEXTURE texture +in mediump vec2 aPosition; +out mediump vec2 vTexCoord; +layout(std140) uniform VertBlock +{ + highp mat4 uMvpMatrix; + highp vec3 uSize; +}; +void main() +{ + gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0); + vTexCoord = aPosition + vec2(0.5); +} diff --git a/automated-tests/resources/shaders/canvas-view.vert.glsl-spirv b/automated-tests/resources/shaders/canvas-view.vert.glsl-spirv new file mode 100644 index 0000000..3663488 --- /dev/null +++ b/automated-tests/resources/shaders/canvas-view.vert.glsl-spirv @@ -0,0 +1,16 @@ +#version 430 + + +#define TEXTURE texture +layout(location = 0) in mediump vec2 aPosition; +layout(location=0) out mediump vec2 vTexCoord; +layout(set=0, binding=0, std140) uniform VertBlock +{ + highp mat4 uMvpMatrix; + highp vec3 uSize; +}; +void main() +{ + gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0); + vTexCoord = aPosition + vec2(0.5); +} diff --git a/automated-tests/src/dali-graphics/CMakeLists.txt b/automated-tests/src/dali-graphics/CMakeLists.txt index 8ec9991..39a4494 100644 --- a/automated-tests/src/dali-graphics/CMakeLists.txt +++ b/automated-tests/src/dali-graphics/CMakeLists.txt @@ -15,6 +15,7 @@ SET(TC_SOURCES utc-Dali-GraphicsProgram.cpp utc-Dali-GraphicsSampler.cpp utc-Dali-GraphicsShader.cpp + utc-Dali-GraphicsShaderParser.cpp utc-Dali-GraphicsTexture.cpp ) diff --git a/automated-tests/src/dali-graphics/utc-Dali-GraphicsShader.cpp b/automated-tests/src/dali-graphics/utc-Dali-GraphicsShader.cpp index 6b9bad3..51c67bd 100644 --- a/automated-tests/src/dali-graphics/utc-Dali-GraphicsShader.cpp +++ b/automated-tests/src/dali-graphics/utc-Dali-GraphicsShader.cpp @@ -37,9 +37,10 @@ int UtcDaliGlesStripLegacyCodeIfNeededTest1(void) info.SetSourceSize(vertexShader.size()); info.SetSourceMode(Dali::Graphics::ShaderSourceMode::TEXT); - size_t dataSize = 0; - size_t dataIndex = 0; - Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, dataSize); + size_t dataSize = 0; + size_t dataIndex = 0; + uint32_t version; + Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, version, dataSize); DALI_TEST_EQUALS(dataIndex, 0, TEST_LOCATION); DALI_TEST_EQUALS(dataSize, vertexShader.size(), TEST_LOCATION); @@ -71,9 +72,10 @@ int UtcDaliGlesStripLegacyCodeTestDifferentPrefix(void) info.SetSourceSize(prefixedVertexShader.size()); info.SetSourceMode(Dali::Graphics::ShaderSourceMode::TEXT); - size_t dataSize = 0; - size_t dataIndex = 0; - Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, dataSize); + size_t dataSize = 0; + size_t dataIndex = 0; + uint32_t version; + Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, version, dataSize); auto index = prefixedVertexShader.find("//@version"); @@ -107,9 +109,10 @@ int UtcDaliGlesStripLegacyCodeIfNeededTest2(void) info.SetSourceSize(prefixedVertexShader.size()); info.SetSourceMode(Dali::Graphics::ShaderSourceMode::TEXT); - size_t dataSize = 0; - size_t dataIndex = 0; - Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, dataSize); + size_t dataSize = 0; + size_t dataIndex = 0; + uint32_t version; + Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, version, dataSize); DALI_TEST_EQUALS(dataIndex, vertexPrefix.length(), TEST_LOCATION); @@ -143,9 +146,10 @@ int UtcDaliGlesLegacyCodeTest(void) info.SetSourceSize(prefixedVertexShader.size()); info.SetSourceMode(Dali::Graphics::ShaderSourceMode::TEXT); - size_t dataSize = 0; - size_t dataIndex = 0; - Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, dataSize); + size_t dataSize = 0; + size_t dataIndex = 0; + uint32_t version; + Graphics::GLES::ShaderImpl::StripLegacyCodeIfNeeded(info, dataIndex, version, dataSize); auto index = prefixedVertexShader.find("#version"); diff --git a/automated-tests/src/dali-graphics/utc-Dali-GraphicsShaderParser.cpp b/automated-tests/src/dali-graphics/utc-Dali-GraphicsShaderParser.cpp new file mode 100644 index 0000000..cd26743 --- /dev/null +++ b/automated-tests/src/dali-graphics/utc-Dali-GraphicsShaderParser.cpp @@ -0,0 +1,271 @@ +/* + * 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 + +#include +#include +#include + +#ifndef TEST_RESOURCE_DIR +#define TEST_RESOURCE_DIR "" +#endif + +using namespace Dali::Internal::ShaderParser; + +static std::string LoadTextFile(std::string filename) +{ + std::ifstream t(filename); + std::string str((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + return str; +} + +static bool CompareFileWithString(std::string file1, std::string stringToCompare) +{ + std::ifstream infile1(file1); + + std::ostringstream sstr; + sstr << infile1.rdbuf(); + auto s = sstr.str(); + + auto result = (s == stringToCompare); + if(!result) + { + tet_printf("%s\n", s.c_str()); + tet_printf("---\n%s\n", stringToCompare.c_str()); + } + return result; +} + +int UtcParseGLES2Shader(void) +{ + tet_infoline("UtcParseGLES2Shader - Tests parser output for generating GLES2"); + + auto vertexShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.vert"); + auto fragmentShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.frag"); + + std::vector outStrings; + + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexShader; + parseInfo.fragmentShaderCode = &fragmentShader; + parseInfo.vertexShaderLegacyVersion = 0; + parseInfo.fragmentShaderLegacyVersion = 0; + parseInfo.language = Internal::ShaderParser::OutputLanguage::GLSL2; // We default to GLSL3 + parseInfo.outputVersion = 0; + + Parse(parseInfo, outStrings); + auto& outVertexShader = outStrings[0]; + auto& outFragmentShader = outStrings[1]; + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.gles2", outVertexShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.frag.gles2", outFragmentShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + END_TEST; +} + +int UtcParseGLES2ShaderWithOutput(void) +{ + tet_infoline("UtcParseGLES2ShaderWithOutput - Tests parser output for generating GLES2"); + + // Load fragment shader with gl_FragColor + auto vertexShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.vert"); + auto fragmentShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag"); + + std::vector outStrings; + + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexShader; + parseInfo.fragmentShaderCode = &fragmentShader; + parseInfo.vertexShaderLegacyVersion = 0; + parseInfo.fragmentShaderLegacyVersion = 0; + parseInfo.language = Internal::ShaderParser::OutputLanguage::GLSL2; // We default to GLSL3 + parseInfo.outputVersion = 0; + Parse(parseInfo, outStrings); + + auto& outVertexShader = outStrings[0]; + auto& outFragmentShader = outStrings[1]; + + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.gles2", outVertexShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag.gles2", outFragmentShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + END_TEST; +} + +int UtcParseGLES3Shader(void) +{ + tet_infoline("UtcParseGLES3Shader - Tests parser output for generating GLES3"); + + auto vertexShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.vert"); + auto fragmentShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.frag"); + + std::vector outStrings; + + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexShader; + parseInfo.fragmentShaderCode = &fragmentShader; + parseInfo.vertexShaderLegacyVersion = 0; + parseInfo.fragmentShaderLegacyVersion = 0; + parseInfo.language = Internal::ShaderParser::OutputLanguage::GLSL3; + parseInfo.outputVersion = 0; + Parse(parseInfo, outStrings); + auto& outVertexShader = outStrings[0]; + auto& outFragmentShader = outStrings[1]; + + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.gles3", outVertexShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.frag.gles3", outFragmentShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + END_TEST; +} + +int UtcParseGLES3ShaderWithOutput(void) +{ + tet_infoline("UtcParseGLES3ShaderWithOutput - Tests parser output for generating GLES3 with OUTPUT in fragment shader"); + + auto vertexShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.vert"); + auto fragmentShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag"); + + std::vector outStrings; + + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexShader; + parseInfo.fragmentShaderCode = &fragmentShader; + parseInfo.vertexShaderLegacyVersion = 0; + parseInfo.fragmentShaderLegacyVersion = 0; + parseInfo.language = Internal::ShaderParser::OutputLanguage::GLSL3; + parseInfo.outputVersion = 0; + Parse(parseInfo, outStrings); + + // save to compare + + { + //std::ofstream outv(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.gles3"); + //std::ofstream outf(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag.gles3"); + //outv << outVertexShader; + //outf << outFragmentShader; + } + auto& outVertexShader = outStrings[0]; + auto& outFragmentShader = outStrings[1]; + + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.gles3", outVertexShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag.gles3", outFragmentShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + END_TEST; +} + +int UtcParseSPIRVShader(void) +{ + tet_infoline("UtcParseSPIRVShader - Tests parser output for generating GLES3"); + + // TODO: this test should fail in future after modifying sampler keywords! + + auto vertexShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.vert"); + auto fragmentShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.frag"); + + std::vector outStrings; + + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexShader; + parseInfo.fragmentShaderCode = &fragmentShader; + parseInfo.vertexShaderLegacyVersion = 0; + parseInfo.fragmentShaderLegacyVersion = 0; + parseInfo.language = Internal::ShaderParser::OutputLanguage::SPIRV_GLSL; + parseInfo.outputVersion = 0; + Parse(parseInfo, outStrings); + + // save to compare + + { + //std::ofstream outv(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.glsl-spirv"); + //std::ofstream outf(TEST_RESOURCE_DIR "/shaders/canvas-view.frag.glsl-spirv"); + //outv << outVertexShader; + //outf << outFragmentShader; + } + auto& outVertexShader = outStrings[0]; + auto& outFragmentShader = outStrings[1]; + + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.glsl-spirv", outVertexShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.frag.glsl-spirv", outFragmentShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + END_TEST; +} + +int UtcParseSPIRVShaderWithOutput(void) +{ + tet_infoline("UtcParseSPIRVShaderWithOutput - Tests parser output for generating GLES3"); + + // TODO: this test should fail in future after modifying sampler keywords! + + auto vertexShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view.vert"); + auto fragmentShader = LoadTextFile(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag"); + + std::vector outStrings; + + Internal::ShaderParser::ShaderParserInfo parseInfo{}; + parseInfo.vertexShaderCode = &vertexShader; + parseInfo.fragmentShaderCode = &fragmentShader; + parseInfo.vertexShaderLegacyVersion = 0; + parseInfo.fragmentShaderLegacyVersion = 0; + parseInfo.language = Internal::ShaderParser::OutputLanguage::SPIRV_GLSL; + parseInfo.outputVersion = 0; + Parse(parseInfo, outStrings); + + // save to compare + + { + //std::ofstream outv(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.glsl-spirv"); + //std::ofstream outf(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag.glsl-spirv"); + //outv << outVertexShader; + //outf << outFragmentShader; + } + auto& outVertexShader = outStrings[0]; + auto& outFragmentShader = outStrings[1]; + + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view.vert.glsl-spirv", outVertexShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + { + bool cmp = CompareFileWithString(TEST_RESOURCE_DIR "/shaders/canvas-view-with-output.frag.glsl-spirv", outFragmentShader); + DALI_TEST_EQUALS(cmp, true, TEST_LOCATION); + } + END_TEST; +} diff --git a/dali/internal/graphics/common/shader-parser.cpp b/dali/internal/graphics/common/shader-parser.cpp new file mode 100644 index 0000000..0447c8e --- /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 +#include + +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 +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 +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 +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& 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 0000000..376fb71 --- /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 +#include +#include +#include +#include + +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; +struct CodeLine +{ + std::vector tokens; + std::vector replacement; + std::string line; +}; + +struct Shader +{ + std::vector codeLines; + std::string output; + int customOutputLineIndex; + int mainLine; +}; + +struct Program +{ + Shader vertexShader; + Shader fragmentShader; + std::map 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& 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 3f8b5dd..006e744 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 dcd0966..d0636fc 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 #include "egl-graphics-controller.h" #include "gles-graphics-reflection.h" #include "gles-graphics-shader.h" // EXTERNAL HEADERS -#include -#include +#include #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(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(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 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(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 ee8a888..cc89b71 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 36ffd27..2ee3020 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(_createInfo.sourceData) + dataStartIndex, @@ -106,8 +106,8 @@ struct ShaderImpl::Impl if(pipelineStage) { auto shader = gl->CreateShader(pipelineStage); - const auto src = reinterpret_cast(createInfo.sourceData); - GLint size = createInfo.sourceSize; + const auto src = !sourcePreprocessed.empty() ? reinterpret_cast(sourcePreprocessed.data()) : reinterpret_cast(createInfo.sourceData); + GLint size = !sourcePreprocessed.empty() ? GLint(sourcePreprocessed.size()) : createInfo.sourceSize; gl->ShaderSource(shader, 1, const_cast(&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(createInfo.sourceData)); + DALI_LOG_ERROR("Code: %.*s\n", size, reinterpret_cast(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(data), + reinterpret_cast(data) + size, + sourcePreprocessed.data()); + } + EglGraphicsController& controller; ShaderCreateInfo createInfo; std::vector source{}; + std::vector 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(info.sourceData); + auto text = reinterpret_cast(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 45fce49..ac85822 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}; }; -- 2.7.4