Fix a clipping mode bug
[platform/core/uifw/dali-core.git] / dali / internal / update / manager / render-task-processor.cpp
1 /*
2  * Copyright (c) 2023 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/update/manager/render-task-processor.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/internal/render/common/render-instruction-container.h>
24 #include <dali/internal/render/common/render-instruction.h>
25 #include <dali/internal/render/common/render-item.h>
26 #include <dali/internal/render/common/render-tracker.h>
27 #include <dali/internal/render/renderers/render-renderer.h>
28 #include <dali/internal/update/manager/sorted-layers.h>
29 #include <dali/internal/update/nodes/scene-graph-layer.h>
30 #include <dali/internal/update/render-tasks/scene-graph-render-task-list.h>
31 #include <dali/internal/update/render-tasks/scene-graph-render-task.h>
32
33 #if defined(DEBUG_ENABLED)
34 extern Debug::Filter* gRenderTaskLogFilter;
35 #endif
36
37 namespace Dali
38 {
39 namespace Internal
40 {
41 namespace SceneGraph
42 {
43 namespace // Unnamed namespace
44 {
45 // Return false if the node or it's parents are exclusive to another render-task.
46 bool CheckExclusivity(const Node& node, const RenderTask& task)
47 {
48   const RenderTask* exclusiveTo = node.GetExclusiveRenderTask();
49   if(exclusiveTo)
50   {
51     return (exclusiveTo == &task);
52   }
53
54   const Node* parent = node.GetParent();
55   if(parent)
56   {
57     return CheckExclusivity(*parent, task);
58   }
59
60   // No exclusive flags set.
61   return true;
62 }
63
64 Layer* FindLayer(Node& node)
65 {
66   Node*  currentNode(&node);
67   Layer* layer(nullptr);
68   while(currentNode)
69   {
70     if((layer = currentNode->GetLayer()) != nullptr)
71     {
72       return layer;
73     }
74
75     currentNode = currentNode->GetParent();
76   }
77
78   return nullptr;
79 }
80
81 /**
82  * Rebuild the Layer::colorRenderables and overlayRenderables members,
83  * including only renderers which are included in the current render-task.
84  *
85  * @param[in]  updateBufferIndex The current update buffer index.
86  * @param[in]  node The current node of the scene-graph.
87  * @param[in]  currentLayer The current layer containing lists of opaque/transparent renderables.
88  * @param[in]  renderTask The current render-task.
89  * @param[in]  inheritedDrawMode The draw mode of the parent
90  * @param[in]  parentDepthIndex The inherited parent node depth index
91  * @param[in]  currentClippingId The current Clipping Id
92  *               Note: ClippingId is passed by reference, so it is permanently modified when traversing back up the tree for uniqueness.
93  * @param[in]  clippingDepth The current stencil clipping depth
94  * @param[in]  clippingDepth The current scissor clipping depth
95  * @param[out] clippingUsed  Gets set to true if any clipping nodes have been found
96  * @return true if rendering should be kept, false otherwise.
97  */
98 bool AddRenderablesForTask(BufferIndex updateBufferIndex,
99                            Node&       node,
100                            Layer&      currentLayer,
101                            RenderTask& renderTask,
102                            int         inheritedDrawMode,
103                            uint32_t&   currentClippingId,
104                            uint32_t    clippingDepth,
105                            uint32_t    scissorDepth,
106                            bool&       clippingUsed)
107 {
108   bool keepRendering = false;
109
110   // Short-circuit for invisible nodes
111   if(!node.IsVisible(updateBufferIndex))
112   {
113     node.GetPartialRenderingData().mVisible = false;
114     return keepRendering;
115   }
116
117   // If the node was not previously visible
118   if(!node.GetPartialRenderingData().mVisible)
119   {
120     node.GetPartialRenderingData().mVisible = true;
121     node.SetUpdatedTree(true);
122   }
123
124   // Check whether node is exclusive to a different render-task
125   const RenderTask* exclusiveTo = node.GetExclusiveRenderTask();
126   if(exclusiveTo && (exclusiveTo != &renderTask))
127   {
128     return keepRendering;
129   }
130
131   // Assume all children go to this layer (if this node is a layer).
132   Layer* layer = node.GetLayer();
133   if(layer)
134   {
135     // Layers do not inherit the DrawMode from their parents
136     inheritedDrawMode = node.GetDrawMode();
137   }
138   else
139   {
140     // This node is not a layer.
141     layer = &currentLayer;
142     inheritedDrawMode |= node.GetDrawMode();
143   }
144
145   DALI_ASSERT_DEBUG(NULL != layer);
146
147   const uint32_t count = node.GetRendererCount();
148
149   // Update the clipping Id and depth for this node (if clipping is enabled).
150   const Dali::ClippingMode::Type clippingMode = node.GetClippingMode();
151   if(DALI_UNLIKELY(clippingMode != ClippingMode::DISABLED))
152   {
153     if(DALI_LIKELY(clippingMode == ClippingMode::CLIP_TO_BOUNDING_BOX))
154     {
155       ++scissorDepth; // This only modifies the local value (which is passed in when the method recurses).
156       // If we do not have any renderers, create one to house the scissor operation.
157       if(count == 0u)
158       {
159         RenderableContainer& target = (inheritedDrawMode == DrawMode::NORMAL) ? layer->colorRenderables : layer->overlayRenderables;
160         target.PushBack(Renderable(&node, RendererKey{}));
161       }
162     }
163     else
164     {
165       // We only need clipping Id for stencil clips. This means we can deliberately avoid modifying it for bounding box clips,
166       // thus allowing bounding box clipping to still detect clip depth changes without turning on the stencil buffer for non-clipped nodes.
167       ++currentClippingId; // This modifies the reference passed in as well as the local value, causing the value to be global to the recursion.
168       ++clippingDepth;     // This only modifies the local value (which is passed in when the method recurses).
169     }
170     clippingUsed = true;
171   }
172   // Set the information in the node.
173   node.SetClippingInformation(currentClippingId, clippingDepth, scissorDepth);
174
175   RenderableContainer& target = DALI_LIKELY(inheritedDrawMode == DrawMode::NORMAL) ? layer->colorRenderables : layer->overlayRenderables;
176   for(uint32_t i = 0; i < count; ++i)
177   {
178     SceneGraph::RendererKey rendererKey = node.GetRendererAt(i);
179
180     target.PushBack(Renderable(&node, rendererKey));
181     keepRendering = keepRendering || (rendererKey->GetRenderingBehavior() == DevelRenderer::Rendering::CONTINUOUSLY);
182   }
183
184   // Recurse children.
185   NodeContainer& children = node.GetChildren();
186   const NodeIter endIter  = children.End();
187   for(NodeIter iter = children.Begin(); iter != endIter; ++iter)
188   {
189     Node& child = **iter;
190     keepRendering |= AddRenderablesForTask(updateBufferIndex, child, *layer, renderTask, inheritedDrawMode, currentClippingId, clippingDepth, scissorDepth, clippingUsed);
191   }
192
193   return keepRendering;
194 }
195
196 /**
197  * Process the list of render-tasks; the output is a series of render instructions.
198  * @note When ProcessRenderTasks is called, the layers should already the transparent/opaque renderers which are ready to render.
199  * If there is only one default render-task, then no further processing is required.
200  * @param[in]  updateBufferIndex          The current update buffer index.
201  * @param[in]  taskContainer              The container of render-tasks.
202  * @param[in]  rootNode                   The root node of the scene-graph.
203  * @param[in]  sortedLayers               The layers containing lists of opaque / transparent renderables.
204  * @param[out] instructions               The instructions for rendering the next frame.
205  * @param[in]  renderInstructionProcessor An instance of the RenderInstructionProcessor used to sort and handle the renderers for each layer.
206  * @param[in]  renderToFboEnabled         Whether rendering into the Frame Buffer Object is enabled (used to measure FPS above 60)
207  * @param[in]  isRenderingToFbo           Whether this frame is being rendered into the Frame Buffer Object (used to measure FPS above 60)
208  * @param[in]  processOffscreen           Whether the offscreen render tasks are the ones processed. Otherwise it processes the onscreen tasks.
209  * @return true if rendering should be kept, false otherwise.
210  */
211 bool ProcessTasks(BufferIndex                          updateBufferIndex,
212                   RenderTaskList::RenderTaskContainer& taskContainer,
213                   Layer&                               rootNode,
214                   SortedLayerPointers&                 sortedLayers,
215                   RenderInstructionContainer&          instructions,
216                   RenderInstructionProcessor&          renderInstructionProcessor,
217                   bool                                 renderToFboEnabled,
218                   bool                                 isRenderingToFbo,
219                   bool                                 processOffscreen)
220 {
221   uint32_t clippingId       = 0u;
222   bool     hasClippingNodes = false;
223
224   bool isFirstRenderTask = true;
225   bool keepRendering     = false;
226
227   // Retrieve size of Scene and default camera position to update viewport of each RenderTask if the RenderTask uses ViewportGuideNode.
228   RenderTaskList::RenderTaskContainer::Iterator iter                  = taskContainer.Begin();
229   RenderTask&                                   defaultRenderTask     = **iter;
230   const Camera&                                 defaultCamera         = defaultRenderTask.GetCamera();
231   auto                                          defaultRootNode       = defaultRenderTask.GetSourceNode();
232   Vector3                                       defaultCameraPosition = Vector3::ZERO;
233   Vector2                                       sceneSize             = Vector2::ZERO;
234   if(defaultRootNode)
235   {
236     defaultCameraPosition = defaultCamera.GetWorldPosition(updateBufferIndex);
237     sceneSize             = Vector2(defaultRootNode->GetSize(updateBufferIndex) * defaultRootNode->GetWorldScale(updateBufferIndex));
238   }
239
240   for(RenderTaskList::RenderTaskContainer::Iterator iter = taskContainer.Begin(), endIter = taskContainer.End(); endIter != iter; ++iter)
241   {
242     RenderTask& renderTask = **iter;
243
244     const bool hasFrameBuffer      = nullptr != renderTask.GetFrameBuffer();
245     const bool isDefaultRenderTask = isFirstRenderTask;
246     isFirstRenderTask              = false;
247
248     if((!renderToFboEnabled && ((!processOffscreen && hasFrameBuffer) ||
249                                 (processOffscreen && !hasFrameBuffer))) ||
250        (renderToFboEnabled && ((processOffscreen && !hasFrameBuffer) ||
251                                (isDefaultRenderTask && processOffscreen) ||
252                                (!isDefaultRenderTask && !processOffscreen && hasFrameBuffer))) ||
253        !renderTask.ReadyToRender(updateBufferIndex))
254     {
255       // Skip to next task.
256       continue;
257     }
258
259     Node* sourceNode = renderTask.GetSourceNode();
260     DALI_ASSERT_DEBUG(NULL != sourceNode); // Otherwise Prepare() should return false
261
262     // Check that the source node is not exclusive to another task.
263     if(!CheckExclusivity(*sourceNode, renderTask))
264     {
265       continue;
266     }
267
268     Layer* layer = FindLayer(*sourceNode);
269     if(!layer)
270     {
271       // Skip to next task as no layer.
272       continue;
273     }
274
275     renderTask.UpdateViewport(updateBufferIndex, sceneSize, defaultCameraPosition);
276
277     const uint32_t currentNumberOfInstructions = instructions.Count(updateBufferIndex);
278
279     if(renderTask.IsRenderRequired())
280     {
281       for(auto&& sortedLayer : sortedLayers)
282       {
283         sortedLayer->ClearRenderables();
284       }
285
286       keepRendering |= AddRenderablesForTask(updateBufferIndex,
287                                              *sourceNode,
288                                              *layer,
289                                              renderTask,
290                                              sourceNode->GetDrawMode(),
291                                              clippingId,
292                                              0u,
293                                              0u,
294                                              hasClippingNodes);
295
296       renderInstructionProcessor.Prepare(updateBufferIndex,
297                                          sortedLayers,
298                                          renderTask,
299                                          renderTask.GetCullMode(),
300                                          hasClippingNodes,
301                                          instructions);
302     }
303
304     if(!processOffscreen && isDefaultRenderTask && renderToFboEnabled && !isRenderingToFbo && hasFrameBuffer)
305     {
306       // Traverse the instructions of the default render task and mark them to be rendered into the frame buffer.
307       const uint32_t count = instructions.Count(updateBufferIndex);
308       for(uint32_t index = currentNumberOfInstructions; index < count; ++index)
309       {
310         RenderInstruction& instruction = instructions.At(updateBufferIndex, index);
311         instruction.mIgnoreRenderToFbo = true;
312       }
313     }
314   }
315
316   return keepRendering;
317 }
318
319 } // Anonymous namespace.
320
321 RenderTaskProcessor::RenderTaskProcessor() = default;
322
323 RenderTaskProcessor::~RenderTaskProcessor() = default;
324
325 bool RenderTaskProcessor::Process(BufferIndex                 updateBufferIndex,
326                                   RenderTaskList&             renderTasks,
327                                   Layer&                      rootNode,
328                                   SortedLayerPointers&        sortedLayers,
329                                   RenderInstructionContainer& instructions,
330                                   bool                        renderToFboEnabled,
331                                   bool                        isRenderingToFbo)
332 {
333   RenderTaskList::RenderTaskContainer& taskContainer = renderTasks.GetTasks();
334   bool                                 keepRendering = false;
335
336   if(taskContainer.IsEmpty())
337   {
338     // Early-exit if there are no tasks to process
339     return keepRendering;
340   }
341
342   // For each render-task:
343   //   1) Prepare the render-task
344   //   2) Clear the layer-stored lists of renderers (TODO check if the layer is not changed and don't clear in this case)
345   //   3) Traverse the scene-graph, filling the lists for the current render-task
346   //   4) Prepare render-instructions
347
348   DALI_LOG_INFO(gRenderTaskLogFilter, Debug::General, "RenderTaskProcessor::Process() Offscreens first\n");
349
350   // First process off screen render tasks - we may need the results of these for the on screen renders
351
352   keepRendering = ProcessTasks(updateBufferIndex,
353                                taskContainer,
354                                rootNode,
355                                sortedLayers,
356                                instructions,
357                                mRenderInstructionProcessor,
358                                renderToFboEnabled,
359                                isRenderingToFbo,
360                                true);
361
362   DALI_LOG_INFO(gRenderTaskLogFilter, Debug::General, "RenderTaskProcessor::Process() Onscreen\n");
363
364   // Now that the off screen renders are done we can process on screen render tasks.
365   // Reset the clipping Id for the OnScreen render tasks.
366
367   keepRendering |= ProcessTasks(updateBufferIndex,
368                                 taskContainer,
369                                 rootNode,
370                                 sortedLayers,
371                                 instructions,
372                                 mRenderInstructionProcessor,
373                                 renderToFboEnabled,
374                                 isRenderingToFbo,
375                                 false);
376
377   return keepRendering;
378 }
379
380 } // namespace SceneGraph
381
382 } // namespace Internal
383
384 } // namespace Dali