2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/event/effects/shader-effect-impl.h>
25 #include <dali/public-api/math/vector2.h>
26 #include <dali/public-api/math/matrix.h>
27 #include <dali/public-api/math/matrix3.h>
28 #include <dali/public-api/shader-effects/shader-effect.h>
29 #include <dali/public-api/object/type-registry.h>
30 #include <dali/public-api/scripting/scripting.h>
31 #include "dali-shaders.h"
32 #include <dali/internal/event/effects/shader-declarations.h>
33 #include <dali/internal/event/effects/shader-factory.h>
34 #include <dali/internal/event/images/image-impl.h>
35 #include <dali/internal/update/common/animatable-property.h>
36 #include <dali/internal/update/manager/update-manager.h>
37 #include <dali/internal/event/common/stage-impl.h>
38 #include <dali/internal/event/common/thread-local-storage.h>
39 #include <dali/internal/render/shaders/shader.h>
40 #include <dali/internal/update/common/property-owner-messages.h>
41 #include <dali/internal/update/animation/scene-graph-constraint-base.h>
43 using Dali::Internal::SceneGraph::UpdateManager;
44 using Dali::Internal::SceneGraph::UniformMeta;
45 using Dali::Internal::SceneGraph::Shader;
46 using Dali::Internal::SceneGraph::AnimatableProperty;
47 using Dali::Internal::SceneGraph::PropertyBase;
48 using Dali::Internal::SceneGraph::PropertyBase;
49 using Dali::Internal::SceneGraph::RenderQueue;
55 const Property::Index ShaderEffect::GRID_DENSITY = 0;
56 const Property::Index ShaderEffect::IMAGE = 1;
57 const Property::Index ShaderEffect::PROGRAM = 2;
58 const Property::Index ShaderEffect::GEOMETRY_HINTS = 3;
63 ShaderEffect::DefaultPropertyLookup* ShaderEffect::mDefaultPropertyLookup = NULL;
70 const char* vertexShaderPrefix;
71 const char* fragmentShaderPrefix;
72 const char* vertexShaderPostfix;
73 const char* fragmentShaderPostfix;
76 WrapperStrings customShaderWrappers [] =
79 CustomImagePrefixVertex, CustomImagePrefixFragment,
80 CustomImagePostfixVertex, CustomImagePostfixFragment
83 CustomFontPrefixVertex, CustomFontPrefixFragment,
84 CustomFontPostfixVertex, CustomFontPostfixFragment
87 CustomMeshPrefixVertex, CustomMeshPrefixFragment,
88 MeshVertex, MeshFragment
94 Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New( );
96 ShaderFactory::LoadTextSubtypeShaders(internal);
98 return Dali::ShaderEffect(internal.Get());
101 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
103 const std::string DEFAULT_PROPERTY_NAMES[] =
110 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_NAMES ) / sizeof( std::string );
112 const Property::Type DEFAULT_PROPERTY_TYPES[DEFAULT_PROPERTY_COUNT] =
114 Property::FLOAT, // "grid-density",
115 Property::MAP, // "image",
116 Property::MAP, // "program",
117 Property::INTEGER, // "geometry-hints",
120 std::string GetFileContents( const std::string& filename )
122 std::ifstream input( filename.c_str() );
123 return std::string( std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>() );
126 std::string GetShader(const std::string& field, const Property::Value& property)
128 if( property.HasKey(field) )
130 DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
132 // we could also check here for an array of strings as convenience for json not having multi line strings
133 return property.GetValue(field).Get<std::string>();
137 // convention of shader field appended with -filename signifies a file
138 std::string filenameKey(std::string(field) + std::string("-filename"));
140 if( property.HasKey( filenameKey ) )
142 DALI_ASSERT_ALWAYS(property.GetValue(filenameKey).GetType() == Property::STRING && "Shader filename property is not a string" );
143 // this should eventually be done by an adaptor interface
144 return GetFileContents( property.GetValue(filenameKey).Get<std::string>() );
148 return std::string();
154 ShaderEffectPtr ShaderEffect::New( const string& vertexShader,
155 const string& fragmentShader,
156 GeometryType geometryType,
157 Dali::ShaderEffect::GeometryHints hints )
159 return NewWithPrefix( "", vertexShader, "", fragmentShader, geometryType, hints);
162 ShaderEffectPtr ShaderEffect::NewWithPrefix( const string& vertexShaderPrefix,
163 const string& vertexShader,
164 const string& fragmentShaderPrefix,
165 const string& fragmentShader,
166 GeometryType geometryTypes,
167 Dali::ShaderEffect::GeometryHints hints )
169 ShaderEffectPtr shaderEffect( New(hints) );
170 ShaderFactory::LoadTextSubtypeShaders(shaderEffect);
172 shaderEffect->SetPrograms( geometryTypes, vertexShaderPrefix, vertexShader, fragmentShaderPrefix, fragmentShader );
176 ShaderEffectPtr ShaderEffect::New( const string& imageVertexShader,
177 const string& imageFragmentShader,
178 const string& textVertexShader,
179 const string& textFragmentShader,
180 const string& texturedMeshVertexShader,
181 const string& texturedMeshFragmentShader,
182 const string& meshVertexShader,
183 const string& meshFragmentShader,
184 Dali::ShaderEffect::GeometryHints hints )
186 ShaderEffectPtr shaderEffect( New(hints) );
188 ShaderFactory::LoadTextSubtypeShaders(shaderEffect);
190 shaderEffect->SetWrappedProgram( GEOMETRY_TYPE_IMAGE, SHADER_SUBTYPE_ALL, "", "", imageVertexShader, imageFragmentShader );
191 shaderEffect->SetWrappedProgram( GEOMETRY_TYPE_TEXT, SHADER_DEFAULT, "", "", textVertexShader, textFragmentShader );
192 shaderEffect->SetWrappedProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_SUBTYPE_ALL, "", "", texturedMeshVertexShader, texturedMeshFragmentShader );
193 shaderEffect->SetWrappedProgram( GEOMETRY_TYPE_MESH, SHADER_SUBTYPE_ALL, "", "", meshVertexShader, meshFragmentShader );
198 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
200 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
201 UpdateManager& updateManager = tls.GetUpdateManager();
203 ShaderEffectPtr shaderEffect( new ShaderEffect( updateManager, hints ) );
204 shaderEffect->RegisterObject();
209 ShaderEffect::ShaderEffect( UpdateManager& updateManager, Dali::ShaderEffect::GeometryHints hints )
210 : mUpdateManager( updateManager ),
211 mConnectionCount (0),
212 mGeometryHints( hints )
214 mSceneObject = new Shader( hints );
215 DALI_ASSERT_DEBUG( NULL != mSceneObject );
217 // Transfer shader ownership to a scene message
218 AddShaderMessage( mUpdateManager, *mSceneObject );
221 ShaderEffect::~ShaderEffect()
223 DALI_ASSERT_DEBUG( mSceneObject != NULL );
225 // Guard to allow handle destruction after Core has been destroyed
226 if ( Stage::IsInstalled() )
228 // Remove scene-object using a message to the UpdateManager
229 RemoveShaderMessage( mUpdateManager, *mSceneObject );
235 void ShaderEffect::SetEffectImage( Dali::Image image )
237 // if images are the same, do nothing
243 if (mImage && mConnectionCount > 0)
245 // unset previous image
246 GetImplementation(mImage).Disconnect();
249 // in case image is empty this will reset our image handle
254 // mSceneShader can be in a separate thread; queue a setter message
255 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, 0 );
259 // tell image that we're using it
260 if (mConnectionCount > 0)
262 GetImplementation(mImage).Connect();
264 // mSceneShader can be in a separate thread; queue a setter message
265 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
269 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
271 // Register the property if it does not exist
272 Property::Index index = GetPropertyIndex( name );
273 if ( Property::INVALID_INDEX == index )
275 index = RegisterProperty( name, value );
278 SetProperty( index, value );
280 SetCoordinateTypeMessage( mUpdateManager.GetEventToUpdate(), *mCustomMetadata[index], uniformCoordinateType );
283 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
285 DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
286 mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
289 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
291 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
295 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
297 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
301 void ShaderEffect::SetProgram( GeometryType geometryType, ShaderSubTypes subType,
302 const string& vertexSource, const string& fragmentSource,
303 GeometryState modifiesGeometry )
305 SetProgramImpl(geometryType, subType, vertexSource, fragmentSource, modifiesGeometry);
308 void ShaderEffect::SetProgram( GeometryType geometryType, ShaderSubTypes subType,
309 const std::string& vertexPrefix, const std::string& fragmentPrefix,
310 const std::string& vertexSource, const std::string& fragmentSource,
311 GeometryState modifiesGeometry )
313 const std::string vertex( vertexPrefix + vertexSource );
314 const std::string fragment( fragmentPrefix + fragmentSource );
315 SetProgramImpl( geometryType, subType, vertex, fragment, modifiesGeometry );
318 void ShaderEffect::Connect()
322 if (mImage && mConnectionCount == 1)
324 GetImplementation(mImage).Connect();
326 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
327 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
331 void ShaderEffect::Disconnect()
333 DALI_ASSERT_DEBUG(mConnectionCount > 0);
336 if (mImage && mConnectionCount == 0)
338 GetImplementation(mImage).Disconnect();
342 bool ShaderEffect::IsSceneObjectRemovable() const
344 return false; // The Shader is not removed during this proxy's lifetime
347 unsigned int ShaderEffect::GetDefaultPropertyCount() const
349 return DEFAULT_PROPERTY_COUNT;
352 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
354 indices.reserve( DEFAULT_PROPERTY_COUNT );
356 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
358 indices.push_back( i );
362 const std::string& ShaderEffect::GetDefaultPropertyName(Property::Index index) const
364 if( index < DEFAULT_PROPERTY_COUNT )
366 return DEFAULT_PROPERTY_NAMES[index];
370 // index out of range..return empty string
371 static const std::string INVALID_PROPERTY_NAME;
372 return INVALID_PROPERTY_NAME;
376 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
378 Property::Index index = Property::INVALID_INDEX;
380 // Lazy initialization of static mDefaultPropertyLookup
381 if (!mDefaultPropertyLookup)
383 mDefaultPropertyLookup = new DefaultPropertyLookup();
385 for (int i=0; i<DEFAULT_PROPERTY_COUNT; ++i)
387 (*mDefaultPropertyLookup)[DEFAULT_PROPERTY_NAMES[i]] = i;
390 DALI_ASSERT_DEBUG( NULL != mDefaultPropertyLookup );
392 // Look for name in default properties
393 DefaultPropertyLookup::const_iterator result = mDefaultPropertyLookup->find( name );
394 if ( mDefaultPropertyLookup->end() != result )
396 index = result->second;
402 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
407 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
412 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
417 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
419 if( index < DEFAULT_PROPERTY_COUNT )
421 return DEFAULT_PROPERTY_TYPES[index];
425 // index out of range...return Property::NONE
426 return Property::NONE;
430 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
434 case Dali::ShaderEffect::GRID_DENSITY:
436 SetGridDensityMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, propertyValue.Get<float>() );
440 case Dali::ShaderEffect::IMAGE:
442 Dali::Image img(Scripting::NewImage( propertyValue ));
445 SetEffectImage( img );
449 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
454 case Dali::ShaderEffect::PROGRAM:
456 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
457 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
458 std::string vertex = GetShader("vertex", propertyValue);
459 std::string fragment = GetShader("fragment", propertyValue);
461 GeometryType geometryType = GEOMETRY_TYPE_IMAGE;
463 if( propertyValue.HasKey("geometry-type") )
465 Property::Value geometryValue = propertyValue.GetValue("geometry-type");
466 DALI_ASSERT_ALWAYS(geometryValue.GetType() == Property::STRING && "Geometry type is not a string" );
468 std::string s = geometryValue.Get<std::string>();
469 if(s == "GEOMETRY_TYPE_IMAGE")
471 geometryType = GEOMETRY_TYPE_IMAGE;
473 else if (s == "GEOMETRY_TYPE_TEXT")
475 geometryType = GEOMETRY_TYPE_TEXT;
477 else if( s == "GEOMETRY_TYPE_MESH")
479 geometryType = GEOMETRY_TYPE_MESH;
481 else if( s == "GEOMETRY_TYPE_TEXTURED_MESH")
483 geometryType = GEOMETRY_TYPE_TEXTURED_MESH;
487 DALI_ASSERT_ALWAYS(!"Geometry type unknown" );
490 SetPrograms( geometryType, vertexPrefix, vertex, fragmentPrefix, fragment );
494 case Dali::ShaderEffect::GEOMETRY_HINTS:
496 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
497 Property::Value geometryHintsValue = propertyValue.GetValue("geometry-hints");
499 std::string s = geometryHintsValue.Get<std::string>();
502 hint = Dali::ShaderEffect::HINT_NONE;
504 else if(s == "HINT_GRID_X")
506 hint = Dali::ShaderEffect::HINT_GRID_X;
508 else if(s == "HINT_GRID_Y")
510 hint = Dali::ShaderEffect::HINT_GRID_Y;
512 else if(s == "HINT_GRID")
514 hint = Dali::ShaderEffect::HINT_GRID;
516 else if(s == "HINT_DEPTH_BUFFER")
518 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
520 else if(s == "HINT_BLENDING")
522 hint = Dali::ShaderEffect::HINT_BLENDING;
524 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
526 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
530 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
533 SetHintsMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, hint );
540 DALI_ASSERT_ALWAYS(false && "ShaderEffect property enumeration out of range"); // should not come here
546 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
548 // none of our properties are readable so return empty
549 return Property::Value();
552 void ShaderEffect::InstallSceneObjectProperty( PropertyBase& newProperty, const std::string& name, unsigned int index )
554 // Warning - the property is added to the Shader object in the Update thread and the meta-data is added in the Render thread (through a secondary message)
556 // mSceneObject is being used in a separate thread; queue a message to add the property
557 InstallCustomPropertyMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, newProperty ); // Message takes ownership
559 // mSceneObject requires metadata for each custom property (uniform)
560 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
561 // mSceneObject is being used in a separate thread; queue a message to add the property
562 InstallUniformMetaMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, *meta ); // Message takes ownership
564 // Add entry to the metadata lookup
565 mCustomMetadata[index] = meta;
568 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
573 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
575 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
577 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
579 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
581 return dynamic_cast<const PropertyBase*>( entry->second.GetSceneGraphProperty() );
584 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
586 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
588 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
590 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
592 return entry->second.GetSceneGraphProperty();
595 void ShaderEffect::SetPrograms( GeometryType geometryTypes,
596 const std::string& vertexShaderPrefix,
597 const std::string& vertexShader,
598 const std::string& fragmentShaderPrefix,
599 const std::string& fragmentShader )
601 static std::string emptyStr;
603 if( geometryTypes & GEOMETRY_TYPE_IMAGE )
605 SetWrappedProgram( GEOMETRY_TYPE_IMAGE, SHADER_SUBTYPE_ALL, vertexShaderPrefix, fragmentShaderPrefix, vertexShader, fragmentShader );
609 SetWrappedProgram( GEOMETRY_TYPE_IMAGE, SHADER_SUBTYPE_ALL, emptyStr, emptyStr, emptyStr, emptyStr );
612 if( geometryTypes & GEOMETRY_TYPE_TEXT )
614 // Only change the default program, leaving the other sub-types as-is.
615 SetWrappedProgram( GEOMETRY_TYPE_TEXT, SHADER_DEFAULT, vertexShaderPrefix, fragmentShaderPrefix, vertexShader, fragmentShader );
619 SetWrappedProgram( GEOMETRY_TYPE_TEXT, SHADER_DEFAULT, emptyStr, emptyStr, emptyStr, emptyStr );
622 if( geometryTypes & GEOMETRY_TYPE_TEXTURED_MESH )
624 SetWrappedProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_SUBTYPE_ALL, vertexShaderPrefix, fragmentShaderPrefix, vertexShader, fragmentShader );
628 SetWrappedProgram( GEOMETRY_TYPE_TEXTURED_MESH, SHADER_SUBTYPE_ALL, emptyStr, emptyStr, emptyStr, emptyStr );
631 if( geometryTypes & GEOMETRY_TYPE_MESH )
633 SetWrappedProgram( GEOMETRY_TYPE_MESH, SHADER_SUBTYPE_ALL, vertexShaderPrefix, fragmentShaderPrefix, vertexShader, fragmentShader );
637 SetWrappedProgram( GEOMETRY_TYPE_MESH, SHADER_SUBTYPE_ALL, emptyStr, emptyStr, emptyStr, emptyStr );
641 void ShaderEffect::SetWrappedProgram( GeometryType geometryType, ShaderSubTypes subType,
642 const string& vertexPrefix, const string& fragmentPrefix,
643 const string& vertexSnippet, const string& fragmentSnippet )
645 // create complete shader program strings for the given geometry type
646 unsigned int index = 0;
647 switch( geometryType )
649 case GEOMETRY_TYPE_IMAGE:
654 case GEOMETRY_TYPE_TEXT:
659 case GEOMETRY_TYPE_MESH:
660 case GEOMETRY_TYPE_TEXTURED_MESH:
665 case GEOMETRY_TYPE_LAST:
667 DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
672 string vertexSource = vertexPrefix + customShaderWrappers[index].vertexShaderPrefix;
673 string fragmentSource = fragmentPrefix + customShaderWrappers[index].fragmentShaderPrefix;
675 // Append the custom vertex shader code if supplied, otherwise append the default
676 if ( vertexSnippet.length() > 0 )
678 vertexSource.append( vertexSnippet );
682 vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
685 // Append the custom fragment shader code if supplied, otherwise append the default
686 if ( fragmentSnippet.length() > 0 )
688 fragmentSource.append( fragmentSnippet );
692 fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
696 SetProgramImpl( geometryType, subType, vertexSource, fragmentSource );
699 void ShaderEffect::SetProgramImpl( GeometryType geometryType, ShaderSubTypes subType,
700 const string& vertexSource, const string& fragmentSource )
702 GeometryState modifiesGeometry = MODIFIES_GEOMETRY;
704 if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
706 modifiesGeometry = DOESNT_MODIFY_GEOMETRY;
709 SetProgramImpl( geometryType, subType, vertexSource, fragmentSource, modifiesGeometry );
712 void ShaderEffect::SetProgramImpl( GeometryType geometryType, ShaderSubTypes subType,
713 const string& vertexSource, const string& fragmentSource,
714 GeometryState modifiesGeometry )
716 // Load done asynchronously in update thread. SetProgram message below must be processed afterwards.
717 // Therefore, resource manager cannot farm out the loading to the adaptor resource threads,
718 // but must instead use synchronous loading via PlatformAbstraction::LoadFile()
720 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
721 ShaderFactory& shaderFactory = tls.GetShaderFactory();
724 ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
726 DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
728 // Add shader program to scene-object using a message to the UpdateManager
729 SetShaderProgramMessage( mUpdateManager, *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry==MODIFIES_GEOMETRY );
731 mTickets.push_back(ticket); // add ticket to collection to keep it alive.
735 } // namespace Internal