[dali_2.3.33] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / graphics / gles-impl / gles-graphics-shader.cpp
1 /*
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "gles-graphics-shader.h"
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include "egl-graphics-controller.h"
24
25 namespace Dali::Graphics::GLES
26 {
27 struct ShaderImpl::Impl
28 {
29   explicit Impl(Graphics::EglGraphicsController& _controller, const Graphics::ShaderCreateInfo& _createInfo)
30   : controller(_controller)
31   {
32     createInfo.pipelineStage  = _createInfo.pipelineStage;
33     createInfo.shaderlanguage = _createInfo.shaderlanguage;
34     createInfo.sourceMode     = _createInfo.sourceMode;
35     createInfo.shaderVersion  = _createInfo.shaderVersion;
36
37     // Make a copy of source code. if code is meant to be used
38     // by modern parser, skip the prefix part
39     size_t dataStartIndex = 0;
40     size_t dataSize;
41
42     ShaderImpl::StripLegacyCodeIfNeeded(_createInfo, dataStartIndex, glslVersion, dataSize);
43
44     source.resize(dataSize);
45     std::copy(reinterpret_cast<const uint8_t*>(_createInfo.sourceData) + dataStartIndex,
46               reinterpret_cast<const uint8_t*>(_createInfo.sourceData) + dataStartIndex + dataSize,
47               source.data());
48
49     // Substitute pointer
50     createInfo.sourceData = source.data();
51     createInfo.sourceSize = dataSize;
52   }
53
54   ~Impl(){};
55
56   bool Compile()
57   {
58     auto gl = controller.GetGL();
59
60     if(!gl)
61     {
62       return false;
63     }
64
65     if(!glShader)
66     {
67       GLenum pipelineStage{0u};
68       switch(createInfo.pipelineStage)
69       {
70         case Graphics::PipelineStage::TOP_OF_PIPELINE:
71         {
72           break;
73         }
74         case Graphics::PipelineStage::VERTEX_SHADER:
75         {
76           pipelineStage = GL_VERTEX_SHADER;
77           break;
78         }
79         case Graphics::PipelineStage::GEOMETRY_SHADER:
80         {
81           break;
82         }
83         case Graphics::PipelineStage::FRAGMENT_SHADER:
84         {
85           pipelineStage = GL_FRAGMENT_SHADER;
86           break;
87         }
88         case Graphics::PipelineStage::COMPUTE_SHADER:
89         {
90           break;
91         }
92         case Graphics::PipelineStage::TESSELATION_CONTROL:
93         {
94           break;
95         }
96         case Graphics::PipelineStage::TESSELATION_EVALUATION:
97         {
98           break;
99         }
100         case Graphics::PipelineStage::BOTTOM_OF_PIPELINE:
101         {
102           break;
103         }
104       }
105
106       if(pipelineStage)
107       {
108         auto       shader = gl->CreateShader(pipelineStage);
109         const auto src    = !sourcePreprocessed.empty() ? reinterpret_cast<const char*>(sourcePreprocessed.data()) : reinterpret_cast<const char*>(createInfo.sourceData);
110         GLint      size   = !sourcePreprocessed.empty() ? GLint(sourcePreprocessed.size()) : createInfo.sourceSize;
111         gl->ShaderSource(shader, 1, const_cast<const char**>(&src), &size);
112         gl->CompileShader(shader);
113
114         GLint status{0};
115         gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
116         if(status != GL_TRUE)
117         {
118           char    output[4096];
119           GLsizei outputSize{0u};
120           gl->GetShaderInfoLog(shader, 4096, &outputSize, output);
121           DALI_LOG_ERROR("Code: %.*s\n", size, reinterpret_cast<const char*>(src));
122           DALI_LOG_ERROR("glCompileShader() failed: \n%s\n", output);
123           gl->DeleteShader(shader);
124           return false;
125         }
126         glShader = shader;
127       }
128       return true;
129     }
130     return true;
131   }
132
133   void Destroy()
134   {
135     auto gl = controller.GetGL();
136
137     if(gl && glShader)
138     {
139       gl->DeleteShader(glShader);
140       glShader = 0;
141     }
142   }
143
144   void SetPreprocessedCode(void* data, uint32_t size)
145   {
146     sourcePreprocessed.resize(size);
147
148     std::copy(reinterpret_cast<const uint8_t*>(data),
149               reinterpret_cast<const uint8_t*>(data) + size,
150               sourcePreprocessed.data());
151   }
152
153   EglGraphicsController& controller;
154   ShaderCreateInfo       createInfo;
155   std::vector<uint8_t>   source{};
156   std::vector<uint8_t>   sourcePreprocessed{};
157
158   uint32_t glShader{};
159   uint32_t refCount{0u};
160   uint32_t flushCount{0u};  ///< Number of frames at refCount=0
161   uint32_t glslVersion{0u}; ///< 0 - unknown, otherwise valid #version like 130, 300, etc.
162 };
163
164 ShaderImpl::ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
165 {
166   mImpl = std::make_unique<Impl>(controller, createInfo);
167 }
168
169 ShaderImpl::~ShaderImpl()
170 {
171   if(!mImpl->controller.IsShuttingDown())
172   {
173     mImpl->Destroy();
174   }
175 }
176
177 uint32_t ShaderImpl::Retain()
178 {
179   mImpl->flushCount = 0;
180   return ++mImpl->refCount;
181 }
182
183 uint32_t ShaderImpl::Release()
184 {
185   uint32_t remainingCount = --mImpl->refCount;
186   mImpl->flushCount       = 0;
187   return remainingCount;
188 }
189
190 [[nodiscard]] uint32_t ShaderImpl::GetRefCount() const
191 {
192   return mImpl->refCount;
193 }
194
195 [[nodiscard]] uint32_t ShaderImpl::IncreaseFlushCount()
196 {
197   return ++mImpl->flushCount;
198 }
199
200 [[nodiscard]] uint32_t ShaderImpl::GetFlushCount() const
201 {
202   return mImpl->flushCount;
203 }
204
205 [[nodiscard]] uint32_t ShaderImpl::GetGLSLVersion() const
206 {
207   return mImpl->glslVersion;
208 }
209
210 /**
211  * @brief Compiles shader
212  *
213  * @return True on success
214  */
215 [[nodiscard]] bool ShaderImpl::Compile() const
216 {
217   return mImpl->Compile();
218 }
219
220 [[nodiscard]] uint32_t ShaderImpl::GetGLShader() const
221 {
222   return mImpl->glShader;
223 }
224
225 const ShaderCreateInfo& ShaderImpl::GetCreateInfo() const
226 {
227   return mImpl->createInfo;
228 }
229
230 [[nodiscard]] EglGraphicsController& ShaderImpl::GetController() const
231 {
232   return mImpl->controller;
233 }
234
235 void ShaderImpl::StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, uint32_t& glslVersion, size_t& finalDataSize)
236 {
237   // Make a copy of source code. if code is meant to be used
238   // by modern parser, skip the prefix part
239   auto text   = reinterpret_cast<const char*>(info.sourceData);
240   auto result = std::string_view(text).find("//@legacy-prefix-end");
241   glslVersion = 0u;
242   if(info.shaderVersion != 0)
243   {
244     if(result != 0 && result != std::string::npos)
245     {
246       DALI_LOG_ERROR("Shader processing: @legacy-prefix-end must be a very first statement!\n");
247     }
248     else if(result == 0)
249     {
250       char* end;
251       startIndex = std::strtoul(reinterpret_cast<const char*>(info.sourceData) + 21, &end, 10);
252     }
253   }
254   else
255   {
256     // For legacy shaders we need to make sure that the #version is a very first line
257     // so need to strip //@legacy-prefix-end tag
258     auto versionPos = std::string_view(text).find("#version", 0);
259     if(versionPos == std::string::npos)
260     {
261       startIndex = 0; // not trimming anything
262
263       // if there's no version yet it's a legacy shader we assign 100
264       glslVersion = 100;
265     }
266     else
267     {
268       // save version of legacy shader
269       char* end;
270       glslVersion = uint32_t(std::strtol(std::string_view(text).data() + versionPos + 9, &end, 10));
271       startIndex  = versionPos;
272     }
273   }
274   finalDataSize = info.sourceSize - startIndex;
275 }
276
277 void ShaderImpl::SetPreprocessedCode(void* data, uint32_t size)
278 {
279   mImpl->SetPreprocessedCode(data, size);
280 }
281
282 Shader::~Shader()
283 {
284   if(!mShader->Release())
285   {
286     GetImplementation()->GetController().GetPipelineCache().MarkShaderCacheFlushRequired();
287   }
288 }
289
290 [[nodiscard]] const ShaderCreateInfo& Shader::GetCreateInfo() const
291 {
292   return GetImplementation()->GetCreateInfo();
293 }
294
295 void Shader::DiscardResource()
296 {
297   auto& controller = GetImplementation()->GetController();
298   if(!controller.IsShuttingDown())
299   {
300     controller.DiscardResource(this);
301   }
302 }
303
304 uint32_t Shader::GetGLSLVersion() const
305 {
306   return GetImplementation()->GetGLSLVersion();
307 }
308
309 } // namespace Dali::Graphics::GLES