Merge "Clean up the code to build successfully on macOS" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / effects / shader-factory.cpp
index 6c488d3..c91f2ed 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.
 #include <dali/internal/event/effects/shader-factory.h>
 
 // EXTERNAL INCLUDES
-#include <algorithm>
 #include <sstream>
 
 // INTERNAL INCLUDES
-#include <dali/internal/common/dali-hash.h>
+#include <dali/public-api/dali-core-version.h>
 #include <dali/public-api/common/dali-common.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>
+#include <dali/integration-api/platform-abstraction.h>
+#include <dali/internal/event/common/thread-local-storage.h>
 
-// the generated shader strings
-#include "dali-shaders.h"
-
-
-// Use pre-compiler constants in order to utilize string concatenation
-#define SHADER_DEF_USE_BONES    "#define USE_BONES\n"
-#define SHADER_DEF_USE_LIGHTING "#define USE_LIGHTING\n"
-#define SHADER_DEF_USE_COLOR    "#define USE_COLOR\n"
-#define SHADER_DEF_USE_GRADIENT "#define USE_GRADIENT\n"
-
-using namespace Dali::Integration;
+namespace
+{
+const char* VERSION_SEPARATOR = "-";
+const char* SHADER_SUFFIX = ".dali-bin";
+}
 
 namespace Dali
 {
@@ -48,180 +41,116 @@ 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() = default;
+
 ShaderFactory::~ShaderFactory()
 {
+  // Let all the cached objects destroy themselves:
+  for( std::size_t i = 0, cacheSize = mShaderBinaryCache.Size(); i < cacheSize; ++i )
+  {
+    if( mShaderBinaryCache[i] )
+    {
+      mShaderBinaryCache[i]->Unreference();
+    }
+  }
 }
 
-ResourceTicketPtr ShaderFactory::Load(const std::string& vertexSource, const std::string& fragmentSource, size_t& shaderHash)
+ShaderDataPtr ShaderFactory::Load( const std::string& vertexSource, const std::string& fragmentSource, const Dali::Shader::Hint::Value hints, size_t& shaderHash )
 {
-  ResourceTicketPtr ticket;
-
-  shaderHash = HashShaderSource(vertexSource, fragmentSource);
-  std::stringstream stringHash;
-  stringHash << shaderHash;
-  std::string filename = DALI_SHADERBIN_DIR;
-  filename += stringHash.str();
-  filename += ".dali-bin";
+  // 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 );
 
-  ShaderResourceType resourceType(shaderHash, vertexSource, fragmentSource);
-  ResourceTypePath typePath(resourceType, filename);
+  ShaderDataPtr shaderData;
 
-  // Search for a matching resource
-  ResourceTypePathIdIter iter = mResourceTypePathIdMap.end();
-  if ( !mResourceTypePathIdMap.empty() )
+  /// Check a cache of previously loaded shaders:
+  for( std::size_t i = 0, cacheSize = mShaderBinaryCache.Size(); i < cacheSize; ++i )
   {
-    iter = mResourceTypePathIdMap.find( typePath );
+    if( mShaderBinaryCache[i]->GetHashValue() == shaderHash )
+    {
+      shaderData = mShaderBinaryCache[i];
+
+      DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "Mem cache hit on path: \"%s\"\n", binaryShaderFilename.c_str() );
+      break;
+    }
   }
 
-  if ( mResourceTypePathIdMap.end() != iter )
+  // If memory cache failed check the file system for a binary or return a source-only ShaderData:
+  if( shaderData.Get() == nullptr )
   {
-    // The resource was previously requested
-    unsigned int resourceId = iter->second;
+    // Allocate the structure that returns the loaded shader:
+    shaderData = new ShaderData( vertexSource, fragmentSource, hints );
+    shaderData->SetHashValue( shaderHash );
+    shaderData->GetBuffer().Clear();
 
-    // The ticket may still be alive, request another copy
-    ticket = mResourceClient.RequestResourceTicket( resourceId );
+    // 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() );
 
-    // Clean-up the map of resource IDs, if the ticket has been discarded
-    if ( !ticket )
-    {
-      mResourceTypePathIdMap.erase( iter );
-    }
-    else
+    if( loaded )
     {
-      DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "ShaderFactory::Load filename= %s already requested to Load\n", filename.c_str());
+      MemoryCacheInsert( *shaderData );
     }
-  }
-
-  if ( !ticket )
-  {
-    // Load a shader (loaded in Update thread)
-    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());
 
