Reduce Render::Renderer size
[platform/core/uifw/dali-core.git] / dali / internal / render / common / render-algorithms.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/internal/render/common/render-algorithms.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/internal/render/common/render-debug.h>
23 #include <dali/internal/render/common/render-instruction.h>
24 #include <dali/internal/render/common/render-list.h>
25 #include <dali/internal/render/renderers/render-renderer.h>
26 #include <dali/internal/update/nodes/scene-graph-layer.h>
27
28 using Dali::Internal::SceneGraph::RenderInstruction;
29 using Dali::Internal::SceneGraph::RenderItem;
30 using Dali::Internal::SceneGraph::RenderList;
31 using Dali::Internal::SceneGraph::RenderListContainer;
32
33 namespace Dali
34 {
35 namespace Internal
36 {
37 namespace Render
38 {
39 namespace
40 {
41 struct GraphicsDepthCompareOp
42 {
43   constexpr explicit GraphicsDepthCompareOp(DepthFunction::Type compareOp)
44   {
45     switch(compareOp)
46     {
47       case DepthFunction::NEVER:
48         op = Graphics::CompareOp::NEVER;
49         break;
50       case DepthFunction::LESS:
51         op = Graphics::CompareOp::LESS;
52         break;
53       case DepthFunction::EQUAL:
54         op = Graphics::CompareOp::EQUAL;
55         break;
56       case DepthFunction::LESS_EQUAL:
57         op = Graphics::CompareOp::LESS_OR_EQUAL;
58         break;
59       case DepthFunction::GREATER:
60         op = Graphics::CompareOp::GREATER;
61         break;
62       case DepthFunction::NOT_EQUAL:
63         op = Graphics::CompareOp::NOT_EQUAL;
64         break;
65       case DepthFunction::GREATER_EQUAL:
66         op = Graphics::CompareOp::GREATER_OR_EQUAL;
67         break;
68       case DepthFunction::ALWAYS:
69         op = Graphics::CompareOp::ALWAYS;
70         break;
71     }
72   }
73   Graphics::CompareOp op{Graphics::CompareOp::NEVER};
74 };
75
76 struct GraphicsStencilCompareOp
77 {
78   constexpr explicit GraphicsStencilCompareOp(StencilFunction::Type compareOp)
79   {
80     switch(compareOp)
81     {
82       case StencilFunction::NEVER:
83         op = Graphics::CompareOp::NEVER;
84         break;
85       case StencilFunction::LESS:
86         op = Graphics::CompareOp::LESS;
87         break;
88       case StencilFunction::EQUAL:
89         op = Graphics::CompareOp::EQUAL;
90         break;
91       case StencilFunction::LESS_EQUAL:
92         op = Graphics::CompareOp::LESS_OR_EQUAL;
93         break;
94       case StencilFunction::GREATER:
95         op = Graphics::CompareOp::GREATER;
96         break;
97       case StencilFunction::NOT_EQUAL:
98         op = Graphics::CompareOp::NOT_EQUAL;
99         break;
100       case StencilFunction::GREATER_EQUAL:
101         op = Graphics::CompareOp::GREATER_OR_EQUAL;
102         break;
103       case StencilFunction::ALWAYS:
104         op = Graphics::CompareOp::ALWAYS;
105         break;
106     }
107   }
108   Graphics::CompareOp op{Graphics::CompareOp::NEVER};
109 };
110
111 struct GraphicsStencilOp
112 {
113   constexpr explicit GraphicsStencilOp(StencilOperation::Type stencilOp)
114   {
115     switch(stencilOp)
116     {
117       case Dali::StencilOperation::KEEP:
118         op = Graphics::StencilOp::KEEP;
119         break;
120       case Dali::StencilOperation::ZERO:
121         op = Graphics::StencilOp::ZERO;
122         break;
123       case Dali::StencilOperation::REPLACE:
124         op = Graphics::StencilOp::REPLACE;
125         break;
126       case Dali::StencilOperation::INCREMENT:
127         op = Graphics::StencilOp::INCREMENT_AND_CLAMP;
128         break;
129       case Dali::StencilOperation::DECREMENT:
130         op = Graphics::StencilOp::DECREMENT_AND_CLAMP;
131         break;
132       case Dali::StencilOperation::INVERT:
133         op = Graphics::StencilOp::INVERT;
134         break;
135       case Dali::StencilOperation::INCREMENT_WRAP:
136         op = Graphics::StencilOp::INCREMENT_AND_WRAP;
137         break;
138       case Dali::StencilOperation::DECREMENT_WRAP:
139         op = Graphics::StencilOp::DECREMENT_AND_WRAP;
140         break;
141     }
142   }
143   Graphics::StencilOp op{Graphics::StencilOp::KEEP};
144 };
145
146 inline Graphics::Viewport ViewportFromClippingBox(const Uint16Pair& sceneSize, ClippingBox clippingBox, int orientation)
147 {
148   Graphics::Viewport viewport{static_cast<float>(clippingBox.x), static_cast<float>(clippingBox.y), static_cast<float>(clippingBox.width), static_cast<float>(clippingBox.height), 0.0f, 0.0f};
149
150   if(orientation == 90 || orientation == 270)
151   {
152     if(orientation == 90)
153     {
154       viewport.x = sceneSize.GetY() - (clippingBox.y + clippingBox.height);
155       viewport.y = clippingBox.x;
156     }
157     else // orientation == 270
158     {
159       viewport.x = clippingBox.y;
160       viewport.y = sceneSize.GetX() - (clippingBox.x + clippingBox.width);
161     }
162     viewport.width  = static_cast<float>(clippingBox.height);
163     viewport.height = static_cast<float>(clippingBox.width);
164   }
165   else if(orientation == 180)
166   {
167     viewport.x = sceneSize.GetX() - (clippingBox.x + clippingBox.width);
168     viewport.y = sceneSize.GetY() - (clippingBox.y + clippingBox.height);
169   }
170   return viewport;
171 }
172
173 inline Graphics::Rect2D RecalculateRect(Graphics::Rect2D rect, int orientation, Graphics::Viewport viewport)
174 {
175   Graphics::Rect2D newRect;
176
177   // scissor's value should be set based on the default system coordinates.
178   // when the surface is rotated, the input valus already were set with the rotated angle.
179   // So, re-calculation is needed.
180   if(orientation == 90)
181   {
182     newRect.x      = viewport.height - (rect.y + rect.height);
183     newRect.y      = rect.x;
184     newRect.width  = rect.height;
185     newRect.height = rect.width;
186   }
187   else if(orientation == 180)
188   {
189     newRect.x      = viewport.width - (rect.x + rect.width);
190     newRect.y      = viewport.height - (rect.y + rect.height);
191     newRect.width  = rect.width;
192     newRect.height = rect.height;
193   }
194   else if(orientation == 270)
195   {
196     newRect.x      = rect.y;
197     newRect.y      = viewport.width - (rect.x + rect.width);
198     newRect.width  = rect.height;
199     newRect.height = rect.width;
200   }
201   else
202   {
203     newRect.x      = rect.x;
204     newRect.y      = rect.y;
205     newRect.width  = rect.width;
206     newRect.height = rect.height;
207   }
208   return newRect;
209 }
210
211 inline Graphics::Rect2D Rect2DFromClippingBox(ClippingBox clippingBox, int orientation, Graphics::Viewport viewport)
212 {
213   Graphics::Rect2D rect2D{clippingBox.x, clippingBox.y, static_cast<uint32_t>(abs(clippingBox.width)), static_cast<uint32_t>(abs(clippingBox.height))};
214   return RecalculateRect(rect2D, orientation, viewport);
215 }
216
217 inline Graphics::Rect2D Rect2DFromRect(Dali::Rect<int> rect, int orientation, Graphics::Viewport viewport)
218 {
219   Graphics::Rect2D rect2D{rect.x, rect.y, static_cast<uint32_t>(abs(rect.width)), static_cast<uint32_t>(abs(rect.height))};
220   return RecalculateRect(rect2D, orientation, viewport);
221 }
222
223 /**
224  * @brief Find the intersection of two AABB rectangles.
225  * This is a logical AND operation. IE. The intersection is the area overlapped by both rectangles.
226  * @param[in]     aabbA                  Rectangle A
227  * @param[in]     aabbB                  Rectangle B
228  * @return                               The intersection of rectangle A & B (result is a rectangle)
229  */
230 inline ClippingBox IntersectAABB(const ClippingBox& aabbA, const ClippingBox& aabbB)
231 {
232   ClippingBox intersectionBox;
233
234   // First calculate the largest starting positions in X and Y.
235   intersectionBox.x = std::max(aabbA.x, aabbB.x);
236   intersectionBox.y = std::max(aabbA.y, aabbB.y);
237
238   // Now calculate the smallest ending positions, and take the largest starting
239   // positions from the result, to get the width and height respectively.
240   // If the two boxes do not intersect at all, then we need a 0 width and height clipping area.
241   // We use max here to clamp both width and height to >= 0 for this use-case.
242   intersectionBox.width  = std::max(std::min(aabbA.x + aabbA.width, aabbB.x + aabbB.width) - intersectionBox.x, 0);
243   intersectionBox.height = std::max(std::min(aabbA.y + aabbA.height, aabbB.y + aabbB.height) - intersectionBox.y, 0);
244
245   return intersectionBox;
246 }
247
248 /**
249  * @brief Set up the stencil and color buffer for automatic clipping (StencilMode::AUTO).
250  * @param[in]     item                     The current RenderItem about to be rendered
251  * @param[in,out] commandBuffer            The command buffer to write stencil commands into
252  * @param[in,out] lastClippingDepth        The stencil depth of the last renderer drawn.
253  * @param[in,out] lastClippingId           The clipping ID of the last renderer drawn.
254  */
255 inline void SetupStencilClipping(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, uint32_t& lastClippingDepth, uint32_t& lastClippingId)
256 {
257   const Dali::Internal::SceneGraph::Node* node       = item.mNode;
258   const uint32_t                          clippingId = node->GetClippingId();
259   // If there is no clipping Id, then either we haven't reached a clipping Node yet, or there aren't any.
260   // Either way we can skip clipping setup for this renderer.
261   if(clippingId == 0u)
262   {
263     // Exit immediately if there are no clipping actions to perform (EG. we have not yet hit a clipping node).
264     commandBuffer.SetStencilTestEnable(false);
265     return;
266   }
267   commandBuffer.SetStencilTestEnable(true);
268
269   const uint32_t clippingDepth = node->GetClippingDepth();
270
271   // Pre-calculate a mask which has all bits set up to and including the current clipping depth.
272   // EG. If depth is 3, the mask would be "111" in binary.
273   const uint32_t currentDepthMask = (1u << clippingDepth) - 1u;
274
275   // Are we are writing to the stencil buffer?
276   if(item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_CHILDREN)
277   {
278     // We are writing to the stencil buffer.
279     // If clipping Id is 1, this is the first clipping renderer within this render-list.
280     if(clippingId == 1u)
281     {
282       // We are enabling the stencil-buffer for the first time within this render list.
283       // Clear the buffer at this point.
284
285       commandBuffer.SetStencilWriteMask(0xFF);
286       commandBuffer.ClearStencilBuffer();
287     }
288     else if((clippingDepth < lastClippingDepth) ||
289             ((clippingDepth == lastClippingDepth) && (clippingId > lastClippingId)))
290     {
291       // The above if() statement tests if we need to clear some (not all) stencil bit-planes.
292       // We need to do this if either of the following are true:
293       //   1) We traverse up the scene-graph to a previous stencil depth
294       //   2) We are at the same stencil depth but the clipping Id has increased.
295       //
296       // This calculation takes the new depth to move to, and creates an inverse-mask of that number of consecutive bits.
297       // This has the effect of clearing everything except the bit-planes up to (and including) our current depth.
298       const uint32_t stencilClearMask = (currentDepthMask >> 1u) ^ 0xff;
299
300       commandBuffer.SetStencilWriteMask(stencilClearMask);
301       commandBuffer.ClearStencilBuffer();
302     }
303
304     // We keep track of the last clipping Id and depth so we can determine when we are
305     // moving back up the scene graph and require some of the stencil bit-planes to be deleted.
306     lastClippingDepth = clippingDepth;
307     lastClippingId    = clippingId;
308
309     // We only ever write to bit-planes up to the current depth as we may need
310     // to erase individual bit-planes and revert to a previous clipping area.
311     // Our reference value for testing (in StencilFunc) is written to to the buffer, but we actually
312     // want to test a different value. IE. All the bit-planes up to but not including the current depth.
313     // So we use the Mask parameter of StencilFunc to mask off the top bit-plane when testing.
314     // Here we create our test mask to innore the top bit of the reference test value.
315     // As the mask is made up of contiguous "1" values, we can do this quickly with a bit-shift.
316     const uint32_t testMask = currentDepthMask >> 1u;
317
318     // Test against existing stencil bit-planes. All must match up to (but not including) this depth.
319     commandBuffer.SetStencilFunc(Graphics::CompareOp::EQUAL, currentDepthMask, testMask);
320     // Write to the new stencil bit-plane (the other previous bit-planes are also written to).
321     commandBuffer.SetStencilWriteMask(currentDepthMask);
322     commandBuffer.SetStencilOp(Graphics::StencilOp::KEEP, Graphics::StencilOp::REPLACE, Graphics::StencilOp::REPLACE);
323   }
324   else
325   {
326     // We are reading from the stencil buffer. Set up the stencil accordingly
327     // This calculation sets all the bits up to the current depth bit.
328     // This has the effect of testing that the pixel being written to exists in every bit-plane up to the current depth.
329     commandBuffer.SetStencilFunc(Graphics::CompareOp::EQUAL, currentDepthMask, currentDepthMask);
330     commandBuffer.SetStencilOp(Graphics::StencilOp::KEEP, Graphics::StencilOp::KEEP, Graphics::StencilOp::KEEP);
331   }
332 }
333
334 /**
335  * @brief Sets up the depth buffer for reading and writing based on the current render item.
336  * The items read and write mode are used if specified.
337  *  - If AUTO is selected for reading, the decision will be based on the Layer Behavior.
338  *  - If AUTO is selected for writing, the decision will be based on the items opacity.
339  * @param[in]     item                The RenderItem to set up the depth buffer for.
340  * @param[in,out] secondaryCommandBuffer The secondary command buffer to write depth commands to
341  * @param[in]     depthTestEnabled    True if depth testing has been enabled.
342  * @param[in/out] firstDepthBufferUse Initialize to true on the first call, this method will set it to false afterwards.
343  */
344 inline void SetupDepthBuffer(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, bool depthTestEnabled, bool& firstDepthBufferUse)
345 {
346   // Set up whether or not to write to the depth buffer.
347   const DepthWriteMode::Type depthWriteMode = item.mRenderer->GetDepthWriteMode();
348   // Most common mode (AUTO) is tested first.
349   const bool enableDepthWrite = ((depthWriteMode == DepthWriteMode::AUTO) && depthTestEnabled && item.mIsOpaque) ||
350                                 (depthWriteMode == DepthWriteMode::ON);
351
352   // Set up whether or not to read from (test) the depth buffer.
353   const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
354
355   // Most common mode (AUTO) is tested first.
356   const bool enableDepthTest = ((depthTestMode == DepthTestMode::AUTO) && depthTestEnabled) ||
357                                (depthTestMode == DepthTestMode::ON);
358
359   // Is the depth buffer in use?
360   if(enableDepthWrite || enableDepthTest)
361   {
362     // The depth buffer must be enabled if either reading or writing.
363     commandBuffer.SetDepthTestEnable(true);
364
365     // Look-up the depth function from the Dali::DepthFunction enum, and set it.
366     commandBuffer.SetDepthCompareOp(GraphicsDepthCompareOp(item.mRenderer->GetDepthFunction()).op);
367
368     // If this is the first use of the depth buffer this RenderTask, perform a clear.
369     // Note: We could do this at the beginning of the RenderTask and rely on the
370     // graphics implementation to ignore the clear if not required, but, we would
371     // have to enable the depth buffer to do so, which could be a redundant enable.
372     if(DALI_UNLIKELY(firstDepthBufferUse))
373     {
374       // This is the first time the depth buffer is being written to or read.
375       firstDepthBufferUse = false;
376
377       // Note: The buffer will only be cleared if written to since a previous clear.
378       commandBuffer.SetDepthWriteEnable(true);
379       commandBuffer.ClearDepthBuffer();
380     }
381
382     // Set up the depth mask based on our depth write setting.
383     commandBuffer.SetDepthWriteEnable(enableDepthWrite);
384   }
385   else
386   {
387     // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
388     commandBuffer.SetDepthTestEnable(false);
389   }
390 }
391
392 } // Unnamed namespace
393
394 /**
395  * @brief This method is responsible for making decisions on when to apply and unapply scissor clipping, and what rectangular dimensions should be used.
396  * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied.
397  * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation.
398  * @param[in]     item                     The current RenderItem about to be rendered
399  * @param[in,out] commandBuffer            The command buffer to write into
400  * @param[in]     instruction              The render-instruction to process.
401  * @param[in]     orientation              The Scene's surface orientation.
402  */
403 inline void RenderAlgorithms::SetupScissorClipping(
404   const RenderItem&        item,
405   Graphics::CommandBuffer& commandBuffer,
406   const RenderInstruction& instruction,
407   int                      orientation)
408 {
409   // Get the number of child scissors in the stack (do not include layer or root box).
410   size_t         childStackDepth = mScissorStack.size() - 1u;
411   const uint32_t scissorDepth    = item.mNode->GetScissorDepth();
412   const bool     clippingNode    = item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_TO_BOUNDING_BOX;
413   bool           traversedUpTree = false;
414
415   // If we are using scissor clipping and we are at the same depth (or less), we need to undo previous clips.
416   // We do this by traversing up the scissor clip stack and then apply the appropriate clip for the current render item.
417   // To know this, we use clippingDepth. This value is set on *every* node, but only increased as clipping nodes are hit depth-wise.
418   // So we know if we are at depth 4 and the stackDepth is 5, that we have gone up.
419   // If the depth is the same then we are effectively part of a different sub-tree from the parent, we must also remove the current clip.
420   // Note: Stack depth must always be at least 1, as we will have the layer or stage size as the root value.
421   if((childStackDepth > 0u) && (scissorDepth < childStackDepth))
422   {
423     while(scissorDepth < childStackDepth)
424     {
425       mScissorStack.pop_back();
426       --childStackDepth;
427     }
428
429     // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
430     traversedUpTree = true;
431   }
432   if(clippingNode && childStackDepth > 0u && childStackDepth == scissorDepth) // case of sibling clip area
433   {
434     mScissorStack.pop_back();
435     --childStackDepth;
436   }
437
438   // If we are on a clipping node, or we have traveled up the tree and gone back past a clipping node, may need to apply a new scissor clip.
439   if(clippingNode || traversedUpTree)
440   {
441     // First, check if we are a clipping node.
442     if(clippingNode)
443     {
444       // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
445
446       // Get the AABB bounding box for the current render item.
447       const ClippingBox scissorBox(RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3::ZERO, item.mSize, mViewportRectangle.width, mViewportRectangle.height));
448
449       // Get the AABB for the parent item that we must intersect with.
450       const ClippingBox& parentBox(mScissorStack.back());
451
452       // We must reduce the clipping area based on the parents area to allow nested clips. This is a set intersection function.
453       // We add the new scissor box to the stack so we can return to it if needed.
454       mScissorStack.emplace_back(IntersectAABB(parentBox, scissorBox));
455     }
456
457     // The scissor test is enabled if we have any children on the stack, OR, if there are none but it is a user specified layer scissor box.
458     // IE. It is not enabled if we are at the top of the stack and the layer does not have a specified clipping box.
459     const bool scissorEnabled = (!mScissorStack.empty()) || mHasLayerScissor;
460
461     // Enable the scissor test based on the above calculation
462     if(scissorEnabled)
463     {
464       commandBuffer.SetScissorTestEnable(scissorEnabled);
465     }
466
467     // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
468     if(scissorEnabled)
469     {
470       ClippingBox useScissorBox(mScissorStack.back());
471
472       if(instruction.mFrameBuffer && instruction.GetCamera()->IsYAxisInverted())
473       {
474         useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y;
475       }
476
477       Graphics::Viewport graphicsViewport = ViewportFromClippingBox(Uint16Pair{0, 0}, mViewportRectangle, 0);
478       commandBuffer.SetScissor(Rect2DFromClippingBox(useScissorBox, orientation, graphicsViewport));
479     }
480   }
481   else
482   {
483     // If there is render callback on the Renderer we need to calculate the scissor box and provide it to the
484     // callback so it may be clipped
485     if(item.mRenderer->GetRenderCallback())
486     {
487       // store clipping box inside the render callback input structure
488       auto& input       = item.mRenderer->GetRenderCallbackInput();
489       input.clippingBox = ClippingBox(RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3::ZERO, item.mSize, mViewportRectangle.width, mViewportRectangle.height));
490     }
491   }
492 }
493
494 inline void RenderAlgorithms::SetupClipping(const RenderItem&                   item,
495                                             Graphics::CommandBuffer&            commandBuffer,
496                                             bool&                               usedStencilBuffer,
497                                             uint32_t&                           lastClippingDepth,
498                                             uint32_t&                           lastClippingId,
499                                             Integration::StencilBufferAvailable stencilBufferAvailable,
500                                             const RenderInstruction&            instruction,
501                                             int                                 orientation)
502 {
503   RenderMode::Type renderMode = RenderMode::AUTO;
504   const Renderer*  renderer   = item.mRenderer;
505   if(renderer)
506   {
507     renderMode = renderer->GetRenderMode();
508   }
509
510   // Setup the stencil using either the automatic clipping feature, or, the manual per-renderer stencil API.
511   // Note: This switch is in order of most likely value first.
512   switch(renderMode)
513   {
514     case RenderMode::AUTO:
515     {
516       // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
517       commandBuffer.SetColorMask(true);
518
519       // The automatic clipping feature will manage the scissor and stencil functions, only if stencil buffer is available for the latter.
520       // As both scissor and stencil clips can be nested, we may be simultaneously traversing up the scissor tree, requiring a scissor to be un-done. Whilst simultaneously adding a new stencil clip.
521       // We process both based on our current and old clipping depths for each mode.
522       // Both methods with return rapidly if there is nothing to be done for that type of clipping.
523       SetupScissorClipping(item, commandBuffer, instruction, orientation);
524
525       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
526       {
527         SetupStencilClipping(item, commandBuffer, lastClippingDepth, lastClippingId);
528       }
529       break;
530     }
531
532     case RenderMode::NONE:
533     case RenderMode::COLOR:
534     {
535       // No clipping is performed for these modes.
536       // Note: We do not turn off scissor clipping as it may be used for the whole layer.
537       // The stencil buffer will not be used at all, but we only need to disable it if it's available.
538       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
539       {
540         commandBuffer.SetStencilTestEnable(false);
541       }
542
543       // Setup the color buffer based on the RenderMode.
544       commandBuffer.SetColorMask(renderMode == RenderMode::COLOR);
545       break;
546     }
547
548     case RenderMode::STENCIL:
549     case RenderMode::COLOR_STENCIL:
550     {
551       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
552       {
553         // We are using the low-level Renderer Stencil API.
554         // The stencil buffer must be enabled for every renderer with stencil mode on, as renderers in between can disable it.
555         // Note: As the command state is cached, it is only sent when needed.
556         commandBuffer.SetStencilTestEnable(true);
557
558         // Setup the color buffer based on the RenderMode.
559         commandBuffer.SetColorMask(renderMode == RenderMode::COLOR_STENCIL);
560
561         // If this is the first use of the stencil buffer within this RenderList, clear it (this avoids unnecessary clears).
562         if(!usedStencilBuffer)
563         {
564           commandBuffer.ClearStencilBuffer();
565           usedStencilBuffer = true;
566         }
567
568         // Setup the stencil buffer based on the renderer's properties.
569         commandBuffer.SetStencilOp(GraphicsStencilOp(renderer->GetStencilOperationOnFail()).op,
570                                    GraphicsStencilOp(renderer->GetStencilOperationOnZPass()).op,
571                                    GraphicsStencilOp(renderer->GetStencilOperationOnZFail()).op);
572         commandBuffer.SetStencilFunc(GraphicsStencilCompareOp(renderer->GetStencilFunction()).op,
573                                      renderer->GetStencilFunctionReference(),
574                                      renderer->GetStencilFunctionMask());
575         commandBuffer.SetStencilWriteMask(renderer->GetStencilMask());
576       }
577       break;
578     }
579   }
580 }
581
582 inline void RenderAlgorithms::ProcessRenderList(const RenderList&                   renderList,
583                                                 BufferIndex                         bufferIndex,
584                                                 const Matrix&                       viewMatrix,
585                                                 const Matrix&                       projectionMatrix,
586                                                 Integration::DepthBufferAvailable   depthBufferAvailable,
587                                                 Integration::StencilBufferAvailable stencilBufferAvailable,
588                                                 Vector<Graphics::Texture*>&         boundTextures,
589                                                 const RenderInstruction&            instruction,
590                                                 const Rect<int32_t>&                viewport,
591                                                 const Rect<int>&                    rootClippingRect,
592                                                 int                                 orientation,
593                                                 const Uint16Pair&                   sceneSize)
594 {
595   DALI_PRINT_RENDER_LIST(renderList);
596
597   // Note: The depth buffer is enabled or disabled on a per-renderer basis.
598   // Here we pre-calculate the value to use if these modes are set to AUTO.
599   const bool        autoDepthTestMode((depthBufferAvailable == Integration::DepthBufferAvailable::TRUE) &&
600                                !(renderList.GetSourceLayer()->IsDepthTestDisabled()) &&
601                                renderList.HasColorRenderItems());
602   const std::size_t count = renderList.Count();
603   uint32_t          lastClippingDepth(0u);
604   uint32_t          lastClippingId(0u);
605   bool              usedStencilBuffer(false);
606   bool              firstDepthBufferUse(true);
607
608   mViewportRectangle = viewport;
609
610   auto* mutableRenderList      = const_cast<RenderList*>(&renderList);
611   auto& secondaryCommandBuffer = mutableRenderList->GetCommandBuffer(mGraphicsController);
612   secondaryCommandBuffer.Reset();
613   secondaryCommandBuffer.SetViewport(ViewportFromClippingBox(sceneSize, mViewportRectangle, orientation));
614   mHasLayerScissor = false;
615
616   // Setup Scissor testing (for both viewport and per-node scissor)
617   mScissorStack.clear();
618
619   // Add root clipping rect (set manually for Render function by partial update for example)
620   // on the bottom of the stack
621   if(!rootClippingRect.IsEmpty())
622   {
623     Graphics::Viewport graphicsViewport = ViewportFromClippingBox(sceneSize, mViewportRectangle, 0);
624     secondaryCommandBuffer.SetScissorTestEnable(true);
625     secondaryCommandBuffer.SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport));
626     mScissorStack.push_back(rootClippingRect);
627   }
628   // We are not performing a layer clip and no clipping rect set. Add the viewport as the root scissor rectangle.
629   else if(!renderList.IsClipping())
630   {
631     secondaryCommandBuffer.SetScissorTestEnable(false);
632     mScissorStack.push_back(mViewportRectangle);
633   }
634
635   if(renderList.IsClipping())
636   {
637     Graphics::Viewport graphicsViewport = ViewportFromClippingBox(sceneSize, mViewportRectangle, 0);
638     secondaryCommandBuffer.SetScissorTestEnable(true);
639     const ClippingBox& layerScissorBox = renderList.GetClippingBox();
640     secondaryCommandBuffer.SetScissor(Rect2DFromClippingBox(layerScissorBox, orientation, graphicsViewport));
641     mScissorStack.push_back(layerScissorBox);
642     mHasLayerScissor = true;
643   }
644
645   // Loop through all RenderItems in the RenderList, set up any prerequisites to render them, then perform the render.
646   for(uint32_t index = 0u; index < count; ++index)
647   {
648     const RenderItem& item = renderList.GetItem(index);
649
650     // Discard renderers outside the root clipping rect
651     bool skip = true;
652     if(!rootClippingRect.IsEmpty())
653     {
654       Vector4 updateArea = item.mRenderer ? item.mRenderer->GetVisualTransformedUpdateArea(bufferIndex, item.mUpdateArea) : item.mUpdateArea;
655       auto    rect       = RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3(updateArea.x, updateArea.y, 0.0f), Vector3(updateArea.z, updateArea.w, 0.0f), mViewportRectangle.width, mViewportRectangle.height);
656
657       if(rect.Intersect(rootClippingRect))
658       {
659         skip = false;
660       }
661     }
662     else
663     {
664       skip = false;
665     }
666
667     DALI_PRINT_RENDER_ITEM(item);
668
669     // Set up clipping based on both the Renderer and Actor APIs.
670     // The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
671     SetupClipping(item, secondaryCommandBuffer, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction, orientation);
672
673     if(DALI_LIKELY(item.mRenderer))
674     {
675       // Set up the depth buffer based on per-renderer flags if depth buffer is available
676       // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
677       // draw-mode state, such as Overlays.
678       // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will
679       // always disable depth testing and writing. Color Renderers will enable them if the Layer does.
680       if(depthBufferAvailable == Integration::DepthBufferAvailable::TRUE)
681       {
682         SetupDepthBuffer(item, secondaryCommandBuffer, autoDepthTestMode, firstDepthBufferUse);
683       }
684
685       // Depending on whether the renderer has draw commands attached or not the rendering process will
686       // iterate through all the render queues. If there are no draw commands attached, only one
687       // iteration must be done and the default behaviour of the renderer will be executed.
688       // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
689       // It is similar to the multi-pass rendering.
690       if(!skip)
691       {
692         auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
693         for(auto queue = 0u; queue < MAX_QUEUE; ++queue)
694         {
695           // Render the item. It will write into the command buffer everything it has to render
696           item.mRenderer->Render(secondaryCommandBuffer, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
697         }
698       }
699     }
700   }
701 }
702
703 RenderAlgorithms::RenderAlgorithms(Graphics::Controller& graphicsController)
704 : mGraphicsController(graphicsController),
705   mViewportRectangle(),
706   mHasLayerScissor(false)
707 {
708 }
709
710 void RenderAlgorithms::ResetCommandBuffer()
711 {
712   // Reset main command buffer
713   if(!mGraphicsCommandBuffer)
714   {
715     mGraphicsCommandBuffer = mGraphicsController.CreateCommandBuffer(
716       Graphics::CommandBufferCreateInfo()
717         .SetLevel(Graphics::CommandBufferLevel::PRIMARY),
718       nullptr);
719   }
720   else
721   {
722     mGraphicsCommandBuffer->Reset();
723   }
724 }
725
726 void RenderAlgorithms::SubmitCommandBuffer()
727 {
728   // Submit main command buffer
729   Graphics::SubmitInfo submitInfo;
730   submitInfo.cmdBuffer.push_back(mGraphicsCommandBuffer.get());
731   submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
732   mGraphicsController.SubmitCommandBuffers(submitInfo);
733 }
734
735 void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction&            instruction,
736                                                 BufferIndex                         bufferIndex,
737                                                 Integration::DepthBufferAvailable   depthBufferAvailable,
738                                                 Integration::StencilBufferAvailable stencilBufferAvailable,
739                                                 Vector<Graphics::Texture*>&         boundTextures,
740                                                 const Rect<int32_t>&                viewport,
741                                                 const Rect<int>&                    rootClippingRect,
742                                                 int                                 orientation,
743                                                 const Uint16Pair&                   sceneSize)
744 {
745   DALI_PRINT_RENDER_INSTRUCTION(instruction, bufferIndex);
746
747   const Matrix* viewMatrix       = instruction.GetViewMatrix(bufferIndex);
748   const Matrix* projectionMatrix = instruction.GetProjectionMatrix(bufferIndex);
749
750   DALI_ASSERT_DEBUG(viewMatrix);
751   DALI_ASSERT_DEBUG(projectionMatrix);
752
753   if(viewMatrix && projectionMatrix)
754   {
755     std::vector<const Graphics::CommandBuffer*> buffers;
756     const RenderListContainer::SizeType         count = instruction.RenderListCount();
757
758     // Iterate through each render list in order. If a pair of render lists
759     // are marked as interleaved, then process them together.
760     for(RenderListContainer::SizeType index = 0; index < count; ++index)
761     {
762       const RenderList* renderList = instruction.GetRenderList(index);
763
764       if(renderList && !renderList->IsEmpty())
765       {
766         ProcessRenderList(*renderList,
767                           bufferIndex,
768                           *viewMatrix,
769                           *projectionMatrix,
770                           depthBufferAvailable,
771                           stencilBufferAvailable,
772                           boundTextures,
773                           instruction, //added for reflection effect
774                           viewport,
775                           rootClippingRect,
776                           orientation,
777                           sceneSize);
778
779         // Execute command buffer
780         auto* commandBuffer = renderList->GetCommandBuffer();
781         if(commandBuffer)
782         {
783           buffers.push_back(commandBuffer);
784         }
785       }
786     }
787     if(!buffers.empty())
788     {
789       mGraphicsCommandBuffer->ExecuteCommandBuffers(std::move(buffers));
790     }
791   }
792 }
793
794 } // namespace Render
795
796 } // namespace Internal
797
798 } // namespace Dali