2 * Copyright (c) 2022 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(const Uint16Pair& sceneSize, 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 if(orientation == 90)
154 viewport.x = sceneSize.GetY() - (clippingBox.y + clippingBox.height);
155 viewport.y = clippingBox.x;
157 else // orientation == 270
159 viewport.x = clippingBox.y;
160 viewport.y = sceneSize.GetX() - (clippingBox.x + clippingBox.width);
162 viewport.width = static_cast<float>(clippingBox.height);
163 viewport.height = static_cast<float>(clippingBox.width);
165 else if(orientation == 180)
167 viewport.x = sceneSize.GetX() - (clippingBox.x + clippingBox.width);
168 viewport.y = sceneSize.GetY() - (clippingBox.y + clippingBox.height);
173 inline Graphics::Rect2D RecalculateRect(Graphics::Rect2D rect, int orientation, Graphics::Viewport viewport)
175 Graphics::Rect2D newRect;
177 // scissor's value should be set based on the default system coordinates.
178 // when the surface is rotated, the input valus already were set with the rotated angle.
179 // So, re-calculation is needed.
180 if(orientation == 90)
182 newRect.x = viewport.height - (rect.y + rect.height);
184 newRect.width = rect.height;
185 newRect.height = rect.width;
187 else if(orientation == 180)
189 newRect.x = viewport.width - (rect.x + rect.width);
190 newRect.y = viewport.height - (rect.y + rect.height);
191 newRect.width = rect.width;
192 newRect.height = rect.height;
194 else if(orientation == 270)
197 newRect.y = viewport.width - (rect.x + rect.width);
198 newRect.width = rect.height;
199 newRect.height = rect.width;
205 newRect.width = rect.width;
206 newRect.height = rect.height;
211 inline Graphics::Rect2D Rect2DFromClippingBox(ClippingBox clippingBox, int orientation, Graphics::Viewport viewport)
213 Graphics::Rect2D rect2D{clippingBox.x, clippingBox.y, static_cast<uint32_t>(abs(clippingBox.width)), static_cast<uint32_t>(abs(clippingBox.height))};
214 return RecalculateRect(rect2D, orientation, viewport);
217 inline Graphics::Rect2D Rect2DFromRect(Dali::Rect<int> rect, int orientation, Graphics::Viewport viewport)
219 Graphics::Rect2D rect2D{rect.x, rect.y, static_cast<uint32_t>(abs(rect.width)), static_cast<uint32_t>(abs(rect.height))};
220 return RecalculateRect(rect2D, orientation, viewport);
224 * @brief Find the intersection of two AABB rectangles.
225 * This is a logical AND operation. IE. The intersection is the area overlapped by both rectangles.
226 * @param[in] aabbA Rectangle A
227 * @param[in] aabbB Rectangle B
228 * @return The intersection of rectangle A & B (result is a rectangle)
230 inline ClippingBox IntersectAABB(const ClippingBox& aabbA, const ClippingBox& aabbB)
232 ClippingBox intersectionBox;
234 // First calculate the largest starting positions in X and Y.
235 intersectionBox.x = std::max(aabbA.x, aabbB.x);
236 intersectionBox.y = std::max(aabbA.y, aabbB.y);
238 // Now calculate the smallest ending positions, and take the largest starting
239 // positions from the result, to get the width and height respectively.
240 // If the two boxes do not intersect at all, then we need a 0 width and height clipping area.
241 // We use max here to clamp both width and height to >= 0 for this use-case.
242 intersectionBox.width = std::max(std::min(aabbA.x + aabbA.width, aabbB.x + aabbB.width) - intersectionBox.x, 0);
243 intersectionBox.height = std::max(std::min(aabbA.y + aabbA.height, aabbB.y + aabbB.height) - intersectionBox.y, 0);
245 return intersectionBox;
249 * @brief Set up the stencil and color buffer for automatic clipping (StencilMode::AUTO).
250 * @param[in] item The current RenderItem about to be rendered
251 * @param[in,out] commandBuffer The command buffer to write stencil commands into
252 * @param[in,out] lastClippingDepth The stencil depth of the last renderer drawn.
253 * @param[in,out] lastClippingId The clipping ID of the last renderer drawn.
255 inline void SetupStencilClipping(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, uint32_t& lastClippingDepth, uint32_t& lastClippingId)
257 const Dali::Internal::SceneGraph::Node* node = item.mNode;
258 const uint32_t clippingId = node->GetClippingId();
259 // If there is no clipping Id, then either we haven't reached a clipping Node yet, or there aren't any.
260 // Either way we can skip clipping setup for this renderer.
263 // Exit immediately if there are no clipping actions to perform (EG. we have not yet hit a clipping node).
264 commandBuffer.SetStencilTestEnable(false);
267 commandBuffer.SetStencilTestEnable(true);
269 const uint32_t clippingDepth = node->GetClippingDepth();
271 // Pre-calculate a mask which has all bits set up to and including the current clipping depth.
272 // EG. If depth is 3, the mask would be "111" in binary.
273 const uint32_t currentDepthMask = (1u << clippingDepth) - 1u;
275 // Are we are writing to the stencil buffer?
276 if(item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_CHILDREN)
278 // We are writing to the stencil buffer.
279 // If clipping Id is 1, this is the first clipping renderer within this render-list.
282 // We are enabling the stencil-buffer for the first time within this render list.
283 // Clear the buffer at this point.
285 commandBuffer.SetStencilWriteMask(0xFF);
286 commandBuffer.ClearStencilBuffer();
288 else if((clippingDepth < lastClippingDepth) ||
289 ((clippingDepth == lastClippingDepth) && (clippingId > lastClippingId)))
291 // The above if() statement tests if we need to clear some (not all) stencil bit-planes.
292 // We need to do this if either of the following are true:
293 // 1) We traverse up the scene-graph to a previous stencil depth
294 // 2) We are at the same stencil depth but the clipping Id has increased.
296 // This calculation takes the new depth to move to, and creates an inverse-mask of that number of consecutive bits.
297 // This has the effect of clearing everything except the bit-planes up to (and including) our current depth.
298 const uint32_t stencilClearMask = (currentDepthMask >> 1u) ^ 0xff;
300 commandBuffer.SetStencilWriteMask(stencilClearMask);
301 commandBuffer.ClearStencilBuffer();
304 // We keep track of the last clipping Id and depth so we can determine when we are
305 // moving back up the scene graph and require some of the stencil bit-planes to be deleted.
306 lastClippingDepth = clippingDepth;
307 lastClippingId = clippingId;
309 // We only ever write to bit-planes up to the current depth as we may need
310 // to erase individual bit-planes and revert to a previous clipping area.
311 // Our reference value for testing (in StencilFunc) is written to to the buffer, but we actually
312 // want to test a different value. IE. All the bit-planes up to but not including the current depth.
313 // So we use the Mask parameter of StencilFunc to mask off the top bit-plane when testing.
314 // Here we create our test mask to innore the top bit of the reference test value.
315 // As the mask is made up of contiguous "1" values, we can do this quickly with a bit-shift.
316 const uint32_t testMask = currentDepthMask >> 1u;
318 // Test against existing stencil bit-planes. All must match up to (but not including) this depth.
319 commandBuffer.SetStencilFunc(Graphics::CompareOp::EQUAL, currentDepthMask, testMask);
320 // Write to the new stencil bit-plane (the other previous bit-planes are also written to).
321 commandBuffer.SetStencilWriteMask(currentDepthMask);
322 commandBuffer.SetStencilOp(Graphics::StencilOp::KEEP, Graphics::StencilOp::REPLACE, Graphics::StencilOp::REPLACE);
326 // We are reading from the stencil buffer. Set up the stencil accordingly
327 // This calculation sets all the bits up to the current depth bit.
328 // This has the effect of testing that the pixel being written to exists in every bit-plane up to the current depth.
329 commandBuffer.SetStencilFunc(Graphics::CompareOp::EQUAL, currentDepthMask, currentDepthMask);
330 commandBuffer.SetStencilOp(Graphics::StencilOp::KEEP, Graphics::StencilOp::KEEP, Graphics::StencilOp::KEEP);
335 * @brief Sets up the depth buffer for reading and writing based on the current render item.
336 * The items read and write mode are used if specified.
337 * - If AUTO is selected for reading, the decision will be based on the Layer Behavior.
338 * - If AUTO is selected for writing, the decision will be based on the items opacity.
339 * @param[in] item The RenderItem to set up the depth buffer for.
340 * @param[in,out] secondaryCommandBuffer The secondary command buffer to write depth commands to
341 * @param[in] depthTestEnabled True if depth testing has been enabled.
342 * @param[in/out] firstDepthBufferUse Initialize to true on the first call, this method will set it to false afterwards.
344 inline void SetupDepthBuffer(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, bool depthTestEnabled, bool& firstDepthBufferUse)
346 // Set up whether or not to write to the depth buffer.
347 const DepthWriteMode::Type depthWriteMode = item.mRenderer->GetDepthWriteMode();
348 // Most common mode (AUTO) is tested first.
349 const bool enableDepthWrite = ((depthWriteMode == DepthWriteMode::AUTO) && depthTestEnabled && item.mIsOpaque) ||
350 (depthWriteMode == DepthWriteMode::ON);
352 // Set up whether or not to read from (test) the depth buffer.
353 const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
355 // Most common mode (AUTO) is tested first.
356 const bool enableDepthTest = ((depthTestMode == DepthTestMode::AUTO) && depthTestEnabled) ||
357 (depthTestMode == DepthTestMode::ON);
359 // Is the depth buffer in use?
360 if(enableDepthWrite || enableDepthTest)
362 // The depth buffer must be enabled if either reading or writing.
363 commandBuffer.SetDepthTestEnable(true);
365 // Look-up the depth function from the Dali::DepthFunction enum, and set it.
366 commandBuffer.SetDepthCompareOp(GraphicsDepthCompareOp(item.mRenderer->GetDepthFunction()).op);
368 // If this is the first use of the depth buffer this RenderTask, perform a clear.
369 // Note: We could do this at the beginning of the RenderTask and rely on the
370 // graphics implementation to ignore the clear if not required, but, we would
371 // have to enable the depth buffer to do so, which could be a redundant enable.
372 if(DALI_UNLIKELY(firstDepthBufferUse))
374 // This is the first time the depth buffer is being written to or read.
375 firstDepthBufferUse = false;
377 // Note: The buffer will only be cleared if written to since a previous clear.
378 commandBuffer.SetDepthWriteEnable(true);
379 commandBuffer.ClearDepthBuffer();
382 // Set up the depth mask based on our depth write setting.
383 commandBuffer.SetDepthWriteEnable(enableDepthWrite);
387 // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
388 commandBuffer.SetDepthTestEnable(false);
392 } // Unnamed namespace
395 * @brief This method is responsible for making decisions on when to apply and unapply scissor clipping, and what rectangular dimensions should be used.
396 * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied.
397 * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation.
398 * @param[in] item The current RenderItem about to be rendered
399 * @param[in,out] commandBuffer The command buffer to write into
400 * @param[in] instruction The render-instruction to process.
401 * @param[in] orientation The Scene's surface orientation.
403 inline void RenderAlgorithms::SetupScissorClipping(
404 const RenderItem& item,
405 Graphics::CommandBuffer& commandBuffer,
406 const RenderInstruction& instruction,
409 // Get the number of child scissors in the stack (do not include layer or root box).
410 size_t childStackDepth = mScissorStack.size() - 1u;
411 const uint32_t scissorDepth = item.mNode->GetScissorDepth();
412 const bool clippingNode = item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_TO_BOUNDING_BOX;
413 bool traversedUpTree = false;
415 // If we are using scissor clipping and we are at the same depth (or less), we need to undo previous clips.
416 // We do this by traversing up the scissor clip stack and then apply the appropriate clip for the current render item.
417 // To know this, we use clippingDepth. This value is set on *every* node, but only increased as clipping nodes are hit depth-wise.
418 // So we know if we are at depth 4 and the stackDepth is 5, that we have gone up.
419 // If the depth is the same then we are effectively part of a different sub-tree from the parent, we must also remove the current clip.
420 // Note: Stack depth must always be at least 1, as we will have the layer or stage size as the root value.
421 if((childStackDepth > 0u) && (scissorDepth < childStackDepth))
423 while(scissorDepth < childStackDepth)
425 mScissorStack.pop_back();
429 // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
430 traversedUpTree = true;
432 if(clippingNode && childStackDepth > 0u && childStackDepth == scissorDepth) // case of sibling clip area
434 mScissorStack.pop_back();
438 // If we are on a clipping node, or we have traveled up the tree and gone back past a clipping node, may need to apply a new scissor clip.
439 if(clippingNode || traversedUpTree)
441 // First, check if we are a clipping node.
444 // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
446 // Get the AABB bounding box for the current render item.
447 const ClippingBox scissorBox(RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3::ZERO, item.mSize, mViewportRectangle.width, mViewportRectangle.height));
449 // Get the AABB for the parent item that we must intersect with.
450 const ClippingBox& parentBox(mScissorStack.back());
452 // We must reduce the clipping area based on the parents area to allow nested clips. This is a set intersection function.
453 // We add the new scissor box to the stack so we can return to it if needed.
454 mScissorStack.emplace_back(IntersectAABB(parentBox, scissorBox));
457 // The scissor test is enabled if we have any children on the stack, OR, if there are none but it is a user specified layer scissor box.
458 // IE. It is not enabled if we are at the top of the stack and the layer does not have a specified clipping box.
459 const bool scissorEnabled = (!mScissorStack.empty()) || mHasLayerScissor;
461 // Enable the scissor test based on the above calculation
464 commandBuffer.SetScissorTestEnable(scissorEnabled);
467 // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
470 ClippingBox useScissorBox(mScissorStack.back());
472 if(instruction.mFrameBuffer && instruction.GetCamera()->IsYAxisInverted())
474 useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y;
477 Graphics::Viewport graphicsViewport{static_cast<float>(mViewportRectangle.x), static_cast<float>(mViewportRectangle.y), static_cast<float>(mViewportRectangle.width), static_cast<float>(mViewportRectangle.height), 0.0f, 0.0f};
478 commandBuffer.SetScissor(Rect2DFromClippingBox(useScissorBox, orientation, graphicsViewport));
483 // If there is render callback on the Renderer we need to calculate the scissor box and provide it to the
484 // callback so it may be clipped
485 if(item.mRenderer->GetRenderCallback())
487 // store clipping box inside the render callback input structure
488 auto& input = item.mRenderer->GetRenderCallbackInput();
489 input.clippingBox = ClippingBox(RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3::ZERO, item.mSize, mViewportRectangle.width, mViewportRectangle.height));
494 inline void RenderAlgorithms::SetupClipping(const RenderItem& item,
495 Graphics::CommandBuffer& commandBuffer,
496 bool& usedStencilBuffer,
497 uint32_t& lastClippingDepth,
498 uint32_t& lastClippingId,
499 Integration::StencilBufferAvailable stencilBufferAvailable,
500 const RenderInstruction& instruction,
503 RenderMode::Type renderMode = RenderMode::AUTO;
504 const Renderer* renderer = item.mRenderer;
507 renderMode = renderer->GetRenderMode();
510 // Setup the stencil using either the automatic clipping feature, or, the manual per-renderer stencil API.
511 // Note: This switch is in order of most likely value first.
514 case RenderMode::AUTO:
516 // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
517 commandBuffer.SetColorMask(true);
519 // The automatic clipping feature will manage the scissor and stencil functions, only if stencil buffer is available for the latter.
520 // As both scissor and stencil clips can be nested, we may be simultaneously traversing up the scissor tree, requiring a scissor to be un-done. Whilst simultaneously adding a new stencil clip.
521 // We process both based on our current and old clipping depths for each mode.
522 // Both methods with return rapidly if there is nothing to be done for that type of clipping.
523 SetupScissorClipping(item, commandBuffer, instruction, orientation);
525 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
527 SetupStencilClipping(item, commandBuffer, lastClippingDepth, lastClippingId);
532 case RenderMode::NONE:
533 case RenderMode::COLOR:
535 // No clipping is performed for these modes.
536 // Note: We do not turn off scissor clipping as it may be used for the whole layer.
537 // The stencil buffer will not be used at all, but we only need to disable it if it's available.
538 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
540 commandBuffer.SetStencilTestEnable(false);
543 // Setup the color buffer based on the RenderMode.
544 commandBuffer.SetColorMask(renderMode == RenderMode::COLOR);
548 case RenderMode::STENCIL:
549 case RenderMode::COLOR_STENCIL:
551 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
553 // We are using the low-level Renderer Stencil API.
554 // The stencil buffer must be enabled for every renderer with stencil mode on, as renderers in between can disable it.
555 // Note: As the command state is cached, it is only sent when needed.
556 commandBuffer.SetStencilTestEnable(true);
558 // Setup the color buffer based on the RenderMode.
559 commandBuffer.SetColorMask(renderMode == RenderMode::COLOR_STENCIL);
561 // If this is the first use of the stencil buffer within this RenderList, clear it (this avoids unnecessary clears).
562 if(!usedStencilBuffer)
564 commandBuffer.ClearStencilBuffer();
565 usedStencilBuffer = true;
568 // Setup the stencil buffer based on the renderer's properties.
569 commandBuffer.SetStencilOp(GraphicsStencilOp(renderer->GetStencilOperationOnFail()).op,
570 GraphicsStencilOp(renderer->GetStencilOperationOnZPass()).op,
571 GraphicsStencilOp(renderer->GetStencilOperationOnZFail()).op);
572 commandBuffer.SetStencilFunc(GraphicsStencilCompareOp(renderer->GetStencilFunction()).op,
573 renderer->GetStencilFunctionReference(),
574 renderer->GetStencilFunctionMask());
575 commandBuffer.SetStencilWriteMask(renderer->GetStencilMask());
582 inline void RenderAlgorithms::ProcessRenderList(const RenderList& renderList,
583 BufferIndex bufferIndex,
584 const Matrix& viewMatrix,
585 const Matrix& projectionMatrix,
586 Integration::DepthBufferAvailable depthBufferAvailable,
587 Integration::StencilBufferAvailable stencilBufferAvailable,
588 Vector<Graphics::Texture*>& boundTextures,
589 const RenderInstruction& instruction,
590 const Rect<int32_t>& viewport,
591 const Rect<int>& rootClippingRect,
593 const Uint16Pair& sceneSize)
595 DALI_PRINT_RENDER_LIST(renderList);
597 // Note: The depth buffer is enabled or disabled on a per-renderer basis.
598 // Here we pre-calculate the value to use if these modes are set to AUTO.
599 const bool autoDepthTestMode((depthBufferAvailable == Integration::DepthBufferAvailable::TRUE) &&
600 !(renderList.GetSourceLayer()->IsDepthTestDisabled()) &&
601 renderList.HasColorRenderItems());
602 const std::size_t count = renderList.Count();
603 uint32_t lastClippingDepth(0u);
604 uint32_t lastClippingId(0u);
605 bool usedStencilBuffer(false);
606 bool firstDepthBufferUse(true);
608 mViewportRectangle = viewport;
610 auto* mutableRenderList = const_cast<RenderList*>(&renderList);
611 auto& secondaryCommandBuffer = mutableRenderList->GetCommandBuffer(mGraphicsController);
612 secondaryCommandBuffer.Reset();
613 secondaryCommandBuffer.SetViewport(ViewportFromClippingBox(sceneSize, mViewportRectangle, orientation));
614 mHasLayerScissor = false;
616 // Setup Scissor testing (for both viewport and per-node scissor)
617 mScissorStack.clear();
619 // Add root clipping rect (set manually for Render function by partial update for example)
620 // on the bottom of the stack
621 if(!rootClippingRect.IsEmpty())
623 Graphics::Viewport graphicsViewport{static_cast<float>(mViewportRectangle.x), static_cast<float>(mViewportRectangle.y), static_cast<float>(mViewportRectangle.width), static_cast<float>(mViewportRectangle.height), 0.0f, 0.0f};
624 secondaryCommandBuffer.SetScissorTestEnable(true);
625 secondaryCommandBuffer.SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport));
626 mScissorStack.push_back(rootClippingRect);
628 // We are not performing a layer clip and no clipping rect set. Add the viewport as the root scissor rectangle.
629 else if(!renderList.IsClipping())
631 secondaryCommandBuffer.SetScissorTestEnable(false);
632 mScissorStack.push_back(mViewportRectangle);
635 if(renderList.IsClipping())
637 Graphics::Viewport graphicsViewport{static_cast<float>(mViewportRectangle.x), static_cast<float>(mViewportRectangle.y), static_cast<float>(mViewportRectangle.width), static_cast<float>(mViewportRectangle.height), 0.0f, 0.0f};
638 secondaryCommandBuffer.SetScissorTestEnable(true);
639 const ClippingBox& layerScissorBox = renderList.GetClippingBox();
640 secondaryCommandBuffer.SetScissor(Rect2DFromClippingBox(layerScissorBox, orientation, graphicsViewport));
641 mScissorStack.push_back(layerScissorBox);
642 mHasLayerScissor = true;
645 // Loop through all RenderItems in the RenderList, set up any prerequisites to render them, then perform the render.
646 for(uint32_t index = 0u; index < count; ++index)
648 const RenderItem& item = renderList.GetItem(index);
650 // Discard renderers outside the root clipping rect
652 if(!rootClippingRect.IsEmpty())
654 auto rect = RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3(item.mUpdateArea.x, item.mUpdateArea.y, 0.0f), Vector3(item.mUpdateArea.z, item.mUpdateArea.w, 0.0f), mViewportRectangle.width, mViewportRectangle.height);
656 if(rect.Intersect(rootClippingRect))
666 DALI_PRINT_RENDER_ITEM(item);
668 // Set up clipping based on both the Renderer and Actor APIs.
669 // The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
670 SetupClipping(item, secondaryCommandBuffer, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction, orientation);
672 if(DALI_LIKELY(item.mRenderer))
674 // Set up the depth buffer based on per-renderer flags if depth buffer is available
675 // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
676 // draw-mode state, such as Overlays.
677 // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will
678 // always disable depth testing and writing. Color Renderers will enable them if the Layer does.
679 if(depthBufferAvailable == Integration::DepthBufferAvailable::TRUE)
681 SetupDepthBuffer(item, secondaryCommandBuffer, autoDepthTestMode, firstDepthBufferUse);
684 // Depending on whether the renderer has draw commands attached or not the rendering process will
685 // iterate through all the render queues. If there are no draw commands attached, only one
686 // iteration must be done and the default behaviour of the renderer will be executed.
687 // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
688 // It is similar to the multi-pass rendering.
691 auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
692 for(auto queue = 0u; queue < MAX_QUEUE; ++queue)
694 // Render the item. It will write into the command buffer everything it has to render
695 item.mRenderer->Render(secondaryCommandBuffer, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
702 RenderAlgorithms::RenderAlgorithms(Graphics::Controller& graphicsController)
703 : mGraphicsController(graphicsController),
704 mViewportRectangle(),
705 mHasLayerScissor(false)
709 void RenderAlgorithms::ResetCommandBuffer()
711 // Reset main command buffer
712 if(!mGraphicsCommandBuffer)
714 mGraphicsCommandBuffer = mGraphicsController.CreateCommandBuffer(
715 Graphics::CommandBufferCreateInfo()
716 .SetLevel(Graphics::CommandBufferLevel::PRIMARY),
721 mGraphicsCommandBuffer->Reset();
725 void RenderAlgorithms::SubmitCommandBuffer()
727 // Submit main command buffer
728 Graphics::SubmitInfo submitInfo;
729 submitInfo.cmdBuffer.push_back(mGraphicsCommandBuffer.get());
730 submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
731 mGraphicsController.SubmitCommandBuffers(submitInfo);
734 void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction& instruction,
735 BufferIndex bufferIndex,
736 Integration::DepthBufferAvailable depthBufferAvailable,
737 Integration::StencilBufferAvailable stencilBufferAvailable,
738 Vector<Graphics::Texture*>& boundTextures,
739 const Rect<int32_t>& viewport,
740 const Rect<int>& rootClippingRect,
742 const Uint16Pair& sceneSize)
744 DALI_PRINT_RENDER_INSTRUCTION(instruction, bufferIndex);
746 const Matrix* viewMatrix = instruction.GetViewMatrix(bufferIndex);
747 const Matrix* projectionMatrix = instruction.GetProjectionMatrix(bufferIndex);
749 DALI_ASSERT_DEBUG(viewMatrix);
750 DALI_ASSERT_DEBUG(projectionMatrix);
752 if(viewMatrix && projectionMatrix)
754 std::vector<const Graphics::CommandBuffer*> buffers;
755 const RenderListContainer::SizeType count = instruction.RenderListCount();
757 // Iterate through each render list in order. If a pair of render lists
758 // are marked as interleaved, then process them together.
759 for(RenderListContainer::SizeType index = 0; index < count; ++index)
761 const RenderList* renderList = instruction.GetRenderList(index);
763 if(renderList && !renderList->IsEmpty())
765 ProcessRenderList(*renderList,
769 depthBufferAvailable,
770 stencilBufferAvailable,
772 instruction, //added for reflection effect
778 // Execute command buffer
779 auto* commandBuffer = renderList->GetCommandBuffer();
782 buffers.push_back(commandBuffer);
788 mGraphicsCommandBuffer->ExecuteCommandBuffers(std::move(buffers));
793 } // namespace Render
795 } // namespace Internal