Skip to content

Commit

Permalink
[graphite] Set up atlas-based clipping.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Commit-Queue: Jim Van Verth <[email protected]>
  • Loading branch information
jvanverth authored and SkCQ committed Feb 3, 2025
1 parent 2e61d30 commit 1c083af
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 31 deletions.
13 changes: 4 additions & 9 deletions src/gpu/graphite/ClipAtlasManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ namespace skgpu::graphite {

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,
Expand Down Expand Up @@ -130,14 +130,9 @@ void draw_to_sw_mask(RasterMaskHelper* helper,

// 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);
Expand Down
68 changes: 66 additions & 2 deletions src/gpu/graphite/ClipStack_graphite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -35,6 +39,22 @@ Rect subtract(const Rect& a, const Rect& b, bool exact) {
}
}

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:
Expand Down Expand Up @@ -641,7 +661,8 @@ ClipStack::SaveRecord::SaveRecord(const Rect& deviceBounds)
, fOldestValidIndex(0)
, fDeferredSaveCount(0)
, fStackOp(SkClipOp::kIntersect)
, fState(ClipState::kWideOpen) {}
, fState(ClipState::kWideOpen)
, fGenID(kInvalidGenID) {}

ClipStack::SaveRecord::SaveRecord(const SaveRecord& prior,
int startingElementIndex)
Expand All @@ -652,12 +673,26 @@ ClipStack::SaveRecord::SaveRecord(const SaveRecord& prior,
, 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;
Expand Down Expand Up @@ -932,6 +967,8 @@ bool ClipStack::SaveRecord::appendElement(RawElement&& toAdd,
elements->back() = std::move(toAdd);
}

// Changing this will prompt ClipStack to invalidate any masks associated with this record.
fGenID = next_gen_id();
return true;
}

Expand Down Expand Up @@ -961,6 +998,7 @@ void ClipStack::SaveRecord::replaceWithElement(RawElement&& toAdd,

// This invalidates all older elements that are owned by save records lower in the clip stack.
fOldestValidIndex = fStartingElementIndex;
fGenID = next_gen_id();
}

///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1190,6 +1228,7 @@ Clip ClipStack::visitClipStackForDraw(const Transform& localToDevice,
const Geometry& geometry,
const SkStrokeRec& style,
bool outsetBoundsForAA,
bool msaaSupported,
ClipStack::ElementList* outEffectiveElements) const {
static const Clip kClippedOut = {
Rect::InfiniteInverted(), Rect::InfiniteInverted(), SkIRect::MakeEmpty(),
Expand Down Expand Up @@ -1352,6 +1391,31 @@ Clip ClipStack::visitClipStackForDraw(const Transform& localToDevice,
}
}

#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());
}

Expand Down
3 changes: 3 additions & 0 deletions src/gpu/graphite/ClipStack_graphite.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class ClipStack {
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
Expand Down Expand Up @@ -273,6 +274,7 @@ class ClipStack {
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; }
Expand Down Expand Up @@ -329,6 +331,7 @@ class ClipStack {
// 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;
Expand Down
6 changes: 3 additions & 3 deletions src/gpu/graphite/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1272,7 +1272,7 @@ void Device::drawGeometry(const Transform& localToDevice,
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;
Expand All @@ -1284,8 +1284,8 @@ void Device::drawGeometry(const Transform& localToDevice,
: 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;
}
Expand Down
2 changes: 2 additions & 0 deletions src/gpu/graphite/DrawParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class Clip {

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.
Expand Down
21 changes: 15 additions & 6 deletions src/gpu/graphite/PaintParams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,20 @@ void PaintParams::handleClipping(const KeyContext& keyContext,
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();
Expand All @@ -272,7 +281,7 @@ void PaintParams::handleClipping(const KeyContext& keyContext,
atlasClip.fOutPos.y() - atlasClip.fMaskBounds.top()};

NonMSAAClipBlock::NonMSAAClipData data(
analyticClip.fBounds.makeOutset(0.5f).asSkRect(),
analyticBounds,
radiusPair,
analyticClip.edgeSelectRect(),
texCoordOffset,
Expand Down
12 changes: 8 additions & 4 deletions src/gpu/graphite/RasterPathUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ bool RasterMaskHelper::init(SkISize pixmapSize, skvx::float2 transformedMaskOffs
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
Expand All @@ -56,8 +60,8 @@ void RasterMaskHelper::drawShape(const Shape& shape,
// 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
Expand Down
8 changes: 1 addition & 7 deletions src/gpu/graphite/RasterPathUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,7 @@ class RasterMaskHelper : SkNoncopyable {

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,
Expand Down

0 comments on commit 1c083af

Please sign in to comment.