Merge branch 'tizen' of platform/core/uifw/dali-core into devel/new_mesh
[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/public-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"
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", INTEGER, 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     CustomTextDistanceFieldPrefixVertex, CustomTextDistanceFieldPrefixFragment,
93     CustomTextDistanceFieldPostfixVertex, CustomTextDistanceFieldPostfixFragment
94   },
95   {
96     CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
97     CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
98   },
99   {
100     CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
101     CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
102   }
103 };
104
105 /**
106  * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
107  * @param[in] effect of the shader
108  * @param[in] actualGeometryType of the shader
109  * @param[in] expectedGeometryType of the shader
110  * @param[in] vertexPrefix from application
111  * @param[in] fragmentPrefix from application
112  * @param[in] vertexBody from application
113  * @param[in] fragmentBody from application
114  * @param[in] modifiesGeometry based on flags and vertex shader
115  */
116 void WrapAndSetProgram( Internal::ShaderEffect& effect,
117                         GeometryType actualGeometryType, GeometryType expectedGeometryType,
118                         const std::string& vertexPrefix, const std::string& fragmentPrefix,
119                         const std::string& vertexBody, const std::string& fragmentBody,
120                         bool modifiesGeometry )
121 {
122   // if geometry type matches and there is some real data in the strings
123   if( ( actualGeometryType & expectedGeometryType )&&
124       ( ( vertexPrefix.length() > 0   )||
125         ( fragmentPrefix.length() > 0 )||
126         ( vertexBody.length() > 0     )||
127         ( fragmentBody.length() > 0   ) ) )
128   {
129     std::string vertexSource = vertexPrefix;
130     std::string fragmentSource = fragmentPrefix;
131
132     // create complete shader program strings for the given geometry type
133     unsigned int index = 0;
134     switch( expectedGeometryType )
135     {
136       case GEOMETRY_TYPE_IMAGE:
137       {
138         index = 0;
139         break;
140       }
141       case GEOMETRY_TYPE_TEXT:
142       {
143         index = 1;
144         break;
145       }
146       case GEOMETRY_TYPE_UNTEXTURED_MESH:
147       {
148         index = 2;
149         break;
150       }
151       case GEOMETRY_TYPE_TEXTURED_MESH:
152       {
153         index = 3;
154         break;
155       }
156       case GEOMETRY_TYPE_LAST:
157       {
158         DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
159         break;
160       }
161     }
162
163     vertexSource += customShaderWrappers[index].vertexShaderPrefix;
164     // Append the custom vertex shader code if supplied, otherwise append the default
165     if ( vertexBody.length() > 0 )
166     {
167       vertexSource.append( vertexBody );
168     }
169     else
170     {
171       vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
172     }
173
174     fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
175     // Append the custom fragment shader code if supplied, otherwise append the default
176     if ( fragmentBody.length() > 0 )
177     {
178       fragmentSource.append( fragmentBody );
179     }
180     else
181     {
182       fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
183     }
184
185     effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
186   }
187 }
188
189 std::string GetShader(const std::string& field, const Property::Value& property)
190 {
191   std::string value;
192   if( property.HasKey(field) )
193   {
194     DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
195
196     // we could also check here for an array of strings as convenience for json not having multi line strings
197     value = property.GetValue(field).Get<std::string>();
198   }
199
200   return value;
201 }
202
203 } // unnamed namespace
204
205 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
206 {
207   Stage* stage = Stage::GetCurrent();
208
209   ShaderEffectPtr shaderEffect( new ShaderEffect( *stage, hints ) );
210   shaderEffect->RegisterObject();
211
212   return shaderEffect;
213 }
214
215 ShaderEffect::ShaderEffect( EventThreadServices& eventThreadServices, Dali::ShaderEffect::GeometryHints hints )
216 : mEventThreadServices( eventThreadServices ),
217   mConnectionCount (0),
218   mGeometryHints( hints )
219 {
220   mSceneObject = new SceneGraph::Shader( hints );
221   DALI_ASSERT_DEBUG( NULL != mSceneObject );
222
223   // Transfer shader ownership to a scene message
224   AddShaderMessage( eventThreadServices.GetUpdateManager(), *mSceneObject );
225 }
226
227 ShaderEffect::~ShaderEffect()
228 {
229   // Guard to allow handle destruction after Core has been destroyed
230   if ( Stage::IsInstalled() )
231   {
232     // Remove scene-object using a message to the UpdateManager
233     if( mSceneObject )
234     {
235       RemoveShaderMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject );
236     }
237     UnregisterObject();
238   }
239 }
240
241 void ShaderEffect::SetEffectImage( Dali::Image image )
242 {
243   // if images are the same, do nothing
244   if (mImage == image)
245   {
246     return;
247   }
248
249   if (mImage && mConnectionCount > 0)
250   {
251     // unset previous image
252     GetImplementation(mImage).Disconnect();
253   }
254
255   // in case image is empty this will reset our image handle
256   mImage = image;
257
258   if (!image)
259   {
260     // mSceneShader can be in a separate thread; queue a setter message
261     SetTextureIdMessage( mEventThreadServices, *mSceneObject, 0 );
262   }
263   else
264   {
265     // tell image that we're using it
266     if (mConnectionCount > 0)
267     {
268       GetImplementation(mImage).Connect();
269     }
270     // mSceneShader can be in a separate thread; queue a setter message
271     SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
272   }
273 }
274
275 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
276 {
277   // Register the property if it does not exist
278   Property::Index index = GetPropertyIndex( name );
279   if ( Property::INVALID_INDEX == index )
280   {
281     index = RegisterProperty( name, value );
282   }
283
284   SetProperty( index, value );
285
286   // RegisterProperty guarantees a positive value as index
287   DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
288   unsigned int metaIndex = index - CustomPropertyStartIndex();
289   // check if there's space in cache
290   if( mCoordinateTypes.Count() < (metaIndex + 1) )
291   {
292     mCoordinateTypes.Resize( metaIndex + 1 );
293   }
294   // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
295   if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
296   {
297     mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
298     SetCoordinateTypeMessage( mEventThreadServices, *mSceneObject, metaIndex, uniformCoordinateType );
299   }
300 }
301
302 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
303 {
304   DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
305   mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
306 }
307
308 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
309 {
310   DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
311   return *mExtension;
312 }
313
314 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
315 {
316   DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
317   return *mExtension;
318 }
319
320 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
321 {
322   SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
323 }
324
325 void ShaderEffect::SetPrograms( GeometryType geometryType,
326                                 const std::string& vertexPrefix, const std::string& fragmentPrefix,
327                                 const std::string& vertexSource, const std::string& fragmentSource )
328 {
329   bool modifiesGeometry = true;
330   // check if the vertex shader is empty (means it cannot modify geometry)
331   if( (vertexPrefix.length() == 0 )&&( vertexSource.length() == 0 ) )
332   {
333     modifiesGeometry = false;
334   }
335   // check the hint second
336   if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
337   {
338     modifiesGeometry = false;
339   }
340
341   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_IMAGE, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
342   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXT, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
343   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
344   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_UNTEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
345 }
346
347 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
348                                        const string& vertexSource, const string& fragmentSource,
349                                        bool modifiesGeometry )
350 {
351   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
352   ShaderFactory& shaderFactory = tls.GetShaderFactory();
353   size_t shaderHash;
354
355   ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
356
357   DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
358
359   // Add shader program to scene-object using a message to the UpdateManager
360   SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
361
362   mTickets.push_back(ticket);       // add ticket to collection to keep it alive.
363 }
364
365 void ShaderEffect::Connect()
366 {
367   ++mConnectionCount;
368
369   if (mImage && mConnectionCount == 1)
370   {
371     GetImplementation(mImage).Connect();
372
373     // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
374     SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
375   }
376 }
377
378 void ShaderEffect::Disconnect()
379 {
380   DALI_ASSERT_DEBUG(mConnectionCount > 0);
381   --mConnectionCount;
382
383   if (mImage && mConnectionCount == 0)
384   {
385      GetImplementation(mImage).Disconnect();
386   }
387 }
388
389 unsigned int ShaderEffect::GetDefaultPropertyCount() const
390 {
391   return DEFAULT_PROPERTY_COUNT;
392 }
393
394 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
395 {
396   indices.reserve( DEFAULT_PROPERTY_COUNT );
397
398   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
399   {
400     indices.push_back( i );
401   }
402 }
403
404 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
405 {
406   if( index < DEFAULT_PROPERTY_COUNT )
407   {
408     return DEFAULT_PROPERTY_DETAILS[index].name;
409   }
410
411   return NULL;
412 }
413
414 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
415 {
416   Property::Index index = Property::INVALID_INDEX;
417
418   // Look for name in default properties
419   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
420   {
421     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
422     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
423     {
424       index = i;
425       break;
426     }
427   }
428
429   return index;
430 }
431
432 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
433 {
434   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
435 }
436
437 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
438 {
439   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
440 }
441
442 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
443 {
444   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
445 }
446
447 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
448 {
449   if( index < DEFAULT_PROPERTY_COUNT )
450   {
451     return DEFAULT_PROPERTY_DETAILS[index].type;
452   }
453
454   // index out of range...return Property::NONE
455   return Property::NONE;
456 }
457
458 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
459 {
460   switch ( index )
461   {
462     case Dali::ShaderEffect::Property::GRID_DENSITY:
463     {
464       SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
465       break;
466     }
467
468     case Dali::ShaderEffect::Property::IMAGE:
469     {
470       Dali::Image img(Scripting::NewImage( propertyValue ));
471       if(img)
472       {
473         SetEffectImage( img );
474       }
475       else
476       {
477         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
478       }
479       break;
480     }
481
482     case Dali::ShaderEffect::Property::PROGRAM:
483     {
484       std::string vertexPrefix   = GetShader("vertex-prefix", propertyValue);
485       std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
486       std::string vertex         = GetShader("vertex", propertyValue);
487       std::string fragment       = GetShader("fragment", propertyValue);
488
489       GeometryType geometryType      = GEOMETRY_TYPE_IMAGE;
490
491       if( propertyValue.HasKey("geometry-type") )
492       {
493         Property::Value geometryValue  = propertyValue.GetValue("geometry-type");
494         DALI_ASSERT_ALWAYS(geometryValue.GetType() == Property::STRING && "Geometry type is not a string" );
495
496         std::string s = geometryValue.Get<std::string>();
497         if(s == "GEOMETRY_TYPE_IMAGE")
498         {
499           geometryType  = GEOMETRY_TYPE_IMAGE;
500         }
501         else if (s == "GEOMETRY_TYPE_TEXT")
502         {
503           geometryType  = GEOMETRY_TYPE_TEXT;
504         }
505         else if( s == "GEOMETRY_TYPE_UNTEXTURED_MESH")
506         {
507           geometryType  = GEOMETRY_TYPE_UNTEXTURED_MESH;
508         }
509         else if( s == "GEOMETRY_TYPE_TEXTURED_MESH")
510         {
511           geometryType  = GEOMETRY_TYPE_TEXTURED_MESH;
512         }
513         else
514         {
515           DALI_ASSERT_ALWAYS(!"Geometry type unknown" );
516         }
517       }
518       SetPrograms( geometryType, vertexPrefix, fragmentPrefix, vertex, fragment );
519       break;
520     }
521
522     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
523     {
524       Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
525       Property::Value geometryHintsValue   = propertyValue.GetValue("geometry-hints");
526
527       std::string s = geometryHintsValue.Get<std::string>();
528       if(s == "HINT_NONE")
529       {
530         hint = Dali::ShaderEffect::HINT_NONE;
531       }
532       else if(s == "HINT_GRID_X")
533       {
534         hint = Dali::ShaderEffect::HINT_GRID_X;
535       }
536       else if(s == "HINT_GRID_Y")
537       {
538         hint = Dali::ShaderEffect::HINT_GRID_Y;
539       }
540       else if(s == "HINT_GRID")
541       {
542         hint = Dali::ShaderEffect::HINT_GRID;
543       }
544       else if(s == "HINT_DEPTH_BUFFER")
545       {
546         hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
547       }
548       else if(s == "HINT_BLENDING")
549       {
550         hint = Dali::ShaderEffect::HINT_BLENDING;
551       }
552       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
553       {
554         hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
555       }
556       else
557       {
558         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
559       }
560
561       SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
562
563       break;
564     }
565
566     default:
567     {
568       // nothing to do
569       break;
570     }
571   }
572 }
573
574 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
575 {
576   // none of our properties are readable so return empty
577   return Property::Value();
578 }
579
580 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
581 {
582   // 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)
583
584   // mSceneObject requires metadata for each custom property (uniform)
585   UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
586   // mSceneObject is being used in a separate thread; queue a message to add the property
587   InstallUniformMetaMessage( mEventThreadServices, *mSceneObject, *meta ); // Message takes ownership
588 }
589
590 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
591 {
592   return mSceneObject;
593 }
594
595 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
596 {
597   PropertyMetadata* property = index >= PROPERTY_CUSTOM_START_INDEX ? static_cast<PropertyMetadata*>(FindCustomProperty( index )) : static_cast<PropertyMetadata*>(FindAnimatableProperty( index ));
598   DALI_ASSERT_ALWAYS( property && "Property index is invalid" );
599   return property->GetSceneGraphProperty();
600 }
601
602 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
603 {
604   return GetSceneObjectAnimatableProperty( index );
605 }
606
607 } // namespace Internal
608
609 } // namespace Dali