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