2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/render/common/render-algorithms.h>
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>
28 using Dali::Internal::SceneGraph::RenderInstruction;
29 using Dali::Internal::SceneGraph::RenderItem;
30 using Dali::Internal::SceneGraph::RenderList;
31 using Dali::Internal::SceneGraph::RenderListContainer;
41 struct GraphicsDepthCompareOp
43 constexpr explicit GraphicsDepthCompareOp(DepthFunction::Type compareOp)
47 case DepthFunction::NEVER:
48 op = Graphics::CompareOp::NEVER;
50 case DepthFunction::LESS:
51 op = Graphics::CompareOp::LESS;
53 case DepthFunction::EQUAL:
54 op = Graphics::CompareOp::EQUAL;
56 case DepthFunction::LESS_EQUAL:
57 op = Graphics::CompareOp::LESS_OR_EQUAL;
59 case DepthFunction::GREATER:
60 op = Graphics::CompareOp::GREATER;
62 case DepthFunction::NOT_EQUAL:
63 op = Graphics::CompareOp::NOT_EQUAL;
65 case DepthFunction::GREATER_EQUAL:
66 op = Graphics::CompareOp::GREATER_OR_EQUAL;
68 case DepthFunction::ALWAYS:
69 op = Graphics::CompareOp::ALWAYS;
73 Graphics::CompareOp op{Graphics::CompareOp::NEVER};
76 struct GraphicsStencilCompareOp
78 constexpr explicit GraphicsStencilCompareOp(StencilFunction::Type compareOp)
82 case StencilFunction::NEVER:
83 op = Graphics::CompareOp::NEVER;
85 case StencilFunction::LESS:
86 op = Graphics::CompareOp::LESS;
88 case StencilFunction::EQUAL:
89 op = Graphics::CompareOp::EQUAL;
91 case StencilFunction::LESS_EQUAL:
92 op = Graphics::CompareOp::LESS_OR_EQUAL;
94 case StencilFunction::GREATER:
95 op = Graphics::CompareOp::GREATER;
97 case StencilFunction::NOT_EQUAL:
98 op = Graphics::CompareOp::NOT_EQUAL;
100 case StencilFunction::GREATER_EQUAL:
101 op = Graphics::CompareOp::GREATER_OR_EQUAL;
103 case StencilFunction::ALWAYS:
104 op = Graphics::CompareOp::ALWAYS;
108 Graphics::CompareOp op{Graphics::CompareOp::NEVER};
111 struct GraphicsStencilOp
113 constexpr explicit GraphicsStencilOp(StencilOperation::Type stencilOp)
117 case Dali::StencilOperation::KEEP:
118 op = Graphics::StencilOp::KEEP;
120 case Dali::StencilOperation::ZERO:
121 op = Graphics::StencilOp::ZERO;
123 case Dali::StencilOperation::REPLACE:
124 op = Graphics::StencilOp::REPLACE;
126 case Dali::StencilOperation::INCREMENT:
127 op = Graphics::StencilOp::INCREMENT_AND_CLAMP;
129 case Dali::StencilOperation::DECREMENT:
130 op = Graphics::StencilOp::DECREMENT_AND_CLAMP;
132 case Dali::StencilOperation::INVERT:
133 op = Graphics::StencilOp::INVERT;
135 case Dali::StencilOperation::INCREMENT_WRAP:
136 op = Graphics::StencilOp::INCREMENT_AND_WRAP;
138 case Dali::StencilOperation::DECREMENT_WRAP:
139 op = Graphics::StencilOp::DECREMENT_AND_WRAP;
143 Graphics::StencilOp op{Graphics::StencilOp::KEEP};
146 inline Graphics::Viewport ViewportFromClippingBox(ClippingBox clippingBox, int orientation)
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};
150 if(orientation == 90 || orientation == 270)
152 viewport.width = static_cast<float>(clippingBox.height);
153 viewport.height = static_cast<float>(clippingBox.width);
158 inline Graphics::Rect2D RecalculateRect(Graphics::Rect2D rect, int orientation, Graphics::Viewport viewport)
160 Graphics::Rect2D newRect;
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)
167 newRect.x = viewport.height - (rect.y + rect.height);
169 newRect.width = rect.height;
170 newRect.height = rect.width;
172 else if(orientation == 180)
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;
179 else if(orientation == 270)
182 newRect.y = viewport.width - (rect.x + rect.width);
183 newRect.width = rect.height;
184 newRect.height = rect.width;
190 newRect.width = rect.width;
191 newRect.height = rect.height;
196 inline Graphics::Rect2D Rect2DFromClippingBox(ClippingBox clippingBox, int orientation, Graphics::Viewport viewport)
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);
202 inline Graphics::Rect2D Rect2DFromRect(Dali::Rect<int> rect, int orientation, Graphics::Viewport viewport)
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);
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)
215 inline ClippingBox IntersectAABB(const ClippingBox& aabbA, const ClippingBox& aabbB)
217 ClippingBox intersectionBox;
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);
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);
230 return intersectionBox;
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.
240 inline void SetupStencilClipping(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, uint32_t& lastClippingDepth, uint32_t& lastClippingId)
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.
248 // Exit immediately if there are no clipping actions to perform (EG. we have not yet hit a clipping node).
249 commandBuffer.SetStencilTestEnable(false);
252 commandBuffer.SetStencilTestEnable(true);
254 const uint32_t clippingDepth = node->GetClippingDepth();
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;
260 // Are we are writing to the stencil buffer?
261 if(item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_CHILDREN)
263 // We are writing to the stencil buffer.
264 // If clipping Id is 1, this is the first clipping renderer within this render-list.
267 // We are enabling the stencil-buffer for the first time within this render list.
268 // Clear the buffer at this point.
270 commandBuffer.SetStencilWriteMask(0xFF);
271 commandBuffer.ClearStencilBuffer();
273 else if((clippingDepth < lastClippingDepth) ||
274 ((clippingDepth == lastClippingDepth) && (clippingId > lastClippingId)))
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.
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;
285 commandBuffer.SetStencilWriteMask(stencilClearMask);
286 commandBuffer.ClearStencilBuffer();
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;
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;
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);
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);
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.
329 inline void SetupDepthBuffer(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, bool depthTestEnabled, bool& firstDepthBufferUse)
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);
337 // Set up whether or not to read from (test) the depth buffer.
338 const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
340 // Most common mode (AUTO) is tested first.
341 const bool enableDepthTest = ((depthTestMode == DepthTestMode::AUTO) && depthTestEnabled) ||
342 (depthTestMode == DepthTestMode::ON);
344 // Is the depth buffer in use?
345 if(enableDepthWrite || enableDepthTest)
347 // The depth buffer must be enabled if either reading or writing.
348 commandBuffer.SetDepthTestEnable(true);
350 // Look-up the depth function from the Dali::DepthFunction enum, and set it.
351 commandBuffer.SetDepthCompareOp(GraphicsDepthCompareOp(item.mRenderer->GetDepthFunction()).op);
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))
359 // This is the first time the depth buffer is being written to or read.
360 firstDepthBufferUse = false;
362 // Note: The buffer will only be cleared if written to since a previous clear.
363 commandBuffer.SetDepthWriteEnable(true);
364 commandBuffer.ClearDepthBuffer();
367 // Set up the depth mask based on our depth write setting.
368 //context.DepthMask(enableDepthWrite);
369 commandBuffer.SetDepthWriteEnable(enableDepthWrite);
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);
378 } // Unnamed namespace
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.
388 inline void RenderAlgorithms::SetupScissorClipping(
389 const RenderItem& item,
390 Graphics::CommandBuffer& commandBuffer,
391 const RenderInstruction& instruction)
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;
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))
407 while(scissorDepth < childStackDepth)
409 mScissorStack.pop_back();
413 // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
414 traversedUpTree = true;
416 if(clippingNode && childStackDepth > 0u && childStackDepth == scissorDepth) // case of sibling clip area
418 mScissorStack.pop_back();
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)
425 // First, check if we are a clipping node.
428 // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
430 // Get the AABB bounding box for the current render item.
431 const ClippingBox scissorBox(item.CalculateViewportSpaceAABB(item.mSize, mViewportRectangle.width, mViewportRectangle.height));
433 // Get the AABB for the parent item that we must intersect with.
434 const ClippingBox& parentBox(mScissorStack.back());
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));
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;
445 // Enable the scissor test based on the above calculation
448 commandBuffer.SetScissorTestEnable(scissorEnabled);
451 // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
454 ClippingBox useScissorBox(mScissorStack.back());
456 if(instruction.mFrameBuffer && instruction.GetCamera()->IsYAxisInverted())
458 useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y;
460 Graphics::Rect2D scissorBox = {useScissorBox.x, useScissorBox.y, uint32_t(useScissorBox.width), uint32_t(useScissorBox.height)};
461 commandBuffer.SetScissor(scissorBox);
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)
474 RenderMode::Type renderMode = RenderMode::AUTO;
475 const Renderer* renderer = item.mRenderer;
478 renderMode = renderer->GetRenderMode();
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.
485 case RenderMode::AUTO:
487 // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
488 commandBuffer.SetColorMask(true);
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);
496 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
498 SetupStencilClipping(item, commandBuffer, lastClippingDepth, lastClippingId);
503 case RenderMode::NONE:
504 case RenderMode::COLOR:
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)
511 commandBuffer.SetStencilTestEnable(false);
514 // Setup the color buffer based on the RenderMode.
515 commandBuffer.SetColorMask(renderMode == RenderMode::COLOR);
519 case RenderMode::STENCIL:
520 case RenderMode::COLOR_STENCIL:
522 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
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);
529 // Setup the color buffer based on the RenderMode.
530 commandBuffer.SetColorMask(renderMode == RenderMode::COLOR_STENCIL);
532 // If this is the first use of the stencil buffer within this RenderList, clear it (this avoids unnecessary clears).
533 if(!usedStencilBuffer)
535 commandBuffer.ClearStencilBuffer();
536 usedStencilBuffer = true;
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());
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,
565 DALI_PRINT_RENDER_LIST(renderList);
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);
578 mViewportRectangle = viewport;
580 auto* mutableRenderList = const_cast<RenderList*>(&renderList);
581 auto& secondaryCommandBuffer = mutableRenderList->GetCommandBuffer(mGraphicsController);
582 secondaryCommandBuffer.Reset();
584 secondaryCommandBuffer.SetViewport(ViewportFromClippingBox(mViewportRectangle, orientation));
585 mHasLayerScissor = false;
587 // Setup Scissor testing (for both viewport and per-node scissor)
588 mScissorStack.clear();
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())
594 Graphics::Viewport graphicsViewport = ViewportFromClippingBox(mViewportRectangle, 0);
595 secondaryCommandBuffer.SetScissorTestEnable(true);
596 secondaryCommandBuffer.SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport));
597 mScissorStack.push_back(rootClippingRect);
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())
602 secondaryCommandBuffer.SetScissorTestEnable(false);
603 mScissorStack.push_back(mViewportRectangle);
606 if(renderList.IsClipping())
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;
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)
619 const RenderItem& item = renderList.GetItem(index);
621 // Discard renderers outside the root clipping rect
623 if(!rootClippingRect.IsEmpty())
625 auto rect = item.CalculateViewportSpaceAABB(item.mUpdateSize, mViewportRectangle.width, mViewportRectangle.height);
627 if(rect.Intersect(rootClippingRect))
637 DALI_PRINT_RENDER_ITEM(item);
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);
643 if(DALI_LIKELY(item.mRenderer))
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)
652 SetupDepthBuffer(item, secondaryCommandBuffer, autoDepthTestMode, firstDepthBufferUse);
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.
662 auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
663 for(auto queue = 0u; queue < MAX_QUEUE; ++queue)
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);
673 RenderAlgorithms::RenderAlgorithms(Graphics::Controller& graphicsController)
674 : mGraphicsController(graphicsController),
675 mViewportRectangle(),
676 mHasLayerScissor(false)
680 void RenderAlgorithms::ResetCommandBuffer()
682 // Reset main command buffer
683 if(!mGraphicsCommandBuffer)
685 mGraphicsCommandBuffer = mGraphicsController.CreateCommandBuffer(
686 Graphics::CommandBufferCreateInfo()
687 .SetLevel(Graphics::CommandBufferLevel::PRIMARY),
692 mGraphicsCommandBuffer->Reset();
696 void RenderAlgorithms::SubmitCommandBuffer()
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);
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,
714 DALI_PRINT_RENDER_INSTRUCTION(instruction, bufferIndex);
716 const Matrix* viewMatrix = instruction.GetViewMatrix(bufferIndex);
717 const Matrix* projectionMatrix = instruction.GetProjectionMatrix(bufferIndex);
719 DALI_ASSERT_DEBUG(viewMatrix);
720 DALI_ASSERT_DEBUG(projectionMatrix);
722 if(viewMatrix && projectionMatrix)
724 std::vector<const Graphics::CommandBuffer*> buffers;
725 const RenderListContainer::SizeType count = instruction.RenderListCount();
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)
731 const RenderList* renderList = instruction.GetRenderList(index);
733 if(renderList && !renderList->IsEmpty())
735 ProcessRenderList(*renderList,
739 depthBufferAvailable,
740 stencilBufferAvailable,
742 instruction, //added for reflection effect
747 // Execute command buffer
748 auto* commandBuffer = renderList->GetCommandBuffer();
751 buffers.push_back(commandBuffer);
755 if(buffers.size() > 0)
757 mGraphicsCommandBuffer->ExecuteCommandBuffers(std::move(buffers));
762 } // namespace Render
764 } // namespace Internal