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)
TestGraphicsApplication::~TestGraphicsApplication()
{
+ mGraphicsController.Shutdown();
Dali::Integration::Log::UninstallLogFunction();
delete mCore;
}
dali2-core
dali2-adaptor
glesv2
- ecore
- ecore-x
)
ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror )
/*
- * 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.
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"
"{\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");
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<ActiveUniform> 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;
+}
Graphics::UniquePtr<Program> EglGraphicsController::CreateProgram(
const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram)
{
- // Create program cache if needed
+ // Create pipeline cache if needed
if(!mPipelineCache)
{
mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
{
- return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
+ // Create pipeline cache if needed
+ if(!mPipelineCache)
+ {
+ mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
+ }
+ return mPipelineCache->GetShader(shaderCreateInfo, std::move(oldShader));
}
Graphics::UniquePtr<Sampler> EglGraphicsController::CreateSampler(const SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr<Sampler>&& oldSampler)
#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
{
/**
explicit Impl(EglGraphicsController& _controller)
: controller(_controller),
pipelineEntriesFlushRequired(false),
- programEntriesFlushRequired(false)
+ programEntriesFlushRequired(false),
+ shaderEntriesFlushRequired(false)
{
// Initialise lookup table
InitialiseStateCompareLookupTable();
uint32_t stateBitmask{0u};
};
- EglGraphicsController& controller;
- std::vector<CacheEntry> entries;
-
/**
* @brief Sorted array of shaders used to create program
*/
UniquePtr<ProgramImpl> program{nullptr};
};
+ struct ShaderCacheEntry
+ {
+ UniquePtr<ShaderImpl> shaderImpl{nullptr};
+ };
+
+ EglGraphicsController& controller;
+ std::vector<CacheEntry> entries;
std::vector<ProgramCacheEntry> programEntries;
+ std::vector<ShaderCacheEntry> shaderEntries;
bool pipelineEntriesFlushRequired : 1;
bool programEntriesFlushRequired : 1;
+ bool shaderEntriesFlushRequired : 1;
};
PipelineCache::PipelineCache(EglGraphicsController& controller)
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<Graphics::Shader> PipelineCache::GetShader(const ShaderCreateInfo& shaderCreateInfo,
+ Graphics::UniquePtr<Graphics::Shader>&& oldShader)
+{
+ ShaderImpl* cachedShader = FindShaderImpl(shaderCreateInfo);
+
+ // Return same pointer if nothing changed
+ if(oldShader && *static_cast<GLES::Shader*>(oldShader.get()) == cachedShader)
+ {
+ return std::move(oldShader);
+ }
+
+ if(!cachedShader)
+ {
+ auto shader = MakeUnique<GLES::ShaderImpl>(shaderCreateInfo, mImpl->controller);
+ cachedShader = shader.get();
+
+ mImpl->shaderEntries.emplace_back();
+ mImpl->shaderEntries.back().shaderImpl = std::move(shader);
+ }
+ auto wrapper = MakeUnique<GLES::Shader, CachedObjectDeleter<GLES::Shader>>(cachedShader);
+ return std::move(wrapper);
+}
+
void PipelineCache::FlushCache()
{
if(mImpl->pipelineEntriesFlushRequired)
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()
mImpl->programEntriesFlushRequired = true;
}
+void PipelineCache::MarkShaderCacheFlushRequired()
+{
+ mImpl->shaderEntriesFlushRequired = true;
+}
+
} // namespace Dali::Graphics::GLES
#include <dali/graphics-api/graphics-pipeline.h>
#include <dali/graphics-api/graphics-program-create-info.h>
#include <dali/graphics-api/graphics-program.h>
+#include <dali/graphics-api/graphics-shader-create-info.h>
+#include <dali/graphics-api/graphics-shader.h>
// INTERNAL INCLUDES
#include "gles-graphics-resource.h"
class PipelineImpl;
class Program;
class ProgramImpl;
+class Shader;
+class ShaderImpl;
+
/**
* @brief PipelineCache manages pipeline and program
* objects so there are no duplicates created.
Graphics::UniquePtr<Graphics::Program> GetProgram(const ProgramCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Program>&& 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<Graphics::Shader> GetShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Graphics::Shader>&& oldShader);
+
+ /**
* @brief Flushes pipeline and program cache
*
* Removes cached items when they are no longer needed. This function
*/
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
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<Impl> mImpl;
const auto* shader = static_cast<const GLES::Shader*>(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);
#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.
///////////////////////////////////////////////////////////////
/**
- * @brief Wrapper for the pipeline implementation
+ * @brief Wrapper for the program implementation
*
* This object is returned back to the client-side
*/
namespace Dali::Graphics::GLES
{
-struct Shader::Impl
+struct ShaderImpl::Impl
{
- Impl() = default;
- ~Impl() = default;
-
- std::vector<char> 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<Impl>();
-
- // Make a copy of source code
- mImpl->source.resize(createInfo.sourceSize);
- std::copy(reinterpret_cast<const char*>(mCreateInfo.sourceData),
- reinterpret_cast<const char*>(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<const char*>(_createInfo.sourceData),
+ reinterpret_cast<const char*>(_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<const char*>(createInfo.sourceData);
+ GLint size = createInfo.sourceSize;
+ gl->ShaderSource(shader, 1, const_cast<const char**>(&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<const char*>(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<const char*>(GetCreateInfo().sourceData);
- GLint size = GetCreateInfo().sourceSize;
- gl->ShaderSource(shader, 1, const_cast<const char**>(&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<const char*>(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<char> 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<Impl>(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
#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.
namespace Dali::Graphics::GLES
{
-using ShaderResource = Resource<Graphics::Shader, Graphics::ShaderCreateInfo>;
-
-class Shader : public ShaderResource
+class ShaderImpl
{
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
[[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<Impl> 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