2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "gles-graphics-shader.h"
22 #include <dali/integration-api/debug.h>
23 #include "egl-graphics-controller.h"
25 namespace Dali::Graphics::GLES
27 struct ShaderImpl::Impl
29 explicit Impl(Graphics::EglGraphicsController& _controller, const Graphics::ShaderCreateInfo& _createInfo)
30 : controller(_controller)
32 createInfo.pipelineStage = _createInfo.pipelineStage;
33 createInfo.shaderlanguage = _createInfo.shaderlanguage;
34 createInfo.sourceMode = _createInfo.sourceMode;
35 createInfo.shaderVersion = _createInfo.shaderVersion;
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;
42 ShaderImpl::StripLegacyCodeIfNeeded(_createInfo, dataStartIndex, glslVersion, dataSize);
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,
50 createInfo.sourceData = source.data();
51 createInfo.sourceSize = dataSize;
58 auto gl = controller.GetGL();
67 GLenum pipelineStage{0u};
68 switch(createInfo.pipelineStage)
70 case Graphics::PipelineStage::TOP_OF_PIPELINE:
74 case Graphics::PipelineStage::VERTEX_SHADER:
76 pipelineStage = GL_VERTEX_SHADER;
79 case Graphics::PipelineStage::GEOMETRY_SHADER:
83 case Graphics::PipelineStage::FRAGMENT_SHADER:
85 pipelineStage = GL_FRAGMENT_SHADER;
88 case Graphics::PipelineStage::COMPUTE_SHADER:
92 case Graphics::PipelineStage::TESSELATION_CONTROL:
96 case Graphics::PipelineStage::TESSELATION_EVALUATION:
100 case Graphics::PipelineStage::BOTTOM_OF_PIPELINE:
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);
115 gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
116 if(status != GL_TRUE)
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);
135 auto gl = controller.GetGL();
139 gl->DeleteShader(glShader);
144 void SetPreprocessedCode(void* data, uint32_t size)
146 sourcePreprocessed.resize(size);
148 std::copy(reinterpret_cast<const uint8_t*>(data),
149 reinterpret_cast<const uint8_t*>(data) + size,
150 sourcePreprocessed.data());
153 EglGraphicsController& controller;
154 ShaderCreateInfo createInfo;
155 std::vector<uint8_t> source{};
156 std::vector<uint8_t> sourcePreprocessed{};
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.
164 ShaderImpl::ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
166 mImpl = std::make_unique<Impl>(controller, createInfo);
169 ShaderImpl::~ShaderImpl()
171 if(!mImpl->controller.IsShuttingDown())
177 uint32_t ShaderImpl::Retain()
179 mImpl->flushCount = 0;
180 return ++mImpl->refCount;
183 uint32_t ShaderImpl::Release()
185 uint32_t remainingCount = --mImpl->refCount;
186 mImpl->flushCount = 0;
187 return remainingCount;
190 [[nodiscard]] uint32_t ShaderImpl::GetRefCount() const
192 return mImpl->refCount;
195 [[nodiscard]] uint32_t ShaderImpl::IncreaseFlushCount()
197 return ++mImpl->flushCount;
200 [[nodiscard]] uint32_t ShaderImpl::GetFlushCount() const
202 return mImpl->flushCount;
205 [[nodiscard]] uint32_t ShaderImpl::GetGLSLVersion() const
207 return mImpl->glslVersion;
211 * @brief Compiles shader
213 * @return True on success
215 [[nodiscard]] bool ShaderImpl::Compile() const
217 return mImpl->Compile();
220 [[nodiscard]] uint32_t ShaderImpl::GetGLShader() const
222 return mImpl->glShader;
225 const ShaderCreateInfo& ShaderImpl::GetCreateInfo() const
227 return mImpl->createInfo;
230 [[nodiscard]] EglGraphicsController& ShaderImpl::GetController() const
232 return mImpl->controller;
235 void ShaderImpl::StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, uint32_t& glslVersion, size_t& finalDataSize)
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");
242 if(info.shaderVersion != 0)
244 if(result != 0 && result != std::string::npos)
246 DALI_LOG_ERROR("Shader processing: @legacy-prefix-end must be a very first statement!\n");
251 startIndex = std::strtoul(reinterpret_cast<const char*>(info.sourceData) + 21, &end, 10);
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)
261 startIndex = 0; // not trimming anything
263 // if there's no version yet it's a legacy shader we assign 100
268 // save version of legacy shader
270 glslVersion = uint32_t(std::strtol(std::string_view(text).data() + versionPos + 9, &end, 10));
271 startIndex = versionPos;
274 finalDataSize = info.sourceSize - startIndex;
277 void ShaderImpl::SetPreprocessedCode(void* data, uint32_t size)
279 mImpl->SetPreprocessedCode(data, size);
284 if(!mShader->Release())
286 GetImplementation()->GetController().GetPipelineCache().MarkShaderCacheFlushRequired();
290 [[nodiscard]] const ShaderCreateInfo& Shader::GetCreateInfo() const
292 return GetImplementation()->GetCreateInfo();
295 void Shader::DiscardResource()
297 auto& controller = GetImplementation()->GetController();
298 if(!controller.IsShuttingDown())
300 controller.DiscardResource(this);
304 uint32_t Shader::GetGLSLVersion() const
306 return GetImplementation()->GetGLSLVersion();
309 } // namespace Dali::Graphics::GLES