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>
24 #include <dali/public-api/math/vector2.h>
25 #include <dali/public-api/math/matrix.h>
26 #include <dali/public-api/math/matrix3.h>
27 #include <dali/public-api/shader-effects/shader-effect.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/public-api/scripting/scripting.h>
30 #include "dali-shaders.h"
31 #include <dali/internal/event/effects/shader-declarations.h>
32 #include <dali/internal/event/effects/shader-factory.h>
33 #include <dali/internal/event/images/image-impl.h>
34 #include <dali/internal/update/common/animatable-property.h>
35 #include <dali/internal/update/manager/update-manager.h>
36 #include <dali/internal/event/common/stage-impl.h>
37 #include <dali/internal/event/common/thread-local-storage.h>
38 #include <dali/internal/render/shaders/shader.h>
39 #include <dali/internal/update/common/property-owner-messages.h>
40 #include <dali/internal/update/animation/scene-graph-constraint-base.h>
42 using Dali::Internal::SceneGraph::UpdateManager;
43 using Dali::Internal::SceneGraph::UniformMeta;
44 using Dali::Internal::SceneGraph::Shader;
45 using Dali::Internal::SceneGraph::AnimatableProperty;
46 using Dali::Internal::SceneGraph::PropertyBase;
47 using Dali::Internal::SceneGraph::PropertyBase;
48 using Dali::Internal::SceneGraph::RenderQueue;
54 const Property::Index ShaderEffect::GRID_DENSITY = 0;
55 const Property::Index ShaderEffect::IMAGE = 1;
56 const Property::Index ShaderEffect::PROGRAM = 2;
57 const Property::Index ShaderEffect::GEOMETRY_HINTS = 3;
62 ShaderEffect::DefaultPropertyLookup* ShaderEffect::mDefaultPropertyLookup = NULL;
69 const char* vertexShaderPrefix;
70 const char* fragmentShaderPrefix;
71 const char* vertexShaderPostfix;
72 const char* fragmentShaderPostfix;
75 WrapperStrings customShaderWrappers [] =
78 CustomImagePrefixVertex, CustomImagePrefixFragment,
79 CustomImagePostfixVertex, CustomImagePostfixFragment
82 CustomTextDistanceFieldPrefixVertex, CustomTextDistanceFieldPrefixFragment,
83 CustomTextDistanceFieldPostfixVertex, CustomTextDistanceFieldPostfixFragment
86 CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
87 CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
90 CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
91 CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
96 * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
97 * @param[in] effect of the shader
98 * @param[in] actualGeometryType of the shader
99 * @param[in] expectedGeometryType of the shader
100 * @param[in] vertexPrefix from application
101 * @param[in] fragmentPrefix from application
102 * @param[in] vertexBody from application
103 * @param[in] fragmentBody from application
104 * @param[in] modifiesGeometry based on flags and vertex shader
106 void WrapAndSetProgram( ShaderEffect& effect,
107 GeometryType actualGeometryType, GeometryType expectedGeometryType,
108 const std::string& vertexPrefix, const std::string& fragmentPrefix,
109 const std::string& vertexBody, const std::string& fragmentBody,
110 bool modifiesGeometry )
112 // if geometry type matches and there is some real data in the strings
113 if( ( actualGeometryType & expectedGeometryType )&&
114 ( ( vertexPrefix.length() > 0 )||
115 ( fragmentPrefix.length() > 0 )||
116 ( vertexBody.length() > 0 )||
117 ( fragmentBody.length() > 0 ) ) )
119 std::string vertexSource = vertexPrefix;
120 std::string fragmentSource = fragmentPrefix;
122 // create complete shader program strings for the given geometry type
123 unsigned int index = 0;
124 switch( expectedGeometryType )
126 case GEOMETRY_TYPE_IMAGE:
131 case GEOMETRY_TYPE_TEXT:
136 case GEOMETRY_TYPE_UNTEXTURED_MESH:
141 case GEOMETRY_TYPE_TEXTURED_MESH:
146 case GEOMETRY_TYPE_LAST:
148 DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
153 vertexSource += customShaderWrappers[index].vertexShaderPrefix;
154 // Append the custom vertex shader code if supplied, otherwise append the default
155 if ( vertexBody.length() > 0 )
157 vertexSource.append( vertexBody );
161 vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
164 fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
165 // Append the custom fragment shader code if supplied, otherwise append the default
166 if ( fragmentBody.length() > 0 )
168 fragmentSource.append( fragmentBody );
172 fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
175 effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
181 Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
183 return Dali::ShaderEffect(internal.Get());
186 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
188 const std::string DEFAULT_PROPERTY_NAMES[] =
195 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_NAMES ) / sizeof( std::string );
197 const Property::Type DEFAULT_PROPERTY_TYPES[DEFAULT_PROPERTY_COUNT] =
199 Property::FLOAT, // "grid-density",
200 Property::MAP, // "image",
201 Property::MAP, // "program",
202 Property::INTEGER, // "geometry-hints",
205 std::string GetShader(const std::string& field, const Property::Value& property)
208 if( property.HasKey(field) )
210 DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
212 // we could also check here for an array of strings as convenience for json not having multi line strings
213 value = property.GetValue(field).Get<std::string>();
219 } // unnamed namespace
221 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
223 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
224 UpdateManager& updateManager = tls.GetUpdateManager();
226 ShaderEffectPtr shaderEffect( new ShaderEffect( updateManager, hints ) );
227 shaderEffect->RegisterObject();
232 ShaderEffect::ShaderEffect( UpdateManager& updateManager, Dali::ShaderEffect::GeometryHints hints )
233 : mUpdateManager( updateManager ),
234 mConnectionCount (0),
235 mGeometryHints( hints )
237 mSceneObject = new Shader( hints );
238 DALI_ASSERT_DEBUG( NULL != mSceneObject );
240 // Transfer shader ownership to a scene message
241 AddShaderMessage( mUpdateManager, *mSceneObject );
244 ShaderEffect::~ShaderEffect()
246 // Guard to allow handle destruction after Core has been destroyed
247 if ( Stage::IsInstalled() )
249 // Remove scene-object using a message to the UpdateManager
252 RemoveShaderMessage( mUpdateManager, *mSceneObject );
258 void ShaderEffect::SetEffectImage( Dali::Image image )
260 // if images are the same, do nothing
266 if (mImage && mConnectionCount > 0)
268 // unset previous image
269 GetImplementation(mImage).Disconnect();
272 // in case image is empty this will reset our image handle
277 // mSceneShader can be in a separate thread; queue a setter message
278 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, 0 );
282 // tell image that we're using it
283 if (mConnectionCount > 0)
285 GetImplementation(mImage).Connect();
287 // mSceneShader can be in a separate thread; queue a setter message
288 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
292 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
294 // Register the property if it does not exist
295 Property::Index index = GetPropertyIndex( name );
296 if ( Property::INVALID_INDEX == index )
298 index = RegisterProperty( name, value );
301 SetProperty( index, value );
303 SetCoordinateTypeMessage( mUpdateManager.GetEventToUpdate(), *mCustomMetadata[index], uniformCoordinateType );
306 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
308 DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
309 mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
312 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
314 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
318 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
320 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
324 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
326 SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
329 void ShaderEffect::SetPrograms( GeometryType geometryType,
330 const std::string& vertexPrefix, const std::string& fragmentPrefix,
331 const std::string& vertexSource, const std::string& fragmentSource )
333 bool modifiesGeometry = true;
334 // check if the vertex shader is empty (means it cannot modify geometry)
335 if( (vertexPrefix.length() == 0 )&&( vertexSource.length() == 0 ) )
337 modifiesGeometry = false;
339 // check the hint second
340 if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
342 modifiesGeometry = false;
345 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_IMAGE, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
346 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXT, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
347 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
348 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_UNTEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
351 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
352 const string& vertexSource, const string& fragmentSource,
353 bool modifiesGeometry )
355 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
356 ShaderFactory& shaderFactory = tls.GetShaderFactory();
359 ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
361 DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
363 // Add shader program to scene-object using a message to the UpdateManager
364 SetShaderProgramMessage( mUpdateManager, *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
366 mTickets.push_back(ticket); // add ticket to collection to keep it alive.
369 void ShaderEffect::Connect()
373 if (mImage && mConnectionCount == 1)
375 GetImplementation(mImage).Connect();
377 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
378 SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
382 void ShaderEffect::Disconnect()
384 DALI_ASSERT_DEBUG(mConnectionCount > 0);
387 if (mImage && mConnectionCount == 0)
389 GetImplementation(mImage).Disconnect();
393 bool ShaderEffect::IsSceneObjectRemovable() const
395 return false; // The Shader is not removed during this proxy's lifetime
398 unsigned int ShaderEffect::GetDefaultPropertyCount() const
400 return DEFAULT_PROPERTY_COUNT;
403 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
405 indices.reserve( DEFAULT_PROPERTY_COUNT );
407 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
409 indices.push_back( i );
413 const std::string& ShaderEffect::GetDefaultPropertyName(Property::Index index) const
415 if( index < DEFAULT_PROPERTY_COUNT )
417 return DEFAULT_PROPERTY_NAMES[index];
421 // index out of range..return empty string
422 static const std::string INVALID_PROPERTY_NAME;
423 return INVALID_PROPERTY_NAME;
427 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
429 Property::Index index = Property::INVALID_INDEX;
431 // Lazy initialization of static mDefaultPropertyLookup
432 if (!mDefaultPropertyLookup)
434 mDefaultPropertyLookup = new DefaultPropertyLookup();
436 for (int i=0; i<DEFAULT_PROPERTY_COUNT; ++i)
438 (*mDefaultPropertyLookup)[DEFAULT_PROPERTY_NAMES[i]] = i;
441 DALI_ASSERT_DEBUG( NULL != mDefaultPropertyLookup );
443 // Look for name in default properties
444 DefaultPropertyLookup::const_iterator result = mDefaultPropertyLookup->find( name );
445 if ( mDefaultPropertyLookup->end() != result )
447 index = result->second;
453 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
458 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
463 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
468 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
470 if( index < DEFAULT_PROPERTY_COUNT )
472 return DEFAULT_PROPERTY_TYPES[index];
476 // index out of range...return Property::NONE
477 return Property::NONE;
481 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
485 case Dali::ShaderEffect::GRID_DENSITY:
487 SetGridDensityMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, propertyValue.Get<float>() );
491 case Dali::ShaderEffect::IMAGE:
493 Dali::Image img(Scripting::NewImage( propertyValue ));
496 SetEffectImage( img );
500 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
505 case Dali::ShaderEffect::PROGRAM:
507 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
508 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
509 std::string vertex = GetShader("vertex", propertyValue);
510 std::string fragment = GetShader("fragment", propertyValue);
512 GeometryType geometryType = GEOMETRY_TYPE_IMAGE;
514 if( propertyValue.HasKey("geometry-type") )
516 Property::Value geometryValue = propertyValue.GetValue("geometry-type");
517 DALI_ASSERT_ALWAYS(geometryValue.GetType() == Property::STRING && "Geometry type is not a string" );
519 std::string s = geometryValue.Get<std::string>();
520 if(s == "GEOMETRY_TYPE_IMAGE")
522 geometryType = GEOMETRY_TYPE_IMAGE;
524 else if (s == "GEOMETRY_TYPE_TEXT")
526 geometryType = GEOMETRY_TYPE_TEXT;
528 else if( s == "GEOMETRY_TYPE_UNTEXTURED_MESH")
530 geometryType = GEOMETRY_TYPE_UNTEXTURED_MESH;
532 else if( s == "GEOMETRY_TYPE_TEXTURED_MESH")
534 geometryType = GEOMETRY_TYPE_TEXTURED_MESH;
538 DALI_ASSERT_ALWAYS(!"Geometry type unknown" );
541 SetPrograms( geometryType, vertexPrefix, vertex, fragmentPrefix, fragment );
545 case Dali::ShaderEffect::GEOMETRY_HINTS:
547 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
548 Property::Value geometryHintsValue = propertyValue.GetValue("geometry-hints");
550 std::string s = geometryHintsValue.Get<std::string>();
553 hint = Dali::ShaderEffect::HINT_NONE;
555 else if(s == "HINT_GRID_X")
557 hint = Dali::ShaderEffect::HINT_GRID_X;
559 else if(s == "HINT_GRID_Y")
561 hint = Dali::ShaderEffect::HINT_GRID_Y;
563 else if(s == "HINT_GRID")
565 hint = Dali::ShaderEffect::HINT_GRID;
567 else if(s == "HINT_DEPTH_BUFFER")
569 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
571 else if(s == "HINT_BLENDING")
573 hint = Dali::ShaderEffect::HINT_BLENDING;
575 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
577 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
581 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
584 SetHintsMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, hint );
591 DALI_ASSERT_ALWAYS(false && "ShaderEffect property enumeration out of range"); // should not come here
597 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
599 // none of our properties are readable so return empty
600 return Property::Value();
603 void ShaderEffect::InstallSceneObjectProperty( PropertyBase& newProperty, const std::string& name, unsigned int index )
605 // 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)
607 // mSceneObject is being used in a separate thread; queue a message to add the property
608 InstallCustomPropertyMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, newProperty ); // Message takes ownership
610 // mSceneObject requires metadata for each custom property (uniform)
611 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
612 // mSceneObject is being used in a separate thread; queue a message to add the property
613 InstallUniformMetaMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, *meta ); // Message takes ownership
615 // Add entry to the metadata lookup
616 mCustomMetadata[index] = meta;
619 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
624 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
626 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
628 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
630 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
632 return dynamic_cast<const PropertyBase*>( entry->second.GetSceneGraphProperty() );
635 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
637 CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
639 DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
641 DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
643 return entry->second.GetSceneGraphProperty();
646 } // namespace Internal