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