use modern construct 'nullptr' instead of 'NULL' or '0'
[platform/core/uifw/dali-core.git] / dali / internal / event / effects / shader-factory.cpp
index 2627345..59832f9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
 {
@@ -39,118 +35,124 @@ const char* VERSION_SEPARATOR = "-";
 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