-    mResourceTypePathIdMap.insert( ResourceTypePathIdPair( typePath, ticket->GetId() ) );
+    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::LoadDefaultShaders()
+void ShaderFactory::SaveBinary( Internal::ShaderDataPtr shaderData )
 {
-  mDefaultShader = ShaderEffect::New();
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_IMAGE, SHADER_DEFAULT, FlatColorTextureVertex, FlatColorTextureFragment, ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_TEXT, SHADER_DEFAULT, DistanceFieldFontVertex, DistanceFieldFontFragment, ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  LoadTextSubtypeShaders(mDefaultShader); // TODO: Remove when applications no longer need these shaders
-
-  // Untextured meshes
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_MESH, SHADER_DEFAULT,
-                              "", // Vertex shader defs
-                              SHADER_DEF_USE_LIGHTING, // fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshColorNoTextureVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshColorNoTextureFragment),
-                              ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_MESH, SHADER_EVENLY_LIT,
-                              "", // Vertex shader defs
-                              "", // fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshColorNoTextureVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshColorNoTextureFragment),
-                              ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_MESH, SHADER_RIGGED_AND_LIT,
-                              SHADER_DEF_USE_BONES,    // vertex shader defs
-                              SHADER_DEF_USE_LIGHTING, // fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshColorNoTextureVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshColorNoTextureFragment),
-                              ShaderEffect::MODIFIES_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_MESH, SHADER_RIGGED_AND_EVENLY_LIT,
-                              SHADER_DEF_USE_BONES, // Vertex shader defs
-                              "",                   // Fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshColorNoTextureVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshColorNoTextureFragment),
-                              ShaderEffect::MODIFIES_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_MESH, SHADER_RIGGED_AND_VERTEX_COLOR,
-                              (SHADER_DEF_USE_BONES SHADER_DEF_USE_COLOR), // Vertex shader defs
-                              SHADER_DEF_USE_COLOR,                        // Fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshColorNoTextureVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshColorNoTextureFragment),
-                              ShaderEffect::MODIFIES_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_MESH, SHADER_VERTEX_COLOR,
-                              SHADER_DEF_USE_COLOR,  // Vertex shader defs
-                              SHADER_DEF_USE_COLOR,  // Fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshColorNoTextureVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshColorNoTextureFragment),
-                              ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  // Textured meshes
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_DEFAULT,
-                              "",                      // Vertex shader defs
-                              SHADER_DEF_USE_LIGHTING, // fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshFragment),
-                              ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_EVENLY_LIT,
-                              "",                      // Vertex shader defs
-                              "",                      // Fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshFragment),
-                              ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_RIGGED_AND_LIT,
-                              SHADER_DEF_USE_BONES,    // Vertex shader defs
-                              SHADER_DEF_USE_LIGHTING, // Fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshFragment),
-                              ShaderEffect::MODIFIES_GEOMETRY );
-
-  mDefaultShader->SetProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_RIGGED_AND_EVENLY_LIT,
-                              SHADER_DEF_USE_BONES, // Vertex shader defs
-                              "",                   // Fragment shader defs
-                              std::string(CustomMeshPrefixVertex) + std::string(MeshVertex),
-                              std::string(CustomMeshPrefixFragment) + std::string(MeshFragment),
-                              ShaderEffect::MODIFIES_GEOMETRY );
-}
-
-void ShaderFactory::LoadTextSubtypeShaders(ShaderEffectPtr shaderEffect)
-{
-  shaderEffect->SetProgram(GEOMETRY_TYPE_TEXT, SHADER_GRADIENT,
-                           SHADER_DEF_USE_GRADIENT,
-                           SHADER_DEF_USE_GRADIENT,
-                           DistanceFieldFontVertex, DistanceFieldFontFragment,
-                           ShaderEffect::DOESNT_MODIFY_GEOMETRY );
-
-  shaderEffect->SetProgram(GEOMETRY_TYPE_TEXT, SHADER_GRADIENT_GLOW, DistanceFieldFontGlowVertex, DistanceFieldFontGlowFragment, ShaderEffect::DOESNT_MODIFY_GEOMETRY );
+  // Save the binary to the file system:
+  std::string binaryShaderFilename;
+  shaderBinaryFilename( shaderData->GetHashValue(), binaryShaderFilename );
 
-  shaderEffect->SetProgram(GEOMETRY_TYPE_TEXT, SHADER_GRADIENT_SHADOW, DistanceFieldFontShadowVertex, DistanceFieldFontShadowFragment, ShaderEffect::DOESNT_MODIFY_GEOMETRY );
+  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
 
-  shaderEffect->SetProgram(GEOMETRY_TYPE_TEXT, SHADER_GRADIENT_OUTLINE, DistanceFieldFontOutlineVertex, DistanceFieldFontOutlineFragment, ShaderEffect::DOESNT_MODIFY_GEOMETRY );
+  // Save the binary into to memory cache:
+  MemoryCacheInsert( *shaderData );
 
-  shaderEffect->SetProgram(GEOMETRY_TYPE_TEXT, SHADER_GRADIENT_OUTLINE_GLOW, DistanceFieldFontOutlineGlowVertex, DistanceFieldFontOutlineGlowFragment, ShaderEffect::DOESNT_MODIFY_GEOMETRY );
+  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
 }
 
-
-size_t ShaderFactory::HashShaderSource(const std::string& vertexSource, const std::string& fragmentSource) const
+void ShaderFactory::MemoryCacheInsert( ShaderData& shaderData )
 {
-  std::string source = vertexSource + fragmentSource;
-
-  // remove all white spaces, tabs and new lines
-  source.erase(std::remove(source.begin(), source.end(), ' '), source.end());
-  source.erase(std::remove(source.begin(), source.end(), '\n'), source.end());
-  source.erase(std::remove(source.begin(), source.end(), '\t'), source.end());
+  DALI_ASSERT_DEBUG( shaderData.GetBufferSize() > 0 );
 
-  StringHash hasher;
-  return hasher( source );
+  // 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