/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2018 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.
// INTERNAL INCLUDES
#include <dali/public-api/dali-core-version.h>
#include <dali/public-api/common/dali-common.h>
-#include <dali/public-api/common/hash.h>
+#include <dali/devel-api/common/hash.h>
#include <dali/integration-api/debug.h>
-#include <dali/internal/event/resources/resource-client.h>
-#include <dali/internal/event/effects/shader-effect-impl.h>
-#include <dali/internal/event/effects/shader-declarations.h>
-
-// compile time generated shader strings
-#include "dali-shaders.h"
+#include <dali/integration-api/platform-abstraction.h>
+#include <dali/internal/event/common/thread-local-storage.h>
namespace
{
const char* SHADER_SUFFIX = ".dali-bin";
}
-// Use pre-compiler constants in order to utilize string concatenation
-#define SHADER_DEF_USE_BONES "#define USE_BONES\n"
-#define SHADER_DEF_USE_COLOR "#define USE_COLOR\n"
-#define SHADER_DEF_USE_GRADIENT "#define USE_GRADIENT\n"
-
-using namespace Dali::Integration;
-
namespace Dali
{
namespace Internal
{
-ShaderFactory::ShaderFactory(ResourceClient& resourceClient)
-: mResourceClient(resourceClient)
+namespace
{
+
+/**
+ * @brief Generates a filename for a shader binary based on the hash value passed in.
+ * @param[in] shaderHash A hash over shader sources.
+ * @param[out] filename A string to overwrite with the filename.
+ */
+void shaderBinaryFilename( size_t shaderHash, std::string& filename )
+{
+ std::stringstream binaryShaderFilenameBuilder( std::ios_base::out );
+ binaryShaderFilenameBuilder << CORE_MAJOR_VERSION << VERSION_SEPARATOR << CORE_MINOR_VERSION << VERSION_SEPARATOR << CORE_MICRO_VERSION << VERSION_SEPARATOR
+ << shaderHash
+ << SHADER_SUFFIX;
+ filename = binaryShaderFilenameBuilder.str();
}
-ShaderFactory::~ShaderFactory()
+}
+
+ShaderFactory::ShaderFactory()
{
}
-ResourceTicketPtr ShaderFactory::Load(const std::string& vertexSource, const std::string& fragmentSource, size_t& shaderHash)
+ShaderFactory::~ShaderFactory()
{
- ResourceTicketPtr ticket;
-
- shaderHash = CalculateHash(vertexSource, fragmentSource);
- std::stringstream stringHash;
- stringHash << CORE_MAJOR_VERSION << VERSION_SEPARATOR << CORE_MINOR_VERSION << VERSION_SEPARATOR << CORE_MICRO_VERSION << VERSION_SEPARATOR;
- stringHash << shaderHash;
- std::string filename;
- filename.append( stringHash.str() );
- filename.append( SHADER_SUFFIX );
-
- ShaderResourceType resourceType(shaderHash, vertexSource, fragmentSource);
- ResourceTypePath typePath(resourceType, filename);
-
- // Search for a matching resource
- ResourceTypePathIdIter iter = mResourceTypePathIdMap.end();
- if ( !mResourceTypePathIdMap.empty() )
+ // Let all the cached objects destroy themselves:
+ for( std::size_t i = 0, cacheSize = mShaderBinaryCache.Size(); i < cacheSize; ++i )
{
- iter = mResourceTypePathIdMap.find( typePath );
+ if( mShaderBinaryCache[i] )
+ {
+ mShaderBinaryCache[i]->Unreference();
+ }
}
+}
- if ( mResourceTypePathIdMap.end() != iter )
- {
- // The resource was previously requested
- unsigned int resourceId = iter->second;
+ShaderDataPtr ShaderFactory::Load( const std::string& vertexSource, const std::string& fragmentSource, const Dali::Shader::Hint::Value hints, size_t& shaderHash )
+{
+ // Work out the filename for the binary that the glsl source will be compiled and linked to:
+ shaderHash = CalculateHash( vertexSource.c_str(), fragmentSource.c_str() );
+ std::string binaryShaderFilename;
+ shaderBinaryFilename( shaderHash, binaryShaderFilename );
- // The ticket may still be alive, request another copy
- ticket = mResourceClient.RequestResourceTicket( resourceId );
+ ShaderDataPtr shaderData;
- // Clean-up the map of resource IDs, if the ticket has been discarded
- if ( !ticket )
- {
- mResourceTypePathIdMap.erase( iter );
- }
- else
+ /// Check a cache of previously loaded shaders:
+ for( std::size_t i = 0, cacheSize = mShaderBinaryCache.Size(); i < cacheSize; ++i )
+ {
+ if( mShaderBinaryCache[i]->GetHashValue() == shaderHash )
{
- DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "ShaderFactory::Load filename= %s already requested to Load\n", filename.c_str());
+ shaderData = mShaderBinaryCache[i];
+
+ DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "Mem cache hit on path: \"%s\"\n", binaryShaderFilename.c_str() );
+ break;
}
}
- if ( !ticket )
+ // If memory cache failed check the file system for a binary or return a source-only ShaderData:
+ if( shaderData.Get() == nullptr )
{
- // Load the shader (loaded synchronously in Update thread so its ready by the time the set shader message is processed)
- ticket = mResourceClient.LoadShader(resourceType, filename);
- DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "ShaderFactory::Load Ticket ID:%d, path: \"%s\"\n", ticket->GetId(), filename.c_str());
+ // Allocate the structure that returns the loaded shader:
+ shaderData = new ShaderData( vertexSource, fragmentSource, hints );
+ shaderData->SetHashValue( shaderHash );
+ shaderData->GetBuffer().Clear();
+
+ // Try to load the binary (this will fail if the shader source has never been compiled before):
+ ThreadLocalStorage& tls = ThreadLocalStorage::Get();
+ Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
+ const bool loaded = platformAbstraction.LoadShaderBinaryFile( binaryShaderFilename, shaderData->GetBuffer() );
- mResourceTypePathIdMap.insert( ResourceTypePathIdPair( typePath, ticket->GetId() ) );
+ if( loaded )
+ {
+ MemoryCacheInsert( *shaderData );
+ }
+
+ DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, loaded ?
+ "loaded on path: \"%s\"\n" :
+ "failed to load on path: \"%s\"\n",
+ binaryShaderFilename.c_str());
}
- return ticket;
+ return shaderData;
+}
+
+void ShaderFactory::SaveBinary( Internal::ShaderDataPtr shaderData )
+{
+ // Save the binary to the file system:
+ std::string binaryShaderFilename;
+ shaderBinaryFilename( shaderData->GetHashValue(), binaryShaderFilename );
+
+ ThreadLocalStorage& tls = ThreadLocalStorage::Get();
+ Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
+ 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 );
+
+ 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::LoadDefaultShaders()
+void ShaderFactory::MemoryCacheInsert( ShaderData& shaderData )
{
- mDefaultShader = ShaderEffect::New();
-
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_IMAGE, SHADER_DEFAULT, ImageVertex, ImageFragment, false );
-
- // Untextured meshes
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_UNTEXTURED_MESH, SHADER_EVENLY_LIT,
- UntexturedMeshVertex,
- UntexturedMeshFragment,
- false );
-
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_UNTEXTURED_MESH, SHADER_RIGGED_AND_EVENLY_LIT,
- std::string( SHADER_DEF_USE_BONES ) + UntexturedMeshVertex,
- UntexturedMeshFragment,
- true );
-
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_UNTEXTURED_MESH, SHADER_RIGGED_AND_VERTEX_COLOR,
- std::string( SHADER_DEF_USE_BONES SHADER_DEF_USE_COLOR ) + UntexturedMeshVertex,
- std::string( SHADER_DEF_USE_COLOR ) + UntexturedMeshFragment,
- true );
-
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_UNTEXTURED_MESH, SHADER_VERTEX_COLOR,
- std::string( SHADER_DEF_USE_COLOR ) + UntexturedMeshVertex,
- std::string( SHADER_DEF_USE_COLOR ) + UntexturedMeshFragment,
- false );
-
- // Textured meshes
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_EVENLY_LIT,
- TexturedMeshVertex,
- TexturedMeshFragment,
- false );
-
- mDefaultShader->SendProgramMessage( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_RIGGED_AND_EVENLY_LIT,
- std::string( SHADER_DEF_USE_BONES ) + TexturedMeshVertex,
- TexturedMeshFragment,
- true );
+ DALI_ASSERT_DEBUG( shaderData.GetBufferSize() > 0 );
+
+ // Save the binary into to memory cache:
+ if( shaderData.GetBufferSize() > 0 )
+ {
+ mShaderBinaryCache.Reserve( mShaderBinaryCache.Size() + 1 ); // Make sure the push won't throw after we inc the ref count.
+ shaderData.Reference();
+ mShaderBinaryCache.PushBack( &shaderData );
+ DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "CACHED BINARY FOR HASH: %u\n", shaderData.GetHashValue() );
+ }
}
} // namespace Internal