FastTrackUpload task implement 58/295658/35
authorEunki, Hong <eunkiki.hong@samsung.com>
Wed, 12 Jul 2023 07:01:56 +0000 (16:01 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 4 Aug 2023 03:47:38 +0000 (12:47 +0900)
Let we make another loaderTask for upload texture at work thread.
It will use TextureUploadManager.

Change-Id: Ibc3e19aad3e1a20f3a553b4cf394b315de66980f
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
14 files changed:
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-graphics-controller.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-texture-upload-manager.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-texture-upload-manager.h [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/file.list
dali-toolkit/internal/image-loader/fast-track-loading-task.cpp [new file with mode: 0644]
dali-toolkit/internal/image-loader/fast-track-loading-task.h [new file with mode: 0644]
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h

index 1ddb118..f11bfbf 100755 (executable)
@@ -112,6 +112,7 @@ SET(TEST_HARNESS_SOURCES
   dali-toolkit-test-utils/toolkit-physical-keyboard.cpp
   dali-toolkit-test-utils/toolkit-style-monitor.cpp
   dali-toolkit-test-utils/toolkit-test-application.cpp
+  dali-toolkit-test-utils/toolkit-texture-upload-manager.cpp
   dali-toolkit-test-utils/toolkit-timer.cpp
   dali-toolkit-test-utils/toolkit-trigger-event-factory.cpp
   dali-toolkit-test-utils/toolkit-tts-player.cpp
index 4bac7a6..527b9f4 100644 (file)
@@ -1409,4 +1409,71 @@ bool TestGraphicsController::GetProgramParameter(Graphics::Program& program, uin
   return graphicsProgram->GetParameter(parameterId, outData);
 }
 
+Graphics::Texture* TestGraphicsController::CreateTextureByResourceId(uint32_t resourceId, const Graphics::TextureCreateInfo& createInfo)
+{
+  Graphics::Texture*                     ret = nullptr;
+  Graphics::UniquePtr<Graphics::Texture> texture;
+  TraceCallStack::NamedParams            namedParams;
+  namedParams["resourceId"] << resourceId;
+
+  auto iter = mTextureUploadBindMapper.find(resourceId);
+  DALI_ASSERT_ALWAYS(iter == mTextureUploadBindMapper.end());
+
+  // Create new graphics texture.
+  texture = CreateTexture(createInfo, std::move(texture));
+  ret     = texture.get();
+
+  mTextureUploadBindMapper.insert(std::make_pair(resourceId, std::move(texture)));
+
+  mCallStack.PushCall("CreateTextureByResourceId", "", namedParams);
+  return ret;
+}
+
+void TestGraphicsController::DiscardTextureFromResourceId(uint32_t resourceId)
+{
+  TraceCallStack::NamedParams namedParams;
+  namedParams["resourceId"] << resourceId;
+
+  mTextureUploadBindMapper.erase(resourceId);
+
+  mCallStack.PushCall("DiscardTextureFromResourceId", "", namedParams);
+}
+
+Graphics::Texture* TestGraphicsController::GetTextureFromResourceId(uint32_t resourceId)
+{
+  TraceCallStack::NamedParams namedParams;
+  namedParams["resourceId"] << resourceId;
+
+  Graphics::Texture* ret = nullptr;
+
+  auto iter = mTextureUploadBindMapper.find(resourceId);
+  if(iter != mTextureUploadBindMapper.end())
+  {
+    ret = iter->second.get();
+  }
+
+  mCallStack.PushCall("GetTextureFromResourceId", "", namedParams);
+
+  return ret;
+}
+
+Graphics::UniquePtr<Graphics::Texture> TestGraphicsController::ReleaseTextureFromResourceId(uint32_t resourceId)
+{
+  TraceCallStack::NamedParams namedParams;
+  namedParams["resourceId"] << resourceId;
+
+  Graphics::UniquePtr<Graphics::Texture> texture;
+
+  auto iter = mTextureUploadBindMapper.find(resourceId);
+  if(iter != mTextureUploadBindMapper.end())
+  {
+    texture = std::move(iter->second);
+    mTextureUploadBindMapper.erase(iter);
+  }
+
+  mCallStack.PushCall("ReleaseTextureFromResourceId", "", namedParams);
+
+  return texture;
+}
+
 } // namespace Dali
index ade48d7..9540c3e 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <dali/graphics-api/graphics-controller.h>
+#include <unordered_map>
 #include "test-gl-abstraction.h"
 #include "test-gl-context-helper-abstraction.h"
 #include "test-graphics-command-buffer.h"
@@ -375,6 +376,53 @@ public:
    */
   bool PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const override;
 
+  /**
+   * @brief Retrieves program parameters
+   *
+   * This function can be used to retrieve data from internal implementation
+   *
+   * @param[in] program Valid program object
+   * @param[in] parameterId Integer parameter id
+   * @param[out] outData Pointer to output memory
+   * @return True on success
+   */
+  bool GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData) override;
+
+public: // ResourceId relative API.
+  /**
+   * @brief Create Graphics::Texture as resourceId.
+   * The ownership of Graphics::Texture will be hold on this controller.
+   * @note If some Graphics::Texture already created before, assert.
+   * @post DiscardTextureFromResourceId() or ReleaseTextureFromResourceId() should be called when we don't use resourceId texture anymore.
+   *
+   * @param[in] resourceId The unique id of resouces.
+   * @return Pointer of Graphics::Texture, or nullptr if we fail to create.
+   */
+  Graphics::Texture* CreateTextureByResourceId(uint32_t resourceId, const Graphics::TextureCreateInfo& createInfo) override;
+
+  /**
+   * @brief Discard Graphics::Texture as resourceId.
+   *
+   * @param[in] resourceId The unique id of resouces.
+   */
+  void DiscardTextureFromResourceId(uint32_t resourceId) override;
+
+  /**
+   * @brief Get the Graphics::Texture as resourceId.
+   *
+   * @param[in] resourceId The unique id of resouces.
+   * @return Pointer of Graphics::Texture, or nullptr if there is no valid objects.
+   */
+  Graphics::Texture* GetTextureFromResourceId(uint32_t resourceId) override;
+
+  /**
+   * @brief Get the ownership of Graphics::Texture as resourceId.
+   *
+   * @param[in] resourceId The unique id of resouces.
+   * @return Pointer of Graphics::Texture.
+   */
+  Graphics::UniquePtr<Graphics::Texture> ReleaseTextureFromResourceId(uint32_t resourceId) override;
+
 public: // Test Functions
   void SetVertexFormats(Property::Array& vfs)
   {
@@ -396,18 +444,6 @@ public: // Test Functions
     mSubmitStack.clear();
   }
 
-  /**
-   * @brief Retrieves program parameters
-   *
-   * This function can be used to retrieve data from internal implementation
-   *
-   * @param[in] program Valid program object
-   * @param[in] parameterId Integer parameter id
-   * @param[out] outData Pointer to output memory
-   * @return True on success
-   */
-  bool GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData) override;
-
   void ProcessCommandBuffer(TestGraphicsCommandBuffer& commandBuffer);
 
   void BindPipeline(TestGraphicsPipeline* pipeline);
