From 8f41528e0ebad2deb1ba442d21275547aa504a7f Mon Sep 17 00:00:00 2001 From: 3x3 Date: Tue, 27 Jun 2023 16:59:17 +0800 Subject: [PATCH] Add support for caching video compositions into disk file. (#989) * support disk cache of SequenceComposition * support disk cache of SequenceComposition * support disk cache of SequenceComposition * Rename function. * support disk cache of SequenceComposition * support disk cache of SequenceComposition * support disk cache of SequenceComposition * support disk cache of SequenceComposition * update comments. --------- Co-authored-by: liamcli Co-authored-by: kevingpqi Co-authored-by: domrjchen --- .../src/main/java/org/libpag/PAGPlayer.java | 12 ++ .../src/main/java/org/libpag/PAGView.java | 16 +++ include/pag/pag.h | 17 +++ src/platform/android/JPAGPlayer.cpp | 16 +++ src/platform/cocoa/PAGPlayer.h | 12 ++ src/platform/cocoa/PAGPlayer.m | 8 ++ src/platform/cocoa/private/PAGPlayerImpl.h | 4 + src/platform/cocoa/private/PAGPlayerImpl.mm | 8 ++ src/platform/ios/PAGView.h | 12 ++ src/platform/ios/PAGView.m | 8 ++ src/rendering/PAGDecoder.cpp | 32 +++-- src/rendering/PAGPlayer.cpp | 10 ++ src/rendering/caches/RenderCache.cpp | 2 +- src/rendering/caches/RenderCache.h | 17 +++ .../sequences/BitmapSequenceReader.cpp | 32 +++-- .../sequences/BitmapSequenceReader.h | 4 +- .../sequences/DiskSequenceReader.cpp | 129 ++++++++++++++++++ src/rendering/sequences/DiskSequenceReader.h | 49 +++++++ .../sequences/SequenceImageProxy.cpp | 2 +- .../sequences/SequenceImageQueue.cpp | 15 +- src/rendering/sequences/SequenceImageQueue.h | 5 +- src/rendering/sequences/SequenceInfo.cpp | 26 ++-- src/rendering/sequences/SequenceInfo.h | 16 ++- 23 files changed, 406 insertions(+), 46 deletions(-) create mode 100644 src/rendering/sequences/DiskSequenceReader.cpp create mode 100644 src/rendering/sequences/DiskSequenceReader.h diff --git a/android/libpag/src/main/java/org/libpag/PAGPlayer.java b/android/libpag/src/main/java/org/libpag/PAGPlayer.java index ab953badb3..bf9b555b4c 100644 --- a/android/libpag/src/main/java/org/libpag/PAGPlayer.java +++ b/android/libpag/src/main/java/org/libpag/PAGPlayer.java @@ -68,6 +68,18 @@ public void setSurface(PAGSurface surface) { */ public native void setCacheEnabled(boolean value); + /** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ + public native boolean useDiskCache(); + + /** + * Set the value of useDiskCache property. + */ + public native void setUseDiskCache(boolean value); + /** * This value defines the scale factor for internal graphics caches, ranges from 0.0 to 1.0. The * scale factors less than 1.0 may result in blurred output, but it can reduce the usage of diff --git a/android/libpag/src/main/java/org/libpag/PAGView.java b/android/libpag/src/main/java/org/libpag/PAGView.java index a9f3d7365d..ef4095bb34 100644 --- a/android/libpag/src/main/java/org/libpag/PAGView.java +++ b/android/libpag/src/main/java/org/libpag/PAGView.java @@ -281,6 +281,22 @@ public void setCacheEnabled(boolean value) { pagPlayer.setCacheEnabled(value); } + /** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ + public boolean useDiskCache() { + return pagPlayer.useDiskCache(); + } + + /** + * Set the value of useDiskCache property. + */ + public void setUseDiskCache(boolean value) { + pagPlayer.setUseDiskCache(value); + } + /** * This value defines the scale factor for internal graphics caches, ranges from 0.0 to 1.0. The * scale factors less than 1.0 may result in blurred output, but it can reduce the usage of diff --git a/include/pag/pag.h b/include/pag/pag.h index 899d8dd3df..1d4e200120 100644 --- a/include/pag/pag.h +++ b/include/pag/pag.h @@ -1328,6 +1328,18 @@ class PAG_API PAGPlayer { */ void setCacheEnabled(bool value); + /** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ + bool useDiskCache(); + + /** + * Set the value of useDiskCache property. + */ + void setUseDiskCache(bool value); + /** * This value defines the scale factor for internal graphics caches, ranges from 0.0 to 1.0. The * scale factors less than 1.0 may result in blurred output, but it can reduce the usage of @@ -1621,6 +1633,8 @@ class PAG_API PAGDecoder { std::shared_ptr sequenceFile = nullptr; std::shared_ptr reader = nullptr; std::vector staticTimeRanges = {}; + std::function)> cacheKeyGeneratorFun = + nullptr; static Composition* GetSingleComposition(std::shared_ptr pagComposition); static std::pair GetFrameCountAndRate(std::shared_ptr pagComposition, @@ -1638,6 +1652,9 @@ class PAG_API PAGDecoder { void checkCompositionChange(std::shared_ptr composition); std::string generateCacheKey(std::shared_ptr composition); std::shared_ptr getComposition(); + void setCacheKeyGeneratorFun( + std::function)> fun); + friend class DiskSequenceReader; }; /** diff --git a/src/platform/android/JPAGPlayer.cpp b/src/platform/android/JPAGPlayer.cpp index 3d6ba18a95..803591ba87 100644 --- a/src/platform/android/JPAGPlayer.cpp +++ b/src/platform/android/JPAGPlayer.cpp @@ -315,4 +315,20 @@ PAG_API jboolean Java_org_libpag_PAGPlayer_hitTestPoint(JNIEnv* env, jobject thi auto pagLayer = ToPAGLayerNativeObject(env, layer); return (jboolean)player->hitTestPoint(pagLayer, x, y, pixelHitTest); } + +PAG_API void Java_org_libpag_PAGPlayer_setUseDiskCache(JNIEnv* env, jobject thiz, jboolean value) { + auto player = getPAGPlayer(env, thiz); + if (player == nullptr) { + return; + } + player->setUseDiskCache(value); +} + +PAG_API jboolean Java_org_libpag_PAGPlayer_useDiskCache(JNIEnv* env, jobject thiz) { + auto player = getPAGPlayer(env, thiz); + if (player == nullptr) { + return JNI_FALSE; + } + return player->useDiskCache(); +} } diff --git a/src/platform/cocoa/PAGPlayer.h b/src/platform/cocoa/PAGPlayer.h index 7e6d06ccd5..4226e46abc 100644 --- a/src/platform/cocoa/PAGPlayer.h +++ b/src/platform/cocoa/PAGPlayer.h @@ -69,6 +69,18 @@ PAG_API @interface PAGPlayer : NSObject */ - (void)setCacheEnabled:(BOOL)value; +/** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ +- (BOOL)useDiskCache; + +/** + * Set the value of useDiskCache property. + */ +- (void)setUseDiskCache:(BOOL)value; + /** * This value defines the scale factor for internal graphics caches, ranges from 0.0 to 1.0. The * scale factors less than 1.0 may result in blurred output, but it can reduce the usage of graphics diff --git a/src/platform/cocoa/PAGPlayer.m b/src/platform/cocoa/PAGPlayer.m index 89d42c0121..b9ca972c47 100644 --- a/src/platform/cocoa/PAGPlayer.m +++ b/src/platform/cocoa/PAGPlayer.m @@ -88,6 +88,14 @@ - (void)setCacheEnabled:(BOOL)value { [pagPlayer setCacheEnabled:value]; } +- (BOOL)useDiskCache { + return [pagPlayer useDiskCache]; +} + +- (void)setUseDiskCache:(BOOL)value { + [pagPlayer setUseDiskCache:value]; +} + - (float)cacheScale { return [pagPlayer cacheScale]; } diff --git a/src/platform/cocoa/private/PAGPlayerImpl.h b/src/platform/cocoa/private/PAGPlayerImpl.h index 74fe9e7a31..5b6b7b818f 100644 --- a/src/platform/cocoa/private/PAGPlayerImpl.h +++ b/src/platform/cocoa/private/PAGPlayerImpl.h @@ -35,6 +35,10 @@ - (void)setCacheEnabled:(BOOL)value; +- (BOOL)useDiskCache; + +- (void)setUseDiskCache:(BOOL)value; + - (float)cacheScale; - (void)setCacheScale:(float)value; diff --git a/src/platform/cocoa/private/PAGPlayerImpl.mm b/src/platform/cocoa/private/PAGPlayerImpl.mm index ec6b456173..5aaeb97705 100644 --- a/src/platform/cocoa/private/PAGPlayerImpl.mm +++ b/src/platform/cocoa/private/PAGPlayerImpl.mm @@ -82,6 +82,14 @@ - (void)setCacheEnabled:(BOOL)value { pagPlayer->setCacheEnabled(value); } +- (BOOL)useDiskCache { + return pagPlayer->useDiskCache(); +} + +- (void)setUseDiskCache:(BOOL)value { + pagPlayer->setUseDiskCache(value); +} + - (float)cacheScale { return pagPlayer->cacheScale(); } diff --git a/src/platform/ios/PAGView.h b/src/platform/ios/PAGView.h index 938a46c49e..717c86d583 100644 --- a/src/platform/ios/PAGView.h +++ b/src/platform/ios/PAGView.h @@ -164,6 +164,18 @@ PAG_API @interface PAGView : UIView */ - (void)setCacheEnabled:(BOOL)value; +/** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ +- (BOOL)useDiskCache; + +/** + * Set the value of useDiskCache property. + */ +- (void)setUseDiskCache:(BOOL)value; + /** * This value defines the scale factor for internal graphics caches, ranges from 0.0 to 1.0. The * scale factors less than 1.0 may result in blurred output, but it can reduce the usage of graphics diff --git a/src/platform/ios/PAGView.m b/src/platform/ios/PAGView.m index 39747e1b0c..5749522b4e 100644 --- a/src/platform/ios/PAGView.m +++ b/src/platform/ios/PAGView.m @@ -240,6 +240,14 @@ - (void)setCacheEnabled:(BOOL)value { [pagPlayer setCacheEnabled:value]; } +- (BOOL)useDiskCache { + return [pagPlayer useDiskCache]; +} + +- (void)setUseDiskCache:(BOOL)value { + [pagPlayer setUseDiskCache:value]; +} + - (float)cacheScale { return [pagPlayer cacheScale]; } diff --git a/src/rendering/PAGDecoder.cpp b/src/rendering/PAGDecoder.cpp index 8374b5384e..aa5eeec40d 100644 --- a/src/rendering/PAGDecoder.cpp +++ b/src/rendering/PAGDecoder.cpp @@ -28,6 +28,21 @@ #include "rendering/utils/LockGuard.h" namespace pag { + +static std::string DefaultCacheKeyGeneratorFunc(PAGDecoder* decoder, + std::shared_ptr composition) { + if (!composition->isPAGFile() || pag::ContentVersion::Get(composition) > 0) { + return ""; + } + auto filePath = static_cast(composition.get())->path(); + filePath = Platform::Current()->getSandboxPath(filePath); + if (filePath.empty()) { + return ""; + } + return filePath + "." + std::to_string(decoder->width()) + "x" + + std::to_string(decoder->height()); +} + Composition* PAGDecoder::GetSingleComposition(std::shared_ptr pagComposition) { auto numChildren = pagComposition->numChildren(); if (numChildren == 0) { @@ -258,15 +273,8 @@ void PAGDecoder::checkCompositionChange(std::shared_ptr composit } std::string PAGDecoder::generateCacheKey(std::shared_ptr composition) { - if (!composition->isPAGFile() || composition->contentModified()) { - return ""; - } - auto filePath = static_cast(composition.get())->path(); - filePath = Platform::Current()->getSandboxPath(filePath); - if (filePath.empty()) { - return ""; - } - return filePath + "." + std::to_string(_width) + "x" + std::to_string(_height); + return cacheKeyGeneratorFun == nullptr ? DefaultCacheKeyGeneratorFunc(this, composition) + : cacheKeyGeneratorFun(this, composition); } std::shared_ptr PAGDecoder::getComposition() { @@ -278,4 +286,10 @@ std::shared_ptr PAGDecoder::getComposition() { } return nullptr; } + +void PAGDecoder::setCacheKeyGeneratorFun( + std::function composition)> fun) { + cacheKeyGeneratorFun = fun; +} + } // namespace pag diff --git a/src/rendering/PAGPlayer.cpp b/src/rendering/PAGPlayer.cpp index 0a44671111..a5c57d1ec5 100644 --- a/src/rendering/PAGPlayer.cpp +++ b/src/rendering/PAGPlayer.cpp @@ -122,6 +122,16 @@ void PAGPlayer::setCacheEnabled(bool value) { renderCache->setSnapshotEnabled(value); } +bool PAGPlayer::useDiskCache() { + LockGuard autoLock(rootLocker); + return renderCache->useDiskCache(); +} + +void PAGPlayer::setUseDiskCache(bool value) { + LockGuard autoLock(rootLocker); + renderCache->setUseDiskCache(value); +} + float PAGPlayer::cacheScale() { LockGuard autoLock(rootLocker); return stage->cacheScale(); diff --git a/src/rendering/caches/RenderCache.cpp b/src/rendering/caches/RenderCache.cpp index e46c27cb15..21f18362cb 100644 --- a/src/rendering/caches/RenderCache.cpp +++ b/src/rendering/caches/RenderCache.cpp @@ -581,7 +581,7 @@ SequenceImageQueue* RenderCache::makeSequenceImageQueue(std::shared_ptrgetLayerFromReferenceMap(sequence->uniqueID()); - auto queue = SequenceImageQueue::MakeFrom(sequence, layer).release(); + auto queue = SequenceImageQueue::MakeFrom(sequence, layer, _useDiskCache).release(); if (queue == nullptr) { return nullptr; } diff --git a/src/rendering/caches/RenderCache.h b/src/rendering/caches/RenderCache.h index d95acd260c..6df69f9806 100644 --- a/src/rendering/caches/RenderCache.h +++ b/src/rendering/caches/RenderCache.h @@ -96,6 +96,22 @@ class RenderCache : public Performance { return snapshotCaches.count(assetID) > 0; } + /** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ + bool useDiskCache() const { + return _useDiskCache; + } + + /** + * Set the value of useDiskCache property. + */ + void setUseDiskCache(bool value) { + _useDiskCache = value; + } + /** * Returns a snapshot cache of specified asset id. Returns null if there is no associated cache * available. This is a read-only query which is used usually during hit testing. @@ -171,6 +187,7 @@ class RenderCache : public Performance { size_t graphicsMemory = 0; bool _videoEnabled = true; bool _snapshotEnabled = true; + bool _useDiskCache = false; std::unordered_set usedAssets = {}; std::unordered_map snapshotCaches = {}; std::list snapshotLRU = {}; diff --git a/src/rendering/sequences/BitmapSequenceReader.cpp b/src/rendering/sequences/BitmapSequenceReader.cpp index 39229ec2e6..1f8efbce06 100644 --- a/src/rendering/sequences/BitmapSequenceReader.cpp +++ b/src/rendering/sequences/BitmapSequenceReader.cpp @@ -26,11 +26,11 @@ BitmapSequenceReader::BitmapSequenceReader(std::shared_ptr file, BitmapSeq : file(std::move(file)), sequence(sequence) { // Force allocating a raster PixelBuffer if staticContent is false, otherwise the asynchronous // decoding will fail due to the memory sharing mechanism. - auto staticContent = sequence->composition->staticContent(); - if (staticContent) { - bitmap.allocPixels(sequence->width, sequence->height, false); - bitmap.clear(); - } else { + if (tgfx::HardwareBufferAvailable() && sequence->composition->staticContent()) { + hardWareBuffer = tgfx::HardwareBufferAllocate(sequence->width, sequence->height, false); + info = tgfx::HardwareBufferGetInfo(hardWareBuffer); + } + if (hardWareBuffer == nullptr) { info = tgfx::ImageInfo::Make(sequence->width, sequence->height, tgfx::ColorType::RGBA_8888); tgfx::Buffer buffer(info.byteSize()); buffer.clear(); @@ -38,20 +38,30 @@ BitmapSequenceReader::BitmapSequenceReader(std::shared_ptr file, BitmapSeq } } +BitmapSequenceReader::~BitmapSequenceReader() { + if (hardWareBuffer) { + tgfx::HardwareBufferRelease(hardWareBuffer); + } +} + std::shared_ptr BitmapSequenceReader::onMakeBuffer(Frame targetFrame) { // a locker is required here because decodeFrame() could be called from multiple threads. std::lock_guard autoLock(locker); if (lastDecodeFrame == targetFrame) { return imageBuffer; } - if (bitmap.isEmpty() && pixels == nullptr) { + if (hardWareBuffer == nullptr && pixels == nullptr) { return nullptr; } imageBuffer = nullptr; lastDecodeFrame = -1; tgfx::Pixmap pixmap = {}; - if (!bitmap.isEmpty()) { - pixmap.reset(bitmap); + if (hardWareBuffer) { + auto hardwarePixels = tgfx::HardwareBufferLock(hardWareBuffer); + if (hardwarePixels == nullptr) { + return nullptr; + } + pixmap.reset(info, hardwarePixels); } else { pixmap.reset(info, const_cast(pixels->data())); } @@ -75,14 +85,16 @@ std::shared_ptr BitmapSequenceReader::onMakeBuffer(Frame targ auto result = codec->readPixels( pixmap.info(), reinterpret_cast(pixmap.writablePixels()) + offset); if (!result) { + tgfx::HardwareBufferUnlock(hardWareBuffer); return nullptr; } firstRead = false; } } } - if (!bitmap.isEmpty()) { - imageBuffer = bitmap.makeBuffer(); + if (hardWareBuffer) { + tgfx::HardwareBufferUnlock(hardWareBuffer); + imageBuffer = tgfx::ImageBuffer::MakeFrom(hardWareBuffer); } else { imageBuffer = tgfx::ImageBuffer::MakeFrom(info, pixels); } diff --git a/src/rendering/sequences/BitmapSequenceReader.h b/src/rendering/sequences/BitmapSequenceReader.h index 453d3eb5ea..4a35bf2859 100644 --- a/src/rendering/sequences/BitmapSequenceReader.h +++ b/src/rendering/sequences/BitmapSequenceReader.h @@ -36,6 +36,8 @@ class BitmapSequenceReader : public SequenceReader { return sequence->height; } + ~BitmapSequenceReader() override; + protected: std::shared_ptr onMakeBuffer(Frame targetFrame) override; @@ -49,8 +51,8 @@ class BitmapSequenceReader : public SequenceReader { BitmapSequence* sequence = nullptr; Frame lastDecodeFrame = -1; std::shared_ptr imageBuffer = nullptr; - tgfx::Bitmap bitmap = {}; tgfx::ImageInfo info = {}; std::shared_ptr pixels = nullptr; + HardwareBufferRef hardWareBuffer = nullptr; }; } // namespace pag diff --git a/src/rendering/sequences/DiskSequenceReader.cpp b/src/rendering/sequences/DiskSequenceReader.cpp new file mode 100644 index 0000000000..201f604354 --- /dev/null +++ b/src/rendering/sequences/DiskSequenceReader.cpp @@ -0,0 +1,129 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. +// +// 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 "DiskSequenceReader.h" +#include "base/utils/TGFXCast.h" +#include "platform/Platform.h" +#include "tgfx/utils/Buffer.h" + +namespace pag { + +std::shared_ptr DiskSequenceReader::Make(std::shared_ptr file, + Sequence* sequence) { + if (sequence == nullptr || file == nullptr) { + return nullptr; + } + return std::shared_ptr(new DiskSequenceReader(std::move(file), sequence)); +} + +DiskSequenceReader::DiskSequenceReader(std::shared_ptr file, Sequence* sequence) + : sequence(sequence), file(file) { +} + +DiskSequenceReader::~DiskSequenceReader() { + if (frontHardWareBuffer) { + tgfx::HardwareBufferRelease(frontHardWareBuffer); + } + if (backHardwareBuffer) { + tgfx::HardwareBufferRelease(backHardwareBuffer); + } +} + +int DiskSequenceReader::width() const { + return sequence->composition->width; +} + +int DiskSequenceReader::height() const { + return sequence->composition->height; +} + +std::shared_ptr DiskSequenceReader::onMakeBuffer(Frame targetFrame) { + // Need a locker here in case there are other threads are decoding at the same time. + std::lock_guard autoLock(locker); + if (pagDecoder == nullptr) { + auto root = PAGComposition::Make(sequence->width, sequence->height); + auto composition = std::make_shared( + file, PreComposeLayer::Wrap(sequence->composition).release()); + composition->setMatrix( + Matrix::MakeScale(sequence->width * 1.f / sequence->composition->width, + sequence->height * 1.f / sequence->composition->height)); + root->addLayer(composition); + pagDecoder = PAGDecoder::MakeFrom(root, sequence->frameRate); + pagDecoder->setCacheKeyGeneratorFun( + [this](PAGDecoder*, std::shared_ptr) -> std::string { + auto cachePath = Platform::Current()->getSandboxPath(file->path); + if (cachePath.empty()) { + return ""; + } + return std::string(cachePath + ".bmp." + std::to_string(sequence->composition->id)); + }); + } + if (pagDecoder == nullptr) { + return nullptr; + } + if (frontHardWareBuffer == nullptr && pixels == nullptr) { + if (tgfx::HardwareBufferAvailable()) { + frontHardWareBuffer = + tgfx::HardwareBufferAllocate(pagDecoder->width(), pagDecoder->height(), false); + if (frontHardWareBuffer && !sequence->composition->staticContent()) { + backHardwareBuffer = + tgfx::HardwareBufferAllocate(pagDecoder->width(), pagDecoder->height(), false); + } + } + if (frontHardWareBuffer == nullptr) { + info = tgfx::ImageInfo::Make(pagDecoder->width(), pagDecoder->height(), + tgfx::ColorType::RGBA_8888); + tgfx::Buffer buffer(info.byteSize()); + buffer.clear(); + pixels = buffer.release(); + } + } + + if (!pagDecoder->checkFrameChanged(targetFrame)) { + return imageBuffer; + } + bool success = false; + auto renderBuffer = useFrontBuffer ? frontHardWareBuffer : backHardwareBuffer; + if (frontHardWareBuffer) { + success = pagDecoder->readFrame(targetFrame, renderBuffer); + } else { + if (pixels) { + success = + pagDecoder->readFrame(targetFrame, const_cast(pixels->data()), info.rowBytes(), + ToPAG(info.colorType()), ToPAG(info.alphaType())); + } + } + if (!success) { + LOGE("DiskSequenceReader: Error on readFrame.\n"); + return nullptr; + } + if (frontHardWareBuffer) { + if (backHardwareBuffer) { + useFrontBuffer = !useFrontBuffer; + } + imageBuffer = tgfx::ImageBuffer::MakeFrom(renderBuffer); + } else { + imageBuffer = tgfx::ImageBuffer::MakeFrom(info, pixels); + } + return imageBuffer; +} + +void DiskSequenceReader::onReportPerformance(Performance*, int64_t) { +} + +} // namespace pag diff --git a/src/rendering/sequences/DiskSequenceReader.h b/src/rendering/sequences/DiskSequenceReader.h new file mode 100644 index 0000000000..ac9d8ae495 --- /dev/null +++ b/src/rendering/sequences/DiskSequenceReader.h @@ -0,0 +1,49 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. +// +// 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. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once +#include "rendering/sequences/VideoReader.h" +#include "tgfx/core/Bitmap.h" + +namespace pag { +class DiskSequenceReader : public SequenceReader { + public: + static std::shared_ptr Make(std::shared_ptr file, Sequence* sequence); + + int width() const override; + + int height() const override; + + ~DiskSequenceReader() override; + + private: + DiskSequenceReader(std::shared_ptr file, Sequence* sequence); + Sequence* sequence = nullptr; + std::shared_ptr pagDecoder; + std::shared_ptr file; + std::shared_ptr onMakeBuffer(Frame targetFrame) override; + void onReportPerformance(Performance* performance, int64_t decodingTime) override; + std::shared_ptr imageBuffer = nullptr; + std::mutex locker = {}; + tgfx::ImageInfo info = {}; + std::shared_ptr pixels = nullptr; + bool useFrontBuffer = true; + HardwareBufferRef backHardwareBuffer = nullptr; + HardwareBufferRef frontHardWareBuffer = nullptr; +}; +} // namespace pag diff --git a/src/rendering/sequences/SequenceImageProxy.cpp b/src/rendering/sequences/SequenceImageProxy.cpp index 095983952d..232ffb2e3e 100644 --- a/src/rendering/sequences/SequenceImageProxy.cpp +++ b/src/rendering/sequences/SequenceImageProxy.cpp @@ -47,6 +47,6 @@ std::shared_ptr SequenceImageProxy::makeImage(RenderCache* cache) c return nullptr; } auto file = cache->getFileByAssetID(sequence->uniqueID()); - return sequence->makeStaticImage(std::move(file)); + return sequence->makeStaticImage(std::move(file), cache->useDiskCache()); } } // namespace pag diff --git a/src/rendering/sequences/SequenceImageQueue.cpp b/src/rendering/sequences/SequenceImageQueue.cpp index 17514e1771..5dff453d10 100644 --- a/src/rendering/sequences/SequenceImageQueue.cpp +++ b/src/rendering/sequences/SequenceImageQueue.cpp @@ -20,23 +20,24 @@ namespace pag { std::unique_ptr SequenceImageQueue::MakeFrom( - std::shared_ptr sequence, PAGLayer* pagLayer) { + std::shared_ptr sequence, PAGLayer* pagLayer, bool useDiskCache) { if (sequence == nullptr || pagLayer == nullptr || sequence->staticContent()) { return nullptr; } - auto reader = sequence->makeReader(pagLayer->getFile(), pagLayer->rootFile); + auto reader = sequence->makeReader(pagLayer->getFile(), pagLayer->rootFile, useDiskCache); if (reader == nullptr) { return nullptr; } auto firstFrame = sequence->firstVisibleFrame(pagLayer->getLayer()); return std::unique_ptr( - new SequenceImageQueue(sequence, std::move(reader), firstFrame)); + new SequenceImageQueue(sequence, std::move(reader), firstFrame, useDiskCache)); } SequenceImageQueue::SequenceImageQueue(std::shared_ptr sequence, - std::shared_ptr reader, Frame firstFrame) + std::shared_ptr reader, Frame firstFrame, + bool useDiskCache) : sequence(sequence), reader(std::move(reader)), firstFrame(firstFrame), - totalFrames(sequence->duration()) { + totalFrames(sequence->duration()), useDiskCache(useDiskCache) { } void SequenceImageQueue::prepareNextImage() { @@ -51,7 +52,7 @@ void SequenceImageQueue::prepare(Frame targetFrame) { if (preparedImage != nullptr || targetFrame < 0 || targetFrame >= totalFrames) { return; } - auto image = sequence->makeFrameImage(reader, targetFrame); + auto image = sequence->makeFrameImage(reader, targetFrame, useDiskCache); preparedImage = image->makeDecoded(); preparedFrame = targetFrame; } @@ -66,7 +67,7 @@ std::shared_ptr SequenceImageQueue::getImage(Frame targetFrame) { currentFrame = preparedFrame; return currentImage; } - auto image = sequence->makeFrameImage(reader, targetFrame); + auto image = sequence->makeFrameImage(reader, targetFrame, useDiskCache); if (image == nullptr) { return nullptr; } diff --git a/src/rendering/sequences/SequenceImageQueue.h b/src/rendering/sequences/SequenceImageQueue.h index fd843f65af..d13d69d3e5 100644 --- a/src/rendering/sequences/SequenceImageQueue.h +++ b/src/rendering/sequences/SequenceImageQueue.h @@ -27,7 +27,7 @@ namespace pag { class SequenceImageQueue { public: static std::unique_ptr MakeFrom(std::shared_ptr sequence, - PAGLayer* pagLayer); + PAGLayer* pagLayer, bool useDiskCache); /** * Prepares the image of the next frame. @@ -58,9 +58,10 @@ class SequenceImageQueue { Frame preparedFrame = -1; std::shared_ptr currentImage = nullptr; std::shared_ptr preparedImage = nullptr; + bool useDiskCache = false; SequenceImageQueue(std::shared_ptr sequence, std::shared_ptr reader, - Frame firstFrame); + Frame firstFrame, bool useDiskCache); friend class RenderCache; }; diff --git a/src/rendering/sequences/SequenceInfo.cpp b/src/rendering/sequences/SequenceInfo.cpp index a6b10eabe6..42081665ec 100644 --- a/src/rendering/sequences/SequenceInfo.cpp +++ b/src/rendering/sequences/SequenceInfo.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "SequenceInfo.h" +#include "DiskSequenceReader.h" #include "rendering/sequences/BitmapSequenceReader.h" #include "rendering/sequences/VideoReader.h" #include "rendering/sequences/VideoSequenceDemuxer.h" @@ -27,9 +28,9 @@ namespace pag { static std::shared_ptr MakeSequenceImage( - std::shared_ptr generator, Sequence* sequence) { + std::shared_ptr generator, Sequence* sequence, bool useDiskCache) { auto image = tgfx::Image::MakeFrom(std::move(generator)); - if (sequence->composition->type() == CompositionType::Video) { + if (!useDiskCache && sequence->composition->type() == CompositionType::Video) { auto videoSequence = static_cast(sequence); image = image->makeRGBAAA(sequence->width, sequence->height, videoSequence->alphaStartX, videoSequence->alphaStartY); @@ -50,12 +51,18 @@ SequenceInfo::SequenceInfo(Sequence* sequence) : sequence(sequence) { } std::shared_ptr SequenceInfo::makeReader(std::shared_ptr file, - PAGFile* pagFile) { + PAGFile* pagFile, bool useDiskCache) { if (sequence == nullptr || file == nullptr) { return nullptr; } std::shared_ptr reader = nullptr; auto composition = sequence->composition; + if (useDiskCache) { + reader = DiskSequenceReader::Make(std::move(file), sequence); + if (reader) { + return reader; + } + } if (composition->type() == CompositionType::Bitmap) { reader = std::make_shared(std::move(file), static_cast(sequence)); @@ -72,7 +79,8 @@ std::shared_ptr SequenceInfo::makeReader(std::shared_ptr f return reader; } -std::shared_ptr SequenceInfo::makeStaticImage(std::shared_ptr file) { +std::shared_ptr SequenceInfo::makeStaticImage(std::shared_ptr file, + bool useDiskCache) { if (sequence == nullptr || file == nullptr || !staticContent()) { return nullptr; } @@ -83,18 +91,18 @@ std::shared_ptr SequenceInfo::makeStaticImage(std::shared_ptr width = videoSequence->getVideoWidth(); height = videoSequence->getVideoHeight(); } - auto generator = - std::make_shared(std::move(file), weakThis.lock(), width, height); - return MakeSequenceImage(std::move(generator), sequence); + auto generator = std::make_shared(std::move(file), weakThis.lock(), + width, height, useDiskCache); + return MakeSequenceImage(std::move(generator), sequence, useDiskCache); } std::shared_ptr SequenceInfo::makeFrameImage(std::shared_ptr reader, - Frame targetFrame) { + Frame targetFrame, bool useDiskCache) { if (reader == nullptr || sequence == nullptr) { return nullptr; } auto generator = std::make_shared(std::move(reader), targetFrame); - return MakeSequenceImage(std::move(generator), sequence); + return MakeSequenceImage(std::move(generator), sequence, useDiskCache); } bool SequenceInfo::staticContent() const { diff --git a/src/rendering/sequences/SequenceInfo.h b/src/rendering/sequences/SequenceInfo.h index ccd756eef7..bcb94a3efa 100644 --- a/src/rendering/sequences/SequenceInfo.h +++ b/src/rendering/sequences/SequenceInfo.h @@ -32,11 +32,13 @@ class SequenceInfo { virtual ~SequenceInfo() = default; virtual std::shared_ptr makeReader(std::shared_ptr file, - PAGFile* pagFile = nullptr); + PAGFile* pagFile = nullptr, + bool useDiskCache = false); - virtual std::shared_ptr makeStaticImage(std::shared_ptr file); + virtual std::shared_ptr makeStaticImage(std::shared_ptr file, + bool useDiskCache); virtual std::shared_ptr makeFrameImage(std::shared_ptr reader, - Frame targetFrame); + Frame targetFrame, bool useDiskCache); virtual bool staticContent() const; virtual ID uniqueID() const; @@ -59,8 +61,9 @@ class SequenceInfo { class StaticSequenceGenerator : public tgfx::ImageGenerator { public: StaticSequenceGenerator(std::shared_ptr file, std::shared_ptr info, int width, - int height) - : tgfx::ImageGenerator(width, height), file(std::move(file)), info(info) { + int height, bool useDiskCache) + : tgfx::ImageGenerator(width, height), file(std::move(file)), info(info), + useDiskCache(useDiskCache) { } bool isAlphaOnly() const override { @@ -75,13 +78,14 @@ class StaticSequenceGenerator : public tgfx::ImageGenerator { protected: std::shared_ptr onMakeBuffer(bool) const override { - auto reader = info->makeReader(file); + auto reader = info->makeReader(file, nullptr, useDiskCache); return reader->readBuffer(0); } private: std::shared_ptr file = nullptr; std::shared_ptr info = nullptr; + bool useDiskCache = false; }; class SequenceFrameGenerator : public tgfx::ImageGenerator {