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;
63 const PropertyDetails DEFAULT_PROPERTY_DETAILS[] =
65 // Name Type writable animatable constraint-input
66 { "grid-density", Property::FLOAT, true, false, false }, // GRID_DENSITY
67 { "image", Property::MAP, true, false, false }, // IMAGE
68 { "program", Property::MAP, true, false, false }, // PROGRAM
69 { "geometry-hints", Property::INTEGER, true, false, false }, // GEOMETRY_HINTS
72 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_DETAILS ) / sizeof( DEFAULT_PROPERTY_DETAILS[0] );
76 Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
78 return Dali::ShaderEffect(internal.Get());
81 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
85 const char* vertexShaderPrefix;
86 const char* fragmentShaderPrefix;
87 const char* vertexShaderPostfix;
88 const char* fragmentShaderPostfix;
91 WrapperStrings customShaderWrappers [] =
94 CustomImagePrefixVertex, CustomImagePrefixFragment,
95 CustomImagePostfixVertex, CustomImagePostfixFragment
98 CustomTextDistanceFieldPrefixVertex, CustomTextDistanceFieldPrefixFragment,
99 CustomTextDistanceFieldPostfixVertex, CustomTextDistanceFieldPostfixFragment
102 CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
103 CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
106 CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
107 CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
112 * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
113 * @param[in] effect of the shader
114 * @param[in] actualGeometryType of the shader
115 * @param[in] expectedGeometryType of the shader
116 * @param[in] vertexPrefix from application
117 * @param[in] fragmentPrefix from application
118 * @param[in] vertexBody from application
119 * @param[in] fragmentBody from application
120 * @param[in] modifiesGeometry based on flags and vertex shader
122 void WrapAndSetProgram( Internal::ShaderEffect& effect,
123 GeometryType actualGeometryType, GeometryType expectedGeometryType,
124 const std::string& vertexPrefix, const std::string& fragmentPrefix,
125 const std::string& vertexBody, const std::string& fragmentBody,
126 bool modifiesGeometry )
128 // if geometry type matches and there is some real data in the strings
129 if( ( actualGeometryType & expectedGeometryType )&&
130 ( ( vertexPrefix.length() > 0 )||
131 ( fragmentPrefix.length() > 0 )||
132 ( vertexBody.length() > 0 )||
133 ( fragmentBody.length() > 0 ) ) )
135 std::string vertexSource = vertexPrefix;
136 std::string fragmentSource = fragmentPrefix;
138 // create complete shader program strings for the given geometry type
139 unsigned int index = 0;
140 switch( expectedGeometryType )
142 case GEOMETRY_TYPE_IMAGE:
147 case GEOMETRY_TYPE_TEXT:
152 case GEOMETRY_TYPE_UNTEXTURED_MESH:
157 case GEOMETRY_TYPE_TEXTURED_MESH:
162 case GEOMETRY_TYPE_LAST:
164 DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
169 vertexSource += customShaderWrappers[index].vertexShaderPrefix;
170 // Append the custom vertex shader code if supplied, otherwise append the default
171 if ( vertexBody.length() > 0 )
173 vertexSource.append( vertexBody );
177 vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
180 fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
181 // Append the custom fragment shader code if supplied, otherwise append the default
182 if ( fragmentBody.length() > 0 )
184 fragmentSource.append( fragmentBody );
188 fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
191 effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
195 std::string GetShader(const std::string& field, const Property::Value& property)
198 if( property.HasKey(field) )
200 DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
202 // we could also check here for an array of strings as convenience for json not having multi line strings
203 value = property.GetValue(field).Get<std::string>();
209 } // unnamed namespace
211 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
213 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
214 UpdateManager& updateManager = tls.GetUpdateManager();
216 ShaderEffectPtr shaderEffect( new ShaderEffect( updateManager, hints ) );
217 shaderEffect->RegisterObject();
222 ShaderEffect::ShaderEffect( UpdateManager& updateManager, Dali::ShaderEffect::GeometryHints hints )
223 : mUpdateManager( updateManager ),
224 mConnectionCount (0),
225 mGeometryHints( hints )
227 mSceneObject = new Shader( hints );
228 DALI_ASSERT_DEBUG( NULL != mSceneObject );
230 // Transfer shader ownership to a scene message
231 AddShaderMessage( mUpdateManager, *mSceneObject );
234 ShaderEffect::~ShaderEffect()
236 // Guard to allow handle destruction after Core has been destroyed
237 if ( Stage::IsInstalled() )
239 // Remove scene-object using a message to the UpdateManager
242 RemoveShaderMessage( mUpdateManager, *mSceneObject );
248 void ShaderEffect::SetEffectImage( Dali::Image image )
250 // if images are the same, do nothing
256 if (mImage && mConnectionCount > 0)
258 // unset previous image
259 GetImplementation(mImage).Disconnect();
262 // in case image is empty this will reset our image handle
267 // mSceneShader can be in a separate thread; queue a setter message
268 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, 0 );
272 // tell image that we're using it
273 if (mConnectionCount > 0)
275 GetImplementation(mImage).Connect();
277 // mSceneShader can be in a separate thread; queue a setter message
278 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
282 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
284 // Register the property if it does not exist
285 Property::Index index = GetPropertyIndex( name );
286 if ( Property::INVALID_INDEX == index )
288 index = RegisterProperty( name, value );
291 SetProperty( index, value );
293 // RegisterProperty guarantees a positive value as index
294 DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
295 unsigned int metaIndex = index - CustomPropertyStartIndex();
296 // check if there's space in cache
297 if( mCoordinateTypes.Count() < (metaIndex + 1) )
299 mCoordinateTypes.Resize( metaIndex + 1 );
301 // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
302 if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
304 mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
305 SetCoordinateTypeMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, metaIndex, uniformCoordinateType );
309 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
311 DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
312 mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
315 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
317 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
321 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
323 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
327 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
329 SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
332 void ShaderEffect::SetPrograms( GeometryType geometryType,
333 const std::string& vertexPrefix, const std::string& fragmentPrefix,
334 const std::string& vertexSource, const std::string& fragmentSource )
336 bool modifiesGeometry = true;
337 // check if the vertex shader is empty (means it cannot modify geometry)
338 if( (vertexPrefix.length() == 0 )&&( vertexSource.length() == 0 ) )
340 modifiesGeometry = false;
342 // check the hint second
343 if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
345 modifiesGeometry = false;
348 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_IMAGE, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
349 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXT, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
350 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
351 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_UNTEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
354 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
355 const string& vertexSource, const string& fragmentSource,
356 bool modifiesGeometry )
358 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
359 ShaderFactory& shaderFactory = tls.GetShaderFactory();
362 ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
364 DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
366 // Add shader program to scene-object using a message to the UpdateManager
367 SetShaderProgramMessage( mUpdateManager, *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
369 mTickets.push_back(ticket); // add ticket to collection to keep it alive.
372 void ShaderEffect::Connect()
376 if (mImage && mConnectionCount == 1)
378 GetImplementation(mImage).Connect();
380 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
381 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
385 void ShaderEffect::Disconnect()
387 DALI_ASSERT_DEBUG(mConnectionCount > 0);
390 if (mImage && mConnectionCount == 0)
392 GetImplementation(mImage).Disconnect();
396 bool ShaderEffect::IsSceneObjectRemovable() const
398 return false; // The Shader is not removed during this proxy's lifetime
401 unsigned int ShaderEffect::GetDefaultPropertyCount() const
403 return DEFAULT_PROPERTY_COUNT;
406 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
408 indices.reserve( DEFAULT_PROPERTY_COUNT );
410 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
412 indices.push_back( i );
416 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
418 if( index < DEFAULT_PROPERTY_COUNT )
420 return DEFAULT_PROPERTY_DETAILS[index].name;
428 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
430 Property::Index index = Property::INVALID_INDEX;
432 // Look for name in default properties
433 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
435 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
436 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
447 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
449 return true; // all properties are writable
452 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
454 return false; // all properties are non animatable
457 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
459 return false; // all properties cannot be used as constraint input
462 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
464 if( index < DEFAULT_PROPERTY_COUNT )
466 return DEFAULT_PROPERTY_DETAILS[index].type;
470 // index out of range...return Property::NONE
471 return Property::NONE;
475 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
479 case Dali::ShaderEffect::GRID_DENSITY:
481 SetGridDensityMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, propertyValue.Get<float>() );
485 case Dali::ShaderEffect::IMAGE:
487 Dali::Image img(Scripting::NewImage( propertyValue ));
490 SetEffectImage( img );
494 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
499 case Dali::ShaderEffect::PROGRAM:
501 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
502 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
503 std::string vertex = GetShader("vertex", propertyValue);
504 std::string fragment = GetShader("fragment", propertyValue);
506 GeometryType geometryType = GEOMETRY_TYPE_IMAGE;
508 if( propertyValue.HasKey("geometry-type") )
510 Property::Value geometryValue = propertyValue.GetValue("geometry-type");
511 DALI_ASSERT_ALWAYS(geometryValue.GetType() == Property::STRING && "Geometry type is not a string" );
513 std::string s = geometryValue.Get<std::string>();
514 if(s == "GEOMETRY_TYPE_IMAGE")
516 geometryType = GEOMETRY_TYPE_IMAGE;
518 else if (s == "GEOMETRY_TYPE_TEXT")
520 geometryType = GEOMETRY_TYPE_TEXT;
522 else if( s == "GEOMETRY_TYPE_UNTEXTURED_MESH")
524 geometryType = GEOMETRY_TYPE_UNTEXTURED_MESH;
526 else if( s == "GEOMETRY_TYPE_TEXTURED_MESH")
528 geometryType = GEOMETRY_TYPE_TEXTURED_MESH;
532 DALI_ASSERT_ALWAYS(!"Geometry type unknown" );
535 SetPrograms( geometryType, vertexPrefix, fragmentPrefix, vertex, fragment );
539 case Dali::ShaderEffect::GEOMETRY_HINTS:
541 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
542 Property::Value geometryHintsValue = propertyValue.GetValue("geometry-hints");
544 std::string s = geometryHintsValue.Get<std::string>();
547 hint = Dali::ShaderEffect::HINT_NONE;
549 else if(s == "HINT_GRID_X")
551 hint = Dali::ShaderEffect::HINT_GRID_X;
553 else if(s == "HINT_GRID_Y")
555 hint = Dali::ShaderEffect::HINT_GRID_Y;
557 else if(s == "HINT_GRID")
559 hint = Dali::ShaderEffect::HINT_GRID;
561 else if(s == "HINT_DEPTH_BUFFER")
563 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
565 else if(s == "HINT_BLENDING")
567 hint = Dali::ShaderEffect::HINT_BLENDING;
569 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
571 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
575 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
578 SetHintsMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, hint );
585 DALI_ASSERT_ALWAYS(false && "ShaderEffect property enumeration out of range"); // should not come here
591 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
593 // none of our properties are readable so return empty
594 return Property::Value();
597 void ShaderEffect::InstallSceneObjectProperty( PropertyBase& newProperty, const std::string& name, unsigned int index )
599 // 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)
601 // mSceneObject is being used in a separate thread; queue a message to add the property
602 InstallCustomPropertyMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, newProperty ); // Message takes ownership
604 // mSceneObject requires metadata for each custom property (uniform)
605 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
606 // mSceneObject is being used in a separate thread; queue a message to add the property
607 InstallUniformMetaMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, *meta ); // Message takes ownership
610 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
615 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
617 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
619 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
621 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
623 return dynamic_cast<const PropertyBase*>( entry->second.GetSceneGraphProperty() );
626 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
628 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
630 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
632 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
634 return entry->second.GetSceneGraphProperty();
637 } // namespace Internal