@@ -436,6 +472,8 @@ public:
 
   std::vector<TestGraphicsBuffer*> mAllocatedBuffers;
 
+  std::unordered_map<uint32_t, Graphics::UniquePtr<Graphics::Texture>> mTextureUploadBindMapper;
+
   struct PipelineCache
   {
   };
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-texture-upload-manager.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-texture-upload-manager.cpp
new file mode 100644 (file)
index 0000000..f1c336c
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+// CLASS HEADER
+#include <toolkit-texture-upload-manager.h>
+
+// EXTERNAL INCLUDE
+#include <dali/devel-api/adaptor-framework/texture-upload-manager.h>
+#include <dali/graphics-api/graphics-controller.h>
+#include <dali/integration-api/pixel-data-integ.h>
+#include <dali/integration-api/texture-integ.h>
+
+// INTERNAL INCLUDE
+#include <test-graphics-controller.h>
+#include <toolkit-application.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+class TextureUploadManager : public Dali::BaseObject
+{
+public:
+  using ResourceId = Dali::Devel::TextureUploadManager::ResourceId;
+
+public:
+  static Dali::Devel::TextureUploadManager Get();
+  TextureUploadManager();
+  ~TextureUploadManager() = default;
+
+  Dali::Texture GenerateTexture2D();
+
+public: // Update thread called method
+  bool ResourceUpload();
+
+  void InitalizeGraphicsController(Dali::Graphics::Controller& graphicsController);
+
+private:                                                             // Update thread called method
+  using UploadRequestItem  = std::pair<ResourceId, Dali::PixelData>; ///< TODO : PixelData? PixelBuffer?
+  using RequestUploadQueue = std::vector<UploadRequestItem>;
+
+  bool ProcessUploadQueue(RequestUploadQueue&& queue);
+
+public: // Worker thread called method
+  /**
+   * @copydoc Dali::Devel::TextureUploadManager::RequestUpload()
+   */
+  void RequestUpload(ResourceId id, Dali::PixelData pixelData);
+
+private:
+  Dali::Graphics::Controller* mGraphicsController;
+  RequestUploadQueue          mRequestUploadQueue{};
+
+public:
+  ResourceId gUniqueResourceId{0u};
+};
+
+inline Internal::Adaptor::TextureUploadManager& GetImplementation(Dali::Devel::TextureUploadManager& obj)
+{
+  DALI_ASSERT_ALWAYS(obj && "TextureUploadManager is empty");
+
+  Dali::BaseObject& handle = obj.GetBaseObject();
+
+  return static_cast<Internal::Adaptor::TextureUploadManager&>(handle);
+}
+
+inline const Internal::Adaptor::TextureUploadManager& GetImplementation(const Dali::Devel::TextureUploadManager& obj)
+{
+  DALI_ASSERT_ALWAYS(obj && "TextureUploadManager is empty");
+
+  const Dali::BaseObject& handle = obj.GetBaseObject();
+
+  return static_cast<const Internal::Adaptor::TextureUploadManager&>(handle);
+}
+
+/********************************************************************************/
+/*********************************  INTERNAL CLASS  *****************************/
+/********************************************************************************/
+
+namespace
+{
+Devel::TextureUploadManager gTextureUploadManager;
+} // namespace
+
+Devel::TextureUploadManager TextureUploadManager::Get()
+{
+  if(!gTextureUploadManager)
+  {
+    gTextureUploadManager = Devel::TextureUploadManager(new TextureUploadManager());
+  }
+  return gTextureUploadManager;
+}
+
+TextureUploadManager::TextureUploadManager()
+: mGraphicsController{nullptr}
+{
+}
+
+Dali::Texture TextureUploadManager::GenerateTexture2D()
+{
+  ResourceId resourceId = ++gUniqueResourceId;
+
+  Dali::Texture ret = Dali::Integration::NewTextureWithResourceId(Dali::TextureType::TEXTURE_2D, resourceId);
+
+  return ret;
+}
+
+// Called by update thread
+
+bool TextureUploadManager::ResourceUpload()
+{
+  DALI_ASSERT_DEBUG(mGraphicsController && "GraphicsController is not prepared!");
+
+  // Upload.
+  bool uploaded = ProcessUploadQueue(std::move(mRequestUploadQueue));
+  mRequestUploadQueue.clear();
+
+  return uploaded;
+}
+
+void TextureUploadManager::InitalizeGraphicsController(Dali::Graphics::Controller& graphicsController)
+{
+  mGraphicsController = &graphicsController;
+}
+
+bool TextureUploadManager::ProcessUploadQueue(RequestUploadQueue&& queue)
+{
+  bool uploaded = false;
+
+  if(!queue.empty())
+  {
+    for(auto& requests : queue)
+    {
+      auto& resourceId = requests.first;
+      auto& pixelData  = requests.second;
+
+      Graphics::Texture* graphicsTexture = nullptr;
+
+      // TODO : Could we detect TEXTURE_2D or TEXTURE_CUBE case in future?
+      {
+        // We always need to create new one
+        auto createInfo = Graphics::TextureCreateInfo();
+        createInfo
+          .SetTextureType(Dali::Graphics::ConvertTextureType(Dali::TextureType::TEXTURE_2D))
+          .SetUsageFlags(static_cast<Graphics::TextureUsageFlags>(Graphics::TextureUsageFlagBits::SAMPLE))
+          .SetFormat(Dali::Graphics::ConvertPixelFormat(pixelData.GetPixelFormat()))
+          .SetSize({pixelData.GetWidth(), pixelData.GetHeight()})
+          .SetLayout(Graphics::TextureLayout::LINEAR)
+          .SetData(nullptr)
+          .SetDataSize(0u)
+          .SetNativeImage(nullptr)
+          .SetMipMapFlag(Graphics::TextureMipMapFlag::DISABLED);
+
+        graphicsTexture = mGraphicsController->CreateTextureByResourceId(resourceId, createInfo);
+      }
+
+      if(graphicsTexture)
+      {
+        Graphics::TextureUpdateInfo info{};
+
+        info.dstTexture   = graphicsTexture;
+        info.dstOffset2D  = {0u, 0u};
+        info.layer        = 0u;
+        info.level        = 0u;
+        info.srcReference = 0;
+        info.srcExtent2D  = {pixelData.GetWidth(), pixelData.GetHeight()};
+        info.srcOffset    = 0;
+        info.srcSize      = Dali::Integration::GetPixelDataBuffer(pixelData).bufferSize;
+        info.srcStride    = pixelData.GetStride();
+        info.srcFormat    = Dali::Graphics::ConvertPixelFormat(pixelData.GetPixelFormat());
+
+        Graphics::TextureUpdateSourceInfo updateSourceInfo{};
+        updateSourceInfo.sourceType                = Graphics::TextureUpdateSourceInfo::Type::PIXEL_DATA;
+        updateSourceInfo.pixelDataSource.pixelData = pixelData;
+
+        mGraphicsController->UpdateTextures({info}, {updateSourceInfo});
+
+        uploaded = true;
+      }
+      else
+      {
+        // Invalidate resouce id! ignore.
+      }
+    }
+
+    if(uploaded)
+    {
+      // Flush here
+      Graphics::SubmitInfo submitInfo;
+      submitInfo.cmdBuffer.clear(); // Only flush
+      submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
+      mGraphicsController->SubmitCommandBuffers(submitInfo);
+    }
+  }
+
+  return uploaded;
+}
+
+// Called by worker thread
+
+void TextureUploadManager::RequestUpload(Dali::Devel::TextureUploadManager::ResourceId resourceId, Dali::PixelData pixelData)
+{
+  mRequestUploadQueue.push_back(std::move(UploadRequestItem(resourceId, pixelData)));
+}
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+/********************************************************************************/
+/*********************************  PUBLIC CLASS  *******************************/
+/********************************************************************************/
+
+namespace Devel
+{
+// Called by main thread
+
+TextureUploadManager::TextureUploadManager() = default;
+
+TextureUploadManager::~TextureUploadManager() = default;
+
+TextureUploadManager TextureUploadManager::Get()
+{
+  return Internal::Adaptor::TextureUploadManager::Get();
+}
+
+Dali::Texture TextureUploadManager::GenerateTexture2D()
+{
+  return Internal::Adaptor::GetImplementation(*this).GenerateTexture2D();
+}
+
+TextureUploadManager::TextureUploadManager(Internal::Adaptor::TextureUploadManager* impl)
+: BaseHandle(impl)
+{
+}
+
+// Called by update thread
+
+bool TextureUploadManager::ResourceUpload()
+{
+  return Internal::Adaptor::GetImplementation(*this).ResourceUpload();
+}
+
+// Called by worker thread
+
+void TextureUploadManager::RequestUpload(ResourceId resourceId, PixelData pixelData)
+{
+  Internal::Adaptor::GetImplementation(*this).RequestUpload(resourceId, pixelData);
+}
+
+} // namespace Devel
+
+} // namespace Dali
+
+namespace Test
+{
+namespace TextureUploadManager
+{
+void InitalizeGraphicsController(Dali::Graphics::Controller& graphicsController)
+{
+  auto textureUploadManager = Dali::Devel::TextureUploadManager::Get();
+  Internal::Adaptor::GetImplementation(textureUploadManager).InitalizeGraphicsController(graphicsController);
+}
+} // namespace TextureUploadManager
+} // namespace Test
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-texture-upload-manager.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-texture-upload-manager.h
new file mode 100644 (file)
index 0000000..18585fd
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef DALI_TOOLKIT_TOOLKIT_TEXTURE_UPLOAD_MANAGER_H
+#define DALI_TOOLKIT_TOOLKIT_TEXTURE_UPLOAD_MANAGER_H
+
+/*
+ * Copyright (c) 2023 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 INCLUDES
+#include <dali/devel-api/adaptor-framework/texture-upload-manager.h>
+
+namespace Dali
+{
+namespace Graphics DALI_INTERNAL
+{
+class Controller;
+} // namespace DALI_INTERNAL
+} // namespace Dali
+
+namespace Test
+{
+namespace TextureUploadManager
+{
+void InitalizeGraphicsController(Dali::Graphics::Controller& graphicsController);
+}
+} // namespace Test
+
+#endif // DALI_TOOLKIT_TOOLKIT_TEXTURE_UPLOAD_MANAGER_H
index 9583e09..4b5e546 100644 (file)
 #include <iostream>
 #include <vector>
 
+#include <unistd.h>
+#include <thread>
+
 #include <dali-toolkit-test-suite-utils.h>
 
 #include <toolkit-environment-variable.h>
 #include <toolkit-event-thread-callback.h>
+#include <toolkit-texture-upload-manager.h>
 #include <toolkit-timer.h>
 
 #include <dali-toolkit/dali-toolkit.h>
 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
 #include <dali-toolkit/devel-api/visual-factory/transition-data.h>
 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
+#include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali-toolkit/public-api/image-loader/image.h>
 
+#include <dali/devel-api/adaptor-framework/texture-upload-manager.h>
+
 #include "dummy-control.h"
 #include "test-encoded-image-buffer.h"
 #include "test-native-image-source.h"
@@ -3550,3 +3557,315 @@ int UtcDaliImageVisualLoadImagePlanes03(void)
 
   END_TEST;
 }
+
+int UtcDaliImageVisualLoadFastTrackImage01(void)
+{
+  tet_infoline("Test worker thread uploading with Local URL");
+  ToolkitTestApplication application;
+
+  Test::TextureUploadManager::InitalizeGraphicsController(application.GetGraphicsController());
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  // Pair of filename - expect GenTextures count.
+  std::vector<std::pair<std::string, int>> testCases = {
+    {TEST_IMAGE_FILE_NAME, 1},
+    {TEST_REMOTE_IMAGE_FILE_NAME, 1},
+    {TEST_INVALID_FILE_NAME, 0},
+  };
+
+  for(auto&& tc : testCases)
+  {
+    auto& filename    = tc.first;
+    auto& expectValue = tc.second;
+
+    tet_printf("Test %s\n", filename.c_str());
+
+    Property::Map propertyMap;
+    propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+    propertyMap.Insert(ImageVisual::Property::URL, filename.c_str());
+    propertyMap.Insert(DevelImageVisual::Property::FAST_TRACK_UPLOADING, true);
+
+    Visual::Base visual = factory.CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    DummyControl      actor     = DummyControl::New();
+    DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+    actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+    TestGlAbstraction& gl           = application.GetGlAbstraction();
+    TraceCallStack&    textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    application.GetScene().Add(actor);
+
+    application.SendNotification();
+    application.Render();
+
+    // EventThread without callback
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 30, false), true, TEST_LOCATION);
+
+    DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+    {
+      // TODO : There is no way to flush TextureUploadManager in test-application's Render() now.
+      // How can we make it? Should it be integration-api?
+      auto textureUploadManager = Dali::Devel::TextureUploadManager::Get();
+      DALI_TEST_EQUALS(textureUploadManager.ResourceUpload(), expectValue > 0, TEST_LOCATION);
+    }
+    // Render only without SendNotification(). And check whether glTexImage2D called or not.
+    application.Render();
+
+    DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), expectValue, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    textureTrace.Reset();
+  }
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualLoadFastTrackImage02(void)
+{
+  tet_infoline("Test worker thread uploading with Local URL");
+  ToolkitTestApplication application;
+
+  Test::TextureUploadManager::InitalizeGraphicsController(application.GetGraphicsController());
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  {
+    auto filename    = std::string(TEST_IMAGE_FILE_NAME);
+    auto expectValue = 1;
+
+    tet_printf("Test %s\n", filename.c_str());
+
+    Property::Map propertyMap;
+    propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+    propertyMap.Insert(ImageVisual::Property::URL, filename.c_str());
+    propertyMap.Insert("fastTrackUploading", true);
+
+    Visual::Base visual = factory.CreateVisual(propertyMap);
+    DALI_TEST_CHECK(visual);
+
+    DummyControl      actor     = DummyControl::New();
+    DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+    actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+    TestGlAbstraction& gl           = application.GetGlAbstraction();
+    TraceCallStack&    textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    application.GetScene().Add(actor);
+
+    application.SendNotification();
+    application.Render();
+
+    // EventThread without callback
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 30, false), true, TEST_LOCATION);
+
+    DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+    {
+      // TODO : There is no way to flush TextureUploadManager in test-application's Render() now.
+      // How can we make it? Should it be integration-api?
+      auto textureUploadManager = Dali::Devel::TextureUploadManager::Get();
+      DALI_TEST_EQUALS(textureUploadManager.ResourceUpload(), expectValue > 0, TEST_LOCATION);
+    }
+    // Render only without SendNotification(). And check whether glTexImage2D called or not.
+    application.Render();
+
+    DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), expectValue, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    textureTrace.Reset();
+  }
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualLoadFastTrackImageResourceReady(void)
+{
+  tet_infoline("Test worker thread uploading with Local URL");
+  ToolkitTestApplication application;
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME);
+  propertyMap.Insert(DevelImageVisual::Property::FAST_TRACK_UPLOADING, true);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  // EventThread with callback
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check resource ready comes after
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualLoadFastTrackImageReload(void)
+{
+  tet_infoline("Test worker thread uploading with Local URL");
+  ToolkitTestApplication application;
+
+  Test::TextureUploadManager::InitalizeGraphicsController(application.GetGraphicsController());
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME);
+  propertyMap.Insert(DevelImageVisual::Property::FAST_TRACK_UPLOADING, true);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  // EventThread without callback
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 30, false), true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+  {
+    // TODO : There is no way to flush TextureUploadManager in test-application's Render() now.
+    // How can we make it? Should it be integration-api?
+    auto textureUploadManager = Dali::Devel::TextureUploadManager::Get();
+    DALI_TEST_EQUALS(textureUploadManager.ResourceUpload(), true, TEST_LOCATION);
+  }
+  // Render only without SendNotification(). And check whether glTexImage2D called or not.
+  application.Render();
+
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 1, TEST_LOCATION);
+
+  // Reload
+  Property::Map attributes;
+  DevelControl::DoAction(actor, Control::CONTROL_PROPERTY_END_INDEX + 1, DevelImageVisual::Action::RELOAD, attributes);
+
+  // EventThread with callback
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check resource ready comes after
+  application.SendNotification();
+  application.Render();
+
+  // Check whether renderer count is 1 or not.
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualLoadFastTrackImagePlanes01(void)
+{
+#if 0 //< Do not open this UTC yet.
+  EnvironmentVariable::SetTestEnvironmentVariable(LOAD_IMAGE_YUV_PLANES_ENV, "1");
+  EnvironmentVariable::SetTestEnvironmentVariable(ENABLE_DECODE_JPEG_TO_YUV_420_ENV, "1");
+
+  ToolkitTestApplication application;
+
+  Test::TextureUploadManager::InitalizeGraphicsController(application.GetGraphicsController());
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_YUV420_IMAGE_FILE_NAME);
+  propertyMap.Insert(DevelImageVisual::Property::FAST_TRACK_UPLOADING, true);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+  actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  // EventThread without callback
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 30, false), true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION);
+
+  {
+    // TODO : There is no way to flush TextureUploadManager in test-application's Render() now.
+    // How can we make it? Should it be integration-api?
+    auto textureUploadManager = Dali::Devel::TextureUploadManager::Get();
+    textureUploadManager.ResourceUpload();
+    application.Render();
+  }
+  // Render only without SendNotification(). And check whether glTexImage2D called or not.
+  application.Render();
+
+  DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 3, TEST_LOCATION);
+
+  // Event thread don't know the result yet.
+  DALI_TEST_EQUALS(actor.IsResourceReady(), false, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  // Check resource ready comes after
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+#else
+  DALI_TEST_CHECK(true);
+#endif
+  END_TEST;
+}
\ No newline at end of file
index 3c0e7d6..35dc49d 100644 (file)
@@ -158,6 +158,23 @@ enum Type
   MASKING_TYPE = ORIENTATION_CORRECTION + 12,
 
   /**
+   * @brief If true, uploads texture before ResourceReady signal is emitted. Otherwise uploads after texture load is completed.
+   * @details Name "fastTrackUploading", type Property::BOOLEAN
+   * If true, the upload happens without event-thread dependency, but the following need to be considered:
+   *  - Texture size is not valid until upload is fully complete.
+   *  - Texture cannot be cached (a new image is uploaded every time).
+   *  - Seamless visual change is not supported.
+   *  - The following, if set are also not supported and will be ignored:
+   *    - Alpha masking
+   *    - Synchronous loading
+   *    - Reload action
+   *    - Atlas loading
+   *    - Custom shader
+   * @note Used by the ImageVisual. The default is false.
+   */
+  FAST_TRACK_UPLOADING = ORIENTATION_CORRECTION + 13,
+
+  /**
    * @brief Whether to enable broken image in image visual.
    * Some of visual don't need to show broken image(ex. placeholder)
    * Disable broken image for these visuals.
index 3ac6c81..9933e1a 100644 (file)
@@ -141,6 +141,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/filters/spread-filter.cpp
    ${toolkit_src_dir}/image-loader/async-image-loader-impl.cpp
    ${toolkit_src_dir}/image-loader/atlas-packer.cpp
+   ${toolkit_src_dir}/image-loader/fast-track-loading-task.cpp
    ${toolkit_src_dir}/image-loader/image-atlas-impl.cpp
    ${toolkit_src_dir}/image-loader/loading-task.cpp
    ${toolkit_src_dir}/image-loader/image-url-impl.cpp
diff --git a/dali-toolkit/internal/image-loader/fast-track-loading-task.cpp b/dali-toolkit/internal/image-loader/fast-track-loading-task.cpp
new file mode 100644 (file)
index 0000000..607abbc
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/image-loader/fast-track-loading-task.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/adaptor-framework/texture-upload-manager.h>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/texture-integ.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+FastTrackLoadingTask::FastTrackLoadingTask(const VisualUrl& url, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad, CallbackBase* callback)
+: AsyncTask(MakeCallback(this, &FastTrackLoadingTask::OnComplete), url.GetProtocolType() == VisualUrl::ProtocolType::REMOTE ? AsyncTask::PriorityType::LOW : AsyncTask::PriorityType::HIGH),
+  mUrl(url),
+  mTexture(),
+  mDimensions(dimensions),
+  mFittingMode(fittingMode),
+  mSamplingMode(samplingMode),
+  mPreMultiplyOnLoad(preMultiplyOnLoad),
+  mCallback(),
+  mTextureUploadManager(Dali::Devel::TextureUploadManager::Get()),
+  mImageWidth(0u),
+  mImageHeight(0u),
+  mImageFormat(Pixel::INVALID),
+  mPixelData(),
+  mResourceId(0u),
+  mOrientationCorrection(orientationCorrection),
+  mLoadSuccess(false),
+  mPremultiplied(false)
+{
+  mCallback = std::unique_ptr<CallbackBase>(callback);
+  PrepareTexture();
+}
+
+FastTrackLoadingTask::~FastTrackLoadingTask()
+{
+}
+
+void FastTrackLoadingTask::PrepareTexture()
+{
+  mTexture    = mTextureUploadManager.GenerateTexture2D();
+  mResourceId = Integration::GetTextureResourceId(mTexture);
+}
+
+void FastTrackLoadingTask::OnComplete(AsyncTaskPtr task)
+{
+  if(mLoadSuccess)
+  {
+    Dali::Integration::SetTextureSize(mTexture, Dali::ImageDimensions(mImageWidth, mImageHeight));
+    Dali::Integration::SetTexturePixelFormat(mTexture, mImageFormat);
+  }
+  if(mCallback)
+  {
+    CallbackBase::Execute(*mCallback, FastTrackLoadingTaskPtr(reinterpret_cast<FastTrackLoadingTask*>(task.Get())));
+  }
+}
+
+// Called by worker thread
+
+void FastTrackLoadingTask::Process()
+{
+  Load();
+  UploadToTexture();
+}
+
+bool FastTrackLoadingTask::IsReady()
+{
+  return true;
+}
+
+void FastTrackLoadingTask::Load()
+{
+  Devel::PixelBuffer              pixelBuffer;
+  std::vector<Devel::PixelBuffer> pixelBuffers;
+
+  if(mUrl.IsValid() && mUrl.IsLocalResource())
+  {
+    // TODO : We need to consider YUV case in future.
+    //Dali::LoadImagePlanesFromFile(mUrl.GetUrl(), pixelBuffers, mDimensions, mFittingMode, mSamplingMode, mOrientationCorrection);
+
+    pixelBuffer = Dali::LoadImageFromFile(mUrl.GetUrl(), mDimensions, mFittingMode, mSamplingMode, mOrientationCorrection);
+  }
+  else if(mUrl.IsValid())
+  {
+    pixelBuffer = Dali::DownloadImageSynchronously(mUrl.GetUrl(), mDimensions, mFittingMode, mSamplingMode, mOrientationCorrection);
+  }
+
+  if(pixelBuffer)
+  {
+    pixelBuffers.push_back(pixelBuffer);
+  }
+
+  if(pixelBuffers.empty())
+  {
+    DALI_LOG_ERROR("FastTrackLoadingTask::Load: Loading is failed: ResourceId : %d, url : [%s]\n", mResourceId, mUrl.GetUrl().c_str());
+  }
+  else
+  {
+    if(pixelBuffers.size() == 1u)
+    {
+      mLoadSuccess = true;
+      MultiplyAlpha(pixelBuffers[0]);
+      mPixelData = Dali::Devel::PixelBuffer::Convert(pixelBuffers[0]);
+    }
+    else
+    {
+      DALI_LOG_ERROR("FastTrackLoadingTask::Load: ??? Undefined case. PixelBuffers.size() : %zu : ResourceId : %d, url : [%s]\n", pixelBuffers.size(), mResourceId, mUrl.GetUrl().c_str());
+    }
+  }
+}
+
+void FastTrackLoadingTask::MultiplyAlpha(Dali::Devel::PixelBuffer pixelBuffer)
+{
+  if(mPreMultiplyOnLoad == DevelAsyncImageLoader::PreMultiplyOnLoad::ON && Pixel::HasAlpha(pixelBuffer.GetPixelFormat()))
+  {
+    pixelBuffer.MultiplyColorByAlpha();
+    mPremultiplied = pixelBuffer.IsAlphaPreMultiplied();
+  }
+}
+
+void FastTrackLoadingTask::UploadToTexture()
+{
+  if(mLoadSuccess)
+  {
+    mImageWidth  = mPixelData.GetWidth();
+    mImageHeight = mPixelData.GetHeight();
+    mImageFormat = mPixelData.GetPixelFormat();
+
+    mTextureUploadManager.RequestUpload(mResourceId, mPixelData);
+  }
+
+  mPixelData.Reset();
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/image-loader/fast-track-loading-task.h b/dali-toolkit/internal/image-loader/fast-track-loading-task.h
new file mode 100644 (file)
index 0000000..0458352
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef DALI_TOOLKIT_FAST_TRACK_IMAGE_LOADING_TASK_H
+#define DALI_TOOLKIT_FAST_TRACK_IMAGE_LOADING_TASK_H
+
+/*
+ * Copyright (c) 2023 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 INCLUDES
+#include <dali-toolkit/devel-api/image-loader/async-image-loader-devel.h>
+#include <dali-toolkit/internal/texture-manager/texture-manager-type.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/devel-api/adaptor-framework/texture-upload-manager.h>
+#include <dali/integration-api/adaptor-framework/log-factory-interface.h>
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
+#include <dali/public-api/rendering/texture.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class FastTrackLoadingTask;
+using FastTrackLoadingTaskPtr = IntrusivePtr<FastTrackLoadingTask>;
+
+/**
+ * @brief The task of loading image and uploading texture on fast track.
+ * It is available that upload the loaded image data on texture at image loader threads.
+ */
+class FastTrackLoadingTask : public AsyncTask
+{
+public:
+  /**
+   * @brief Constructor.
+   */
+  FastTrackLoadingTask(const VisualUrl&                         url,
+                       ImageDimensions                          dimensions,
+                       FittingMode::Type                        fittingMode,
+                       SamplingMode::Type                       samplingMode,
+                       bool                                     orientationCorrection,
+                       DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad,
+                       CallbackBase*                            callback);
+
+  /**
+   * @brief Destructor.
+   */
+  ~FastTrackLoadingTask() override;
+
+  /**
+   * @brief Process the task accodring to the type
+   */
+  void Process() override;
+
+  /**
+   * @brief Whether the task is ready to process.
+   * @return True if the task is ready to process.
+   */
+  bool IsReady() override;
+
+private:
+  // Undefined
+  FastTrackLoadingTask(const FastTrackLoadingTask& queue) = delete;
+  FastTrackLoadingTask& operator=(const FastTrackLoadingTask& queue) = delete;
+
+  /**
+   * @brief Create textures for this task.
+   * @note This should be called in construct timing.
+   */
+  void PrepareTexture();
+
+  /**
+   * @brief Load the image.
+   */
+  void Load();
+
+  /**
+   * @brief Multiply alpha if required.
+   *
+   * @param[in,out] pixelBuffer target pixel buffer that need to be multiplied alpha.
+   */
+  void MultiplyAlpha(Dali::Devel::PixelBuffer pixelBuffer);
+
+  /**
+   * @brief Upload loaded pixelBuffer into texture
+   */
+  void UploadToTexture();
+
+private:
+  /**
+   * @brief Complete callback.
+   *
+   * @param[in] task The pointer of task who call this callback.
+   */
+  void OnComplete(AsyncTaskPtr task);
+
+public:
+  VisualUrl     mUrl;     ///< url of the image to load.
+  Dali::Texture mTexture; ///< texture for regular image.
+
+private:
+  ImageDimensions                          mDimensions;   ///< dimensions to load
+  FittingMode::Type                        mFittingMode;  ///< fitting options
+  SamplingMode::Type                       mSamplingMode; ///< sampling options
+  DevelAsyncImageLoader::PreMultiplyOnLoad mPreMultiplyOnLoad;
+  std::unique_ptr<CallbackBase>            mCallback;
+
+  // Texture Upload relative API
+  Dali::Devel::TextureUploadManager mTextureUploadManager;
+
+  // Note : mPixelData is invalid after upload requested. We should keep image size informations.
+  uint32_t      mImageWidth;
+  uint32_t      mImageHeight;
+  Pixel::Format mImageFormat;
+
+  Dali::PixelData mPixelData;
+
+  uint32_t mResourceId;
+
+  bool mOrientationCorrection : 1; ///< If orientation correction is needed
+
+public:
+  bool mLoadSuccess : 1;   ///< Whether image load successed or not.
+  bool mPremultiplied : 1; ///< True if the image's color was multiplied by it's alpha
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_FAST_TRACK_IMAGE_LOADING_TASK_H
index 18cf2b4..5b2aff8 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali/devel-api/scripting/scripting.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/actors/layer.h>
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
 #include <dali/public-api/rendering/decorated-visual-renderer.h>
 #include <cstring> // for strlen()
 
@@ -200,6 +201,8 @@ ImageVisual::~ImageVisual()
     {
       RemoveTexture();
     }
+
+    ResetFastTrackLoadingTask();
   }
 }
 
