Flutter Engine
The Flutter Engine
PathAtlas.h
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef skgpu_graphite_PathAtlas_DEFINED
9#define skgpu_graphite_PathAtlas_DEFINED
10
13#include "src/core/SkTHash.h"
14#include "src/gpu/AtlasTypes.h"
15#include "src/gpu/ResourceKey.h"
18
19namespace skgpu::graphite {
20
21class Caps;
22class DrawContext;
23class Recorder;
24class Rect;
25class Renderer;
26class Shape;
27class TextureProxy;
28class Transform;
29
30/**
31 * PathAtlas manages one or more atlas textures that store coverage masks for path rendering.
32 *
33 * The contents of a PathAtlas are intended to be transient: atlas regions are considered valid only
34 * for the scope of the render passes that sample them. Unlike DrawAtlas, PathAtlas does not
35 * necessarily support partial eviction and reuse of subregions. In most subclasses, once an atlas
36 * texture is filled up all of its sub-allocations must be invalidated before it can be reused.
37 *
38 * PathAtlas does not prescribe how atlas contents get uploaded to the GPU. The specific task
39 * mechanism is defined by subclasses.
40 */
41class PathAtlas {
42public:
43 /**
44 * The PathAtlas will use textures of the requested size or the system's maximum texture size,
45 * whichever is smaller.
46 */
47 PathAtlas(Recorder* recorder, uint32_t requestedWidth, uint32_t requestedHeight);
48 virtual ~PathAtlas();
49
50 using MaskAndOrigin = std::pair<CoverageMaskShape, SkIPoint>;
51
52 // Subclasses should ensure that the recorded masks have this much padding around each entry.
53 // PathAtlas passes in un-padded sizes to onAddShape and assumes that padding has been included
54 // in the outPos value.
55 static constexpr int kEntryPadding = 1;
56
57 /**
58 * Searches the atlas for a slot that can fit a coverage mask for a clipped shape with the given
59 * bounds in device coordinates and submits the mask to be drawn into the found atlas region.
60 * For atlases that cache coverage masks, will first search the cache before adding.
61 *
62 * Returns an empty result if a the shape cannot fit in the atlas. Otherwise, returns the
63 * CoverageMaskShape (including the texture proxy) for sampling the eventually-rendered coverage
64 * mask and the device-space origin the mask should be drawn at (e.g. its recorded draw should
65 * be an integer translation matrix), and the Renderer that should be used to draw that shape.
66 * The Renderer should have single-channel coverage, require AA bounds outsetting, and have a
67 * single renderStep.
68 *
69 * The bounds of the atlas entry is laid out with a 1 pixel outset from the given dimensions.
70 * The returned shape's UV origin accounts for the padding, and its mask size does not include
71 * the padding. This allows the mask to be sampled safely with linear filtering without worrying
72 * about HW filtering accessing pixels from other entries.
73 *
74 * `shape` will be drawn after applying the linear components (scale, rotation, skew) of the
75 * provided `localToDevice` transform. This is done by translating the shape by the inverse of
76 * the rounded out `transformedShapeBounds` offset. For an unclipped shape this amounts to
77 * translating it back to its origin while preserving any sub-pixel translation. For a clipped
78 * shape, this ensures that the visible portions of the mask are centered in the atlas slot
79 * while invisible portions that would lie outside the atlas slot get clipped out.
80 *
81 * `addShape()` schedules the shape to be drawn but when and how the rendering happens is
82 * specified by the subclass implementation.
83 *
84 * The stroke-and-fill style is drawn as a single combined coverage mask containing the stroke
85 * and the fill.
86 */
87 std::pair<const Renderer*, std::optional<MaskAndOrigin>> addShape(
88 const Rect& transformedShapeBounds,
89 const Shape& shape,
90 const Transform& localToDevice,
91 const SkStrokeRec& style);
92
93 /**
94 * Returns true if a path coverage mask with the given device-space bounds is sufficiently
95 * small to benefit from atlasing without causing too many atlas renders.
96 *
97 * `transformedShapeBounds` represents the device-space bounds of the coverage mask shape
98 * unrestricted by clip and viewport bounds.
99 *
100 * `clipBounds` represents the conservative bounding box of the union of the clip stack that
101 * should apply to the shape.
102 */
103 virtual bool isSuitableForAtlasing(const Rect& transformedShapeBounds,
104 const Rect& clipBounds) const {
105 return true;
106 }
107
108 uint32_t width() const { return fWidth; }
109 uint32_t height() const { return fHeight; }
110
111protected:
112 // The 'transform' has been adjusted to draw the Shape into a logical image from (0,0) to
113 // 'maskSize'. The actual rendering into the returned TextureProxy will need to be further
114 // translated by the value written to 'outPos', which is the responsibility of subclasses.
115 virtual const TextureProxy* onAddShape(const Shape&,
116 const Transform& transform,
117 const SkStrokeRec&,
118 skvx::half2 maskSize,
119 skvx::half2* outPos) = 0;
120
121 // Wrapper class to manage DrawAtlas and associated caching operations
123 public:
124 const TextureProxy* findOrCreateEntry(Recorder* recorder,
125 const Shape& shape,
126 const Transform& transform,
127 const SkStrokeRec& strokeRec,
128 skvx::half2 maskSize,
129 skvx::half2* outPos);
130 // Adds to DrawAtlas but not the cache
131 const TextureProxy* addToAtlas(Recorder* recorder,
132 const Shape& shape,
133 const Transform& transform,
134 const SkStrokeRec& strokeRec,
135 skvx::half2 maskSize,
136 skvx::half2* outPos,
137 AtlasLocator* locator);
139 void evict(PlotLocator) override;
140 void postFlush(Recorder*);
141
142 protected:
143 DrawAtlasMgr(size_t width, size_t height,
144 size_t plotWidth, size_t plotHeight,
145 DrawAtlas::UseStorageTextures useStorageTextures,
146 std::string_view label, const Caps*);
147
148 bool virtual onAddToAtlas(const Shape&,
149 const Transform& transform,
150 const SkStrokeRec&,
151 SkIRect shapeBounds,
152 const AtlasLocator&) = 0;
153
154 std::unique_ptr<DrawAtlas> fDrawAtlas;
155
156 private:
157 // Tracks whether a shape is already in the DrawAtlas, and its location in the atlas
158 struct UniqueKeyHash {
159 uint32_t operator()(const skgpu::UniqueKey& key) const { return key.hash(); }
160 };
162 ShapeCache fShapeCache;
163
164 // List of stored keys per Plot, used to invalidate cache entries.
165 // When a Plot is invalidated via evict(), we'll get its index and Page index from the
166 // PlotLocator, index into the fKeyLists array to get the ShapeKeyList for that Plot,
167 // then iterate through the list and remove entries matching those keys from the ShapeCache.
168 struct ShapeKeyEntry {
169 skgpu::UniqueKey fKey;
171 };
172 using ShapeKeyList = SkTInternalLList<ShapeKeyEntry>;
173 SkTDArray<ShapeKeyList> fKeyLists;
174 };
175
176 // The Recorder that created and owns this Atlas.
178
179 uint32_t fWidth = 0;
180 uint32_t fHeight = 0;
181};
182
183} // namespace skgpu::graphite
184
185#endif // skgpu_graphite_PathAtlas_DEFINED
#define SK_DECLARE_INTERNAL_LLIST_INTERFACE(ClassName)
Shape
Definition: aaxfermodes.cpp:43
bool recordUploads(DrawContext *, Recorder *)
Definition: PathAtlas.cpp:184
void evict(PlotLocator) override
Definition: PathAtlas.cpp:188
const TextureProxy * addToAtlas(Recorder *recorder, const Shape &shape, const Transform &transform, const SkStrokeRec &strokeRec, skvx::half2 maskSize, skvx::half2 *outPos, AtlasLocator *locator)
Definition: PathAtlas.cpp:142
DrawAtlasMgr(size_t width, size_t height, size_t plotWidth, size_t plotHeight, DrawAtlas::UseStorageTextures useStorageTextures, std::string_view label, const Caps *)
Definition: PathAtlas.cpp:76
virtual bool onAddToAtlas(const Shape &, const Transform &transform, const SkStrokeRec &, SkIRect shapeBounds, const AtlasLocator &)=0
std::unique_ptr< DrawAtlas > fDrawAtlas
Definition: PathAtlas.h:154
const TextureProxy * findOrCreateEntry(Recorder *recorder, const Shape &shape, const Transform &transform, const SkStrokeRec &strokeRec, skvx::half2 maskSize, skvx::half2 *outPos)
Definition: PathAtlas.cpp:107
virtual const TextureProxy * onAddShape(const Shape &, const Transform &transform, const SkStrokeRec &, skvx::half2 maskSize, skvx::half2 *outPos)=0
std::pair< CoverageMaskShape, SkIPoint > MaskAndOrigin
Definition: PathAtlas.h:50
virtual bool isSuitableForAtlasing(const Rect &transformedShapeBounds, const Rect &clipBounds) const
Definition: PathAtlas.h:103
uint32_t height() const
Definition: PathAtlas.h:109
static constexpr int kEntryPadding
Definition: PathAtlas.h:55
std::pair< const Renderer *, std::optional< MaskAndOrigin > > addShape(const Rect &transformedShapeBounds, const Shape &shape, const Transform &localToDevice, const SkStrokeRec &style)
Definition: PathAtlas.cpp:38
PathAtlas(Recorder *recorder, uint32_t requestedWidth, uint32_t requestedHeight)
Definition: PathAtlas.cpp:26
uint32_t width() const
Definition: PathAtlas.h:108
TRect< Scalar > Rect
Definition: rect.h:769
skgpu::graphite::Transform Transform
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
Definition: SkRect.h:32
Definition: SkVx.h:83