0d4dae594dfca71ae72e5383f7c22f8b42856799
[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   ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
296
297   DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(ticket.id:%d)\n", ticket->GetId() );
298
299   // Add shader program to scene-object using a message to the UpdateManager
300   SetShaderProgramMessage( mEventThreadServices.GetUpdateManager(), *mSceneObject, ticket->GetId(), shaderHash, modifiesGeometry );
301
302   mTickets.push_back(ticket);       // add ticket to collection to keep it alive.
303 }
304
305 void ShaderEffect::Connect()
306 {
307   ++mConnectionCount;
308
309   if (mImage && mConnectionCount == 1)
310   {
311     GetImplementation(mImage).Connect();
312
313     // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
314     SetTextureIdMessage( mEventThreadServices, *mSceneObject, GetImplementation(mImage).GetResourceId() );
315   }
316 }
317
318 void ShaderEffect::Disconnect()
319 {
320   DALI_ASSERT_DEBUG(mConnectionCount > 0);
321   --mConnectionCount;
322
323   if (mImage && mConnectionCount == 0)
324   {
325      GetImplementation(mImage).Disconnect();
326   }
327 }
328
329 unsigned int ShaderEffect::GetDefaultPropertyCount() const
330 {
331   return DEFAULT_PROPERTY_COUNT;
332 }
333
334 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
335 {
336   indices.Reserve( DEFAULT_PROPERTY_COUNT );
337
338   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
339   {
340     indices.PushBack( i );
341   }
342 }
343
344 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
345 {
346   if( index < DEFAULT_PROPERTY_COUNT )
347   {
348     return DEFAULT_PROPERTY_DETAILS[index].name;
349   }
350
351   return NULL;
352 }
353
354 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
355 {
356   Property::Index index = Property::INVALID_INDEX;
357
358   // Look for name in default properties
359   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
360   {
361     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
362     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
363     {
364       index = i;
365       break;
366     }
367   }
368
369   return index;
370 }
371
372 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
373 {
374   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
375 }
376
377 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
378 {
379   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
380 }
381
382 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
383 {
384   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
385 }
386
387 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
388 {
389   if( index < DEFAULT_PROPERTY_COUNT )
390   {
391     return DEFAULT_PROPERTY_DETAILS[index].type;
392   }
393
394   // index out of range...return Property::NONE
395   return Property::NONE;
396 }
397
398 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
399 {
400   switch ( index )
401   {
402     case Dali::ShaderEffect::Property::GRID_DENSITY:
403     {
404       SetGridDensityMessage( mEventThreadServices, *mSceneObject, propertyValue.Get<float>() );
405       break;
406     }
407
408     case Dali::ShaderEffect::Property::IMAGE:
409     {
410       Dali::Image img(Scripting::NewImage( propertyValue ));
411       if(img)
412       {
413         SetEffectImage( img );
414       }
415       else
416       {
417         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
418       }
419       break;
420     }
421
422     case Dali::ShaderEffect::Property::PROGRAM:
423     {
424       std::string vertexPrefix   = GetShader("vertex-prefix", propertyValue);
425       std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
426       std::string vertex         = GetShader("vertex", propertyValue);
427       std::string fragment       = GetShader("fragment", propertyValue);
428
429       SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
430       break;
431     }
432
433     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
434     {
435       Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
436       std::string s = propertyValue.Get<std::string>();
437       if(s == "HINT_NONE")
438       {
439         hint = Dali::ShaderEffect::HINT_NONE;
440       }
441       else if(s == "HINT_GRID_X")
442       {
443         hint = Dali::ShaderEffect::HINT_GRID_X;
444       }
445       else if(s == "HINT_GRID_Y")
446       {
447         hint = Dali::ShaderEffect::HINT_GRID_Y;
448       }
449       else if(s == "HINT_GRID")
450       {
451         hint = Dali::ShaderEffect::HINT_GRID;
452       }
453       else if(s == "HINT_DEPTH_BUFFER")
454       {
455         hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
456       }
457       else if(s == "HINT_BLENDING")
458       {
459         hint = Dali::ShaderEffect::HINT_BLENDING;
460       }
461       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
462       {
463         hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
464       }
465       else
466       {
467         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
468       }
469
470       SetHintsMessage( mEventThreadServices, *mSceneObject, hint );
471
472       break;
473     }
474
475     default:
476     {
477       // nothing to do
478       break;
479     }
480   }
481 }
482
483 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
484 {
485   // none of our properties are readable so return empty
486   return Property::Value();
487 }
488
489 void ShaderEffect::NotifyScenePropertyInstalled( const SceneGraph::PropertyBase& newProperty, const std::string& name, unsigned int index ) const
490 {
491   // 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)
492
493   // mSceneObject requires metadata for each custom property (uniform)
494   UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
495   // mSceneObject is being used in a separate thread; queue a message to add the property
496   InstallUniformMetaMessage( mEventThreadServices, *mSceneObject, *meta ); // Message takes ownership
497 }
498
499 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
500 {
501   return mSceneObject;
502 }
503
504 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
505 {
506   PropertyMetadata* property = index >= PROPERTY_CUSTOM_START_INDEX ? static_cast<PropertyMetadata*>(FindCustomProperty( index )) : static_cast<PropertyMetadata*>(FindAnimatableProperty( index ));
507   DALI_ASSERT_ALWAYS( property && "Property index is invalid" );
508   return property->GetSceneGraphProperty();
509 }
510
511 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
512 {
513   return GetSceneObjectAnimatableProperty( index );
514 }
515
516 } // namespace Internal
517
518 } // namespace Dali