@@ -283,6 +286,10 @@ void ImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, keyValue.second);
       }
+      else if(keyValue.first == FAST_TRACK_UPLOADING_NAME)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING, keyValue.second);
+      }
     }
   }
   // Load image immediately if LOAD_POLICY requires it
@@ -477,6 +484,16 @@ void ImageVisual::DoSetProperty(Property::Index index, const Property::Value& va
       }
       break;
     }
+
+    case Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING:
+    {
+      bool fastTrackUploading = false;
+      if(value.Get(fastTrackUploading))
+      {
+        mUseFastTrackUploading = fastTrackUploading;
+      }
+      break;
+    }
   }
 }
 
@@ -634,9 +651,76 @@ void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& te
                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
   bool synchronousLoading = IsSynchronousLoadingRequired();
-  bool loadingStatus;
+  bool loadingStatus      = false;
+
+  // Remove previous loading task.
+  ResetFastTrackLoadingTask();
+
+  // Rare case. If someone call LoadTexture during fast track loading task running, (Ex : Action::RELOAD)
+  // we should remove previously added renderer now.
+  if(mRendererAdded)
+  {
+    Actor actor = mPlacementActor.GetHandle();
+    if(actor)
+    {
+      actor.RemoveRenderer(mImpl->mRenderer);
+      mRendererAdded = false;
+    }
+  }
+
+  /**
+   * @brief Check whether FastTrackUploading is avaliable or not.
+   * @return True if we can use fast track uploading feature. False otherwise.
+   */
+  auto IsFastTrackUploadingAvailable = [&]() {
+    if(mUseFastTrackUploading &&
+       mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::ATTACHED &&
+       mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED &&
+       forceReload == TextureManager::ReloadPolicy::CACHED &&
+       (mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE) &&
+       !synchronousLoading &&
+       !atlasing &&
+       !mImpl->mCustomShader &&
+       !(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()))
+    {
+      return true;
+    }
+    else if(mUseFastTrackUploading)
+    {
+      DALI_LOG_DEBUG_INFO("FastTrack : Fail to load fast track. mUrl : [%s]%s%s%s%s%s%s%s%s\n",
+                          mImageUrl.GetUrl().c_str(),
+                          (mLoadPolicy != Toolkit::ImageVisual::LoadPolicy::ATTACHED) ? "/ mLoadPolicy != ATTACHED" : "",
+                          (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::DETACHED) ? "/ mReleasePolicy != DETACHED" : "",
+                          (forceReload != TextureManager::ReloadPolicy::CACHED) ? "/ forceReload != CACHED" : "",
+                          (!(mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE)) ? "/ url is not image" : "",
+                          (synchronousLoading) ? "/ synchronousLoading" : "",
+                          (atlasing) ? "/ atlasing" : "",
+                          (mImpl->mCustomShader) ? "/ use customs shader" : "",
+                          (mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()) ? "/ use masking url" : "");
+    }
+    return false;
+  };
+
+  if(IsFastTrackUploadingAvailable())
+  {
+    // Enable PremultipliedAlpha first.
+    EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
+
+    // Set new TextureSet with fast track loading task
+    mFastTrackLoadingTask = new FastTrackLoadingTask(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mOrientationCorrection, preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, MakeCallback(this, &ImageVisual::FastLoadComplete));
+
+    TextureSet textureSet = TextureSet::New();
+    textureSet.SetTexture(0u, mFastTrackLoadingTask->mTexture);
+    mImpl->mRenderer.SetTextures(textureSet);
 
-  textures = textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
+    Dali::AsyncTaskManager::Get().AddTask(mFastTrackLoadingTask);
+
+    mLoadState = TextureManager::LoadState::LOADING;
+  }
+  else
+  {
+    textures = textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
+  }
 
   if(textures)
   {
@@ -770,6 +854,7 @@ void ImageVisual::DoSetOnScene(Actor& actor)
   if(mLoadState == TextureManager::LoadState::LOAD_FINISHED)
   {
     actor.AddRenderer(mImpl->mRenderer);
+    mRendererAdded = true;
     mPlacementActor.Reset();
 
     // Image loaded and ready to display
@@ -780,6 +865,14 @@ void ImageVisual::DoSetOnScene(Actor& actor)
     ShowBrokenImage();
     ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
   }
+  else
+  {
+    if(mFastTrackLoadingTask)
+    {
+      actor.AddRenderer(mImpl->mRenderer);
+      mRendererAdded = true;
+    }
+  }
 }
 
 void ImageVisual::DoSetOffScene(Actor& actor)
