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 if( property.HasKey(field) )
149 DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
151 // we could also check here for an array of strings as convenience for json not having multi line strings
152 value = property.GetValue(field).Get<std::string>();
158 } // unnamed namespace
160 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
162 Stage* stage = Stage::GetCurrent();
164 ShaderEffectPtr shaderEffect( new ShaderEffect( *stage, hints ) );
165 shaderEffect->RegisterObject();
170 ShaderEffect::ShaderEffect( EventThreadServices& eventThreadServices, Dali::ShaderEffect::GeometryHints hints )
171 : mEventThreadServices( eventThreadServices ),
172 mConnectionCount (0),
173 mGeometryHints( hints )
175 mSceneObject = new SceneGraph::Shader( hints );
176 DALI_ASSERT_DEBUG( NULL != mSceneObject );
178 // Transfer shader ownership to a scene message
179 AddShaderMessage( eventThreadServices.GetUpdateManager(), *mSceneObject );
182 ShaderEffect::~ShaderEffect()
184 // Guard to allow handle destruction after Core has been destroyed
185 if ( Stage::IsInstalled() )
187 // Remove scene-object using a message to the UpdateManager
190 RemoveShaderMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject );
196 void ShaderEffect::SetEffectImage( Dali::Image image )
198 // if images are the same, do nothing
204 if (mImage && mConnectionCount > 0)
206 // unset previous image
207 GetImplementation(mImage).Disconnect();
210 // in case image is empty this will reset our image handle
215 // mSceneShader can be in a separate thread; queue a setter message
216 SetTextureIdMessage( mEventThreadServices, *mSceneObject, 0 );
220 // tell image that we're using it
221 if (mConnectionCount > 0)
223 GetImplementation(mImage).Connect();
225 // mSceneShader can be in a separate thread; queue a setter message
226 SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
230 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
232 // Register the property if it does not exist
233 Property::Index index = GetPropertyIndex( name );
234 if ( Property::INVALID_INDEX == index )
236 index = RegisterProperty( name, value );
239 SetProperty( index, value );
241 // RegisterProperty guarantees a positive value as index
242 DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
243 unsigned int metaIndex = index - CustomPropertyStartIndex();
244 // check if there's space in cache
245 if( mCoordinateTypes.Count() < (metaIndex + 1) )
247 mCoordinateTypes.Resize( metaIndex + 1 );
249 // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
250 if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
252 mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
253 SetCoordinateTypeMessage( mEventThreadServices, *mSceneObject, metaIndex, uniformCoordinateType );
257 void ShaderEffect::SetPrograms( const string& vertexSource, const string& fragmentSource )
259 SetPrograms( "", "", vertexSource, fragmentSource );
262 void ShaderEffect::SetPrograms( const std::string& vertexPrefix, const std::string& fragmentPrefix,
263 const std::string& vertexSource, const std::string& fragmentSource )
265 bool modifiesGeometry = true;
266 // check if the vertex shader is empty (means it cannot modify geometry)
267 if( (vertexPrefix.length() == 0) && (vertexSource.length() == 0) )
269 modifiesGeometry = false;
271 // check the hint second
272 if( ( mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
274 modifiesGeometry = false;
277 WrapAndSetProgram( *this, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
280 void ShaderEffect::SendProgramMessage( const string& vertexSource, const string& fragmentSource,
281 bool modifiesGeometry )
283 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
284 ShaderFactory& shaderFactory = tls.GetShaderFactory();
287 ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
289 DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(ticket.id:%d)\n", ticket->GetId() );
291 // Add shader program to scene-object using a message to the UpdateManager
292 SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, ticket->GetId(), shaderHash, modifiesGeometry );
294 mTickets.push_back(ticket); // add ticket to collection to keep it alive.
297 void ShaderEffect::Connect()
301 if (mImage && mConnectionCount == 1)
303 GetImplementation(mImage).Connect();
305 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
306 SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
310 void ShaderEffect::Disconnect()
312 DALI_ASSERT_DEBUG(mConnectionCount > 0);
315 if (mImage && mConnectionCount == 0)
317 GetImplementation(mImage).Disconnect();
321 unsigned int ShaderEffect::GetDefaultPropertyCount() const
323 return DEFAULT_PROPERTY_COUNT;
326 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
328 indices.Reserve( DEFAULT_PROPERTY_COUNT );
330 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
332 indices.PushBack( i );
336 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
338 if( index < DEFAULT_PROPERTY_COUNT )
340 return DEFAULT_PROPERTY_DETAILS[index].name;
346 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
348 Property::Index index = Property::INVALID_INDEX;
350 // Look for name in default properties
351 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
353 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
354 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
364 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
366 return DEFAULT_PROPERTY_DETAILS[ index ].writable;
369 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
371 return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
374 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
376 return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
379 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
381 if( index < DEFAULT_PROPERTY_COUNT )
383 return DEFAULT_PROPERTY_DETAILS[index].type;
386 // index out of range...return Property::NONE
387 return Property::NONE;
390 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
394 case Dali::ShaderEffect::Property::GRID_DENSITY:
396 SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
400 case Dali::ShaderEffect::Property::IMAGE:
402 Dali::Image img(Scripting::NewImage( propertyValue ));
405 SetEffectImage( img );
409 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
414 case Dali::ShaderEffect::Property::PROGRAM:
416 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
417 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
418 std::string vertex = GetShader("vertex", propertyValue);
419 std::string fragment = GetShader("fragment", propertyValue);
421 SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
425 case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
427 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
428 std::string s = propertyValue.Get<std::string>();
431 hint = Dali::ShaderEffect::HINT_NONE;
433 else if(s == "HINT_GRID_X")
435 hint = Dali::ShaderEffect::HINT_GRID_X;
437 else if(s == "HINT_GRID_Y")
439 hint = Dali::ShaderEffect::HINT_GRID_Y;
441 else if(s == "HINT_GRID")
443 hint = Dali::ShaderEffect::HINT_GRID;
445 else if(s == "HINT_DEPTH_BUFFER")
447 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
449 else if(s == "HINT_BLENDING")
451 hint = Dali::ShaderEffect::HINT_BLENDING;
453 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
455 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
459 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
462 SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
475 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
477 // none of our properties are readable so return empty
478 return Property::Value();
481 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
483 // 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)
485 // mSceneObject requires metadata for each custom property (uniform)
486 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
487 // mSceneObject is being used in a separate thread; queue a message to add the property
488 InstallUniformMetaMessage( mEventThreadServices, *mSceneObject, *meta ); // Message takes ownership
491 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
496 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
498 PropertyMetadata* property = index >= PROPERTY_CUSTOM_START_INDEX ? static_cast<PropertyMetadata*>(FindCustomProperty( index )) : static_cast<PropertyMetadata*>(FindAnimatableProperty( index ));
499 DALI_ASSERT_ALWAYS( property && "Property index is invalid" );
500 return property->GetSceneGraphProperty();
503 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
505 return GetSceneObjectAnimatableProperty( index );
508 } // namespace Internal