Merge "Implemented shader program property" 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 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/math/matrix.h>
26 #include <dali/public-api/math/matrix3.h>
27 #include <dali/public-api/math/vector2.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/devel-api/scripting/scripting.h>
30 #include <dali/public-api/shader-effects/shader-effect.h>
31 #include <dali/internal/event/actors/image-actor-impl.h>
32 #include <dali/internal/event/common/property-helper.h>
33 #include <dali/internal/event/images/image-impl.h>
34 #include "dali-shaders.h"
35
36 using std::string;
37
38 namespace Dali
39 {
40
41 namespace Internal
42 {
43
44 namespace
45 {
46
47 // Properties
48
49 //              Name             Type   writable animatable constraint-input  enum for index-checking
50 DALI_PROPERTY_TABLE_BEGIN
51 DALI_PROPERTY( "gridDensity",    FLOAT,   true,    false,   false,   Dali::ShaderEffect::Property::GRID_DENSITY   )
52 DALI_PROPERTY( "image",          MAP,     true,    false,   false,   Dali::ShaderEffect::Property::IMAGE          )
53 DALI_PROPERTY( "program",        MAP,     true,    false,   false,   Dali::ShaderEffect::Property::PROGRAM        )
54 DALI_PROPERTY( "geometryHints",  STRING,  true,    false,   false,   Dali::ShaderEffect::Property::GEOMETRY_HINTS )
55 DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX )
56
57 BaseHandle Create()
58 {
59   Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
60
61   return Dali::ShaderEffect(internal.Get());
62 }
63
64 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
65
66 struct WrapperStrings
67 {
68   const char* vertexShaderPrefix;
69   const char* fragmentShaderPrefix;
70   const char* vertexShaderPostfix;
71   const char* fragmentShaderPostfix;
72 };
73
74 WrapperStrings customImageShaderWrappers =
75 {
76   CustomImagePrefixVertex, CustomImagePrefixFragment,
77   CustomImagePostfixVertex, CustomImagePostfixFragment
78 };
79
80 /**
81  * Helper to wrap the program with our default pre and postfix if needed
82  * @param[in] vertexPrefix from application
83  * @param[in] vertexBody from application
84  */
85 std::string WrapVertexShader( const std::string& vertexPrefix, const std::string& vertexBody )
86 {
87   std::string vertexSource = vertexPrefix + customImageShaderWrappers.vertexShaderPrefix;
88
89   // Append the custom vertex shader code if supplied, otherwise append the default
90   if ( vertexBody.length() > 0 )
91   {
92     vertexSource.append( vertexBody );
93   }
94   else
95   {
96     vertexSource.append( customImageShaderWrappers.vertexShaderPostfix );
97   }
98
99   return vertexSource;
100 }
101
102 /**
103  * Helper to wrap the program with our default pre and postfix if needed
104  * @param[in] fragmentPrefix from application
105  * @param[in] fragmentBody from application
106  */
107 std::string WrapFragmentShader( const std::string& fragmentPrefix, const std::string& fragmentBody )
108 {
109   std::string fragmentSource = fragmentPrefix + customImageShaderWrappers.fragmentShaderPrefix;
110
111   // Append the custom fragment shader code if supplied, otherwise append the default
112   if ( fragmentBody.length() > 0 )
113   {
114     fragmentSource.append( fragmentBody );
115   }
116   else
117   {
118     fragmentSource.append( customImageShaderWrappers.fragmentShaderPostfix );
119   }
120
121   return fragmentSource;
122 }
123
124 std::string GetStringProperty(const std::string& field, const Property::Value& property)
125 {
126   std::string retval;
127   const Property::Map* map = property.GetMap();
128   if( map )
129   {
130     const Property::Value* value = map->Find( field );
131     if( value )
132     {
133       value->Get( retval );
134     }
135   }
136
137   return retval;
138 }
139
140 Dali::Shader::ShaderHints ConvertHints( Dali::ShaderEffect::GeometryHints hints)
141 {
142   int convertedHints = Dali::Shader::HINT_NONE;
143   if( hints & Dali::ShaderEffect::HINT_DEPTH_BUFFER )
144   {
145     convertedHints |= Dali::Shader::HINT_REQUIRES_SELF_DEPTH_TEST;
146   }
147   if( hints & Dali::ShaderEffect::HINT_BLENDING )
148   {
149     convertedHints |= Dali::Shader::HINT_OUTPUT_IS_TRANSPARENT;
150   }
151   if( !(hints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY) )
152   {
153     convertedHints |= Dali::Shader::HINT_MODIFIES_GEOMETRY;
154   }
155
156   return Dali::Shader::ShaderHints( convertedHints );
157 }
158
159 } // unnamed namespace
160
161 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
162 {
163   ShaderEffectPtr shaderEffect( new ShaderEffect( hints ) );
164   shaderEffect->RegisterObject();
165   return shaderEffect;
166 }
167
168 ShaderEffect::ShaderEffect( Dali::ShaderEffect::GeometryHints hints )
169 : mGridDensity( Dali::ShaderEffect::DEFAULT_GRID_DENSITY ),
170   mGeometryHints( hints )
171 {
172 }
173
174 ShaderEffect::~ShaderEffect()
175 {
176   // Guard to allow handle destruction after Core has been destroyed
177   UnregisterObject();
178 }
179
180 void ShaderEffect::SetEffectImage( Dali::Image image )
181 {
182   // if images are the same, do nothing
183   if ( mEffectImage == image )
184   {
185     return;
186   }
187
188   if ( mEffectImage && mConnectedActors.size() > 0 )
189   {
190     // unset previous image
191     GetImplementation( mEffectImage ).Disconnect();
192   }
193
194   // in case image is empty this will reset our image handle
195   mEffectImage = image;
196
197   if( image )
198   {
199     // tell image that we're using it
200     if (mConnectedActors.size() > 0)
201     {
202       GetImplementation( mEffectImage ).Connect();
203     }
204   }
205
206   //inform connected actors the image has been unset
207   for(std::vector< ActorPtr >::iterator it = mConnectedActors.begin(); it != mConnectedActors.end(); ++it )
208   {
209     ImageActor* imageActor = dynamic_cast< ImageActor* >( it->Get() );
210     if( imageActor )
211     {
212       imageActor->EffectImageUpdated();
213     }
214   }
215 }
216
217 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
218 {
219   // Register the property if it does not exist
220   mShader->RegisterProperty( name, value );
221 }
222
223 void ShaderEffect::SetPrograms( const string& vertexSource, const string& fragmentSource )
224 {
225   SetPrograms( "", "", vertexSource, fragmentSource );
226 }
227
228 void ShaderEffect::SetPrograms( const std::string& vertexPrefix, const std::string& fragmentPrefix,
229                                 const std::string& vertexSource, const std::string& fragmentSource )
230 {
231   mShader = Shader::New( WrapVertexShader( vertexPrefix, vertexSource ),
232                          WrapFragmentShader( fragmentPrefix, fragmentSource ),
233                          ConvertHints( mGeometryHints ) );
234 }
235
236 Vector2 ShaderEffect::GetGridSize( const Vector2& size )
237 {
238   Vector2 gridSize( 1.f, 1.f );
239
240   if( mGridDensity > 0 )
241   {
242     if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_X ) )
243     {
244       gridSize.x = ceil( size.width / mGridDensity );
245     }
246     if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_Y ) )
247     {
248       gridSize.y = ceil( size.height / mGridDensity );
249     }
250   }
251
252   return gridSize;
253 }
254
255 void ShaderEffect::Connect( ActorPtr actor )
256 {
257   if( !actor )
258   {
259     return;
260   }
261
262   std::vector< ActorPtr >::const_iterator it = std::find( mConnectedActors.begin(), mConnectedActors.end(), actor );
263   if( it == mConnectedActors.end() )
264   {
265     mConnectedActors.push_back( actor );
266   }
267
268   if( mEffectImage && mConnectedActors.size() == 1 )
269   {
270     GetImplementation( mEffectImage ).Connect();
271   }
272 }
273 void ShaderEffect::Disconnect( ActorPtr actor )
274 {
275   if( !actor )
276   {
277     return;
278   }
279
280   DALI_ASSERT_DEBUG(mConnectedActors.size() > 0);
281   std::vector< ActorPtr >::iterator match( std::remove( mConnectedActors.begin(), mConnectedActors.end(), actor ) );
282   mConnectedActors.erase( match, mConnectedActors.end() );
283
284   if (mEffectImage && mConnectedActors.size() == 0)
285   {
286      GetImplementation(mEffectImage).Disconnect();
287   }
288 }
289
290 unsigned int ShaderEffect::GetPropertyCount() const
291 {
292   return GetDefaultPropertyCount() + mShader->GetPropertyCount();
293 }
294
295 std::string ShaderEffect::GetPropertyName( Property::Index index ) const
296 {
297   if ( index < DEFAULT_PROPERTY_COUNT )
298   {
299     return GetDefaultPropertyName( index );
300   }
301   else
302   {
303     return mShader->GetPropertyName( index );
304   }
305 }
306
307 Property::Index ShaderEffect::GetPropertyIndex( const std::string& name ) const
308 {
309   Property::Index index = GetDefaultPropertyIndex( name );
310   if( index == Property::INVALID_INDEX )
311   {
312     return mShader->GetPropertyIndex( name );
313   }
314   else
315   {
316     return index;
317   }
318 }
319
320 bool ShaderEffect::IsPropertyWritable( Property::Index index ) const
321 {
322   if ( index < DEFAULT_PROPERTY_COUNT )
323   {
324     return IsDefaultPropertyWritable( index );
325   }
326   else
327   {
328     return mShader->IsPropertyWritable( index );
329   }
330 }
331
332 bool ShaderEffect::IsPropertyAnimatable( Property::Index index ) const
333 {
334   if ( index < DEFAULT_PROPERTY_COUNT )
335   {
336     return IsDefaultPropertyAnimatable( index );
337   }
338   else
339   {
340     return mShader->IsPropertyAnimatable( index );
341   }
342 }
343
344 bool ShaderEffect::IsPropertyAConstraintInput( Property::Index index ) const
345 {
346   if ( index < DEFAULT_PROPERTY_COUNT )
347   {
348     return IsDefaultPropertyAConstraintInput( index );
349   }
350   else
351   {
352     return mShader->IsPropertyAConstraintInput( index );
353   }
354 }
355
356 Property::Type ShaderEffect::GetPropertyType( Property::Index index ) const
357 {
358   if ( index < DEFAULT_PROPERTY_COUNT )
359   {
360     return GetDefaultPropertyType( index );
361   }
362   return mShader->GetPropertyType( index );
363 }
364
365 void ShaderEffect::SetProperty( Property::Index index, const Property::Value& propertyValue )
366 {
367   if ( index < DEFAULT_PROPERTY_COUNT )
368   {
369     SetDefaultProperty( index, propertyValue );
370   }
371   else
372   {
373     mShader->SetProperty( index, propertyValue );
374   }
375 }
376
377 Property::Value ShaderEffect::GetProperty( Property::Index index ) const
378 {
379   if ( index < DEFAULT_PROPERTY_COUNT )
380   {
381     return GetDefaultProperty( index );
382   }
383   return mShader->GetProperty( index );
384 }
385
386 void ShaderEffect::GetPropertyIndices( Property::IndexContainer& indices ) const
387 {
388   mShader->GetPropertyIndices( indices );
389   GetDefaultPropertyIndices( indices );
390 }
391
392 Property::Index ShaderEffect::RegisterProperty( const std::string& name, const Property::Value& propertyValue )
393 {
394   return mShader->RegisterProperty( name, propertyValue );
395 }
396
397 Property::Index ShaderEffect::RegisterProperty( const std::string& name, const Property::Value& propertyValue, Property::AccessMode accessMode )
398 {
399   return mShader->RegisterProperty( name, propertyValue, accessMode );
400 }
401
402 Dali::PropertyNotification ShaderEffect::AddPropertyNotification( Property::Index index,
403                                                                   int componentIndex,
404                                                                   const Dali::PropertyCondition& condition )
405 {
406   return mShader->AddPropertyNotification( index, componentIndex, condition );
407 }
408
409 void ShaderEffect::RemovePropertyNotification( Dali::PropertyNotification propertyNotification )
410 {
411   mShader->RemovePropertyNotification( propertyNotification );
412 }
413
414 void ShaderEffect::RemovePropertyNotifications()
415 {
416   mShader->RemovePropertyNotifications();
417 }
418
419 unsigned int ShaderEffect::GetDefaultPropertyCount() const
420 {
421   return DEFAULT_PROPERTY_COUNT;
422 }
423
424 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
425 {
426   indices.Reserve( indices.Size() + DEFAULT_PROPERTY_COUNT );
427
428   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
429   {
430     indices.PushBack( i );
431   }
432 }
433
434 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
435 {
436   if( index < DEFAULT_PROPERTY_COUNT )
437   {
438     return DEFAULT_PROPERTY_DETAILS[index].name;
439   }
440
441   return NULL;
442 }
443
444 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
445 {
446   Property::Index index = Property::INVALID_INDEX;
447
448   // Look for name in default properties
449   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
450   {
451     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
452     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
453     {
454       index = i;
455       break;
456     }
457   }
458
459   return index;
460 }
461
462 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
463 {
464   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
465 }
466
467 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
468 {
469   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
470 }
471
472 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
473 {
474   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
475 }
476
477 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
478 {
479   if( index < DEFAULT_PROPERTY_COUNT )
480   {
481     return DEFAULT_PROPERTY_DETAILS[index].type;
482   }
483
484   // index out of range...return Property::NONE
485   return Property::NONE;
486 }
487
488 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
489 {
490   switch ( index )
491   {
492     case Dali::ShaderEffect::Property::GRID_DENSITY:
493     {
494       propertyValue.Get( mGridDensity );
495       if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_X ) ||
496           ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_Y ) )
497       {
498         //inform all the connected actors
499         for(std::vector< ActorPtr >::iterator it = mConnectedActors.begin(); it != mConnectedActors.end(); ++it )
500         {
501           (*it)->RelayoutRequest();
502         }
503       }
504       break;
505     }
506
507     case Dali::ShaderEffect::Property::IMAGE:
508     {
509       Dali::Image img(Scripting::NewImage( propertyValue ));
510       if(img)
511       {
512         SetEffectImage( img );
513       }
514       else
515       {
516         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
517       }
518       break;
519     }
520
521     case Dali::ShaderEffect::Property::PROGRAM:
522     {
523       std::string vertexPrefix   = GetStringProperty("vertexPrefix",  propertyValue);
524       std::string fragmentPrefix = GetStringProperty("fragmentPrefix",  propertyValue);
525       std::string vertex         = GetStringProperty("vertex", propertyValue);
526       std::string fragment       = GetStringProperty("fragment", propertyValue);
527
528       SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
529       break;
530     }
531
532     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
533     {
534       mGeometryHints = Dali::ShaderEffect::HINT_NONE;
535       std::string s = propertyValue.Get<std::string>();
536       if(s == "HINT_NONE")
537       {
538         mGeometryHints = Dali::ShaderEffect::HINT_NONE;
539       }
540       else if(s == "HINT_GRID_X")
541       {
542         mGeometryHints = Dali::ShaderEffect::HINT_GRID_X;
543       }
544       else if(s == "HINT_GRID_Y")
545       {
546         mGeometryHints = Dali::ShaderEffect::HINT_GRID_Y;
547       }
548       else if(s == "HINT_GRID")
549       {
550         mGeometryHints = Dali::ShaderEffect::HINT_GRID;
551       }
552       else if(s == "HINT_DEPTH_BUFFER")
553       {
554         mGeometryHints = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
555       }
556       else if(s == "HINT_BLENDING")
557       {
558         mGeometryHints = Dali::ShaderEffect::HINT_BLENDING;
559       }
560       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
561       {
562         mGeometryHints = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
563       }
564       else
565       {
566         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
567       }
568       break;
569     }
570
571     default:
572     {
573       // nothing to do
574       break;
575     }
576   }
577 }
578
579 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
580 {
581   // none of our properties are readable so return empty
582   return Property::Value();
583 }
584
585 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
586 {
587   return mShader->GetSceneObject();
588 }
589
590 const SceneGraph::PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
591 {
592   return mShader->GetSceneObjectAnimatableProperty( index );
593 }
594
595 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
596 {
597   return mShader->GetSceneObjectInputProperty( index );
598 }
599
600 int ShaderEffect::GetPropertyComponentIndex( Property::Index index ) const
601 {
602   return mShader->GetPropertyComponentIndex( index );
603 }
604
605 } // namespace Internal
606
607 } // namespace Dali