a038ef647a52471f61eadb2dee1b948e86bb1382
[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/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", 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 customImageShaderWrappers =
86 {
87   CustomImagePrefixVertex, CustomImagePrefixFragment,
88   CustomImagePostfixVertex, CustomImagePostfixFragment
89 };
90
91 /**
92  * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
93  * @param[in] effect of the shader
94  * @param[in] vertexPrefix from application
95  * @param[in] fragmentPrefix from application
96  * @param[in] vertexBody from application
97  * @param[in] fragmentBody from application
98  * @param[in] modifiesGeometry based on flags and vertex shader
99  */
100 void WrapAndSetProgram( Internal::ShaderEffect& effect,
101                         const std::string& vertexPrefix, const std::string& fragmentPrefix,
102                         const std::string& vertexBody, const std::string& fragmentBody,
103                         bool modifiesGeometry )
104 {
105   // if there is some real data in the strings
106   if( ( vertexPrefix.length()   > 0 ) ||
107       ( fragmentPrefix.length() > 0 ) ||
108       ( vertexBody.length()     > 0 ) ||
109       ( fragmentBody.length()   > 0 ) )
110   {
111     std::string vertexSource = vertexPrefix;
112     std::string fragmentSource = fragmentPrefix;
113
114     // Create complete shader program strings:
115
116     vertexSource += customImageShaderWrappers.vertexShaderPrefix;
117
118     // Append the custom vertex shader code if supplied, otherwise append the default
119     if ( vertexBody.length() > 0 )
120     {
121       vertexSource.append( vertexBody );
122     }
123     else
124     {
125       vertexSource.append( customImageShaderWrappers.vertexShaderPostfix );
126     }
127
128     fragmentSource += customImageShaderWrappers.fragmentShaderPrefix;
129
130     // Append the custom fragment shader code if supplied, otherwise append the default
131     if ( fragmentBody.length() > 0 )
132     {
133       fragmentSource.append( fragmentBody );
134     }
135     else
136     {
137       fragmentSource.append( customImageShaderWrappers.fragmentShaderPostfix );
138     }
139
140     effect.SendProgramMessage( vertexSource, fragmentSource, modifiesGeometry );
141   }
142 }
143
144 std::string GetShader(const std::string& field, const Property::Value& property)
145 {
146   std::string retval;
147   const Property::Map* map = property.GetMap();
148   if( map )
149   {
150     const Property::Value* value = map->Find( field );
151     if( value )
152     {
153       value->Get( retval );
154     }
155   }
156
157   return retval;
158 }
159
160 } // unnamed namespace
161
162 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
163 {
164   Stage* stage = Stage::GetCurrent();
165
166   if( stage )
167   {
168     ShaderEffectPtr shaderEffect( new ShaderEffect( *stage, hints ) );
169     shaderEffect->RegisterObject();
170     return shaderEffect;
171   }
172   else
173   {
174     return NULL;
175   }
176 }
177
178 ShaderEffect::ShaderEffect( EventThreadServices& eventThreadServices, Dali::ShaderEffect::GeometryHints hints )
179 : mEventThreadServices( eventThreadServices ),
180   mConnectionCount (0),
181   mGeometryHints( hints )
182 {
183   mSceneObject = new SceneGraph::Shader( hints );
184   DALI_ASSERT_DEBUG( NULL != mSceneObject );
185
186   // Transfer shader ownership to a scene message
187   AddShaderMessage( eventThreadServices.GetUpdateManager(), *mSceneObject );
188 }
189
190 ShaderEffect::~ShaderEffect()
191 {
192   // Guard to allow handle destruction after Core has been destroyed
193   if ( Stage::IsInstalled() )
194   {
195     // Remove scene-object using a message to the UpdateManager
196     if( mSceneObject )
197     {
198       RemoveShaderMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject );
199     }
200     UnregisterObject();
201   }
202 }
203
204 void ShaderEffect::SetEffectImage( Dali::Image image )
205 {
206   // if images are the same, do nothing
207   if (mImage == image)
208   {
209     return;
210   }
211
212   if (mImage && mConnectionCount > 0)
213   {
214     // unset previous image
215     GetImplementation(mImage).Disconnect();
216   }
217
218   // in case image is empty this will reset our image handle
219   mImage = image;
220
221   if (!image)
222   {
223     // mSceneShader can be in a separate thread; queue a setter message
224     SetTextureIdMessage( mEventThreadServices, *mSceneObject, 0 );
225   }
226   else
227   {
228     // tell image that we're using it
229     if (mConnectionCount > 0)
230     {
231       GetImplementation(mImage).Connect();
232     }
233     // mSceneShader can be in a separate thread; queue a setter message
234     SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
235   }
236 }
237
238 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
239 {
240   // Register the property if it does not exist
241   Property::Index index = GetPropertyIndex( name );
242   if ( Property::INVALID_INDEX == index )
243   {
244     index = RegisterProperty( name, value );
245   }
246
247   SetProperty( index, value );
248
249   // RegisterProperty guarantees a positive value as index
250   DALI_ASSERT_DEBUG( static_cast<unsigned int>(index) >= CustomPropertyStartIndex() );
251   unsigned int metaIndex = index - CustomPropertyStartIndex();
252   // check if there's space in cache
253   if( mCoordinateTypes.Count() < (metaIndex + 1) )
254   {
255     mCoordinateTypes.Resize( metaIndex + 1 );
256   }
257   // only send message if the value is different than current, initial value is COORDINATE_TYPE_DEFAULT (0)
258   if( uniformCoordinateType != mCoordinateTypes[ metaIndex ] )
259   {
260     mCoordinateTypes[ metaIndex ] = uniformCoordinateType;
261     SetCoordinateTypeMessage( mEventThreadServices, *mSceneObject, metaIndex, uniformCoordinateType );
262   }
263 }
264
265 void ShaderEffect::SetPrograms( const string& vertexSource, const string& fragmentSource )
266 {
267   SetPrograms( "", "", vertexSource, fragmentSource );
268 }
269
270 void ShaderEffect::SetPrograms( const std::string& vertexPrefix, const std::string& fragmentPrefix,
271                                 const std::string& vertexSource, const std::string& fragmentSource )
272 {
273   bool modifiesGeometry = true;
274   // check if the vertex shader is empty (means it cannot modify geometry)
275   if( (vertexPrefix.length() == 0) && (vertexSource.length() == 0) )
276   {
277     modifiesGeometry = false;
278   }
279   // check the hint second
280   if( ( mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
281   {
282     modifiesGeometry = false;
283   }
284
285   WrapAndSetProgram( *this, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
286 }
287
288 void ShaderEffect::SendProgramMessage( const string& vertexSource, const string& fragmentSource,
289                                        bool modifiesGeometry )
290 {
291   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
292   ShaderFactory& shaderFactory = tls.GetShaderFactory();
293   size_t shaderHash;
294
295   Internal::ShaderDataPtr shaderData = shaderFactory.Load( vertexSource, fragmentSource, shaderHash );
296   DALI_ASSERT_DEBUG( shaderHash != 0U );
297
298   // Add shader program to scene-object using a message to the UpdateManager
299   SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, shaderData, modifiesGeometry );
300 }
301
302 void ShaderEffect::Connect()
303 {
304   ++mConnectionCount;
305
306   if (mImage && mConnectionCount == 1)
307   {
308     GetImplementation(mImage).Connect();
309
310     // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
311     SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
312   }
313 }
314
315 void ShaderEffect::Disconnect()
316 {
317   DALI_ASSERT_DEBUG(mConnectionCount > 0);
318   --mConnectionCount;
319
320   if (mImage && mConnectionCount == 0)
321   {
322      GetImplementation(mImage).Disconnect();
323   }
324 }
325
326 unsigned int ShaderEffect::GetDefaultPropertyCount() const
327 {
328   return DEFAULT_PROPERTY_COUNT;
329 }
330
331 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
332 {
333   indices.Reserve( DEFAULT_PROPERTY_COUNT );
334
335   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
336   {
337     indices.PushBack( i );
338   }
339 }
340
341 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
342 {
343   if( index < DEFAULT_PROPERTY_COUNT )
344   {
345     return DEFAULT_PROPERTY_DETAILS[index].name;
346   }
347
348   return NULL;
349 }
350
351 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
352 {
353   Property::Index index = Property::INVALID_INDEX;
354
355   // Look for name in default properties
356   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
357   {
358     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
359     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
360     {
361       index = i;
362       break;
363     }
364   }
365
366   return index;
367 }
368
369 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
370 {
371   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
372 }
373
374 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
375 {
376   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
377 }
378
379 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
380 {
381   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
382 }
383
384 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
385 {
386   if( index < DEFAULT_PROPERTY_COUNT )
387   {
388     return DEFAULT_PROPERTY_DETAILS[index].type;
389   }
390
391   // index out of range...return Property::NONE
392   return Property::NONE;
393 }
394
395 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
396 {
397   switch ( index )
398   {
399     case Dali::ShaderEffect::Property::GRID_DENSITY:
400     {
401       SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
402       break;
403     }
404
405     case Dali::ShaderEffect::Property::IMAGE:
406     {
407       Dali::Image img(Scripting::NewImage( propertyValue ));
408       if(img)
409       {
410         SetEffectImage( img );
411       }
412       else
413       {
414         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
415       }
416       break;
417     }
418
419     case Dali::ShaderEffect::Property::PROGRAM:
420     {
421       std::string vertexPrefix   = GetShader("vertex-prefix", propertyValue);
422       std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
423       std::string vertex         = GetShader("vertex", propertyValue);
424       std::string fragment       = GetShader("fragment", propertyValue);
425
426       SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
427       break;
428     }
429
430     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
431     {
432       Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
433       std::string s = propertyValue.Get<std::string>();
434       if(s == "HINT_NONE")
435       {
436         hint = Dali::ShaderEffect::HINT_NONE;
437       }
438       else if(s == "HINT_GRID_X")
439       {
440         hint = Dali::ShaderEffect::HINT_GRID_X;
441       }
442       else if(s == "HINT_GRID_Y")
443       {
444         hint = Dali::ShaderEffect::HINT_GRID_Y;
445       }
446       else if(s == "HINT_GRID")
447       {
448         hint = Dali::ShaderEffect::HINT_GRID;
449       }
450       else if(s == "HINT_DEPTH_BUFFER")
451       {
452         hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
453       }
454       else if(s == "HINT_BLENDING")
455       {
456         hint = Dali::ShaderEffect::HINT_BLENDING;
457       }
458       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
459       {
460         hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
461       }
462       else
463       {
464         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
465       }
466
467       SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
468
469       break;
470     }
471
472     default:
473     {
474       // nothing to do
475       break;
476     }
477   }
478 }
479
480 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
481 {
482   // none of our properties are readable so return empty
483   return Property::Value();
484 }
485
486 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
487 {
488   // 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)
489
490   // mSceneObject requires metadata for each custom property (uniform)
491   UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
492   // mSceneObject is being used in a separate thread; queue a message to add the property
493   InstallUniformMetaMessage( mEventThreadServices, *mSceneObject, *meta ); // Message takes ownership
494 }
495
496 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
497 {
498   return mSceneObject;
499 }
500
501 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
502 {
503   PropertyMetadata* property = index >= PROPERTY_CUSTOM_START_INDEX ? static_cast<PropertyMetadata*>(FindCustomProperty( index )) : static_cast<PropertyMetadata*>(FindAnimatableProperty( index ));
504   DALI_ASSERT_ALWAYS( property && "Property index is invalid" );
505   return property->GetSceneGraphProperty();
506 }
507
508 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
509 {
510   return GetSceneObjectAnimatableProperty( index );
511 }
512
513 } // namespace Internal
514
515 } // namespace Dali