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/matrix.h>
23 #include <dali/public-api/math/matrix3.h>
24 #include <dali/public-api/math/vector2.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/devel-api/scripting/scripting.h>
27 #include <dali/public-api/shader-effects/shader-effect.h>
28 #include <dali/internal/event/common/property-helper.h>
29 #include <dali/internal/event/common/stage-impl.h>
30 #include <dali/internal/event/common/thread-local-storage.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/render/shaders/scene-graph-shader.h>
35 #include <dali/internal/render/shaders/uniform-meta.h>
36 #include <dali/internal/update/animation/scene-graph-constraint-base.h>
37 #include <dali/internal/update/common/animatable-property.h>
38 #include <dali/internal/update/manager/update-manager.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::RenderQueue;
60 // Name Type writable animatable constraint-input enum for index-checking
61 DALI_PROPERTY_TABLE_BEGIN
62 DALI_PROPERTY( "grid-density", FLOAT, true, false, false, Dali::ShaderEffect::Property::GRID_DENSITY )
63 DALI_PROPERTY( "image", MAP, true, false, false, Dali::ShaderEffect::Property::IMAGE )
64 DALI_PROPERTY( "program", MAP, true, false, false, Dali::ShaderEffect::Property::PROGRAM )
65 DALI_PROPERTY( "geometry-hints", STRING, true, false, false, Dali::ShaderEffect::Property::GEOMETRY_HINTS )
66 DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX )
70 Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
72 return Dali::ShaderEffect(internal.Get());
75 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
79 const char* vertexShaderPrefix;
80 const char* fragmentShaderPrefix;
81 const char* vertexShaderPostfix;
82 const char* fragmentShaderPostfix;
85 WrapperStrings customImageShaderWrappers =
87 CustomImagePrefixVertex, CustomImagePrefixFragment,
88 CustomImagePostfixVertex, CustomImagePostfixFragment
92 * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
93 * @param[in] effect of the shader
94 * @param[in] vertexPrefix from application
95 * @param[in] fragmentPrefix from application
96 * @param[in] vertexBody from application
97 * @param[in] fragmentBody from application
98 * @param[in] modifiesGeometry based on flags and vertex shader
100 void WrapAndSetProgram( Internal::ShaderEffect& effect,
101 const std::string& vertexPrefix, const std::string& fragmentPrefix,
102 const std::string& vertexBody, const std::string& fragmentBody,
103 bool modifiesGeometry )
105 // if there is some real data in the strings
106 if( ( vertexPrefix.length() > 0 ) ||
107 ( fragmentPrefix.length() > 0 ) ||
108 ( vertexBody.length() > 0 ) ||
109 ( fragmentBody.length() > 0 ) )
111 std::string vertexSource = vertexPrefix;
112 std::string fragmentSource = fragmentPrefix;
114 // Create complete shader program strings:
116 vertexSource += customImageShaderWrappers.vertexShaderPrefix;
118 // Append the custom vertex shader code if supplied, otherwise append the default
119 if ( vertexBody.length() > 0 )
121 vertexSource.append( vertexBody );
125 vertexSource.append( customImageShaderWrappers.vertexShaderPostfix );
128 fragmentSource += customImageShaderWrappers.fragmentShaderPrefix;
130 // Append the custom fragment shader code if supplied, otherwise append the default
131 if ( fragmentBody.length() > 0 )
133 fragmentSource.append( fragmentBody );
137 fragmentSource.append( customImageShaderWrappers.fragmentShaderPostfix );
140 effect.SendProgramMessage( vertexSource, fragmentSource, modifiesGeometry );
144 std::string GetShader(const std::string& field, const Property::Value& property)
147 const Property::Map* map = property.GetMap();
150 const Property::Value* value = map->Find( field );
153 value->Get( retval );
160 } // unnamed namespace
162 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
164 Stage* stage = Stage::GetCurrent();
168 ShaderEffectPtr shaderEffect( new ShaderEffect( *stage, hints ) );
169 shaderEffect->RegisterObject();
178 ShaderEffect::ShaderEffect( EventThreadServices& eventThreadServices, Dali::ShaderEffect::GeometryHints hints )
179 : mEventThreadServices( eventThreadServices ),
180 mConnectionCount (0),
181 mGeometryHints( hints )
183 mSceneObject = new SceneGraph::Shader( hints );
184 DALI_ASSERT_DEBUG( NULL != mSceneObject );
186 // Transfer shader ownership to a scene message
187 AddShaderMessage( eventThreadServices.GetUpdateManager(), *mSceneObject );
190 ShaderEffect::~ShaderEffect()
192 // Guard to allow handle destruction after Core has been destroyed
193 if ( Stage::IsInstalled() )
195 // Remove scene-object using a message to the UpdateManager
198 RemoveShaderMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject );
204 void ShaderEffect::SetEffectImage( Dali::Image image )
206 // if images are the same, do nothing
212 if (mImage && mConnectionCount > 0)
214 // unset previous image
215 GetImplementation(mImage).Disconnect();
218 // in case image is empty this will reset our image handle
223 // mSceneShader can be in a separate thread; queue a setter message
224 SetTextureIdMessage( mEventThreadServices, *mSceneObject, 0 );
228 // tell image that we're using it
229 if (mConnectionCount > 0)
231 GetImplementation(mImage).Connect();
233 // mSceneShader can be in a separate thread; queue a setter message
234 SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
238 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
240 // Register the property if it does not exist
241 Property::Index index = GetPropertyIndex( name );
242 if ( Property::INVALID_INDEX == index )
244 index = RegisterProperty( name, value );
247 SetProperty( index, value );
249 // RegisterProperty guarantees a positive value as index
250 DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
251 unsigned int metaIndex = index - CustomPropertyStartIndex();
252 // check if there's space in cache
253 if( mCoordinateTypes.Count() < (metaIndex + 1) )
255 mCoordinateTypes.Resize( metaIndex + 1 );
257 // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
258 if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
260 mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
261 SetCoordinateTypeMessage( mEventThreadServices, *mSceneObject, metaIndex, uniformCoordinateType );
265 void ShaderEffect::SetPrograms( const string& vertexSource, const string& fragmentSource )
267 SetPrograms( "", "", vertexSource, fragmentSource );
270 void ShaderEffect::SetPrograms( const std::string& vertexPrefix, const std::string& fragmentPrefix,
271 const std::string& vertexSource, const std::string& fragmentSource )
273 bool modifiesGeometry = true;
274 // check if the vertex shader is empty (means it cannot modify geometry)
275 if( (vertexPrefix.length() == 0) && (vertexSource.length() == 0) )
277 modifiesGeometry = false;
279 // check the hint second
280 if( ( mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
282 modifiesGeometry = false;
285 WrapAndSetProgram( *this, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
288 void ShaderEffect::SendProgramMessage( const string& vertexSource, const string& fragmentSource,
289 bool modifiesGeometry )
291 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
292 ShaderFactory& shaderFactory = tls.GetShaderFactory();
295 Integration::ShaderDataPtr shaderData = shaderFactory.Load( vertexSource, fragmentSource, shaderHash );
296 DALI_ASSERT_DEBUG( shaderHash != 0U );
298 // Add shader program to scene-object using a message to the UpdateManager
299 SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, shaderData, false );
302 void ShaderEffect::Connect()
306 if (mImage && mConnectionCount == 1)
308 GetImplementation(mImage).Connect();
310 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
311 SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
315 void ShaderEffect::Disconnect()
317 DALI_ASSERT_DEBUG(mConnectionCount > 0);
320 if (mImage && mConnectionCount == 0)
322 GetImplementation(mImage).Disconnect();
326 unsigned int ShaderEffect::GetDefaultPropertyCount() const
328 return DEFAULT_PROPERTY_COUNT;
331 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
333 indices.Reserve( DEFAULT_PROPERTY_COUNT );
335 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
337 indices.PushBack( i );
341 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
343 if( index < DEFAULT_PROPERTY_COUNT )
345 return DEFAULT_PROPERTY_DETAILS[index].name;
351 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
353 Property::Index index = Property::INVALID_INDEX;
355 // Look for name in default properties
356 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
358 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
359 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
369 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
371 return DEFAULT_PROPERTY_DETAILS[ index ].writable;
374 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
376 return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
379 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
381 return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
384 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
386 if( index < DEFAULT_PROPERTY_COUNT )
388 return DEFAULT_PROPERTY_DETAILS[index].type;
391 // index out of range...return Property::NONE
392 return Property::NONE;
395 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
399 case Dali::ShaderEffect::Property::GRID_DENSITY:
401 SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
405 case Dali::ShaderEffect::Property::IMAGE:
407 Dali::Image img(Scripting::NewImage( propertyValue ));
410 SetEffectImage( img );
414 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
419 case Dali::ShaderEffect::Property::PROGRAM:
421 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
422 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
423 std::string vertex = GetShader("vertex", propertyValue);
424 std::string fragment = GetShader("fragment", propertyValue);
426 SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
430 case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
432 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
433 std::string s = propertyValue.Get<std::string>();
436 hint = Dali::ShaderEffect::HINT_NONE;
438 else if(s == "HINT_GRID_X")
440 hint = Dali::ShaderEffect::HINT_GRID_X;
442 else if(s == "HINT_GRID_Y")
444 hint = Dali::ShaderEffect::HINT_GRID_Y;
446 else if(s == "HINT_GRID")
448 hint = Dali::ShaderEffect::HINT_GRID;
450 else if(s == "HINT_DEPTH_BUFFER")
452 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
454 else if(s == "HINT_BLENDING")
456 hint = Dali::ShaderEffect::HINT_BLENDING;
458 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
460 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
464 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
467 SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
480 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
482 // none of our properties are readable so return empty
483 return Property::Value();
486 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
488 // 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)
490 // mSceneObject requires metadata for each custom property (uniform)
491 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
492 // mSceneObject is being used in a separate thread; queue a message to add the property
493 InstallUniformMetaMessage( mEventThreadServices, *mSceneObject, *meta ); // Message takes ownership
496 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
501 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
503 PropertyMetadata* property = index >= PROPERTY_CUSTOM_START_INDEX ? static_cast<PropertyMetadata*>(FindCustomProperty( index )) : static_cast<PropertyMetadata*>(FindAnimatableProperty( index ));
504 DALI_ASSERT_ALWAYS( property && "Property index is invalid" );
505 return property->GetSceneGraphProperty();
508 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
510 return GetSceneObjectAnimatableProperty( index );
513 } // namespace Internal