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