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