Merge branch 'devel/master' into devel/graphics
[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/gl-resources/context.h>
26 #include <dali/internal/render/renderers/render-renderer.h>
27 #include <dali/internal/update/nodes/scene-graph-layer.h>
28
29 using Dali::Internal::SceneGraph::RenderInstruction;
30 using Dali::Internal::SceneGraph::RenderItem;
31 using Dali::Internal::SceneGraph::RenderList;
32 using Dali::Internal::SceneGraph::RenderListContainer;
33
34 namespace Dali
35 {
36 namespace Internal
37 {
38 namespace Render
39 {
40 namespace
41 {
42 // Table for fast look-up of Dali::DepthFunction enum to a GL depth function.
43 // Note: These MUST be in the same order as Dali::DepthFunction enum.
44 const int DaliDepthToGLDepthTable[] = {GL_NEVER, GL_ALWAYS, GL_LESS, GL_GREATER, GL_EQUAL, GL_NOTEQUAL, GL_LEQUAL, GL_GEQUAL};
45
46 // Table for fast look-up of Dali::StencilFunction enum to a GL stencil function.
47 // Note: These MUST be in the same order as Dali::StencilFunction enum.
48 const int DaliStencilFunctionToGL[] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS};
49
50 // Table for fast look-up of Dali::StencilOperation enum to a GL stencil operation.
51 // Note: These MUST be in the same order as Dali::StencilOperation enum.
52 const int DaliStencilOperationToGL[] = {GL_ZERO, GL_KEEP, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP};
53
54 /**
55  * @brief Find the intersection of two AABB rectangles.
56  * This is a logical AND operation. IE. The intersection is the area overlapped by both rectangles.
57  * @param[in]     aabbA                  Rectangle A
58  * @param[in]     aabbB                  Rectangle B
59  * @return                               The intersection of rectangle A & B (result is a rectangle)
60  */
61 inline ClippingBox IntersectAABB(const ClippingBox& aabbA, const ClippingBox& aabbB)
62 {
63   ClippingBox intersectionBox;
64
65   // First calculate the largest starting positions in X and Y.
66   intersectionBox.x = std::max(aabbA.x, aabbB.x);
67   intersectionBox.y = std::max(aabbA.y, aabbB.y);
68
69   // Now calculate the smallest ending positions, and take the largest starting
70   // positions from the result, to get the width and height respectively.
71   // If the two boxes do not intersect at all, then we need a 0 width and height clipping area.
72   // We use max here to clamp both width and height to >= 0 for this use-case.
73   intersectionBox.width  = std::max(std::min(aabbA.x + aabbA.width, aabbB.x + aabbB.width) - intersectionBox.x, 0);
74   intersectionBox.height = std::max(std::min(aabbA.y + aabbA.height, aabbB.y + aabbB.height) - intersectionBox.y, 0);
75
76   return intersectionBox;
77 }
78
79 /**
80  * @brief Set up the stencil and color buffer for automatic clipping (StencilMode::AUTO).
81  * @param[in]     item                     The current RenderItem about to be rendered
82  * @param[in]     context                  The context
83  * @param[in/out] lastClippingDepth        The stencil depth of the last renderer drawn.
84  * @param[in/out] lastClippingId           The clipping ID of the last renderer drawn.
85  */
86 inline void SetupStencilClipping(const RenderItem& item, Context& context, uint32_t& lastClippingDepth, uint32_t& lastClippingId)
87 {
88   const Dali::Internal::SceneGraph::Node* node       = item.mNode;
89   const uint32_t                          clippingId = node->GetClippingId();
90   // If there is no clipping Id, then either we haven't reached a clipping Node yet, or there aren't any.
91   // Either way we can skip clipping setup for this renderer.
92   if(clippingId == 0u)
93   {
94     // Exit immediately if there are no clipping actions to perform (EG. we have not yet hit a clipping node).
95     context.EnableStencilBuffer(false);
96     return;
97   }
98
99   context.EnableStencilBuffer(true);
100
101   const uint32_t clippingDepth = node->GetClippingDepth();
102
103   // Pre-calculate a mask which has all bits set up to and including the current clipping depth.
104   // EG. If depth is 3, the mask would be "111" in binary.
105   const uint32_t currentDepthMask = (1u << clippingDepth) - 1u;
106
107   // Are we are writing to the stencil buffer?
108   if(item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_CHILDREN)
109   {
110     // We are writing to the stencil buffer.
111     // If clipping Id is 1, this is the first clipping renderer within this render-list.
112     if(clippingId == 1u)
113     {
114       // We are enabling the stencil-buffer for the first time within this render list.
115       // Clear the buffer at this point.
116       context.StencilMask(0xff);
117       context.Clear(GL_STENCIL_BUFFER_BIT, Context::CHECK_CACHED_VALUES);
118     }
119     else if((clippingDepth < lastClippingDepth) ||
120             ((clippingDepth == lastClippingDepth) && (clippingId > lastClippingId)))
121     {
122       // The above if() statement tests if we need to clear some (not all) stencil bit-planes.
123       // We need to do this if either of the following are true:
124       //   1) We traverse up the scene-graph to a previous stencil depth
125       //   2) We are at the same stencil depth but the clipping Id has increased.
126       //
127       // This calculation takes the new depth to move to, and creates an inverse-mask of that number of consecutive bits.
128       // This has the effect of clearing everything except the bit-planes up to (and including) our current depth.
129       const uint32_t stencilClearMask = (currentDepthMask >> 1u) ^ 0xff;
130
131       context.StencilMask(stencilClearMask);
132       context.Clear(GL_STENCIL_BUFFER_BIT, Context::CHECK_CACHED_VALUES);
133     }
134
135     // We keep track of the last clipping Id and depth so we can determine when we are
136     // moving back up the scene graph and require some of the stencil bit-planes to be deleted.
137     lastClippingDepth = clippingDepth;
138     lastClippingId    = clippingId;
139
140     // We only ever write to bit-planes up to the current depth as we may need
141     // to erase individual bit-planes and revert to a previous clipping area.
142     // Our reference value for testing (in StencilFunc) is written to to the buffer, but we actually
143     // want to test a different value. IE. All the bit-planes up to but not including the current depth.
144     // So we use the Mask parameter of StencilFunc to mask off the top bit-plane when testing.
145     // Here we create our test mask to innore the top bit of the reference test value.
146     // As the mask is made up of contiguous "1" values, we can do this quickly with a bit-shift.
147     const uint32_t testMask = currentDepthMask >> 1u;
148
149     context.StencilFunc(GL_EQUAL, currentDepthMask, testMask); // Test against existing stencil bit-planes. All must match up to (but not including) this depth.
150     context.StencilMask(currentDepthMask);                     // Write to the new stencil bit-plane (the other previous bit-planes are also written to).
151     context.StencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
152   }
153   else
154   {
155     // We are reading from the stencil buffer. Set up the stencil accordingly
156     // This calculation sets all the bits up to the current depth bit.
157     // This has the effect of testing that the pixel being written to exists in every bit-plane up to the current depth.
158     context.StencilFunc(GL_EQUAL, currentDepthMask, currentDepthMask);
159     context.StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
160   }
161 }
162
163 /**
164  * @brief Sets up the depth buffer for reading and writing based on the current render item.
165  * The items read and write mode are used if specified.
166  *  - If AUTO is selected for reading, the decision will be based on the Layer Behavior.
167  *  - If AUTO is selected for writing, the decision will be based on the items opacity.
168  * @param[in]     item                The RenderItem to set up the depth buffer for.
169  * @param[in]     context             The context used to execute GL commands.
170  * @param[in]     depthTestEnabled    True if depth testing has been enabled.
171  * @param[in/out] firstDepthBufferUse Initialize to true on the first call, this method will set it to false afterwards.
172  */
173 inline void SetupDepthBuffer(const RenderItem& item, Context& context, bool depthTestEnabled, bool& firstDepthBufferUse)
174 {
175   // Set up whether or not to write to the depth buffer.
176   const DepthWriteMode::Type depthWriteMode = item.mRenderer->GetDepthWriteMode();
177   // Most common mode (AUTO) is tested first.
178   const bool enableDepthWrite = ((depthWriteMode == DepthWriteMode::AUTO) && depthTestEnabled && item.mIsOpaque) ||
179                                 (depthWriteMode == DepthWriteMode::ON);
180
181   // Set up whether or not to read from (test) the depth buffer.
182   const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
183   // Most common mode (AUTO) is tested first.
184   const bool enableDepthTest = ((depthTestMode == DepthTestMode::AUTO) && depthTestEnabled) ||
185                                (depthTestMode == DepthTestMode::ON);
186
187   // Is the depth buffer in use?
188   if(enableDepthWrite || enableDepthTest)
189   {
190     // The depth buffer must be enabled if either reading or writing.
191     context.EnableDepthBuffer(true);
192
193     // Look-up the GL depth function from the Dali::DepthFunction enum, and set it.
194     context.DepthFunc(DaliDepthToGLDepthTable[item.mRenderer->GetDepthFunction()]);
195
196     // If this is the first use of the depth buffer this RenderTask, perform a clear.
197     // Note: We could do this at the beginning of the RenderTask and rely on the
198     // context cache to ignore the clear if not required, but, we would have to enable
199     // the depth buffer to do so, which could be a redundant enable.
200     if(DALI_UNLIKELY(firstDepthBufferUse))
201     {
202       // This is the first time the depth buffer is being written to or read.
203       firstDepthBufferUse = false;
204
205       // Note: The buffer will only be cleared if written to since a previous clear.
206       context.DepthMask(true);
207       context.Clear(GL_DEPTH_BUFFER_BIT, Context::CHECK_CACHED_VALUES);
208     }
209
210     // Set up the depth mask based on our depth write setting.
211     context.DepthMask(enableDepthWrite);
212   }
213   else
214   {
215     // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
216     context.EnableDepthBuffer(false);
217   }
218 }
219
220 } // Unnamed namespace
221
222 /**
223  * @brief This method is responsible for making decisions on when to apply and unapply scissor clipping, and what rectangular dimensions should be used.
224  * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied.
225  * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation.
226  * @param[in]     item                     The current RenderItem about to be rendered
227  * @param[in]     context                  The context
228  * @param[in]     instruction              The render-instruction to process.
229  */
230 inline void RenderAlgorithms::SetupScissorClipping(const RenderItem& item, Context& context, const RenderInstruction& instruction)
231 {
232   // Get the number of child scissors in the stack (do not include layer or root box).
233   size_t         childStackDepth = mScissorStack.size() - 1u;
234   const uint32_t scissorDepth    = item.mNode->GetScissorDepth();
235   const bool     clippingNode    = item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_TO_BOUNDING_BOX;
236   bool           traversedUpTree = false;
237
238   // If we are using scissor clipping and we are at the same depth (or less), we need to undo previous clips.
239   // We do this by traversing up the scissor clip stack and then apply the appropriate clip for the current render item.
240   // To know this, we use clippingDepth. This value is set on *every* node, but only increased as clipping nodes are hit depth-wise.
241   // So we know if we are at depth 4 and the stackDepth is 5, that we have gone up.
242   // 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.
243   // Note: Stack depth must always be at least 1, as we will have the layer or stage size as the root value.
244   if((childStackDepth > 0u) && (scissorDepth < childStackDepth))
245   {
246     while(scissorDepth < childStackDepth)
247     {
248       mScissorStack.pop_back();
249       --childStackDepth;
250     }
251
252     // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
253     traversedUpTree = true;
254   }
255   if(clippingNode && childStackDepth > 0u && childStackDepth == scissorDepth) // case of sibling clip area
256   {
257     mScissorStack.pop_back();
258     --childStackDepth;
259   }
260
261   // 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.
262   if(clippingNode || traversedUpTree)
263   {
264     // First, check if we are a clipping node.
265     if(clippingNode)
266     {
267       // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
268
269       // Get the AABB bounding box for the current render item.
270       const ClippingBox scissorBox(item.CalculateViewportSpaceAABB(item.mSize, mViewportRectangle.width, mViewportRectangle.height));
271
272       // Get the AABB for the parent item that we must intersect with.
273       const ClippingBox& parentBox(mScissorStack.back());
274
275       // We must reduce the clipping area based on the parents area to allow nested clips. This is a set intersection function.
276       // We add the new scissor box to the stack so we can return to it if needed.
277       mScissorStack.emplace_back(IntersectAABB(parentBox, scissorBox));
278     }
279
280     // 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.
281     // IE. It is not enabled if we are at the top of the stack and the layer does not have a specified clipping box.
282     const bool scissorEnabled = (mScissorStack.size() > 0u) || mHasLayerScissor;
283
284     // Enable the scissor test based on the above calculation
285     context.SetScissorTest(scissorEnabled);
286
287     // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
288     if(scissorEnabled)
289     {
290       ClippingBox useScissorBox(mScissorStack.back());
291
292       if(instruction.mFrameBuffer && instruction.GetCamera()->IsYAxisInverted())
293       {
294         useScissorBox.y = (instruction.mFrameBuffer->GetHeight() - useScissorBox.height) - useScissorBox.y;
295       }
296       context.Scissor(useScissorBox.x, useScissorBox.y, useScissorBox.width, useScissorBox.height);
297     }
298   }
299 }
300
301 inline void RenderAlgorithms::SetupClipping(const RenderItem&                   item,
302                                             Context&                            context,
303                                             bool&                               usedStencilBuffer,
304                                             uint32_t&                           lastClippingDepth,
305                                             uint32_t&                           lastClippingId,
306                                             Integration::StencilBufferAvailable stencilBufferAvailable,
307                                             const RenderInstruction&            instruction)
308 {
309   RenderMode::Type renderMode = RenderMode::AUTO;
310   const Renderer*  renderer   = item.mRenderer;
311   if(renderer)
312   {
313     renderMode = renderer->GetRenderMode();
314   }
315
316   // Setup the stencil using either the automatic clipping feature, or, the manual per-renderer stencil API.
317   // Note: This switch is in order of most likely value first.
318   switch(renderMode)
319   {
320     case RenderMode::AUTO:
321     {
322       // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
323       context.ColorMask(true);
324
325       // The automatic clipping feature will manage the scissor and stencil functions, only if stencil buffer is available for the latter.
326       // 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.
327       // We process both based on our current and old clipping depths for each mode.
328       // Both methods with return rapidly if there is nothing to be done for that type of clipping.
329       SetupScissorClipping(item, context, instruction);
330
331       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
332       {
333         SetupStencilClipping(item, context, lastClippingDepth, lastClippingId);
334       }
335       break;
336     }
337
338     case RenderMode::NONE:
339     case RenderMode::COLOR:
340     {
341       // No clipping is performed for these modes.
342       // Note: We do not turn off scissor clipping as it may be used for the whole layer.
343       // The stencil buffer will not be used at all, but we only need to disable it if it's available.
344       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
345       {
346         context.EnableStencilBuffer(false);
347       }
348
349       // Setup the color buffer based on the RenderMode.
350       context.ColorMask(renderMode == RenderMode::COLOR);
351       break;
352     }
353
354     case RenderMode::STENCIL:
355     case RenderMode::COLOR_STENCIL:
356     {
357       if(stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE)
358       {
359         // We are using the low-level Renderer Stencil API.
360         // The stencil buffer must be enabled for every renderer with stencil mode on, as renderers in between can disable it.
361         // Note: As the command state is cached, it is only sent when needed.
362         context.EnableStencilBuffer(true);
363
364         // Setup the color buffer based on the RenderMode.
365         context.ColorMask(renderMode == RenderMode::COLOR_STENCIL);
366
367         // If this is the first use of the stencil buffer within this RenderList, clear it (this avoids unnecessary clears).
368         if(!usedStencilBuffer)
369         {
370           context.Clear(GL_STENCIL_BUFFER_BIT, Context::CHECK_CACHED_VALUES);
371           usedStencilBuffer = true;
372         }
373
374         // Setup the stencil buffer based on the renderers properties.
375         context.StencilFunc(DaliStencilFunctionToGL[renderer->GetStencilFunction()],
376                             renderer->GetStencilFunctionReference(),
377                             renderer->GetStencilFunctionMask());
378         context.StencilOp(DaliStencilOperationToGL[renderer->GetStencilOperationOnFail()],
379                           DaliStencilOperationToGL[renderer->GetStencilOperationOnZFail()],
380                           DaliStencilOperationToGL[renderer->GetStencilOperationOnZPass()]);
381         context.StencilMask(renderer->GetStencilMask());
382       }
383       break;
384     }
385   }
386 }
387
388 inline void RenderAlgorithms::ProcessRenderList(const RenderList&                   renderList,
389                                                 Context&                            context,
390                                                 BufferIndex                         bufferIndex,
391                                                 const Matrix&                       viewMatrix,
392                                                 const Matrix&                       projectionMatrix,
393                                                 Integration::DepthBufferAvailable   depthBufferAvailable,
394                                                 Integration::StencilBufferAvailable stencilBufferAvailable,
395                                                 Vector<Graphics::Texture*>&         boundTextures,
396                                                 const RenderInstruction&            instruction,
397                                                 const Rect<int>&                    rootClippingRect)
398 {
399   DALI_PRINT_RENDER_LIST(renderList);
400
401   // Note: The depth buffer is enabled or disabled on a per-renderer basis.
402   // Here we pre-calculate the value to use if these modes are set to AUTO.
403   const bool        autoDepthTestMode((depthBufferAvailable == Integration::DepthBufferAvailable::TRUE) &&
404                                !(renderList.GetSourceLayer()->IsDepthTestDisabled()) &&
405                                renderList.HasColorRenderItems());
406   const std::size_t count = renderList.Count();
407   uint32_t          lastClippingDepth(0u);
408   uint32_t          lastClippingId(0u);
409   bool              usedStencilBuffer(false);
410   bool              firstDepthBufferUse(true);
411   mViewportRectangle = context.GetViewport();
412   mHasLayerScissor   = false;
413
414   // Setup Scissor testing (for both viewport and per-node scissor)
415   mScissorStack.clear();
416
417   // Add root clipping rect (set manually for Render function ny partial update for example)
418   // on the bottom of the stack
419   if(!rootClippingRect.IsEmpty())
420   {
421     context.SetScissorTest(true);
422     context.Scissor(rootClippingRect.x, rootClippingRect.y, rootClippingRect.width, rootClippingRect.height);
423     mScissorStack.push_back(rootClippingRect);
424   }
425   // We are not performing a layer clip and no clipping rect set. Add the viewport as the root scissor rectangle.
426   else if(!renderList.IsClipping())
427   {
428     context.SetScissorTest(false);
429     mScissorStack.push_back(mViewportRectangle);
430   }
431
432   if(renderList.IsClipping())
433   {
434     context.SetScissorTest(true);
435     const ClippingBox& layerScissorBox = renderList.GetClippingBox();
436     context.Scissor(layerScissorBox.x, layerScissorBox.y, layerScissorBox.width, layerScissorBox.height);
437     mScissorStack.push_back(layerScissorBox);
438     mHasLayerScissor = true;
439   }
440
441   // Loop through all RenderList in the RenderList, set up any prerequisites to render them, then perform the render.
442   for(uint32_t index = 0u; index < count; ++index)
443   {
444     const RenderItem& item = renderList.GetItem(index);
445
446     // Discard renderers outside the root clipping rect
447     bool skip = true;
448     if(!rootClippingRect.IsEmpty())
449     {
450       auto rect = item.CalculateViewportSpaceAABB(item.mUpdateSize, mViewportRectangle.width, mViewportRectangle.height);
451
452       if(rect.Intersect(rootClippingRect))
453       {
454         skip = false;
455       }
456     }
457     else
458     {
459       skip = false;
460     }
461
462     DALI_PRINT_RENDER_ITEM(item);
463
464     // Set up clipping based on both the Renderer and Actor APIs.
465     // The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
466     SetupClipping(item, context, usedStencilBuffer, lastClippingDepth, lastClippingId, stencilBufferAvailable, instruction);
467
468     if(DALI_LIKELY(item.mRenderer))
469     {
470       // Set up the depth buffer based on per-renderer flags if depth buffer is available
471       // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
472       // draw-mode state, such as Overlays.
473       // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will always
474       // disable depth testing and writing. Color Renderers will enable them if the Layer does.
475       if(depthBufferAvailable == Integration::DepthBufferAvailable::TRUE)
476       {
477         SetupDepthBuffer(item, context, autoDepthTestMode, firstDepthBufferUse);
478       }
479
480       // Depending on whether the renderer has draw commands attached or not the rendering process will
481       // iterate through all the render queues. If there are no draw commands attached, only one
482       // iteration must be done and the default behaviour of the renderer will be executed.
483       // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
484       // It is similar to the multi-pass rendering.
485       if(!skip)
486       {
487         auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
488         for(auto queue = 0u; queue < MAX_QUEUE; ++queue)
489         {
490           // Render the item.
491           item.mRenderer->Render(context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix, viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
492         }
493       }
494     }
495   }
496 }
497
498 RenderAlgorithms::RenderAlgorithms()
499 : mViewportRectangle(),
500   mHasLayerScissor(false)
501 {
502 }
503
504 void RenderAlgorithms::ProcessRenderInstruction(const RenderInstruction&            instruction,
505                                                 Context&                            context,
506                                                 BufferIndex                         bufferIndex,
507                                                 Integration::DepthBufferAvailable   depthBufferAvailable,
508                                                 Integration::StencilBufferAvailable stencilBufferAvailable,
509                                                 Vector<Graphics::Texture*>&         boundTextures,
510                                                 const Rect<int>&                    rootClippingRect)
511 {
512   DALI_PRINT_RENDER_INSTRUCTION(instruction, bufferIndex);
513
514   const Matrix* viewMatrix       = instruction.GetViewMatrix(bufferIndex);
515   const Matrix* projectionMatrix = instruction.GetProjectionMatrix(bufferIndex);
516
517   DALI_ASSERT_DEBUG(viewMatrix);
518   DALI_ASSERT_DEBUG(projectionMatrix);
519
520   if(viewMatrix && projectionMatrix)
521   {
522     const RenderListContainer::SizeType count = instruction.RenderListCount();
523
524     // Iterate through each render list in order. If a pair of render lists
525     // are marked as interleaved, then process them together.
526     for(RenderListContainer::SizeType index = 0; index < count; ++index)
527     {
528       const RenderList* renderList = instruction.GetRenderList(index);
529
530       if(renderList && !renderList->IsEmpty())
531       {
532         ProcessRenderList(*renderList,
533                           context,
534                           bufferIndex,
535                           *viewMatrix,
536                           *projectionMatrix,
537                           depthBufferAvailable,
538                           stencilBufferAvailable,
539                           boundTextures,
540                           instruction, //added for reflection effect
541                           rootClippingRect);
542       }
543     }
544   }
545 }
546
547 } // namespace Render
548
549 } // namespace Internal
550
551 } // namespace Dali