[Tizen] Make SceneView FBO multisampling + Sync utc harness
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / scene-view / scene-view-impl.cpp
1 /*
2  * Copyright (c) 2022 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-scene3d/internal/controls/scene-view/scene-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali-toolkit/dali-toolkit.h>
23 #include <dali-toolkit/devel-api/controls/control-devel.h>
24 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
25 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
26 #include <dali-toolkit/public-api/image-loader/image-url.h>
27 #include <dali-toolkit/public-api/image-loader/image.h>
28 #include <dali/devel-api/actors/camera-actor-devel.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/devel-api/common/stage.h>
31 #include <dali/integration-api/adaptor-framework/adaptor.h>
32 #include <dali/devel-api/rendering/frame-buffer-devel.h>
33 #include <dali/integration-api/debug.h>
34 #include <dali/public-api/object/type-registry-helper.h>
35 #include <dali/public-api/object/type-registry.h>
36
37 // INTERNAL INCLUDES
38 #include <dali-scene3d/internal/controls/model/model-impl.h>
39 #include <dali-scene3d/public-api/loader/cube-map-loader.h>
40
41 using namespace Dali;
42
43 namespace Dali
44 {
45 namespace Scene3D
46 {
47 namespace Internal
48 {
49 namespace
50 {
51 BaseHandle Create()
52 {
53   return Scene3D::SceneView::New();
54 }
55
56 // Setup properties, signals and actions using the type-registry.
57 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::SceneView, Toolkit::Control, Create);
58 DALI_TYPE_REGISTRATION_END()
59
60 Property::Index   RENDERING_BUFFER    = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
61 constexpr int32_t DEFAULT_ORIENTATION = 0;
62
63 constexpr uint8_t DEFAULT_FRAME_BUFFER_MULTI_SAMPLING_LEVEL = 4u;
64 } // anonymous namespace
65
66 SceneView::SceneView()
67 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
68   mWindowOrientation(DEFAULT_ORIENTATION),
69   mIblLoadedCallback(nullptr)
70 {
71 }
72
73 SceneView::~SceneView()
74 {
75   if(mIblLoadedCallback && Adaptor::IsAvailable())
76   {
77     // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
78     Adaptor::Get().RemoveIdle(mIblLoadedCallback);
79   }
80
81   if(Stage::IsInstalled())
82   {
83     if(mRenderTask)
84     {
85       RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
86       taskList.RemoveTask(mRenderTask);
87       mRenderTask.Reset();
88     }
89   }
90 }
91
92 Dali::Scene3D::SceneView SceneView::New()
93 {
94   SceneView* impl = new SceneView();
95
96   Dali::Scene3D::SceneView handle = Dali::Scene3D::SceneView(*impl);
97
98   // Second-phase init of the implementation
99   // This can only be done after the CustomActor connection has been made...
100   impl->Initialize();
101
102   return handle;
103 }
104
105 void SceneView::AddCamera(CameraActor camera)
106 {
107   if(camera)
108   {
109     if(mCameras.empty())
110     {
111       UpdateCamera(camera);
112     }
113     mCameras.push_back(camera);
114   }
115 }
116
117 void SceneView::RemoveCamera(CameraActor camera)
118 {
119   if(camera == mDefaultCamera)
120   {
121     DALI_LOG_ERROR("Default Camera cannot removed.\n");
122     return;
123   }
124
125   if(camera)
126   {
127     for(uint32_t i = 0; i < mCameras.size(); ++i)
128     {
129       if(mCameras[i] == camera)
130       {
131         mCameras.erase(mCameras.begin() + i);
132         break;
133       }
134     }
135
136     if(mSelectedCamera == camera)
137     {
138       CameraActor newCurrentCamera = *mCameras.begin();
139       UpdateCamera(newCurrentCamera);
140     }
141   }
142 }
143
144 uint32_t SceneView::GetCameraCount() const
145 {
146   return mCameras.size();
147 }
148
149 CameraActor SceneView::GetSelectedCamera() const
150 {
151   return mSelectedCamera;
152 }
153
154 CameraActor SceneView::GetCamera(uint32_t index) const
155 {
156   if(index < mCameras.size())
157   {
158     return mCameras[index];
159   }
160   DALI_LOG_ERROR("Input index is out of bounds\n");
161   return CameraActor();
162 }
163
164 CameraActor SceneView::GetCamera(const std::string& name) const
165 {
166   CameraActor returnCamera;
167   for(auto&& camera : mCameras)
168   {
169     if(camera.GetProperty<std::string>(Actor::Property::NAME) == name)
170     {
171       returnCamera = camera;
172       break;
173     }
174   }
175   return returnCamera;
176 }
177
178 void SceneView::SelectCamera(uint32_t index)
179 {
180   UpdateCamera(GetCamera(index));
181 }
182
183 void SceneView::SelectCamera(const std::string& name)
184 {
185   UpdateCamera(GetCamera(name));
186 }
187
188 void SceneView::RegisterSceneItem(Scene3D::Internal::ImageBasedLightObserver* item)
189 {
190   if(item)
191   {
192     item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor);
193     mItems.push_back(item);
194   }
195 }
196
197 void SceneView::UnregisterSceneItem(Scene3D::Internal::ImageBasedLightObserver* item)
198 {
199   if(item)
200   {
201     for(uint32_t i = 0; i < mItems.size(); ++i)
202     {
203       if(mItems[i] == item)
204       {
205         mItems.erase(mItems.begin() + i);
206         break;
207       }
208     }
209   }
210 }
211
212 void SceneView::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
213 {
214   // Request asynchronous model loading
215   if(!mIblLoadedCallback)
216   {
217     mIBLResourceReady = false;
218     mDiffuseIblUrl    = diffuseUrl;
219     mSpecularIblUrl   = specularUrl;
220     mIblScaleFactor   = scaleFactor;
221     // The callback manager takes the ownership of the callback object.
222     mIblLoadedCallback = MakeCallback(this, &SceneView::OnLoadComplete);
223     Adaptor::Get().AddIdle(mIblLoadedCallback, false);
224   }
225 }
226
227 void SceneView::SetImageBasedLightScaleFactor(float scaleFactor)
228 {
229   mIblScaleFactor = scaleFactor;
230   for(auto&& item : mItems)
231   {
232     if(item)
233     {
234       item->NotifyImageBasedLightScaleFactor(scaleFactor);
235     }
236   }
237 }
238
239 float SceneView::GetImageBasedLightScaleFactor() const
240 {
241   return mIblScaleFactor;
242 }
243
244 void SceneView::UseFramebuffer(bool useFramebuffer)
245 {
246   if(mUseFrameBuffer != useFramebuffer)
247   {
248     mUseFrameBuffer = useFramebuffer;
249     UpdateRenderTask();
250   }
251 }
252
253 bool SceneView::IsUsingFramebuffer() const
254 {
255   return mUseFrameBuffer;
256 }
257
258 ///////////////////////////////////////////////////////////
259 //
260 // Private methods
261 //
262
263 void SceneView::OnSceneConnection(int depth)
264 {
265   UpdateRenderTask();
266
267   Window window = DevelWindow::Get(Self());
268   if(window)
269   {
270     window.ResizeSignal().Connect(this, &SceneView::OnWindowResized);
271   }
272
273   Control::OnSceneConnection(depth);
274 }
275
276 void SceneView::OnSceneDisconnection()
277 {
278   mItems.clear();
279
280   Window window = DevelWindow::Get(Self());
281   if(window)
282   {
283     window.ResizeSignal().Disconnect(this, &SceneView::OnWindowResized);
284   }
285
286   Control::OnSceneDisconnection();
287 }
288
289 void SceneView::OnInitialize()
290 {
291   Actor self = Self();
292   mRootLayer = Layer::New();
293   mRootLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
294   mRootLayer.SetProperty(Layer::Property::DEPTH_TEST, true);
295   // The models in the SceneView should be have independent coordinate with DALi default coordinate.
296   mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_POSITION, false);
297   mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_ORIENTATION, false);
298   mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_SCALE, false);
299   self.Add(mRootLayer);
300
301   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
302   mRenderTask             = taskList.CreateTask();
303   mRenderTask.SetSourceActor(mRootLayer);
304   mRenderTask.SetExclusive(true);
305   mRenderTask.SetInputEnabled(true);
306   mRenderTask.SetCullMode(false);
307   mRenderTask.SetScreenToFrameBufferMappingActor(Self());
308
309   mDefaultCamera = Dali::CameraActor::New();
310   mDefaultCamera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
311   mDefaultCamera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
312   mDefaultCamera.SetNearClippingPlane(1.0f);
313   AddCamera(mDefaultCamera);
314   UpdateCamera(mDefaultCamera);
315 }
316
317 void SceneView::OnChildAdd(Actor& child)
318 {
319   if(child != mRootLayer)
320   {
321     mRootLayer.Add(child);
322   }
323   Control::OnChildAdd(child);
324 }
325
326 void SceneView::OnChildRemove(Actor& child)
327 {
328   mRootLayer.Remove(child);
329   Control::OnChildRemove(child);
330 }
331
332 float SceneView::GetHeightForWidth(float width)
333 {
334   Extents padding;
335   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
336   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
337 }
338
339 float SceneView::GetWidthForHeight(float height)
340 {
341   Extents padding;
342   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
343   return Control::GetWidthForHeight(height) + padding.start + padding.end;
344 }
345
346 void SceneView::OnRelayout(const Vector2& size, RelayoutContainer& container)
347 {
348   Control::OnRelayout(size, container);
349   // Change canvas size of camera actor.
350   UpdateRenderTask();
351 }
352
353 bool SceneView::IsResourceReady() const
354 {
355   return mIBLResourceReady;
356 }
357
358 void SceneView::UpdateCamera(CameraActor camera)
359 {
360   if(camera)
361   {
362     if(mSelectedCamera && mSelectedCamera.GetParent())
363     {
364       mSelectedCamera.Unparent();
365     }
366     mRootLayer.Add(camera);
367   }
368
369   mSelectedCamera = camera;
370   UpdateRenderTask();
371 }
372
373 void SceneView::UpdateRenderTask()
374 {
375   if(mRenderTask)
376   {
377     if(mSelectedCamera != mRenderTask.GetCameraActor())
378     {
379       mRenderTask.SetCameraActor(mSelectedCamera);
380     }
381
382     Vector3     size        = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
383     const float aspectRatio = size.width / size.height;
384     mSelectedCamera.SetAspectRatio(aspectRatio);
385     const bool projectionVertical = mSelectedCamera.GetProperty<int>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION) == Dali::DevelCameraActor::VERTICAL;
386
387     // if projectionVertical, Top / Bottom is +-ve to keep consistency with orthographic values
388     // else, Left / Right is +-ve to keep consistency with orthographic values
389     const float orthographicSize = DALI_LIKELY(projectionVertical) ? mSelectedCamera[Dali::CameraActor::Property::TOP_PLANE_DISTANCE] : mSelectedCamera[Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE];
390     const float halfHeight       = DALI_LIKELY(projectionVertical) ? orthographicSize : orthographicSize / aspectRatio;
391     const float halfWidth        = DALI_LIKELY(projectionVertical) ? orthographicSize * aspectRatio : orthographicSize;
392
393     mSelectedCamera[Dali::CameraActor::Property::LEFT_PLANE_DISTANCE]   = -halfWidth;
394     mSelectedCamera[Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE]  = halfWidth;
395     mSelectedCamera[Dali::CameraActor::Property::TOP_PLANE_DISTANCE]    = halfHeight;
396     mSelectedCamera[Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE] = -halfHeight;
397
398     if(mUseFrameBuffer)
399     {
400       Dali::FrameBuffer currentFrameBuffer = mRenderTask.GetFrameBuffer();
401       if(!currentFrameBuffer ||
402          currentFrameBuffer.GetColorTexture().GetWidth() != size.width ||
403          currentFrameBuffer.GetColorTexture().GetHeight() != size.height)
404       {
405         mRenderTask.ResetViewportGuideActor();
406         mRenderTask.SetViewport(Dali::Viewport(Vector4::ZERO));
407
408         // create offscreen buffer of new size to render our child actors to
409         mTexture     = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(size.width), unsigned(size.height));
410         mFrameBuffer = FrameBuffer::New(size.width, size.height, FrameBuffer::Attachment::DEPTH_STENCIL);
411         mFrameBuffer.AttachColorTexture(mTexture);
412         DevelFrameBuffer::SetMultiSamplingLevel(mFrameBuffer, DEFAULT_FRAME_BUFFER_MULTI_SAMPLING_LEVEL);
413         Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(mFrameBuffer, 0u);
414
415         Property::Map imagePropertyMap;
416         imagePropertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
417         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, imageUrl.GetUrl());
418         // To flip rendered scene without CameraActor::SetInvertYAxis() to avoid backface culling.
419         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, Vector4(0.0f, 1.0f, 1.0f, -1.0f));
420         mVisual = Toolkit::VisualFactory::Get().CreateVisual(imagePropertyMap);
421
422         Toolkit::DevelControl::RegisterVisual(*this, RENDERING_BUFFER, mVisual);
423
424         mRenderTask.SetFrameBuffer(mFrameBuffer);
425         mRenderTask.SetClearEnabled(true);
426         mRenderTask.SetClearColor(Color::TRANSPARENT);
427       }
428     }
429     else
430     {
431       mRenderTask.SetViewportGuideActor(Self());
432       if(mRenderTask.GetFrameBuffer())
433       {
434         FrameBuffer framebuffer;
435         mRenderTask.SetFrameBuffer(framebuffer);
436         mRenderTask.SetClearEnabled(false);
437
438         Toolkit::DevelControl::UnregisterVisual(*this, RENDERING_BUFFER);
439
440         mVisual.Reset();
441         mFrameBuffer.Reset();
442         mTexture.Reset();
443       }
444     }
445
446     RotateCamera();
447   }
448 }
449
450 void SceneView::OnWindowResized(Window window, Window::WindowSize size)
451 {
452   mWindowOrientation = DevelWindow::GetPhysicalOrientation(window);
453   RotateCamera();
454 }
455
456 void SceneView::RotateCamera()
457 {
458   if(mUseFrameBuffer)
459   {
460     DevelCameraActor::RotateProjection(mSelectedCamera, DEFAULT_ORIENTATION);
461   }
462   else
463   {
464     DevelCameraActor::RotateProjection(mSelectedCamera, mWindowOrientation);
465   }
466 }
467
468 void SceneView::LoadImageBasedLight()
469 {
470   Texture diffuseTexture  = Dali::Scene3D::Loader::LoadCubeMap(mDiffuseIblUrl);
471   Texture specularTexture = Dali::Scene3D::Loader::LoadCubeMap(mSpecularIblUrl);
472
473   if(diffuseTexture && specularTexture)
474   {
475     mDiffuseTexture  = diffuseTexture;
476     mSpecularTexture = specularTexture;
477     for(auto&& item : mItems)
478     {
479       if(item)
480       {
481         item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor);
482       }
483     }
484   }
485 }
486
487 void SceneView::OnLoadComplete()
488 {
489   // TODO: In this implementation, we cannot know which request occurs this OnLoadComplete Callback.
490   // Currently it is no problem because the all loading is processed in this method.
491
492   // Prevent to emit unnecessary resource ready signal.
493   if(IsResourceReady())
494   {
495     return;
496   }
497
498   if(!mIBLResourceReady)
499   {
500     LoadImageBasedLight();
501     mIBLResourceReady  = true;
502     mIblLoadedCallback = nullptr;
503   }
504
505   if(IsResourceReady())
506   {
507     Control::SetResourceReady(false);
508   }
509 }
510
511 } // namespace Internal
512 } // namespace Scene3D
513 } // namespace Dali