8dbb2628981873ec0b051710389ef5f97a3dcdde
[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
23 // INTERNAL INCLUDES
24 #include <dali/public-api/math/vector2.h>
25 #include <dali/public-api/math/matrix.h>
26 #include <dali/public-api/math/matrix3.h>
27 #include <dali/public-api/shader-effects/shader-effect.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/public-api/scripting/scripting.h>
30 #include "dali-shaders.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/update/common/animatable-property.h>
35 #include <dali/internal/update/manager/update-manager.h>
36 #include <dali/internal/event/common/stage-impl.h>
37 #include <dali/internal/event/common/thread-local-storage.h>
38 #include <dali/internal/render/shaders/shader.h>
39 #include <dali/internal/update/common/property-owner-messages.h>
40 #include <dali/internal/update/animation/scene-graph-constraint-base.h>
41
42 using Dali::Internal::SceneGraph::UpdateManager;
43 using Dali::Internal::SceneGraph::UniformMeta;
44 using Dali::Internal::SceneGraph::Shader;
45 using Dali::Internal::SceneGraph::AnimatableProperty;
46 using Dali::Internal::SceneGraph::PropertyBase;
47 using Dali::Internal::SceneGraph::PropertyBase;
48 using Dali::Internal::SceneGraph::RenderQueue;
49 using std::string;
50
51 namespace Dali
52 {
53
54 const Property::Index ShaderEffect::GRID_DENSITY        = 0;
55 const Property::Index ShaderEffect::IMAGE               = 1;
56 const Property::Index ShaderEffect::PROGRAM             = 2;
57 const Property::Index ShaderEffect::GEOMETRY_HINTS      = 3;
58
59 namespace Internal
60 {
61
62 ShaderEffect::DefaultPropertyLookup* ShaderEffect::mDefaultPropertyLookup = NULL;
63
64 namespace
65 {
66
67 struct WrapperStrings
68 {
69   const char* vertexShaderPrefix;
70   const char* fragmentShaderPrefix;
71   const char* vertexShaderPostfix;
72   const char* fragmentShaderPostfix;
73 };
74
75 WrapperStrings customShaderWrappers [] =
76 {
77   {
78     CustomImagePrefixVertex, CustomImagePrefixFragment,
79     CustomImagePostfixVertex, CustomImagePostfixFragment
80   },
81   {
82     CustomTextDistanceFieldPrefixVertex, CustomTextDistanceFieldPrefixFragment,
83     CustomTextDistanceFieldPostfixVertex, CustomTextDistanceFieldPostfixFragment
84   },
85   {
86     CustomUntexturedMeshPrefixVertex, CustomUntexturedMeshPrefixFragment,
87     CustomUntexturedMeshPostfixVertex, CustomUntexturedMeshPostfixFragment
88   },
89   {
90     CustomTexturedMeshPrefixVertex, CustomTexturedMeshPrefixFragment,
91     CustomTexturedMeshPostfixVertex, CustomTexturedMeshPostfixFragment
92   }
93 };
94
95 /**
96  * Helper to wrap the program with our default pre and postfix if needed and then send it to update/render thread
97  * @param[in] effect of the shader
98  * @param[in] actualGeometryType of the shader
99  * @param[in] expectedGeometryType of the shader
100  * @param[in] vertexPrefix from application
101  * @param[in] fragmentPrefix from application
102  * @param[in] vertexBody from application
103  * @param[in] fragmentBody from application
104  * @param[in] modifiesGeometry based on flags and vertex shader
105  */
106 void WrapAndSetProgram( ShaderEffect& effect,
107                         GeometryType actualGeometryType, GeometryType expectedGeometryType,
108                         const std::string& vertexPrefix, const std::string& fragmentPrefix,
109                         const std::string& vertexBody, const std::string& fragmentBody,
110                         bool modifiesGeometry )
111 {
112   // if geometry type matches and there is some real data in the strings
113   if( ( actualGeometryType & expectedGeometryType )&&
114       ( ( vertexPrefix.length() > 0   )||
115         ( fragmentPrefix.length() > 0 )||
116         ( vertexBody.length() > 0     )||
117         ( fragmentBody.length() > 0   ) ) )
118   {
119     std::string vertexSource = vertexPrefix;
120     std::string fragmentSource = fragmentPrefix;
121
122     // create complete shader program strings for the given geometry type
123     unsigned int index = 0;
124     switch( expectedGeometryType )
125     {
126       case GEOMETRY_TYPE_IMAGE:
127       {
128         index = 0;
129         break;
130       }
131       case GEOMETRY_TYPE_TEXT:
132       {
133         index = 1;
134         break;
135       }
136       case GEOMETRY_TYPE_UNTEXTURED_MESH:
137       {
138         index = 2;
139         break;
140       }
141       case GEOMETRY_TYPE_TEXTURED_MESH:
142       {
143         index = 3;
144         break;
145       }
146       case GEOMETRY_TYPE_LAST:
147       {
148         DALI_ASSERT_DEBUG(0 && "Wrong geometry type");
149         break;
150       }
151     }
152
153     vertexSource += customShaderWrappers[index].vertexShaderPrefix;
154     // Append the custom vertex shader code if supplied, otherwise append the default
155     if ( vertexBody.length() > 0 )
156     {
157       vertexSource.append( vertexBody );
158     }
159     else
160     {
161       vertexSource.append( customShaderWrappers[index].vertexShaderPostfix );
162     }
163
164     fragmentSource += customShaderWrappers[index].fragmentShaderPrefix;
165     // Append the custom fragment shader code if supplied, otherwise append the default
166     if ( fragmentBody.length() > 0 )
167     {
168       fragmentSource.append( fragmentBody );
169     }
170     else
171     {
172       fragmentSource.append( customShaderWrappers[index].fragmentShaderPostfix );
173     }
174
175     effect.SendProgramMessage( expectedGeometryType, SHADER_SUBTYPE_ALL, vertexSource, fragmentSource, modifiesGeometry );
176   }
177 }
178
179 BaseHandle Create()
180 {
181   Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
182
183   return Dali::ShaderEffect(internal.Get());
184 }
185
186 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
187
188 const std::string DEFAULT_PROPERTY_NAMES[] =
189 {
190   "grid-density",
191   "image",
192   "program",
193   "geometry-hints",
194 };
195 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_NAMES ) / sizeof( std::string );
196
197 const Property::Type DEFAULT_PROPERTY_TYPES[DEFAULT_PROPERTY_COUNT] =
198 {
199   Property::FLOAT,    // "grid-density",
200   Property::MAP,      // "image",
201   Property::MAP,      // "program",
202   Property::INTEGER,  // "geometry-hints",
203 };
204
205 std::string GetShader(const std::string& field, const Property::Value& property)
206 {
207   std::string value;
208   if( property.HasKey(field) )
209   {
210     DALI_ASSERT_ALWAYS(property.GetValue(field).GetType() == Property::STRING && "Shader property is not a string" );
211
212     // we could also check here for an array of strings as convenience for json not having multi line strings
213     value = property.GetValue(field).Get<std::string>();
214   }
215
216   return value;
217 }
218
219 } // unnamed namespace
220
221 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
222 {
223   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
224   UpdateManager& updateManager = tls.GetUpdateManager();
225
226   ShaderEffectPtr shaderEffect( new ShaderEffect( updateManager, hints ) );
227   shaderEffect->RegisterObject();
228
229   return shaderEffect;
230 }
231
232 ShaderEffect::ShaderEffect( UpdateManager& updateManager, Dali::ShaderEffect::GeometryHints hints )
233 : mUpdateManager( updateManager ),
234   mConnectionCount (0),
235   mGeometryHints( hints )
236 {
237   mSceneObject = new Shader( hints );
238   DALI_ASSERT_DEBUG( NULL != mSceneObject );
239
240   // Transfer shader ownership to a scene message
241   AddShaderMessage( mUpdateManager, *mSceneObject );
242 }
243
244 ShaderEffect::~ShaderEffect()
245 {
246   // Guard to allow handle destruction after Core has been destroyed
247   if ( Stage::IsInstalled() )
248   {
249     // Remove scene-object using a message to the UpdateManager
250     if( mSceneObject )
251     {
252       RemoveShaderMessage( mUpdateManager, *mSceneObject );
253     }
254     UnregisterObject();
255   }
256 }
257
258 void ShaderEffect::SetEffectImage( Dali::Image image )
259 {
260   // if images are the same, do nothing
261   if (mImage == image)
262   {
263     return;
264   }
265
266   if (mImage && mConnectionCount > 0)
267   {
268     // unset previous image
269     GetImplementation(mImage).Disconnect();
270   }
271
272   // in case image is empty this will reset our image handle
273   mImage = image;
274
275   if (!image)
276   {
277     // mSceneShader can be in a separate thread; queue a setter message
278     SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, 0 );
279   }
280   else
281   {
282     // tell image that we're using it
283     if (mConnectionCount > 0)
284     {
285       GetImplementation(mImage).Connect();
286     }
287     // mSceneShader can be in a separate thread; queue a setter message
288     SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
289   }
290 }
291
292 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
293 {
294   // Register the property if it does not exist
295   Property::Index index = GetPropertyIndex( name );
296   if ( Property::INVALID_INDEX == index )
297   {
298     index = RegisterProperty( name, value );
299   }
300
301   SetProperty( index, value );
302
303   SetCoordinateTypeMessage( mUpdateManager.GetEventToUpdate(), *mCustomMetadata[index], uniformCoordinateType );
304 }
305
306 void ShaderEffect::AttachExtension( Dali::ShaderEffect::Extension *extension )
307 {
308   DALI_ASSERT_ALWAYS( extension != NULL && "Attaching uninitialized extension" );
309   mExtension = IntrusivePtr<Dali::ShaderEffect::Extension>( extension );
310 }
311
312 Dali::ShaderEffect::Extension& ShaderEffect::GetExtension()
313 {
314   DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
315   return *mExtension;
316 }
317
318 const Dali::ShaderEffect::Extension& ShaderEffect::GetExtension() const
319 {
320   DALI_ASSERT_ALWAYS( mExtension && "Getting uninitialized extension" );
321   return *mExtension;
322 }
323
324 void ShaderEffect::SetPrograms( GeometryType geometryType, const string& vertexSource, const string& fragmentSource )
325 {
326   SetPrograms( geometryType, "", "", vertexSource, fragmentSource );
327 }
328
329 void ShaderEffect::SetPrograms( GeometryType geometryType,
330                                 const std::string& vertexPrefix, const std::string& fragmentPrefix,
331                                 const std::string& vertexSource, const std::string& fragmentSource )
332 {
333   bool modifiesGeometry = true;
334   // check if the vertex shader is empty (means it cannot modify geometry)
335   if( (vertexPrefix.length() == 0 )&&( vertexSource.length() == 0 ) )
336   {
337     modifiesGeometry = false;
338   }
339   // check the hint second
340   if( (mGeometryHints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY ) != 0 )
341   {
342     modifiesGeometry = false;
343   }
344
345   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_IMAGE, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
346   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXT, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
347   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_TEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
348   WrapAndSetProgram( *this, geometryType, GEOMETRY_TYPE_UNTEXTURED_MESH, vertexPrefix, fragmentPrefix, vertexSource, fragmentSource, modifiesGeometry );
349 }
350
351 void ShaderEffect::SendProgramMessage( GeometryType geometryType, ShaderSubTypes subType,
352                                        const string& vertexSource, const string& fragmentSource,
353                                        bool modifiesGeometry )
354 {
355   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
356   ShaderFactory& shaderFactory = tls.GetShaderFactory();
357   size_t shaderHash;
358
359   ResourceTicketPtr ticket( shaderFactory.Load(vertexSource, fragmentSource, shaderHash) );
360
361   DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "ShaderEffect: SetProgram(geometryType %d subType:%d ticket.id:%d)\n", geometryType, subType, ticket->GetId() );
362
363   // Add shader program to scene-object using a message to the UpdateManager
364   SetShaderProgramMessage( mUpdateManager, *mSceneObject, geometryType, subType, ticket->GetId(), shaderHash, modifiesGeometry );
365
366   mTickets.push_back(ticket);       // add ticket to collection to keep it alive.
367 }
368
369 void ShaderEffect::Connect()
370 {
371   ++mConnectionCount;
372
373   if (mImage && mConnectionCount == 1)
374   {
375     GetImplementation(mImage).Connect();
376
377     // Image may have changed resource due to load/release policy. Ensure correct texture ID is set on scene graph object
378     SetTextureIdMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, GetImplementation(mImage).GetResourceId() );
379   }
380 }
381
382 void ShaderEffect::Disconnect()
383 {
384   DALI_ASSERT_DEBUG(mConnectionCount > 0);
385   --mConnectionCount;
386
387   if (mImage && mConnectionCount == 0)
388   {
389      GetImplementation(mImage).Disconnect();
390   }
391 }
392
393 bool ShaderEffect::IsSceneObjectRemovable() const
394 {
395   return false; // The Shader is not removed during this proxy's lifetime
396 }
397
398 unsigned int ShaderEffect::GetDefaultPropertyCount() const
399 {
400   return DEFAULT_PROPERTY_COUNT;
401 }
402
403 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
404 {
405   indices.reserve( DEFAULT_PROPERTY_COUNT );
406
407   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
408   {
409     indices.push_back( i );
410   }
411 }
412
413 const std::string& ShaderEffect::GetDefaultPropertyName(Property::Index index) const
414 {
415   if( index < DEFAULT_PROPERTY_COUNT )
416   {
417     return DEFAULT_PROPERTY_NAMES[index];
418   }
419   else
420   {
421     // index out of range..return empty string
422     static const std::string INVALID_PROPERTY_NAME;
423     return INVALID_PROPERTY_NAME;
424   }
425 }
426
427 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
428 {
429   Property::Index index = Property::INVALID_INDEX;
430
431   // Lazy initialization of static mDefaultPropertyLookup
432   if (!mDefaultPropertyLookup)
433   {
434     mDefaultPropertyLookup = new DefaultPropertyLookup();
435
436     for (int i=0; i<DEFAULT_PROPERTY_COUNT; ++i)
437     {
438       (*mDefaultPropertyLookup)[DEFAULT_PROPERTY_NAMES[i]] = i;
439     }
440   }
441   DALI_ASSERT_DEBUG( NULL != mDefaultPropertyLookup );
442
443   // Look for name in default properties
444   DefaultPropertyLookup::const_iterator result = mDefaultPropertyLookup->find( name );
445   if ( mDefaultPropertyLookup->end() != result )
446   {
447     index = result->second;
448   }
449
450   return index;
451 }
452
453 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
454 {
455   return true;
456 }
457
458 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
459 {
460   return false;
461 }
462
463 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
464 {
465   return false;
466 }
467
468 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
469 {
470   if( index < DEFAULT_PROPERTY_COUNT )
471   {
472     return DEFAULT_PROPERTY_TYPES[index];
473   }
474   else
475   {
476     // index out of range...return Property::NONE
477     return Property::NONE;
478   }
479 }
480
481 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
482 {
483   switch ( index )
484   {
485     case Dali::ShaderEffect::GRID_DENSITY:
486     {
487       SetGridDensityMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, propertyValue.Get<float>() );
488       break;
489     }
490
491     case Dali::ShaderEffect::IMAGE:
492     {
493       Dali::Image img(Scripting::NewImage( propertyValue ));
494       if(img)
495       {
496         SetEffectImage( img );
497       }
498       else
499       {
500         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
501       }
502       break;
503     }
504
505     case Dali::ShaderEffect::PROGRAM:
506     {
507       std::string vertexPrefix   = GetShader("vertex-prefix", propertyValue);
508       std::string fragmentPrefix = GetShader("fragment-prefix", propertyValue);
509       std::string vertex         = GetShader("vertex", propertyValue);
510       std::string fragment       = GetShader("fragment", propertyValue);
511
512       GeometryType geometryType      = GEOMETRY_TYPE_IMAGE;
513
514       if( propertyValue.HasKey("geometry-type") )
515       {
516         Property::Value geometryValue  = propertyValue.GetValue("geometry-type");
517         DALI_ASSERT_ALWAYS(geometryValue.GetType() == Property::STRING && "Geometry type is not a string" );
518
519         std::string s = geometryValue.Get<std::string>();
520         if(s == "GEOMETRY_TYPE_IMAGE")
521         {
522           geometryType  = GEOMETRY_TYPE_IMAGE;
523         }
524         else if (s == "GEOMETRY_TYPE_TEXT")
525         {
526           geometryType  = GEOMETRY_TYPE_TEXT;
527         }
528         else if( s == "GEOMETRY_TYPE_UNTEXTURED_MESH")
529         {
530           geometryType  = GEOMETRY_TYPE_UNTEXTURED_MESH;
531         }
532         else if( s == "GEOMETRY_TYPE_TEXTURED_MESH")
533         {
534           geometryType  = GEOMETRY_TYPE_TEXTURED_MESH;
535         }
536         else
537         {
538           DALI_ASSERT_ALWAYS(!"Geometry type unknown" );
539         }
540       }
541       SetPrograms( geometryType, vertexPrefix, vertex, fragmentPrefix, fragment );
542       break;
543     }
544
545     case Dali::ShaderEffect::GEOMETRY_HINTS:
546     {
547       Dali::ShaderEffect::GeometryHints hint = Dali::ShaderEffect::HINT_NONE;
548       Property::Value geometryHintsValue   = propertyValue.GetValue("geometry-hints");
549
550       std::string s = geometryHintsValue.Get<std::string>();
551       if(s == "HINT_NONE")
552       {
553         hint = Dali::ShaderEffect::HINT_NONE;
554       }
555       else if(s == "HINT_GRID_X")
556       {
557         hint = Dali::ShaderEffect::HINT_GRID_X;
558       }
559       else if(s == "HINT_GRID_Y")
560       {
561         hint = Dali::ShaderEffect::HINT_GRID_Y;
562       }
563       else if(s == "HINT_GRID")
564       {
565         hint = Dali::ShaderEffect::HINT_GRID;
566       }
567       else if(s == "HINT_DEPTH_BUFFER")
568       {
569         hint = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
570       }
571       else if(s == "HINT_BLENDING")
572       {
573         hint = Dali::ShaderEffect::HINT_BLENDING;
574       }
575       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
576       {
577         hint = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
578       }
579       else
580       {
581         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
582       }
583
584       SetHintsMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, hint );
585
586       break;
587     }
588
589     default:
590     {
591       DALI_ASSERT_ALWAYS(false && "ShaderEffect property enumeration out of range"); // should not come here
592       break;
593     }
594   }
595 }
596
597 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
598 {
599   // none of our properties are readable so return empty
600   return Property::Value();
601 }
602
603 void ShaderEffect::InstallSceneObjectProperty( PropertyBase& newProperty, const std::string& name, unsigned int index )
604 {
605   // 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)
606
607   // mSceneObject is being used in a separate thread; queue a message to add the property
608   InstallCustomPropertyMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, newProperty ); // Message takes ownership
609
610   // mSceneObject requires metadata for each custom property (uniform)
611   UniformMeta* meta = UniformMeta::New( name, newProperty, Dali::ShaderEffect::COORDINATE_TYPE_DEFAULT );
612   // mSceneObject is being used in a separate thread; queue a message to add the property
613   InstallUniformMetaMessage( mUpdateManager.GetEventToUpdate(), *mSceneObject, *meta ); // Message takes ownership
614
615   // Add entry to the metadata lookup
616   mCustomMetadata[index] = meta;
617 }
618
619 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
620 {
621   return mSceneObject;
622 }
623
624 const PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
625 {
626   CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
627
628   DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
629
630   DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
631
632   return dynamic_cast<const PropertyBase*>( entry->second.GetSceneGraphProperty() );
633 }
634
635 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
636 {
637   CustomPropertyLookup::const_iterator entry = GetCustomPropertyLookup().find( index );
638
639   DALI_ASSERT_ALWAYS( GetCustomPropertyLookup().end() != entry && "Property index is invalid" );
640
641   DALI_ASSERT_ALWAYS( entry->second.IsAnimatable() && "shader effect has only animatable properties" );
642
643   return entry->second.GetSceneGraphProperty();
644 }
645
646 } // namespace Internal
647
648 } // namespace Dali