From 1c40442ec4f0b79fd336f57cdd0235f6426c7360 Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Tue, 24 Sep 2019 20:42:45 +0000 Subject: [PATCH] Bug 1551088 - Part 5. Expose SurfaceFilter input row directly to avoid copy if possible. r=tnikkel Some filters can do the copy of the given data into the working buffer as part of the filter operation. For those that cannot, we will just copy the data first, and then advance the row. Differential Revision: https://phabricator.services.mozilla.com/D46448 --HG-- extra : moz-landing-system : lando --- image/DownscalingFilter.h | 12 +++++++-- image/SurfaceFilters.h | 30 ++++++++++++++++++--- image/SurfacePipe.cpp | 5 ++++ image/SurfacePipe.h | 55 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/image/DownscalingFilter.h b/image/DownscalingFilter.h index 6a2b93c69d5b..41d796f45057 100644 --- a/image/DownscalingFilter.h +++ b/image/DownscalingFilter.h @@ -78,6 +78,10 @@ class DownscalingFilter final : public SurfaceFilter { MOZ_CRASH(); return nullptr; } + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { + MOZ_CRASH(); + return nullptr; + } uint8_t* DoAdvanceRow() override { MOZ_CRASH(); return nullptr; @@ -206,7 +210,7 @@ class DownscalingFilter final : public SurfaceFilter { return GetRowPointer(); } - uint8_t* DoAdvanceRow() override { + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { if (mInputRow >= mInputSize.height) { NS_WARNING("Advancing DownscalingFilter past the end of the input"); return nullptr; @@ -226,7 +230,7 @@ class DownscalingFilter final : public SurfaceFilter { if (mInputRow == inputRowToRead) { MOZ_RELEASE_ASSERT(mRowsInWindow < mWindowCapacity, "Need more rows than capacity!"); - mXFilter.ConvolveHorizontally(mRowBuffer.get(), mWindow[mRowsInWindow++], + mXFilter.ConvolveHorizontally(aInputRow, mWindow[mRowsInWindow++], mHasAlpha); } @@ -249,6 +253,10 @@ class DownscalingFilter final : public SurfaceFilter { return mInputRow < mInputSize.height ? GetRowPointer() : nullptr; } + uint8_t* DoAdvanceRow() override { + return DoAdvanceRowFromBuffer(mRowBuffer.get()); + } + private: uint8_t* GetRowPointer() const { return mRowBuffer.get(); } diff --git a/image/SurfaceFilters.h b/image/SurfaceFilters.h index a3aff1e6955c..7a8bdbdefcc0 100644 --- a/image/SurfaceFilters.h +++ b/image/SurfaceFilters.h @@ -80,12 +80,16 @@ class ColorManagementFilter final : public SurfaceFilter { protected: uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); } - uint8_t* DoAdvanceRow() override { - uint8_t* rowPtr = mNext.CurrentRowPointer(); - qcms_transform_data(mTransform, rowPtr, rowPtr, mNext.InputSize().width); + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { + qcms_transform_data(mTransform, aInputRow, mNext.CurrentRowPointer(), + mNext.InputSize().width); return mNext.AdvanceRow(); } + uint8_t* DoAdvanceRow() override { + return DoAdvanceRowFromBuffer(mNext.CurrentRowPointer()); + } + Next mNext; /// The next SurfaceFilter in the chain. qcms_transform* mTransform; @@ -182,6 +186,11 @@ class DeinterlacingFilter final : public SurfaceFilter { return GetRowPointer(mOutputRow); } + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { + CopyInputRow(aInputRow); + return DoAdvanceRow(); + } + uint8_t* DoAdvanceRow() override { if (mPass >= 4) { return nullptr; // We already finished all passes. @@ -638,6 +647,11 @@ class BlendAnimationFilter final : public SurfaceFilter { return nullptr; // We're done. } + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { + CopyInputRow(aInputRow); + return DoAdvanceRow(); + } + uint8_t* DoAdvanceRow() override { uint8_t* rowPtr = nullptr; @@ -912,6 +926,11 @@ class RemoveFrameRectFilter final : public SurfaceFilter { return nullptr; // We're done. } + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { + CopyInputRow(aInputRow); + return DoAdvanceRow(); + } + uint8_t* DoAdvanceRow() override { uint8_t* rowPtr = nullptr; @@ -1097,6 +1116,11 @@ class ADAM7InterpolatingFilter final : public SurfaceFilter { return mCurrentRow.get(); } + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override { + CopyInputRow(aInputRow); + return DoAdvanceRow(); + } + uint8_t* DoAdvanceRow() override { MOZ_ASSERT(0 < mPass && mPass <= 7, "Invalid pass"); diff --git a/image/SurfacePipe.cpp b/image/SurfacePipe.cpp index f5595e2d5c7b..1fbb25470d40 100644 --- a/image/SurfacePipe.cpp +++ b/image/SurfacePipe.cpp @@ -36,6 +36,11 @@ uint8_t* AbstractSurfaceSink::DoResetToFirstRow() { return GetRowPointer(); } +uint8_t* AbstractSurfaceSink::DoAdvanceRowFromBuffer(const uint8_t* aInputRow) { + CopyInputRow(aInputRow); + return DoAdvanceRow(); +} + uint8_t* AbstractSurfaceSink::DoAdvanceRow() { if (mRow >= uint32_t(InputSize().height)) { return nullptr; diff --git a/image/SurfacePipe.h b/image/SurfacePipe.h index 224680f29c0e..7bcadf050b39 100644 --- a/image/SurfacePipe.h +++ b/image/SurfacePipe.h @@ -127,6 +127,19 @@ class SurfaceFilter { return mRowPointer; } + /** + * Called by WriteBuffer() to advance this filter to the next row, if the + * supplied row is a full row. + * + * @return a pointer to the buffer for the next row, or nullptr to indicate + * that we've finished the entire surface. + */ + uint8_t* AdvanceRow(const uint8_t* aInputRow) { + mCol = 0; + mRowPointer = DoAdvanceRowFromBuffer(aInputRow); + return mRowPointer; + } + /// @return a pointer to the buffer for the current row. uint8_t* CurrentRowPointer() const { return mRowPointer; } @@ -269,7 +282,22 @@ class SurfaceFilter { */ template WriteState WriteBuffer(const PixelType* aSource) { - return WriteBuffer(aSource, 0, mInputSize.width); + MOZ_ASSERT(mPixelSize == 1 || mPixelSize == 4); + MOZ_ASSERT_IF(mPixelSize == 1, sizeof(PixelType) == sizeof(uint8_t)); + MOZ_ASSERT_IF(mPixelSize == 4, sizeof(PixelType) == sizeof(uint32_t)); + + if (IsSurfaceFinished()) { + return WriteState::FINISHED; // Already done. + } + + if (MOZ_UNLIKELY(!aSource)) { + NS_WARNING("Passed a null pointer to WriteBuffer"); + return WriteState::FAILURE; + } + + AdvanceRow(reinterpret_cast(aSource)); + return IsSurfaceFinished() ? WriteState::FINISHED + : WriteState::NEED_MORE_DATA; } /** @@ -440,6 +468,16 @@ class SurfaceFilter { */ virtual uint8_t* DoResetToFirstRow() = 0; + /** + * Called by AdvanceRow() to actually advance this filter to the next row. + * + * @param aInputRow The input row supplied by the decoder. + * + * @return a pointer to the buffer for the next row, or nullptr to indicate + * that we've finished the entire surface. + */ + virtual uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) = 0; + /** * Called by AdvanceRow() to actually advance this filter to the next row. * @@ -470,6 +508,20 @@ class SurfaceFilter { ResetToFirstRow(); } + /** + * Called by subclasses' DoAdvanceRowFromBuffer() methods to copy a decoder + * supplied row buffer into its internal row pointer. Ideally filters at the + * top of the filter pipeline are able to consume the decoder row buffer + * directly without the extra copy prior to performing its transformation. + * + * @param aInputRow The input row supplied by the decoder. + */ + void CopyInputRow(const uint8_t* aInputRow) { + MOZ_ASSERT(aInputRow); + MOZ_ASSERT(mCol == 0); + memcpy(mRowPointer, aInputRow, mPixelSize * mInputSize.width); + } + private: /** * An internal method used to implement WritePixelBlocks. This method writes @@ -719,6 +771,7 @@ class AbstractSurfaceSink : public SurfaceFilter { protected: uint8_t* DoResetToFirstRow() final; + uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) final; uint8_t* DoAdvanceRow() final; virtual uint8_t* GetRowPointer() const = 0;