2 * Copyright (c) 2023 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>
27 #include <dali/public-api/math/uint-16-pair.h>
29 using Dali::Internal::SceneGraph::RenderInstruction;
30 using Dali::Internal::SceneGraph::RenderItem;
31 using Dali::Internal::SceneGraph::RenderList;
32 using Dali::Internal::SceneGraph::RenderListContainer;
42 struct GraphicsDepthCompareOp
44 constexpr explicit GraphicsDepthCompareOp(DepthFunction::Type compareOp)
48 case DepthFunction::NEVER:
49 op = Graphics::CompareOp::NEVER;
51 case DepthFunction::LESS:
52 op = Graphics::CompareOp::LESS;
54 case DepthFunction::EQUAL:
55 op = Graphics::CompareOp::EQUAL;
57 case DepthFunction::LESS_EQUAL:
58 op = Graphics::CompareOp::LESS_OR_EQUAL;
60 case DepthFunction::GREATER:
61 op = Graphics::CompareOp::GREATER;
63 case DepthFunction::NOT_EQUAL:
64 op = Graphics::CompareOp::NOT_EQUAL;
66 case DepthFunction::GREATER_EQUAL:
67 op = Graphics::CompareOp::GREATER_OR_EQUAL;
69 case DepthFunction::ALWAYS:
70 op = Graphics::CompareOp::ALWAYS;
74 Graphics::CompareOp op{Graphics::CompareOp::NEVER};
77 struct GraphicsStencilCompareOp
79 constexpr explicit GraphicsStencilCompareOp(StencilFunction::Type compareOp)
83 case StencilFunction::NEVER:
84 op = Graphics::CompareOp::NEVER;
86 case StencilFunction::LESS:
87 op = Graphics::CompareOp::LESS;
89 case StencilFunction::EQUAL:
90 op = Graphics::CompareOp::EQUAL;
92 case StencilFunction::LESS_EQUAL:
93 op = Graphics::CompareOp::LESS_OR_EQUAL;
95 case StencilFunction::GREATER:
96 op = Graphics::CompareOp::GREATER;
98 case StencilFunction::NOT_EQUAL:
99 op = Graphics::CompareOp::NOT_EQUAL;
101 case StencilFunction::GREATER_EQUAL:
102 op = Graphics::CompareOp::GREATER_OR_EQUAL;
104 case StencilFunction::ALWAYS:
105 op = Graphics::CompareOp::ALWAYS;
109 Graphics::CompareOp op{Graphics::CompareOp::NEVER};
112 struct GraphicsStencilOp
114 constexpr explicit GraphicsStencilOp(StencilOperation::Type stencilOp)
118 case Dali::StencilOperation::KEEP:
119 op = Graphics::StencilOp::KEEP;
121 case Dali::StencilOperation::ZERO:
122 op = Graphics::StencilOp::ZERO;
124 case Dali::StencilOperation::REPLACE:
125 op = Graphics::StencilOp::REPLACE;
127 case Dali::StencilOperation::INCREMENT:
128 op = Graphics::StencilOp::INCREMENT_AND_CLAMP;
130 case Dali::StencilOperation::DECREMENT:
131 op = Graphics::StencilOp::DECREMENT_AND_CLAMP;
133 case Dali::StencilOperation::INVERT:
134 op = Graphics::StencilOp::INVERT;
136 case Dali::StencilOperation::INCREMENT_WRAP:
137 op = Graphics::StencilOp::INCREMENT_AND_WRAP;
139 case Dali::StencilOperation::DECREMENT_WRAP:
140 op = Graphics::StencilOp::DECREMENT_AND_WRAP;
144 Graphics::StencilOp op{Graphics::StencilOp::KEEP};
147 inline Graphics::Viewport ViewportFromClippingBox(const Uint16Pair& sceneSize, ClippingBox clippingBox, int orientation)
149 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};
151 if(orientation == 90 || orientation == 270)
153 if(orientation == 90)
155 viewport.x = sceneSize.GetY() - (clippingBox.y + clippingBox.height);
156 viewport.y = clippingBox.x;
158 else // orientation == 270
160 viewport.x = clippingBox.y;
161 viewport.y = sceneSize.GetX() - (clippingBox.x + clippingBox.width);
163 viewport.width = static_cast<float>(clippingBox.height);
164 viewport.height = static_cast<float>(clippingBox.width);
166 else if(orientation == 180)
168 viewport.x = sceneSize.GetX() - (clippingBox.x + clippingBox.width);
169 viewport.y = sceneSize.GetY() - (clippingBox.y + clippingBox.height);
174 inline Graphics::Rect2D RecalculateRect(Graphics::Rect2D rect, int orientation, Graphics::Viewport viewport)
176 Graphics::Rect2D newRect;
178 // scissor's value should be set based on the default system coordinates.
179 // when the surface is rotated, the input valus already were set with the rotated angle.
180 // So, re-calculation is needed.
181 if(orientation == 90)
183 newRect.x = viewport.height - (rect.y + rect.height);
185 newRect.width = rect.height;
186 newRect.height = rect.width;
188 else if(orientation == 180)
190 newRect.x = viewport.width - (rect.x + rect.width);
191 newRect.y = viewport.height - (rect.y + rect.height);
192 newRect.width = rect.width;
193 newRect.height = rect.height;
195 else if(orientation == 270)
198 newRect.y = viewport.width - (rect.x + rect.width);
199 newRect.width = rect.height;
200 newRect.height = rect.width;
206 newRect.width = rect.width;
207 newRect.height = rect.height;
212 inline Graphics::Rect2D Rect2DFromClippingBox(ClippingBox clippingBox, int orientation, Graphics::Viewport viewport)
214 Graphics::Rect2D rect2D{clippingBox.x, clippingBox.y, static_cast<uint32_t>(abs(clippingBox.width)), static_cast<uint32_t>(abs(clippingBox.height))};
215 return RecalculateRect(rect2D, orientation, viewport);
218 inline Graphics::Rect2D Rect2DFromRect(Dali::Rect<int> rect, int orientation, Graphics::Viewport viewport)
220 Graphics::Rect2D rect2D{rect.x, rect.y, static_cast<uint32_t>(abs(rect.width)), static_cast<uint32_t>(abs(rect.height))};
221 return RecalculateRect(rect2D, orientation, viewport);
225 * @brief Find the intersection of two AABB rectangles.
226 * This is a logical AND operation. IE. The intersection is the area overlapped by both rectangles.
227 * @param[in] aabbA Rectangle A
228 * @param[in] aabbB Rectangle B
229 * @return The intersection of rectangle A & B (result is a rectangle)
231 inline ClippingBox IntersectAABB(const ClippingBox& aabbA, const ClippingBox& aabbB)
233 ClippingBox intersectionBox;
235 // First calculate the largest starting positions in X and Y.
236 intersectionBox.x = std::max(aabbA.x, aabbB.x);
237 intersectionBox.y = std::max(aabbA.y, aabbB.y);
239 // Now calculate the smallest ending positions, and take the largest starting
240 // positions from the result, to get the width and height respectively.
241 // If the two boxes do not intersect at all, then we need a 0 width and height clipping area.
242 // We use max here to clamp both width and height to >= 0 for this use-case.
243 intersectionBox.width = std::max(std::min(aabbA.x + aabbA.width, aabbB.x + aabbB.width) - intersectionBox.x, 0);
244 intersectionBox.height = std::max(std::min(aabbA.y + aabbA.height, aabbB.y + aabbB.height) - intersectionBox.y, 0);
246 return intersectionBox;
250 * @brief Set up the stencil and color buffer for automatic clipping (StencilMode::AUTO).
251 * @param[in] item The current RenderItem about to be rendered
252 * @param[in,out] commandBuffer The command buffer to write stencil commands into
253 * @param[in,out] lastClippingDepth The stencil depth of the last renderer drawn.
254 * @param[in,out] lastClippingId The clipping ID of the last renderer drawn.
256 inline void SetupStencilClipping(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, uint32_t& lastClippingDepth, uint32_t& lastClippingId)
258 const Dali::Internal::SceneGraph::Node* node = item.mNode;
259 const uint32_t clippingId = node->GetClippingId();
260 // If there is no clipping Id, then either we haven't reached a clipping Node yet, or there aren't any.
261 // Either way we can skip clipping setup for this renderer.
264 // Exit immediately if there are no clipping actions to perform (EG. we have not yet hit a clipping node).
265 commandBuffer.SetStencilTestEnable(false);
268 commandBuffer.SetStencilTestEnable(true);
270 const uint32_t clippingDepth = node->GetClippingDepth();
272 // Pre-calculate a mask which has all bits set up to and including the current clipping depth.
273 // EG. If depth is 3, the mask would be "111" in binary.
274 const uint32_t currentDepthMask = (1u << clippingDepth) - 1u;
276 // Are we are writing to the stencil buffer?
277 if(item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_CHILDREN)
279 // We are writing to the stencil buffer.
280 // If clipping Id is 1, this is the first clipping renderer within this render-list.
283 // We are enabling the stencil-buffer for the first time within this render list.
284 // Clear the buffer at this point.
286 commandBuffer.SetStencilWriteMask(0xFF);
287 commandBuffer.ClearStencilBuffer();
289 else if((clippingDepth < lastClippingDepth) ||
290 ((clippingDepth == lastClippingDepth) && (clippingId > lastClippingId)))
292 // The above if() statement tests if we need to clear some (not all) stencil bit-planes.
293 // We need to do this if either of the following are true:
294 // 1) We traverse up the scene-graph to a previous stencil depth
295 // 2) We are at the same stencil depth but the clipping Id has increased.
297 // This calculation takes the new depth to move to, and creates an inverse-mask of that number of consecutive bits.
298 // This has the effect of clearing everything except the bit-planes up to (and including) our current depth.
299 const uint32_t stencilClearMask = (currentDepthMask >> 1u) ^ 0xff;
301 commandBuffer.SetStencilWriteMask(stencilClearMask);
302 commandBuffer.ClearStencilBuffer();
305 // We keep track of the last clipping Id and depth so we can determine when we are
306 // moving back up the scene graph and require some of the stencil bit-planes to be deleted.
307 lastClippingDepth = clippingDepth;
308 lastClippingId = clippingId;
310 // We only ever write to bit-planes up to the current depth as we may need
311 // to erase individual bit-planes and revert to a previous clipping area.
312 // Our reference value for testing (in StencilFunc) is written to to the buffer, but we actually
313 // want to test a different value. IE. All the bit-planes up to but not including the current depth.
314 // So we use the Mask parameter of StencilFunc to mask off the top bit-plane when testing.
315 // Here we create our test mask to innore the top bit of the reference test value.
316 // As the mask is made up of contiguous "1" values, we can do this quickly with a bit-shift.
317 const uint32_t testMask = currentDepthMask >> 1u;
319 // Test against existing stencil bit-planes. All must match up to (but not including) this depth.
320 commandBuffer.SetStencilFunc(Graphics::CompareOp::EQUAL, currentDepthMask, testMask);
321 // Write to the new stencil bit-plane (the other previous bit-planes are also written to).
322 commandBuffer.SetStencilWriteMask(currentDepthMask);
323 commandBuffer.SetStencilOp(Graphics::StencilOp::KEEP, Graphics::StencilOp::REPLACE, Graphics::StencilOp::REPLACE);
327 // We are reading from the stencil buffer. Set up the stencil accordingly
328 // This calculation sets all the bits up to the current depth bit.
329 // This has the effect of testing that the pixel being written to exists in every bit-plane up to the current depth.
330 commandBuffer.SetStencilFunc(Graphics::CompareOp::EQUAL, currentDepthMask, currentDepthMask);
331 commandBuffer.SetStencilOp(Graphics::StencilOp::KEEP, Graphics::StencilOp::KEEP, Graphics::StencilOp::KEEP);
336 * @brief Sets up the depth buffer for reading and writing based on the current render item.
337 * The items read and write mode are used if specified.
338 * - If AUTO is selected for reading, the decision will be based on the Layer Behavior.
339 * - If AUTO is selected for writing, the decision will be based on the items opacity.
340 * @param[in] item The RenderItem to set up the depth buffer for.
341 * @param[in,out] secondaryCommandBuffer The secondary command buffer to write depth commands to
342 * @param[in] depthTestEnabled True if depth testing has been enabled.
343 * @param[in/out] firstDepthBufferUse Initialize to true on the first call, this method will set it to false afterwards.
345 inline void SetupDepthBuffer(const RenderItem& item, Graphics::CommandBuffer& commandBuffer, bool depthTestEnabled, bool& firstDepthBufferUse)
347 // Set up whether or not to write to the depth buffer.
348 const DepthWriteMode::Type depthWriteMode = item.mRenderer->GetDepthWriteMode();
349 // Most common mode (AUTO) is tested first.
350 const bool enableDepthWrite = ((depthWriteMode == DepthWriteMode::AUTO) && depthTestEnabled && item.mIsOpaque) ||
351 (depthWriteMode == DepthWriteMode::ON);
353 // Set up whether or not to read from (test) the depth buffer.
354 const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
356 // Most common mode (AUTO) is tested first.
357 const bool enableDepthTest = ((depthTestMode == DepthTestMode::AUTO) && depthTestEnabled) ||
358 (depthTestMode == DepthTestMode::ON);
360 // Is the depth buffer in use?
361 if(enableDepthWrite || enableDepthTest)
363 // The depth buffer must be enabled if either reading or writing.
364 commandBuffer.SetDepthTestEnable(true);
366 // Look-up the depth function from the Dali::DepthFunction enum, and set it.
367 commandBuffer.SetDepthCompareOp(GraphicsDepthCompareOp(item.mRenderer->GetDepthFunction()).op);
369 // If this is the first use of the depth buffer this RenderTask, perform a clear.
370 // Note: We could do this at the beginning of the RenderTask and rely on the
371 // graphics implementation to ignore the clear if not required, but, we would
372 // have to enable the depth buffer to do so, which could be a redundant enable.
373 if(DALI_UNLIKELY(firstDepthBufferUse))
375 // This is the first time the depth buffer is being written to or read.
376 firstDepthBufferUse = false;
378 // Note: The buffer will only be cleared if written to since a previous clear.
379 commandBuffer.SetDepthWriteEnable(true);
380 commandBuffer.ClearDepthBuffer();
383 // Set up the depth mask based on our depth write setting.
384 commandBuffer.SetDepthWriteEnable(enableDepthWrite);
388 // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
389 commandBuffer.SetDepthTestEnable(false);
393 } // Unnamed namespace
396 * @brief This method is responsible for making decisions on when to apply and unapply scissor clipping, and what rectangular dimensions should be used.
397 * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied.
398 * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation.
399 * @param[in] item The current RenderItem about to be rendered
400 * @param[in,out] commandBuffer The command buffer to write into
401 * @param[in] instruction The render-instruction to process.
402 * @param[in] orientation The Scene's surface orientation.
404 inline void RenderAlgorithms::SetupScissorClipping(
405 const RenderItem& item,
406 Graphics::CommandBuffer& commandBuffer,
407 const RenderInstruction& instruction,
410 // Get the number of child scissors in the stack (do not include layer or root box).
411 size_t childStackDepth = mScissorStack.size() - 1u;
412 const uint32_t scissorDepth = item.mNode->GetScissorDepth();
413 const bool clippingNode = item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_TO_BOUNDING_BOX;
414 bool traversedUpTree = false;
416 // If we are using scissor clipping and we are at the same depth (or less), we need to undo previous clips.
417 // We do this by traversing up the scissor clip stack and then apply the appropriate clip for the current render item.
418 // To know this, we use clippingDepth. This value is set on *every* node, but only increased as clipping nodes are hit depth-wise.
419 // So we know if we are at depth 4 and the stackDepth is 5, that we have gone up.
420 // 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.
421 // Note: Stack depth must always be at least 1, as we will have the layer or stage size as the root value.
422 if((childStackDepth > 0u) && (scissorDepth < childStackDepth))
424 while(scissorDepth < childStackDepth)
426 mScissorStack.pop_back();
430 // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
431 traversedUpTree = true;
433 if(clippingNode && childStackDepth > 0u && childStackDepth == scissorDepth) // case of sibling clip area
435 mScissorStack.pop_back();
439 // 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.
440 if(clippingNode || traversedUpTree)
442 // First, check if we are a clipping node.
445 // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
447 // Get the AABB bounding box for the current render item.
448 const ClippingBox scissorBox(RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3::ZERO, item.mSize, mViewportRectangle.width, mViewportRectangle.height));
450 // Get the AABB for the parent item that we must intersect with.
451 const ClippingBox& parentBox(mScissorStack.back());
453 // We must reduce the clipping area based on the parents area to allow nested clips. This is a set intersection function.
454 // We add the new scissor box to the stack so we can return to it if needed.
455 mScissorStack.emplace_back(IntersectAABB(parentBox, scissorBox));
458 // 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.
459 // IE. It is not enabled if we are at the top of the stack and the layer does not have a specified clipping box.
460 const bool scissorEnabled = (!mScissorStack.empty()) || mHasLayerScissor;
462 // Enable the scissor test based on the above calculation
465 commandBuffer.SetScissorTestEnable(scissorEnabled);
468 // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
471 ClippingBox useScissorBox(mScissorStack.back());
473 if(instruction.mFrameBuffer && instruction.GetCamera()->IsYAxisInverted())
475 useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y;
478 Graphics::Viewport graphicsViewport = ViewportFromClippingBox(Uint16Pair{0, 0}, mViewportRectangle, 0);
479 commandBuffer.SetScissor(Rect2DFromClippingBox(useScissorBox, orientation, graphicsViewport));
484 // If there is render callback on the Renderer we need to calculate the scissor box and provide it to the
485 // callback so it may be clipped
486 if(item.mRenderer->GetRenderCallback())
488 // store clipping box inside the render callback input structure
489 auto& input = item.mRenderer->GetRenderCallbackInput();
490 input.clippingBox = ClippingBox(RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3::ZERO, item.mSize, mViewportRectangle.width, mViewportRectangle.height));
495 inline void RenderAlgorithms::SetupClipping(const RenderItem& item,
496 Graphics::CommandBuffer& commandBuffer,
497 bool& usedStencilBuffer,
498 uint32_t& lastClippingDepth,
499 uint32_t& lastClippingId,
500 Integration::StencilBufferAvailable stencilBufferAvailable,
501 const RenderInstruction& instruction,
504 RenderMode::Type renderMode = RenderMode::AUTO;
505 RendererKey renderer = item.mRenderer;
508 renderMode = renderer->GetRenderMode();
511 // Setup the stencil using either the automatic clipping feature, or, the manual per-renderer stencil API.
512 // Note: This switch is in order of most likely value first.
515 case RenderMode::AUTO:
517 // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
518 commandBuffer.SetColorMask(true);
520 // The automatic clipping feature will manage the scissor and stencil functions, only if stencil buffer is available for the latter.
521 // 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.
522 // We process both based on our current and old clipping depths for each mode.
523 // Both methods with return rapidly if there is nothing to be done for that type of clipping.
524 SetupScissorClipping(item, commandBuffer, instruction, orientation);
526 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
528 SetupStencilClipping(item, commandBuffer, lastClippingDepth, lastClippingId);
533 case RenderMode::NONE:
534 case RenderMode::COLOR:
536 // No clipping is performed for these modes.
537 // Note: We do not turn off scissor clipping as it may be used for the whole layer.
538 // The stencil buffer will not be used at all, but we only need to disable it if it's available.
539 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
541 commandBuffer.SetStencilTestEnable(false);
544 // Setup the color buffer based on the RenderMode.
545 commandBuffer.SetColorMask(renderMode == RenderMode::COLOR);
549 case RenderMode::STENCIL:
550 case RenderMode::COLOR_STENCIL:
552 if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
554 // We are using the low-level Renderer Stencil API.
555 // The stencil buffer must be enabled for every renderer with stencil mode on, as renderers in between can disable it.
556 // Note: As the command state is cached, it is only sent when needed.
557 commandBuffer.SetStencilTestEnable(true);
559 // Setup the color buffer based on the RenderMode.
560 commandBuffer.SetColorMask(renderMode == RenderMode::COLOR_STENCIL);
562 // If this is the first use of the stencil buffer within this RenderList, clear it (this avoids unnecessary clears).
563 if(!usedStencilBuffer)
565 commandBuffer.ClearStencilBuffer();
566 usedStencilBuffer = true;
569 // Setup the stencil buffer based on the renderer's properties.
570 commandBuffer.SetStencilOp(GraphicsStencilOp(renderer->GetStencilOperationOnFail()).op,
571 GraphicsStencilOp(renderer->GetStencilOperationOnZPass()).op,
572 GraphicsStencilOp(renderer->GetStencilOperationOnZFail()).op);
573 commandBuffer.SetStencilFunc(GraphicsStencilCompareOp(renderer->GetStencilFunction()).op,
574 renderer->GetStencilFunctionReference(),
575 renderer->GetStencilFunctionMask());
576 commandBuffer.SetStencilWriteMask(renderer->GetStencilMask());
583 inline void RenderAlgorithms::ProcessRenderList(const RenderList& renderList,
584 BufferIndex bufferIndex,
585 const Matrix& viewMatrix,
586 const Matrix& projectionMatrix,
587 Integration::DepthBufferAvailable depthBufferAvailable,
588 Integration::StencilBufferAvailable stencilBufferAvailable,
589 Vector<Graphics::Texture*>& boundTextures,
590 const RenderInstruction& instruction,
591 const Rect<int32_t>& viewport,
592 const Rect<int>& rootClippingRect,
594 const Uint16Pair& sceneSize)
596 DALI_PRINT_RENDER_LIST(renderList);
598 // Note: The depth buffer is enabled or disabled on a per-renderer basis.
599 // Here we pre-calculate the value to use if these modes are set to AUTO.
600 const bool autoDepthTestMode((depthBufferAvailable == Integration::DepthBufferAvailable::TRUE) &&
601 !(renderList.GetSourceLayer()->IsDepthTestDisabled()) &&
602 renderList.HasColorRenderItems());
603 const std::size_t count = renderList.Count();
604 uint32_t lastClippingDepth(0u);
605 uint32_t lastClippingId(0u);
606 bool usedStencilBuffer(false);
607 bool firstDepthBufferUse(true);
609 mViewportRectangle = viewport;
611 auto* mutableRenderList = const_cast<RenderList*>(&renderList);
612 auto& secondaryCommandBuffer = mutableRenderList->GetCommandBuffer(mGraphicsController);
613 secondaryCommandBuffer.Reset();
614 secondaryCommandBuffer.SetViewport(ViewportFromClippingBox(sceneSize, mViewportRectangle, orientation));
615 mHasLayerScissor = false;
617 // Setup Scissor testing (for both viewport and per-node scissor)
618 mScissorStack.clear();
620 // Add root clipping rect (set manually for Render function by partial update for example)
621 // on the bottom of the stack
622 if(!rootClippingRect.IsEmpty())
624 Graphics::Viewport graphicsViewport = ViewportFromClippingBox(sceneSize, mViewportRectangle, 0);
625 secondaryCommandBuffer.SetScissorTestEnable(true);
626 secondaryCommandBuffer.SetScissor(Rect2DFromRect(rootClippingRect, orientation, graphicsViewport));
627 mScissorStack.push_back(rootClippingRect);
629 // We are not performing a layer clip and no clipping rect set. Add the viewport as the root scissor rectangle.
630 else if(!renderList.IsClipping())
632 secondaryCommandBuffer.SetScissorTestEnable(false);
633 mScissorStack.push_back(mViewportRectangle);
636 if(renderList.IsClipping())
638 Graphics::Viewport graphicsViewport = ViewportFromClippingBox(sceneSize, mViewportRectangle, 0);
639 secondaryCommandBuffer.SetScissorTestEnable(true);
640 const ClippingBox& layerScissorBox = renderList.GetClippingBox();
641 secondaryCommandBuffer.SetScissor(Rect2DFromClippingBox(layerScissorBox, orientation, graphicsViewport));
642 mScissorStack.push_back(layerScissorBox);
643 mHasLayerScissor = true;
646 // Loop through all RenderItems in the RenderList, set up any prerequisites to render them, then perform the render.
647 for(uint32_t index = 0u; index < count; ++index)
649 const RenderItem& item = renderList.GetItem(index);
651 // Discard renderers outside the root clipping rect
653 if(!rootClippingRect.IsEmpty())
655 Vector4 updateArea = item.mRenderer ? item.mRenderer->GetVisualTransformedUpdateArea(bufferIndex, item.mUpdateArea) : item.mUpdateArea;
656 auto rect = RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3(updateArea.x, updateArea.y, 0.0f), Vector3(updateArea.z, updateArea.w, 0.0f), mViewportRectangle.width, mViewportRectangle.height);
658 if(rect.Intersect(rootClippingRect))
668 DALI_PRINT_RENDER_ITEM(item);
670 // Set up clipping based on both the Renderer and Actor APIs.
671 // The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
672 SetupClipping(item, secondaryCommandBuffer, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction, orientation);
674 if(DALI_LIKELY(item.mRenderer))
676 // Set up the depth buffer based on per-renderer flags if depth buffer is available
677 // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
678 // draw-mode state, such as Overlays.
679 // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will
680 // always disable depth testing and writing. Color Renderers will enable them if the Layer does.
681 if(depthBufferAvailable == Integration::DepthBufferAvailable::TRUE)
683 SetupDepthBuffer(item, secondaryCommandBuffer, autoDepthTestMode, firstDepthBufferUse);
686 // Depending on whether the renderer has draw commands attached or not the rendering process will
687 // iterate through all the render queues. If there are no draw commands attached, only one
688 // iteration must be done and the default behaviour of the renderer will be executed.
689 // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
690 // It is similar to the multi-pass rendering.
693 auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
694 for(auto queue = 0u; queue < MAX_QUEUE; ++queue)
696 // Render the item. It will write into the command buffer everything it has to render
697 item.mRenderer->Render(secondaryCommandBuffer, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
704 RenderAlgorithms::RenderAlgorithms(Graphics::Controller& graphicsController)
705 : mGraphicsController(graphicsController),
706 mViewportRectangle(),
707 mHasLayerScissor(false)
711 void RenderAlgorithms::ResetCommandBuffer()
713 // Reset main command buffer
714 if(!mGraphicsCommandBuffer)
716 mGraphicsCommandBuffer = mGraphicsController.CreateCommandBuffer(
717 Graphics::CommandBufferCreateInfo()
718 .SetLevel(Graphics::CommandBufferLevel::PRIMARY),
723 mGraphicsCommandBuffer->Reset();
727 void RenderAlgorithms::SubmitCommandBuffer()
729 // Submit main command buffer
730 Graphics::SubmitInfo submitInfo;
731 submitInfo.cmdBuffer.push_back(mGraphicsCommandBuffer.get());
732 submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
733 mGraphicsController.SubmitCommandBuffers(submitInfo);
736 void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction& instruction,
737 BufferIndex bufferIndex,
738 Integration::DepthBufferAvailable depthBufferAvailable,
739 Integration::StencilBufferAvailable stencilBufferAvailable,
740 Vector<Graphics::Texture*>& boundTextures,
741 const Rect<int32_t>& viewport,
742 const Rect<int>& rootClippingRect,
744 const Uint16Pair& sceneSize)
746 DALI_PRINT_RENDER_INSTRUCTION(instruction, bufferIndex);
748 const Matrix* viewMatrix = instruction.GetViewMatrix(bufferIndex);
749 const Matrix* projectionMatrix = instruction.GetProjectionMatrix(bufferIndex);
751 DALI_ASSERT_DEBUG(viewMatrix);
752 DALI_ASSERT_DEBUG(projectionMatrix);
754 if(viewMatrix && projectionMatrix)
756 std::vector<const Graphics::CommandBuffer*> buffers;
757 const RenderListContainer::SizeType count = instruction.RenderListCount();
759 // Iterate through each render list in order. If a pair of render lists
760 // are marked as interleaved, then process them together.
761 for(RenderListContainer::SizeType index = 0; index < count; ++index)
763 const RenderList* renderList = instruction.GetRenderList(index);
765 if(renderList && !renderList->IsEmpty())
767 ProcessRenderList(*renderList,
771 depthBufferAvailable,
772 stencilBufferAvailable,
774 instruction, //added for reflection effect
780 // Execute command buffer
781 auto* commandBuffer = renderList->GetCommandBuffer();
784 buffers.push_back(commandBuffer);
790 mGraphicsCommandBuffer->ExecuteCommandBuffers(std::move(buffers));
795 } // namespace Render
797 } // namespace Internal