[graphite] Set up atlas-based clipping.
Disabled for now as there are some correctness issues. But includes:
* Adds genID to ClipStack::SaveRecord for atlas key generation
* Adds use of ClipAtlas to find/create SW clip entries
* Fixes null analytic clip when used with atlas clip
* Various other fixes and clean up
Bug: b/388810102
Change-Id: Ia595dff40c67f3f0a7eeb1ea5df9cdaa56aeef34
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/928449
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/graphite/ClipAtlasManager.cpp b/src/gpu/graphite/ClipAtlasManager.cpp
index 6b6e312..4f16ced 100644
--- a/src/gpu/graphite/ClipAtlasManager.cpp
+++ b/src/gpu/graphite/ClipAtlasManager.cpp
@@ -19,8 +19,8 @@
ClipAtlasManager::ClipAtlasManager(Recorder* recorder) : fRecorder(recorder) {
static constexpr SkColorType kColorType = kAlpha_8_SkColorType;
- static constexpr int kWidth = 2048;
- static constexpr int kHeight = 2048;
+ static constexpr int kWidth = 4096;
+ static constexpr int kHeight = 4096;
const Caps* caps = recorder->priv().caps();
fDrawAtlas = DrawAtlas::Make(kColorType,
@@ -130,14 +130,9 @@
// Draw the shape; based on how we've initialized the buffer and chosen alpha+invert,
// every element is drawn with the kReplace_Op
- if (invert) {
- // Must invert the path
- SkASSERT(!e.fShape.inverted());
- // TODO: this is an extra copy effectively, just so we can toggle inversion; would be
- // better perhaps to just call a drawPath() since we know it'll use path rendering w/
- // the inverse fill type.
+ if (invert != e.fShape.inverted()) {
Shape inverted(e.fShape);
- inverted.setInverted(true);
+ inverted.setInverted(invert);
helper->drawClip(inverted, e.fLocalToDevice, alpha, resultBounds);
} else {
helper->drawClip(e.fShape, e.fLocalToDevice, alpha, resultBounds);
diff --git a/src/gpu/graphite/ClipStack_graphite.cpp b/src/gpu/graphite/ClipStack_graphite.cpp
index 8c65492..b4a1125 100644
--- a/src/gpu/graphite/ClipStack_graphite.cpp
+++ b/src/gpu/graphite/ClipStack_graphite.cpp
@@ -10,12 +10,16 @@
#include "include/core/SkMatrix.h"
#include "include/core/SkShader.h"
#include "include/core/SkStrokeRec.h"
+#include "include/gpu/graphite/Recorder.h"
#include "src/base/SkTLazy.h"
#include "src/core/SkPathPriv.h"
#include "src/core/SkRRectPriv.h"
#include "src/core/SkRectPriv.h"
+#include "src/gpu/graphite/AtlasProvider.h"
+#include "src/gpu/graphite/ClipAtlasManager.h"
#include "src/gpu/graphite/Device.h"
#include "src/gpu/graphite/DrawParams.h"
+#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/geom/BoundsManager.h"
#include "src/gpu/graphite/geom/Geometry.h"
@@ -35,6 +39,22 @@
}
}
+static constexpr uint32_t kInvalidGenID = 0;
+static constexpr uint32_t kEmptyGenID = 1;
+static constexpr uint32_t kWideOpenGenID = 2;
+
+uint32_t next_gen_id() {
+ // 0-2 are reserved for invalid, empty & wide-open
+ static const uint32_t kFirstUnreservedGenID = 3;
+ static std::atomic<uint32_t> nextID{kFirstUnreservedGenID};
+
+ uint32_t id;
+ do {
+ id = nextID.fetch_add(1, std::memory_order_relaxed);
+ } while (id < kFirstUnreservedGenID);
+ return id;
+}
+
bool oriented_bbox_intersection(const Rect& a, const Transform& aXform,
const Rect& b, const Transform& bXform) {
// NOTE: We intentionally exclude projected bounds for two reasons:
@@ -641,7 +661,8 @@
, fOldestValidIndex(0)
, fDeferredSaveCount(0)
, fStackOp(SkClipOp::kIntersect)
- , fState(ClipState::kWideOpen) {}
+ , fState(ClipState::kWideOpen)
+ , fGenID(kInvalidGenID) {}
ClipStack::SaveRecord::SaveRecord(const SaveRecord& prior,
int startingElementIndex)
@@ -652,12 +673,26 @@
, fOldestValidIndex(prior.fOldestValidIndex)
, fDeferredSaveCount(0)
, fStackOp(prior.fStackOp)
- , fState(prior.fState) {
+ , fState(prior.fState)
+ , fGenID(kInvalidGenID) {
// If the prior record added an element, this one will insert into the same index
// (that's okay since we'll remove it when this record is popped off the stack).
SkASSERT(startingElementIndex >= prior.fStartingElementIndex);
}
+uint32_t ClipStack::SaveRecord::genID() const {
+ if (fState == ClipState::kEmpty) {
+ return kEmptyGenID;
+ } else if (fState == ClipState::kWideOpen) {
+ return kWideOpenGenID;
+ } else {
+ // The gen ID shouldn't be empty or wide open, since they are reserved for the above
+ // if-cases. It may be kInvalid if the record hasn't had any elements added to it yet.
+ SkASSERT(fGenID != kEmptyGenID && fGenID != kWideOpenGenID);
+ return fGenID;
+ }
+}
+
ClipStack::ClipState ClipStack::SaveRecord::state() const {
if (fShader && fState != ClipState::kEmpty) {
return ClipState::kComplex;
@@ -932,6 +967,8 @@
elements->back() = std::move(toAdd);
}
+ // Changing this will prompt ClipStack to invalidate any masks associated with this record.
+ fGenID = next_gen_id();
return true;
}
@@ -961,6 +998,7 @@
// This invalidates all older elements that are owned by save records lower in the clip stack.
fOldestValidIndex = fStartingElementIndex;
+ fGenID = next_gen_id();
}
///////////////////////////////////////////////////////////////////////////////
@@ -1190,6 +1228,7 @@
const Geometry& geometry,
const SkStrokeRec& style,
bool outsetBoundsForAA,
+ bool msaaSupported,
ClipStack::ElementList* outEffectiveElements) const {
static const Clip kClippedOut = {
Rect::InfiniteInverted(), Rect::InfiniteInverted(), SkIRect::MakeEmpty(),
@@ -1352,6 +1391,31 @@
}
}
+#if defined(SK_GRAPHITE_ENABLE_CLIP_ATLAS)
+ // If there is no MSAA supported, rasterize any remaining elements by flattening them
+ // into a single mask and storing in an atlas. Otherwise these will be handled by
+ // Device::drawClip().
+ AtlasProvider* atlasProvider = fDevice->recorder()->priv().atlasProvider();
+ if (!msaaSupported && !outEffectiveElements->empty()) {
+ ClipAtlasManager* clipAtlas = atlasProvider->getClipAtlasManager();
+ SkASSERT(clipAtlas);
+ AtlasClip* atlasClip = &nonMSAAClip.fAtlasClip;
+
+ const TextureProxy* proxy = clipAtlas->findOrCreateEntry(cs.genID(),
+ outEffectiveElements,
+ cs.outerBounds(),
+ &atlasClip->fOutPos);
+ if (proxy) {
+ // Add to Clip
+ atlasClip->fMaskBounds = scissor;
+ atlasClip->fAtlasTexture = sk_ref_sp(proxy);
+
+ // Elements are represented in the clip atlas, discard.
+ outEffectiveElements->clear();
+ }
+ }
+#endif
+
return Clip(drawBounds, transformedShapeBounds, scissor.asSkIRect(), nonMSAAClip, cs.shader());
}
diff --git a/src/gpu/graphite/ClipStack_graphite.h b/src/gpu/graphite/ClipStack_graphite.h
index f738eee..1c1cff9 100644
--- a/src/gpu/graphite/ClipStack_graphite.h
+++ b/src/gpu/graphite/ClipStack_graphite.h
@@ -97,6 +97,7 @@
const Geometry&,
const SkStrokeRec&,
bool outsetBoundsForAA,
+ bool msaaSupported,
ElementList* outEffectiveElements) const;
// Update the per-clip element state for later rendering using pre-computed clip state data for
@@ -273,6 +274,7 @@
const Rect& innerBounds() const { return fInnerBounds; }
SkClipOp op() const { return fStackOp; }
ClipState state() const;
+ uint32_t genID() const;
int firstActiveElementIndex() const { return fStartingElementIndex; }
int oldestElementIndex() const { return fOldestValidIndex; }
@@ -329,6 +331,7 @@
// because if kDifference then there is an implicit extra outer bounds at the device edges.
SkClipOp fStackOp;
ClipState fState;
+ uint32_t fGenID;
};
Rect deviceBounds() const;
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index 7b39362..ed88963 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -1272,7 +1272,7 @@
ClipStack::ElementList clipElements;
const Clip clip =
fClip.visitClipStackForDraw(localToDevice, geometry, style, outsetBoundsForAA,
- &clipElements);
+ fMSAASupported, &clipElements);
if (clip.isClippedOut()) {
// Clipped out, so don't record anything.
return;
@@ -1284,8 +1284,8 @@
: SkBlendMode::kSrcOver;
Coverage rendererCoverage = renderer ? renderer->coverage()
: Coverage::kSingleChannel;
- if ((clip.shader() || !clip.nonMSAAClip().isEmpty()) && rendererCoverage == Coverage::kNone) {
- // Must upgrade to single channel coverage if there is a clip shader or analytic clip;
+ if (clip.needsCoverage() && rendererCoverage == Coverage::kNone) {
+ // Must upgrade to single channel coverage if the clip requires coverage;
// but preserve LCD coverage if the Renderer uses that.
rendererCoverage = Coverage::kSingleChannel;
}
diff --git a/src/gpu/graphite/DrawParams.h b/src/gpu/graphite/DrawParams.h
index a96f4ab..f807f8e 100644
--- a/src/gpu/graphite/DrawParams.h
+++ b/src/gpu/graphite/DrawParams.h
@@ -98,6 +98,8 @@
bool isClippedOut() const { return fDrawBounds.isEmptyNegativeOrNaN(); }
+ bool needsCoverage() const { return SkToBool(fShader) || !fNonMSAAClip.isEmpty(); }
+
private:
// DrawList assumes the DrawBounds are correct for a given shape, transform, and style. They
// are provided to the DrawList to avoid re-calculating the same bounds.
diff --git a/src/gpu/graphite/PaintParams.cpp b/src/gpu/graphite/PaintParams.cpp
index 6eca33d..085e580 100644
--- a/src/gpu/graphite/PaintParams.cpp
+++ b/src/gpu/graphite/PaintParams.cpp
@@ -258,11 +258,20 @@
PipelineDataGatherer* gatherer) const {
if (!fNonMSAAClip.isEmpty()) {
const AnalyticClip& analyticClip = fNonMSAAClip.fAnalyticClip;
- float radius = analyticClip.fRadius + 0.5f;
- // N.B.: Because the clip data is normally used with depth-based clipping,
- // the shape is inverted from its usual state. We re-invert here to
- // match what the shader snippet expects.
- SkPoint radiusPair = {(analyticClip.fInverted) ? radius : -radius, 1.0f/radius};
+ SkPoint radiusPair;
+ SkRect analyticBounds;
+ if (!analyticClip.isEmpty()) {
+ float radius = analyticClip.fRadius + 0.5f;
+ // N.B.: Because the clip data is normally used with depth-based clipping,
+ // the shape is inverted from its usual state. We re-invert here to
+ // match what the shader snippet expects.
+ radiusPair = {(analyticClip.fInverted) ? radius : -radius, 1.0f/radius};
+ analyticBounds = analyticClip.fBounds.makeOutset(0.5f).asSkRect();
+ } else {
+ // This will generate no analytic clip.
+ radiusPair = { -0.5f, 1.f };
+ analyticBounds = { 0, 0, 0, 0 };
+ }
const AtlasClip& atlasClip = fNonMSAAClip.fAtlasClip;
skvx::float2 maskSize = atlasClip.fMaskBounds.size();
@@ -272,7 +281,7 @@
atlasClip.fOutPos.y() - atlasClip.fMaskBounds.top()};
NonMSAAClipBlock::NonMSAAClipData data(
- analyticClip.fBounds.makeOutset(0.5f).asSkRect(),
+ analyticBounds,
radiusPair,
analyticClip.edgeSelectRect(),
texCoordOffset,
diff --git a/src/gpu/graphite/RasterPathUtils.cpp b/src/gpu/graphite/RasterPathUtils.cpp
index cafd33e..a62ae8a 100644
--- a/src/gpu/graphite/RasterPathUtils.cpp
+++ b/src/gpu/graphite/RasterPathUtils.cpp
@@ -39,11 +39,15 @@
return true;
}
+void RasterMaskHelper::clear(uint8_t alpha, const SkIRect& shapeBounds) {
+ fPixels->erase(SkColorSetARGB(alpha, 0xFF, 0xFF, 0xFF), shapeBounds);
+}
+
void RasterMaskHelper::drawShape(const Shape& shape,
const Transform& localToDevice,
const SkStrokeRec& strokeRec,
- const SkIRect& resultBounds) {
- fRasterClip.setRect(resultBounds);
+ const SkIRect& shapeBounds) {
+ fRasterClip.setRect(shapeBounds);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc); // "Replace" mode
@@ -56,8 +60,8 @@
// The atlas transform of the shape is `localToDevice` translated by the top-left offset of the
// resultBounds and the inverse of the base mask transform offset for the current set of shapes.
// We will need to translate draws so the bound's UL corner is at the origin
- translatedMatrix.postTranslate(resultBounds.x() - fTransformedMaskOffset.x(),
- resultBounds.y() - fTransformedMaskOffset.y());
+ translatedMatrix.postTranslate(shapeBounds.x() - fTransformedMaskOffset.x(),
+ shapeBounds.y() - fTransformedMaskOffset.y());
fDraw.fCTM = &translatedMatrix;
// TODO: use drawRect, drawRRect, drawArc
diff --git a/src/gpu/graphite/RasterPathUtils.h b/src/gpu/graphite/RasterPathUtils.h
index 3dd05c1..b5f09c1 100644
--- a/src/gpu/graphite/RasterPathUtils.h
+++ b/src/gpu/graphite/RasterPathUtils.h
@@ -45,13 +45,7 @@
bool init(SkISize pixmapSize, skvx::float2 transformedMaskOffset);
- void clear(uint8_t alpha, const SkIRect& resultBounds) {
- SkPaint paint;
- SkMatrix identity;
- paint.setColor(SkColorSetARGB(alpha, 0xFF, 0xFF, 0xFF));
- fDraw.fCTM = &identity;
- fDraw.drawRect(SkRect::Make(resultBounds), paint);
- }
+ void clear(uint8_t alpha, const SkIRect& resultBounds);
// Draw a single shape into the bitmap (as a path) at location resultBounds.
void drawShape(const Shape& shape,