diff --git a/include/tgfx/core/Mask.h b/include/tgfx/core/Mask.h index 8543422c..c52a339b 100644 --- a/include/tgfx/core/Mask.h +++ b/include/tgfx/core/Mask.h @@ -144,6 +144,6 @@ class Mask { bool fillText(const GlyphRunList* glyphRunList, const Stroke* stroke = nullptr); friend class ImageReader; - friend class TextRasterizer; + friend class GlyphRasterizer; }; } // namespace tgfx diff --git a/src/gpu/opengl/GLVertexArrayCreateTask.h b/src/core/DataProvider.cpp similarity index 64% rename from src/gpu/opengl/GLVertexArrayCreateTask.h rename to src/core/DataProvider.cpp index d8780920..7c63c0a7 100644 --- a/src/gpu/opengl/GLVertexArrayCreateTask.h +++ b/src/core/DataProvider.cpp @@ -2,7 +2,7 @@ // // Tencent is pleased to support the open source community by making tgfx available. // -// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. // // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,15 +16,23 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once - -#include "gpu/tasks/ResourceTask.h" +#include "DataProvider.h" namespace tgfx { -class GLVertexArrayCreateTask : public ResourceTask { +class DataWrapper : public DataProvider { public: - explicit GLVertexArrayCreateTask(UniqueKey uniqueKey); + explicit DataWrapper(std::shared_ptr data) : data(std::move(data)) { + } + + std::shared_ptr getData() const override { + return data; + } - std::shared_ptr onMakeResource(Context* context) override; + private: + std::shared_ptr data = nullptr; }; -} // namespace tgfx + +std::shared_ptr DataProvider::Wrap(std::shared_ptr data) { + return std::make_shared(std::move(data)); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/core/DataProvider.h b/src/core/DataProvider.h index 9c44d5ac..46fb2748 100644 --- a/src/core/DataProvider.h +++ b/src/core/DataProvider.h @@ -26,6 +26,11 @@ namespace tgfx { */ class DataProvider { public: + /** + * Wraps the existing data into a DataProvider. + */ + static std::shared_ptr Wrap(std::shared_ptr data); + virtual ~DataProvider() = default; /** diff --git a/src/core/ImageDecoder.cpp b/src/core/ImageDecoder.cpp index 1f8eab56..04e90765 100644 --- a/src/core/ImageDecoder.cpp +++ b/src/core/ImageDecoder.cpp @@ -17,7 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "ImageDecoder.h" -#include "tgfx/core/Task.h" +#include "core/utils/DataTask.h" namespace tgfx { @@ -74,22 +74,12 @@ class ImageGeneratorWrapper : public ImageDecoder { bool tryHardware = true; }; -struct ImageBufferHolder { - std::shared_ptr imageBuffer; -}; - class AsyncImageDecoder : public ImageDecoder { public: AsyncImageDecoder(std::shared_ptr generator, bool tryHardware) : imageGenerator(std::move(generator)) { - holder = std::make_shared(); - task = Task::Run([result = holder, generator = imageGenerator, tryHardware]() { - result->imageBuffer = generator->makeBuffer(tryHardware); - }); - } - - ~AsyncImageDecoder() override { - task->cancel(); + task = DataTask::Run( + [generator = imageGenerator, tryHardware]() { return generator->makeBuffer(tryHardware); }); } int width() const override { @@ -105,14 +95,12 @@ class AsyncImageDecoder : public ImageDecoder { } std::shared_ptr decode() const override { - task->wait(); - return holder->imageBuffer; + return task->wait(); } private: std::shared_ptr imageGenerator = nullptr; - std::shared_ptr holder = nullptr; - std::shared_ptr task = nullptr; + std::shared_ptr> task = nullptr; }; std::shared_ptr ImageDecoder::Wrap(std::shared_ptr imageBuffer) { diff --git a/src/core/ImageFilter.cpp b/src/core/ImageFilter.cpp index 9490b174..c1434dd2 100644 --- a/src/core/ImageFilter.cpp +++ b/src/core/ImageFilter.cpp @@ -53,7 +53,7 @@ std::shared_ptr ImageFilter::lockTextureProxy(std::shared_ptrgetTextureProxy(); } diff --git a/src/core/Mask.cpp b/src/core/Mask.cpp index a9c7311e..a23fd1e5 100644 --- a/src/core/Mask.cpp +++ b/src/core/Mask.cpp @@ -19,7 +19,6 @@ #include "tgfx/core/Mask.h" #include "core/GlyphRunList.h" #include "core/ImageStream.h" -#include "tgfx/core/PathEffect.h" namespace tgfx { void Mask::fillPath(const Path& path, const Stroke* stroke) { diff --git a/src/core/Matrix.cpp b/src/core/Matrix.cpp index ad7e7623..a71b3213 100644 --- a/src/core/Matrix.cpp +++ b/src/core/Matrix.cpp @@ -77,7 +77,7 @@ void Matrix::setTranslate(float tx, float ty) { } } -static inline float sdot(float a, float b, float c, float d) { +inline float sdot(float a, float b, float c, float d) { return a * b + c * d; } diff --git a/src/core/Pixmap.cpp b/src/core/Pixmap.cpp index e741dada..55a43601 100644 --- a/src/core/Pixmap.cpp +++ b/src/core/Pixmap.cpp @@ -23,11 +23,11 @@ namespace tgfx { -static inline void* AddOffset(void* pixels, size_t offset) { +inline void* AddOffset(void* pixels, size_t offset) { return reinterpret_cast(pixels) + offset; } -static inline const void* AddOffset(const void* pixels, size_t offset) { +inline const void* AddOffset(const void* pixels, size_t offset) { return reinterpret_cast(pixels) + offset; } diff --git a/src/core/Rasterizer.cpp b/src/core/Rasterizer.cpp index 27264f63..a3e1cfae 100644 --- a/src/core/Rasterizer.cpp +++ b/src/core/Rasterizer.cpp @@ -19,55 +19,37 @@ #include "Rasterizer.h" namespace tgfx { -class TextRasterizer : public Rasterizer { +class GlyphRasterizer : public Rasterizer { public: - TextRasterizer(std::shared_ptr glyphRunList, const ISize& clipSize, - const Matrix& matrix, bool antiAlias, const Stroke* stroke) + GlyphRasterizer(std::shared_ptr glyphRunList, const ISize& clipSize, + const Matrix& matrix, bool antiAlias, const Stroke* stroke) : Rasterizer(clipSize, matrix, antiAlias, stroke), glyphRunList(std::move(glyphRunList)) { } protected: - void onRasterize(Mask* mask, const Stroke* stroke) const override { + std::shared_ptr onMakeBuffer(bool tryHardware) const override { + auto mask = Mask::Make(width(), height(), tryHardware); + if (!mask) { + return nullptr; + } + mask->setAntiAlias(antiAlias); + mask->setMatrix(matrix); mask->fillText(glyphRunList.get(), stroke); + return mask->makeBuffer(); } private: std::shared_ptr glyphRunList = nullptr; }; -class PathRasterizer : public Rasterizer { - public: - PathRasterizer(Path path, const ISize& clipSize, const Matrix& matrix, bool antiAlias, - const Stroke* stroke) - : Rasterizer(clipSize, matrix, antiAlias, stroke), path(std::move(path)) { - } - - protected: - void onRasterize(Mask* mask, const Stroke* stroke) const override { - mask->fillPath(path, stroke); - } - - private: - Path path = {}; -}; - std::shared_ptr Rasterizer::MakeFrom(std::shared_ptr glyphRunList, const ISize& clipSize, const Matrix& matrix, bool antiAlias, const Stroke* stroke) { if (glyphRunList == nullptr || clipSize.isEmpty()) { return nullptr; } - return std::make_shared(std::move(glyphRunList), clipSize, matrix, antiAlias, - stroke); -} - -std::shared_ptr Rasterizer::MakeFrom(Path path, const ISize& clipSize, - const Matrix& matrix, bool antiAlias, - const Stroke* stroke) { - if (path.isEmpty() || clipSize.isEmpty()) { - return nullptr; - } - return std::make_shared(std::move(path), clipSize, matrix, antiAlias, stroke); + return std::make_shared(std::move(glyphRunList), clipSize, matrix, antiAlias, + stroke); } Rasterizer::Rasterizer(const ISize& clipSize, const Matrix& matrix, bool antiAlias, const Stroke* s) @@ -88,15 +70,4 @@ bool Rasterizer::asyncSupport() const { return true; #endif } - -std::shared_ptr Rasterizer::onMakeBuffer(bool tryHardware) const { - auto mask = Mask::Make(width(), height(), tryHardware); - if (!mask) { - return nullptr; - } - mask->setAntiAlias(antiAlias); - mask->setMatrix(matrix); - onRasterize(mask.get(), stroke); - return mask->makeBuffer(); -} } // namespace tgfx diff --git a/src/core/Rasterizer.h b/src/core/Rasterizer.h index 0c416f5e..be0c1125 100644 --- a/src/core/Rasterizer.h +++ b/src/core/Rasterizer.h @@ -23,27 +23,28 @@ #include "tgfx/core/Mask.h" namespace tgfx { +class ShapeRasterizer; + /** - * An ImageGenerator that can take vector graphics (paths, texts) and convert them into a raster - * image. + * Rasterizer is the base class for converting vector graphics (shapes and glyphs) into their + * rasterized forms. */ class Rasterizer : public ImageGenerator { public: /** - * Creates a Rasterizer from a TextBlob. + * Creates a Rasterizer from a GlyphRunList. */ static std::shared_ptr MakeFrom(std::shared_ptr glyphRunList, const ISize& clipSize, const Matrix& matrix, bool antiAlias, const Stroke* stroke = nullptr); - /** * Creates a Rasterizer from a Path. */ - static std::shared_ptr MakeFrom(Path path, const ISize& clipSize, - const Matrix& matrix, bool antiAlias, - const Stroke* stroke = nullptr); + static std::shared_ptr MakeFrom(Path path, const ISize& clipSize, + const Matrix& matrix, bool antiAlias, + const Stroke* stroke = nullptr); - virtual ~Rasterizer(); + ~Rasterizer() override; bool isAlphaOnly() const override { return true; @@ -52,15 +53,10 @@ class Rasterizer : public ImageGenerator { bool asyncSupport() const override; protected: - Rasterizer(const ISize& clipSize, const Matrix& matrix, bool antiAlias, const Stroke* stroke); - - std::shared_ptr onMakeBuffer(bool tryHardware) const override; - - virtual void onRasterize(Mask* mask, const Stroke* stroke) const = 0; - - private: Matrix matrix = Matrix::I(); bool antiAlias = true; Stroke* stroke = nullptr; + + Rasterizer(const ISize& clipSize, const Matrix& matrix, bool antiAlias, const Stroke* stroke); }; } // namespace tgfx diff --git a/src/gpu/opengl/GLVertexArrayCreateTask.cpp b/src/core/ShapeBuffer.cpp similarity index 62% rename from src/gpu/opengl/GLVertexArrayCreateTask.cpp rename to src/core/ShapeBuffer.cpp index e3e96e3b..64dec754 100644 --- a/src/gpu/opengl/GLVertexArrayCreateTask.cpp +++ b/src/core/ShapeBuffer.cpp @@ -2,7 +2,7 @@ // // Tencent is pleased to support the open source community by making tgfx available. // -// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. // // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,19 +16,20 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "GLVertexArrayCreateTask.h" -#include "GLVertexArray.h" +#include "ShapeBuffer.h" namespace tgfx { -GLVertexArrayCreateTask::GLVertexArrayCreateTask(UniqueKey uniqueKey) - : ResourceTask(std::move(uniqueKey)) { +std::shared_ptr ShapeBuffer::MakeFrom(std::shared_ptr triangles) { + if (triangles == nullptr || triangles->empty()) { + return nullptr; + } + return std::shared_ptr(new ShapeBuffer(std::move(triangles))); } -std::shared_ptr GLVertexArrayCreateTask::onMakeResource(Context* context) { - auto vertexArray = GLVertexArray::Make(context); - if (vertexArray == nullptr) { - LOGE("GLVertexArrayCreateTask::onMakeResource() Failed to create the vertex array!"); +std::shared_ptr ShapeBuffer::MakeFrom(std::shared_ptr imageBuffer) { + if (imageBuffer == nullptr) { + return nullptr; } - return vertexArray; + return std::shared_ptr(new ShapeBuffer(std::move(imageBuffer))); } } // namespace tgfx diff --git a/src/core/ShapeBuffer.h b/src/core/ShapeBuffer.h new file mode 100644 index 00000000..cfd62272 --- /dev/null +++ b/src/core/ShapeBuffer.h @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "tgfx/core/Data.h" +#include "tgfx/core/ImageBuffer.h" + +namespace tgfx { +/** + * ShapeBuffer is a container for the rasterized shape data, either as triangles or as an image + * buffer. + */ +class ShapeBuffer { + public: + /** + * Creates a new ShapeBuffer from a set of triangles. Returns nullptr if the triangles are empty. + */ + static std::shared_ptr MakeFrom(std::shared_ptr triangles); + + /** + * Creates a new ShapeBuffer from an image buffer. Returns nullptr if the image buffer is nullptr. + */ + static std::shared_ptr MakeFrom(std::shared_ptr imageBuffer); + + /** + * Returns the triangles data of the shape buffer. Returns nullptr if the shape buffer is backed + * by an image buffer. + */ + std::shared_ptr triangles() const { + return _triangles; + } + + /** + * Returns the image buffer of the shape buffer. Returns nullptr if the shape buffer is backed by + * triangles. + */ + std::shared_ptr imageBuffer() const { + return _imageBuffer; + } + + private: + std::shared_ptr _triangles = nullptr; + std::shared_ptr _imageBuffer = nullptr; + + explicit ShapeBuffer(std::shared_ptr triangles) : _triangles(std::move(triangles)) { + } + + explicit ShapeBuffer(std::shared_ptr imageBuffer) + : _imageBuffer(std::move(imageBuffer)) { + } +}; +} // namespace tgfx diff --git a/src/core/ShapeRasterizer.cpp b/src/core/ShapeRasterizer.cpp new file mode 100644 index 00000000..7462482b --- /dev/null +++ b/src/core/ShapeRasterizer.cpp @@ -0,0 +1,103 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "ShapeRasterizer.h" +#include "core/PathTriangulator.h" +#include "tgfx/core/Mask.h" + +namespace tgfx { +// https://chromium-review.googlesource.com/c/chromium/src/+/1099564/ +static constexpr int AA_TESSELLATOR_MAX_VERB_COUNT = 100; +// A factor used to estimate the memory size of a tessellated path, based on the average value of +// Buffer.size() / Path.countPoints() from 4300+ tessellated path data. +static constexpr int AA_TESSELLATOR_BUFFER_SIZE_FACTOR = 170; + +bool ShapeRasterizer::ShouldTriangulatePath(const Path& path) { + if (path.countVerbs() <= AA_TESSELLATOR_MAX_VERB_COUNT) { + return true; + } + auto bounds = path.getBounds(); + auto width = static_cast(ceilf(bounds.width())); + auto height = static_cast(ceilf(bounds.height())); + return path.countPoints() * AA_TESSELLATOR_BUFFER_SIZE_FACTOR <= width * height; +} + +std::shared_ptr Rasterizer::MakeFrom(Path path, const ISize& clipSize, + const Matrix& matrix, bool antiAlias, + const Stroke* stroke) { + if (path.isEmpty() || clipSize.isEmpty()) { + return nullptr; + } + return std::shared_ptr( + new ShapeRasterizer(std::move(path), clipSize, matrix, antiAlias, stroke)); +} + +ShapeRasterizer::ShapeRasterizer(Path path, const ISize& clipSize, const Matrix& matrix, + bool antiAlias, const Stroke* stroke) + : Rasterizer(clipSize, matrix, antiAlias, stroke), path(std::move(path)) { +} + +std::shared_ptr ShapeRasterizer::makeRasterized(bool tryHardware) const { + auto finalPath = getFinalPath(); + if (ShouldTriangulatePath(finalPath)) { + return ShapeBuffer::MakeFrom(makeTriangles(finalPath)); + } + return ShapeBuffer::MakeFrom(makeImageBuffer(finalPath, tryHardware)); +} + +std::shared_ptr ShapeRasterizer::onMakeBuffer(bool tryHardware) const { + auto finalPath = getFinalPath(); + return makeImageBuffer(finalPath, tryHardware); +} + +Path ShapeRasterizer::getFinalPath() const { + auto finalPath = path; + if (stroke != nullptr) { + stroke->applyToPath(&finalPath); + } + finalPath.transform(matrix); + return finalPath; +} + +std::shared_ptr ShapeRasterizer::makeTriangles(const Path& finalPath) const { + std::vector vertices = {}; + size_t count = 0; + auto bounds = Rect::MakeWH(width(), height()); + if (antiAlias) { + count = PathTriangulator::ToAATriangles(finalPath, bounds, &vertices); + } else { + count = PathTriangulator::ToTriangles(finalPath, bounds, &vertices); + } + if (count == 0) { + // The path is not a filled path, or it is invisible. + return nullptr; + } + return Data::MakeWithCopy(vertices.data(), vertices.size() * sizeof(float)); +} + +std::shared_ptr ShapeRasterizer::makeImageBuffer(const Path& finalPath, + bool tryHardware) const { + auto mask = Mask::Make(width(), height(), tryHardware); + if (!mask) { + return nullptr; + } + mask->setAntiAlias(antiAlias); + mask->fillPath(finalPath); + return mask->makeBuffer(); +} +} // namespace tgfx diff --git a/src/core/ShapeRasterizer.h b/src/core/ShapeRasterizer.h new file mode 100644 index 00000000..2178de4a --- /dev/null +++ b/src/core/ShapeRasterizer.h @@ -0,0 +1,61 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "core/Rasterizer.h" +#include "core/ShapeBuffer.h" +#include "tgfx/core/Data.h" + +namespace tgfx { +/** + * ShapeRasterizer converts a shape into its rasterized form. + */ +class ShapeRasterizer : public Rasterizer { + public: + /** + * Returns true if the path should be triangulated instead of rasterized as an image. + */ + static bool ShouldTriangulatePath(const Path& path); + + /** + * Rasterizes the shape into a ShapeBuffer. Unlike the makeBuffer() method, which always returns + * an image buffer, this method returns a ShapeBuffer that may contain either a triangle mesh or + * an image buffer, depending on the shape's complexity. This method aims to balance performance + * and memory usage. Returns nullptr if rasterization fails. + */ + std::shared_ptr makeRasterized(bool tryHardware = true) const; + + protected: + std::shared_ptr onMakeBuffer(bool tryHardware) const override; + + private: + Path path; + + ShapeRasterizer(Path path, const ISize& clipSize, const Matrix& matrix, bool antiAlias, + const Stroke* stroke); + + Path getFinalPath() const; + + std::shared_ptr makeTriangles(const Path& finalPath) const; + + std::shared_ptr makeImageBuffer(const Path& finalPath, bool tryHardware) const; + + friend class Rasterizer; +}; +} // namespace tgfx diff --git a/src/core/codecs/webp/WebpUtility.cpp b/src/core/codecs/webp/WebpUtility.cpp index 456b1551..40efeec4 100644 --- a/src/core/codecs/webp/WebpUtility.cpp +++ b/src/core/codecs/webp/WebpUtility.cpp @@ -70,17 +70,17 @@ static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = { 0x0001ff, 0x0003ff, 0x0007ff, 0x000fff, 0x001fff, 0x003fff, 0x007fff, 0x00ffff, 0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff}; -static inline int GetLE16(const uint8_t* const data) { +inline int GetLE16(const uint8_t* const data) { return static_cast((data[0] << 0) | (data[1] << 8)); } -static inline int GetLE24(const uint8_t* const data) { +inline int GetLE24(const uint8_t* const data) { return GetLE16(data) | (data[2] << 16); } -static inline uint32_t GetLE32(const uint8_t* const data) { +inline uint32_t GetLE32(const uint8_t* const data) { return (uint32_t)GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16); } -static inline void Skip(WebpFile* const ptr, size_t size) { +inline void Skip(WebpFile* const ptr, size_t size) { ptr->_start += size; } static bool VP8CheckSignature(const uint8_t* const data) { @@ -92,13 +92,13 @@ static bool VP8LCheckSignature(const uint8_t* const data) { } // Return the prefetched bits, so they can be looked up. -static inline uint32_t VP8LPrefetchBits(VP8LBitReader* const br) { +inline uint32_t VP8LPrefetchBits(VP8LBitReader* const br) { return (uint32_t)(br->val_ >> (br->bit_pos_ & (VP8L_LBITS - 1))); } // Returns true if there was an attempt at reading bit past the end of // the buffer. Doesn't set br->eos_ flag. -static inline int VP8LIsEndOfStream(const VP8LBitReader* const br) { +inline int VP8LIsEndOfStream(const VP8LBitReader* const br) { ASSERT(br->pos_ <= br->len_); return br->eos_ || ((br->pos_ == br->len_) && (br->bit_pos_ > VP8L_LBITS)); } diff --git a/src/core/filters/BlurImageFilter.cpp b/src/core/filters/BlurImageFilter.cpp index 647dae9e..d711e3fc 100644 --- a/src/core/filters/BlurImageFilter.cpp +++ b/src/core/filters/BlurImageFilter.cpp @@ -86,7 +86,7 @@ BlurImageFilter::BlurImageFilter(Point blurOffset, float downScaling, int iterat void BlurImageFilter::draw(std::shared_ptr renderTarget, std::unique_ptr imageProcessor, - const Rect& imageBounds, bool isDown) const { + const Rect& imageBounds, bool isDown, uint32_t renderFlags) const { auto dstWidth = static_cast(renderTarget->width()); auto dstHeight = static_cast(renderTarget->height()); auto uvMatrix = @@ -96,7 +96,7 @@ void BlurImageFilter::draw(std::shared_ptr renderTarget, auto blurProcessor = DualBlurFragmentProcessor::Make(isDown ? DualBlurPassMode::Down : DualBlurPassMode::Up, std::move(imageProcessor), blurOffset, texelSize); - OpContext opContext(std::move(renderTarget)); + OpContext opContext(std::move(renderTarget), renderFlags); opContext.fillWithFP(std::move(blurProcessor), uvMatrix, true); } @@ -133,14 +133,14 @@ std::shared_ptr BlurImageFilter::lockTextureProxy( if (renderTarget == nullptr) { return nullptr; } - draw(renderTarget, std::move(processor), imageBounds, true); + draw(renderTarget, std::move(processor), imageBounds, true, args.renderFlags); lastRenderTarget = renderTarget; imageBounds = Rect::MakeWH(downWidth, downHeight); } for (size_t i = renderTargets.size(); i > 0; --i) { processor = TextureEffect::Make(lastRenderTarget->getTextureProxy(), {}, nullptr, isAlphaOnly); auto renderTarget = renderTargets[i - 1]; - draw(renderTarget, std::move(processor), imageBounds, false); + draw(renderTarget, std::move(processor), imageBounds, false, args.renderFlags); lastRenderTarget = renderTarget; imageBounds = Rect::MakeWH(renderTarget->width(), renderTarget->height()); } diff --git a/src/core/filters/BlurImageFilter.h b/src/core/filters/BlurImageFilter.h index c86ac81d..607de630 100644 --- a/src/core/filters/BlurImageFilter.h +++ b/src/core/filters/BlurImageFilter.h @@ -39,8 +39,8 @@ class BlurImageFilter : public ImageFilter { const Matrix* uvMatrix) const override; void draw(std::shared_ptr renderTarget, - std::unique_ptr imageProcessor, const Rect& imageBounds, - bool isDown) const; + std::unique_ptr imageProcessor, const Rect& imageBounds, bool isDown, + uint32_t renderFlags) const; private: Point blurOffset; diff --git a/src/core/images/Image.cpp b/src/core/images/Image.cpp index 5ba6662c..54b92b7d 100644 --- a/src/core/images/Image.cpp +++ b/src/core/images/Image.cpp @@ -257,7 +257,7 @@ std::shared_ptr Image::lockTextureProxy(const TPArgs& args, if (processor == nullptr) { return nullptr; } - OpContext opContext(renderTarget); + OpContext opContext(renderTarget, args.renderFlags); opContext.fillWithFP(std::move(processor), Matrix::I(), true); return textureProxy; } diff --git a/src/core/images/MipmapImage.cpp b/src/core/images/MipmapImage.cpp index 2730b048..4abf14a1 100644 --- a/src/core/images/MipmapImage.cpp +++ b/src/core/images/MipmapImage.cpp @@ -21,20 +21,13 @@ #include "core/utils/UniqueID.h" namespace tgfx { -static BytesKey MakeMipmapBytesKey() { - auto mipmapFlag = UniqueID::Next(); - BytesKey bytesKey(1); - bytesKey.write(mipmapFlag); - return bytesKey; -} - std::shared_ptr MipmapImage::MakeFrom(std::shared_ptr source) { if (source == nullptr) { return nullptr; } DEBUG_ASSERT(!source->hasMipmaps()); - static const auto MipmapBytesKey = MakeMipmapBytesKey(); - auto uniqueKey = UniqueKey::Combine(source->uniqueKey, MipmapBytesKey); + static const auto MipmapFlag = UniqueID::Next(); + auto uniqueKey = UniqueKey::Append(source->uniqueKey, &MipmapFlag, 1); auto image = std::shared_ptr(new MipmapImage(std::move(uniqueKey), std::move(source))); image->weakThis = image; diff --git a/src/core/images/RGBAAAImage.cpp b/src/core/images/RGBAAAImage.cpp index c6027e5d..db261c78 100644 --- a/src/core/images/RGBAAAImage.cpp +++ b/src/core/images/RGBAAAImage.cpp @@ -20,7 +20,7 @@ #include "core/utils/Log.h" #include "core/utils/NeedMipmaps.h" #include "gpu/TPArgs.h" -#include "gpu/ops/FillRectOp.h" +#include "gpu/ops/RectDrawOp.h" #include "gpu/processors/TextureEffect.h" #include "gpu/processors/TiledTextureEffect.h" diff --git a/src/core/images/RasterImage.cpp b/src/core/images/RasterImage.cpp index e9aff3e1..61303436 100644 --- a/src/core/images/RasterImage.cpp +++ b/src/core/images/RasterImage.cpp @@ -20,7 +20,6 @@ #include "gpu/OpContext.h" #include "gpu/ProxyProvider.h" #include "gpu/ops/DrawOp.h" -#include "gpu/processors/FragmentProcessor.h" #include "tgfx/core/RenderFlags.h" namespace tgfx { diff --git a/src/core/images/ResourceImage.cpp b/src/core/images/ResourceImage.cpp index a0a29606..1f2d6184 100644 --- a/src/core/images/ResourceImage.cpp +++ b/src/core/images/ResourceImage.cpp @@ -18,7 +18,7 @@ #include "ResourceImage.h" #include "core/images/MipmapImage.h" -#include "gpu/ops/FillRectOp.h" +#include "gpu/ops/RectDrawOp.h" #include "gpu/processors/TiledTextureEffect.h" namespace tgfx { diff --git a/src/core/utils/DataTask.h b/src/core/utils/DataTask.h new file mode 100644 index 00000000..984ce92d --- /dev/null +++ b/src/core/utils/DataTask.h @@ -0,0 +1,65 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "tgfx/core/Task.h" + +namespace tgfx { +/** + * The DataTask class handles running a generator function concurrently, which produces a value of + * type T. The generator function runs asynchronously, and its result is stored in a holder object. + */ +template +class DataTask { + public: + struct Holder { + std::shared_ptr data; + }; + + /** + * Schedules an asynchronous task to run the generator function immediately and store the result + * in the holder. + */ + static std::shared_ptr> Run(std::function()> generator) { + return std::shared_ptr>(new DataTask(std::move(generator))); + } + + ~DataTask() { + task->cancel(); + } + + /** + * Waits for the generator function to finish executing and returns the result. After this method + * is called, the task is considered finished and cannot be restarted. + */ + std::shared_ptr wait() const { + task->wait(); + return holder->data; + } + + private: + std::shared_ptr holder = std::make_shared(); + std::shared_ptr task = nullptr; + + explicit DataTask(std::function()> generator) { + task = Task::Run( + [holder = holder, generator = std::move(generator)]() { holder->data = generator(); }); + } +}; +} // namespace tgfx diff --git a/src/core/utils/MathExtra.h b/src/core/utils/MathExtra.h index 8b7a7d88..c638be76 100644 --- a/src/core/utils/MathExtra.h +++ b/src/core/utils/MathExtra.h @@ -26,33 +26,33 @@ static constexpr float M_PI_2_F = static_cast(M_PI_2); static constexpr float FLOAT_NEARLY_ZERO = 1.0f / (1 << 12); static constexpr float FLOAT_SQRT2 = 1.41421356f; -static inline float DegreesToRadians(float degrees) { +inline float DegreesToRadians(float degrees) { return degrees * (static_cast(M_PI) / 180.0f); } -static inline float RadiansToDegrees(float radians) { +inline float RadiansToDegrees(float radians) { return radians * (180.0f / static_cast(M_PI)); } -static inline bool FloatNearlyZero(float x, float tolerance = FLOAT_NEARLY_ZERO) { +inline bool FloatNearlyZero(float x, float tolerance = FLOAT_NEARLY_ZERO) { return fabsf(x) <= tolerance; } -static inline bool FloatNearlyEqual(float x, float y, float tolerance = FLOAT_NEARLY_ZERO) { +inline bool FloatNearlyEqual(float x, float y, float tolerance = FLOAT_NEARLY_ZERO) { return fabsf(x - y) <= tolerance; } -static inline float SinSnapToZero(float radians) { +inline float SinSnapToZero(float radians) { float v = sinf(radians); return FloatNearlyZero(v) ? 0.0f : v; } -static inline float CosSnapToZero(float radians) { +inline float CosSnapToZero(float radians) { float v = cosf(radians); return FloatNearlyZero(v) ? 0.0f : v; } -static inline bool FloatsAreFinite(const float array[], int count) { +inline bool FloatsAreFinite(const float array[], int count) { float prod = 0; for (int i = 0; i < count; ++i) { prod *= array[i]; diff --git a/src/core/utils/OrientationHelper.h b/src/core/utils/OrientationHelper.h index af195388..fdc90faa 100644 --- a/src/core/utils/OrientationHelper.h +++ b/src/core/utils/OrientationHelper.h @@ -27,7 +27,7 @@ namespace tgfx { * Indicates if the data is little endian * Is unaffected on false returns */ -static inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEndian) { +inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEndian) { // II indicates Intel (little endian) and MM indicates motorola (big endian). if (('I' != data[0] || 'I' != data[1]) && ('M' != data[0] || 'M' != data[1])) { return false; @@ -37,7 +37,7 @@ static inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEnd return true; } -static inline uint32_t get_endian_int(const uint8_t* data, bool littleEndian) { +inline uint32_t get_endian_int(const uint8_t* data, bool littleEndian) { if (littleEndian) { return static_cast((data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0])); } diff --git a/src/core/vectors/coregraphics/CGScalerContext.cpp b/src/core/vectors/coregraphics/CGScalerContext.cpp index 853be9c5..9ce3db7c 100644 --- a/src/core/vectors/coregraphics/CGScalerContext.cpp +++ b/src/core/vectors/coregraphics/CGScalerContext.cpp @@ -32,7 +32,7 @@ static constexpr float StdFakeBoldInterpValues[] = { 1.f / 32.f, }; -static inline float Interpolate(const float& a, const float& b, const float& t) { +inline float Interpolate(const float& a, const float& b, const float& t) { return a + (b - a) * t; } diff --git a/src/gpu/DrawingManager.cpp b/src/gpu/DrawingManager.cpp index 097db887..b6dda54a 100644 --- a/src/gpu/DrawingManager.cpp +++ b/src/gpu/DrawingManager.cpp @@ -26,9 +26,9 @@ namespace tgfx { std::shared_ptr DrawingManager::addOpsTask( - std::shared_ptr renderTargetProxy) { + std::shared_ptr renderTargetProxy, uint32_t renderFlags) { checkIfResolveNeeded(renderTargetProxy); - auto opsTask = std::make_shared(renderTargetProxy); + auto opsTask = std::make_shared(renderTargetProxy, renderFlags); addRenderTask(opsTask); activeOpsTask = opsTask; return opsTask; @@ -72,12 +72,11 @@ void DrawingManager::addResourceTask(std::shared_ptr resourceTask) if (resourceTask == nullptr) { return; } +#ifdef DEBUG auto result = resourceTaskMap.find(resourceTask->uniqueKey); - if (result != resourceTaskMap.end()) { - // Remove the UniqueKey from the old task, so it will be skipped when the task is executed. - result->second->uniqueKey = {}; - } + DEBUG_ASSERT(result == resourceTaskMap.end()); resourceTaskMap[resourceTask->uniqueKey] = resourceTask.get(); +#endif resourceTasks.push_back(std::move(resourceTask)); } @@ -100,7 +99,9 @@ bool DrawingManager::flush() { for (auto& task : resourceTasks) { task->execute(context); } +#ifdef DEBUG resourceTaskMap = {}; +#endif resourceTasks = {}; for (auto& task : renderTasks) { task->execute(context->gpu()); diff --git a/src/gpu/DrawingManager.h b/src/gpu/DrawingManager.h index d0224558..2e102803 100644 --- a/src/gpu/DrawingManager.h +++ b/src/gpu/DrawingManager.h @@ -32,7 +32,8 @@ class DrawingManager { explicit DrawingManager(Context* context) : context(context) { } - std::shared_ptr addOpsTask(std::shared_ptr renderTargetProxy); + std::shared_ptr addOpsTask(std::shared_ptr renderTargetProxy, + uint32_t renderFlags); void addRuntimeDrawTask(std::shared_ptr target, std::shared_ptr source, @@ -52,11 +53,13 @@ class DrawingManager { private: Context* context = nullptr; - ResourceKeyMap resourceTaskMap = {}; std::unordered_set> needResolveTargets = {}; std::vector> resourceTasks = {}; std::vector> renderTasks = {}; std::shared_ptr activeOpsTask = nullptr; +#ifdef DEBUG + ResourceKeyMap resourceTaskMap = {}; +#endif void addRenderTask(std::shared_ptr renderTask); void checkIfResolveNeeded(std::shared_ptr renderTargetProxy); diff --git a/src/gpu/GpuBuffer.h b/src/gpu/GpuBuffer.h index 0df685cb..ac997a21 100644 --- a/src/gpu/GpuBuffer.h +++ b/src/gpu/GpuBuffer.h @@ -28,8 +28,8 @@ enum class BufferType { class GpuBuffer : public Resource { public: - static std::shared_ptr Make(Context* context, const void* buffer, size_t size, - BufferType bufferType); + static std::shared_ptr Make(Context* context, BufferType bufferType, + const void* buffer = nullptr, size_t size = 0); BufferType bufferType() const { return _bufferType; diff --git a/src/gpu/GradientCache.cpp b/src/gpu/GradientCache.cpp index 09fb1ec8..8e6dd680 100644 --- a/src/gpu/GradientCache.cpp +++ b/src/gpu/GradientCache.cpp @@ -17,7 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "GradientCache.h" -#include +#include "tgfx/core/Pixmap.h" namespace tgfx { // Each bitmap will be 256x1. diff --git a/src/gpu/GradientCache.h b/src/gpu/GradientCache.h index f3c981c0..30d18707 100644 --- a/src/gpu/GradientCache.h +++ b/src/gpu/GradientCache.h @@ -23,7 +23,6 @@ #include "core/PixelBuffer.h" #include "tgfx/core/BytesKey.h" #include "tgfx/core/Color.h" -#include "tgfx/core/Pixmap.h" namespace tgfx { class GradientCache { diff --git a/src/gpu/OpContext.cpp b/src/gpu/OpContext.cpp index 91f23386..3067f87c 100644 --- a/src/gpu/OpContext.cpp +++ b/src/gpu/OpContext.cpp @@ -18,7 +18,7 @@ #include "OpContext.h" #include "gpu/DrawingManager.h" -#include "gpu/ops/FillRectOp.h" +#include "gpu/ops/RectDrawOp.h" namespace tgfx { static constexpr uint32_t InvalidContentVersion = 0; @@ -34,7 +34,7 @@ void OpContext::fillRectWithFP(const Rect& dstRect, std::unique_ptraddColorFP(std::move(fp)); op->setBlendMode(BlendMode::Src); addOp(std::move(op)); @@ -47,7 +47,7 @@ void OpContext::fillRectWithFP(const Rect& dstRect, std::unique_ptr op) { if (opsTask == nullptr || opsTask->isClosed()) { auto drawingManager = renderTargetProxy->getContext()->drawingManager(); - opsTask = drawingManager->addOpsTask(renderTargetProxy); + opsTask = drawingManager->addOpsTask(renderTargetProxy, renderFlags); } opsTask->addOp(std::move(op)); do { diff --git a/src/gpu/OpContext.h b/src/gpu/OpContext.h index f2160259..28621d09 100644 --- a/src/gpu/OpContext.h +++ b/src/gpu/OpContext.h @@ -28,8 +28,8 @@ namespace tgfx { */ class OpContext { public: - explicit OpContext(std::shared_ptr renderTargetProxy) - : renderTargetProxy(std::move(renderTargetProxy)) { + explicit OpContext(std::shared_ptr renderTargetProxy, uint32_t renderFlags) + : renderTargetProxy(std::move(renderTargetProxy)), renderFlags(renderFlags) { } RenderTargetProxy* renderTarget() const { @@ -51,6 +51,7 @@ class OpContext { private: std::shared_ptr renderTargetProxy = nullptr; std::shared_ptr opsTask = nullptr; + uint32_t renderFlags = 0; uint32_t _contentVersion = 1u; }; } // namespace tgfx diff --git a/src/gpu/ProxyProvider.cpp b/src/gpu/ProxyProvider.cpp index dfb83f27..33741804 100644 --- a/src/gpu/ProxyProvider.cpp +++ b/src/gpu/ProxyProvider.cpp @@ -17,30 +17,21 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "ProxyProvider.h" +#include "core/utils/DataTask.h" +#include "core/utils/UniqueID.h" #include "gpu/DrawingManager.h" #include "gpu/PlainTexture.h" #include "gpu/proxies/TextureRenderTargetProxy.h" -#include "gpu/tasks/GpuBufferCreateTask.h" +#include "gpu/tasks/GpuBufferUploadTask.h" #include "gpu/tasks/RenderTargetCreateTask.h" +#include "gpu/tasks/ShapeBufferUploadTask.h" #include "gpu/tasks/TextureCreateTask.h" +#include "gpu/tasks/TextureUploadTask.h" namespace tgfx { ProxyProvider::ProxyProvider(Context* context) : context(context) { } -class DataWrapper : public DataProvider { - public: - DataWrapper(std::shared_ptr data) : data(std::move(data)) { - } - - std::shared_ptr getData() const override { - return data; - } - - private: - std::shared_ptr data = nullptr; -}; - std::shared_ptr ProxyProvider::createGpuBufferProxy(const UniqueKey& uniqueKey, std::shared_ptr data, BufferType bufferType, @@ -48,11 +39,25 @@ std::shared_ptr ProxyProvider::createGpuBufferProxy(const Unique if (data == nullptr || data->empty()) { return nullptr; } - auto provider = std::make_shared(std::move(data)); + auto provider = DataProvider::Wrap(std::move(data)); renderFlags |= RenderFlags::DisableAsyncTask; return createGpuBufferProxy(uniqueKey, std::move(provider), bufferType, renderFlags); } +class AsyncDataProvider : public DataProvider { + public: + explicit AsyncDataProvider(std::shared_ptr provider) { + task = DataTask::Run([provider = std::move(provider)]() { return provider->getData(); }); + } + + std::shared_ptr getData() const override { + return task->wait(); + } + + private: + std::shared_ptr> task = nullptr; +}; + std::shared_ptr ProxyProvider::createGpuBufferProxy( const UniqueKey& uniqueKey, std::shared_ptr provider, BufferType bufferType, uint32_t renderFlags) { @@ -60,9 +65,14 @@ std::shared_ptr ProxyProvider::createGpuBufferProxy( if (proxy != nullptr) { return proxy; } + if (provider == nullptr) { + return nullptr; + } + if (!(renderFlags & RenderFlags::DisableAsyncTask)) { + provider = std::make_shared(std::move(provider)); + } auto proxyKey = GetProxyKey(uniqueKey, renderFlags); - auto async = !(renderFlags & RenderFlags::DisableAsyncTask); - auto task = GpuBufferCreateTask::MakeFrom(proxyKey, bufferType, std::move(provider), async); + auto task = GpuBufferUploadTask::MakeFrom(proxyKey, bufferType, std::move(provider)); if (task == nullptr) { return nullptr; } @@ -72,6 +82,77 @@ std::shared_ptr ProxyProvider::createGpuBufferProxy( return proxy; } +class ShapeRasterizerWrapper : public ShapeBufferProvider { + public: + explicit ShapeRasterizerWrapper(std::shared_ptr rasterizer) + : rasterizer(std::move(rasterizer)) { + } + + std::shared_ptr getBuffer() const override { + return rasterizer->makeRasterized(); + } + + private: + std::shared_ptr rasterizer = nullptr; +}; + +class AsyncShapeBufferProvider : public ShapeBufferProvider { + public: + explicit AsyncShapeBufferProvider(std::shared_ptr rasterizer) { + task = DataTask::Run( + [rasterizer = std::move(rasterizer)]() { return rasterizer->makeRasterized(); }); + } + + std::shared_ptr getBuffer() const override { + return task->wait(); + } + + private: + std::shared_ptr> task = nullptr; +}; + +std::shared_ptr ProxyProvider::createGpuShapeProxy( + const UniqueKey& uniqueKey, std::shared_ptr rasterizer, uint32_t renderFlags) { + static const auto TriangleShapeType = UniqueID::Next(); + static const auto TextureShapeType = UniqueID::Next(); + auto triangleKey = UniqueKey::Append(uniqueKey, &TriangleShapeType, 1); + auto triangleProxy = findOrWrapGpuBufferProxy(triangleKey); + if (triangleProxy != nullptr) { + return std::make_shared(triangleProxy, nullptr); + } + auto textureKey = UniqueKey::Append(uniqueKey, &TextureShapeType, 1); + auto textureProxy = findOrWrapTextureProxy(textureKey); + if (textureProxy != nullptr) { + return std::make_shared(nullptr, textureProxy); + } + if (rasterizer == nullptr) { + return nullptr; + } + auto width = rasterizer->width(); + auto height = rasterizer->height(); + std::shared_ptr provider = nullptr; + if (!(renderFlags & RenderFlags::DisableAsyncTask) && rasterizer->asyncSupport()) { + provider = std::make_shared(std::move(rasterizer)); + } else { + provider = std::make_shared(std::move(rasterizer)); + } + auto triangleProxyKey = GetProxyKey(triangleKey, renderFlags); + auto textureProxyKey = GetProxyKey(textureKey, renderFlags); + auto task = + ShapeBufferUploadTask::MakeFrom(triangleProxyKey, textureProxyKey, std::move(provider)); + if (task == nullptr) { + return nullptr; + } + context->drawingManager()->addResourceTask(std::move(task)); + triangleProxy = + std::shared_ptr(new GpuBufferProxy(triangleProxyKey, BufferType::Vertex)); + addResourceProxy(triangleProxy, triangleKey); + textureProxy = + std::shared_ptr(new TextureProxy(textureProxyKey, width, height, false, true)); + addResourceProxy(textureProxy, textureKey); + return std::make_shared(triangleProxy, textureProxy); +} + std::shared_ptr ProxyProvider::createTextureProxy( const UniqueKey& uniqueKey, std::shared_ptr imageBuffer, bool mipmapped, uint32_t renderFlags) { @@ -105,7 +186,7 @@ std::shared_ptr ProxyProvider::doCreateTextureProxy( const UniqueKey& uniqueKey, std::shared_ptr decoder, bool mipmapped, uint32_t renderFlags) { auto proxyKey = GetProxyKey(uniqueKey, renderFlags); - auto task = TextureCreateTask::MakeFrom(proxyKey, decoder, mipmapped); + auto task = TextureUploadTask::MakeFrom(proxyKey, decoder, mipmapped); if (task == nullptr) { return nullptr; } diff --git a/src/gpu/ProxyProvider.h b/src/gpu/ProxyProvider.h index 6ac3362e..6d0a6e0e 100644 --- a/src/gpu/ProxyProvider.h +++ b/src/gpu/ProxyProvider.h @@ -20,7 +20,9 @@ #include "core/DataProvider.h" #include "core/ImageDecoder.h" +#include "core/ShapeRasterizer.h" #include "gpu/proxies/GpuBufferProxy.h" +#include "gpu/proxies/GpuShapeProxy.h" #include "gpu/proxies/RenderTargetProxy.h" #include "gpu/proxies/TextureProxy.h" #include "tgfx/core/ImageGenerator.h" @@ -62,6 +64,14 @@ class ProxyProvider { BufferType bufferType, uint32_t renderFlags = 0); + /** + * Creates a GpuShapeProxy for the given ShapeRasterizer. The rasterizer will be released after + * being uploaded to the GPU. + */ + std::shared_ptr createGpuShapeProxy(const UniqueKey& uniqueKey, + std::shared_ptr rasterizer, + uint32_t renderFlags = 0); + /* * Creates a TextureProxy for the given ImageBuffer. The image buffer will be released after being * uploaded to the GPU. diff --git a/src/gpu/Quad.cpp b/src/gpu/Quad.cpp index 8eb445d9..b36806ee 100644 --- a/src/gpu/Quad.cpp +++ b/src/gpu/Quad.cpp @@ -19,13 +19,15 @@ #include "Quad.h" namespace tgfx { -Quad Quad::MakeFromRect(const Rect& rect, const Matrix& matrix) { +Quad Quad::MakeFrom(const Rect& rect, const Matrix* matrix) { std::vector points; points.push_back(Point::Make(rect.left, rect.top)); points.push_back(Point::Make(rect.left, rect.bottom)); points.push_back(Point::Make(rect.right, rect.top)); points.push_back(Point::Make(rect.right, rect.bottom)); - matrix.mapPoints(points.data(), 4); + if (matrix) { + matrix->mapPoints(points.data(), 4); + } return Quad(points); } diff --git a/src/gpu/Quad.h b/src/gpu/Quad.h index 012f0e99..4473e9a8 100644 --- a/src/gpu/Quad.h +++ b/src/gpu/Quad.h @@ -24,7 +24,7 @@ namespace tgfx { class Quad { public: - static Quad MakeFromRect(const Rect& rect, const Matrix& matrix); + static Quad MakeFrom(const Rect& rect, const Matrix* matrix = nullptr); const Point& point(size_t i) const { return points[i]; diff --git a/src/gpu/RenderContext.cpp b/src/gpu/RenderContext.cpp index 41037411..aa74f468 100644 --- a/src/gpu/RenderContext.cpp +++ b/src/gpu/RenderContext.cpp @@ -19,24 +19,18 @@ #include "RenderContext.h" #include "core/PathRef.h" #include "core/Rasterizer.h" -#include "core/images/TextureImage.h" #include "core/utils/StrokeKey.h" #include "gpu/DrawingManager.h" #include "gpu/OpContext.h" #include "gpu/ProxyProvider.h" #include "gpu/ops/ClearOp.h" -#include "gpu/ops/FillRectOp.h" -#include "gpu/ops/RRectOp.h" -#include "gpu/ops/TriangulatingPathOp.h" +#include "gpu/ops/RRectDrawOp.h" +#include "gpu/ops/RectDrawOp.h" +#include "gpu/ops/ShapeDrawOp.h" #include "gpu/processors/AARectEffect.h" #include "gpu/processors/TextureEffect.h" namespace tgfx { -// https://chromium-review.googlesource.com/c/chromium/src/+/1099564/ -static constexpr int AA_TESSELLATOR_MAX_VERB_COUNT = 100; -// A factor used to estimate the memory size of a tessellated path, based on the average value of -// Buffer.size() / Path.countPoints() from 4300+ tessellated path data. -static constexpr int AA_TESSELLATOR_BUFFER_SIZE_FACTOR = 170; /** * Defines the maximum distance a draw can extend beyond a clip's boundary and still be considered * 'on the other side'. This tolerance accounts for potential floating point rounding errors. The @@ -48,12 +42,12 @@ static constexpr float BOUNDS_TOLERANCE = 1e-3f; RenderContext::RenderContext(std::shared_ptr renderTargetProxy, uint32_t renderFlags) : renderFlags(renderFlags) { - opContext = new OpContext(std::move(renderTargetProxy)); + opContext = new OpContext(std::move(renderTargetProxy), renderFlags); } RenderContext::RenderContext(Surface* surface) : surface(surface) { - renderFlags = surface->renderFlags(); - opContext = new OpContext(surface->renderTargetProxy); + renderFlags = surface->_renderFlags; + opContext = new OpContext(surface->renderTargetProxy, renderFlags); } RenderContext::~RenderContext() { @@ -96,7 +90,7 @@ void RenderContext::drawRect(const Rect& rect, const MCState& state, const FillS if (localBounds.isEmpty()) { return; } - auto drawOp = FillRectOp::Make(style.color, localBounds, state.matrix); + auto drawOp = RectDrawOp::Make(style.color, localBounds, state.matrix); addDrawOp(std::move(drawOp), localBounds, state, style); } @@ -132,22 +126,10 @@ void RenderContext::drawRRect(const RRect& rRect, const MCState& state, const Fi if (localBounds.isEmpty()) { return; } - auto drawOp = RRectOp::Make(style.color, rRect, state.matrix); + auto drawOp = RRectDrawOp::Make(style.color, rRect, state.matrix); addDrawOp(std::move(drawOp), localBounds, state, style); } -static bool ShouldTriangulatePath(const Path& path, const Matrix& viewMatrix) { - if (path.countVerbs() <= AA_TESSELLATOR_MAX_VERB_COUNT) { - return true; - } - auto scales = viewMatrix.getAxisScales(); - auto bounds = path.getBounds(); - bounds.scale(scales.x, scales.y); - auto width = static_cast(ceilf(bounds.width())); - auto height = static_cast(ceilf(bounds.height())); - return path.countPoints() * AA_TESSELLATOR_BUFFER_SIZE_FACTOR <= width * height; -} - void RenderContext::drawPath(const Path& path, const MCState& state, const FillStyle& style, const Stroke* stroke) { auto pathBounds = path.getBounds(); @@ -158,46 +140,10 @@ void RenderContext::drawPath(const Path& path, const MCState& state, const FillS if (localBounds.isEmpty()) { return; } - std::unique_ptr drawOp = nullptr; - if (ShouldTriangulatePath(path, state.matrix)) { - drawOp = TriangulatingPathOp::Make(style.color, path, state.matrix, stroke, renderFlags); - } else { - auto maskFP = makeTextureMask(path, state.matrix, getAAType(style), stroke); - if (maskFP != nullptr) { - drawOp = FillRectOp::Make(style.color, localBounds, state.matrix); - drawOp->addCoverageFP(std::move(maskFP)); - } - } + auto drawOp = ShapeDrawOp::Make(style.color, path, state.matrix, stroke); addDrawOp(std::move(drawOp), localBounds, state, style); } -std::unique_ptr RenderContext::makeTextureMask(const Path& path, - const Matrix& viewMatrix, - AAType aaType, - const Stroke* stroke) { - auto scales = viewMatrix.getAxisScales(); - auto bounds = path.getBounds(); - bounds.scale(scales.x, scales.y); - static const auto TexturePathType = UniqueID::Next(); - BytesKey bytesKey(3 + (stroke ? StrokeKeyCount : 0)); - bytesKey.write(TexturePathType); - bytesKey.write(scales.x); - bytesKey.write(scales.y); - if (stroke) { - WriteStrokeKey(&bytesKey, stroke); - } - auto uniqueKey = UniqueKey::Combine(PathRef::GetUniqueKey(path), bytesKey); - auto width = ceilf(bounds.width()); - auto height = ceilf(bounds.height()); - auto rasterizeMatrix = Matrix::MakeScale(scales.x, scales.y); - rasterizeMatrix.postTranslate(-bounds.x(), -bounds.y()); - auto rasterizer = Rasterizer::MakeFrom(path, ISize::Make(width, height), rasterizeMatrix, - aaType == AAType::Coverage, stroke); - auto proxyProvider = getContext()->proxyProvider(); - auto textureProxy = proxyProvider->createTextureProxy(uniqueKey, rasterizer, false, renderFlags); - return TextureEffect::Make(std::move(textureProxy), {}, &rasterizeMatrix, true); -} - void RenderContext::drawImage(std::shared_ptr image, const SamplingOptions& sampling, const MCState& state, const FillStyle& style) { if (image == nullptr) { @@ -223,7 +169,7 @@ void RenderContext::drawImageRect(std::shared_ptr image, const Rect& rect if (processor == nullptr) { return; } - auto drawOp = FillRectOp::Make(style.color, localBounds, state.matrix); + auto drawOp = RectDrawOp::Make(style.color, localBounds, state.matrix); drawOp->addColorFP(std::move(processor)); if (!isAlphaOnly && style.shader) { auto fillStyle = style; @@ -270,7 +216,7 @@ void RenderContext::drawGlyphRunList(std::shared_ptr glyphRunList, if (processor == nullptr) { return; } - auto drawOp = FillRectOp::Make(style.color, localBounds, state.matrix); + auto drawOp = RectDrawOp::Make(style.color, localBounds, state.matrix); drawOp->addCoverageFP(std::move(processor)); addDrawOp(std::move(drawOp), localBounds, state, style); } @@ -387,16 +333,15 @@ std::shared_ptr RenderContext::getClipTexture(const Path& clip, AA auto width = static_cast(ceilf(bounds.width())); auto height = static_cast(ceilf(bounds.height())); auto rasterizeMatrix = Matrix::MakeTrans(-bounds.left, -bounds.top); - if (ShouldTriangulatePath(clip, rasterizeMatrix)) { - auto drawOp = - TriangulatingPathOp::Make(Color::White(), clip, rasterizeMatrix, nullptr, renderFlags); + if (ShapeRasterizer::ShouldTriangulatePath(clip)) { + auto drawOp = ShapeDrawOp::Make(Color::White(), clip, rasterizeMatrix); drawOp->setAA(aaType); auto renderTarget = RenderTargetProxy::MakeFallback(getContext(), width, height, true, 1, false, ImageOrigin::TopLeft, true); if (renderTarget == nullptr) { return nullptr; } - OpContext context(renderTarget); + OpContext context(renderTarget, renderFlags); context.addOp(std::move(drawOp)); clipTexture = renderTarget->getTextureProxy(); } else { @@ -447,7 +392,7 @@ void RenderContext::addDrawOp(std::unique_ptr op, const Rect& localBound return; } FPArgs args = {getContext(), renderFlags, localBounds, state.matrix}; - auto isRectOp = op->classID() == FillRectOp::ClassID(); + auto isRectOp = op->classID() == RectDrawOp::ClassID(); auto aaType = getAAType(style); if (aaType == AAType::Coverage && isRectOp && args.viewMatrix.rectStaysRect() && IsPixelAligned(op->bounds())) { @@ -526,7 +471,7 @@ bool RenderContext::wouldOverwriteEntireRT(const Rect& localBounds, const MCStat void RenderContext::replaceRenderTarget(std::shared_ptr newRenderTargetProxy) { delete opContext; - opContext = new OpContext(std::move(newRenderTargetProxy)); + opContext = new OpContext(std::move(newRenderTargetProxy), renderFlags); } } // namespace tgfx diff --git a/src/gpu/RenderContext.h b/src/gpu/RenderContext.h index 308ecd74..a630bdb1 100644 --- a/src/gpu/RenderContext.h +++ b/src/gpu/RenderContext.h @@ -74,8 +74,6 @@ class RenderContext : public DrawContext { const Matrix& viewMatrix, AAType aaType, Rect* scissorRect); Rect clipLocalBounds(const Rect& localBounds, const MCState& state); - std::unique_ptr makeTextureMask(const Path& path, const Matrix& viewMatrix, - AAType aaType, const Stroke* stroke = nullptr); bool drawAsClear(const Rect& rect, const MCState& state, const FillStyle& style); void drawColorGlyphs(std::shared_ptr glyphRunList, const MCState& state, const FillStyle& style); diff --git a/src/gpu/RenderPass.cpp b/src/gpu/RenderPass.cpp index fbf9352a..6e8d4b11 100644 --- a/src/gpu/RenderPass.cpp +++ b/src/gpu/RenderPass.cpp @@ -58,7 +58,20 @@ void RenderPass::bindBuffers(std::shared_ptr indexBuffer, if (drawPipelineStatus != DrawPipelineStatus::Ok) { return; } - onBindBuffers(std::move(indexBuffer), std::move(vertexBuffer)); + _indexBuffer = std::move(indexBuffer); + _vertexBuffer = std::move(vertexBuffer); + _vertexData = nullptr; +} + +void RenderPass::bindBuffers(std::shared_ptr indexBuffer, + std::shared_ptr vertexData) { + if (drawPipelineStatus != DrawPipelineStatus::Ok) { + return; + } + _indexBuffer = std::move(indexBuffer); + _vertexBuffer = nullptr; + _vertexData = std::move(vertexData); + ; } void RenderPass::draw(PrimitiveType primitiveType, size_t baseVertex, size_t vertexCount) { diff --git a/src/gpu/RenderPass.h b/src/gpu/RenderPass.h index dc05e3f4..f8df5193 100644 --- a/src/gpu/RenderPass.h +++ b/src/gpu/RenderPass.h @@ -59,6 +59,7 @@ class RenderPass { void end(); void bindProgramAndScissorClip(const ProgramInfo* programInfo, const Rect& drawBounds); void bindBuffers(std::shared_ptr indexBuffer, std::shared_ptr vertexBuffer); + void bindBuffers(std::shared_ptr indexBuffer, std::shared_ptr vertexData); void draw(PrimitiveType primitiveType, size_t baseVertex, size_t vertexCount); void drawIndexed(PrimitiveType primitiveType, size_t baseIndex, size_t indexCount); void clear(const Rect& scissor, Color color); @@ -69,8 +70,6 @@ class RenderPass { virtual bool onBindProgramAndScissorClip(const ProgramInfo* programInfo, const Rect& drawBounds) = 0; - virtual void onBindBuffers(std::shared_ptr indexBuffer, - std::shared_ptr vertexBuffer) = 0; virtual void onDraw(PrimitiveType primitiveType, size_t baseVertex, size_t vertexCount) = 0; virtual void onDrawIndexed(PrimitiveType primitiveType, size_t baseIndex, size_t indexCount) = 0; virtual void onClear(const Rect& scissor, Color color) = 0; @@ -79,8 +78,9 @@ class RenderPass { std::shared_ptr _renderTarget = nullptr; std::shared_ptr _renderTargetTexture = nullptr; Program* _program = nullptr; - std::shared_ptr _indexBuffer; - std::shared_ptr _vertexBuffer; + std::shared_ptr _indexBuffer = nullptr; + std::shared_ptr _vertexBuffer = nullptr; + std::shared_ptr _vertexData = nullptr; private: enum class DrawPipelineStatus { Ok = 0, NotConfigured, FailedToBind }; diff --git a/src/gpu/ResourceKey.cpp b/src/gpu/ResourceKey.cpp index 634a2f78..e739cf69 100644 --- a/src/gpu/ResourceKey.cpp +++ b/src/gpu/ResourceKey.cpp @@ -88,27 +88,27 @@ UniqueKey UniqueKey::Make() { return UniqueKey(new UniqueDomain()); } -UniqueKey UniqueKey::Combine(const UniqueKey& uniqueKey, const BytesKey& bytesKey) { +UniqueKey UniqueKey::Append(const UniqueKey& uniqueKey, const uint32_t* data, size_t count) { if (uniqueKey.empty()) { return {}; } - if (bytesKey.size() == 0) { + if (count == 0) { return uniqueKey; } auto offset = std::max(uniqueKey.count, static_cast(2)); - auto data = CopyData(bytesKey.data(), bytesKey.size(), offset); - if (data == nullptr) { + auto newData = CopyData(data, count, offset); + if (newData == nullptr) { return uniqueKey; } if (uniqueKey.count > 2) { - memcpy(data + 2, uniqueKey.data + 2, (uniqueKey.count - 2) * sizeof(uint32_t)); + memcpy(newData + 2, uniqueKey.data + 2, (uniqueKey.count - 2) * sizeof(uint32_t)); } - auto count = bytesKey.size() + offset; + auto newCount = count + offset; auto domain = uniqueKey.uniqueDomain; - data[1] = domain->uniqueID(); - data[0] = HashRange(data + 1, count - 1); + newData[1] = domain->uniqueID(); + newData[0] = HashRange(newData + 1, newCount - 1); domain->addReference(); - return {data, count, domain}; + return {newData, newCount, domain}; } static uint32_t* MakeDomainData(UniqueDomain* uniqueDomain) { @@ -144,7 +144,7 @@ UniqueKey::UniqueKey(const UniqueType& type) } } -UniqueKey::UniqueKey(tgfx::UniqueType&& type) noexcept +UniqueKey::UniqueKey(UniqueType&& type) noexcept : ResourceKey(MakeDomainData(type.domain), 1), uniqueDomain(type.domain) { type.domain = nullptr; } diff --git a/src/gpu/ResourceKey.h b/src/gpu/ResourceKey.h index 14ef3579..0a8f9608 100644 --- a/src/gpu/ResourceKey.h +++ b/src/gpu/ResourceKey.h @@ -148,10 +148,11 @@ class UniqueKey : public ResourceKey { static UniqueKey Make(); /** - * Creates a new UniqueKey by combining an existing UniqueKey and a BytesKey. The returned - * UniqueKey will share the same unique domain as the original UniqueKey. + * Creates a new UniqueKey by appending a list of uint32_t values to the given UniqueKey. The + * returned UniqueKey will share the same unique domain as the original UniqueKey. Returns the + * original UniqueKey if the data is empty. */ - static UniqueKey Combine(const UniqueKey& uniqueKey, const BytesKey& bytesKey); + static UniqueKey Append(const UniqueKey& uniqueKey, const uint32_t* data, size_t count); /** * Creates an empty UniqueKey. diff --git a/src/gpu/ResourceProvider.cpp b/src/gpu/ResourceProvider.cpp index 1efed928..0225361e 100644 --- a/src/gpu/ResourceProvider.cpp +++ b/src/gpu/ResourceProvider.cpp @@ -96,7 +96,7 @@ std::shared_ptr ResourceProvider::createNonAAQuadIndexBuffer() { // clang-format on auto provider = std::make_shared( kNonAAQuadIndexPattern, kIndicesPerNonAAQuad, kMaxNumNonAAQuads, kVerticesPerNonAAQuad); - return GpuBufferProxy::MakeFrom(context, std::move(provider), BufferType::Index); + return GpuBufferProxy::MakeFrom(context, std::move(provider), BufferType::Index, 0); } uint16_t ResourceProvider::MaxNumNonAAQuads() { @@ -123,7 +123,7 @@ std::shared_ptr ResourceProvider::createAAQuadIndexBuffer() { // clang-format on auto provider = std::make_shared( kAAQuadIndexPattern, kIndicesPerAAQuad, kMaxNumAAQuads, kVerticesPerAAQuad); - return GpuBufferProxy::MakeFrom(context, std::move(provider), BufferType::Index); + return GpuBufferProxy::MakeFrom(context, std::move(provider), BufferType::Index, 0); } uint16_t ResourceProvider::MaxNumAAQuads() { diff --git a/src/gpu/Texture.h b/src/gpu/Texture.h index bb18b747..324c77fe 100644 --- a/src/gpu/Texture.h +++ b/src/gpu/Texture.h @@ -21,7 +21,6 @@ #include "gpu/Resource.h" #include "gpu/TextureSampler.h" #include "tgfx/core/ImageBuffer.h" -#include "tgfx/core/ImageInfo.h" #include "tgfx/core/Point.h" #include "tgfx/core/YUVColorSpace.h" #include "tgfx/core/YUVData.h" diff --git a/src/gpu/opengl/GLBuffer.cpp b/src/gpu/opengl/GLBuffer.cpp index 6f232494..7dcc8335 100644 --- a/src/gpu/opengl/GLBuffer.cpp +++ b/src/gpu/opengl/GLBuffer.cpp @@ -22,15 +22,13 @@ #include "core/utils/UniqueID.h" namespace tgfx { -std::shared_ptr GpuBuffer::Make(Context* context, const void* buffer, size_t size, - BufferType bufferType) { - if (buffer == nullptr || size == 0) { - return nullptr; +std::shared_ptr GpuBuffer::Make(Context* context, BufferType bufferType, + const void* buffer, size_t size) { + if (buffer == nullptr) { + size = 0; } - // 防止前面产生的GLError,导致后面CheckGLError逻辑返回错误结果 + // Clear the GL errors generated by the previous operations. CheckGLError(context); - - unsigned target = bufferType == BufferType::Index ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; auto gl = GLFunctions::Get(context); unsigned bufferID = 0; gl->genBuffers(1, &bufferID); @@ -38,13 +36,16 @@ std::shared_ptr GpuBuffer::Make(Context* context, const void* buffer, return nullptr; } auto glBuffer = Resource::AddToCache(context, new GLBuffer(bufferType, size, bufferID)); + if (size == 0) { + return glBuffer; + } + unsigned target = bufferType == BufferType::Index ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; gl->bindBuffer(target, glBuffer->_bufferID); gl->bufferData(target, static_cast(size), buffer, GL_STATIC_DRAW); + gl->bindBuffer(target, 0); if (!CheckGLError(context)) { - gl->bindBuffer(target, 0); return nullptr; } - gl->bindBuffer(target, 0); return glBuffer; } diff --git a/src/gpu/opengl/GLRenderPass.cpp b/src/gpu/opengl/GLRenderPass.cpp index 8b379bea..26d591e9 100644 --- a/src/gpu/opengl/GLRenderPass.cpp +++ b/src/gpu/opengl/GLRenderPass.cpp @@ -20,8 +20,7 @@ #include "GLUtil.h" #include "gpu/DrawingManager.h" #include "gpu/ProgramCache.h" -#include "gpu/opengl/GLVertexArray.h" -#include "gpu/opengl/GLVertexArrayCreateTask.h" +#include "gpu/opengl/GLProgram.h" namespace tgfx { struct AttribLayout { @@ -47,11 +46,12 @@ static AttribLayout GetAttribLayout(SLType type) { GLRenderPass::GLRenderPass(Context* context) : RenderPass(context) { if (GLCaps::Get(context)->vertexArrayObjectSupport) { - vertexArrayHandle = UniqueKey::Make(); - // Using VAO is required in the core profile. - auto task = std::make_shared(vertexArrayHandle.key()); - context->drawingManager()->addResourceTask(std::move(task)); + vertexArray = GLVertexArray::Make(context); + DEBUG_ASSERT(vertexArray != nullptr); } + sharedVertexBuffer = + std::static_pointer_cast(GpuBuffer::Make(context, BufferType::Vertex)); + DEBUG_ASSERT(sharedVertexBuffer != nullptr); } static void UpdateScissor(Context* context, const Rect& scissorRect) { @@ -111,12 +111,6 @@ bool GLRenderPass::onBindProgramAndScissorClip(const ProgramInfo* programInfo, return true; } -void GLRenderPass::onBindBuffers(std::shared_ptr indexBuffer, - std::shared_ptr vertexBuffer) { - _indexBuffer = std::move(indexBuffer); - _vertexBuffer = std::move(vertexBuffer); -} - static const unsigned gPrimitiveType[] = {GL_TRIANGLES, GL_TRIANGLE_STRIP}; void GLRenderPass::onDraw(PrimitiveType primitiveType, size_t baseVertex, size_t vertexCount) { @@ -142,11 +136,16 @@ void GLRenderPass::onDrawIndexed(PrimitiveType primitiveType, size_t baseIndex, void GLRenderPass::draw(const std::function& func) { auto gl = GLFunctions::Get(context); - auto vertexArray = Resource::Find(context, vertexArrayHandle.key()); if (vertexArray) { gl->bindVertexArray(vertexArray->id()); } - gl->bindBuffer(GL_ARRAY_BUFFER, std::static_pointer_cast(_vertexBuffer)->bufferID()); + if (_vertexBuffer) { + gl->bindBuffer(GL_ARRAY_BUFFER, std::static_pointer_cast(_vertexBuffer)->bufferID()); + } else { + gl->bindBuffer(GL_ARRAY_BUFFER, sharedVertexBuffer->bufferID()); + gl->bufferData(GL_ARRAY_BUFFER, static_cast(_vertexData->size()), + _vertexData->data(), GL_STATIC_DRAW); + } auto* program = static_cast(_program); for (const auto& attribute : program->vertexAttributes()) { const AttribLayout& layout = GetAttribLayout(attribute.gpuType); diff --git a/src/gpu/opengl/GLRenderPass.h b/src/gpu/opengl/GLRenderPass.h index 5d0d56a0..2499a002 100644 --- a/src/gpu/opengl/GLRenderPass.h +++ b/src/gpu/opengl/GLRenderPass.h @@ -18,16 +18,10 @@ #pragma once -#include -#include "GLBuffer.h" -#include "GLProgram.h" -#include "gpu/AAType.h" -#include "gpu/Pipeline.h" #include "gpu/RenderPass.h" +#include "gpu/opengl/GLBuffer.h" +#include "gpu/opengl/GLVertexArray.h" #include "gpu/ops/Op.h" -#include "gpu/processors/FragmentProcessor.h" -#include "gpu/processors/GeometryProcessor.h" -#include "tgfx/core/BlendMode.h" namespace tgfx { @@ -37,14 +31,13 @@ class GLRenderPass : public RenderPass { protected: bool onBindProgramAndScissorClip(const ProgramInfo* programInfo, const Rect& drawBounds) override; - void onBindBuffers(std::shared_ptr indexBuffer, - std::shared_ptr vertexBuffer) override; void onDraw(PrimitiveType primitiveType, size_t baseVertex, size_t vertexCount) override; void onDrawIndexed(PrimitiveType primitiveType, size_t baseIndex, size_t indexCount) override; void onClear(const Rect& scissor, Color color) override; private: - ResourceHandle vertexArrayHandle = {}; + std::shared_ptr vertexArray = nullptr; + std::shared_ptr sharedVertexBuffer = nullptr; void draw(const std::function& func); }; diff --git a/src/gpu/ops/ClearOp.h b/src/gpu/ops/ClearOp.h index e894b550..c9c267fc 100644 --- a/src/gpu/ops/ClearOp.h +++ b/src/gpu/ops/ClearOp.h @@ -27,6 +27,9 @@ class ClearOp : public Op { static std::unique_ptr Make(Color color, const Rect& scissor); + void prepare(Context*, uint32_t) override { + } + void execute(RenderPass* renderPass) override; private: diff --git a/src/gpu/ops/Op.h b/src/gpu/ops/Op.h index 32ffc425..82f01177 100644 --- a/src/gpu/ops/Op.h +++ b/src/gpu/ops/Op.h @@ -40,8 +40,7 @@ class Op { virtual ~Op() = default; - virtual void prepare(Context*) { - } + virtual void prepare(Context* context, uint32_t renderFlags) = 0; virtual void execute(RenderPass* renderPass) = 0; diff --git a/src/gpu/ops/RRectOp.cpp b/src/gpu/ops/RRectDrawOp.cpp similarity index 86% rename from src/gpu/ops/RRectOp.cpp rename to src/gpu/ops/RRectDrawOp.cpp index 84cb6b4c..7fb6b8ee 100644 --- a/src/gpu/ops/RRectOp.cpp +++ b/src/gpu/ops/RRectDrawOp.cpp @@ -16,12 +16,13 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "RRectOp.h" +#include "RRectDrawOp.h" #include "core/utils/MathExtra.h" #include "gpu/Gpu.h" #include "gpu/GpuBuffer.h" #include "gpu/processors/EllipseGeometryProcessor.h" #include "tgfx/core/Buffer.h" +#include "tgfx/core/RenderFlags.h" namespace tgfx { // We have three possible cases for geometry for a round rect. @@ -276,29 +277,31 @@ class RRectIndicesProvider : public DataProvider { std::vector> rRectPaints = {}; }; -std::unique_ptr RRectOp::Make(Color color, const RRect& rRect, const Matrix& viewMatrix) { +std::unique_ptr RRectDrawOp::Make(Color color, const RRect& rRect, + const Matrix& viewMatrix) { Matrix matrix = Matrix::I(); if (!viewMatrix.invert(&matrix)) { return nullptr; } if (/*!isStrokeOnly && */ 0.5f <= rRect.radii.x && 0.5f <= rRect.radii.y) { - return std::unique_ptr(new RRectOp(color, rRect, viewMatrix, matrix)); + return std::unique_ptr(new RRectDrawOp(color, rRect, viewMatrix, matrix)); } return nullptr; } -RRectOp::RRectOp(Color color, const RRect& rRect, const Matrix& viewMatrix, const Matrix& uvMatrix) +RRectDrawOp::RRectDrawOp(Color color, const RRect& rRect, const Matrix& viewMatrix, + const Matrix& uvMatrix) : DrawOp(ClassID()), uvMatrix(uvMatrix) { setTransformedBounds(rRect.rect, viewMatrix); auto rRectPaint = std::make_shared(color, 0.f, 0.f, rRect, viewMatrix); rRectPaints.push_back(std::move(rRectPaint)); } -bool RRectOp::onCombineIfPossible(Op* op) { +bool RRectDrawOp::onCombineIfPossible(Op* op) { if (!DrawOp::onCombineIfPossible(op)) { return false; } - auto* that = static_cast(op); + auto* that = static_cast(op); if (uvMatrix != that->uvMatrix) { return false; } @@ -310,21 +313,36 @@ static bool UseScale(Context* context) { return !context->caps()->floatIs32Bits; } -void RRectOp::prepare(Context* context) { +void RRectDrawOp::prepare(Context* context, uint32_t renderFlags) { + auto indexProvider = std::make_shared(rRectPaints); + indexBufferProxy = + GpuBufferProxy::MakeFrom(context, std::move(indexProvider), BufferType::Index, renderFlags); auto useScale = UseScale(context); - auto vertexData = std::make_shared(rRectPaints, aa, useScale); - vertexBufferProxy = GpuBufferProxy::MakeFrom(context, std::move(vertexData), BufferType::Vertex); - auto indexData = std::make_shared(rRectPaints); - indexBufferProxy = GpuBufferProxy::MakeFrom(context, std::move(indexData), BufferType::Index); + auto vertexProvider = std::make_shared(rRectPaints, aa, useScale); + if (rRectPaints.size() > 1) { + vertexBufferProxy = GpuBufferProxy::MakeFrom(context, std::move(vertexProvider), + BufferType::Vertex, renderFlags); + } else { + // If we only have one rect, it is not worth the async task overhead. + vertexData = vertexProvider->getData(); + } } -void RRectOp::execute(RenderPass* renderPass) { - if (indexBufferProxy == nullptr || vertexBufferProxy == nullptr) { +void RRectDrawOp::execute(RenderPass* renderPass) { + if (indexBufferProxy == nullptr) { return; } - auto vertexBuffer = vertexBufferProxy->getBuffer(); auto indexBuffer = indexBufferProxy->getBuffer(); - if (vertexBuffer == nullptr || indexBuffer == nullptr) { + if (indexBuffer == nullptr) { + return; + } + std::shared_ptr vertexBuffer = nullptr; + if (vertexBufferProxy) { + vertexBuffer = vertexBufferProxy->getBuffer(); + if (vertexBuffer == nullptr) { + return; + } + } else if (vertexData == nullptr) { return; } auto pipeline = createPipeline( @@ -332,7 +350,11 @@ void RRectOp::execute(RenderPass* renderPass) { renderPass->renderTarget()->height(), false, UseScale(renderPass->getContext()), uvMatrix)); renderPass->bindProgramAndScissorClip(pipeline.get(), scissorRect()); - renderPass->bindBuffers(indexBuffer, vertexBuffer); + if (vertexBuffer) { + renderPass->bindBuffers(indexBuffer, vertexBuffer); + } else { + renderPass->bindBuffers(indexBuffer, vertexData); + } renderPass->drawIndexed(PrimitiveType::Triangles, 0, rRectPaints.size() * kIndicesPerFillRRect); } } // namespace tgfx diff --git a/src/gpu/ops/RRectOp.h b/src/gpu/ops/RRectDrawOp.h similarity index 72% rename from src/gpu/ops/RRectOp.h rename to src/gpu/ops/RRectDrawOp.h index 49a237da..e6847420 100644 --- a/src/gpu/ops/RRectOp.h +++ b/src/gpu/ops/RRectDrawOp.h @@ -24,25 +24,27 @@ namespace tgfx { class RRectPaint; -class RRectOp : public DrawOp { +class RRectDrawOp : public DrawOp { public: DEFINE_OP_CLASS_ID - static std::unique_ptr Make(Color color, const RRect& rRect, const Matrix& viewMatrix); + static std::unique_ptr Make(Color color, const RRect& rRect, + const Matrix& viewMatrix); - void prepare(Context* context) override; + void prepare(Context* context, uint32_t renderFlags) override; void execute(RenderPass* renderPass) override; private: - RRectOp(Color color, const RRect& rRect, const Matrix& viewMatrix, const Matrix& uvMatrix); + RRectDrawOp(Color color, const RRect& rRect, const Matrix& viewMatrix, const Matrix& uvMatrix); bool onCombineIfPossible(Op* op) override; std::vector> rRectPaints; Matrix uvMatrix = Matrix::I(); - std::shared_ptr vertexBufferProxy; - std::shared_ptr indexBufferProxy; + std::shared_ptr indexBufferProxy = nullptr; + std::shared_ptr vertexBufferProxy = nullptr; + std::shared_ptr vertexData = nullptr; // bool stroked = false; // Point strokeWidths = Point::Zero(); diff --git a/src/gpu/ops/FillRectOp.cpp b/src/gpu/ops/RectDrawOp.cpp similarity index 76% rename from src/gpu/ops/FillRectOp.cpp rename to src/gpu/ops/RectDrawOp.cpp index 9fbbf391..dbab750b 100644 --- a/src/gpu/ops/FillRectOp.cpp +++ b/src/gpu/ops/RectDrawOp.cpp @@ -16,13 +16,13 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "FillRectOp.h" -#include "core/utils/UniqueID.h" +#include "RectDrawOp.h" #include "gpu/Gpu.h" #include "gpu/Quad.h" #include "gpu/ResourceProvider.h" #include "gpu/processors/QuadPerEdgeAAGeometryProcessor.h" #include "tgfx/core/Buffer.h" +#include "tgfx/core/RenderFlags.h" namespace tgfx { class RectPaint { @@ -59,12 +59,12 @@ class RectCoverageVerticesProvider : public DataProvider { // we want the new edge to be .5px away from the old line. auto padding = 0.5f / scale; auto insetBounds = rect.makeInset(padding, padding); - auto insetQuad = Quad::MakeFromRect(insetBounds, viewMatrix); + auto insetQuad = Quad::MakeFrom(insetBounds, &viewMatrix); auto outsetBounds = rect.makeOutset(padding, padding); - auto outsetQuad = Quad::MakeFromRect(outsetBounds, viewMatrix); + auto outsetQuad = Quad::MakeFrom(outsetBounds, &viewMatrix); - auto normalInsetQuad = Quad::MakeFromRect(insetBounds, uvMatrix); - auto normalOutsetQuad = Quad::MakeFromRect(outsetBounds, uvMatrix); + auto normalInsetQuad = Quad::MakeFrom(insetBounds, &uvMatrix); + auto normalOutsetQuad = Quad::MakeFrom(outsetBounds, &uvMatrix); for (int j = 0; j < 2; ++j) { const auto& quad = j == 0 ? insetQuad : outsetQuad; @@ -101,7 +101,7 @@ class RectNonCoverageVerticesProvider : public DataProvider { } std::shared_ptr getData() const override { - auto floatCount = rectPaints.size() * 4 * (hasColor ? 9 : 5); + auto floatCount = rectPaints.size() * 4 * (hasColor ? 8 : 4); Buffer buffer(floatCount * sizeof(float)); auto vertices = reinterpret_cast(buffer.data()); auto index = 0; @@ -109,8 +109,8 @@ class RectNonCoverageVerticesProvider : public DataProvider { auto& viewMatrix = rectPaint->viewMatrix; auto& rect = rectPaint->rect; auto& uvMatrix = rectPaint->uvMatrix; - auto quad = Quad::MakeFromRect(rect, viewMatrix); - auto localQuad = Quad::MakeFromRect(rect, uvMatrix); + auto quad = Quad::MakeFrom(rect, &viewMatrix); + auto localQuad = Quad::MakeFrom(rect, &uvMatrix); for (size_t j = 4; j >= 1; --j) { vertices[index++] = quad.point(j - 1).x; vertices[index++] = quad.point(j - 1).y; @@ -133,12 +133,12 @@ class RectNonCoverageVerticesProvider : public DataProvider { bool hasColor; }; -std::unique_ptr FillRectOp::Make(std::optional color, const Rect& rect, +std::unique_ptr RectDrawOp::Make(std::optional color, const Rect& rect, const Matrix& viewMatrix, const Matrix* uvMatrix) { - return std::unique_ptr(new FillRectOp(color, rect, viewMatrix, uvMatrix)); + return std::unique_ptr(new RectDrawOp(color, rect, viewMatrix, uvMatrix)); } -FillRectOp::FillRectOp(std::optional color, const Rect& rect, const Matrix& viewMatrix, +RectDrawOp::RectDrawOp(std::optional color, const Rect& rect, const Matrix& viewMatrix, const Matrix* uvMatrix) : DrawOp(ClassID()), hasColor(color) { auto rectPaint = std::make_shared(color, rect, viewMatrix, uvMatrix); @@ -147,14 +147,14 @@ FillRectOp::FillRectOp(std::optional color, const Rect& rect, const Matri setBounds(bounds); } -bool FillRectOp::canAdd(size_t count) const { +bool RectDrawOp::canAdd(size_t count) const { return rectPaints.size() + count <= static_cast(aa == AAType::Coverage ? ResourceProvider::MaxNumAAQuads() : ResourceProvider::MaxNumNonAAQuads()); } -bool FillRectOp::onCombineIfPossible(Op* op) { - auto* that = static_cast(op); +bool RectDrawOp::onCombineIfPossible(Op* op) { + auto* that = static_cast(op); if (hasColor != that->hasColor || !canAdd(that->rectPaints.size()) || !DrawOp::onCombineIfPossible(op)) { return false; @@ -163,33 +163,34 @@ bool FillRectOp::onCombineIfPossible(Op* op) { return true; } -bool FillRectOp::needsIndexBuffer() const { +bool RectDrawOp::needsIndexBuffer() const { return rectPaints.size() > 1 || aa == AAType::Coverage; } -void FillRectOp::prepare(Context* context) { - std::shared_ptr vertexData = nullptr; +void RectDrawOp::prepare(Context* context, uint32_t renderFlags) { + if (needsIndexBuffer()) { + if (aa == AAType::Coverage) { + indexBufferProxy = context->resourceProvider()->aaQuadIndexBuffer(); + } else { + indexBufferProxy = context->resourceProvider()->nonAAQuadIndexBuffer(); + } + } + std::shared_ptr dataProvider = nullptr; if (aa == AAType::Coverage) { - vertexData = std::make_shared(rectPaints, hasColor); + dataProvider = std::make_shared(rectPaints, hasColor); } else { - vertexData = std::make_shared(rectPaints, hasColor); + dataProvider = std::make_shared(rectPaints, hasColor); } - vertexBufferProxy = GpuBufferProxy::MakeFrom(context, std::move(vertexData), BufferType::Vertex); - if (aa == AAType::Coverage) { - indexBufferProxy = context->resourceProvider()->aaQuadIndexBuffer(); + if (rectPaints.size() > 1) { + vertexBufferProxy = + GpuBufferProxy::MakeFrom(context, std::move(dataProvider), BufferType::Vertex, renderFlags); } else { - indexBufferProxy = context->resourceProvider()->nonAAQuadIndexBuffer(); + // If we only have one rect, it is not worth the async task overhead. + vertexData = dataProvider->getData(); } } -void FillRectOp::execute(RenderPass* renderPass) { - if (vertexBufferProxy == nullptr) { - return; - } - auto vertexBuffer = vertexBufferProxy->getBuffer(); - if (vertexBuffer == nullptr) { - return; - } +void RectDrawOp::execute(RenderPass* renderPass) { std::shared_ptr indexBuffer; if (needsIndexBuffer()) { if (indexBufferProxy == nullptr) { @@ -200,13 +201,26 @@ void FillRectOp::execute(RenderPass* renderPass) { return; } } + std::shared_ptr vertexBuffer; + if (vertexBufferProxy) { + vertexBuffer = vertexBufferProxy->getBuffer(); + if (vertexBuffer == nullptr) { + return; + } + } else if (vertexData == nullptr) { + return; + } auto pipeline = createPipeline( renderPass, QuadPerEdgeAAGeometryProcessor::Make(renderPass->renderTarget()->width(), renderPass->renderTarget()->height(), aa, hasColor)); renderPass->bindProgramAndScissorClip(pipeline.get(), scissorRect()); - renderPass->bindBuffers(indexBuffer, vertexBuffer); - if (needsIndexBuffer()) { + if (vertexBuffer) { + renderPass->bindBuffers(indexBuffer, vertexBuffer); + } else { + renderPass->bindBuffers(indexBuffer, vertexData); + } + if (indexBuffer != nullptr) { uint16_t numIndicesPerQuad; if (aa == AAType::Coverage) { numIndicesPerQuad = ResourceProvider::NumIndicesPerAAQuad(); diff --git a/src/gpu/ops/FillRectOp.h b/src/gpu/ops/RectDrawOp.h similarity index 79% rename from src/gpu/ops/FillRectOp.h rename to src/gpu/ops/RectDrawOp.h index fc917f69..7259357c 100644 --- a/src/gpu/ops/FillRectOp.h +++ b/src/gpu/ops/RectDrawOp.h @@ -24,20 +24,20 @@ namespace tgfx { class RectPaint; -class FillRectOp : public DrawOp { +class RectDrawOp : public DrawOp { public: DEFINE_OP_CLASS_ID - static std::unique_ptr Make(std::optional color, const Rect& rect, + static std::unique_ptr Make(std::optional color, const Rect& rect, const Matrix& viewMatrix, const Matrix* uvMatrix = nullptr); - void prepare(Context* context) override; + void prepare(Context* context, uint32_t renderFlags) override; void execute(RenderPass* renderPass) override; private: - FillRectOp(std::optional color, const Rect& rect, const Matrix& viewMatrix, + RectDrawOp(std::optional color, const Rect& rect, const Matrix& viewMatrix, const Matrix* uvMatrix = nullptr); bool onCombineIfPossible(Op* op) override; @@ -48,7 +48,8 @@ class FillRectOp : public DrawOp { bool hasColor = true; std::vector> rectPaints = {}; - std::shared_ptr vertexBufferProxy; - std::shared_ptr indexBufferProxy; + std::shared_ptr indexBufferProxy = nullptr; + std::shared_ptr vertexBufferProxy = nullptr; + std::shared_ptr vertexData = nullptr; }; } // namespace tgfx diff --git a/src/gpu/ops/ShapeDrawOp.cpp b/src/gpu/ops/ShapeDrawOp.cpp new file mode 100644 index 00000000..cdd756a1 --- /dev/null +++ b/src/gpu/ops/ShapeDrawOp.cpp @@ -0,0 +1,167 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "ShapeDrawOp.h" +#include "core/PathRef.h" +#include "core/PathTriangulator.h" +#include "core/ShapeRasterizer.h" +#include "core/utils/StrokeKey.h" +#include "gpu/ProxyProvider.h" +#include "gpu/Quad.h" +#include "gpu/processors/DefaultGeometryProcessor.h" +#include "gpu/processors/TextureEffect.h" +#include "tgfx/core/Buffer.h" + +namespace tgfx { +std::unique_ptr ShapeDrawOp::Make(Color color, const Path& path, + const Matrix& viewMatrix, const Stroke* stroke) { + if (path.isEmpty()) { + return nullptr; + } + + return std::unique_ptr(new ShapeDrawOp(color, path, viewMatrix, stroke)); +} + +ShapeDrawOp::ShapeDrawOp(Color color, Path p, const Matrix& viewMatrix, const Stroke* stroke) + : DrawOp(ClassID()), color(color), path(std::move(p)), viewMatrix(viewMatrix), + stroke(stroke ? new Stroke(*stroke) : nullptr) { + auto bounds = path.getBounds(); + viewMatrix.mapRect(&bounds); + setBounds(bounds); +} + +ShapeDrawOp::~ShapeDrawOp() { + delete stroke; +} + +bool ShapeDrawOp::onCombineIfPossible(Op*) { + return false; +} + +void ShapeDrawOp::prepare(Context* context, uint32_t renderFlags) { + BytesKey bytesKey = {}; + auto scales = viewMatrix.getAxisScales(); + if (scales.x == scales.y) { + rasterizeMatrix.setScale(scales.x, scales.y); + bytesKey.reserve(1 + (stroke ? StrokeKeyCount : 0)); + bytesKey.write(scales.x); + } else { + rasterizeMatrix = viewMatrix; + rasterizeMatrix.setTranslateX(0); + rasterizeMatrix.setTranslateY(0); + bytesKey.reserve(4 + (stroke ? StrokeKeyCount : 0)); + bytesKey.write(rasterizeMatrix.getScaleX()); + bytesKey.write(rasterizeMatrix.getSkewX()); + bytesKey.write(rasterizeMatrix.getSkewY()); + bytesKey.write(rasterizeMatrix.getScaleY()); + } + if (stroke) { + WriteStrokeKey(&bytesKey, stroke); + } + auto uniqueKey = UniqueKey::Append(PathRef::GetUniqueKey(path), bytesKey.data(), bytesKey.size()); + auto bounds = path.getBounds(); + if (stroke) { + bounds.outset(stroke->width, stroke->width); + } + rasterizeMatrix.mapRect(&bounds); + rasterizeMatrix.postTranslate(-bounds.x(), -bounds.y()); + auto width = ceilf(bounds.width()); + auto height = ceilf(bounds.height()); + auto rasterizer = Rasterizer::MakeFrom(path, ISize::Make(width, height), rasterizeMatrix, + aa == AAType::Coverage, stroke); + shapeProxy = + context->proxyProvider()->createGpuShapeProxy(uniqueKey, std::move(rasterizer), renderFlags); +} + +static std::shared_ptr MakeVertexData(const Rect& rect) { + Buffer buffer(8 * sizeof(float)); + auto vertices = static_cast(buffer.data()); + auto quad = Quad::MakeFrom(rect); + int index = 0; + for (size_t i = 4; i >= 1; --i) { + vertices[index++] = quad.point(i - 1).x; + vertices[index++] = quad.point(i - 1).y; + } + return buffer.release(); +} + +static std::shared_ptr MakeAAVertexData(const Rect& rect) { + Buffer buffer(24 * sizeof(float)); + auto vertices = static_cast(buffer.data()); + // There is no need to scale the padding by the view matrix scale, as the view matrix scale is + // already applied to the path. + auto padding = 0.5f; + auto insetBounds = rect.makeInset(padding, padding); + auto insetQuad = Quad::MakeFrom(insetBounds); + auto outsetBounds = rect.makeOutset(padding, padding); + auto outsetQuad = Quad::MakeFrom(outsetBounds); + auto index = 0; + for (int j = 0; j < 2; ++j) { + const auto& quad = j == 0 ? insetQuad : outsetQuad; + auto coverage = j == 0 ? 1.0f : 0.0f; + for (size_t k = 0; k < 4; ++k) { + vertices[index++] = quad.point(k).x; + vertices[index++] = quad.point(k).y; + vertices[index++] = coverage; + } + } + return buffer.release(); +} + +void ShapeDrawOp::execute(RenderPass* renderPass) { + if (shapeProxy == nullptr) { + return; + } + Matrix uvMatrix = {}; + if (!rasterizeMatrix.invert(&uvMatrix)) { + return; + } + auto realViewMatrix = viewMatrix; + realViewMatrix.preConcat(uvMatrix); + auto vertexBuffer = shapeProxy->getTriangles(); + std::shared_ptr vertexData = nullptr; + if (vertexBuffer == nullptr) { + auto textureProxy = shapeProxy->getTextureProxy(); + if (textureProxy == nullptr) { + return; + } + auto rect = Rect::MakeWH(textureProxy->width(), textureProxy->height()); + vertexData = aa == AAType::Coverage ? MakeAAVertexData(rect) : MakeVertexData(rect); + auto maskFP = TextureEffect::Make(std::move(textureProxy), {}, &rasterizeMatrix, true); + if (maskFP == nullptr) { + return; + } + addCoverageFP(std::move(maskFP)); + } + auto pipeline = createPipeline( + renderPass, DefaultGeometryProcessor::Make(color, renderPass->renderTarget()->width(), + renderPass->renderTarget()->height(), aa, + realViewMatrix, uvMatrix)); + renderPass->bindProgramAndScissorClip(pipeline.get(), scissorRect()); + auto vertexDataSize = vertexBuffer ? vertexBuffer->size() : vertexData->size(); + auto vertexCount = aa == AAType::Coverage ? PathTriangulator::GetAATriangleCount(vertexDataSize) + : PathTriangulator::GetTriangleCount(vertexDataSize); + if (vertexBuffer != nullptr) { + renderPass->bindBuffers(nullptr, vertexBuffer); + renderPass->draw(PrimitiveType::Triangles, 0, vertexCount); + } else { + renderPass->bindBuffers(nullptr, vertexData); + renderPass->draw(PrimitiveType::TriangleStrip, 0, vertexCount); + } +} +} // namespace tgfx diff --git a/src/gpu/ops/TriangulatingPathOp.h b/src/gpu/ops/ShapeDrawOp.h similarity index 66% rename from src/gpu/ops/TriangulatingPathOp.h rename to src/gpu/ops/ShapeDrawOp.h index 0253bf39..deacdc0c 100644 --- a/src/gpu/ops/TriangulatingPathOp.h +++ b/src/gpu/ops/ShapeDrawOp.h @@ -19,23 +19,21 @@ #pragma once #include "DrawOp.h" -#include "gpu/GpuBuffer.h" +#include "gpu/proxies/GpuShapeProxy.h" #include "tgfx/core/Path.h" #include "tgfx/core/Stroke.h" namespace tgfx { -class TriangulatingPathOp : public DrawOp { +class ShapeDrawOp : public DrawOp { public: DEFINE_OP_CLASS_ID - static std::unique_ptr Make(Color color, const Path& path, - const Matrix& viewMatrix, - const Stroke* stroke = nullptr, - uint32_t renderFlags = 0); + static std::unique_ptr Make(Color color, const Path& path, const Matrix& viewMatrix, + const Stroke* stroke = nullptr); - ~TriangulatingPathOp() override; + ~ShapeDrawOp() override; - void prepare(Context* context) override; + void prepare(Context* context, uint32_t renderFlags) override; void execute(RenderPass* renderPass) override; @@ -48,10 +46,8 @@ class TriangulatingPathOp : public DrawOp { Matrix viewMatrix = Matrix::I(); Matrix rasterizeMatrix = Matrix::I(); Stroke* stroke = nullptr; - uint32_t renderFlags = 0; - std::shared_ptr vertexBuffer = nullptr; + std::shared_ptr shapeProxy = nullptr; - TriangulatingPathOp(Color color, Path path, const Matrix& viewMatrix, const Stroke* stroke, - uint32_t renderFlags); + ShapeDrawOp(Color color, Path path, const Matrix& viewMatrix, const Stroke* stroke); }; } // namespace tgfx diff --git a/src/gpu/ops/TriangulatingPathOp.cpp b/src/gpu/ops/TriangulatingPathOp.cpp deleted file mode 100644 index 7cdacd93..00000000 --- a/src/gpu/ops/TriangulatingPathOp.cpp +++ /dev/null @@ -1,157 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// 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 "TriangulatingPathOp.h" -#include "core/PathRef.h" -#include "core/PathTriangulator.h" -#include "core/utils/StrokeKey.h" -#include "gpu/Gpu.h" -#include "gpu/ProxyProvider.h" -#include "gpu/processors/DefaultGeometryProcessor.h" -#include "tgfx/core/PathEffect.h" - -namespace tgfx { -class PathTriangles : public DataProvider { - public: - static std::shared_ptr Make(Path path, const Matrix& matrix, const Stroke* stroke, - AAType aaType) { - if (path.isEmpty()) { - return nullptr; - } - return std::shared_ptr( - new PathTriangles(std::move(path), matrix, stroke, aaType)); - } - - ~PathTriangles() override { - delete stroke; - } - - std::shared_ptr getData() const override { - std::vector vertices = {}; - auto finalPath = path; - if (stroke != nullptr) { - stroke->applyToPath(&finalPath); - } - finalPath.transform(matrix); - auto clipBounds = finalPath.getBounds(); - size_t count = 0; - if (aaType == AAType::Coverage) { - count = PathTriangulator::ToAATriangles(finalPath, clipBounds, &vertices); - } else { - count = PathTriangulator::ToTriangles(finalPath, clipBounds, &vertices); - } - if (count == 0) { - // The path is not a filled path, or it is invisible. - return nullptr; - } - return Data::MakeWithCopy(vertices.data(), vertices.size() * sizeof(float)); - } - - private: - Path path = {}; - Matrix matrix = Matrix::I(); - Stroke* stroke = nullptr; - AAType aaType = AAType::None; - - PathTriangles(Path path, const Matrix& matrix, const Stroke* s, AAType aaType) - : path(std::move(path)), matrix(matrix), aaType(aaType) { - if (s) { - stroke = new Stroke(*s); - } - } -}; - -std::unique_ptr TriangulatingPathOp::Make(Color color, const Path& path, - const Matrix& viewMatrix, - const Stroke* stroke, - uint32_t renderFlags) { - if (path.isEmpty()) { - return nullptr; - } - return std::unique_ptr( - new TriangulatingPathOp(color, path, viewMatrix, stroke, renderFlags)); -} - -TriangulatingPathOp::TriangulatingPathOp(Color color, Path p, const Matrix& viewMatrix, - const Stroke* stroke, uint32_t renderFlags) - : DrawOp(ClassID()), color(color), path(std::move(p)), viewMatrix(viewMatrix), - stroke(stroke ? new Stroke(*stroke) : nullptr), renderFlags(renderFlags) { - auto bounds = path.getBounds(); - viewMatrix.mapRect(&bounds); - setBounds(bounds); -} - -TriangulatingPathOp::~TriangulatingPathOp() { - delete stroke; -} - -bool TriangulatingPathOp::onCombineIfPossible(Op*) { - return false; -} - -void TriangulatingPathOp::prepare(Context* context) { - static const auto TriangulatingPathType = UniqueID::Next(); - BytesKey bytesKey = {}; - auto scales = viewMatrix.getAxisScales(); - if (scales.x == scales.y) { - rasterizeMatrix.setScale(scales.x, scales.y); - bytesKey.reserve(2 + (stroke ? StrokeKeyCount : 0)); - bytesKey.write(TriangulatingPathType); - bytesKey.write(scales.x); - } else { - rasterizeMatrix = viewMatrix; - rasterizeMatrix.setTranslateX(0); - rasterizeMatrix.setTranslateY(0); - bytesKey.reserve(5 + (stroke ? StrokeKeyCount : 0)); - bytesKey.write(TriangulatingPathType); - bytesKey.write(rasterizeMatrix.getScaleX()); - bytesKey.write(rasterizeMatrix.getSkewX()); - bytesKey.write(rasterizeMatrix.getSkewY()); - bytesKey.write(rasterizeMatrix.getScaleY()); - } - if (stroke) { - WriteStrokeKey(&bytesKey, stroke); - } - auto uniqueKey = UniqueKey::Combine(PathRef::GetUniqueKey(path), bytesKey); - auto pathTriangles = PathTriangles::Make(path, rasterizeMatrix, stroke, aa); - vertexBuffer = context->proxyProvider()->createGpuBufferProxy(uniqueKey, pathTriangles, - BufferType::Vertex, renderFlags); -} - -void TriangulatingPathOp::execute(RenderPass* renderPass) { - auto buffer = vertexBuffer->getBuffer(); - if (buffer == nullptr) { - return; - } - Matrix uvMatrix = {}; - if (!rasterizeMatrix.invert(&uvMatrix)) { - return; - } - auto realViewMatrix = viewMatrix; - realViewMatrix.preConcat(uvMatrix); - auto pipeline = createPipeline( - renderPass, DefaultGeometryProcessor::Make(color, renderPass->renderTarget()->width(), - renderPass->renderTarget()->height(), aa, - realViewMatrix, uvMatrix)); - renderPass->bindProgramAndScissorClip(pipeline.get(), scissorRect()); - renderPass->bindBuffers(nullptr, buffer); - auto vertexCount = aa == AAType::Coverage ? PathTriangulator::GetAATriangleCount(buffer->size()) - : PathTriangulator::GetTriangleCount(buffer->size()); - renderPass->draw(PrimitiveType::Triangles, 0, vertexCount); -} -} // namespace tgfx diff --git a/src/gpu/proxies/GpuBufferProxy.cpp b/src/gpu/proxies/GpuBufferProxy.cpp index c386677f..a74c0d41 100644 --- a/src/gpu/proxies/GpuBufferProxy.cpp +++ b/src/gpu/proxies/GpuBufferProxy.cpp @@ -22,20 +22,24 @@ namespace tgfx { std::shared_ptr GpuBufferProxy::MakeFrom(Context* context, std::shared_ptr data, - BufferType bufferType) { + BufferType bufferType, + uint32_t renderFlags) { if (context == nullptr) { return nullptr; } - return context->proxyProvider()->createGpuBufferProxy({}, std::move(data), bufferType); + return context->proxyProvider()->createGpuBufferProxy({}, std::move(data), bufferType, + renderFlags); } std::shared_ptr GpuBufferProxy::MakeFrom(Context* context, std::shared_ptr dataProvider, - BufferType bufferType) { + BufferType bufferType, + uint32_t renderFlags) { if (context == nullptr) { return nullptr; } - return context->proxyProvider()->createGpuBufferProxy({}, std::move(dataProvider), bufferType); + return context->proxyProvider()->createGpuBufferProxy({}, std::move(dataProvider), bufferType, + renderFlags); } GpuBufferProxy::GpuBufferProxy(UniqueKey uniqueKey, BufferType bufferType) diff --git a/src/gpu/proxies/GpuBufferProxy.h b/src/gpu/proxies/GpuBufferProxy.h index 4882e5f0..2415458d 100644 --- a/src/gpu/proxies/GpuBufferProxy.h +++ b/src/gpu/proxies/GpuBufferProxy.h @@ -29,13 +29,13 @@ class GpuBufferProxy : public ResourceProxy { * Creates a GpuBufferProxy from the given data. */ static std::shared_ptr MakeFrom(Context* context, std::shared_ptr data, - BufferType bufferType); + BufferType bufferType, uint32_t renderFlags); /** * Creates a GpuBufferProxy from the given data provider. */ static std::shared_ptr MakeFrom(Context* context, std::shared_ptr dataProvider, - BufferType bufferType); + BufferType bufferType, uint32_t renderFlags); /** * Returns the type of the buffer. @@ -51,7 +51,6 @@ class GpuBufferProxy : public ResourceProxy { private: BufferType _bufferType = BufferType::Vertex; - size_t _size = 0; GpuBufferProxy(UniqueKey uniqueKey, BufferType bufferType); diff --git a/src/gpu/proxies/GpuShapeProxy.h b/src/gpu/proxies/GpuShapeProxy.h new file mode 100644 index 00000000..ad15589f --- /dev/null +++ b/src/gpu/proxies/GpuShapeProxy.h @@ -0,0 +1,43 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "gpu/proxies/GpuBufferProxy.h" +#include "gpu/proxies/TextureProxy.h" + +namespace tgfx { +class GpuShapeProxy { + public: + GpuShapeProxy(std::shared_ptr triangles, std::shared_ptr texture) + : triangles(std::move(triangles)), texture(std::move(texture)) { + } + + std::shared_ptr getTriangles() const { + return triangles ? triangles->getBuffer() : nullptr; + } + + std::shared_ptr getTextureProxy() const { + return texture; + } + + private: + std::shared_ptr triangles = nullptr; + std::shared_ptr texture = nullptr; +}; +} // namespace tgfx diff --git a/src/gpu/tasks/GpuBufferCreateTask.cpp b/src/gpu/tasks/GpuBufferCreateTask.cpp deleted file mode 100644 index 33fbacab..00000000 --- a/src/gpu/tasks/GpuBufferCreateTask.cpp +++ /dev/null @@ -1,110 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// 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 "GpuBufferCreateTask.h" -#include "tgfx/core/Task.h" - -namespace tgfx { -class GpuBufferCreator : public GpuBufferCreateTask { - public: - GpuBufferCreator(UniqueKey uniqueKey, BufferType bufferType, - std::shared_ptr provider) - : GpuBufferCreateTask(std::move(uniqueKey), bufferType), provider(std::move(provider)) { - } - - protected: - std::shared_ptr getData() override { - if (provider == nullptr) { - return nullptr; - } - return provider->getData(); - } - - private: - std::shared_ptr provider = nullptr; -}; - -struct DataHolder { - std::shared_ptr data; -}; - -class AsyncGpuBufferCreator : public GpuBufferCreateTask { - public: - AsyncGpuBufferCreator(UniqueKey uniqueKey, BufferType bufferType, - std::shared_ptr dataProvider) - : GpuBufferCreateTask(std::move(uniqueKey), bufferType), provider(std::move(dataProvider)) { - holder = std::make_shared(); - task = Task::Run( - [result = holder, dataProvider = provider]() { result->data = dataProvider->getData(); }); - } - - ~AsyncGpuBufferCreator() override { - task->cancel(); - } - - protected: - std::shared_ptr getData() override { - if (provider == nullptr) { - return nullptr; - } - task->wait(); - auto data = holder->data; - if (data != nullptr) { - // Free the data provider immediately to reduce memory pressure. - provider = nullptr; - holder = nullptr; - } - return data; - } - - private: - std::shared_ptr provider = nullptr; - std::shared_ptr holder = nullptr; - std::shared_ptr task = nullptr; -}; - -std::shared_ptr GpuBufferCreateTask::MakeFrom( - UniqueKey uniqueKey, BufferType bufferType, std::shared_ptr provider, - bool async) { - if (provider == nullptr) { - return nullptr; - } - if (async) { - return std::make_shared(std::move(uniqueKey), bufferType, - std::move(provider)); - } - return std::make_shared(std::move(uniqueKey), bufferType, std::move(provider)); -} - -GpuBufferCreateTask::GpuBufferCreateTask(UniqueKey uniqueKey, BufferType bufferType) - : ResourceTask(std::move(uniqueKey)), bufferType(bufferType) { -} - -std::shared_ptr GpuBufferCreateTask::onMakeResource(Context* context) { - auto data = getData(); - if (data == nullptr || data->empty()) { - // We don't need to log an error here because the getData() should have already logged an error. - return nullptr; - } - auto gpuBuffer = GpuBuffer::Make(context, data->data(), data->size(), bufferType); - if (gpuBuffer == nullptr) { - LOGE("GpuBufferCreateTask::onMakeResource failed to create GpuBuffer"); - } - return gpuBuffer; -} -} // namespace tgfx diff --git a/src/gpu/tasks/GpuBufferUploadTask.cpp b/src/gpu/tasks/GpuBufferUploadTask.cpp new file mode 100644 index 00000000..c218418d --- /dev/null +++ b/src/gpu/tasks/GpuBufferUploadTask.cpp @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "GpuBufferUploadTask.h" +#include "tgfx/core/Task.h" + +namespace tgfx { +std::shared_ptr GpuBufferUploadTask::MakeFrom( + UniqueKey uniqueKey, BufferType bufferType, std::shared_ptr provider) { + if (provider == nullptr) { + return nullptr; + } + return std::shared_ptr( + new GpuBufferUploadTask(std::move(uniqueKey), bufferType, std::move(provider))); +} + +GpuBufferUploadTask::GpuBufferUploadTask(UniqueKey uniqueKey, BufferType bufferType, + std::shared_ptr provider) + : ResourceTask(std::move(uniqueKey)), bufferType(bufferType), provider(std::move(provider)) { +} + +std::shared_ptr GpuBufferUploadTask::onMakeResource(Context* context) { + if (provider == nullptr) { + return nullptr; + } + auto data = provider->getData(); + if (data == nullptr || data->empty()) { + LOGE("GpuBufferUploadTask::onMakeResource() Failed to get data!"); + return nullptr; + } + auto gpuBuffer = GpuBuffer::Make(context, bufferType, data->data(), data->size()); + if (gpuBuffer == nullptr) { + LOGE("GpuBufferUploadTask::onMakeResource failed to upload the GpuBuffer!"); + } else { + // Free the data provider immediately to reduce memory pressure. + provider = nullptr; + } + return gpuBuffer; +} +} // namespace tgfx diff --git a/src/gpu/tasks/GpuBufferCreateTask.h b/src/gpu/tasks/GpuBufferUploadTask.h similarity index 67% rename from src/gpu/tasks/GpuBufferCreateTask.h rename to src/gpu/tasks/GpuBufferUploadTask.h index acd98652..fe7064e5 100644 --- a/src/gpu/tasks/GpuBufferCreateTask.h +++ b/src/gpu/tasks/GpuBufferUploadTask.h @@ -23,24 +23,22 @@ #include "gpu/GpuBuffer.h" namespace tgfx { -class GpuBufferCreateTask : public ResourceTask { +class GpuBufferUploadTask : public ResourceTask { public: /** - * Create a new GpuBufferCreateTask to generate a GpuBuffer with the given data provider. If async - * is true, the data provider will be read from an asynchronous thread immediately. Otherwise, the - * data provider will be read from the current thread when the GpuBufferCreateTask is executed. + * Create a new GpuBufferUploadTask to generate a GpuBuffer with the given data provider. */ - static std::shared_ptr MakeFrom(UniqueKey uniqueKey, BufferType bufferType, - std::shared_ptr provider, - bool async); + static std::shared_ptr MakeFrom(UniqueKey uniqueKey, BufferType bufferType, + std::shared_ptr provider); protected: - BufferType bufferType = BufferType::Vertex; - - GpuBufferCreateTask(UniqueKey uniqueKey, BufferType bufferType); - std::shared_ptr onMakeResource(Context* context) override; - virtual std::shared_ptr getData() = 0; + private: + BufferType bufferType = BufferType::Vertex; + std::shared_ptr provider = nullptr; + + GpuBufferUploadTask(UniqueKey uniqueKey, BufferType bufferType, + std::shared_ptr provider); }; } // namespace tgfx diff --git a/src/gpu/tasks/OpsRenderTask.cpp b/src/gpu/tasks/OpsRenderTask.cpp index 0d2c31bd..2cdfd785 100644 --- a/src/gpu/tasks/OpsRenderTask.cpp +++ b/src/gpu/tasks/OpsRenderTask.cpp @@ -31,7 +31,7 @@ void OpsRenderTask::addOp(std::unique_ptr op) { void OpsRenderTask::prepare(Context* context) { renderPass = context->gpu()->getRenderPass(); for (auto& op : ops) { - op->prepare(context); + op->prepare(context, renderFlags); } } diff --git a/src/gpu/tasks/OpsRenderTask.h b/src/gpu/tasks/OpsRenderTask.h index 920f7564..89b44b4b 100644 --- a/src/gpu/tasks/OpsRenderTask.h +++ b/src/gpu/tasks/OpsRenderTask.h @@ -25,8 +25,8 @@ namespace tgfx { class OpsRenderTask : public RenderTask { public: - OpsRenderTask(std::shared_ptr renderTargetProxy) - : RenderTask(std::move(renderTargetProxy)) { + OpsRenderTask(std::shared_ptr renderTargetProxy, uint32_t renderFlags) + : RenderTask(std::move(renderTargetProxy)), renderFlags(renderFlags) { } void addOp(std::unique_ptr op); @@ -45,6 +45,7 @@ class OpsRenderTask : public RenderTask { private: bool closed = false; + uint32_t renderFlags = 0; std::shared_ptr renderPass = nullptr; std::vector> ops = {}; }; diff --git a/src/gpu/tasks/ResourceTask.h b/src/gpu/tasks/ResourceTask.h index f3c5cd0b..b03236a7 100644 --- a/src/gpu/tasks/ResourceTask.h +++ b/src/gpu/tasks/ResourceTask.h @@ -34,14 +34,13 @@ class ResourceTask { /** * Returns false if the resource creation is failed. */ - bool execute(Context* context); + virtual bool execute(Context* context); protected: - virtual std::shared_ptr onMakeResource(Context* context) = 0; - - private: UniqueKey uniqueKey = {}; + virtual std::shared_ptr onMakeResource(Context* context) = 0; + friend class DrawingManager; }; } // namespace tgfx diff --git a/src/gpu/tasks/ShapeBufferUploadTask.cpp b/src/gpu/tasks/ShapeBufferUploadTask.cpp new file mode 100644 index 00000000..05866c26 --- /dev/null +++ b/src/gpu/tasks/ShapeBufferUploadTask.cpp @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "ShapeBufferUploadTask.h" +#include "gpu/GpuBuffer.h" +#include "gpu/Texture.h" + +namespace tgfx { +std::shared_ptr ShapeBufferUploadTask::MakeFrom( + UniqueKey trianglesKey, UniqueKey textureKey, std::shared_ptr provider) { + if (provider == nullptr) { + return nullptr; + } + return std::shared_ptr(new ShapeBufferUploadTask( + std::move(trianglesKey), std::move(textureKey), std::move(provider))); +} + +ShapeBufferUploadTask::ShapeBufferUploadTask(UniqueKey trianglesKey, UniqueKey textureKey, + std::shared_ptr provider) + : ResourceTask(std::move(trianglesKey)), textureKey(std::move(textureKey)), + provider(std::move(provider)) { +} + +bool ShapeBufferUploadTask::execute(Context* context) { + if (uniqueKey.strongCount() <= 0) { + // Skip the resource creation if there is no proxy is referencing it. + return false; + } + if (provider == nullptr) { + return false; + } + auto shapeBuffer = provider->getBuffer(); + if (shapeBuffer == nullptr) { + LOGE("ShapeBufferUploadTask::execute() Failed to get the shape buffer!"); + return false; + } + if (auto triangles = shapeBuffer->triangles()) { + auto gpuBuffer = + GpuBuffer::Make(context, BufferType::Vertex, triangles->data(), triangles->size()); + if (!gpuBuffer) { + LOGE("ShapeBufferUploadTask::execute() Failed to create the GpuBuffer!"); + return false; + } + gpuBuffer->assignUniqueKey(uniqueKey); + } else { + auto imageBuffer = shapeBuffer->imageBuffer(); + auto texture = Texture::MakeFrom(context, std::move(imageBuffer)); + if (!texture) { + LOGE("ShapeBufferUploadTask::execute() Failed to create the texture!"); + return false; + } + texture->assignUniqueKey(textureKey); + } + // Free the data provider immediately to reduce memory pressure. + provider = nullptr; + return true; +} +} // namespace tgfx diff --git a/src/gpu/tasks/ShapeBufferUploadTask.h b/src/gpu/tasks/ShapeBufferUploadTask.h new file mode 100644 index 00000000..883a7e0d --- /dev/null +++ b/src/gpu/tasks/ShapeBufferUploadTask.h @@ -0,0 +1,52 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "ResourceTask.h" +#include "core/ShapeBuffer.h" + +namespace tgfx { +class ShapeBufferProvider { + public: + virtual ~ShapeBufferProvider() = default; + + virtual std::shared_ptr getBuffer() const = 0; +}; + +class ShapeBufferUploadTask : public ResourceTask { + public: + static std::shared_ptr MakeFrom( + UniqueKey trianglesKey, UniqueKey textureKey, std::shared_ptr provider); + + bool execute(Context* context) override; + + protected: + std::shared_ptr onMakeResource(Context*) override { + // The execute() method is already overridden, so this method should never be called. + return nullptr; + } + + private: + UniqueKey textureKey = {}; + std::shared_ptr provider = nullptr; + + ShapeBufferUploadTask(UniqueKey trianglesKey, UniqueKey textureKey, + std::shared_ptr provider); +}; +} // namespace tgfx diff --git a/src/gpu/tasks/TextureCreateTask.cpp b/src/gpu/tasks/TextureCreateTask.cpp index 5dac85ea..1bc816ca 100644 --- a/src/gpu/tasks/TextureCreateTask.cpp +++ b/src/gpu/tasks/TextureCreateTask.cpp @@ -20,30 +20,6 @@ #include "gpu/Texture.h" namespace tgfx { -class EmptyTextureTask : public TextureCreateTask { - public: - EmptyTextureTask(UniqueKey uniqueKey, int width, int height, PixelFormat format, bool mipmapped, - ImageOrigin origin) - : TextureCreateTask(std::move(uniqueKey)), width(width), height(height), format(format), - mipmapped(mipmapped), origin(origin) { - } - - std::shared_ptr onMakeResource(Context* context) override { - auto texture = Texture::MakeFormat(context, width, height, format, mipmapped, origin); - if (texture == nullptr) { - LOGE("EmptyTextureTask::onMakeResource() Failed to create the texture!"); - } - return texture; - } - - private: - int width = 0; - int height = 0; - PixelFormat format = PixelFormat::RGBA_8888; - bool mipmapped = false; - ImageOrigin origin = ImageOrigin::TopLeft; -}; - std::shared_ptr TextureCreateTask::MakeFrom(UniqueKey uniqueKey, int width, int height, PixelFormat format, bool mipmapped, ImageOrigin origin) { @@ -51,44 +27,21 @@ std::shared_ptr TextureCreateTask::MakeFrom(UniqueKey uniqueK return nullptr; } return std::shared_ptr( - new EmptyTextureTask(std::move(uniqueKey), width, height, format, mipmapped, origin)); + new TextureCreateTask(std::move(uniqueKey), width, height, format, mipmapped, origin)); } -class ImageDecoderTask : public TextureCreateTask { - public: - ImageDecoderTask(UniqueKey uniqueKey, std::shared_ptr decoder, bool mipmapped) - : TextureCreateTask(std::move(uniqueKey)), decoder(std::move(decoder)), mipmapped(mipmapped) { - } - - std::shared_ptr onMakeResource(Context* context) override { - if (decoder == nullptr) { - return nullptr; - } - auto imageBuffer = decoder->decode(); - if (imageBuffer == nullptr) { - LOGE("ImageDecoderTask::onMakeResource() Failed to decode the image!"); - return nullptr; - } - auto texture = Texture::MakeFrom(context, imageBuffer, mipmapped); - if (texture == nullptr) { - LOGE("ImageDecoderTask::onMakeResource() Failed to create the texture!"); - } else { - // Free the decoded image buffer immediately to reduce memory pressure. - decoder = nullptr; - } - return texture; - } - - private: - std::shared_ptr decoder = nullptr; - bool mipmapped = false; -}; +TextureCreateTask::TextureCreateTask(UniqueKey uniqueKey, int width, int height, PixelFormat format, + bool mipmapped, ImageOrigin origin) + : ResourceTask(std::move(uniqueKey)), width(width), height(height), format(format), + mipmapped(mipmapped), origin(origin) { +} -std::shared_ptr TextureCreateTask::MakeFrom( - UniqueKey uniqueKey, std::shared_ptr decoder, bool mipmapped) { - if (decoder == nullptr) { - return nullptr; +std::shared_ptr TextureCreateTask::onMakeResource(Context* context) { + auto texture = Texture::MakeFormat(context, width, height, format, mipmapped, origin); + if (texture == nullptr) { + LOGE("TextureCreateTask::onMakeResource() Failed to create the texture!"); } - return std::make_shared(std::move(uniqueKey), std::move(decoder), mipmapped); + return texture; } + } // namespace tgfx diff --git a/src/gpu/tasks/TextureCreateTask.h b/src/gpu/tasks/TextureCreateTask.h index c6031877..e29ecd34 100644 --- a/src/gpu/tasks/TextureCreateTask.h +++ b/src/gpu/tasks/TextureCreateTask.h @@ -19,28 +19,29 @@ #pragma once #include "ResourceTask.h" -#include "core/ImageDecoder.h" #include "tgfx/gpu/ImageOrigin.h" namespace tgfx { class TextureCreateTask : public ResourceTask { public: /** - * Creates a TextureCreateTask to generate a texture using the specified size and format. + * Creates a TextureCreateTask to create a texture using the specified size and format. */ static std::shared_ptr MakeFrom(UniqueKey uniqueKey, int width, int height, PixelFormat format, bool mipmapped = false, ImageOrigin origin = ImageOrigin::TopLeft); - /* - * Creates a TextureCreateTask to generate a texture using the given ImageBuffer. - */ - static std::shared_ptr MakeFrom(UniqueKey uniqueKey, - std::shared_ptr imageDecoder, - bool mipmapped = false); + std::shared_ptr onMakeResource(Context* context) override; + + private: + int width = 0; + int height = 0; + PixelFormat format = PixelFormat::RGBA_8888; + bool mipmapped = false; + ImageOrigin origin = ImageOrigin::TopLeft; - protected: - explicit TextureCreateTask(UniqueKey uniqueKey) : ResourceTask(std::move(uniqueKey)) { - } + TextureCreateTask(UniqueKey uniqueKey, int width, int height, PixelFormat format, bool mipmapped, + ImageOrigin origin); }; + } // namespace tgfx diff --git a/src/gpu/tasks/TextureUploadTask.cpp b/src/gpu/tasks/TextureUploadTask.cpp new file mode 100644 index 00000000..0d60bb67 --- /dev/null +++ b/src/gpu/tasks/TextureUploadTask.cpp @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "TextureUploadTask.h" +#include "gpu/Texture.h" + +namespace tgfx { +std::shared_ptr TextureUploadTask::MakeFrom( + UniqueKey uniqueKey, std::shared_ptr decoder, bool mipmapped) { + if (decoder == nullptr) { + return nullptr; + } + return std::shared_ptr( + new TextureUploadTask(std::move(uniqueKey), std::move(decoder), mipmapped)); +} + +TextureUploadTask::TextureUploadTask(UniqueKey uniqueKey, std::shared_ptr decoder, + bool mipmapped) + : ResourceTask(std::move(uniqueKey)), decoder(std::move(decoder)), mipmapped(mipmapped) { +} + +std::shared_ptr TextureUploadTask::onMakeResource(Context* context) { + if (decoder == nullptr) { + return nullptr; + } + auto imageBuffer = decoder->decode(); + if (imageBuffer == nullptr) { + LOGE("TextureUploadTask::onMakeResource() Failed to decode the image!"); + return nullptr; + } + auto texture = Texture::MakeFrom(context, imageBuffer, mipmapped); + if (texture == nullptr) { + LOGE("TextureUploadTask::onMakeResource() Failed to upload the texture!"); + } else { + // Free the decoded image buffer immediately to reduce memory pressure. + decoder = nullptr; + } + return texture; +} +} // namespace tgfx diff --git a/src/gpu/tasks/TextureUploadTask.h b/src/gpu/tasks/TextureUploadTask.h new file mode 100644 index 00000000..0cdf66ea --- /dev/null +++ b/src/gpu/tasks/TextureUploadTask.h @@ -0,0 +1,42 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 "ResourceTask.h" +#include "core/ImageDecoder.h" + +namespace tgfx { +class TextureUploadTask : public ResourceTask { + public: + /* + * Creates a TextureUploadTask to generate a texture using the given image decoder. + */ + static std::shared_ptr MakeFrom(UniqueKey uniqueKey, + std::shared_ptr imageDecoder, + bool mipmapped = false); + + std::shared_ptr onMakeResource(Context* context) override; + + private: + std::shared_ptr decoder = nullptr; + bool mipmapped = false; + + TextureUploadTask(UniqueKey uniqueKey, std::shared_ptr decoder, bool mipmapped); +}; +} // namespace tgfx diff --git a/test/baseline/version.json b/test/baseline/version.json index 13ef3949..8723f729 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -2,7 +2,7 @@ "CanvasTest": { "Clip": "d010fb8", "NothingToDraw": "d010fb8", - "Picture": "7fd9cd9", + "Picture": "d069eb4", "PictureImage": "8a2ac4d", "PictureImage_Path": "2212c4e", "PictureImage_Text": "2212c4e", @@ -23,7 +23,7 @@ "mipmap_linear_texture_effect": "d010fb8", "mipmap_nearest": "d010fb8", "mipmap_none": "d010fb8", - "path": "d4f3780", + "path": "d069eb4", "rasterized": "f55f586", "rasterized_mipmap": "f55f586", "rasterized_scale_up": "721c9e0", @@ -63,17 +63,17 @@ }, "LayerTest": { "Layer_hitTestPoint": "50c58e6", - "Layer_hitTestPointNested": "50c58e6", + "Layer_hitTestPointNested": "d069eb4", "ModeColorFilter": "43cd416", "PassThoughAndNormal": "43cd416", - "draw_shape": "612c09e", + "draw_shape": "d069eb4", "draw_solid": "b4a1231", "draw_text": "612c09e", "dropShadow": "43cd416", "filterClip": "43cd416", "filterTest": "43cd416", - "getBounds": "af2e3ff", - "getLayersUnderPoint": "50c58e6", + "getBounds": "d069eb4", + "getLayersUnderPoint": "d069eb4", "greyColorMatrix": "a1605b2", "identityMatrix": "a1605b2", "imageLayer": "99a5cd9", @@ -157,6 +157,6 @@ "ImageSnapshot_Surface2": "d010fb8" }, "TextAlignTest": { - "TextAlign": "83c88bb" + "TextAlign": "d069eb4" } } diff --git a/test/src/CanvasTest.cpp b/test/src/CanvasTest.cpp index 9da36085..444f5ce4 100644 --- a/test/src/CanvasTest.cpp +++ b/test/src/CanvasTest.cpp @@ -24,8 +24,8 @@ #include "gpu/Texture.h" #include "gpu/opengl/GLCaps.h" #include "gpu/opengl/GLSampler.h" -#include "gpu/ops/FillRectOp.h" -#include "gpu/ops/RRectOp.h" +#include "gpu/ops/RRectDrawOp.h" +#include "gpu/ops/RectDrawOp.h" #include "tgfx/core/Buffer.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/ImageCodec.h" @@ -142,7 +142,7 @@ TGFX_TEST(CanvasTest, merge_draw_call_rect) { EXPECT_TRUE(drawingManager->renderTasks.size() == 1); auto task = std::static_pointer_cast(drawingManager->renderTasks[0]); ASSERT_TRUE(task->ops.size() == 2); - EXPECT_EQ(static_cast(task->ops[1].get())->rectPaints.size(), drawCallCount); + EXPECT_EQ(static_cast(task->ops[1].get())->rectPaints.size(), drawCallCount); context->flush(); EXPECT_TRUE(Baseline::Compare(surface, "CanvasTest/merge_draw_call_rect")); device->unlock(); @@ -183,7 +183,7 @@ TGFX_TEST(CanvasTest, merge_draw_call_rrect) { EXPECT_TRUE(drawingManager->renderTasks.size() == 1); auto task = std::static_pointer_cast(drawingManager->renderTasks[0]); ASSERT_TRUE(task->ops.size() == 2); - EXPECT_EQ(static_cast(task->ops[1].get())->rRectPaints.size(), drawCallCount); + EXPECT_EQ(static_cast(task->ops[1].get())->rRectPaints.size(), drawCallCount); context->flush(); EXPECT_TRUE(Baseline::Compare(surface, "CanvasTest/merge_draw_call_rrect")); device->unlock(); diff --git a/test/src/ImageReaderTest.cpp b/test/src/ImageReaderTest.cpp index 53bc54f7..a65060ab 100644 --- a/test/src/ImageReaderTest.cpp +++ b/test/src/ImageReaderTest.cpp @@ -17,7 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "gpu/DrawingManager.h" -#include "gpu/ops/FillRectOp.h" +#include "gpu/ops/RectDrawOp.h" #include "tgfx/core/ImageReader.h" #include "tgfx/core/Mask.h" #include "tgfx/core/PathEffect.h"