Let we cache string based shader's ShaderData.
It will be useful to avoid use multiple ShaderData creation
even if we use same source code.
Change-Id: Ifeaa9c2fa47b402690dab9b650b01ce874002069
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
END_TEST;
}
-int UtcDaliInternalShaderSaveAndLoad(void)
+int UtcDaliInternalShaderSaveAndLoad01(void)
{
TestApplication application;
END_TEST;
}
+
+int UtcDaliInternalShaderSaveAndLoad02(void)
+{
+ TestApplication application;
+
+ std::string vertexShader1 = "some vertex code\n";
+ std::string fragmentShader1 = "some fragment code\n";
+
+ std::string vertexShader2 = "some another vertex code\n";
+ std::string fragmentShader2 = "some another fragment code\n";
+
+ Dali::Vector<uint8_t> dummyBuffer(5);
+ for(size_t i = 0; i < 5; i++)
+ {
+ dummyBuffer[i] = static_cast<uint8_t>(i + 21);
+ }
+
+ // Make save and load failed successful
+ auto& platformAbstraction = application.GetPlatform();
+ platformAbstraction.SetLoadFileResult(false, dummyBuffer);
+
+ // Test version number in the shader data
+ Dali::Internal::ThreadLocalStorage& tls = Dali::Internal::ThreadLocalStorage::Get();
+ Dali::Internal::ShaderFactory& shaderFactory = tls.GetShaderFactory();
+
+ tet_printf("Load shader1. It should be cached at string container\n");
+ size_t shaderHash1 = 0u;
+ Internal::ShaderDataPtr shaderData1 = shaderFactory.Load(vertexShader1, fragmentShader1, Shader::Hint::NONE, 0u, "", shaderHash1);
+ DALI_TEST_CHECK(shaderHash1 != 0u);
+
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), true, TEST_LOCATION);
+
+ // Reset trace callstack
+ platformAbstraction.GetTrace().Reset();
+
+ tet_printf("Load shader2. It also should be cached at string container\n");
+ size_t shaderHash2 = 0u;
+ Internal::ShaderDataPtr shaderData2 = shaderFactory.Load(vertexShader2, fragmentShader2, Shader::Hint::NONE, 0u, "", shaderHash2);
+ DALI_TEST_CHECK(shaderHash2 != 0u);
+
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), true, TEST_LOCATION);
+
+ // Reset trace callstack
+ platformAbstraction.GetTrace().Reset();
+
+ tet_printf("Both shader 1 and 2 don't have binary now.\n");
+ DALI_TEST_EQUALS(shaderData1.Get()->HasBinary(), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(shaderData2.Get()->HasBinary(), false, TEST_LOCATION);
+
+ // Copy the memory of dummy
+ shaderData1.Get()->AllocateBuffer(5);
+ for(size_t i = 0; i < 5; i++)
+ {
+ shaderData1.Get()->GetBuffer()[i] = static_cast<uint8_t>(i + 1);
+ }
+
+ DALI_TEST_EQUALS(shaderData1.Get()->HasBinary(), true, TEST_LOCATION);
+
+ tet_printf("Save shaderData1 as binary, but failed.\n");
+ shaderFactory.SaveBinary(shaderData1);
+
+ tet_printf("Check shader saved\n");
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), false, TEST_LOCATION);
+
+ // Reset trace callstack
+ platformAbstraction.GetTrace().Reset();
+
+ tet_printf("Save shaderData1 as binary, and success now.\n");
+ platformAbstraction.SetSaveFileResult(true);
+ shaderFactory.SaveBinary(shaderData1);
+
+ tet_printf("Check shader saved\n");
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), false, TEST_LOCATION);
+
+ // Reset trace callstack
+ platformAbstraction.GetTrace().Reset();
+
+ tet_printf("Load shader with same code as shaderData1\n");
+ size_t shaderHash3 = 0u;
+ Internal::ShaderDataPtr shaderData3 = shaderFactory.Load(vertexShader1, fragmentShader1, Shader::Hint::NONE, 0u, "", shaderHash3);
+
+ tet_printf("Check shaderData1 cached\n");
+ DALI_TEST_EQUALS(shaderHash3, shaderHash1, TEST_LOCATION);
+ DALI_TEST_EQUALS(shaderData3.Get()->HasBinary(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(shaderData3.Get(), shaderData1.Get(), TEST_LOCATION);
+
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), false, TEST_LOCATION);
+
+ // Reset trace callstack
+ platformAbstraction.GetTrace().Reset();
+
+ tet_printf("Load shader with same code as shaderData2\n");
+ size_t shaderHash4 = 0u;
+ Internal::ShaderDataPtr shaderData4 = shaderFactory.Load(vertexShader2, fragmentShader2, Shader::Hint::NONE, 0u, "", shaderHash4);
+
+ tet_printf("Check shaderData2 cached\n");
+ DALI_TEST_EQUALS(shaderHash4, shaderHash2, TEST_LOCATION);
+ DALI_TEST_EQUALS(shaderData4.Get(), shaderData2.Get(), TEST_LOCATION);
+ DALI_TEST_EQUALS(shaderData4.Get()->HasBinary(), false, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), false, TEST_LOCATION);
+
+ // Reset trace callstack
+ platformAbstraction.GetTrace().Reset();
+
+ tet_printf("Allow to load shader binary\n");
+ platformAbstraction.SetLoadFileResult(true, dummyBuffer);
+
+ tet_printf("Load shader same as shaderData1, but difference render pass tag\n");
+ size_t shaderHash5 = 0u;
+ Internal::ShaderDataPtr shaderData5 = shaderFactory.Load(vertexShader1, fragmentShader1, Shader::Hint::NONE, 1u, "", shaderHash5);
+
+ tet_printf("Check shaderData1 and shaderData5 have same hash, but deferent buffer\n");
+ DALI_TEST_EQUALS(shaderHash5, shaderHash1, TEST_LOCATION);
+ DALI_TEST_CHECK(shaderData5.Get() != shaderData1.Get());
+ DALI_TEST_EQUALS(shaderData5.Get()->HasBinary(), true, TEST_LOCATION);
+
+ tet_printf("Check shaderData5 binary same as dummy buffer\n");
+ DALI_TEST_EQUALS(shaderData5.Get()->GetBufferSize(), dummyBuffer.Count(), TEST_LOCATION);
+ for(size_t i = 0; i < 5; ++i)
+ {
+ DALI_TEST_EQUALS(shaderData5.Get()->GetBuffer()[i], dummyBuffer[i], TEST_LOCATION);
+ }
+
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::SaveShaderBinaryFileFunc), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(platformAbstraction.WasCalled(TestPlatformAbstraction::LoadShaderBinaryFileFunc), true, TEST_LOCATION);
+ END_TEST;
+}
#include <iostream>
+#include <test-platform-abstraction.h>
+
using namespace Dali;
void utc_dali_shader_startup(void)
ShaderFactory::~ShaderFactory()
{
// Let all the cached objects destroy themselves:
- for(std::size_t i = 0, cacheSize = mShaderBinaryCache.Size(); i < cacheSize; ++i)
+ for(auto&& listPair : mShaderBinaryCache)
{
- if(mShaderBinaryCache[i])
+ for(auto&& shaderData : listPair.second)
{
- mShaderBinaryCache[i]->Unreference();
+ shaderData->Unreference();
+ }
+ }
+ for(auto&& listPair : mShaderStringCache)
+ {
+ for(auto&& shaderData : listPair.second)
+ {
+ shaderData->Unreference();
}
}
}
{
// Work out the filename for the binary that the glsl source will be compiled and linked to:
shaderHash = CalculateHash(vertexSource, fragmentSource);
- std::string binaryShaderFilename;
- shaderBinaryFilename(shaderHash, binaryShaderFilename);
ShaderDataPtr shaderData;
/// Check a cache of previously loaded shaders:
- for(std::size_t i = 0, cacheSize = mShaderBinaryCache.Size(); i < cacheSize; ++i)
+ if(mShaderBinaryCache.count(shaderHash) > 0u)
{
- if(mShaderBinaryCache[i]->GetHashValue() == shaderHash)
+ const auto& cacheList = mShaderBinaryCache[shaderHash];
+ for(std::size_t i = 0, cacheSize = cacheList.Count(); i < cacheSize; ++i)
{
- shaderData = mShaderBinaryCache[i];
+ if(cacheList[i]->GetHints() == hints &&
+ cacheList[i]->GetRenderPassTag() == renderPassTag)
+ {
+ shaderData = cacheList[i];
+
+#ifdef DEBUG_ENABLED
+ if(Debug::Filter::gShader->IsEnabledFor(Debug::General))
+ {
+ std::string binaryShaderFilename;
+ shaderBinaryFilename(shaderHash, binaryShaderFilename);
+
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Mem cache hit on path: \"%s\", Hint : %d, Tag : %u\n", binaryShaderFilename.c_str(), static_cast<int>(hints), renderPassTag);
+ }
+#endif
+ break;
+ }
+ }
+ }
- DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Mem cache hit on path: \"%s\"\n", binaryShaderFilename.c_str());
- break;
+ /// Check a cache of previously loaded shaders as normal string:
+ if(shaderData.Get() == nullptr && mShaderStringCache.count(shaderHash) > 0u)
+ {
+ const auto& cacheList = mShaderStringCache[shaderHash];
+ for(std::size_t i = 0, cacheSize = cacheList.Size(); i < cacheSize; ++i)
+ {
+ if(cacheList[i]->GetHints() == hints &&
+ cacheList[i]->GetRenderPassTag() == renderPassTag)
+ {
+ shaderData = cacheList[i];
+
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Mem cache hit on string shader. Hash : \"%zu\", Hint : %d, Tag : %u\n", shaderHash, static_cast<int>(hints), renderPassTag);
+ break;
+ }
}
}
// If memory cache failed check the file system for a binary or return a source-only ShaderData:
if(shaderData.Get() == nullptr)
{
+ std::string binaryShaderFilename;
+ shaderBinaryFilename(shaderHash, binaryShaderFilename);
+
// Allocate the structure that returns the loaded shader:
shaderData = new ShaderData(vertexSource, fragmentSource, hints, renderPassTag, name);
shaderData->SetHashValue(shaderHash);
Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
const bool loaded = platformAbstraction.LoadShaderBinaryFile(binaryShaderFilename, shaderData->GetBuffer());
- if(loaded)
- {
- MemoryCacheInsert(*shaderData);
- }
+ MemoryCacheInsert(*shaderData, loaded);
DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, loaded ? "loaded on path: \"%s\"\n" : "failed to load on path: \"%s\"\n", binaryShaderFilename.c_str());
}
const bool saved = platformAbstraction.SaveShaderBinaryFile(binaryShaderFilename, &shaderData->GetBuffer()[0], static_cast<unsigned int>(shaderData->GetBufferSize())); // don't expect buffer larger than unsigned int
// Save the binary into to memory cache:
- MemoryCacheInsert(*shaderData);
+ MemoryCacheInsert(*shaderData, saved);
DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, saved ? "Saved to file: %s\n" : "Save to file failed: %s\n", binaryShaderFilename.c_str());
- if(saved)
- {
- } // Avoid unused variable warning in release builds
}
-void ShaderFactory::MemoryCacheInsert(ShaderData& shaderData)
+void ShaderFactory::MemoryCacheInsert(ShaderData& shaderData, const bool isBinaryCached)
{
- DALI_ASSERT_DEBUG(shaderData.GetBufferSize() > 0);
+ const size_t shaderHash = shaderData.GetHashValue();
// Save the binary into to memory cache:
- if(shaderData.GetBufferSize() > 0)
+ if(isBinaryCached)
+ {
+ DALI_ASSERT_DEBUG(shaderData.GetBufferSize() > 0);
+
+ // Remove shaderdata from string cache if it exists:
+ RemoveStringShaderData(shaderData);
+
+ auto& cacheList = mShaderBinaryCache[shaderHash]; ///< Get or create a new cache list.
+
+ cacheList.Reserve(cacheList.Size() + 1); // Make sure the push won't throw after we inc the ref count.
+ shaderData.Reference();
+ cacheList.PushBack(&shaderData);
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "CACHED BINARY FOR HASH: %u, HINT: %d, TAG: %u\n", shaderHash, static_cast<int>(shaderData.GetHints()), shaderData.GetRenderPassTag());
+ }
+ else
{
- mShaderBinaryCache.Reserve(mShaderBinaryCache.Size() + 1); // Make sure the push won't throw after we inc the ref count.
+ auto& cacheList = mShaderStringCache[shaderHash]; ///< Get or create a new cache list.
+
+ // Ignore shaderdata with string if it already exists:
+ for(auto iter = cacheList.Begin(); iter != cacheList.End(); ++iter)
+ {
+ if((*iter)->GetHints() == shaderData.GetHints() &&
+ (*iter)->GetRenderPassTag() == shaderData.GetRenderPassTag())
+ {
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "ALREADY CACHED NON-BINARY CACHE FOR HASH: %u, HINT: %d, TAG: %u\n", shaderHash, static_cast<int>(shaderData.GetHints()), shaderData.GetRenderPassTag());
+ return;
+ }
+ }
shaderData.Reference();
- mShaderBinaryCache.PushBack(&shaderData);
- DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "CACHED BINARY FOR HASH: %u\n", shaderData.GetHashValue());
+ cacheList.PushBack(&shaderData);
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "CACHED NON-BINARY SHADER FOR HASH: %u, HINT: %d, TAG: %u\n", shaderHash, static_cast<int>(shaderData.GetHints()), shaderData.GetRenderPassTag());
+ }
+}
+
+void ShaderFactory::RemoveStringShaderData(ShaderData& shaderData)
+{
+ const size_t shaderHash = shaderData.GetHashValue();
+
+ if(mShaderStringCache.count(shaderHash) > 0u)
+ {
+ auto& cacheList = mShaderStringCache[shaderHash];
+ for(auto iter = cacheList.Begin(); iter != cacheList.End(); ++iter)
+ {
+ if((*iter)->GetHints() == shaderData.GetHints() &&
+ (*iter)->GetRenderPassTag() == shaderData.GetRenderPassTag())
+ {
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "REMOVE NON-BINARY CACHE FOR HASH: %u, HINT: %d, TAG: %u\n", shaderHash, static_cast<int>(shaderData.GetHints()), shaderData.GetRenderPassTag());
+ // Reduce reference before erase
+ (*iter)->Unreference();
+ cacheList.Erase(iter);
+ break;
+ }
+ }
+ if(cacheList.Count() == 0)
+ {
+ mShaderStringCache.erase(shaderHash);
+ }
}
}
*
*/
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
+
// INTERNAL INCLUDES
#include <dali/internal/common/message.h>
#include <dali/internal/common/shader-data.h>
void SaveBinary(Internal::ShaderDataPtr shader) override;
private:
- void MemoryCacheInsert(Internal::ShaderData& shaderData);
+ void MemoryCacheInsert(Internal::ShaderData& shaderData, const bool isBinaryCached);
+
+ /**
+ * @brief Removes the string shader data from the cache.
+ * @param[in] shaderData The shader data to remove.
+ */
+ void RemoveStringShaderData(Internal::ShaderData& shaderData);
// Undefined
- ShaderFactory(const ShaderFactory&);
+ ShaderFactory(const ShaderFactory&) = delete;
// Undefined
- ShaderFactory& operator=(const ShaderFactory& rhs);
+ ShaderFactory& operator=(const ShaderFactory& rhs) = delete;
private:
- Dali::Vector<Internal::ShaderData*> mShaderBinaryCache; ///< Cache of pre-compiled shaders.
+ using ShaderCacheContainer = std::map<size_t, Dali::Vector<Internal::ShaderData*>>; ///< Container for the shader cache. Key is hash of shader code.
+
+ ShaderCacheContainer mShaderBinaryCache; ///< Cache of pre-compiled shaders.
+ ShaderCacheContainer mShaderStringCache; ///< Cache of non-pre-compiled shaders. (TODO : Could we clean up this cache by user management?)
}; // class ShaderFactory