summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdeel Kazmi <adeel.kazmi@samsung.com>2024-07-05 10:02:07 +0000
committerGerrit Code Review <gerrit@review>2024-07-05 10:02:07 +0000
commitf942e3f7c838455fd5fae1f344faa05ea2c7f9f6 (patch)
tree4075686e6a63ec0b453e07f7969a4121a4fa54d7
parent1953e6ee48fa5e1f5b94b8df97b116b5cd7275b1 (diff)
parentf27d4f47f4680c79278cf73acdaf1fee03df5147 (diff)
downloaddali-adaptor-f942e3f7c838455fd5fae1f344faa05ea2c7f9f6.tar.gz
dali-adaptor-f942e3f7c838455fd5fae1f344faa05ea2c7f9f6.tar.bz2
dali-adaptor-f942e3f7c838455fd5fae1f344faa05ea2c7f9f6.zip
Merge "Shader processor" into devel/master
-rw-r--r--automated-tests/resources/shaders/canvas-view-with-output.frag22
-rw-r--r--automated-tests/resources/shaders/canvas-view-with-output.frag.gles215
-rw-r--r--automated-tests/resources/shaders/canvas-view-with-output.frag.gles318
-rw-r--r--automated-tests/resources/shaders/canvas-view-with-output.frag.glsl-spirv18
-rw-r--r--automated-tests/resources/shaders/canvas-view.frag20
-rw-r--r--automated-tests/resources/shaders/canvas-view.frag.gles213
-rw-r--r--automated-tests/resources/shaders/canvas-view.frag.gles318
-rw-r--r--automated-tests/resources/shaders/canvas-view.frag.glsl-spirv18
-rw-r--r--automated-tests/resources/shaders/canvas-view.vert20
-rw-r--r--automated-tests/resources/shaders/canvas-view.vert.gles213
-rw-r--r--automated-tests/resources/shaders/canvas-view.vert.gles316
-rw-r--r--automated-tests/resources/shaders/canvas-view.vert.glsl-spirv16
-rw-r--r--automated-tests/src/dali-graphics/CMakeLists.txt1
-rw-r--r--automated-tests/src/dali-graphics/utc-Dali-GraphicsShader.cpp28
-rw-r--r--automated-tests/src/dali-graphics/utc-Dali-GraphicsShaderParser.cpp271
-rw-r--r--dali/internal/graphics/common/shader-parser.cpp539
-rw-r--r--dali/internal/graphics/common/shader-parser.h94
-rw-r--r--dali/internal/graphics/file.list1
-rw-r--r--dali/internal/graphics/gles-impl/gles-graphics-program.cpp83
-rw-r--r--dali/internal/graphics/gles-impl/gles-graphics-program.h7
-rw-r--r--dali/internal/graphics/gles-impl/gles-graphics-shader.cpp65
-rw-r--r--dali/internal/graphics/gles-impl/gles-graphics-shader.h46
22 files changed, 1307 insertions, 35 deletions
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 000000000..d7b51ec5c
--- /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 000000000..9e471ea45
--- /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 000000000..91b5774e1
--- /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 000000000..ffa5d20f2
--- /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 000000000..b3a4d067f
--- /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 000000000..a53d66498
--- /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 000000000..0f9435f90
--- /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 000000000..57f5256f6
--- /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 000000000..c663ed5f1
--- /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 000000000..563f2c011
--- /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 000000000..9566dedc3
--- /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 000000000..366348814
--- /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 8ec9991ea..39a4494a6 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 6b9bad306..51c67bdf0 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 000000000..cd2674362
--- /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 <dali/dali.h>
+
+#include <dali-test-suite-utils.h>
+#include <dali/internal/graphics/common/shader-parser.h>
+#include <fstream>
+
+#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<char>(t)),
+ std::istreambuf_iterator<char>());
+ 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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 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};
};