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