Adding Depth/Stencil code
[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     // context cache to ignore the clear if not required, but, we would have to enable
356     // 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     //context.DepthMask(enableDepthWrite);
369     commandBuffer.SetDepthWriteEnable(enableDepthWrite);
370   }
371   else
372   {
373     // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
374     commandBuffer.SetDepthTestEnable(false);
375   }
376 }
377
378 } // Unnamed namespace
379
380 /**
381  * @brief This method is responsible for making decisions on when to apply and unapply scissor clipping, and what rectangular dimensions should be used.
382  * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied.
383  * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation.
384  * @param[in]     item                     The current RenderItem about to be rendered
385  * @param[in,out] commandBuffer            The command buffer to write into
386  * @param[in]     instruction              The render-instruction to process.
387  */
388 inline void RenderAlgorithms::SetupScissorClipping(
389   const RenderItem&        item,
390   Graphics::CommandBuffer& commandBuffer,
391   const RenderInstruction& instruction)
392 {
393   // Get the number of child scissors in the stack (do not include layer or root box).
394   size_t         childStackDepth = mScissorStack.size() - 1u;
395   const uint32_t scissorDepth    = item.mNode->GetScissorDepth();
396   const bool     clippingNode    = item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_TO_BOUNDING_BOX;
397   bool           traversedUpTree = false;
398
399   // If we are using scissor clipping and we are at the same depth (or less), we need to undo previous clips.
400   // We do this by traversing up the scissor clip stack and then apply the appropriate clip for the current render item.
401   // To know this, we use clippingDepth. This value is set on *every* node, but only increased as clipping nodes are hit depth-wise.
402   // So we know if we are at depth 4 and the stackDepth is 5, that we have gone up.
403   // 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.
404   // Note: Stack depth must always be at least 1, as we will have the layer or stage size as the root value.
405   if((childStackDepth > 0u) && (scissorDepth < childStackDepth))
406   {
407     while(scissorDepth < childStackDepth)
408     {
409       mScissorStack.pop_back();
410       --childStackDepth;
411     }
412
413     // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
414     traversedUpTree = true;
415   }
416   if(clippingNode && childStackDepth > 0u && childStackDepth == scissorDepth) // case of sibling clip area
417   {
418     mScissorStack.pop_back();
419     --childStackDepth;
420   }
421
422   // 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.
423   if(clippingNode || traversedUpTree)
424   {
425     // First, check if we are a clipping node.
426     if(clippingNode)
427     {
428       // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
429
430       // Get the AABB bounding box for the current render item.
431       const ClippingBox scissorBox(item.CalculateViewportSpaceAABB(item.mSize, mViewportRectangle.width, mViewportRectangle.height));
432
433       // Get the AABB for the parent item that we must intersect with.
434       const ClippingBox& parentBox(mScissorStack.back());
435
436       // We must reduce the clipping area based on the parents area to allow nested clips. This is a set intersection function.
437       // We add the new scissor box to the stack so we can return to it if needed.
438       mScissorStack.emplace_back(IntersectAABB(parentBox, scissorBox));
439     }
440
441     // 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.
442     // IE. It is not enabled if we are at the top of the stack and the layer does not have a specified clipping box.
443     const bool scissorEnabled = (mScissorStack.size() > 0u) || mHasLayerScissor;
444
445     // Enable the scissor test based on the above calculation
446     if(scissorEnabled)
447     {
448       commandBuffer.SetScissorTestEnable(scissorEnabled);
449     }
450
451     // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
452     if(scissorEnabled)
453     {
454       ClippingBox useScissorBox(mScissorStack.back());
455
456       if(instruction.mFrameBuffer && instruction.GetCamera()->IsYAxisInverted())
457       {
458         useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y;
459       }
460       Graphics::Rect2D scissorBox = {useScissorBox.x, useScissorBox.y, uint32_t(useScissorBox.width), uint32_t(useScissorBox.height)};
461       commandBuffer.SetScissor(scissorBox);
462     }
463   }
464 }
465
466 inline void RenderAlgorithms::SetupClipping(const RenderItem&                   item,
467                                             Graphics::CommandBuffer&            commandBuffer,
468                                             bool&                               usedStencilBuffer,
469                                             uint32_t&                           lastClippingDepth,
470                                             uint32_t&                           lastClippingId,
471                                             Integration::StencilBufferAvailable stencilBufferAvailable,
472                                             const RenderInstruction&            instruction)
473 {
474   RenderMode::Type renderMode = RenderMode::AUTO;
475   const Renderer*  renderer   = item.mRenderer;
476   if(renderer)
477   {
478     renderMode = renderer->GetRenderMode();
479   }
480
481   // Setup the stencil using either the automatic clipping feature, or, the manual per-renderer stencil API.
482   // Note: This switch is in order of most likely value first.
483   switch(renderMode)
484   {
485     case RenderMode::AUTO:
486     {
487       // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
488       commandBuffer.SetColorMask(true);
489
490       // The automatic clipping feature will manage the scissor and stencil functions, only if stencil buffer is available for the latter.
491       // 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.
492       // We process both based on our current and old clipping depths for each mode.
493       // Both methods with return rapidly if there is nothing to be done for that type of clipping.
494       SetupScissorClipping(item, commandBuffer, instruction);
495
496       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
497       {
498         SetupStencilClipping(item, commandBuffer, lastClippingDepth, lastClippingId);
499       }
500       break;
501     }
502
503     case RenderMode::NONE:
504     case RenderMode::COLOR:
505     {
506       // No clipping is performed for these modes.
507       // Note: We do not turn off scissor clipping as it may be used for the whole layer.
508       // The stencil buffer will not be used at all, but we only need to disable it if it's available.
509       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
510       {
511         commandBuffer.SetStencilTestEnable(false);
512       }
513
514       // Setup the color buffer based on the RenderMode.
515       commandBuffer.SetColorMask(renderMode == RenderMode::COLOR);
516       break;
517     }
518
519     case RenderMode::STENCIL:
520     case RenderMode::COLOR_STENCIL:
521     {
522       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
523       {
524         // We are using the low-level Renderer Stencil API.
525         // The stencil buffer must be enabled for every renderer with stencil mode on, as renderers in between can disable it.
526         // Note: As the command state is cached, it is only sent when needed.
527         commandBuffer.SetStencilTestEnable(true);
528
529         // Setup the color buffer based on the RenderMode.
530         commandBuffer.SetColorMask(renderMode == RenderMode::COLOR_STENCIL);
531
532         // If this is the first use of the stencil buffer within this RenderList, clear it (this avoids unnecessary clears).
533         if(!usedStencilBuffer)
534         {
535           commandBuffer.ClearStencilBuffer();
536           usedStencilBuffer = true;
537         }
538
539         // Setup the stencil buffer based on the renderer's properties.
540         commandBuffer.SetStencilOp(GraphicsStencilOp(renderer->GetStencilOperationOnFail()).op,
541                                    GraphicsStencilOp(renderer->GetStencilOperationOnZPass()).op,
542                                    GraphicsStencilOp(renderer->GetStencilOperationOnZFail()).op);
543         commandBuffer.SetStencilFunc(GraphicsStencilCompareOp(renderer->GetStencilFunction()).op,
544                                      renderer->GetStencilFunctionReference(),
545                                      renderer->GetStencilFunctionMask());
546         commandBuffer.SetStencilWriteMask(renderer->GetStencilMask());
547       }
548       break;
549     }
550   }
551 }
552
553 inline void RenderAlgorithms::ProcessRenderList(const RenderList&                   renderList,
554                                                 BufferIndex                         bufferIndex,
555                                                 const Matrix&                       viewMatrix,
556                                                 const Matrix&                       projectionMatrix,
557                                                 Integration::DepthBufferAvailable   depthBufferAvailable,
558                                                 Integration::StencilBufferAvailable stencilBufferAvailable,
559                                                 Vector<Graphics::Texture*>&         boundTextures,
560                                                 const RenderInstruction&            instruction,
561                                                 const Rect<int32_t>&                viewport,
562                                                 const Rect<int>&                    rootClippingRect,
563                                                 int                                 orientation)
564 {
565   DALI_PRINT_RENDER_LIST(renderList);
566
567   // Note: The depth buffer is enabled or disabled on a per-renderer basis.
568   // Here we pre-calculate the value to use if these modes are set to AUTO.
569   const bool        autoDepthTestMode((depthBufferAvailable == Integration::DepthBufferAvailable::TRUE) &&
570                                !(renderList.GetSourceLayer()->IsDepthTestDisabled()) &&
571                                renderList.HasColorRenderItems());
572   const std::size_t count = renderList.Count();
573   uint32_t          lastClippingDepth(0u);
574   uint32_t          lastClippingId(0u);
575   bool              usedStencilBuffer(false);
576   bool              firstDepthBufferUse(true);
577
578   mViewportRectangle = viewport;
579
580   auto* mutableRenderList      = const_cast<RenderList*>(&renderList);
581   auto& secondaryCommandBuffer = mutableRenderList->GetCommandBuffer(mGraphicsController);
582   secondaryCommandBuffer.Reset();
583
584   secondaryCommandBuffer.SetViewport(ViewportFromClippingBox(mViewportRectangle, orientation));
585   mHasLayerScissor = false;
586
587   // Setup Scissor testing (for both viewport and per-node scissor)
588   mScissorStack.clear();
589
590   // Add root clipping rect (set manually for Render function by partial update for example)
591   // on the bottom of the stack
592   if(!rootClippingRect.IsEmpty())
593   {
594     Graphics::Viewport graphicsViewport = ViewportFromClippingBox(mViewportRectangle, 0);
595     secondaryCommandBuffer.SetScissorTestEnable(true);
596     secondaryCommandBuffer.SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport));
597     mScissorStack.push_back(rootClippingRect);
598   }
599   // We are not performing a layer clip and no clipping rect set. Add the viewport as the root scissor rectangle.
600   else if(!renderList.IsClipping())
601   {
602     secondaryCommandBuffer.SetScissorTestEnable(false);
603     mScissorStack.push_back(mViewportRectangle);
604   }
605
606   if(renderList.IsClipping())
607   {
608     Graphics::Viewport graphicsViewport = ViewportFromClippingBox(mViewportRectangle, 0);
609     secondaryCommandBuffer.SetScissorTestEnable(true);
610     const ClippingBox& layerScissorBox = renderList.GetClippingBox();
611     secondaryCommandBuffer.SetScissor(Rect2DFromClippingBox(layerScissorBox, orientation, graphicsViewport));
612     mScissorStack.push_back(layerScissorBox);
613     mHasLayerScissor = true;
614   }
615
616   // Loop through all RenderItems in the RenderList, set up any prerequisites to render them, then perform the render.
617   for(uint32_t index = 0u; index < count; ++index)
618   {
619     const RenderItem& item = renderList.GetItem(index);
620
621     // Discard renderers outside the root clipping rect
622     bool skip = true;
623     if(!rootClippingRect.IsEmpty())
624     {
625       auto rect = item.CalculateViewportSpaceAABB(item.mUpdateSize, mViewportRectangle.width, mViewportRectangle.height);
626
627       if(rect.Intersect(rootClippingRect))
628       {
629         skip = false;
630       }
631     }
632     else
633     {
634       skip = false;
635     }
636
637     DALI_PRINT_RENDER_ITEM(item);
638
639     // Set up clipping based on both the Renderer and Actor APIs.
640     // The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
641     SetupClipping(item, secondaryCommandBuffer, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction);
642
643     if(DALI_LIKELY(item.mRenderer))
644     {
645       // Set up the depth buffer based on per-renderer flags if depth buffer is available
646       // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
647       // draw-mode state, such as Overlays.
648       // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will
649       // always disable depth testing and writing. Color Renderers will enable them if the Layer does.
650       if(depthBufferAvailable == Integration::DepthBufferAvailable::TRUE)
651       {
652         SetupDepthBuffer(item, secondaryCommandBuffer, autoDepthTestMode, firstDepthBufferUse);
653       }
654
655       // Depending on whether the renderer has draw commands attached or not the rendering process will
656       // iterate through all the render queues. If there are no draw commands attached, only one
657       // iteration must be done and the default behaviour of the renderer will be executed.
658       // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
659       // It is similar to the multi-pass rendering.
660       if(!skip)
661       {
662         auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
663         for(auto queue = 0u; queue < MAX_QUEUE; ++queue)
664         {
665           // Render the item. It will write into the command buffer everything it has to render
666           item.mRenderer->Render(secondaryCommandBuffer, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
667         }
668       }
669     }
670   }
671 }
672
673 RenderAlgorithms::RenderAlgorithms(Graphics::Controller& graphicsController)
674 : mGraphicsController(graphicsController),
675   mViewportRectangle(),
676   mHasLayerScissor(false)
677 {
678 }
679
680 void RenderAlgorithms::ResetCommandBuffer()
681 {
682   // Reset main command buffer
683   if(!mGraphicsCommandBuffer)
684   {
685     mGraphicsCommandBuffer = mGraphicsController.CreateCommandBuffer(
686       Graphics::CommandBufferCreateInfo()
687         .SetLevel(Graphics::CommandBufferLevel::PRIMARY),
688       nullptr);
689   }
690   else
691   {
692     mGraphicsCommandBuffer->Reset();
693   }
694 }
695
696 void RenderAlgorithms::SubmitCommandBuffer()
697 {
698   // Submit main command buffer
699   Graphics::SubmitInfo submitInfo;
700   submitInfo.cmdBuffer.push_back(mGraphicsCommandBuffer.get());
701   submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
702   mGraphicsController.SubmitCommandBuffers(submitInfo);
703 }
704
705 void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction&            instruction,
706                                                 BufferIndex                         bufferIndex,
707                                                 Integration::DepthBufferAvailable   depthBufferAvailable,
708                                                 Integration::StencilBufferAvailable stencilBufferAvailable,
709                                                 Vector<Graphics::Texture*>&         boundTextures,
710                                                 const Rect<int32_t>&                viewport,
711                                                 const Rect<int>&                    rootClippingRect,
712                                                 int                                 orientation)
713 {
714   DALI_PRINT_RENDER_INSTRUCTION(instruction, bufferIndex);
715
716   const Matrix* viewMatrix       = instruction.GetViewMatrix(bufferIndex);
717   const Matrix* projectionMatrix = instruction.GetProjectionMatrix(bufferIndex);
718
719   DALI_ASSERT_DEBUG(viewMatrix);
720   DALI_ASSERT_DEBUG(projectionMatrix);
721
722   if(viewMatrix && projectionMatrix)
723   {
724     std::vector<const Graphics::CommandBuffer*> buffers;
725     const RenderListContainer::SizeType         count = instruction.RenderListCount();
726
727     // Iterate through each render list in order. If a pair of render lists
728     // are marked as interleaved, then process them together.
729     for(RenderListContainer::SizeType index = 0; index < count; ++index)
730     {
731       const RenderList* renderList = instruction.GetRenderList(index);
732
733       if(renderList && !renderList->IsEmpty())
734       {
735         ProcessRenderList(*renderList,
736                           bufferIndex,
737                           *viewMatrix,
738                           *projectionMatrix,
739                           depthBufferAvailable,
740                           stencilBufferAvailable,
741                           boundTextures,
742                           instruction, //added for reflection effect
743                           viewport,
744                           rootClippingRect,
745                           orientation);
746
747         // Execute command buffer
748         auto* commandBuffer = renderList->GetCommandBuffer();
749         if(commandBuffer)
750         {
751           buffers.push_back(commandBuffer);
752         }
753       }
754     }
755     if(buffers.size() > 0)
756     {
757       mGraphicsCommandBuffer->ExecuteCommandBuffers(std::move(buffers));
758     }
759   }
760 }
761
762 } // namespace Render
763
764 } // namespace Internal
765
766 } // namespace Dali