From 2777a06676cdadec8c123b3b91f05bf80ae02e4b Mon Sep 17 00:00:00 2001 From: "adam.b" Date: Tue, 21 Jul 2020 11:34:15 +0100 Subject: [PATCH] Rendering optimisation with AddOn To enable, the external addon is required. Change-Id: I932f0fedd157ac00cd6b564ebf7fdb08bd22cc5a --- automated-tests/src/CMakeLists.txt | 1 + .../src/dali-toolkit-internal/CMakeLists.txt | 41 +++- .../dali-toolkit-internal/addons/rendering.cmake | 2 + .../src/dali-toolkit-internal/addons/sample.cmake | 2 + .../addons/test-rendering-addon.cpp | 180 +++++++++++++++++ .../addons/test-sample-addon.cpp | 129 ++++++++++++ .../dali-toolkit-test-utils/test-addon-manager.cpp | 222 +++++++++++++++++++++ .../dali-toolkit-test-utils/test-addon-manager.h | 89 +++++++++ .../src/dali-toolkit-internal/utc-Dali-AddOns.cpp | 96 +++++++++ .../internal/visuals/image/image-visual.cpp | 42 +++- dali-toolkit/internal/visuals/npatch-loader.cpp | 17 +- dali-toolkit/internal/visuals/npatch-loader.h | 24 ++- .../internal/visuals/npatch/npatch-visual.cpp | 28 ++- dali-toolkit/internal/visuals/rendering-addon.h | 93 +++++++++ .../internal/visuals/texture-manager-impl.cpp | 17 ++ .../internal/visuals/texture-manager-impl.h | 18 ++ 16 files changed, 984 insertions(+), 17 deletions(-) create mode 100644 automated-tests/src/dali-toolkit-internal/addons/rendering.cmake create mode 100644 automated-tests/src/dali-toolkit-internal/addons/sample.cmake create mode 100644 automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp create mode 100644 automated-tests/src/dali-toolkit-internal/addons/test-sample-addon.cpp create mode 100644 automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.cpp create mode 100644 automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.h create mode 100644 automated-tests/src/dali-toolkit-internal/utc-Dali-AddOns.cpp create mode 100644 dali-toolkit/internal/visuals/rendering-addon.h mode change 100755 => 100644 dali-toolkit/internal/visuals/texture-manager-impl.h diff --git a/automated-tests/src/CMakeLists.txt b/automated-tests/src/CMakeLists.txt index e4f0a2b..4d59c62 100644 --- a/automated-tests/src/CMakeLists.txt +++ b/automated-tests/src/CMakeLists.txt @@ -1,3 +1,4 @@ +# prebuild test addons IF( DEFINED MODULE ) MESSAGE(STATUS "Building: ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE}") ADD_SUBDIRECTORY(${MODULE}) diff --git a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt index 4513301..6a94a6f 100755 --- a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CAPI_LIB "dali-toolkit-internal") # List of test case sources (Only these get parsed for test cases) SET(TC_SOURCES + utc-Dali-AddOns.cpp utc-Dali-BidirectionalSupport.cpp utc-Dali-ColorConversion.cpp utc-Dali-Control-internal.cpp @@ -73,19 +74,20 @@ LIST(APPEND TC_SOURCES ../dali-toolkit/dali-toolkit-test-utils/test-native-image.cpp dali-toolkit-test-utils/toolkit-text-utils.cpp dali-toolkit-test-utils/dummy-visual.cpp + dali-toolkit-test-utils/test-addon-manager.cpp ) - PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED dali2-core dali2-adaptor dali2-toolkit ) -ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED ) +ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED -fPIC ) ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} ) ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" ) +ADD_DEFINITIONS(-DDALI_ADDONS_PATH=\"${CMAKE_CURRENT_BINARY_DIR}\") FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS}) SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}") @@ -101,9 +103,42 @@ INCLUDE_DIRECTORIES( ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp ${TC_SOURCES}) TARGET_LINK_LIBRARIES(${EXEC_NAME} ${${CAPI_LIB}_LIBRARIES} - -lpthread --coverage + -lpthread --coverage -ldl ) INSTALL(PROGRAMS ${EXEC_NAME} DESTINATION ${BIN_DIR}/${EXEC_NAME} ) + +# build addons +MESSAGE( STATUS "BINDIR: ${CMAKE_CURRENT_BINARY_DIR}") + +FILE( GLOB FILES ${CMAKE_CURRENT_SOURCE_DIR}/addons/*.cmake ) + +FOREACH( INFILE IN ITEMS ${FILES} ) + + INCLUDE( ${INFILE} ) + MESSAGE( STATUS "Building ${INFILE}" ) + + ADD_LIBRARY( ${ADDON_NAME} SHARED ${ADDON_SOURCES} ) + + TARGET_INCLUDE_DIRECTORIES( ${ADDON_NAME} PUBLIC + ../../../ + ${${CAPI_LIB}_INCLUDE_DIRS} + ../dali-toolkit/dali-toolkit-test-utils + dali-toolkit-test-utils) + + TARGET_LINK_LIBRARIES(${ADDON_NAME} + ${CMAKE_CXX_LINK_FLAGS} + ${${CAPI_LIB}_LIBRARIES} + -lpthread -ldl --coverage + ) + + INSTALL( TARGETS ${ADDON_NAME} DESTINATION ${BIN_DIR} ) + + SET( ADDON_LIST "lib${ADDON_NAME}.so +${ADDON_LIST}") +ENDFOREACH() + +# store AddOns list +FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/addons.txt "${ADDON_LIST}" ) \ No newline at end of file diff --git a/automated-tests/src/dali-toolkit-internal/addons/rendering.cmake b/automated-tests/src/dali-toolkit-internal/addons/rendering.cmake new file mode 100644 index 0000000..f9e66e1 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/addons/rendering.cmake @@ -0,0 +1,2 @@ +SET(ADDON_NAME rendering ) +SET(ADDON_SOURCES ./addons/test-rendering-addon.cpp) diff --git a/automated-tests/src/dali-toolkit-internal/addons/sample.cmake b/automated-tests/src/dali-toolkit-internal/addons/sample.cmake new file mode 100644 index 0000000..1c5d781 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/addons/sample.cmake @@ -0,0 +1,2 @@ +SET(ADDON_NAME sample ) +SET(ADDON_SOURCES ./addons/test-sample-addon.cpp) \ No newline at end of file diff --git a/automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp b/automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp new file mode 100644 index 0000000..6756093 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// EXTERNAL HEADERS +#include +#include +#include +#include + +// INTERNAL HEADERS +// Needed to access the private class members +#define private public +#include +#include +#undef private + + +using Dali::Toolkit::Internal::TextureManager; + +namespace Dali +{ +namespace AddOns +{ + +struct DummyTiler +{ +}; + +void* CreateInstance( TextureManager* textureManager ) +{ + fprintf(stderr, "AddOn::CreateInstance( %p )\n", textureManager); + return new DummyTiler; +} + +namespace GeometryTiler +{ +std::vector gCallStack; + +static Geometry GetGeometryInternal(TextureManager::TextureId textureId, uint32_t& o0, uint32_t& o1 ) +{ + gCallStack.emplace_back( "GetGeometry" ); + o0 = 10; + o1 = 5; + fprintf(stderr, "AddOn::GetGeometryInternal()\n"); + return Dali::Geometry::New(); +} + +static Geometry CreateGeometryInternal(TextureManager::TextureId textureId, const Devel::PixelBuffer& pixelBuffer ) +{ + gCallStack.emplace_back( "CreateGeometry" ); + fprintf(stderr, "AddOn::CreateGeometryInternal()\n"); + return Dali::Geometry::New(); +} + +static Geometry CreateGeometryMapInternal(const void* opacityMap, + const Uint16Pair& gridSize, + uint32_t *outElements) +{ + gCallStack.emplace_back( "CreateGeometryGrid" ); + outElements[0] = 2; + outElements[1] = 3; + return Dali::Geometry::New(); +} + +static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchLoader::Data* data ) +{ + gCallStack.emplace_back( "BuildNPatch" ); + fprintf(stderr, "AddOn::NPatchBuild()\n"); + static char dummyData; + return &dummyData; +} + +static void NPatchDestroyInternal(void* object ) +{ + gCallStack.emplace_back( "DestroyNPatch" ); + fprintf(stderr, "AddOn::NPatchDestroy()\n"); +} + +static void SubmitInternal(Renderer& renderer, const void* object ) +{ + gCallStack.emplace_back( "SubmitRenderTask" ); + fprintf(stderr, "AddOn::SubmitInternal()\n"); +} + +static std::vector GetCallStack( bool clear ) +{ + auto retval = gCallStack; + if(clear) + { + gCallStack.clear(); + } + return retval; +} + + +} +} +} + +/** + * OverdrawingAddOn implementation + */ +class TestRenderingAddOn : public Dali::AddOns::AddOnBase +{ +public: + + void GetAddOnInfo( Dali::AddOnInfo& info ) override + { + info.type = Dali::AddOnType::GENERIC; + info.name = "oo-rendering"; + info.version = Dali::DALI_ADDON_VERSION( 1, 0, 0 ); + info.next = nullptr; + } + + /** + * Dispatch table for global functions + * @return + */ + Dali::AddOns::DispatchTable* GetGlobalDispatchTable() override + { + static Dali::AddOns::DispatchTable dispatchTable{}; + if( dispatchTable.Empty() ) + { + dispatchTable["Initialize"] = Dali::AddOns::CreateInstance; + dispatchTable["CreateGeometry"] = Dali::AddOns::GeometryTiler::CreateGeometryInternal; + dispatchTable["GetGeometry"] = Dali::AddOns::GeometryTiler::GetGeometryInternal; + dispatchTable["CreateGeometryGrid"] = Dali::AddOns::GeometryTiler::CreateGeometryMapInternal; + dispatchTable["BuildNPatch"] = Dali::AddOns::GeometryTiler::NPatchBuildInternal; + dispatchTable["DestroyNPatch"] = Dali::AddOns::GeometryTiler::NPatchDestroyInternal; + dispatchTable["SubmitRenderTask"] = Dali::AddOns::GeometryTiler::SubmitInternal; + dispatchTable["GetCallStack"] = Dali::AddOns::GeometryTiler::GetCallStack; + } + return &dispatchTable; + } + + /** + * Lifecycle + */ + + void OnStart() override + { + } + + void OnStop() override + { + } + + void OnPause() override + { + } + + void OnResume() override + { + } + + /** + * Dispatch table for instance functions + * @return + */ + Dali::AddOns::DispatchTable* GetInstanceDispatchTable() override + { + return nullptr; + } +}; + +REGISTER_ADDON_CLASS( TestRenderingAddOn ); diff --git a/automated-tests/src/dali-toolkit-internal/addons/test-sample-addon.cpp b/automated-tests/src/dali-toolkit-internal/addons/test-sample-addon.cpp new file mode 100644 index 0000000..d6f019d --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/addons/test-sample-addon.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +namespace +{ +bool gIsPaused = false; +const auto ANSWER_TO_EVERYTHING = 42; +} + +int StringLen( const char* str ) +{ + return strlen( str ); +} + +int DoSum( int a, int b ) +{ + return a+b; +} + +bool GetLifecycleStatus() +{ + return gIsPaused; +} + +struct AddOnDataInstance +{ + int GetValue() + { + return ANSWER_TO_EVERYTHING; + } + + static int GetValueWithInstance( AddOnDataInstance* instance ) + { + return instance->GetValue(); + } +}; + +AddOnDataInstance* CreateInstance() +{ + return new AddOnDataInstance(); +} + +class TestDummyAddOn : public Dali::AddOns::AddOnBase +{ +public: + + void GetAddOnInfo( Dali::AddOnInfo& info ) override + { + info.type = Dali::AddOnType::GENERIC; + info.name = "SampleAddOn"; + info.version = Dali::DALI_ADDON_VERSION( 1, 0, 0 ); + info.next = nullptr; + tet_printf( "SampleAddOn: GetAddOnInfo() : name = %s\n", info.name.c_str()); + } + + /** + * Dispatch table for global functions + * @return + */ + Dali::AddOns::DispatchTable* GetGlobalDispatchTable() override + { + static Dali::AddOns::DispatchTable dispatchTable{}; + if( dispatchTable.Empty() ) + { + dispatchTable["DoSum"] = DoSum; + dispatchTable["StringLen"] = StringLen; + dispatchTable["GetLifecycleStatus"] = GetLifecycleStatus; + dispatchTable["CreateInstance"] = CreateInstance; + + } + return &dispatchTable; + } + + /** + * Lifecycle + */ + void OnStart() override + { + gIsPaused = false; + } + + void OnStop() override + { + gIsPaused = true; + } + + void OnPause() override + { + gIsPaused = true; + } + + void OnResume() override + { + gIsPaused = false; + } + + /** + * Dispatch table for instance functions + */ + Dali::AddOns::DispatchTable* GetInstanceDispatchTable() override + { + static Dali::AddOns::DispatchTable dispatchTable{}; + if( dispatchTable.Empty() ) + { + dispatchTable["InstanceCall"] = AddOnDataInstance::GetValueWithInstance; + } + return &dispatchTable; + } +}; + +REGISTER_ADDON_CLASS( TestDummyAddOn ); diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.cpp b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.cpp new file mode 100644 index 0000000..6bd9682 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "test-addon-manager.h" +#include +#include +#include + +#ifndef DALI_ADDONS_PATH +#define DALI_ADDONS_PATH "" +#endif + +namespace Dali +{ +namespace Test +{ + +std::vector AddOnManager::EnumerateAddOns() +{ + std::string listFileName(DALI_ADDONS_PATH); + listFileName += "/addons.txt"; + + // Read list of available test addons + tet_printf("Enumerating addons, file: %s\n", listFileName.c_str()); + std::vector addons{}; + auto* fin = fopen( listFileName.c_str(), "r" ); + char* lineBuf = new char[256]; + memset( lineBuf, 0, 256 ); + size_t n = 256; + while( getline( &lineBuf, &n, fin ) > 0 ) + { + char* c = lineBuf; + while( *c ) + { + if( *c == '\n' || *c == '\r' ) + { + *c = 0; + break; + } + ++c; + } + tet_printf("Adding %s\n", lineBuf); + addons.emplace_back( lineBuf ); + memset( lineBuf, 0, 256 ); + } + fclose(fin); + delete [] lineBuf; + std::vector retval{}; + // Open addons + for( auto& name : addons ) + { + std::string path(DALI_ADDONS_PATH); + path += "/"; + path += name; + + mAddOnCache.emplace_back(); + mAddOnCache.back().handle = dlopen( path.c_str(), RTLD_DEEPBIND|RTLD_LAZY ); + if( !mAddOnCache.back().handle ) + { + mAddOnCache.back().valid = false; + tet_printf( "Can't open addon lib: %s, error: '%s'\n", path.c_str(), dlerror()); + continue; + } + // Here addon must self register + if( !mAddOnCache.back().valid ) + { + puts("Addon invalid!"); + } + else + { + tet_printf( "Valid AddOn: %s\n", mAddOnCache.back().name.c_str() ); + retval.emplace_back( mAddOnCache.back().name ); + } + } + + return retval; + + /** + * Check for self-registering addons + */ +} + +void AddOnManager::RegisterAddOnDispatchTable( const AddOnDispatchTable* dispatchTable ) +{ + // Register the dispatch table + auto& entry = mAddOnCache.back(); + entry.name = dispatchTable->name; + tet_printf( "Registering AddOn: %s\n", entry.name.c_str()); + entry.GetGlobalProc = dispatchTable->GetGlobalProc; + entry.GetInstanceProc = dispatchTable->GetInstanceProc; + entry.GetAddOnInfo = dispatchTable->GetAddOnInfo; + entry.OnStart = dispatchTable->OnStart; + entry.OnStop = dispatchTable->OnStop; + entry.OnPause = dispatchTable->OnPause; + entry.OnResume = dispatchTable->OnResume; + entry.valid = true; +} + +bool AddOnManager::GetAddOnInfo(const std::string& name, AddOnInfo& info ) +{ + auto retval = false; + std::find_if( mAddOnCache.begin(), mAddOnCache.end(), + [&retval, name, &info]( AddOnCacheEntry& entry ) + { + if(entry.name == name) + { + entry.GetAddOnInfo( info ); + retval = true; + return true; + } + return false; + }); + return retval; +} + +std::vector AddOnManager::LoadAddOns( const std::vector& addonNames ) +{ + if(mAddOnCache.empty()) + { + EnumerateAddOns(); + } + + std::vector retval{}; + for( auto& name : addonNames) + { + size_t index = 0; + auto iter = std::find_if( mAddOnCache.begin(), mAddOnCache.end(), + [&retval, name, &index]( AddOnCacheEntry& entry ) + { + index++; + if(entry.name == name) + { + return true; + } + return false; + }); + if( iter != mAddOnCache.end() ) + { + retval.emplace_back( *reinterpret_cast( &index ) ); + } + else + { + retval.emplace_back( nullptr ); + } + } + + return retval; +} + +void* AddOnManager::GetGlobalProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) +{ + auto index = *reinterpret_cast( &addOnLibrary ); + return mAddOnCache[index-1].GetGlobalProc( procName ); +} + +void* AddOnManager::GetInstanceProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) +{ + auto index = *reinterpret_cast( &addOnLibrary ); + return mAddOnCache[index-1].GetInstanceProc( procName ); +} + +void AddOnManager::Start() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnStart) + { + entry.OnStart(); + } + } +} + +void AddOnManager::Resume() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnResume) + { + entry.OnResume(); + } + } +} + +void AddOnManager::Stop() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnStop) + { + entry.OnStop(); + } + } +} + +void AddOnManager::Pause() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnPause) + { + entry.OnPause(); + } + } +} + +} +} + diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.h b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.h new file mode 100644 index 0000000..e924876 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/test-addon-manager.h @@ -0,0 +1,89 @@ +#ifndef TEST_ADDON_MANAGER_H +#define TEST_ADDON_MANAGER_H + +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +namespace Dali +{ +namespace Test +{ +class AddOnManager : public Dali::Integration::AddOnManager +{ +public: + + /** + * @brief Constructor, initialised by the Adaptor + */ + AddOnManager() = default; + + /** + * @brief Destructor + */ + virtual ~AddOnManager() = default; + + std::vector EnumerateAddOns() override; + + bool GetAddOnInfo(const std::string& name, AddOnInfo& info ) override; + + std::vector LoadAddOns( const std::vector& addonNames ) override; + + void* GetGlobalProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) override; + + void* GetInstanceProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) override; + + void RegisterAddOnDispatchTable( const AddOnDispatchTable* dispatchTable ) override; + + void Start() override; + + void Resume() override; + + void Stop() override; + + void Pause() override; + + struct AddOnCacheEntry + { + std::string name{}; + AddOnInfo info{}; + + // library handle + void* handle {nullptr}; + + // main function pointers + void(*GetAddOnInfo)(AddOnInfo& ) = nullptr; ///< Returns AddOnInfo structure + void*(*GetInstanceProc)( const char* ) = nullptr; ///< Returns pointer of instance function (member funtion) + void*(*GetGlobalProc)( const char* ) = nullptr; ///< Returns pointer of global function (non-member function) + + void(*OnStart)() = nullptr; + void(*OnResume)() = nullptr; + void(*OnPause)() = nullptr; + void(*OnStop)() = nullptr; + + bool valid = false; + }; + + std::vector mAddOnCache; +}; +} // Namespace Test +} // namespace Dali + +#endif // TEST_ADDON_MANAGER_H diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-AddOns.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-AddOns.cpp new file mode 100644 index 0000000..195656d --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-AddOns.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include +#include "dali-toolkit-test-utils/test-addon-manager.h" +#include + +using namespace Dali::Toolkit::Internal; + +namespace +{ + +const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/application-icon-20.png"; +const char* TEST_IMAGE_FILE_NAME_9 = TEST_RESOURCE_DIR "/heartsframe.9.png"; + +int CountFunctionCalls( const std::vector& callstack, const std::string& function ) +{ + int counter = 0; + std::find_if( callstack.begin(), callstack.end(), [&counter, &function]( const std::string& item ) + { + if( item == function ) + { + counter++; + } + return false; + }); + + return counter; +} + +} + +int UtcRenderingAddOnTestP(void) +{ + Dali::Integration::AddOnManager* addOnManager = new Dali::Test::AddOnManager(); + + bool valid = addOnManager->Get() != nullptr; + DALI_TEST_EQUALS( valid, true, TEST_LOCATION ); + auto addon = addOnManager->GetAddOn( "oo-rendering" ); + auto GetCallStack = addOnManager->GetGlobalProc(bool)>( addon, "GetCallStack" ); + + ToolkitTestApplication application; + tet_infoline( "UtcRenderingAddOnTestP" ); + + // Load regular image view + auto imageView = Dali::Toolkit::ImageView::New( TEST_IMAGE_FILE_NAME ); + imageView.SetProperty( Actor::Property::SIZE, Vector2( 400.f, 60.f ) ); + imageView.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT ); + imageView.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); + + // Load npatch image view + auto imageView2 = Dali::Toolkit::ImageView::New( TEST_IMAGE_FILE_NAME_9 ); + imageView2.SetProperty( Actor::Property::SIZE, Vector2( 400.f, 60.f ) ); + imageView2.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT ); + imageView2.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); + + application.GetScene().Add( imageView ); + application.GetScene().Add( imageView2 ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( ::Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + auto callstack = GetCallStack(true); + + DALI_TEST_EQUALS( CountFunctionCalls( callstack, "GetGeometry" ), 2, TEST_LOCATION); + DALI_TEST_EQUALS( CountFunctionCalls( callstack, "CreateGeometry" ), 1, TEST_LOCATION); + DALI_TEST_EQUALS( CountFunctionCalls( callstack, "CreateGeometryGrid" ), 1, TEST_LOCATION); + DALI_TEST_EQUALS( CountFunctionCalls( callstack, "BuildNPatch" ), 1, TEST_LOCATION); + + delete addOnManager; + + END_TEST; +} diff --git a/dali-toolkit/internal/visuals/image/image-visual.cpp b/dali-toolkit/internal/visuals/image/image-visual.cpp index 23a4793..059e9c8 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.cpp +++ b/dali-toolkit/internal/visuals/image/image-visual.cpp @@ -40,6 +40,7 @@ #include #include #include +#include namespace Dali { @@ -515,7 +516,15 @@ void ImageVisual::CreateRenderer( TextureSet& textureSet ) if( !mImpl->mCustomShader ) { - geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) ); + TextureManager& textureManager = mFactoryCache.GetTextureManager(); + + uint32_t opaqueElementsCount {0u}; + uint32_t transparentElementsCount {0u}; + geometry = textureManager.GetRenderGeometry(mTextureId, opaqueElementsCount, transparentElementsCount); + if(!opaqueElementsCount && !transparentElementsCount) + { + geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) ); + } shader = mImageVisualShaderFactory.GetShader( mFactoryCache, mImpl->mFlags & Impl::IS_ATLASING_APPLIED, @@ -850,6 +859,37 @@ void ImageVisual::UploadComplete( bool loadingSuccess, int32_t textureId, Textur { resourceStatus = Toolkit::Visual::ResourceStatus::FAILED; } + + // use geometry if needed + if( loadingSuccess ) + { + uint32_t opaqueElements{0u}; + uint32_t transparentElements{0u}; + auto geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(mTextureId, opaqueElements, transparentElements); + if (mImpl->mRenderer && geometry) + { + mImpl->mRenderer.SetGeometry(geometry); + Dali::DevelRenderer::DrawCommand drawCommand{}; + drawCommand.drawType = DevelRenderer::DrawType::INDEXED; + + if (opaqueElements) + { + drawCommand.firstIndex = 0; + drawCommand.elementCount = opaqueElements; + drawCommand.queue = 0; + DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand); + } + + if (transparentElements) + { + drawCommand.firstIndex = opaqueElements; + drawCommand.elementCount = transparentElements; + drawCommand.queue = 1; + DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand); + } + } + } + // Signal to observers ( control ) that resources are ready. Must be all resources. ResourceReady( resourceStatus ); mLoading = false; diff --git a/dali-toolkit/internal/visuals/npatch-loader.cpp b/dali-toolkit/internal/visuals/npatch-loader.cpp index d4a1f0e..2e216f3 100644 --- a/dali-toolkit/internal/visuals/npatch-loader.cpp +++ b/dali-toolkit/internal/visuals/npatch-loader.cpp @@ -18,7 +18,10 @@ // CLASS HEADER #include -// EXTERNAL INCLUDES +// INTERNAL HEADERS +#include + +// EXTERNAL HEADERS #include #include @@ -52,6 +55,9 @@ void SetLoadedNPatchData( NPatchLoader::Data* data, Devel::PixelBuffer& pixelBuf data->croppedWidth = pixelBuffer.GetWidth(); data->croppedHeight = pixelBuffer.GetHeight(); + // Create opacity map + data->renderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, data ) : nullptr; + PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() ); @@ -65,6 +71,15 @@ void SetLoadedNPatchData( NPatchLoader::Data* data, Devel::PixelBuffer& pixelBuf } // namespace NPatchBuffer +NPatchLoader::Data::~Data() +{ + // If there is an opacity map, it has to be destroyed using addon call + if( renderingMap ) + { + RenderingAddOn::Get().DestroyNPatch( renderingMap ); + } +} + NPatchLoader::NPatchLoader() { } diff --git a/dali-toolkit/internal/visuals/npatch-loader.h b/dali-toolkit/internal/visuals/npatch-loader.h index 58bd73f..2c0e852 100644 --- a/dali-toolkit/internal/visuals/npatch-loader.h +++ b/dali-toolkit/internal/visuals/npatch-loader.h @@ -63,18 +63,22 @@ public: croppedWidth( 0 ), croppedHeight( 0 ), border( 0, 0, 0, 0 ), - loadCompleted( false ) + loadCompleted( false ), + renderingMap{ nullptr } {} - std::string url; ///< Url of the N-Patch - TextureSet textureSet; ///< Texture containing the cropped image - NPatchUtility::StretchRanges stretchPixelsX; ///< X stretch pixels - NPatchUtility::StretchRanges stretchPixelsY; ///< Y stretch pixels - std::size_t hash; ///< Hash code for the Url - uint32_t croppedWidth; ///< Width of the cropped middle part of N-patch - uint32_t croppedHeight; ///< Height of the cropped middle part of N-patch - Rect< int > border; ///< The size of the border - bool loadCompleted; ///< True if the data loading is completed + ~Data(); + + std::string url; ///< Url of the N-Patch + TextureSet textureSet; ///< Texture containing the cropped image + NPatchUtility::StretchRanges stretchPixelsX; ///< X stretch pixels + NPatchUtility::StretchRanges stretchPixelsY; ///< Y stretch pixels + std::size_t hash; ///< Hash code for the Url + uint32_t croppedWidth; ///< Width of the cropped middle part of N-patch + uint32_t croppedHeight; ///< Height of the cropped middle part of N-patch + Rect< int > border; ///< The size of the border + bool loadCompleted; ///< True if the data loading is completed + void* renderingMap; ///< NPatch rendering data }; public: diff --git a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp index 147f88c..ea72d4f 100644 --- a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp +++ b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp @@ -20,6 +20,7 @@ // EXTERNAL INCLUDES #include +#include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include namespace Dali { @@ -403,6 +405,11 @@ void NPatchVisual::DoSetOnStage( Actor& actor ) mPlacementActor = actor; if( data->loadCompleted ) { + if( RenderingAddOn::Get().IsValid() ) + { + RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->renderingMap ); + } + ApplyTextureAndUniforms(); actor.AddRenderer( mImpl->mRenderer ); mPlacementActor.Reset(); @@ -486,13 +493,30 @@ Geometry NPatchVisual::CreateGeometry() } else { - geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY ); + if( data->renderingMap ) + { + uint32_t elementCount[2]; + geometry = RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, Uint16Pair(3, 3), elementCount ); + } + else + { + geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY ); + } } } else if( data->stretchPixelsX.Size() > 0 || data->stretchPixelsY.Size() > 0) { Uint16Pair gridSize( 2 * data->stretchPixelsX.Size() + 1, 2 * data->stretchPixelsY.Size() + 1 ); - geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize ); + if( !data->renderingMap ) + { + geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize ); + } + else + { + uint32_t elementCount[2]; + geometry = !mBorderOnly ? + RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, gridSize, elementCount ) : CreateBorderGeometry(gridSize ); + } } } else diff --git a/dali-toolkit/internal/visuals/rendering-addon.h b/dali-toolkit/internal/visuals/rendering-addon.h new file mode 100644 index 0000000..5d39f97 --- /dev/null +++ b/dali-toolkit/internal/visuals/rendering-addon.h @@ -0,0 +1,93 @@ +#ifndef DALI_CMAKE_RENDERING_ADDON_H +#define DALI_CMAKE_RENDERING_ADDON_H + +/* +* Copyright (c) 2020 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. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +/** + * Interface of Overdrawing AddOn + */ +class RenderingAddOn : public Dali::AddOn::AddOnBinder +{ + using TextureManager = Dali::Toolkit::Internal::TextureManager; +public: + RenderingAddOn() : Dali::AddOn::AddOnBinder( "oo-rendering", 0u ) + {} + + // Bind AddOn functions + ADDON_BIND_FUNCTION( + GetGeometry, + Dali::Geometry(TextureManager::TextureId, uint32_t&, uint32_t& ) ); + + ADDON_BIND_FUNCTION( + CreateGeometry, + Dali::Geometry( TextureManager::TextureId, const Dali::Devel::PixelBuffer& pixelBuffer ) ); + + ADDON_BIND_FUNCTION( + Initialize, + void*() ); + + ADDON_BIND_FUNCTION( + CreateGeometryGrid, + Dali::Geometry( const void*, const Uint16Pair&, uint32_t*) ); + + ADDON_BIND_FUNCTION( + SubmitRenderTask, + void( Renderer&, const void* ) ); + + ADDON_BIND_FUNCTION( + BuildNPatch, + void*( const Devel::PixelBuffer&, void*) ); + + ADDON_BIND_FUNCTION( + DestroyNPatch, + void( void* ) ); + + /** + * Single instance of the addon + * @return + */ + static RenderingAddOn& Get() + { + static RenderingAddOn* addon = nullptr; + if( !addon ) + { + addon = new RenderingAddOn(); + if(addon->IsValid()) + { + addon->Initialize(); + } + } + return *addon; + } +}; + +} // Internal +} // Toolkit +} // Dali + +#endif //DALI_CMAKE_RENDERING_ADDON_H diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.cpp b/dali-toolkit/internal/visuals/texture-manager-impl.cpp index e23290b..64bbb5b 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.cpp +++ b/dali-toolkit/internal/visuals/texture-manager-impl.cpp @@ -27,11 +27,13 @@ #include #include #include +#include // INTERNAL HEADERS #include #include #include +#include namespace { @@ -133,6 +135,8 @@ TextureManager::TextureManager() mCurrentTextureId( 0 ), mQueueLoadFlag(false) { + // Initialize the AddOn + RenderingAddOn::Get(); } TextureManager::~TextureManager() @@ -1048,6 +1052,12 @@ void TextureManager::UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo // Check if this pixelBuffer is premultiplied textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); + auto& renderingAddOn = RenderingAddOn::Get(); + if( renderingAddOn.IsValid() ) + { + renderingAddOn.CreateGeometry( textureInfo.textureId, pixelBuffer ); + } + Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() ); @@ -1373,6 +1383,13 @@ void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl) mBrokenImageUrl = brokenImageUrl; } +Geometry TextureManager::GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements ) +{ + return RenderingAddOn::Get().IsValid() ? + RenderingAddOn::Get().GetGeometry( textureId, frontElements, backElements) : + Geometry(); +} + } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.h b/dali-toolkit/internal/visuals/texture-manager-impl.h old mode 100755 new mode 100644 index c3cd015..2ad0225 --- a/dali-toolkit/internal/visuals/texture-manager-impl.h +++ b/dali-toolkit/internal/visuals/texture-manager-impl.h @@ -27,6 +27,7 @@ #include #include #include +#include // INTERNAL INCLUDES #include @@ -426,6 +427,15 @@ public: */ void SetBrokenImageUrl(const std::string& brokenImageUrl); + /** + * @brief Returns the geometry associated with texture. + * @param[in] textureId Id of the texture + * @param[out] frontElements number of front elements + * @param[out] backElements number of back elements + * @return Returns valid geometry object + */ + Geometry GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements ); + private: /** @@ -690,6 +700,14 @@ private: void UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo ); /** + * Creates tiled geometry of for the texture which separates fully-opaque + * tiles from ones which use transparency. + * @param pixelBuffer + * @param textureInfo + */ + bool CreateTiledGeometry( const Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo ); + + /** * Mark the texture as complete, and inform observers * @param[in] textureInfo The struct associated with this Texture */ -- 2.7.4