From e5ae770573de0421fdeb9b5509a82fdd14e92284 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Fri, 22 Sep 2017 18:04:51 +0100 Subject: [PATCH] [4.0] Fast bounding-box clipping feature * API: Actor::Property::CLIPPING_MODE enum: ClippingMode::CLIP_TO_BOUNDING_BOX * Actors/Views with CLIP_TO_BOUNDING_BOX will not be rendered. They are used purely to define clipping regions. * Note: Demo will be pushed separately Change-Id: I55d6da274861b4f4ef1473996c8572733964d47f --- .../dali-test-suite-utils/test-gl-abstraction.cpp | 1 + .../dali-test-suite-utils/test-gl-abstraction.h | 15 + automated-tests/src/dali/utc-Dali-Actor.cpp | 168 ++++++++- dali/internal/render/common/render-algorithms.cpp | 383 ++++++++++++++------- dali/internal/render/common/render-algorithms.h | 86 ++++- dali/internal/render/common/render-manager.cpp | 6 +- .../update/manager/render-task-processor.cpp | 60 ++-- dali/internal/update/nodes/node.cpp | 1 + dali/internal/update/nodes/node.h | 23 +- dali/public-api/actors/actor-enumerations.h | 3 +- dali/public-api/math/matrix.cpp | 19 + dali/public-api/math/matrix.h | 17 +- 12 files changed, 600 insertions(+), 182 deletions(-) diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp index c4bc01d..02439bc 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp @@ -85,6 +85,7 @@ void TestGlAbstraction::Initialize() mEnableDisableTrace.Reset(); mShaderTrace.Reset(); mStencilFunctionTrace.Reset(); + mScissorTrace.Reset(); mTextureTrace.Reset(); mTexParamaterTrace.Reset(); mDrawTrace.Reset(); diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h index 8408b10..b306c3e 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h @@ -949,6 +949,15 @@ public: 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) @@ -1951,6 +1960,11 @@ public: // TEST FUNCTIONS 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(); } @@ -2189,6 +2203,7 @@ private: TraceCallStack mDrawTrace; TraceCallStack mDepthFunctionTrace; TraceCallStack mStencilFunctionTrace; + TraceCallStack mScissorTrace; TraceCallStack mSetUniformTrace; // Shaders & Uniforms diff --git a/automated-tests/src/dali/utc-Dali-Actor.cpp b/automated-tests/src/dali/utc-Dali-Actor.cpp index 786be23..247ab80 100644 --- a/automated-tests/src/dali/utc-Dali-Actor.cpp +++ b/automated-tests/src/dali/utc-Dali-Actor.cpp @@ -3988,7 +3988,7 @@ void CheckColorMask( TestGlAbstraction& glAbstraction, bool maskValue ) 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(); @@ -4005,7 +4005,7 @@ int UtcDaliActorPropertyClippingP(void) DALI_TEST_EQUALS( 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. @@ -4018,13 +4018,25 @@ int UtcDaliActorPropertyClippingP(void) DALI_TEST_EQUALS( 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( 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(); @@ -4059,7 +4071,7 @@ int UtcDaliActorPropertyClippingN(void) 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(); @@ -4095,7 +4107,7 @@ int UtcDaliActorPropertyClippingActor(void) 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(); @@ -4141,12 +4153,11 @@ int UtcDaliActorPropertyClippingActorEnableThenDisable(void) 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(); @@ -4223,7 +4234,7 @@ int UtcDaliActorPropertyClippingNestedChildren(void) 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(); @@ -4312,6 +4323,134 @@ int UtcDaliActorPropertyClippingActorDrawOrder(void) 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. @@ -4344,6 +4483,19 @@ int UtcDaliActorPropertyClippingActorWithRendererOverride(void) 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; } diff --git a/dali/internal/render/common/render-algorithms.cpp b/dali/internal/render/common/render-algorithms.cpp index e31b8fe..612092c 100644 --- a/dali/internal/render/common/render-algorithms.cpp +++ b/dali/internal/render/common/render-algorithms.cpp @@ -55,44 +55,42 @@ const int DaliStencilFunctionToGL[] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, // 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 ) @@ -102,17 +100,16 @@ inline void SetupClipping( const RenderItem& item, Context& context, uint32_t& l 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. @@ -123,8 +120,8 @@ inline void SetupClipping( const RenderItem& item, Context& context, uint32_t& l 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: @@ -141,7 +138,7 @@ inline void SetupClipping( const RenderItem& item, Context& context, uint32_t& l // 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 @@ -168,14 +165,179 @@ inline void SetupClipping( const RenderItem& item, Context& context, uint32_t& l } /** - * @brief Set up the stencil and color buffer based on the current Renderers properties. - * @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. + * @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 SetupStencilBuffer( const RenderItem& item, Context& context, bool& usedStencilBuffer, uint32_t& lastStencilDepth, uint32_t& lastClippingId ) +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 + + +inline ClippingBox RenderAlgorithms::CalculateScreenSpaceAABB( const SceneGraph::RenderItem& item ) +{ + // Calculate extent vector of the AABB: + const Vector3& actorSize = item.mSize; + const float halfActorX = actorSize.x * 0.5f; + const float halfActorY = actorSize.y * 0.5f; + + // Transform to absolute oriented bounding box. + const Matrix& worldMatrix = item.mModelViewMatrix; + + // 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 skip the 4th corner here as we can calculate that from the other 3, bypassing matrix multiplication. + // Note: The below * operators trigger a fast (2D) matrix multiply (only 4 multiplications are done). + Vector2 corners[4]{ worldMatrix * Vector2( -halfActorX, -halfActorY ), + worldMatrix * Vector2( halfActorX, -halfActorY ), + worldMatrix * Vector2( 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( ( mViewportRectangle.width / 2 ) - aabb.z - aabb.x, ( mViewportRectangle.height / 2 ) - aabb.w - aabb.y, aabb.z, aabb.w ); +} + +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( CalculateScreenSpaceAABB( item ) ); + // 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 ) { const Renderer *renderer = item.mRenderer; @@ -186,14 +348,23 @@ inline void SetupStencilBuffer( const RenderItem& item, Context& context, bool& { 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 ); @@ -233,71 +404,7 @@ inline void SetupStencilBuffer( const RenderItem& item, Context& context, bool& } } -/** - * @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, @@ -306,16 +413,33 @@ inline void ProcessRenderList( { 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 ); + } for( size_t index( 0u ); index < count; ++index ) { @@ -325,30 +449,30 @@ inline void ProcessRenderList( // 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 + // 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 ); - // 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 ); + + // Render the item (we skip rendering for bounding box clips). + if( item.mNode->GetClippingMode() != ClippingMode::CLIP_TO_BOUNDING_BOX ) + { + 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 ); @@ -370,16 +494,13 @@ void ProcessRenderInstruction( const RenderInstruction& instruction, if( renderList && !renderList->IsEmpty() ) { - ProcessRenderList( *renderList, - context, - bufferIndex, - *viewMatrix, - *projectionMatrix ); + ProcessRenderList( *renderList, context, bufferIndex, *viewMatrix, *projectionMatrix ); } } } } + } // namespace Render } // namespace Internal diff --git a/dali/internal/render/common/render-algorithms.h b/dali/internal/render/common/render-algorithms.h index 0346ef7..156dfb0 100644 --- a/dali/internal/render/common/render-algorithms.h +++ b/dali/internal/render/common/render-algorithms.h @@ -2,7 +2,7 @@ #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. @@ -20,9 +20,11 @@ // INTERNAL INCLUDES #include +#include namespace Dali { + namespace Internal { class Context; @@ -30,21 +32,87 @@ 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: + + typedef std::vector ScissorStackType; ///< 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 diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index b391961..f19c956 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -59,6 +59,7 @@ struct RenderManager::Impl glSyncAbstraction( glSyncAbstraction ), renderQueue(), instructions(), + renderAlgorithms(), backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ), frameCount( 0 ), renderBufferIndex( SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX ), @@ -104,6 +105,7 @@ struct RenderManager::Impl // 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. @@ -530,9 +532,7 @@ void RenderManager::DoRender( RenderInstruction& instruction ) 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 ) ) { diff --git a/dali/internal/update/manager/render-task-processor.cpp b/dali/internal/update/manager/render-task-processor.cpp index 18cb3ee..4aafa1c 100644 --- a/dali/internal/update/manager/render-task-processor.cpp +++ b/dali/internal/update/manager/render-task-processor.cpp @@ -87,15 +87,17 @@ Layer* FindLayer( Node& node ) * 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, @@ -103,7 +105,9 @@ void AddRenderablesForTask( BufferIndex updateBufferIndex, 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 ) ) @@ -135,13 +139,24 @@ void AddRenderablesForTask( BufferIndex updateBufferIndex, DALI_ASSERT_DEBUG( NULL != layer ); // 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). + } + 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 ) @@ -165,7 +180,7 @@ void AddRenderablesForTask( BufferIndex updateBufferIndex, 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 ); } } @@ -248,10 +263,9 @@ void RenderTaskProcessor::Process( BufferIndex updateBufferIndex, 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, @@ -267,6 +281,7 @@ void RenderTaskProcessor::Process( BufferIndex updateBufferIndex, // 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; @@ -308,10 +323,9 @@ void RenderTaskProcessor::Process( BufferIndex updateBufferIndex, 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, diff --git a/dali/internal/update/nodes/node.cpp b/dali/internal/update/nodes/node.cpp index a785e3d..5030a03 100644 --- a/dali/internal/update/nodes/node.cpp +++ b/dali/internal/update/nodes/node.cpp @@ -99,6 +99,7 @@ Node::Node() mExclusiveRenderTask( NULL ), mChildren(), mClippingDepth( 0u ), + mScissorDepth( 0u ), mDepthIndex( 0u ), mRegenerateUniformMap( 0 ), mDirtyFlags( AllFlags ), diff --git a/dali/internal/update/nodes/node.h b/dali/internal/update/nodes/node.h index 2539404..93e6a3f 100644 --- a/dali/internal/update/nodes/node.h +++ b/dali/internal/update/nodes/node.h @@ -140,13 +140,14 @@ public: * 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; @@ -161,6 +162,10 @@ public: // 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; } /** @@ -182,6 +187,15 @@ public: } /** + * 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 */ @@ -865,7 +879,8 @@ protected: 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 diff --git a/dali/public-api/actors/actor-enumerations.h b/dali/public-api/actors/actor-enumerations.h index 72d3d42..6dd36db 100644 --- a/dali/public-api/actors/actor-enumerations.h +++ b/dali/public-api/actors/actor-enumerations.h @@ -172,7 +172,8 @@ namespace ClippingMode 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 all children to within the visible pixels of this actors renderer (the actor will also be visible itself). @SINCE_1_2_5 + CLIP_TO_BOUNDING_BOX ///< This Actor will clip all children within a screen-aligned rectangle encompassing its boundaries (the actor will NOT be visible itself). @SINCE_1_2.59 }; } diff --git a/dali/public-api/math/matrix.cpp b/dali/public-api/math/matrix.cpp index 67ade79..83f9809 100644 --- a/dali/public-api/math/matrix.cpp +++ b/dali/public-api/math/matrix.cpp @@ -25,6 +25,7 @@ // INTERNAL INCLUDES #include +#include #include #include #include @@ -464,6 +465,24 @@ Vector4 Matrix::operator*(const Vector4& rhs) const return temp; } +Vector2 Matrix::operator*( const Vector2& rhs ) const +{ + MATH_INCREASE_BY( PerformanceMonitor::FLOAT_POINT_MULTIPLY, 4 ); + + #ifndef __ARM_NEON__ + // The following optimizations are applied: + // pMatrix[8 -> 11] are optimized out. + // pMatrix[12 -> 15] are always multiplied by 1. + // z & w results are unneeded and so not calculated. + return Vector2( rhs.x * mMatrix[0] + rhs.y * mMatrix[4] + mMatrix[12], rhs.x * mMatrix[1] + rhs.y * mMatrix[5] + mMatrix[13] ); + #else + // Call the operator*( Vector4 ) version for NEON. + // It is suggested the NEON version is removed for this simple case, as the CPU optimization surpasses the NEON overhead. + const Vector4 result( operator*( Vector4( rhs.x, rhs.y, 0.0f, 1.0f ) ) ); + return Vector2( result.x, result.y ); + #endif +} + bool Matrix::operator==(const Matrix& rhs) const { return ( diff --git a/dali/public-api/math/matrix.h b/dali/public-api/math/matrix.h index 7c8a1d8..b947596 100644 --- a/dali/public-api/math/matrix.h +++ b/dali/public-api/math/matrix.h @@ -33,6 +33,7 @@ namespace Dali * @{ */ +class Vector2; class Quaternion; /** @@ -316,15 +317,25 @@ public: static void Multiply( Matrix& result, const Matrix& lhs, const Quaternion& rhs ); /** - * @brief The multiplication operator. + * @brief The multiplication by Vector4 operator. * * @SINCE_1_0.0 - * @param[in] rhs The Matrix to multiply this by - * @return A matrix containing the result + * @param[in] rhs The Vector4 coordinates to multiply this matrix by + * @return A Vector4 containing the result coordinates */ Vector4 operator*(const Vector4& rhs) const; /** + * @brief The multiplication by Vector2 operator. + * Note: This performs an optimized 2D transformation. + * + * @SINCE_1_2.59 + * @param[in] rhs The Vector2 coordinates to multiply this matrix by + * @return A Vector2 containing the result coordinates + */ + Vector2 operator*( const Vector2& rhs ) const; + + /** * @brief The equality operator. * * Utilizes appropriate machine epsilon values. -- 2.7.4