From: Adeel Kazmi Date: Fri, 17 Nov 2023 23:03:13 +0000 (+0000) Subject: Merge "Added shader support to pipeline cache" into devel/master X-Git-Tag: dali_2.3.0~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=636e88675342131ac9bf0b8633994ce68dea6084;hp=350f691d92e973ac7b1ce90f5771d6e7e5b28aa6;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git Merge "Added shader support to pipeline cache" into devel/master --- diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h index 2ca98dc..3f04a6b 100644 --- a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h +++ b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h @@ -1041,7 +1041,32 @@ public: for(const auto& uniform : mActiveUniforms) { - GetUniformLocation(program, uniform.name.c_str()); + std::string name = uniform.name; + if(uniform.size <= 1) + { + GetUniformLocation(program, name.c_str()); + } + else + { + // Convert single active uniform from "uBlah[0]" or "uStruct[0].element" to N versions of the same + std::string suffix; + auto iter = name.find("["); // Search for index operator + if(iter != std::string::npos) + { + name = uniform.name.substr(0, iter); // Strip off index operator + iter = uniform.name.find("]"); + if(iter != std::string::npos && iter + 1 != uniform.name.length()) + { + suffix = uniform.name.substr(iter + 1); + } + } + for(int i = 0; i < uniform.size; ++i) + { + std::stringstream nss; + nss << name << "[" << i << "]" << suffix; + GetUniformLocation(program, nss.str().c_str()); // Generate N uniforms in the uniform map + } + } } for(const auto& uniform : mCustomUniformData) diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-application.cpp b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-application.cpp index 3e8e171..f4de9af 100644 --- a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-application.cpp +++ b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-graphics-application.cpp @@ -98,6 +98,7 @@ void TestGraphicsApplication::InitializeCore() TestGraphicsApplication::~TestGraphicsApplication() { + mGraphicsController.Shutdown(); Dali::Integration::Log::UninstallLogFunction(); delete mCore; } diff --git a/automated-tests/src/dali-graphics/CMakeLists.txt b/automated-tests/src/dali-graphics/CMakeLists.txt index 45ca6cb..1dee934 100644 --- a/automated-tests/src/dali-graphics/CMakeLists.txt +++ b/automated-tests/src/dali-graphics/CMakeLists.txt @@ -40,8 +40,6 @@ PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED dali2-core dali2-adaptor glesv2 - ecore - ecore-x ) ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror ) diff --git a/automated-tests/src/dali-graphics/utc-Dali-GraphicsProgram.cpp b/automated-tests/src/dali-graphics/utc-Dali-GraphicsProgram.cpp index 9677500..c2c3e12 100644 --- a/automated-tests/src/dali-graphics/utc-Dali-GraphicsProgram.cpp +++ b/automated-tests/src/dali-graphics/utc-Dali-GraphicsProgram.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 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. @@ -36,6 +36,18 @@ void utc_dali_program_cleanup(void) namespace { const std::string VERT_SHADER_SOURCE = "myVertShaderSource"; + +const std::string VERT_SHADER_SOURCE2 = + "\n" + "in vec3 aPosition;\n" + "in vec3 aTexCoord;\n" + "out vec2 vTexCoord;\n" + "main()\n" + "{\n" + " gl_Position=aPosition;\n" + " vTexCoord = aTexCoord;\n" + "}\n"; + const std::string FRAG_SHADER_SOURCE = "\n" "uniform sampler2D sAlbedo;\n" @@ -48,9 +60,20 @@ const std::string FRAG_SHADER_SOURCE = "{\n" " gl_fragColor = texture2d(sAlbedo, vTexCoord) + lightDirection*texture2d(sNormals, vTexCoord);\n" "}\n"; + +const std::string FRAG_SHADER_SOURCE2 = + "\n" + "uniform sampler2D sTextures[4];\n" + "uniform mediump vec3 lightDirection;\n" + "in mediump vec2 vTexCoord;\n" + "main()\n" + "{\n" + " gl_fragColor = texture2d(sTextures[0], vTexCoord) + lightDirection*texture2d(sTextures[2], vTexCoord);\n" + "}\n"; + } //anonymous namespace -int UtcDaliGraphicsProgram(void) +int UtcDaliGraphicsProgram01(void) { TestGraphicsApplication app; tet_infoline("UtcDaliProgram - check that right sampler uniforms are bound for textures"); @@ -97,3 +120,160 @@ int UtcDaliGraphicsProgram(void) END_TEST; } + +int UtcDaliGraphicsProgram02(void) +{ + TestGraphicsApplication app; + tet_infoline("UtcDaliProgram - check that sampler arrays are handled and bound to textures"); + + Texture normals = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + Texture metalroughness = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + Texture ao = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + Texture albedo = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + + TextureSet textureSet = TextureSet::New(); + textureSet.SetTexture(0, albedo); + textureSet.SetTexture(1, metalroughness); + textureSet.SetTexture(2, normals); + textureSet.SetTexture(3, ao); + + Actor actor = CreateRenderableActor2(textureSet, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE2); + app.GetScene().Add(actor); + + auto& gl = app.GetGlAbstraction(); + auto& glUniformTrace = gl.GetSetUniformTrace(); + glUniformTrace.Enable(true); + glUniformTrace.EnableLogging(true); + gl.GetShaderTrace().Enable(true); + gl.GetShaderTrace().EnableLogging(true); + + std::vector activeUniforms{ + {"uLightDir", GL_FLOAT_VEC4, 1}, + {"sTextures[0]", GL_SAMPLER_2D, 4}}; // Array of 4 samplers + gl.SetActiveUniforms(activeUniforms); + + app.SendNotification(); + app.Render(16); // The above actor will get rendered and drawn once. + + // Check what uniform values were set: + int value; + DALI_TEST_CHECK(gl.GetUniformValue("sTextures[0]", value)); // First in frag shader + DALI_TEST_EQUALS(value, 0, TEST_LOCATION); + DALI_TEST_CHECK(gl.GetUniformValue("sTextures[3]", value)); // 4th + DALI_TEST_EQUALS(value, 3, TEST_LOCATION); + DALI_TEST_CHECK(gl.GetUniformValue("sTextures[2]", value)); // 3rd + DALI_TEST_EQUALS(value, 2, TEST_LOCATION); + DALI_TEST_CHECK(gl.GetUniformValue("sTextures[1]", value)); // 2nd + DALI_TEST_EQUALS(value, 1, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGraphicsShaderNew(void) +{ + TestGraphicsApplication app; + tet_infoline("UtcDaliProgram - check that multiple shaders from same source only create 1 program"); + + Texture diffuse = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + + // Creates 3 Dali::Shaders + Actor actor1 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE); + Actor actor2 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE); + Actor actor3 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE); + + app.GetScene().Add(actor1); + app.GetScene().Add(actor2); + app.GetScene().Add(actor3); + + auto& gl = app.GetGlAbstraction(); + auto& glShaderTrace = gl.GetShaderTrace(); + glShaderTrace.Enable(true); + glShaderTrace.EnableLogging(true); + + app.SendNotification(); + app.Render(16); // The above actors will get rendered and drawn once, only 2 shaders should be created + + DALI_TEST_EQUALS(glShaderTrace.CountMethod("CreateShader"), 2, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGraphicsShaderNew02(void) +{ + TestGraphicsApplication app; + tet_infoline("UtcDaliProgram - check that mixed up multiple shaders from same source don't create dups"); + + Texture diffuse = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + + // Creates 3 Dali::Shaders + Actor actor1 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE); + Actor actor2 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE2); + Actor actor3 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE2); + Actor actor4 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE); + + app.GetScene().Add(actor1); + app.GetScene().Add(actor2); + app.GetScene().Add(actor3); + app.GetScene().Add(actor4); + + auto& gl = app.GetGlAbstraction(); + auto& glShaderTrace = gl.GetShaderTrace(); + glShaderTrace.Enable(true); + glShaderTrace.EnableLogging(true); + + app.SendNotification(); + app.Render(16); // The above actors will get rendered and drawn once, only 2 shaders should be created + + // Should only be 4 shaders, not 8. + DALI_TEST_EQUALS(glShaderTrace.CountMethod("CreateShader"), 4, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGraphicsShaderFlush(void) +{ + TestGraphicsApplication app; + tet_infoline("UtcDaliProgram - check that unused shaders are flushed"); + + Texture diffuse = CreateTexture(TextureType::TEXTURE_2D, Pixel::RGBA8888, 16u, 16u); + auto& gl = app.GetGlAbstraction(); + auto& glShaderTrace = gl.GetShaderTrace(); + glShaderTrace.Enable(true); + glShaderTrace.EnableLogging(true); + + { + // Creates 3 Dali::Shaders + Actor actor1 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE); + Actor actor2 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE2); + Actor actor3 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE, FRAG_SHADER_SOURCE2); + Actor actor4 = CreateRenderableActor(diffuse, VERT_SHADER_SOURCE2, FRAG_SHADER_SOURCE); + + app.GetScene().Add(actor1); + app.GetScene().Add(actor2); + app.GetScene().Add(actor3); + app.GetScene().Add(actor4); + + app.SendNotification(); + app.Render(16); // The above actors will get rendered and drawn once + + // Should only be 4 shaders, not 8. + DALI_TEST_EQUALS(glShaderTrace.CountMethod("CreateShader"), 4, TEST_LOCATION); + + UnparentAndReset(actor1); + UnparentAndReset(actor2); + UnparentAndReset(actor3); + UnparentAndReset(actor4); + } + + for(int i = 0; i < 1199; ++i) // 3 flushes per frame + { + app.SendNotification(); + app.Render(16); + DALI_TEST_EQUALS(glShaderTrace.CountMethod("DeleteShader"), 0, TEST_LOCATION); + } + app.SendNotification(); + app.Render(16); + DALI_TEST_EQUALS(glShaderTrace.CountMethod("DeleteShader"), 4, TEST_LOCATION); + + END_TEST; +} diff --git a/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp b/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp index 505201c..b211897 100644 --- a/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp +++ b/dali/internal/graphics/gles-impl/egl-graphics-controller.cpp @@ -298,7 +298,7 @@ Graphics::UniquePtr EglGraphicsController::CreatePipeline( Graphics::UniquePtr EglGraphicsController::CreateProgram( const ProgramCreateInfo& programCreateInfo, UniquePtr&& oldProgram) { - // Create program cache if needed + // Create pipeline cache if needed if(!mPipelineCache) { mPipelineCache = std::make_unique(*this); @@ -309,7 +309,12 @@ Graphics::UniquePtr EglGraphicsController::CreateProgram( Graphics::UniquePtr EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr&& oldShader) { - return NewObject(shaderCreateInfo, *this, std::move(oldShader)); + // Create pipeline cache if needed + if(!mPipelineCache) + { + mPipelineCache = std::make_unique(*this); + } + return mPipelineCache->GetShader(shaderCreateInfo, std::move(oldShader)); } Graphics::UniquePtr EglGraphicsController::CreateSampler(const SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr&& oldSampler) diff --git a/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp b/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp index 7f14016..b567a27 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.cpp @@ -21,6 +21,11 @@ #include "gles-graphics-pipeline.h" #include "gles-graphics-program.h" +namespace +{ +constexpr uint32_t CACHE_CLEAN_FLUSH_COUNT = 3600u; // 60fps * 60sec / ~3 flushes per frame +} + namespace Dali::Graphics::GLES { /** @@ -247,7 +252,8 @@ struct PipelineCache::Impl explicit Impl(EglGraphicsController& _controller) : controller(_controller), pipelineEntriesFlushRequired(false), - programEntriesFlushRequired(false) + programEntriesFlushRequired(false), + shaderEntriesFlushRequired(false) { // Initialise lookup table InitialiseStateCompareLookupTable(); @@ -287,9 +293,6 @@ struct PipelineCache::Impl uint32_t stateBitmask{0u}; }; - EglGraphicsController& controller; - std::vector entries; - /** * @brief Sorted array of shaders used to create program */ @@ -300,10 +303,19 @@ struct PipelineCache::Impl UniquePtr program{nullptr}; }; + struct ShaderCacheEntry + { + UniquePtr shaderImpl{nullptr}; + }; + + EglGraphicsController& controller; + std::vector entries; std::vector programEntries; + std::vector shaderEntries; bool pipelineEntriesFlushRequired : 1; bool programEntriesFlushRequired : 1; + bool shaderEntriesFlushRequired : 1; }; PipelineCache::PipelineCache(EglGraphicsController& controller) @@ -467,6 +479,53 @@ Graphics::UniquePtr PipelineCache::GetProgram(const ProgramCr return std::move(wrapper); } +ShaderImpl* PipelineCache::FindShaderImpl(const ShaderCreateInfo& shaderCreateInfo) +{ + if(!mImpl->shaderEntries.empty()) + { + for(auto& item : mImpl->shaderEntries) + { + auto& itemInfo = item.shaderImpl->GetCreateInfo(); + if(itemInfo.pipelineStage != shaderCreateInfo.pipelineStage || + itemInfo.shaderlanguage != shaderCreateInfo.shaderlanguage || + itemInfo.sourceMode != shaderCreateInfo.sourceMode || + itemInfo.sourceSize != shaderCreateInfo.sourceSize) + { + continue; + } + + if(memcmp(itemInfo.sourceData, shaderCreateInfo.sourceData, itemInfo.sourceSize) == 0) + { + return item.shaderImpl.get(); + } + } + } + return nullptr; +} + +Graphics::UniquePtr PipelineCache::GetShader(const ShaderCreateInfo& shaderCreateInfo, + Graphics::UniquePtr&& oldShader) +{ + ShaderImpl* cachedShader = FindShaderImpl(shaderCreateInfo); + + // Return same pointer if nothing changed + if(oldShader && *static_cast(oldShader.get()) == cachedShader) + { + return std::move(oldShader); + } + + if(!cachedShader) + { + auto shader = MakeUnique(shaderCreateInfo, mImpl->controller); + cachedShader = shader.get(); + + mImpl->shaderEntries.emplace_back(); + mImpl->shaderEntries.back().shaderImpl = std::move(shader); + } + auto wrapper = MakeUnique>(cachedShader); + return std::move(wrapper); +} + void PipelineCache::FlushCache() { if(mImpl->pipelineEntriesFlushRequired) @@ -509,6 +568,39 @@ void PipelineCache::FlushCache() mImpl->programEntriesFlushRequired = false; } + + if(mImpl->shaderEntriesFlushRequired) + { + // There is at least 1 unused shader + mImpl->shaderEntriesFlushRequired = false; + bool deleteRequired{false}; + for(auto& entry : mImpl->shaderEntries) + { + if(entry.shaderImpl->GetRefCount() == 0) + { + mImpl->shaderEntriesFlushRequired = true; + auto frameCount = entry.shaderImpl->IncreaseFlushCount(); + if(frameCount > CACHE_CLEAN_FLUSH_COUNT) + { + deleteRequired = true; + } + } + } + if(deleteRequired) + { + decltype(mImpl->shaderEntries) newShaderEntries; + newShaderEntries.reserve(mImpl->shaderEntries.size()); + for(auto& entry : mImpl->shaderEntries) + { + if(entry.shaderImpl->GetRefCount() > 0 || + entry.shaderImpl->GetFlushCount() <= CACHE_CLEAN_FLUSH_COUNT) + { + newShaderEntries.emplace_back(std::move(entry)); + } + } + mImpl->shaderEntries = std::move(newShaderEntries); + } + } } void PipelineCache::MarkPipelineCacheFlushRequired() @@ -521,4 +613,9 @@ void PipelineCache::MarkProgramCacheFlushRequired() mImpl->programEntriesFlushRequired = true; } +void PipelineCache::MarkShaderCacheFlushRequired() +{ + mImpl->shaderEntriesFlushRequired = true; +} + } // namespace Dali::Graphics::GLES diff --git a/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h b/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h index 2a1fd6b..685ad32 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-pipeline-cache.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include // INTERNAL INCLUDES #include "gles-graphics-resource.h" @@ -36,6 +38,9 @@ class Pipeline; class PipelineImpl; class Program; class ProgramImpl; +class Shader; +class ShaderImpl; + /** * @brief PipelineCache manages pipeline and program * objects so there are no duplicates created. @@ -78,6 +83,18 @@ public: Graphics::UniquePtr GetProgram(const ProgramCreateInfo& pipelineCreateInfo, Graphics::UniquePtr&& oldProgram); /** + * @brief Retrieves shader matching the spec + * + * Function returns either existing shader if one is found + * in the cache or creates new one. + * + * @param[in] shaderCreateInfo Valid ShaderCreateInfo structure + * @param[in] oldShader previous shader object + * @return Shader object + */ + Graphics::UniquePtr GetShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr&& oldShader); + + /** * @brief Flushes pipeline and program cache * * Removes cached items when they are no longer needed. This function @@ -95,6 +112,11 @@ public: */ void MarkProgramCacheFlushRequired(); + /** + * @brief Notify that we need to flush shader cache next FlushCache API. + */ + void MarkShaderCacheFlushRequired(); + private: /** * @brief Finds pipeline implementation based on the spec @@ -104,12 +126,20 @@ private: PipelineImpl* FindPipelineImpl(const PipelineCreateInfo& info); /** - * @brief Finds program implementation based on the spec - * @param[in] info Valid create info structure - * @return Returns pointer to program or nullptr - */ + * @brief Finds program implementation based on the spec + * @param[in] info Valid create info structure + * @return Returns pointer to program or nullptr + */ ProgramImpl* FindProgramImpl(const ProgramCreateInfo& info); + /** + * @brief Finds shader implementation based on create info + * + * @param[in] shadercreateinfo Valid create info structure + * @return Returns pointer to shader or nullptr + */ + ShaderImpl* FindShaderImpl(const ShaderCreateInfo& shaderCreateInfo); + private: struct Impl; std::unique_ptr mImpl; diff --git a/dali/internal/graphics/gles-impl/gles-graphics-program.cpp b/dali/internal/graphics/gles-impl/gles-graphics-program.cpp index d7fc0a7..3b28c23 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-program.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-program.cpp @@ -148,9 +148,9 @@ bool ProgramImpl::Create() const auto* shader = static_cast(state.shader); // Compile shader first (ignored when compiled) - if(shader->Compile()) + if(shader->GetImplementation()->Compile()) { - gl->AttachShader(program, shader->GetGLShader()); + gl->AttachShader(program, shader->GetImplementation()->GetGLShader()); } } gl->LinkProgram(program); diff --git a/dali/internal/graphics/gles-impl/gles-graphics-program.h b/dali/internal/graphics/gles-impl/gles-graphics-program.h index 050b04b..ee8a888 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) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 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. @@ -154,7 +154,7 @@ private: /////////////////////////////////////////////////////////////// /** - * @brief Wrapper for the pipeline implementation + * @brief Wrapper for the program implementation * * This object is returned back to the client-side */ diff --git a/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp b/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp index 5e46e97..676884f 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp +++ b/dali/internal/graphics/gles-impl/gles-graphics-shader.cpp @@ -24,131 +24,211 @@ namespace Dali::Graphics::GLES { -struct Shader::Impl +struct ShaderImpl::Impl { - Impl() = default; - ~Impl() = default; - - std::vector source{}; - uint32_t glShader{}; -}; - -Shader::Shader(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller) -: ShaderResource(createInfo, controller) -{ - // push shader to the create queue - mImpl = std::make_unique(); - - // Make a copy of source code - mImpl->source.resize(createInfo.sourceSize); - std::copy(reinterpret_cast(mCreateInfo.sourceData), - reinterpret_cast(mCreateInfo.sourceData) + mCreateInfo.sourceSize, - mImpl->source.begin()); - - // Substitute pointer - mCreateInfo.sourceData = mImpl->source.data(); -} - -Shader::~Shader() = default; - -bool Shader::Compile() const -{ - auto gl = GetController().GetGL(); - - if(!gl) + explicit Impl(Graphics::EglGraphicsController& _controller, const Graphics::ShaderCreateInfo& _createInfo) + : controller(_controller) { - return false; + createInfo.pipelineStage = _createInfo.pipelineStage; + createInfo.shaderlanguage = _createInfo.shaderlanguage; + createInfo.sourceMode = _createInfo.sourceMode; + createInfo.sourceSize = _createInfo.sourceSize; + + // Make a copy of source code + source.resize(_createInfo.sourceSize); + std::copy(reinterpret_cast(_createInfo.sourceData), + reinterpret_cast(_createInfo.sourceData) + _createInfo.sourceSize, + source.data()); + + // Substitute pointer + createInfo.sourceData = source.data(); } - if(!mImpl->glShader) + ~Impl(){}; + + bool Compile() { - GLenum pipelineStage{0u}; - switch(GetCreateInfo().pipelineStage) + auto gl = controller.GetGL(); + + if(!gl) { - case Graphics::PipelineStage::TOP_OF_PIPELINE: - { - break; - } - case Graphics::PipelineStage::VERTEX_SHADER: - { - pipelineStage = GL_VERTEX_SHADER; - break; - } - case Graphics::PipelineStage::GEOMETRY_SHADER: - { - break; - } - case Graphics::PipelineStage::FRAGMENT_SHADER: - { - pipelineStage = GL_FRAGMENT_SHADER; - break; - } - case Graphics::PipelineStage::COMPUTE_SHADER: - { - break; - } - case Graphics::PipelineStage::TESSELATION_CONTROL: - { - break; - } - case Graphics::PipelineStage::TESSELATION_EVALUATION: + return false; + } + + if(!glShader) + { + GLenum pipelineStage{0u}; + switch(createInfo.pipelineStage) { - break; + case Graphics::PipelineStage::TOP_OF_PIPELINE: + { + break; + } + case Graphics::PipelineStage::VERTEX_SHADER: + { + pipelineStage = GL_VERTEX_SHADER; + break; + } + case Graphics::PipelineStage::GEOMETRY_SHADER: + { + break; + } + case Graphics::PipelineStage::FRAGMENT_SHADER: + { + pipelineStage = GL_FRAGMENT_SHADER; + break; + } + case Graphics::PipelineStage::COMPUTE_SHADER: + { + break; + } + case Graphics::PipelineStage::TESSELATION_CONTROL: + { + break; + } + case Graphics::PipelineStage::TESSELATION_EVALUATION: + { + break; + } + case Graphics::PipelineStage::BOTTOM_OF_PIPELINE: + { + break; + } } - case Graphics::PipelineStage::BOTTOM_OF_PIPELINE: + + if(pipelineStage) { - break; + auto shader = gl->CreateShader(pipelineStage); + const auto src = reinterpret_cast(createInfo.sourceData); + GLint size = createInfo.sourceSize; + gl->ShaderSource(shader, 1, const_cast(&src), &size); + gl->CompileShader(shader); + + GLint status{0}; + gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status); + if(status != GL_TRUE) + { + char output[4096]; + GLsizei size{0u}; + gl->GetShaderInfoLog(shader, 4096, &size, output); + DALI_LOG_ERROR("Code: %s\n", reinterpret_cast(createInfo.sourceData)); + DALI_LOG_ERROR("glCompileShader() failed: \n%s\n", output); + gl->DeleteShader(shader); + return false; + } + glShader = shader; } + return true; } + return true; + } + + void Destroy() + { + auto gl = controller.GetGL(); - if(pipelineStage) + if(gl && glShader) { - auto shader = gl->CreateShader(pipelineStage); - const auto src = reinterpret_cast(GetCreateInfo().sourceData); - GLint size = GetCreateInfo().sourceSize; - gl->ShaderSource(shader, 1, const_cast(&src), &size); - gl->CompileShader(shader); - - GLint status{0}; - gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status); - if(status != GL_TRUE) - { - char output[4096]; - GLsizei size{0u}; - gl->GetShaderInfoLog(shader, 4096, &size, output); - DALI_LOG_ERROR("Code: %s\n", reinterpret_cast(GetCreateInfo().sourceData)); - DALI_LOG_ERROR("glCompileShader() failed: \n%s\n", output); - gl->DeleteShader(shader); - return false; - } - mImpl->glShader = shader; + gl->DeleteShader(glShader); + glShader = 0; } - return true; } - return true; + + EglGraphicsController& controller; + ShaderCreateInfo createInfo; + std::vector source{}; + + uint32_t glShader{}; + uint32_t refCount{0u}; + uint32_t flushCount{0u}; ///< Number of frames at refCount=0 +}; + +ShaderImpl::ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller) +{ + mImpl = std::make_unique(controller, createInfo); +} + +ShaderImpl::~ShaderImpl() +{ + if(!mImpl->controller.IsShuttingDown()) + { + mImpl->Destroy(); + } +} + +uint32_t ShaderImpl::Retain() +{ + mImpl->flushCount = 0; + return ++mImpl->refCount; +} + +uint32_t ShaderImpl::Release() +{ + uint32_t remainingCount = --mImpl->refCount; + mImpl->flushCount = 0; + return remainingCount; +} + +[[nodiscard]] uint32_t ShaderImpl::GetRefCount() const +{ + return mImpl->refCount; +} + +[[nodiscard]] uint32_t ShaderImpl::IncreaseFlushCount() +{ + return ++mImpl->flushCount; +} + +[[nodiscard]] uint32_t ShaderImpl::GetFlushCount() const +{ + return mImpl->flushCount; +} + +/** + * @brief Compiles shader + * + * @return True on success + */ +[[nodiscard]] bool ShaderImpl::Compile() const +{ + return mImpl->Compile(); } -uint32_t Shader::GetGLShader() const +[[nodiscard]] uint32_t ShaderImpl::GetGLShader() const { return mImpl->glShader; } -void Shader::DestroyResource() +const ShaderCreateInfo& ShaderImpl::GetCreateInfo() const +{ + return mImpl->createInfo; +} + +[[nodiscard]] EglGraphicsController& ShaderImpl::GetController() const +{ + return mImpl->controller; +} + +Shader::~Shader() { - if(mImpl->glShader) + if(!mShader->Release()) { - auto gl = GetController().GetGL(); - if(!gl) - { - return; - } - gl->DeleteShader(mImpl->glShader); + GetImplementation()->GetController().GetPipelineCache().MarkShaderCacheFlushRequired(); } } +[[nodiscard]] const ShaderCreateInfo& Shader::GetCreateInfo() const +{ + return GetImplementation()->GetCreateInfo(); +} + void Shader::DiscardResource() { - GetController().DiscardResource(this); + auto& controller = GetImplementation()->GetController(); + if(!controller.IsShuttingDown()) + { + controller.DiscardResource(this); + } } } // 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 180a14b..28c7370 100644 --- a/dali/internal/graphics/gles-impl/gles-graphics-shader.h +++ b/dali/internal/graphics/gles-impl/gles-graphics-shader.h @@ -2,7 +2,7 @@ #define DALI_GRAPHICS_GLES_SHADER_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 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. @@ -27,9 +27,7 @@ namespace Dali::Graphics::GLES { -using ShaderResource = Resource; - -class Shader : public ShaderResource +class ShaderImpl { public: /** @@ -37,29 +35,28 @@ public: * @param[in] createInfo Valid createInfo structure * @param[in] controller Reference to the controller */ - Shader(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller); + ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller); + ~ShaderImpl(); - /** - * @brief Destructor - */ - ~Shader() override; + uint32_t Retain(); + + uint32_t Release(); + + [[nodiscard]] uint32_t GetRefCount() const; /** - * @brief Called when GL resources are destroyed + * Whilst unreferenced, increase the flush count and return it + * + * @return The new flush count */ - void DestroyResource() override; + [[nodiscard]] uint32_t IncreaseFlushCount(); /** - * @brief Called when initializing the resource + * Get the flush count whilst unreferenced * - * @return True on success + * @return the flush count */ - bool InitializeResource() override - { - // The Shader has instant initialization, hence no need to initialize GL resource - // here - return true; - } + [[nodiscard]] uint32_t GetFlushCount() const; /** * @brief Compiles shader @@ -69,17 +66,83 @@ public: [[nodiscard]] bool Compile() const; /** - * @brief Called when UniquePtr<> on client-side dies + * @brief Destroys GL shader */ - void DiscardResource() override; + void Destroy(); uint32_t GetGLShader() const; + [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const; + + [[nodiscard]] EglGraphicsController& GetController() const; + private: + friend class Shader; struct Impl; std::unique_ptr mImpl{nullptr}; }; +class Shader : public Graphics::Shader +{ +public: + /** + * @brief Constructor + * + * @param[in] impl Pointer to valid implementation + */ + explicit Shader(ShaderImpl* impl) + : mShader(impl) + { + mShader->Retain(); + } + + /** + * @brief Destructor + */ + ~Shader() override; + + [[nodiscard]] ShaderImpl* GetImplementation() const + { + return mShader; + } + + [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const; + + bool operator==(const GLES::Shader& shader) const + { + return (shader.mShader == mShader); + } + + bool operator==(const GLES::ShaderImpl* shaderImpl) const + { + return (shaderImpl == mShader); + } + + bool operator!=(const GLES::Shader& shader) const + { + return (shader.mShader != mShader); + } + + /** + * @brief Called when UniquePtr<> on client-side dies. + */ + void DiscardResource(); + + /** + * @brief Destroying GL resources + * + * This function is kept for compatibility with Resource<> class + * so can the object can be use with templated functions. + */ + void DestroyResource() + { + // nothing to do here + } + +private: + ShaderImpl* mShader{nullptr}; +}; + } // namespace Dali::Graphics::GLES #endif