@@ -788,6 +881,8 @@ void ImageVisual::DoSetOffScene(Actor& actor)
 
   // Image release is dependent on the ReleasePolicy, renderer is removed.
   actor.RemoveRenderer(mImpl->mRenderer);
+  mRendererAdded = false;
+
   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
   {
     ResetRenderer();
@@ -830,6 +925,8 @@ void ImageVisual::DoCreatePropertyMap(Property::Map& map) const
   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
   map.Insert(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, mOrientationCorrection);
+
+  map.Insert(Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING, mUseFastTrackUploading);
 }
 
 void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
@@ -885,6 +982,7 @@ void ImageVisual::UploadCompleted()
   {
     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
     actor.AddRenderer(mImpl->mRenderer);
+    mRendererAdded = true;
     // reset the weak handle so that the renderer only get added to actor once
     mPlacementActor.Reset();
   }
@@ -894,6 +992,39 @@ void ImageVisual::UploadCompleted()
   mLoadState = TextureManager::LoadState::LOAD_FINISHED;
 }
 
+// From FastTrackLoadingTask
+void ImageVisual::FastLoadComplete(FastTrackLoadingTaskPtr task)
+{
+  Toolkit::Visual::ResourceStatus resourceStatus;
+
+  DALI_ASSERT_ALWAYS(mFastTrackLoadingTask == task && "Task was not canceled successfully!");
+  DALI_ASSERT_ALWAYS(mRendererAdded && "Some FastTrack logic missed!");
+
+  Actor actor = mPlacementActor.GetHandle();
+
+  if(mFastTrackLoadingTask && mFastTrackLoadingTask->mLoadSuccess)
+  {
+    resourceStatus = Toolkit::Visual::ResourceStatus::READY;
+    mLoadState     = TextureManager::LoadState::LOAD_FINISHED;
+
+    // Change premultiplied alpha flag after change renderer.
+    EnablePreMultipliedAlpha(mFastTrackLoadingTask->mPremultiplied);
+  }
+  else
+  {
+    resourceStatus    = Toolkit::Visual::ResourceStatus::FAILED;
+    mLoadState        = TextureManager::LoadState::LOAD_FAILED;
+
+    // Change renderer as broken.
+    ShowBrokenImage();
+  }
+
+  mFastTrackLoadingTask.Reset();
+
+  // Signal to observers ( control ) that resources are ready. Must be all resources.
+  ResourceReady(resourceStatus);
+}
+
 // From Texture Manager
 void ImageVisual::LoadComplete(bool loadingSuccess, TextureInformation textureInformation)
 {
@@ -938,6 +1069,7 @@ void ImageVisual::LoadComplete(bool loadingSuccess, TextureInformation textureIn
       if(actor)
       {
         actor.AddRenderer(mImpl->mRenderer);
+        mRendererAdded = true;
         // reset the weak handle so that the renderer only get added to actor once
         mPlacementActor.Reset();
       }
@@ -1005,6 +1137,8 @@ void ImageVisual::RemoveTexture()
   }
   else
   {
+    ResetFastTrackLoadingTask();
+
     Vector4         atlasRect(0.f, 0.f, 1.f, 1.f);
     Property::Index index = mImpl->mRenderer.GetPropertyIndex(ATLAS_RECT_UNIFORM_NAME);
     if(index != Property::INVALID_INDEX)
@@ -1200,21 +1334,46 @@ void ImageVisual::ShowBrokenImage()
     {
       imageSize           = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
       mPlacementActorSize = imageSize;
+
+      if(mRendererAdded)
+      {
+        actor.RemoveRenderer(mImpl->mRenderer);
+        mRendererAdded = false;
+      }
     }
 
     mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
     if(actor)
     {
       actor.AddRenderer(mImpl->mRenderer);
+      mRendererAdded = true;
       mPlacementActor.Reset();
     }
   }
   else
   {
+    if(mRendererAdded)
+    {
+      Actor actor = mPlacementActor.GetHandle();
+      if(actor)
+      {
+        actor.RemoveRenderer(mImpl->mRenderer);
+        mRendererAdded = false;
+      }
+    }
     ResetRenderer();
   }
 }
 
