Merge "Fix UtcDaliObjectRegistryCopyConstructor" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / effects / shader-effect-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/effects/shader-effect-impl.h>
20
21 // INTERNAL INCLUDES
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"
40
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;
47 using std::string;
48
49 namespace Dali
50 {
51
52 namespace Internal
53 {
54
55 namespace
56 {
57
58 // Properties
59
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 )
67
68 BaseHandle Create()
69 {
70   Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
71
72   return Dali::ShaderEffect(internal.Get());
73 }
74
75 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
76
77 struct WrapperStrings
78 {
79   const char* vertexShaderPrefix;
80   const char* fragmentShaderPrefix;
81   const char* vertexShaderPostfix;
82   const char* fragmentShaderPostfix;
83 };
84
85 WrapperStrings customShaderWrappers [] =
86 {
87   {
88     CustomImagePrefixVertex, CustomImagePrefixFragment,
89     CustomImagePostfixVertex, CustomImagePostfixFragment
90   },
91   {
92     CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
93     CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
94   },
95   {
96     CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
97     CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
98   }
99 };
100
101 /**
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
111  */
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 )
117 {
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   ) ) )
124   {
125     std::string vertexSource = vertexPrefix;
126     std::string fragmentSource = fragmentPrefix;
127
128     // create complete shader program strings for the given geometry type
129     unsigned int index = 0;
130     switch( expectedGeometryType )
131     {
132       case GEOMETRY_TYPE_IMAGE:
133       {
134         index = 0;
135         break;
136       }
137       case GEOMETRY_TYPE_UNTEXTURED_MESH:
138       {
139         index = 1;
140         break;
141       }
142       case GEOMETRY_TYPE_TEXTURED_MESH:
143       {
144         index = 2;
145         break;
146       }
147       case GEOMETRY_TYPE_LAST:
148       {
149         DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
150         break;
151       }
152     }
153
154     vertexSource += customShaderWrappers[index].vertexShaderPrefix;
155     // Append the custom vertex shader code if supplied, otherwise append the default
156     if ( vertexBody.length() > 0 )
157     {
158       vertexSource.append( vertexBody );
159     }
160     else
161     {
162       vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
163     }
164
165     fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
166     // Append the custom fragment shader code if supplied, otherwise append the default
167     if ( fragmentBody.length() > 0 )
168     {
169       fragmentSource.append( fragmentBody );
170     }
171     else
172     {
173       fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
174     }
175
176     effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
177   }
178 }
179
180 std::string GetShader(const std::string& field, const Property::Value& property)
181 {
182   std::string retval;
183   const Property::Map* map = property.GetMap();
184   if( map )
185   {
186     const Property::Value* value = map->Find( field );
187     if( value )
188     {
189       value->Get( retval );
190     }
191   }
192
193   return retval;
194 }
195
196 } // unnamed namespace
197
198 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
199 {
200   Stage* stage = Stage::GetCurrent();
201
202   if( stage )
203   {
204     ShaderEffectPtr shaderEffect( new ShaderEffect( *stage, hints ) );
205     shaderEffect->RegisterObject();
206     return shaderEffect;
207   }
208   else
209   {
210     return NULL;
211   }
212 }
213
214 ShaderEffect::ShaderEffect( EventThreadServices& eventThreadServices, Dali::ShaderEffect::GeometryHints hints )
215 : mEventThreadServices( eventThreadServices ),
216   mConnectionCount (0),
217   mGeometryHints( hints )
218 {
219   mSceneObject = new Shader( hints );
220   DALI_ASSERT_DEBUG( NULL != mSceneObject );
221
222   // Transfer shader ownership to a scene message
223   AddShaderMessage( eventThreadServices.GetUpdateManager(), *mSceneObject );
224 }
225
226 ShaderEffect::~ShaderEffect()
227 {
228   // Guard to allow handle destruction after Core has been destroyed
229   if ( Stage::IsInstalled() )
230   {
231     // Remove scene-object using a message to the UpdateManager
232     if( mSceneObject )
233     {
234       RemoveShaderMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject );
235     }
236     UnregisterObject();
237   }
238 }
239
240 void ShaderEffect::SetEffectImage( Dali::Image image )
241 {
242   // if images are the same, do nothing
243   if (mImage == image)
244   {
245     return;
246   }
247
248   if (mImage && mConnectionCount > 0)
249   {
250     // unset previous image
251     GetImplementation(mImage).Disconnect();
252   }
253
254   // in case image is empty this will reset our image handle
255   mImage = image;
256
257   if (!image)
258   {
259     // mSceneShader can be in a separate thread; queue a setter message
260     SetTextureIdMessage( mEventThreadServices, *mSceneObject, 0 );
261   }
262   else
263   {
264     // tell image that we're using it
265     if (mConnectionCount > 0)
266     {
267       GetImplementation(mImage).Connect();
268     }
269     // mSceneShader can be in a separate thread; queue a setter message
270     SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
271   }
272 }
273
274 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
275 {
276   // Register the property if it does not exist
277   Property::Index index = GetPropertyIndex( name );
278   if ( Property::INVALID_INDEX == index )
279   {
280     index = RegisterProperty( name, value );
281   }
282
283   SetProperty( index, value );
284
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) )
290   {
291     mCoordinateTypes.Resize( metaIndex + 1 );
292   }
293   // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
294   if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
295   {
296     mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
297     SetCoordinateTypeMessage( mEventThreadServices, *mSceneObject, metaIndex, uniformCoordinateType );
298   }
299 }
300
301 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
302 {
303   DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
304   mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
305 }
306
307 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
308 {
309   DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
310   return *mExtension;
311 }
312
313 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
314 {
315   DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
316   return *mExtension;
317 }
318
319 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
320 {
321   SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
322 }
323
324 void ShaderEffect::SetPrograms( GeometryType geometryType,
325                                 const std::string& vertexPrefix, const std::string& fragmentPrefix,
326                                 const std::string& vertexSource, const std::string& fragmentSource )
327 {
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 ) )
331   {
332     modifiesGeometry = false;
333   }
334   // check the hint second
335   if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
336   {
337     modifiesGeometry = false;
338   }
339
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 );
343 }
344
345 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
346                                        const string& vertexSource, const string& fragmentSource,
347                                        bool modifiesGeometry )
348 {
349   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
350   ShaderFactory& shaderFactory = tls.GetShaderFactory();
351   size_t shaderHash;
352
353   ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
354
355   DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
356
357   // Add shader program to scene-object using a message to the UpdateManager
358   SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
359
360   mTickets.push_back(ticket);       // add ticket to collection to keep it alive.
361 }
362
363 void ShaderEffect::Connect()
364 {
365   ++mConnectionCount;
366
367   if (mImage && mConnectionCount == 1)
368   {
369     GetImplementation(mImage).Connect();
370
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() );
373   }
374 }
375
376 void ShaderEffect::Disconnect()
377 {
378   DALI_ASSERT_DEBUG(mConnectionCount > 0);
379   --mConnectionCount;
380
381   if (mImage && mConnectionCount == 0)
382   {
383      GetImplementation(mImage).Disconnect();
384   }
385 }
386
387 unsigned int ShaderEffect::GetDefaultPropertyCount() const
388 {
389   return DEFAULT_PROPERTY_COUNT;
390 }
391
392 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
393 {
394   indices.Reserve( DEFAULT_PROPERTY_COUNT );
395
396   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
397   {
398     indices.PushBack( i );
399   }
400 }
401
402 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
403 {
404   if( index < DEFAULT_PROPERTY_COUNT )
405   {
406     return DEFAULT_PROPERTY_DETAILS[index].name;
407   }
408
409   return NULL;
410 }
411
412 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
413 {
414   Property::Index index = Property::INVALID_INDEX;
415
416   // Look for name in default properties
417   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
418   {
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
421     {
422       index = i;
423       break;
424     }
425   }
426
427   return index;
428 }
429
430 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
431 {
432   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
433 }
434
435 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
436 {
437   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
438 }
439
440 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
441 {
442   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
443 }
444
445 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
446 {
447   if( index < DEFAULT_PROPERTY_COUNT )
448   {
449     return DEFAULT_PROPERTY_DETAILS[index].type;
450   }
451
452   // index out of range...return Property::NONE
453   return Property::NONE;
454 }
455
456 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
457 {
458   switch ( index )
459   {
460     case Dali::ShaderEffect::Property::GRID_DENSITY:
461     {
462       SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
463       break;
464     }
465
466     case Dali::ShaderEffect::Property::IMAGE:
467     {
468       Dali::Image img(Scripting::NewImage( propertyValue ));
469       if(img)
470       {
471         SetEffectImage( img );
472       }
473       else
474       {
475         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
476       }
477       break;
478     }
479
480     case Dali::ShaderEffect::Property::PROGRAM:
481     {
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);
486
487       GeometryType geometryType      = GEOMETRY_TYPE_IMAGE; // only images are supported
488       SetPrograms( geometryType, vertexPrefix, fragmentPrefix, vertex, fragment );
489       break;
490     }
491
492     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
493     {
494       Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
495       std::string s = propertyValue.Get<std::string>();
496       if(s == "HINT_NONE")
497       {
498         hint = Dali::ShaderEffect::HINT_NONE;
499       }
500       else if(s == "HINT_GRID_X")
501       {
502         hint = Dali::ShaderEffect::HINT_GRID_X;
503       }
504       else if(s == "HINT_GRID_Y")
505       {
506         hint = Dali::ShaderEffect::HINT_GRID_Y;
507       }
508       else if(s == "HINT_GRID")
509       {
510         hint = Dali::ShaderEffect::HINT_GRID;
511       }
512       else if(s == "HINT_DEPTH_BUFFER")
513       {
514         hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
515       }
516       else if(s == "HINT_BLENDING")
517       {
518         hint = Dali::ShaderEffect::HINT_BLENDING;
519       }
520       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
521       {
522         hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
523       }
524       else
525       {
526         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
527       }
528
529       SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
530
531       break;
532     }
533
534     default:
535     {
536       // nothing to do
537       break;
538     }
539   }
540 }
541
542 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
543 {
544   // none of our properties are readable so return empty
545   return Property::Value();
546 }
547
548 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
549 {
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)
551
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
556 }
557
558 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
559 {
560   return mSceneObject;
561 }
562
563 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
564 {
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();
568 }
569
570 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
571 {
572   return GetSceneObjectAnimatableProperty( index );
573 }
574
575 } // namespace Internal
576
577 } // namespace Dali