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>
22 #include <dali/public-api/math/vector2.h>
23 #include <dali/public-api/math/matrix.h>
24 #include <dali/public-api/math/matrix3.h>
25 #include <dali/public-api/shader-effects/shader-effect.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/scripting/scripting.h>
28 #include <dali/internal/event/effects/shader-declarations.h>
29 #include <dali/internal/event/effects/shader-factory.h>
30 #include <dali/internal/event/images/image-impl.h>
31 #include <dali/internal/update/common/animatable-property.h>
32 #include <dali/internal/update/manager/update-manager.h>
33 #include <dali/internal/event/common/stage-impl.h>
34 #include <dali/internal/event/common/thread-local-storage.h>
35 #include <dali/internal/render/shaders/shader.h>
36 #include <dali/internal/render/shaders/uniform-meta.h>
37 #include <dali/internal/update/common/property-owner-messages.h>
38 #include <dali/internal/update/animation/scene-graph-constraint-base.h>
39 #include "dali-shaders.h"
41 using Dali::Internal::SceneGraph::UpdateManager;
42 using Dali::Internal::SceneGraph::UniformMeta;
43 using Dali::Internal::SceneGraph::Shader;
44 using Dali::Internal::SceneGraph::AnimatableProperty;
45 using Dali::Internal::SceneGraph::PropertyBase;
46 using Dali::Internal::SceneGraph::PropertyBase;
47 using Dali::Internal::SceneGraph::RenderQueue;
53 const Property::Index ShaderEffect::GRID_DENSITY = 0;
54 const Property::Index ShaderEffect::IMAGE = 1;
55 const Property::Index ShaderEffect::PROGRAM = 2;
56 const Property::Index ShaderEffect::GEOMETRY_HINTS = 3;
61 ShaderEffect::DefaultPropertyLookup* ShaderEffect::mDefaultPropertyLookup = NULL;
68 const char* vertexShaderPrefix;
69 const char* fragmentShaderPrefix;
70 const char* vertexShaderPostfix;
71 const char* fragmentShaderPostfix;
74 WrapperStrings customShaderWrappers [] =
77 CustomImagePrefixVertex, CustomImagePrefixFragment,
78 CustomImagePostfixVertex, CustomImagePostfixFragment
81 CustomTextDistanceFieldPrefixVertex, CustomTextDistanceFieldPrefixFragment,
82 CustomTextDistanceFieldPostfixVertex, CustomTextDistanceFieldPostfixFragment
85 CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
86 CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
89 CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
90 CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
95 * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
96 * @param[in] effect of the shader
97 * @param[in] actualGeometryType of the shader
98 * @param[in] expectedGeometryType of the shader
99 * @param[in] vertexPrefix from application
100 * @param[in] fragmentPrefix from application
101 * @param[in] vertexBody from application
102 * @param[in] fragmentBody from application
103 * @param[in] modifiesGeometry based on flags and vertex shader
105 void WrapAndSetProgram( ShaderEffect& effect,
106 GeometryType actualGeometryType, GeometryType expectedGeometryType,
107 const std::string& vertexPrefix, const std::string& fragmentPrefix,
108 const std::string& vertexBody, const std::string& fragmentBody,
109 bool modifiesGeometry )
111 // if geometry type matches and there is some real data in the strings
112 if( ( actualGeometryType & expectedGeometryType )&&
113 ( ( vertexPrefix.length() > 0 )||
114 ( fragmentPrefix.length() > 0 )||
115 ( vertexBody.length() > 0 )||
116 ( fragmentBody.length() > 0 ) ) )
118 std::string vertexSource = vertexPrefix;
119 std::string fragmentSource = fragmentPrefix;
121 // create complete shader program strings for the given geometry type
122 unsigned int index = 0;
123 switch( expectedGeometryType )
125 case GEOMETRY_TYPE_IMAGE:
130 case GEOMETRY_TYPE_TEXT:
135 case GEOMETRY_TYPE_UNTEXTURED_MESH:
140 case GEOMETRY_TYPE_TEXTURED_MESH:
145 case GEOMETRY_TYPE_LAST:
147 DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
152 vertexSource += customShaderWrappers[index].vertexShaderPrefix;
153 // Append the custom vertex shader code if supplied, otherwise append the default
154 if ( vertexBody.length() > 0 )
156 vertexSource.append( vertexBody );
160 vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
163 fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
164 // Append the custom fragment shader code if supplied, otherwise append the default
165 if ( fragmentBody.length() > 0 )
167 fragmentSource.append( fragmentBody );
171 fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
174 effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
180 Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
182 return Dali::ShaderEffect(internal.Get());
185 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
187 const std::string DEFAULT_PROPERTY_NAMES[] =
194 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_NAMES ) / sizeof( std::string );
196 const Property::Type DEFAULT_PROPERTY_TYPES[DEFAULT_PROPERTY_COUNT] =
198 Property::FLOAT, // "grid-density",
199 Property::MAP, // "image",
200 Property::MAP, // "program",
201 Property::INTEGER, // "geometry-hints",
204 std::string GetShader(const std::string& field, const Property::Value& property)
207 if( property.HasKey(field) )
209 DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
211 // we could also check here for an array of strings as convenience for json not having multi line strings
212 value = property.GetValue(field).Get<std::string>();
218 } // unnamed namespace
220 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
222 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
223 UpdateManager& updateManager = tls.GetUpdateManager();
225 ShaderEffectPtr shaderEffect( new ShaderEffect( updateManager, hints ) );
226 shaderEffect->RegisterObject();
231 ShaderEffect::ShaderEffect( UpdateManager& updateManager, Dali::ShaderEffect::GeometryHints hints )
232 : mUpdateManager( updateManager ),
233 mConnectionCount (0),
234 mGeometryHints( hints )
236 mSceneObject = new Shader( hints );
237 DALI_ASSERT_DEBUG( NULL != mSceneObject );
239 // Transfer shader ownership to a scene message
240 AddShaderMessage( mUpdateManager, *mSceneObject );
243 ShaderEffect::~ShaderEffect()
245 // Guard to allow handle destruction after Core has been destroyed
246 if ( Stage::IsInstalled() )
248 // Remove scene-object using a message to the UpdateManager
251 RemoveShaderMessage( mUpdateManager, *mSceneObject );
257 void ShaderEffect::SetEffectImage( Dali::Image image )
259 // if images are the same, do nothing
265 if (mImage && mConnectionCount > 0)
267 // unset previous image
268 GetImplementation(mImage).Disconnect();
271 // in case image is empty this will reset our image handle
276 // mSceneShader can be in a separate thread; queue a setter message
277 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, 0 );
281 // tell image that we're using it
282 if (mConnectionCount > 0)
284 GetImplementation(mImage).Connect();
286 // mSceneShader can be in a separate thread; queue a setter message
287 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
291 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
293 // Register the property if it does not exist
294 Property::Index index = GetPropertyIndex( name );
295 if ( Property::INVALID_INDEX == index )
297 index = RegisterProperty( name, value );
300 SetProperty( index, value );
302 // RegisterProperty guarantees a positive value as index
303 DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
304 unsigned int metaIndex = index - CustomPropertyStartIndex();
305 // check if there's space in cache
306 if( mCoordinateTypes.Count() < (metaIndex + 1) )
308 mCoordinateTypes.Resize( metaIndex + 1 );
310 // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
311 if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
313 mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
314 SetCoordinateTypeMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, metaIndex, uniformCoordinateType );
318 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
320 DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
321 mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
324 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
326 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
330 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
332 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
336 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
338 SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
341 void ShaderEffect::SetPrograms( GeometryType geometryType,
342 const std::string& vertexPrefix, const std::string& fragmentPrefix,
343 const std::string& vertexSource, const std::string& fragmentSource )
345 bool modifiesGeometry = true;
346 // check if the vertex shader is empty (means it cannot modify geometry)
347 if( (vertexPrefix.length() == 0 )&&( vertexSource.length() == 0 ) )
349 modifiesGeometry = false;
351 // check the hint second
352 if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
354 modifiesGeometry = false;
357 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_IMAGE, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
358 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXT, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
359 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
360 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_UNTEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
363 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
364 const string& vertexSource, const string& fragmentSource,
365 bool modifiesGeometry )
367 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
368 ShaderFactory& shaderFactory = tls.GetShaderFactory();
371 ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
373 DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
375 // Add shader program to scene-object using a message to the UpdateManager
376 SetShaderProgramMessage( mUpdateManager, *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
378 mTickets.push_back(ticket); // add ticket to collection to keep it alive.
381 void ShaderEffect::Connect()
385 if (mImage && mConnectionCount == 1)
387 GetImplementation(mImage).Connect();
389 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
390 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
394 void ShaderEffect::Disconnect()
396 DALI_ASSERT_DEBUG(mConnectionCount > 0);
399 if (mImage && mConnectionCount == 0)
401 GetImplementation(mImage).Disconnect();
405 bool ShaderEffect::IsSceneObjectRemovable() const
407 return false; // The Shader is not removed during this proxy's lifetime
410 unsigned int ShaderEffect::GetDefaultPropertyCount() const
412 return DEFAULT_PROPERTY_COUNT;
415 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
417 indices.reserve( DEFAULT_PROPERTY_COUNT );
419 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
421 indices.push_back( i );
425 const std::string& ShaderEffect::GetDefaultPropertyName(Property::Index index) const
427 if( index < DEFAULT_PROPERTY_COUNT )
429 return DEFAULT_PROPERTY_NAMES[index];
433 // index out of range..return empty string
434 static const std::string INVALID_PROPERTY_NAME;
435 return INVALID_PROPERTY_NAME;
439 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
441 Property::Index index = Property::INVALID_INDEX;
443 // Lazy initialization of static mDefaultPropertyLookup
444 if (!mDefaultPropertyLookup)
446 mDefaultPropertyLookup = new DefaultPropertyLookup();
448 for (int i=0; i<DEFAULT_PROPERTY_COUNT; ++i)
450 (*mDefaultPropertyLookup)[DEFAULT_PROPERTY_NAMES[i]] = i;
453 DALI_ASSERT_DEBUG( NULL != mDefaultPropertyLookup );
455 // Look for name in default properties
456 DefaultPropertyLookup::const_iterator result = mDefaultPropertyLookup->find( name );
457 if ( mDefaultPropertyLookup->end() != result )
459 index = result->second;
465 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
470 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
475 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
480 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
482 if( index < DEFAULT_PROPERTY_COUNT )
484 return DEFAULT_PROPERTY_TYPES[index];
488 // index out of range...return Property::NONE
489 return Property::NONE;
493 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
497 case Dali::ShaderEffect::GRID_DENSITY:
499 SetGridDensityMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, propertyValue.Get<float>() );
503 case Dali::ShaderEffect::IMAGE:
505 Dali::Image img(Scripting::NewImage( propertyValue ));
508 SetEffectImage( img );
512 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
517 case Dali::ShaderEffect::PROGRAM:
519 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
520 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
521 std::string vertex = GetShader("vertex", propertyValue);
522 std::string fragment = GetShader("fragment", propertyValue);
524 GeometryType geometryType = GEOMETRY_TYPE_IMAGE;
526 if( propertyValue.HasKey("geometry-type") )
528 Property::Value geometryValue = propertyValue.GetValue("geometry-type");
529 DALI_ASSERT_ALWAYS(geometryValue.GetType() == Property::STRING && "Geometry type is not a string" );
531 std::string s = geometryValue.Get<std::string>();
532 if(s == "GEOMETRY_TYPE_IMAGE")
534 geometryType = GEOMETRY_TYPE_IMAGE;
536 else if (s == "GEOMETRY_TYPE_TEXT")
538 geometryType = GEOMETRY_TYPE_TEXT;
540 else if( s == "GEOMETRY_TYPE_UNTEXTURED_MESH")
542 geometryType = GEOMETRY_TYPE_UNTEXTURED_MESH;
544 else if( s == "GEOMETRY_TYPE_TEXTURED_MESH")
546 geometryType = GEOMETRY_TYPE_TEXTURED_MESH;
550 DALI_ASSERT_ALWAYS(!"Geometry type unknown" );
553 SetPrograms( geometryType, vertexPrefix, fragmentPrefix, vertex, fragment );
557 case Dali::ShaderEffect::GEOMETRY_HINTS:
559 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
560 Property::Value geometryHintsValue = propertyValue.GetValue("geometry-hints");
562 std::string s = geometryHintsValue.Get<std::string>();
565 hint = Dali::ShaderEffect::HINT_NONE;
567 else if(s == "HINT_GRID_X")
569 hint = Dali::ShaderEffect::HINT_GRID_X;
571 else if(s == "HINT_GRID_Y")
573 hint = Dali::ShaderEffect::HINT_GRID_Y;
575 else if(s == "HINT_GRID")
577 hint = Dali::ShaderEffect::HINT_GRID;
579 else if(s == "HINT_DEPTH_BUFFER")
581 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
583 else if(s == "HINT_BLENDING")
585 hint = Dali::ShaderEffect::HINT_BLENDING;
587 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
589 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
593 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
596 SetHintsMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, hint );
603 DALI_ASSERT_ALWAYS(false && "ShaderEffect property enumeration out of range"); // should not come here
609 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
611 // none of our properties are readable so return empty
612 return Property::Value();
615 void ShaderEffect::InstallSceneObjectProperty( PropertyBase& newProperty, const std::string& name, unsigned int index )
617 // 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)
619 // mSceneObject is being used in a separate thread; queue a message to add the property
620 InstallCustomPropertyMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, newProperty ); // Message takes ownership
622 // mSceneObject requires metadata for each custom property (uniform)
623 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
624 // mSceneObject is being used in a separate thread; queue a message to add the property
625 InstallUniformMetaMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, *meta ); // Message takes ownership
628 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
633 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
635 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
637 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
639 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
641 return dynamic_cast<const PropertyBase*>( entry->second.GetSceneGraphProperty() );
644 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
646 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
648 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
650 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
652 return entry->second.GetSceneGraphProperty();
655 } // namespace Internal