| /* | |
| * Copyright 2022 Rive | |
| */ | |
| #ifndef _RIVE_SERIALIZING_FACTORY_HPP_ | |
| #define _RIVE_SERIALIZING_FACTORY_HPP_ | |
| #include "rive/factory.hpp" | |
| #include "rive/core/vector_binary_writer.hpp" | |
| #include "rive/renderer/cmd/deferred_canvas_host.hpp" | |
| #include <memory> | |
| #include <unordered_map> | |
| #include <vector> | |
| namespace rive | |
| { | |
| namespace gpu | |
| { | |
| class RenderContext; | |
| class RenderCanvas; | |
| } // namespace gpu | |
| // A factory that generates render objects which serialize their rendering | |
| // commands into one buffer that can then be used to replay the commands in a | |
| // viewer app or compare them to ensure that subsequent runs generate the same | |
| // commands. | |
| // | |
| // It is also a DeferredCanvasHost, which is what lets a [silver] test exercise | |
| // cache-as-bitmap: an artboard with a BitmapCache child only rasterizes | |
| // offscreen when its factory answers both renderContext() and | |
| // deferredCanvasHost(). Off by default so existing goldens are untouched -- | |
| // call enableBitmapCache() to turn it on. | |
| class SerializingFactory : public Factory, public cmd::DeferredCanvasHost | |
| { | |
| public: | |
| SerializingFactory(); | |
| ~SerializingFactory() override; | |
| // Wires the two hooks Artboard::drawCachedAsBitmap requires. The artboard | |
| // uses renderContext only to mint the offscreen canvas | |
| // (makeDeferredRenderCanvas), which touches no device, so a null-device | |
| // context such as RenderContextNULL::MakeContext() is enough. Canvas | |
| // content then records inline into this factory's stream, bracketed by | |
| // canvasContentBegin/End, and the composite draws the canvas id. | |
| void enableBitmapCache(gpu::RenderContext* renderContext); | |
| Factory* renderContext() override; | |
| cmd::DeferredCanvasHost* deferredCanvasHost() override; | |
| cmd::DeferredCanvasHost* canvasContentHost() override; | |
| // The op is recorded by applyLayerMask below and applied at replay by | |
| // serialized_replay.cpp, so this host does honour it. Explicit because the | |
| // capability now defaults to no. | |
| bool supportsLayerMask() const override { return true; } | |
| // ---- DeferredCanvasHost ---- | |
| Renderer* beginCanvasContent(gpu::RenderCanvas* canvas, | |
| uint32_t clearColor) override; | |
| void endCanvasContent(gpu::RenderCanvas* canvas) override; | |
| rcp<gpu::RenderCanvas> makeContentCanvas(uint32_t width, | |
| uint32_t height) override; | |
| rcp<RenderImage> contentCanvasImage(gpu::RenderCanvas* canvas) override; | |
| // Stream id for a drawable image. Canvas-backed images are registered | |
| // here; everything else carries its own id. | |
| uint64_t imageId(const RenderImage*) const; | |
| rcp<RenderBuffer> makeRenderBuffer(RenderBufferType, | |
| RenderBufferFlags, | |
| size_t) override; | |
| rcp<ImageMeshInstances> makeImageMeshInstances(size_t count) override; | |
| rcp<RenderShader> makeLinearGradient(float sx, | |
| float sy, | |
| float ex, | |
| float ey, | |
| const ColorInt colors[], // [count] | |
| const float stops[], // [count] | |
| size_t count) override; | |
| rcp<RenderShader> makeRadialGradient(float cx, | |
| float cy, | |
| float radius, | |
| const ColorInt colors[], // [count] | |
| const float stops[], // [count] | |
| size_t count) override; | |
| rcp<RenderPath> makeRenderPath(RawPath&, FillRule) override; | |
| rcp<RenderPath> makeEmptyRenderPath() override; | |
| rcp<RenderPaint> makeRenderPaint() override; | |
| rcp<RenderImage> decodeImage(Span<const uint8_t>) override; | |
| std::unique_ptr<Renderer> makeRenderer(); | |
| void addFrame(); | |
| void frameSize(uint32_t width, uint32_t height); | |
| void save(const char* filename); | |
| bool matches(const char* filename); | |
| // Recorded SRIV stream for replay via serialized_replay.hpp. | |
| Span<const uint8_t> bytes() const | |
| { | |
| return Span<const uint8_t>(m_buffer.data(), m_buffer.size()); | |
| } | |
| private: | |
| void saveTarnished(const char* filename); | |
| // Allocates and records the canvas's id on first sight, then returns it. | |
| uint64_t canvasId(gpu::RenderCanvas* canvas); | |
| std::vector<uint8_t> m_buffer; | |
| VectorBinaryWriter m_writer; | |
| // Non-null once enableBitmapCache() has run. | |
| gpu::RenderContext* m_bitmapCacheContext = nullptr; | |
| // Canvas-backed images live in the same id space as decoded ones so a | |
| // composite is just an ordinary drawImage. Keyed by pointer, so every | |
| // canvas ever registered is retained below: a canvas that died could | |
| // otherwise have its address recycled by the next one and inherit its id, | |
| // which makes the recording depend on the allocator. | |
| std::unordered_map<const RenderImage*, uint64_t> m_canvasImageIds; | |
| std::vector<rcp<gpu::RenderCanvas>> m_retainedCanvases; | |
| std::unique_ptr<Renderer> m_canvasRenderer; | |
| uint64_t m_renderImageId = 0; | |
| uint64_t m_renderPaintId = 0; | |
| uint64_t m_renderPathId = 0; | |
| uint64_t m_renderBufferId = 0; | |
| uint64_t m_renderShaderId = 0; | |
| uint64_t m_imageMeshInstancesId = 0; | |
| }; | |
| } // namespace rive | |
| #endif |