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/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 customShaderWrappers [] =
88 CustomImagePrefixVertex, CustomImagePrefixFragment,
89 CustomImagePostfixVertex, CustomImagePostfixFragment
92 CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
93 CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
96 CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
97 CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
102 * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
103 * @param[in] effect of the shader
104 * @param[in] actualGeometryType of the shader
105 * @param[in] expectedGeometryType of the shader
106 * @param[in] vertexPrefix from application
107 * @param[in] fragmentPrefix from application
108 * @param[in] vertexBody from application
109 * @param[in] fragmentBody from application
110 * @param[in] modifiesGeometry based on flags and vertex shader
112 void WrapAndSetProgram( Internal::ShaderEffect& effect,
113 GeometryType actualGeometryType, GeometryType expectedGeometryType,
114 const std::string& vertexPrefix, const std::string& fragmentPrefix,
115 const std::string& vertexBody, const std::string& fragmentBody,
116 bool modifiesGeometry )
118 // if geometry type matches and there is some real data in the strings
119 if( ( actualGeometryType & expectedGeometryType )&&
120 ( ( vertexPrefix.length() > 0 )||
121 ( fragmentPrefix.length() > 0 )||
122 ( vertexBody.length() > 0 )||
123 ( fragmentBody.length() > 0 ) ) )
125 std::string vertexSource = vertexPrefix;
126 std::string fragmentSource = fragmentPrefix;
128 // create complete shader program strings for the given geometry type
129 unsigned int index = 0;
130 switch( expectedGeometryType )
132 case GEOMETRY_TYPE_IMAGE:
137 case GEOMETRY_TYPE_UNTEXTURED_MESH:
142 case GEOMETRY_TYPE_TEXTURED_MESH:
147 case GEOMETRY_TYPE_LAST:
149 DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
154 vertexSource += customShaderWrappers[index].vertexShaderPrefix;
155 // Append the custom vertex shader code if supplied, otherwise append the default
156 if ( vertexBody.length() > 0 )
158 vertexSource.append( vertexBody );
162 vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
165 fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
166 // Append the custom fragment shader code if supplied, otherwise append the default
167 if ( fragmentBody.length() > 0 )
169 fragmentSource.append( fragmentBody );
173 fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
176 effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
180 std::string GetShader(const std::string& field, const Property::Value& property)
183 const Property::Map* map = property.GetMap();
186 const Property::Value* value = map->Find( field );
189 value->Get( retval );
196 } // unnamed namespace
198 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
200 Stage* stage = Stage::GetCurrent();
204 ShaderEffectPtr shaderEffect( new ShaderEffect( *stage, hints ) );
205 shaderEffect->RegisterObject();
214 ShaderEffect::ShaderEffect( EventThreadServices& eventThreadServices, Dali::ShaderEffect::GeometryHints hints )
215 : mEventThreadServices( eventThreadServices ),
216 mConnectionCount (0),
217 mGeometryHints( hints )
219 mSceneObject = new Shader( hints );
220 DALI_ASSERT_DEBUG( NULL != mSceneObject );
222 // Transfer shader ownership to a scene message
223 AddShaderMessage( eventThreadServices.GetUpdateManager(), *mSceneObject );
226 ShaderEffect::~ShaderEffect()
228 // Guard to allow handle destruction after Core has been destroyed
229 if ( Stage::IsInstalled() )
231 // Remove scene-object using a message to the UpdateManager
234 RemoveShaderMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject );
240 void ShaderEffect::SetEffectImage( Dali::Image image )
242 // if images are the same, do nothing
248 if (mImage && mConnectionCount > 0)
250 // unset previous image
251 GetImplementation(mImage).Disconnect();
254 // in case image is empty this will reset our image handle
259 // mSceneShader can be in a separate thread; queue a setter message
260 SetTextureIdMessage( mEventThreadServices, *mSceneObject, 0 );
264 // tell image that we're using it
265 if (mConnectionCount > 0)
267 GetImplementation(mImage).Connect();
269 // mSceneShader can be in a separate thread; queue a setter message
270 SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
274 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
276 // Register the property if it does not exist
277 Property::Index index = GetPropertyIndex( name );
278 if ( Property::INVALID_INDEX == index )
280 index = RegisterProperty( name, value );
283 SetProperty( index, value );
285 // RegisterProperty guarantees a positive value as index
286 DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
287 unsigned int metaIndex = index - CustomPropertyStartIndex();
288 // check if there's space in cache
289 if( mCoordinateTypes.Count() < (metaIndex + 1) )
291 mCoordinateTypes.Resize( metaIndex + 1 );
293 // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
294 if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
296 mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
297 SetCoordinateTypeMessage( mEventThreadServices, *mSceneObject, metaIndex, uniformCoordinateType );
301 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
303 DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
304 mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
307 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
309 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
313 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
315 DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
319 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
321 SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
324 void ShaderEffect::SetPrograms( GeometryType geometryType,
325 const std::string& vertexPrefix, const std::string& fragmentPrefix,
326 const std::string& vertexSource, const std::string& fragmentSource )
328 bool modifiesGeometry = true;
329 // check if the vertex shader is empty (means it cannot modify geometry)
330 if( (vertexPrefix.length() == 0 )&&( vertexSource.length() == 0 ) )
332 modifiesGeometry = false;
334 // check the hint second
335 if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
337 modifiesGeometry = false;
340 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_IMAGE, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
341 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
342 WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_UNTEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
345 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
346 const string& vertexSource, const string& fragmentSource,
347 bool modifiesGeometry )
349 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
350 ShaderFactory& shaderFactory = tls.GetShaderFactory();
353 ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
355 DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
357 // Add shader program to scene-object using a message to the UpdateManager
358 SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
360 mTickets.push_back(ticket); // add ticket to collection to keep it alive.
363 void ShaderEffect::Connect()
367 if (mImage && mConnectionCount == 1)
369 GetImplementation(mImage).Connect();
371 // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
372 SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
376 void ShaderEffect::Disconnect()
378 DALI_ASSERT_DEBUG(mConnectionCount > 0);
381 if (mImage && mConnectionCount == 0)
383 GetImplementation(mImage).Disconnect();
387 unsigned int ShaderEffect::GetDefaultPropertyCount() const
389 return DEFAULT_PROPERTY_COUNT;
392 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
394 indices.Reserve( DEFAULT_PROPERTY_COUNT );
396 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
398 indices.PushBack( i );
402 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
404 if( index < DEFAULT_PROPERTY_COUNT )
406 return DEFAULT_PROPERTY_DETAILS[index].name;
412 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
414 Property::Index index = Property::INVALID_INDEX;
416 // Look for name in default properties
417 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
419 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
420 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
430 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
432 return DEFAULT_PROPERTY_DETAILS[ index ].writable;
435 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
437 return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
440 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
442 return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
445 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
447 if( index < DEFAULT_PROPERTY_COUNT )
449 return DEFAULT_PROPERTY_DETAILS[index].type;
452 // index out of range...return Property::NONE
453 return Property::NONE;
456 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
460 case Dali::ShaderEffect::Property::GRID_DENSITY:
462 SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
466 case Dali::ShaderEffect::Property::IMAGE:
468 Dali::Image img(Scripting::NewImage( propertyValue ));
471 SetEffectImage( img );
475 DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
480 case Dali::ShaderEffect::Property::PROGRAM:
482 std::string vertexPrefix = GetShader("vertex-prefix", propertyValue);
483 std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
484 std::string vertex = GetShader("vertex", propertyValue);
485 std::string fragment = GetShader("fragment", propertyValue);
487 GeometryType geometryType = GEOMETRY_TYPE_IMAGE; // only images are supported
488 SetPrograms( geometryType, vertexPrefix, fragmentPrefix, vertex, fragment );
492 case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
494 Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
495 std::string s = propertyValue.Get<std::string>();
498 hint = Dali::ShaderEffect::HINT_NONE;
500 else if(s == "HINT_GRID_X")
502 hint = Dali::ShaderEffect::HINT_GRID_X;
504 else if(s == "HINT_GRID_Y")
506 hint = Dali::ShaderEffect::HINT_GRID_Y;
508 else if(s == "HINT_GRID")
510 hint = Dali::ShaderEffect::HINT_GRID;
512 else if(s == "HINT_DEPTH_BUFFER")
514 hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
516 else if(s == "HINT_BLENDING")
518 hint = Dali::ShaderEffect::HINT_BLENDING;
520 else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
522 hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
526 DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
529 SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
542 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
544 // none of our properties are readable so return empty
545 return Property::Value();
548 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
550 // 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)
552 // mSceneObject requires metadata for each custom property (uniform)
553 UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
554 // mSceneObject is being used in a separate thread; queue a message to add the property
555 InstallUniformMetaMessage( mEventThreadServices, *mSceneObject, *meta ); // Message takes ownership
558 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
563 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
565 PropertyMetadata* property = index >= PROPERTY_CUSTOM_START_INDEX ? static_cast<PropertyMetadata*>(FindCustomProperty( index )) : static_cast<PropertyMetadata*>(FindAnimatableProperty( index ));
566 DALI_ASSERT_ALWAYS( property && "Property index is invalid" );
567 return property->GetSceneGraphProperty();
570 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
572 return GetSceneObjectAnimatableProperty( index );
575 } // namespace Internal