From 4a1bc7f4147b5d305baed73f1a53b0f35fa4952b Mon Sep 17 00:00:00 2001 From: lushi Date: Wed, 29 Jun 2022 20:45:16 +0800 Subject: [PATCH] loading same image at same time optimize Signed-off-by: lushi Change-Id: Ia8741f643a89acea8c3623fd57cfee5983757b46 --- frameworks/core/image/image_object.cpp | 30 +++-- frameworks/core/image/image_object.h | 30 +++++ frameworks/core/image/image_provider.cpp | 130 ++++++++++++++++++-- frameworks/core/image/image_provider.h | 45 +++++++ frameworks/core/image/image_source_info.cpp | 5 +- 5 files changed, 212 insertions(+), 28 deletions(-) diff --git a/frameworks/core/image/image_object.cpp b/frameworks/core/image/image_object.cpp index 684398f73d1..5e41fdfccb4 100644 --- a/frameworks/core/image/image_object.cpp +++ b/frameworks/core/image/image_object.cpp @@ -124,10 +124,15 @@ void StaticImageObject::UploadToGpuForRender( LOGE("task executor is null."); return; } + auto key = GenerateCacheKey(imageSource, imageSize); + if (!ImageProvider::TryUploadingImage(key, successCallback, failedCallback)) { + LOGI("other thread is uploading same image to gpu : %{public}s", imageSource.ToString().c_str()); + return; + } fml::RefPtr cachedFlutterImage; auto imageCache = pipelineContext->GetImageCache(); if (imageCache) { - auto cachedImage = imageCache->GetCacheImage(GenerateCacheKey(imageSource, imageSize)); + auto cachedImage = imageCache->GetCacheImage(key); LOGD("image cache valid"); if (cachedImage) { LOGD("cached image found."); @@ -135,10 +140,8 @@ void StaticImageObject::UploadToGpuForRender( } } if (cachedFlutterImage) { - LOGD("get cached image success: %{public}s", GenerateCacheKey(imageSource, imageSize).c_str()); - taskExecutor->PostTask([successCallback, imageSource, - cachedFlutterImage] { successCallback(imageSource, cachedFlutterImage); }, - TaskExecutor::TaskType::UI); + LOGD("get cached image success: %{public}s", key.c_str()); + ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, cachedFlutterImage); return; } @@ -147,32 +150,27 @@ void StaticImageObject::UploadToGpuForRender( skData = ImageProvider::LoadImageRawData(imageSource, pipelineContext, imageSize); if (!skData) { LOGE("reload image data failed. imageSource: %{private}s", imageSource.ToString().c_str()); - taskExecutor->PostTask( - [failedCallback, imageSource] { failedCallback(imageSource); }, TaskExecutor::TaskType::UI); + ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr); return; } } auto rawImage = SkImage::MakeFromEncoded(skData); if (!rawImage) { LOGE("static image MakeFromEncoded fail! imageSource: %{private}s", imageSource.ToString().c_str()); - taskExecutor->PostTask( - [failedCallback, imageSource] { failedCallback(imageSource); }, TaskExecutor::TaskType::UI); + ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr); return; } auto image = ImageProvider::ResizeSkImage(rawImage, imageSource.GetSrc(), imageSize, forceResize); - auto callback = [successCallback, imageSource, taskExecutor, imageCache, imageSize, + auto callback = [successCallback, imageSource, taskExecutor, imageCache, imageSize, key, id = Container::CurrentId()](flutter::SkiaGPUObject image) { ContainerScope scope(id); auto canvasImage = flutter::CanvasImage::Create(); canvasImage->set_image(std::move(image)); if (imageCache) { - LOGD("cache image key: %{public}s", GenerateCacheKey(imageSource, imageSize).c_str()); - imageCache->CacheImage( - GenerateCacheKey(imageSource, imageSize), std::make_shared(canvasImage)); + LOGD("cache image key: %{public}s", key.c_str()); + imageCache->CacheImage(key, std::make_shared(canvasImage)); } - taskExecutor->PostTask( - [successCallback, imageSource, canvasImage] { successCallback(imageSource, canvasImage); }, - TaskExecutor::TaskType::UI); + ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, canvasImage); }; ImageProvider::UploadImageToGPUForRender(image, callback, renderTaskHolder); }; diff --git a/frameworks/core/image/image_object.h b/frameworks/core/image/image_object.h index c8871f35617..723a4207857 100644 --- a/frameworks/core/image/image_object.h +++ b/frameworks/core/image/image_object.h @@ -97,6 +97,11 @@ public: return false; } + virtual RefPtr Clone() + { + return MakeRefPtr(imageSource_, imageSize_, frameCount_, isSvg_); + } + protected: ImageSourceInfo imageSource_; Size imageSize_; @@ -125,6 +130,11 @@ public: void PerformLayoutImageObject(RefPtr image) override; Size MeasureForImage(RefPtr image) override; + RefPtr Clone() override + { + return MakeRefPtr(imageSource_, Size(), frameCount_, skiaDom_); + } + private: sk_sp skiaDom_; }; @@ -150,6 +160,11 @@ public: void PerformLayoutImageObject(RefPtr image) override; Size MeasureForImage(RefPtr image) override; + RefPtr Clone() override + { + return MakeRefPtr(imageSource_, Size(), frameCount_, svgDom_); + } + private: RefPtr svgDom_; }; @@ -184,6 +199,11 @@ public: bool CancelBackgroundTasks() override; + RefPtr Clone() override + { + return MakeRefPtr(imageSource_, imageSize_, frameCount_, skData_); + } + private: sk_sp skData_; CancelableTask uploadForPaintTask_; @@ -232,6 +252,11 @@ public: skData_ = nullptr; } + RefPtr Clone() override + { + return MakeRefPtr(imageSource_, imageSize_, frameCount_, skData_); + } + private: sk_sp skData_; RefPtr animatedPlayer_; @@ -266,6 +291,11 @@ public: return pixmap_; } + RefPtr Clone() override + { + return MakeRefPtr(pixmap_); + } + private: RefPtr pixmap_; }; diff --git a/frameworks/core/image/image_provider.cpp b/frameworks/core/image/image_provider.cpp index 4150ce505e7..08ea8f3349b 100644 --- a/frameworks/core/image/image_provider.cpp +++ b/frameworks/core/image/image_provider.cpp @@ -36,6 +36,114 @@ constexpr double SRGB_GAMUT_AREA = 0.104149; } // namespace +std::mutex ImageProvider::loadingImageMutex_; +std::unordered_map> ImageProvider::loadingImage_; + +std::mutex ImageProvider::uploadMutex_; +std::unordered_map> ImageProvider::uploadingImage_; + +bool ImageProvider::TrySetLoadingImage( + const ImageSourceInfo& imageInfo, + const ImageObjSuccessCallback& successCallback, + const UploadSuccessCallback& uploadCallback, + const FailedCallback& failedCallback) +{ + std::lock_guard lock(loadingImageMutex_); + auto key = imageInfo.GetCacheKey(); + auto iter = loadingImage_.find(key); + if (iter == loadingImage_.end()) { + std::vector callbacks { { successCallback, uploadCallback, failedCallback } }; + loadingImage_.emplace(key, callbacks); + return true; + } else { + LOGI("other thread is loading same image: %{public}s", imageInfo.ToString().c_str()); + iter->second.emplace_back(successCallback, uploadCallback, failedCallback); + return false; + } +} + +void ImageProvider::ProccessLoadingResult( + const RefPtr& taskExecutor, + const ImageSourceInfo& imageInfo, + bool canStartUploadImageObj, + const RefPtr& imageObj, + const RefPtr& context, + const RefPtr& renderTaskHolder) +{ + std::lock_guard lock(loadingImageMutex_); + std::vector callbacks; + auto key = imageInfo.GetCacheKey(); + auto iter = loadingImage_.find(key); + if (iter != loadingImage_.end()) { + std::swap(callbacks, iter->second); + for (const auto& callback : callbacks) { + if (imageObj != nullptr) { + auto obj = imageObj->Clone(); + taskExecutor->PostTask([obj, imageInfo, callback]() { + callback.successCallback(imageInfo, obj); + }, TaskExecutor::TaskType::UI); + if (canStartUploadImageObj) { + bool forceResize = (!obj->IsSvg()) && (imageInfo.IsSourceDimensionValid()); + obj->UploadToGpuForRender( + context, + renderTaskHolder, + callback.uploadCallback, + callback.failedCallback, + obj->GetImageSize(), + forceResize, true); + } + } + } + } else { + LOGW("no loading image: %{public}s", imageInfo.ToString().c_str()); + } + loadingImage_.erase(key); +} + +bool ImageProvider::TryUploadingImage( + const std::string& key, + const UploadSuccessCallback& successCallback, + const FailedCallback& failedCallback) +{ + std::lock_guard lock(uploadMutex_); + auto iter = uploadingImage_.find(key); + if (iter == uploadingImage_.end()) { + std::vector callbacks = { { nullptr, successCallback, failedCallback } }; + uploadingImage_.emplace(key, callbacks); + return true; + } else { + iter->second.emplace_back(nullptr, successCallback, failedCallback); + return false; + } +} + +void ImageProvider::ProccessUploadResult( + const RefPtr& taskExecutor, + const ImageSourceInfo& imageInfo, + const Size& imageSize, + const fml::RefPtr& canvasImage) +{ + std::lock_guard lock(uploadMutex_); + std::vector callbacks; + auto key = ImageObject::GenerateCacheKey(imageInfo, imageSize); + auto iter = uploadingImage_.find(key); + if (iter != uploadingImage_.end()) { + std::swap(callbacks, iter->second); + taskExecutor->PostTask([callbacks, imageInfo, canvasImage]() { + for (auto callback : callbacks) { + if (canvasImage) { + callback.uploadCallback(imageInfo, canvasImage); + } else { + callback.failedCallback(imageInfo); + } + } + }, TaskExecutor::TaskType::UI); + } else { + LOGW("no uploading image: %{public}s", imageInfo.ToString().c_str()); + } + uploadingImage_.erase(key); +} + void ImageProvider::FetchImageObject( const ImageSourceInfo& imageInfo, const ImageObjSuccessCallback& successCallback, @@ -61,6 +169,10 @@ void ImageProvider::FetchImageObject( LOGE("task executor is null. imageInfo: %{private}s", imageInfo.ToString().c_str()); return; } + if (!syncMode && !TrySetLoadingImage(imageInfo, successCallback, uploadSuccessCallback, failedCallback)) { + LOGI("same source is loading: %{private}s", imageInfo.ToString().c_str()); + return; + } RefPtr imageObj = QueryImageObjectFromCache(imageInfo, pipelineContext); if (!imageObj) { // if image object is not in cache, generate a new one. imageObj = GeneraterAceImageObject(imageInfo, pipelineContext, useSkiaSvg); @@ -70,21 +182,19 @@ void ImageProvider::FetchImageObject( failedCallback(imageInfo); return; } - taskExecutor->PostTask( - [failedCallback, imageInfo] { failedCallback(imageInfo); }, TaskExecutor::TaskType::UI); + ProccessLoadingResult(taskExecutor, imageInfo, false, nullptr, pipelineContext, renderTaskHolder); return; } if (syncMode) { successCallback(imageInfo, imageObj); } else { - taskExecutor->PostTask([successCallback, imageInfo, imageObj]() { successCallback(imageInfo, imageObj); }, - TaskExecutor::TaskType::UI); - } - bool canStartUploadImageObj = !needAutoResize && (imageObj->GetFrameCount() == 1); - if (canStartUploadImageObj) { - bool forceResize = (!imageObj->IsSvg()) && (imageInfo.IsSourceDimensionValid()); - imageObj->UploadToGpuForRender(context, renderTaskHolder, uploadSuccessCallback, failedCallback, - imageObj->GetImageSize(), forceResize, true); + ProccessLoadingResult( + taskExecutor, + imageInfo, + !needAutoResize && (imageObj->GetFrameCount() == 1), + imageObj, + pipelineContext, + renderTaskHolder); } }; if (syncMode) { diff --git a/frameworks/core/image/image_provider.h b/frameworks/core/image/image_provider.h index 0f5680ccf29..274883a723c 100644 --- a/frameworks/core/image/image_provider.h +++ b/frameworks/core/image/image_provider.h @@ -59,6 +59,19 @@ using FailedCallback = std::function; using CancelableTask = CancelableCallback; using OnPostBackgroundTask = std::function; +struct LoadCallback { + LoadCallback( + const ImageObjSuccessCallback& success, + const UploadSuccessCallback& upload, + const FailedCallback& failed) + : successCallback(success), uploadCallback(upload), failedCallback(failed) {} + ~LoadCallback() = default; + + ImageObjSuccessCallback successCallback; + UploadSuccessCallback uploadCallback; + FailedCallback failedCallback; +}; + class FlutterRenderImage; class ImageProvider { public: @@ -138,6 +151,38 @@ public: static SkAlphaType AlphaTypeToSkAlphaType(const RefPtr& pixmap); static SkImageInfo MakeSkImageInfoFromPixelMap(const RefPtr& pixmap); static sk_sp ColorSpaceToSkColorSpace(const RefPtr& pixmap); + + static bool TrySetLoadingImage( + const ImageSourceInfo& imageInfo, + const ImageObjSuccessCallback& successCallback, + const UploadSuccessCallback& uploadCallback, + const FailedCallback& failedCallback); + + static void ProccessLoadingResult( + const RefPtr& taskExecutor, + const ImageSourceInfo& imageInfo, + bool canStartUploadImageObj, + const RefPtr& imageObj, + const RefPtr& context, + const RefPtr& renderTaskHolder); + + static bool TryUploadingImage( + const std::string& key, + const UploadSuccessCallback& successCallback, + const FailedCallback& failedCallback); + + static void ProccessUploadResult( + const RefPtr& taskExecutor, + const ImageSourceInfo& imageInfo, + const Size& imageSize, + const fml::RefPtr& canvasImage); + +private: + static std::mutex loadingImageMutex_; + static std::unordered_map> loadingImage_; + + static std::mutex uploadMutex_; + static std::unordered_map> uploadingImage_; }; } // namespace OHOS::Ace diff --git a/frameworks/core/image/image_source_info.cpp b/frameworks/core/image/image_source_info.cpp index 57661c5c5b6..ef09f334b58 100644 --- a/frameworks/core/image/image_source_info.cpp +++ b/frameworks/core/image/image_source_info.cpp @@ -16,8 +16,9 @@ #include "core/image/image_source_info.h" #include -#include "base/log/log.h" +#include "base/log/log.h" +#include "core/common/container.h" namespace OHOS::Ace { bool ImageSourceInfo::IsSVGSource(const std::string& src, InternalResource::ResourceId resourceId) @@ -112,7 +113,7 @@ ImageSourceInfo::ImageSourceInfo( if (count > 1) { LOGW("multi image source set, only one will be load."); } - cacheKey_ = std::to_string(std::hash {}(src_)); + cacheKey_ = std::to_string(std::hash {}(src_ + std::to_string(Container::CurrentId()))); } SrcType ImageSourceInfo::ResolveSrcType() const -- Gitee