mEnableDisableTrace.Reset();
mShaderTrace.Reset();
mStencilFunctionTrace.Reset();
+ mScissorTrace.Reset();
mTextureTrace.Reset();
mTexParamaterTrace.Reset();
mDrawTrace.Reset();
mScissorParams.y = y;
mScissorParams.width = width;
mScissorParams.height = height;
+
+ std::stringstream out;
+ out << x << ", " << y << ", " << width << ", " << height;
+ TraceCallStack::NamedParams namedParams;
+ namedParams["x"] = ToString( x );
+ namedParams["y"] = ToString( y );
+ namedParams["width"] = ToString( width );
+ namedParams["height"] = ToString( height );
+ mScissorTrace.PushCall( "Scissor", out.str(), namedParams );
}
inline void ShaderBinary(GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length)
inline void ResetStencilFunctionCallStack() { mStencilFunctionTrace.Reset(); }
inline TraceCallStack& GetStencilFunctionTrace() { return mStencilFunctionTrace; }
+ //Methods for Scissor verification
+ inline void EnableScissorCallTrace(bool enable) { mScissorTrace.Enable(enable); }
+ inline void ResetScissorCallStack() { mScissorTrace.Reset(); }
+ inline TraceCallStack& GetScissorTrace() { return mScissorTrace; }
+
//Methods for Uniform function verification
inline void EnableSetUniformCallTrace(bool enable) { mSetUniformTrace.Enable(enable); }
inline void ResetSetUniformCallStack() { mSetUniformTrace.Reset(); }
TraceCallStack mDrawTrace;
TraceCallStack mDepthFunctionTrace;
TraceCallStack mStencilFunctionTrace;
+ TraceCallStack mScissorTrace;
TraceCallStack mSetUniformTrace;
// Shaders & Uniforms
int UtcDaliActorPropertyClippingP(void)
{
// This test checks the clippingMode property.
- tet_infoline( "Testing Actor::Property::CLIPPING_MODE P" );
+ tet_infoline( "Testing Actor::Property::ClippingMode: P" );
TestApplication application;
Actor actor = Actor::New();
DALI_TEST_EQUALS<int>( value, ClippingMode::DISABLED, TEST_LOCATION );
}
- // Check setting the property.
+ // Check setting the property to the stencil mode.
actor.SetProperty( Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_CHILDREN );
// Check the new value was set.
DALI_TEST_EQUALS<int>( value, ClippingMode::CLIP_CHILDREN, TEST_LOCATION );
}
+ // Check setting the property to the scissor mode.
+ actor.SetProperty( Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX );
+
+ // Check the new value was set.
+ getValue = actor.GetProperty( Actor::Property::CLIPPING_MODE );
+ getValueResult = getValue.Get( value );
+ DALI_TEST_CHECK( getValueResult );
+
+ if( getValueResult )
+ {
+ DALI_TEST_EQUALS<int>( value, ClippingMode::CLIP_TO_BOUNDING_BOX, TEST_LOCATION );
+ }
END_TEST;
}
int UtcDaliActorPropertyClippingN(void)
{
// Negative test case for Clipping.
- tet_infoline( "Testing Actor::Property::CLIPPING_MODE N" );
+ tet_infoline( "Testing Actor::Property::ClippingMode: N" );
TestApplication application;
Actor actor = Actor::New();
int UtcDaliActorPropertyClippingActor(void)
{
// This test checks that an actor is correctly setup for clipping.
- tet_infoline( "Testing Actor::Property::CLIPPING_MODE actor" );
+ tet_infoline( "Testing Actor::Property::ClippingMode: CLIP_CHILDREN actor" );
TestApplication application;
TestGlAbstraction& glAbstraction = application.GetGlAbstraction();
int UtcDaliActorPropertyClippingActorEnableThenDisable(void)
{
// This test checks that an actor is correctly setup for clipping and then correctly setup when clipping is disabled
- tet_infoline( "Testing Actor::Property::CLIPPING_MODE actor enable and then disable" );
+ tet_infoline( "Testing Actor::Property::ClippingMode: CLIP_CHILDREN actor enable and then disable" );
TestApplication application;
TestGlAbstraction& glAbstraction = application.GetGlAbstraction();
END_TEST;
}
-
int UtcDaliActorPropertyClippingNestedChildren(void)
{
// This test checks that a hierarchy of actors are clipped correctly by
// writing to and reading from the correct bit-planes of the stencil buffer.
- tet_infoline( "Testing Actor::Property::CLIPPING_MODE nested children" );
+ tet_infoline( "Testing Actor::Property::ClippingMode: CLIP_CHILDREN nested children" );
TestApplication application;
TestGlAbstraction& glAbstraction = application.GetGlAbstraction();
TraceCallStack& stencilTrace = glAbstraction.GetStencilFunctionTrace();
int UtcDaliActorPropertyClippingActorDrawOrder(void)
{
// This test checks that a hierarchy of actors are drawn in the correct order when clipping is enabled.
- tet_infoline( "Testing Actor::Property::CLIPPING_MODE draw order" );
+ tet_infoline( "Testing Actor::Property::ClippingMode: CLIP_CHILDREN draw order" );
TestApplication application;
TestGlAbstraction& glAbstraction = application.GetGlAbstraction();
TraceCallStack& enabledDisableTrace = glAbstraction.GetEnableDisableTrace();
END_TEST;
}
+int UtcDaliActorPropertyScissorClippingActor(void)
+{
+ // This test checks that an actor is correctly setup for clipping.
+ tet_infoline( "Testing Actor::Property::ClippingMode: CLIP_TO_BOUNDING_BOX actor" );
+ TestApplication application;
+
+ TestGlAbstraction& glAbstraction = application.GetGlAbstraction();
+ TraceCallStack& scissorTrace = glAbstraction.GetScissorTrace();
+ TraceCallStack& enabledDisableTrace = glAbstraction.GetEnableDisableTrace();
+
+ const Vector2 stageSize( TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT );
+ const Vector2 imageSize( 16.0f, 16.0f );
+
+ // Create a clipping actor.
+ Actor clippingActorA = CreateActorWithContent();
+ // Note: Scissor coords are have flipped Y values compared with DALi's coordinate system.
+ // We choose BOTTOM_LEFT to give us x=0, y=0 starting coordinates for the first test.
+ clippingActorA.SetParentOrigin( ParentOrigin::BOTTOM_LEFT );
+ clippingActorA.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
+ clippingActorA.SetProperty( Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX );
+ Stage::GetCurrent().Add( clippingActorA );
+
+ // Gather the call trace.
+ GenerateTrace( application, enabledDisableTrace, scissorTrace );
+
+ // Check we are writing to the color buffer.
+ CheckColorMask( glAbstraction, true );
+
+ // Check scissor test was enabled.
+ DALI_TEST_CHECK( enabledDisableTrace.FindMethodAndParams( "Enable", "3089" ) ); // 3089 = 0xC11 (GL_SCISSOR_TEST)
+
+ // Check the scissor was set, and the coordinates are correct.
+ std::stringstream compareParametersString;
+ compareParametersString << "0, 0, " << imageSize.x << ", " << imageSize.y;
+ DALI_TEST_CHECK( scissorTrace.FindMethodAndParams( "Scissor", compareParametersString.str() ) ); // Compare with 0, 0, 16, 16
+
+ clippingActorA.SetParentOrigin( ParentOrigin::TOP_RIGHT );
+ clippingActorA.SetAnchorPoint( AnchorPoint::TOP_RIGHT );
+
+ // Gather the call trace.
+ GenerateTrace( application, enabledDisableTrace, scissorTrace );
+
+ // Check the scissor was set, and the coordinates are correct.
+ compareParametersString.str( std::string() );
+ compareParametersString.clear();
+ compareParametersString << ( stageSize.x - imageSize.x ) << ", " << ( stageSize.y - imageSize.y ) << ", " << imageSize.x << ", " << imageSize.y;
+ DALI_TEST_CHECK( scissorTrace.FindMethodAndParams( "Scissor", compareParametersString.str() ) ); // Compare with 464, 784, 16, 16
+
+ END_TEST;
+}
+
+int UtcDaliActorPropertyScissorClippingActorNested(void)
+{
+ // This test checks that an actor is correctly setup for clipping.
+ tet_infoline( "Testing Actor::Property::ClippingMode: CLIP_TO_BOUNDING_BOX actor nested" );
+ TestApplication application;
+
+ TestGlAbstraction& glAbstraction = application.GetGlAbstraction();
+ TraceCallStack& scissorTrace = glAbstraction.GetScissorTrace();
+ TraceCallStack& enabledDisableTrace = glAbstraction.GetEnableDisableTrace();
+
+ const Vector2 stageSize( TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT );
+ const Vector2 imageSize( 16.0f, 16.0f );
+
+ /* Create a nest of 2 scissors to test nesting (intersecting clips).
+
+ A is drawn first - with scissor clipping on
+ B is drawn second - also with scissor clipping on
+ C is the generated clipping region, the intersection ( A ∩ B )
+
+ ┏━━━━━━━┓ ┌───────┐
+ ┃ B ┃ │ B │
+ ┌───╂┄┄┄┐ ┃ ┌┄┄┄╆━━━┓ │
+ │ ┃ ┊ ┃ ━━━━━> ┊ ┃ C ┃ │
+ │ ┗━━━┿━━━┛ ┊ ┗━━━╃───┘
+ │ A │ ┊ A ┊
+ └───────┘ └┄┄┄┄┄┄┄┘
+
+ We then reposition B around each corner of A to test the 4 overlap combinations (thus testing intersecting works correctly).
+ */
+
+ // Create a clipping actor.
+ Actor clippingActorA = CreateActorWithContent();
+ // Note: Scissor coords are have flipped Y values compared with DALi's coordinate system.
+ // We choose BOTTOM_LEFT to give us x=0, y=0 starting coordinates for the first test.
+ clippingActorA.SetParentOrigin( ParentOrigin::CENTER );
+ clippingActorA.SetAnchorPoint( AnchorPoint::CENTER );
+ clippingActorA.SetProperty( Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX );
+ Stage::GetCurrent().Add( clippingActorA );
+
+ // Create a child clipping actor.
+ Actor clippingActorB = CreateActorWithContent();
+ clippingActorB.SetParentOrigin( ParentOrigin::CENTER );
+ clippingActorB.SetAnchorPoint( AnchorPoint::CENTER );
+ clippingActorB.SetProperty( Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX );
+ clippingActorA.Add( clippingActorB );
+
+ // positionModifiers is an array of positions to position B around.
+ // expect is an array of expected scissor clip coordinate results.
+ const Vector2 positionModifiers[4] = { Vector2( 1.0f, 1.0f ), Vector2( -1.0f, 1.0f ), Vector2( -1.0f, -1.0f ), Vector2( 1.0f, -1.0f ) };
+ const Vector4 expect[4] = { Vector4( 240, 392, 8, 8 ), Vector4( 232, 392, 8, 8 ), Vector4( 232, 400, 8, 8 ), Vector4( 240, 400, 8, 8 ) };
+
+ // Loop through each overlap combination.
+ for( unsigned int test = 0u; test < 4u; ++test )
+ {
+ // Position the child clipping actor so it intersects with the 1st clipping actor. This changes each loop.
+ const Vector2 position = ( imageSize / 2.0f ) * positionModifiers[test];
+ clippingActorB.SetPosition( position.x, position.y );
+
+ // Gather the call trace.
+ GenerateTrace( application, enabledDisableTrace, scissorTrace );
+
+ // Check we are writing to the color buffer.
+ CheckColorMask( glAbstraction, true );
+
+ // Check scissor test was enabled.
+ DALI_TEST_CHECK( enabledDisableTrace.FindMethodAndParams( "Enable", "3089" ) ); // 3089 = 0xC11 (GL_SCISSOR_TEST)
+
+ // Check the scissor was set, and the coordinates are correct.
+ const Vector4& expectResults( expect[test] );
+ std::stringstream compareParametersString;
+ compareParametersString << expectResults.x << ", " << expectResults.y << ", " << expectResults.z << ", " << expectResults.w;
+ DALI_TEST_CHECK( scissorTrace.FindMethodAndParams( "Scissor", compareParametersString.str() ) ); // Compare with the expected result
+ }
+
+ END_TEST;
+}
+
int UtcDaliActorPropertyClippingActorWithRendererOverride(void)
{
// This test checks that an actor with clipping will be ignored if overridden by the Renderer properties.
DALI_TEST_CHECK( !stencilTrace.FindMethod( "StencilMask" ) );
DALI_TEST_CHECK( !stencilTrace.FindMethod( "StencilOp" ) );
+ // Check that scissor clipping is overriden by the renderer properties.
+ TraceCallStack& scissorTrace = glAbstraction.GetScissorTrace();
+
+ actorDepth1Clip.SetProperty( Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX );
+
+ // Gather the call trace.
+ GenerateTrace( application, enabledDisableTrace, scissorTrace );
+
+ // Check the stencil buffer was not enabled.
+ DALI_TEST_CHECK( !enabledDisableTrace.FindMethodAndParams( "Enable", "3089" ) ); // 3089 = 0xC11 (GL_SCISSOR_TEST)
+
+ DALI_TEST_CHECK( !scissorTrace.FindMethod( "StencilFunc" ) );
+
END_TEST;
}
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// CLASS HEADER
#include <dali/internal/common/math.h>
-//EXTERNAL INCLUDES
+// EXTERNAL INCLUDES
#include <cmath>
+// INTERNAL INCLUDES
+#include <dali/internal/render/common/performance-monitor.h>
+#include <dali/public-api/common/constants.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/public-api/math/matrix.h>
+
void Dali::Internal::TransformVector3( Vec3 result, const Mat4 m, const Vec3 v )
{
#ifndef __ARM_NEON__
#endif
}
+Dali::Vector2 Dali::Internal::Transform2D( const Dali::Matrix& matrix, const float x, const float y )
+{
+ MATH_INCREASE_BY( PerformanceMonitor::FLOAT_POINT_MULTIPLY, 4 );
+
+ const float* matrixArray( matrix.AsFloat() );
+
+ // The following optimizations are applied:
+ // Matrix[8 -> 11] are optimized out.
+ // Matrix[12 -> 15] are always multiplied by 1.
+ // z & w results (if we were doing a transformation to a Vector4) are unneeded and so not calculated.
+ // As we always multiply by component, we do not store the coordinates in a Vector2 to avoid creation.
+ // Note: For this reason the NEON SIMD version is no faster than the Dali::Matrix '*' Vector4 operator, and therefore not used.
+ return Dali::Vector2( x * matrixArray[0] + y * matrixArray[4] + matrixArray[12], x * matrixArray[1] + y * matrixArray[5] + matrixArray[13] );
+}
+
float Dali::Internal::Length( const Vec3 v )
{
return sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
}
+
-#ifndef __DALI_INTERNAL_MATH_H__
-#define __DALI_INTERNAL_MATH_H__
+#ifndef DALI_INTERNAL_MATH_H
+#define DALI_INTERNAL_MATH_H
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace Dali
{
+class Vector2;
+class Matrix;
+
namespace Internal
{
typedef float Mat4[16];
/**
- * @brief Applies a transformation matrix to a vector
+ * @brief Applies a transformation matrix to a Vector3
*
* @param[out] result The transformed vector
* @param[in] m The transformation matrix
void TransformVector3( Vec3 result, const Mat4 m, const Vec3 v );
/**
+ * @brief Applies a transformation matrix to a Vector2
+ * Note: This performs an optimized 2D transformation.
+ *
+ * @param[in] matrix The matrix to transform by
+ * @param[in] x The x coordinate to multiply the matrix by
+ * @param[in] y The y coordinate to multiply the matrix by
+ * @return A Vector2 containing the resulting coordinates
+ */
+Vector2 Transform2D( const Matrix& matrix, const float x, const float y );
+
+/**
* @brief Computes the length of a vector3
*
* @param[in] v The vector
} // namespace Dali
-#endif //__DALI_INTERNAL_MATH_H__
+#endif // DALI_INTERNAL_MATH_H
// Note: These MUST be in the same order as Dali::StencilOperation enum.
const int DaliStencilOperationToGL[] = { GL_ZERO, GL_KEEP, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP };
-} // Unnamed namespace
-
/**
- * Sets up the scissor test if required.
- * @param[in] renderList The render list from which to get the clipping flag
- * @param[in] context The context
+ * @brief Find the intersection of two AABB rectangles.
+ * This is a logical AND operation. IE. The intersection is the area overlapped by both rectangles.
+ * @param[in] aabbA Rectangle A
+ * @param[in] aabbB Rectangle B
+ * @return The intersection of rectangle A & B (result is a rectangle)
*/
-inline void SetScissorTest( const RenderList& renderList, Context& context )
+inline ClippingBox IntersectAABB( const ClippingBox& aabbA, const ClippingBox& aabbB )
{
- // Scissor testing
- if( renderList.IsClipping() )
- {
- context.SetScissorTest( true );
+ ClippingBox intersectionBox;
- const Dali::ClippingBox& clip = renderList.GetClippingBox();
- context.Scissor( clip.x, clip.y, clip.width, clip.height );
- }
- else
- {
- context.SetScissorTest( false );
- }
+ // First calculate the largest starting positions in X and Y.
+ intersectionBox.x = std::max( aabbA.x, aabbB.x );
+ intersectionBox.y = std::max( aabbA.y, aabbB.y );
+
+ // Now calculate the smallest ending positions, and take the largest starting
+ // positions from the result, to get the width and height respectively.
+ // If the two boxes do not intersect at all, then we need a 0 width and height clipping area.
+ // We use max here to clamp both width and height to >= 0 for this use-case.
+ intersectionBox.width = std::max( std::min( aabbA.x + aabbA.width, aabbB.x + aabbB.width ) - intersectionBox.x, 0 );
+ intersectionBox.height = std::max( std::min( aabbA.y + aabbA.height, aabbB.y + aabbB.height ) - intersectionBox.y, 0 );
+
+ return intersectionBox;
}
/**
* @brief Set up the stencil and color buffer for automatic clipping (StencilMode::AUTO).
* @param[in] item The current RenderItem about to be rendered
* @param[in] context The context
- * @param[in/out] lastStencilDepth The stencil depth of the last renderer drawn.
+ * @param[in/out] lastClippingDepth The stencil depth of the last renderer drawn.
* @param[in/out] lastClippingId The clipping ID of the last renderer drawn.
*/
-inline void SetupClipping( const RenderItem& item, Context& context, uint32_t& lastStencilDepth, uint32_t& lastClippingId )
+inline void SetupStencilClipping( const RenderItem& item, Context& context, uint32_t& lastClippingDepth, uint32_t& lastClippingId )
{
const Dali::Internal::SceneGraph::Node* node = item.mNode;
const uint32_t clippingId = node->GetClippingId();
-
- // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
- context.ColorMask( true );
-
// If there is no clipping Id, then either we haven't reached a clipping Node yet, or there aren't any.
// Either way we can skip clipping setup for this renderer.
if( clippingId == 0u )
return;
}
- const ClippingMode::Type clippingMode( node->GetClippingMode() );
- const uint32_t currentStencilDepth( node->GetClippingDepth() );
-
context.EnableStencilBuffer( true );
+ const uint32_t clippingDepth = node->GetClippingDepth();
+
// Pre-calculate a mask which has all bits set up to and including the current clipping depth.
// EG. If depth is 3, the mask would be "111" in binary.
- const uint32_t currentDepthMask = ( 1u << currentStencilDepth ) - 1u;
+ const uint32_t currentDepthMask = ( 1u << clippingDepth ) - 1u;
- // If we have a clipping mode specified, we are writing to the stencil buffer.
- if( clippingMode != ClippingMode::DISABLED )
+ // Are we are writing to the stencil buffer?
+ if( item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_CHILDREN )
{
// We are writing to the stencil buffer.
// If clipping Id is 1, this is the first clipping renderer within this render-list.
context.StencilMask( 0xff );
context.Clear( GL_STENCIL_BUFFER_BIT, Context::CHECK_CACHED_VALUES );
}
- else if( ( currentStencilDepth < lastStencilDepth ) ||
- ( ( currentStencilDepth == lastStencilDepth ) && ( clippingId > lastClippingId ) ) )
+ else if( ( clippingDepth < lastClippingDepth ) ||
+ ( ( clippingDepth == lastClippingDepth ) && ( clippingId > lastClippingId ) ) )
{
// The above if() statement tests if we need to clear some (not all) stencil bit-planes.
// We need to do this if either of the following are true:
// We keep track of the last clipping Id and depth so we can determine when we are
// moving back up the scene graph and require some of the stencil bit-planes to be deleted.
- lastStencilDepth = currentStencilDepth;
+ lastClippingDepth = clippingDepth;
lastClippingId = clippingId;
// We only ever write to bit-planes up to the current depth as we may need
}
/**
- * @brief Set up the stencil and color buffer based on the current Renderers properties.
+ * @brief Sets up the depth buffer for reading and writing based on the current render item.
+ * The items read and write mode are used if specified.
+ * - If AUTO is selected for reading, the decision will be based on the Layer Behavior.
+ * - If AUTO is selected for writing, the decision will be based on the items opacity.
+ * @param[in] item The RenderItem to set up the depth buffer for.
+ * @param[in] context The context used to execute GL commands.
+ * @param[in] depthTestEnabled True if depth testing has been enabled.
+ * @param[in/out] firstDepthBufferUse Initialize to true on the first call, this method will set it to false afterwards.
+ */
+inline void SetupDepthBuffer( const RenderItem& item, Context& context, bool depthTestEnabled, bool& firstDepthBufferUse )
+{
+ // Set up whether or not to write to the depth buffer.
+ const DepthWriteMode::Type depthWriteMode = item.mRenderer->GetDepthWriteMode();
+ // Most common mode (AUTO) is tested first.
+ const bool enableDepthWrite = ( ( depthWriteMode == DepthWriteMode::AUTO ) && depthTestEnabled && item.mIsOpaque ) ||
+ ( depthWriteMode == DepthWriteMode::ON );
+
+ // Set up whether or not to read from (test) the depth buffer.
+ const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
+ // Most common mode (AUTO) is tested first.
+ const bool enableDepthTest = ( ( depthTestMode == DepthTestMode::AUTO ) && depthTestEnabled ) ||
+ ( depthTestMode == DepthTestMode::ON );
+
+ // Is the depth buffer in use?
+ if( enableDepthWrite || enableDepthTest )
+ {
+ // The depth buffer must be enabled if either reading or writing.
+ context.EnableDepthBuffer( true );
+
+ // Set up the depth mask based on our depth write setting.
+ context.DepthMask( enableDepthWrite );
+
+ // Look-up the GL depth function from the Dali::DepthFunction enum, and set it.
+ context.DepthFunc( DaliDepthToGLDepthTable[ item.mRenderer->GetDepthFunction() ] );
+
+ // If this is the first use of the depth buffer this RenderTask, perform a clear.
+ // Note: We could do this at the beginning of the RenderTask and rely on the
+ // context cache to ignore the clear if not required, but, we would have to enable
+ // the depth buffer to do so, which could be a redundant enable.
+ if( DALI_UNLIKELY( firstDepthBufferUse ) )
+ {
+ // This is the first time the depth buffer is being written to or read.
+ firstDepthBufferUse = false;
+
+ // Note: The buffer will only be cleared if written to since a previous clear.
+ context.Clear( GL_DEPTH_BUFFER_BIT, Context::CHECK_CACHED_VALUES );
+ }
+ }
+ else
+ {
+ // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
+ context.EnableDepthBuffer( false );
+ }
+}
+
+} // Unnamed namespace
+
+
+/**
+ * @brief This method is responsible for making decisions on when to apply and unapply scissor clipping, and what rectangular dimensions should be used.
+ * A stack of scissor clips at each depth of clipping is maintained, so it can be applied and unapplied.
+ * As the clips are hierarchical, this RenderItems AABB is clipped against the current "active" scissor bounds via an intersection operation.
* @param[in] item The current RenderItem about to be rendered
* @param[in] context The context
- * @param[in/out] usedStencilBuffer True if the stencil buffer has been used so far within this RenderList. Used by StencilMode::ON.
- * @param[in/out] lastStencilDepth The stencil depth of the last renderer drawn. Used by the clipping feature.
- * @param[in/out] lastClippingId The clipping ID of the last renderer drawn. Used by the clipping feature.
*/
-inline void SetupStencilBuffer( const RenderItem& item, Context& context, bool& usedStencilBuffer, uint32_t& lastStencilDepth, uint32_t& lastClippingId )
+inline void RenderAlgorithms::SetupScissorClipping( const RenderItem& item, Context& context )
+{
+ // Get the number of child scissors in the stack (do not include layer or root box).
+ size_t childStackDepth = mScissorStack.size() - 1u;
+ const uint32_t scissorDepth = item.mNode->GetScissorDepth();
+ const bool clippingNode = item.mNode->GetClippingMode() == Dali::ClippingMode::CLIP_TO_BOUNDING_BOX;
+ bool traversedUpTree = false;
+
+ // If we are using scissor clipping and we are at the same depth (or less), we need to undo previous clips.
+ // We do this by traversing up the scissor clip stack and then apply the appropriate clip for the current render item.
+ // To know this, we use clippingDepth. This value is set on *every* node, but only increased as clipping nodes are hit depth-wise.
+ // So we know if we are at depth 4 and the stackDepth is 5, that we have gone up.
+ // 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.
+ // Note: Stack depth must always be at least 1, as we will have the layer or stage size as the root value.
+ if( ( childStackDepth > 0u ) && ( scissorDepth < childStackDepth ) )
+ {
+ while( scissorDepth < childStackDepth )
+ {
+ mScissorStack.pop_back();
+ --childStackDepth;
+ }
+
+ // We traversed up the tree, we need to apply a new scissor rectangle (unless we are at the root).
+ traversedUpTree = true;
+ }
+
+ // 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.
+ if( clippingNode || traversedUpTree )
+ {
+ // First, check if we are a clipping node.
+ if( clippingNode )
+ {
+ // This is a clipping node. We generate the AABB for this node and intersect it with the previous intersection further up the tree.
+
+ // Get the AABB bounding box for the current render item.
+ const ClippingBox scissorBox( item.CalculateViewportSpaceAABB( mViewportRectangle.width, mViewportRectangle.height ) );
+
+ // Get the AABB for the parent item that we must intersect with.
+ const ClippingBox& parentBox( mScissorStack.back() );
+
+ // We must reduce the clipping area based on the parents area to allow nested clips. This is a set intersection function.
+ // We add the new scissor box to the stack so we can return to it if needed.
+ mScissorStack.emplace_back( IntersectAABB( parentBox, scissorBox ) );
+ }
+
+ // 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.
+ // IE. It is not enabled if we are at the top of the stack and the layer does not have a specified clipping box.
+ const bool scissorEnabled = ( mScissorStack.size() > 0u ) || mHasLayerScissor;
+
+ // Enable the scissor test based on the above calculation
+ context.SetScissorTest( scissorEnabled );
+
+ // If scissor is enabled, we use the calculated screen-space coordinates (now in the stack).
+ if( scissorEnabled )
+ {
+ ClippingBox useScissorBox( mScissorStack.back() );
+ context.Scissor( useScissorBox.x, useScissorBox.y, useScissorBox.width, useScissorBox.height );
+ }
+ }
+}
+
+inline void RenderAlgorithms::SetupClipping( const RenderItem& item, Context& context, bool& usedStencilBuffer, uint32_t& lastClippingDepth, uint32_t& lastClippingId )
{
+ RenderMode::Type renderMode = RenderMode::AUTO;
const Renderer *renderer = item.mRenderer;
+ if( renderer )
+ {
+ renderMode = renderer->GetRenderMode();
+ }
// Setup the stencil using either the automatic clipping feature, or, the manual per-renderer stencil API.
// Note: This switch is in order of most likely value first.
- RenderMode::Type renderMode = renderer->GetRenderMode();
switch( renderMode )
{
case RenderMode::AUTO:
{
- // The automatic clipping feature will manage the stencil functions and color buffer mask.
- SetupClipping( item, context, lastStencilDepth, lastClippingId );
+ // Turn the color buffer on as we always want to render this renderer, regardless of clipping hierarchy.
+ context.ColorMask( true );
+
+ // The automatic clipping feature will manage the scissor and stencil functions.
+ // 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.
+ // We process both based on our current and old clipping depths for each mode.
+ // Both methods with return rapidly if there is nothing to be done for that type of clipping.
+ SetupScissorClipping( item, context );
+ SetupStencilClipping( item, context, lastClippingDepth, lastClippingId );
break;
}
case RenderMode::NONE:
case RenderMode::COLOR:
{
+ // No clipping is performed for these modes.
+ // Note: We do not turn off scissor clipping as it may be used for the whole layer.
// The stencil buffer will not be used at all.
context.EnableStencilBuffer( false );
}
}
-/**
- * @brief Sets up the depth buffer for reading and writing based on the current render item.
- * The items read and write mode are used if specified.
- * - If AUTO is selected for reading, the decision will be based on the Layer Behavior.
- * - If AUTO is selected for writing, the decision will be based on the items opacity.
- * @param[in] item The RenderItem to set up the depth buffer for.
- * @param[in] context The context used to execute GL commands.
- * @param[in] depthTestEnabled True if depth testing has been enabled.
- * @param[in/out] firstDepthBufferUse Initialise to true on the first call, this method will set it to false afterwards.
- */
-inline void SetupDepthBuffer( const RenderItem& item, Context& context, bool depthTestEnabled, bool& firstDepthBufferUse )
-{
- // Set up whether or not to write to the depth buffer.
- const DepthWriteMode::Type depthWriteMode = item.mRenderer->GetDepthWriteMode();
- // Most common mode (AUTO) is tested first.
- const bool enableDepthWrite = ( ( depthWriteMode == DepthWriteMode::AUTO ) && depthTestEnabled && item.mIsOpaque ) ||
- ( depthWriteMode == DepthWriteMode::ON );
-
- // Set up whether or not to read from (test) the depth buffer.
- const DepthTestMode::Type depthTestMode = item.mRenderer->GetDepthTestMode();
- // Most common mode (AUTO) is tested first.
- const bool enableDepthTest = ( ( depthTestMode == DepthTestMode::AUTO ) && depthTestEnabled ) ||
- ( depthTestMode == DepthTestMode::ON );
-
- // Is the depth buffer in use?
- if( enableDepthWrite || enableDepthTest )
- {
- // The depth buffer must be enabled if either reading or writing.
- context.EnableDepthBuffer( true );
-
- // Set up the depth mask based on our depth write setting.
- context.DepthMask( enableDepthWrite );
-
- // Look-up the GL depth function from the Dali::DepthFunction enum, and set it.
- context.DepthFunc( DaliDepthToGLDepthTable[ item.mRenderer->GetDepthFunction() ] );
-
- // If this is the first use of the depth buffer this RenderTask, perform a clear.
- // Note: We could do this at the beginning of the RenderTask and rely on the
- // context cache to ignore the clear if not required, but, we would have to enable
- // the depth buffer to do so, which could be a redundant enable.
- if( DALI_UNLIKELY( firstDepthBufferUse ) )
- {
- // This is the first time the depth buffer is being written to or read.
- firstDepthBufferUse = false;
-
- // Note: The buffer will only be cleared if written to since a previous clear.
- context.Clear( GL_DEPTH_BUFFER_BIT, Context::CHECK_CACHED_VALUES );
- }
- }
- else
- {
- // The depth buffer is not being used by this renderer, so we must disable it to stop it being tested.
- context.EnableDepthBuffer( false );
- }
-}
-
-/**
- * @brief Process a render-list.
- * @param[in] renderList The render-list to process.
- * @param[in] context The GL context.
- * @param[in] buffer The current render buffer index (previous update buffer)
- * @param[in] viewMatrix The view matrix from the appropriate camera.
- * @param[in] projectionMatrix The projection matrix from the appropriate camera.
- */
-inline void ProcessRenderList(
+inline void RenderAlgorithms::ProcessRenderList(
const RenderList& renderList,
Context& context,
BufferIndex bufferIndex,
{
DALI_PRINT_RENDER_LIST( renderList );
- SetScissorTest( renderList, context );
-
// Note: The depth buffer is enabled or disabled on a per-renderer basis.
// Here we pre-calculate the value to use if these modes are set to AUTO.
const bool autoDepthTestMode( !( renderList.GetSourceLayer()->IsDepthTestDisabled() ) && renderList.HasColorRenderItems() );
const std::size_t count = renderList.Count();
- uint32_t lastStencilDepth( 0u );
+ uint32_t lastClippingDepth( 0u );
uint32_t lastClippingId( 0u );
bool usedStencilBuffer( false );
bool firstDepthBufferUse( true );
+ mViewportRectangle = context.GetViewport();
+ mHasLayerScissor = false;
+ // Setup Scissor testing (for both viewport and per-node scissor)
+ mScissorStack.clear();
+ if( renderList.IsClipping() )
+ {
+ context.SetScissorTest( true );
+ const ClippingBox& layerScissorBox = renderList.GetClippingBox();
+ context.Scissor( layerScissorBox.x, layerScissorBox.y, layerScissorBox.width, layerScissorBox.height );
+ mScissorStack.push_back( layerScissorBox );
+ mHasLayerScissor = true;
+ }
+ else
+ {
+ // We are not performing a layer clip. Add the viewport as the root scissor rectangle.
+ context.SetScissorTest( false );
+ mScissorStack.push_back( mViewportRectangle );
+ }
+
+ // Loop through all RenderList in the RenderList, set up any prerequisites to render them, then perform the render.
for( size_t index( 0u ); index < count; ++index )
{
const RenderItem& item = renderList.GetItem( index );
DALI_PRINT_RENDER_ITEM( item );
- // Set up the depth buffer based on per-renderer flags.
- // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
- // draw-mode state, such as Overlays.
- // If the flags are set to "AUTO", the behaviour then depends on the type of renderer. Overlay Renderers will always
- // disable depth testing and writing. Color Renderers will enable them if the Layer does.
- SetupDepthBuffer( item, context, autoDepthTestMode, firstDepthBufferUse );
-
- // Set up the stencil buffer based on both the Renderer and Actor APIs.
+ // Set up clipping based on both the Renderer and Actor APIs.
// The Renderer API will be used if specified. If AUTO, the Actors automatic clipping feature will be used.
- SetupStencilBuffer( item, context, usedStencilBuffer, lastStencilDepth, lastClippingId );
-
- // Render the item
- item.mRenderer->Render( context,
- bufferIndex,
- *item.mNode,
- item.mModelMatrix,
- item.mModelViewMatrix,
- viewMatrix,
- projectionMatrix,
- item.mSize,
- !item.mIsOpaque );
+ SetupClipping( item, context, usedStencilBuffer, lastClippingDepth, lastClippingId );
+
+ if( DALI_LIKELY( item.mRenderer ) )
+ {
+ // Set up the depth buffer based on per-renderer flags.
+ // If the per renderer flags are set to "ON" or "OFF", they will always override any Layer depth mode or
+ // draw-mode state, such as Overlays.
+ // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will always
+ // disable depth testing and writing. Color Renderers will enable them if the Layer does.
+ SetupDepthBuffer( item, context, autoDepthTestMode, firstDepthBufferUse );
+
+ // Render the item.
+ item.mRenderer->Render( context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
+ viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque );
+ }
}
}
-void ProcessRenderInstruction( const RenderInstruction& instruction,
- Context& context,
- BufferIndex bufferIndex )
+RenderAlgorithms::RenderAlgorithms()
+ : mViewportRectangle(),
+ mHasLayerScissor( false )
+{
+}
+
+void RenderAlgorithms::ProcessRenderInstruction( const RenderInstruction& instruction, Context& context, BufferIndex bufferIndex )
{
DALI_PRINT_RENDER_INSTRUCTION( instruction, bufferIndex );
if( renderList && !renderList->IsEmpty() )
{
- ProcessRenderList( *renderList,
- context,
- bufferIndex,
- *viewMatrix,
- *projectionMatrix );
+ ProcessRenderList( *renderList, context, bufferIndex, *viewMatrix, *projectionMatrix );
}
}
}
}
+
} // namespace Render
} // namespace Internal
#define DALI_INTERNAL_RENDER_ALGORITHMS_H
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// INTERNAL INCLUDES
#include <dali/internal/common/buffer-index.h>
+#include <dali/internal/render/common/render-list.h>
namespace Dali
{
+
namespace Internal
{
class Context;
namespace SceneGraph
{
class RenderInstruction;
-class TextureCache;
+struct RenderItem;
}
namespace Render
{
/**
- * Process a render-instruction.
- * @param[in] instruction The render-instruction to process.
- * @param[in] context The GL context.
- * @param[in] bufferIndex The current render buffer index (previous update buffer)
+ * @brief The responsibility of the RenderAlgorithms object is to action renders required by a RenderInstruction.
*/
-void ProcessRenderInstruction( const SceneGraph::RenderInstruction& instruction,
- Context& context,
- BufferIndex bufferIndex );
+class RenderAlgorithms
+{
+ public:
+
+ /**
+ * Constructor.
+ */
+ RenderAlgorithms();
+
+ /**
+ * Process a render-instruction.
+ * @param[in] instruction The render-instruction to process.
+ * @param[in] context The GL context.
+ * @param[in] bufferIndex The current render buffer index (previous update buffer)
+ */
+ void ProcessRenderInstruction( const SceneGraph::RenderInstruction& instruction, Context& context, BufferIndex bufferIndex );
+
+ private:
+
+ /**
+ * @brief Calculate a 2D AABB (axis aligned bounding box) in screen space.
+ * The RenderItems dimensions are translated and a Z value of 0 is assumed for this purpose.
+ * No projection is performed, but rotation on Z is supported.
+ * @param[in] item The RenderItem to generate an AABB for
+ * @return The generated AABB in screen space
+ */
+ inline Dali::ClippingBox CalculateScreenSpaceAABB( const Dali::Internal::SceneGraph::RenderItem& item );
+
+ /**
+ * @brief Perform any scissor clipping related operations based on the current RenderItem.
+ * This includes:
+ * - Determining if any action is to be taken (so the method can be exited early if not).
+ * - If the node is a clipping node, apply the nodes clip intersected with the current/parent scissor clip.
+ * - If we have gone up the scissor hierarchy, and need to un-apply a scissor clip.
+ * - Disable scissor clipping completely if it is not needed
+ * @param[in] item The current RenderItem (about to be rendered)
+ * @param[in] context The current Context
+ */
+ inline void SetupScissorClipping( const Dali::Internal::SceneGraph::RenderItem& item, Context& context );
+
+ /**
+ * @brief Set up the clipping based on the specified clipping settings.
+ * @param[in] item The current RenderItem (about to be rendered)
+ * @param[in] context The context
+ * @param[in/out] usedStencilBuffer True if the stencil buffer has been used so far within this RenderList. Used by StencilMode::ON.
+ * @param[in/out] lastClippingDepth The stencil depth of the last renderer drawn. Used by the clipping feature.
+ * @param[in/out] lastClippingId The clipping ID of the last renderer drawn. Used by the clipping feature.
+ */
+ inline void SetupClipping( const Dali::Internal::SceneGraph::RenderItem& item, Context& context, bool& usedStencilBuffer, uint32_t& lastClippingDepth, uint32_t& lastClippingId );
+
+ /**
+ * @brief Process a render-list.
+ * @param[in] renderList The render-list to process.
+ * @param[in] context The GL context.
+ * @param[in] buffer The current render buffer index (previous update buffer)
+ * @param[in] viewMatrix The view matrix from the appropriate camera.
+ * @param[in] projectionMatrix The projection matrix from the appropriate camera.
+ */
+ inline void ProcessRenderList( const Dali::Internal::SceneGraph::RenderList& renderList, Context& context, BufferIndex bufferIndex, const Matrix& viewMatrix, const Matrix& projectionMatrix );
+
+ // Prevent copying:
+ RenderAlgorithms( RenderAlgorithms& rhs );
+ RenderAlgorithms& operator=( const RenderAlgorithms& rhs );
+
+
+ // Member variables:
+
+ using ScissorStackType = std::vector<Dali::ClippingBox>; ///< The container type used to maintain the applied scissor hierarchy
+
+ ScissorStackType mScissorStack; ///< Contains the currently applied scissor hierarchy (so we can undo clips)
+ Dali::ClippingBox mViewportRectangle; ///< The viewport dimensions, used to translate AABBs to scissor coordinates
+ bool mHasLayerScissor:1; ///< Marks if the currently process render instruction has a layer-based clipping region
+};
} // namespace Render
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// INTERNAL INCLUDES
#include <dali/internal/common/memory-pool-object-allocator.h>
#include <dali/internal/render/renderers/render-renderer.h>
+#include <dali/internal/common/math.h>
namespace
{
{
}
+
+ClippingBox RenderItem::CalculateViewportSpaceAABB( const int viewportWidth, const int viewportHeight ) const
+{
+ // Calculate extent vector of the AABB:
+ const float halfActorX = mSize.x * 0.5f;
+ const float halfActorY = mSize.y * 0.5f;
+
+ // To transform the actor bounds to screen-space, We do a fast, 2D version of a matrix multiply optimized for 2D quads.
+ // This reduces float multiplications from 64 (16 * 4) to 12 (4 * 3).
+ // We create an array of 4 corners and directly initialize the first 3 with the matrix multiplication result of the respective corner.
+ // This causes the construction of the vector arrays contents in-place for optimization.
+ // We place the coords into the array in clockwise order, so we know opposite corners are always i + 2 from corner i.
+ // We skip the 4th corner here as we can calculate that from the other 3, bypassing matrix multiplication.
+ // Note: The below transform methods use a fast (2D) matrix multiply (only 4 multiplications are done).
+ Vector2 corners[4]{ Transform2D( mModelViewMatrix, -halfActorX, -halfActorY ),
+ Transform2D( mModelViewMatrix, halfActorX, -halfActorY ),
+ Transform2D( mModelViewMatrix, halfActorX, halfActorY ) };
+
+ // As we are dealing with a rectangle, we can do a fast calculation to get the 4th corner from knowing the other 3 (even if rotated).
+ corners[3] = Vector2( corners[0] + ( corners[2] - corners[1] ) );
+
+ // Calculate the AABB:
+ // We use knowledge that opposite corners will be the max/min of each other. Doing this reduces the normal 12 branching comparisons to 3.
+ // The standard equivalent min/max code of the below would be:
+ // Vector2 AABBmax( std::max( corners[0].x, std::max( corners[1].x, std::max( corners[3].x, corners[2].x ) ) ),
+ // std::max( corners[0].y, std::max( corners[1].y, std::max( corners[3].y, corners[2].y ) ) ) );
+ // Vector2 AABBmin( std::min( corners[0].x, std::min( corners[1].x, std::min( corners[3].x, corners[2].x ) ) ),
+ // std::min( corners[0].y, std::min( corners[1].y, std::min( corners[3].y, corners[2].y ) ) ) );
+ unsigned int smallestX = 0u;
+ // Loop 3 times to find the index of the smallest X value.
+ // Note: We deliberately do NOT unroll the code here as this hampers the compilers output.
+ for( unsigned int i = 1u; i < 4u; ++i )
+ {
+ if( corners[i].x < corners[smallestX].x )
+ {
+ smallestX = i;
+ }
+ }
+
+ // As we are dealing with a rectangle, we can assume opposite corners are the largest.
+ // So without doing min/max branching, we can fetch the min/max values of all the remaining X/Y coords from this one index.
+ Vector4 aabb( corners[smallestX].x, corners[( smallestX + 3u ) % 4].y, corners[( smallestX + 2u ) % 4].x, corners[( smallestX + 1u ) % 4].y );
+
+ // Convert maximums to extents.
+ aabb.z -= aabb.x;
+ aabb.w -= aabb.y;
+
+ // Return the AABB in screen-space pixels (x, y, width, height).
+ // Note: This is a algebraic simplification of: ( viewport.x - aabb.width ) / 2 - ( ( aabb.width / 2 ) + aabb.x ) per axis.
+ return ClippingBox( ( viewportWidth / 2 ) - aabb.z - aabb.x, ( viewportHeight / 2 ) - aabb.w - aabb.y, aabb.z, aabb.w );
+}
+
void RenderItem::operator delete( void* ptr )
{
gRenderItemPool.Free( static_cast<RenderItem*>( ptr ) );
}
+
} // namespace SceneGraph
} // namespace Internal
#define DALI_INTERNAL_SCENE_GRAPH_RENDER_ITEM_H
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
~RenderItem();
/**
+ * @brief This method is an optimized calculation of a viewport-space AABB (Axis-Aligned-Bounding-Box).
+ *
+ * We use the model-view-matrix, but we do not use projection. Therefore we assume Z = 0.
+ * As the box is Axis-Aligned (in viewport space) rotations on Z axis are correctly delt with by expanding the box as necessary.
+ * Rotations on X & Y axis will resize the AABB, but it will not handle the projection error due to the new coordinates having non-zero Z values.
+ *
+ * Note: We pass in the viewport dimensions rather than allow the caller to modify the raw AABB in order to optimally generate the final result.
+ *
+ * @param[in] viewportWidth The width of the viewport to calculate for
+ * @param[in] viewportHeight The height of the viewport to calculate for
+ * @return The AABB coordinates in viewport-space (x, y, width, height)
+ */
+ ClippingBox CalculateViewportSpaceAABB( const int viewportWidth, const int viewportHeight ) const;
+
+ /**
* Overriden delete operator.
* Deletes the RenderItem from its global memory pool
* @param[in] A pointer to the RenderItem to delete.
glSyncAbstraction( glSyncAbstraction ),
renderQueue(),
instructions(),
+ renderAlgorithms(),
backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ),
frameCount( 0 ),
renderBufferIndex( SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX ),
// Render instructions describe what should be rendered during RenderManager::Render()
// Owned by RenderManager. Update manager updates instructions for the next frame while we render the current one
RenderInstructionContainer instructions;
+ Render::RenderAlgorithms renderAlgorithms; ///< The RenderAlgorithms object is used to action the renders required by a RenderInstruction
Vector4 backgroundColor; ///< The glClear color used at the beginning of each frame.
mImpl->context.SetScissorTest( false );
}
- Render::ProcessRenderInstruction( instruction,
- mImpl->context,
- mImpl->renderBufferIndex );
+ mImpl->renderAlgorithms.ProcessRenderInstruction( instruction, mImpl->context, mImpl->renderBufferIndex );
if( instruction.mRenderTracker && ( instruction.mFrameBuffer != NULL ) )
{
bool inside( true );
const Node* node = renderable.mNode;
- if( cull && !renderable.mRenderer->GetShader().HintEnabled( Dali::Shader::Hint::MODIFIES_GEOMETRY ) )
+ if( cull && renderable.mRenderer && !renderable.mRenderer->GetShader().HintEnabled( Dali::Shader::Hint::MODIFIES_GEOMETRY ) )
{
const Vector4& boundingSphere = node->GetBoundingSphere();
inside = ( boundingSphere.w > Math::MACHINE_EPSILON_1000 ) &&
if( inside )
{
- Renderer::Opacity opacity = renderable.mRenderer->GetOpacity( updateBufferIndex, *renderable.mNode );
+ Renderer::Opacity opacity = renderable.mRenderer ? renderable.mRenderer->GetOpacity( updateBufferIndex, *renderable.mNode ) : Renderer::OPAQUE;
if( opacity != Renderer::TRANSPARENT )
{
// Get the next free RenderItem.
RenderItem& item = renderList.GetNextFreeItem();
- item.mRenderer = &renderable.mRenderer->GetRenderer();
+
item.mNode = renderable.mNode;
- item.mTextureSet = renderable.mRenderer->GetTextures();
item.mIsOpaque = ( opacity == Renderer::OPAQUE );
- item.mDepthIndex = renderable.mRenderer->GetDepthIndex();
-
if( !isLayer3d )
{
- item.mDepthIndex += renderable.mNode->GetDepthIndex();
+ item.mDepthIndex = renderable.mNode->GetDepthIndex();
+ }
+
+ if( DALI_LIKELY( renderable.mRenderer ) )
+ {
+ item.mRenderer = &renderable.mRenderer->GetRenderer();
+ item.mTextureSet = renderable.mRenderer->GetTextures();
+ item.mDepthIndex += renderable.mRenderer->GetDepthIndex();
+ }
+ else
+ {
+ item.mRenderer = nullptr;
}
// Save ModelView matrix onto the item.
{
RenderItem& item = renderList.GetItem( index );
- item.mRenderer->SetSortAttributes( bufferIndex, mSortingHelper[ index ] );
+ if( item.mRenderer )
+ {
+ item.mRenderer->SetSortAttributes( bufferIndex, mSortingHelper[ index ] );
+ }
// texture set
mSortingHelper[ index ].textureSet = item.mTextureSet;
* Rebuild the Layer::colorRenderables and overlayRenderables members,
* including only renderers which are included in the current render-task.
*
- * @param[in] updateBufferIndex The current update buffer index.
- * @param[in] node The current node of the scene-graph.
- * @param[in] currentLayer The current layer containing lists of opaque/transparent renderables.
- * @param[in] renderTask The current render-task.
- * @param[in] inheritedDrawMode The draw mode of the parent
- * @param[in] parentDepthIndex The inherited parent node depth index
- * @param[in] currentClippingId The current Clipping Id
- * Note: ClippingId is passed by reference, so it is permanently modified when traversing back up the tree for uniqueness.
- * @param[in] clippingDepth The current clipping depth
+ * @param[in] updateBufferIndex The current update buffer index.
+ * @param[in] node The current node of the scene-graph.
+ * @param[in] currentLayer The current layer containing lists of opaque/transparent renderables.
+ * @param[in] renderTask The current render-task.
+ * @param[in] inheritedDrawMode The draw mode of the parent
+ * @param[in] parentDepthIndex The inherited parent node depth index
+ * @param[in] currentClippingId The current Clipping Id
+ * Note: ClippingId is passed by reference, so it is permanently modified when traversing back up the tree for uniqueness.
+ * @param[in] clippingDepth The current stencil clipping depth
+ * @param[in] clippingDepth The current scissor clipping depth
+ * @param[out] clippingUsed Gets set to true if any clipping nodes have been found
*/
void AddRenderablesForTask( BufferIndex updateBufferIndex,
Node& node,
RenderTask& renderTask,
int inheritedDrawMode,
uint32_t& currentClippingId,
- uint32_t clippingDepth )
+ uint32_t clippingDepth,
+ uint32_t scissorDepth,
+ bool& clippingUsed )
{
// Short-circuit for invisible nodes
if( !node.IsVisible( updateBufferIndex ) )
DALI_ASSERT_DEBUG( NULL != layer );
+ const unsigned int count = node.GetRendererCount();
+
// Update the clipping Id and depth for this node (if clipping is enabled).
- if( DALI_UNLIKELY( node.GetClippingMode() != ClippingMode::DISABLED ) )
+ const Dali::ClippingMode::Type clippingMode = node.GetClippingMode();
+ if( DALI_UNLIKELY( clippingMode != ClippingMode::DISABLED ) )
{
- ++currentClippingId; // This modifies the reference passed in as well as the local value, causing the value to be global to the recursion.
- ++clippingDepth; // This only modifies the local value (which is passed in when the method recurses).
+ if( DALI_LIKELY( clippingMode == ClippingMode::CLIP_TO_BOUNDING_BOX ) )
+ {
+ ++scissorDepth; // This only modifies the local value (which is passed in when the method recurses).
+ // If we do not have any renderers, create one to house the scissor operation.
+ if( count == 0u )
+ {
+ layer->colorRenderables.PushBack( Renderable( &node, nullptr ) );
+ }
+ }
+ else
+ {
+ // We only need clipping Id for stencil clips. This means we can deliberately avoid modifying it for bounding box clips,
+ // thus allowing bounding box clipping to still detect clip depth changes without turning on the stencil buffer for non-clipped nodes.
+ ++currentClippingId; // This modifies the reference passed in as well as the local value, causing the value to be global to the recursion.
+ ++clippingDepth; // This only modifies the local value (which is passed in when the method recurses).
+ }
+ clippingUsed = true;
}
// Set the information in the node.
- node.SetClippingInformation( currentClippingId, clippingDepth );
+ node.SetClippingInformation( currentClippingId, clippingDepth, scissorDepth );
- const unsigned int count = node.GetRendererCount();
for( unsigned int i = 0; i < count; ++i )
{
SceneGraph::Renderer* renderer = node.GetRendererAt( i );
for( NodeIter iter = children.Begin(); iter != endIter; ++iter )
{
Node& child = **iter;
- AddRenderablesForTask( updateBufferIndex, child, *layer, renderTask, inheritedDrawMode, currentClippingId, clippingDepth );
+ AddRenderablesForTask( updateBufferIndex, child, *layer, renderTask, inheritedDrawMode, currentClippingId, clippingDepth, scissorDepth, clippingUsed );
}
}
renderTask,
sourceNode->GetDrawMode(),
clippingId,
- 0u );
-
- // If the clipping Id is still 0 after adding all Renderables, there is no clipping required for this RenderTaskList.
- hasClippingNodes = clippingId != 0u;
+ 0u,
+ 0u,
+ hasClippingNodes );
mRenderInstructionProcessor.Prepare( updateBufferIndex,
sortedLayers,
// Now that the off screen renders are done we can process on screen render tasks.
// Reset the clipping Id for the OnScreen render tasks.
clippingId = 0u;
+ hasClippingNodes = false;
for ( RenderTaskList::RenderTaskContainer::Iterator iter = taskContainer.Begin(); endIter != iter; ++iter )
{
RenderTask& renderTask = **iter;
renderTask,
sourceNode->GetDrawMode(),
clippingId,
- 0u );
-
- // If the clipping Id is still 0 after adding all Renderables, there is no clipping required for this RenderTaskList.
- hasClippingNodes = clippingId != 0;
+ 0u,
+ 0u,
+ hasClippingNodes );
mRenderInstructionProcessor.Prepare( updateBufferIndex,
sortedLayers,
mExclusiveRenderTask( NULL ),
mChildren(),
mClippingDepth( 0u ),
+ mScissorDepth( 0u ),
mDepthIndex( 0u ),
mRegenerateUniformMap( 0 ),
mDirtyFlags( AllFlags ),
* A value is calculated that can be used during sorting to increase sort speed.
* @param[in] clippingId The Clipping ID of the node to set
* @param[in] clippingDepth The Clipping Depth of the node to set
+ * @param[in] scissorDepth The Scissor Clipping Depth of the node to set
*/
- void SetClippingInformation( const uint32_t clippingId, const uint32_t clippingDepth )
+ void SetClippingInformation( const uint32_t clippingId, const uint32_t clippingDepth, const uint32_t scissorDepth )
{
- // We only set up the sort value if we have a clipping depth, IE. At least 1 clipping node has been hit.
+ // We only set up the sort value if we have a stencil clipping depth, IE. At least 1 clipping node has been hit.
// If not, if we traverse down a clipping tree and back up, and there is another
// node on the parent, this will have a non-zero clipping ID that must be ignored
- if( DALI_LIKELY( clippingDepth > 0u ) )
+ if( clippingDepth > 0u )
{
mClippingDepth = clippingDepth;
// If we do not have a clipping depth, then set this to 0 so we do not have a Clipping ID either.
mClippingSortModifier = 0u;
}
+
+ // The scissor depth does not modify the clipping sort modifier (as scissor clips are 2D only).
+ // For this reason we can always update the member variable.
+ mScissorDepth = scissorDepth;
}
/**
}
/**
+ * Gets the Scissor Clipping Depth for this node.
+ * @return The Scissor Clipping Depth for this node.
+ */
+ uint32_t GetScissorDepth() const
+ {
+ return mScissorDepth;
+ }
+
+ /**
* Sets the clipping mode for this node.
* @param[in] clippingMode The ClippingMode to set
*/
CollectedUniformMap mCollectedUniformMap[2]; ///< Uniform maps of the node
unsigned int mUniformMapChanged[2]; ///< Records if the uniform map has been altered this frame
- uint32_t mClippingDepth; ///< The number of clipping nodes deep this node is
+ uint32_t mClippingDepth; ///< The number of stencil clipping nodes deep this node is
+ uint32_t mScissorDepth; ///< The number of scissor clipping nodes deep this node is
uint32_t mDepthIndex; ///< Depth index of the node
*/
namespace ClippingMode
{
-
/**
* @brief Enumeration for ClippingMode types.
* @SINCE_1_2_5
enum Type
{
DISABLED, ///< This Actor will not clip its children. @SINCE_1_2_5
- CLIP_CHILDREN, ///< This Actor will clip all children to within its boundaries (the actor will also be visible itself). @SINCE_1_2_5
+ CLIP_CHILDREN, ///< This Actor will clip itself and all children to within the pixel areas of this actors renderers. @SINCE_1_2_5
+ CLIP_TO_BOUNDING_BOX ///< This Actor will clip itself and all children to within a screen-aligned rectangle encompassing its boundaries. @SINCE_1_2.61
};
} // namespace ClippingMode