+void ImageVisual::ResetFastTrackLoadingTask()
+{
+  if(mFastTrackLoadingTask)
+  {
+    Dali::AsyncTaskManager::Get().RemoveTask(mFastTrackLoadingTask);
+    mFastTrackLoadingTask.Reset();
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 1bd414b..925cdad 100644 (file)
 #include <dali/public-api/common/intrusive-ptr.h>
 #include <dali/public-api/images/image-operations.h>
 #include <dali/public-api/object/weak-handle.h>
+#include <dali/public-api/rendering/visual-renderer.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
+#include <dali-toolkit/internal/image-loader/fast-track-loading-task.h>
 #include <dali-toolkit/internal/texture-manager/texture-upload-observer.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
@@ -250,6 +252,13 @@ public:
    */
   void LoadComplete(bool success, TextureInformation textureInformation) override;
 
+  /**
+   * @brief Test callback for FastTrackLoadingTask
+   *
+   * @param[in] task The pointer of task who call this callback.
+   */
+  void FastLoadComplete(FastTrackLoadingTaskPtr task);
+
 private:
   /**
    * Allocate the mask data when a masking property is defined in the property map
@@ -333,6 +342,11 @@ private:
    */
   void ShowBrokenImage();
 
+  /**
+   * @brief Remove current added fast track upload task.
+   */
+  void ResetFastTrackLoadingTask();
+
 private:
   Vector4                            mPixelArea;
   WeakHandle<Actor>                  mPlacementActor;
@@ -345,6 +359,8 @@ private:
   Vector2                   mTextureSize;
   Vector2                   mPlacementActorSize;
 
+  FastTrackLoadingTaskPtr mFastTrackLoadingTask; ///< For fast track uploading.
+
   ImageVisualShaderFactory& mImageVisualShaderFactory;
 
   Dali::FittingMode::Type                         mFittingMode : 3;
@@ -355,11 +371,13 @@ private:
   Dali::Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;
   Vector4                                         mAtlasRect;
   Dali::ImageDimensions                           mAtlasRectSize;
-  TextureManager::LoadState                       mLoadState;             ///< The texture loading state
-  bool                                            mAttemptAtlasing;       ///< If true will attempt atlasing, otherwise create unique texture
-  bool                                            mOrientationCorrection; ///< true if the image will have it's orientation corrected.
-  bool                                            mNeedYuvToRgb{false};   ///< true if we need to convert yuv to rgb.
-  bool                                            mEnableBrokenImage;
+  TextureManager::LoadState                       mLoadState;                    ///< The texture loading state
+  bool                                            mAttemptAtlasing;              ///< If true will attempt atlasing, otherwise create unique texture
+  bool                                            mOrientationCorrection;        ///< true if the image will have it's orientation corrected.
+  bool                                            mNeedYuvToRgb{false};          ///< true if we need to convert yuv to rgb.
+  bool                                            mEnableBrokenImage{true};      ///< true if enable broken image.
+  bool                                            mUseFastTrackUploading{false}; ///< True if we use fast tack feature.
+  bool                                            mRendererAdded{false};         ///< True if renderer added into actor.
 };
 
 } // namespace Internal
index ec888ce..0ffa198 100644 (file)
@@ -123,6 +123,7 @@ const char* const ALPHA_MASK_URL("alphaMaskUrl");
 const char* const REDRAW_IN_SCALING_DOWN_NAME("redrawInScalingDown");
 const char* const MASKING_TYPE_NAME("maskingType");
 const char* const MASK_TEXTURE_RATIO_NAME("maskTextureRatio");
+const char* const FAST_TRACK_UPLOADING_NAME("fastTrackUploading");
 const char* const ENABLE_BROKEN_IMAGE("enableBrokenImage");
 
 // Text visual
index 29d75a9..ff492ec 100644 (file)
@@ -107,6 +107,7 @@ extern const char* const ALPHA_MASK_URL;
 extern const char* const REDRAW_IN_SCALING_DOWN_NAME;
 extern const char* const MASKING_TYPE_NAME;
 extern const char* const MASK_TEXTURE_RATIO_NAME;
+extern const char* const FAST_TRACK_UPLOADING_NAME;
 extern const char* const ENABLE_BROKEN_IMAGE;
 
 // Text visual