Skip to content

Commit

Permalink
[graphite] Hook in atlas clip via PaintParams.
Browse files Browse the repository at this point in the history
Adds NonMSAAClipBlock to encapsulate both analytic clip and atlas clip
data, and a decision point in KeyHelpers to use the analytic clip
alone, or a combination shader.

Also renames any instances of CircularRRectClip to AnalyticClip, to
improve naming consistency and make it less specific -- in case we
want to change it in the future.

Bug: b/388812104
Change-Id: I28170faaecec90ecd349ba1098b44fadcf3c1bac
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/943080
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
jvanverth authored and SkCQ committed Jan 29, 2025
1 parent a009547 commit 05830bf
Showing 10 changed files with 145 additions and 115 deletions.
6 changes: 3 additions & 3 deletions src/gpu/graphite/BuiltInCodeSnippetID.h
Original file line number Diff line number Diff line change
@@ -69,10 +69,10 @@ enum class BuiltInCodeSnippetID : int32_t {
kPrimitiveColor,

// Analytic clip for circular roundrect and AA rect shapes
kCircularRRectClip,
kAnalyticClip,

// Atlas-based clip
kAtlasClip,
// Analytic plus atlas-based clip
kAnalyticAndAtlasClip,

kCompose, // compose 2 children together: outer_1(inner_0(...))
kBlendCompose, // compose 3 children together: outer_2(inner_0(...), inner_1(...))
22 changes: 11 additions & 11 deletions src/gpu/graphite/ClipStack_graphite.cpp
Original file line number Diff line number Diff line change
@@ -1102,8 +1102,8 @@ void ClipStack::clipShape(const Transform& localToDevice,
// rrects are supported. We assume these have been pre-transformed by the RawElement
// constructor, so only identity transforms are allowed.
namespace {
CircularRRectClip can_apply_analytic_clip(const Shape& shape,
const Transform& localToDevice) {
AnalyticClip can_apply_analytic_clip(const Shape& shape,
const Transform& localToDevice) {
if (localToDevice.type() != Transform::Type::kIdentity) {
return {};
}
@@ -1113,7 +1113,7 @@ CircularRRectClip can_apply_analytic_clip(const Shape& shape,

// Can handle Rect directly.
if (shape.isRect()) {
return {shape.rect(), kRadiusMin, CircularRRectClip::kNone_EdgeFlag, shape.inverted()};
return {shape.rect(), kRadiusMin, AnalyticClip::kNone_EdgeFlag, shape.inverted()};
}

// Otherwise we only handle certain kinds of RRects.
@@ -1127,10 +1127,10 @@ CircularRRectClip can_apply_analytic_clip(const Shape& shape,
if (radii.fX < kRadiusMin || radii.fY < kRadiusMin) {
// In this case the corners are extremely close to rectangular and we collapse the
// clip to a rectangular clip.
return {rrect.rect(), kRadiusMin, CircularRRectClip::kNone_EdgeFlag, shape.inverted()};
return {rrect.rect(), kRadiusMin, AnalyticClip::kNone_EdgeFlag, shape.inverted()};
}
if (SkScalarNearlyEqual(radii.fX, radii.fY)) {
return {rrect.rect(), radii.fX, CircularRRectClip::kAll_EdgeFlag, shape.inverted()};
return {rrect.rect(), radii.fX, AnalyticClip::kAll_EdgeFlag, shape.inverted()};
} else {
return {};
}
@@ -1139,10 +1139,10 @@ CircularRRectClip can_apply_analytic_clip(const Shape& shape,
if (rrect.isComplex() || rrect.isNinePatch()) {
// Check for the "tab" cases - two adjacent circular corners and two square corners.
constexpr uint32_t kCornerFlags[4] = {
CircularRRectClip::kTop_EdgeFlag | CircularRRectClip::kLeft_EdgeFlag,
CircularRRectClip::kTop_EdgeFlag | CircularRRectClip::kRight_EdgeFlag,
CircularRRectClip::kBottom_EdgeFlag | CircularRRectClip::kRight_EdgeFlag,
CircularRRectClip::kBottom_EdgeFlag | CircularRRectClip::kLeft_EdgeFlag,
AnalyticClip::kTop_EdgeFlag | AnalyticClip::kLeft_EdgeFlag,
AnalyticClip::kTop_EdgeFlag | AnalyticClip::kRight_EdgeFlag,
AnalyticClip::kBottom_EdgeFlag | AnalyticClip::kRight_EdgeFlag,
AnalyticClip::kBottom_EdgeFlag | AnalyticClip::kLeft_EdgeFlag,
};
SkScalar circularRadius = 0;
uint32_t edgeFlags = 0;
@@ -1167,13 +1167,13 @@ CircularRRectClip can_apply_analytic_clip(const Shape& shape,
edgeFlags |= kCornerFlags[corner];
}

if (edgeFlags == CircularRRectClip::kNone_EdgeFlag) {
if (edgeFlags == AnalyticClip::kNone_EdgeFlag) {
// It's a rect
return {rrect.rect(), kRadiusMin, edgeFlags, shape.inverted()};
} else {
// If any rounded corner pairs are non-adjacent or if there are three rounded
// corners all edge flags will be set, which is not valid.
if (edgeFlags == CircularRRectClip::kAll_EdgeFlag) {
if (edgeFlags == AnalyticClip::kAll_EdgeFlag) {
return {};
// At least one corner is rounded, or two adjacent corners are rounded.
} else {
62 changes: 36 additions & 26 deletions src/gpu/graphite/KeyHelpers.cpp
Original file line number Diff line number Diff line change
@@ -1123,47 +1123,57 @@ void ColorSpaceTransformBlock::AddBlock(const KeyContext& keyContext,
//--------------------------------------------------------------------------------------------------
namespace {

void add_circular_rrect_clip_data(
void add_analytic_clip_data(
const ShaderCodeDictionary* dict,
const CircularRRectClipBlock::CircularRRectClipData& data,
const NonMSAAClipBlock::NonMSAAClipData& data,
PipelineDataGatherer* gatherer) {
BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kCircularRRectClip)
BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAnalyticClip)
gatherer->write(data.fRect);
gatherer->write(data.fRadiusPlusHalf);
gatherer->writeHalf(data.fEdgeSelect);
}

} // anonymous namespace

void CircularRRectClipBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const CircularRRectClipData& data) {
add_circular_rrect_clip_data(keyContext.dict(), data, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kCircularRRectClip);
}

//--------------------------------------------------------------------------------------------------
namespace {

void add_atlas_clip_data(
void add_analytic_and_atlas_clip_data(
const ShaderCodeDictionary* dict,
const AtlasClipBlock::AtlasClipData& data,
const NonMSAAClipBlock::NonMSAAClipData& data,
PipelineDataGatherer* gatherer) {
BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAtlasClip)
BEGIN_WRITE_UNIFORMS(gatherer, dict, BuiltInCodeSnippetID::kAnalyticAndAtlasClip)
gatherer->write(data.fRect);
gatherer->write(data.fRadiusPlusHalf);
gatherer->writeHalf(data.fEdgeSelect);
gatherer->writeHalf(data.fTexCoordOffset);
gatherer->writeHalf(data.fMaskBounds);
gatherer->write(SkSize::Make(1.f/data.fAtlasSize.width(), 1.f/data.fAtlasSize.height()));
if (data.fAtlasTexture) {
gatherer->write(SkSize::Make(1.f/data.fAtlasTexture->dimensions().width(),
1.f/data.fAtlasTexture->dimensions().height()));
} else {
gatherer->write(SkSize::Make(0, 0));
}
}

} // anonymous namespace

void AtlasClipBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const AtlasClipData& data) {
add_atlas_clip_data(keyContext.dict(), data, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kAtlasClip);
void NonMSAAClipBlock::AddBlock(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer,
const NonMSAAClipData& data) {
if (data.fAtlasTexture) {
add_analytic_and_atlas_clip_data(keyContext.dict(), data, gatherer);
builder->beginBlock(BuiltInCodeSnippetID::kAnalyticAndAtlasClip);

const Caps* caps = keyContext.caps();
ImmutableSamplerInfo info =
caps->getImmutableSamplerInfo(data.fAtlasTexture->textureInfo());
SamplerDesc samplerDesc {SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone),
{SkTileMode::kClamp, SkTileMode::kClamp},
info};
gatherer->add(data.fAtlasTexture, samplerDesc);

builder->endBlock();
} else {
add_analytic_clip_data(keyContext.dict(), data, gatherer);
builder->addBlock(BuiltInCodeSnippetID::kAnalyticClip);
}
}

//--------------------------------------------------------------------------------------------------
42 changes: 18 additions & 24 deletions src/gpu/graphite/KeyHelpers.h
Original file line number Diff line number Diff line change
@@ -360,43 +360,37 @@ struct ColorSpaceTransformBlock {
const ColorSpaceTransformData&);
};

struct CircularRRectClipBlock {
struct CircularRRectClipData {
CircularRRectClipData(SkRect rect,
SkPoint radiusPlusHalf,
SkRect edgeSelect)
struct NonMSAAClipBlock {
struct NonMSAAClipData {
NonMSAAClipData(SkRect rect,
SkPoint radiusPlusHalf,
SkRect edgeSelect,
SkPoint texCoordOffset,
SkRect maskBounds,
sk_sp<TextureProxy> atlasTexture)
: fRect(rect)
, fRadiusPlusHalf(radiusPlusHalf)
, fEdgeSelect(edgeSelect) {}
, fEdgeSelect(edgeSelect)
, fTexCoordOffset(texCoordOffset)
, fMaskBounds(maskBounds)
, fAtlasTexture(std::move(atlasTexture)){}
// analytic clip
SkRect fRect; // bounds, outset by 0.5
SkPoint fRadiusPlusHalf; // abs() of .x is radius+0.5, if < 0 indicates inverse fill
// .y is 1/(radius+0.5)
SkRect fEdgeSelect; // 1 indicates a rounded corner on that side (LTRB), 0 otherwise
};

static void AddBlock(const KeyContext&,
PaintParamsKeyBuilder*,
PipelineDataGatherer*,
const CircularRRectClipData&);
};

struct AtlasClipBlock {
struct AtlasClipData {
AtlasClipData(SkPoint texCoordOffset,
SkRect maskBounds,
SkISize atlasSize)
: fTexCoordOffset(texCoordOffset)
, fMaskBounds(maskBounds)
, fAtlasSize(atlasSize) {}
SkPoint fTexCoordOffset; // translation from fragCoords to unnormalized texel coords
// atlas clip
SkPoint fTexCoordOffset; // translation from local coords to unnormalized texel coords
SkRect fMaskBounds; // bounds of mask area, in unnormalized texel coords
SkISize fAtlasSize; // size of atlas texture

sk_sp<TextureProxy> fAtlasTexture;
};

static void AddBlock(const KeyContext&,
PaintParamsKeyBuilder*,
PipelineDataGatherer*,
const AtlasClipData&);
const NonMSAAClipData&);
};

/**
23 changes: 17 additions & 6 deletions src/gpu/graphite/PaintParams.cpp
Original file line number Diff line number Diff line change
@@ -256,17 +256,28 @@ void PaintParams::handleDithering(const KeyContext& keyContext,
void PaintParams::handleClipping(const KeyContext& keyContext,
PaintParamsKeyBuilder* builder,
PipelineDataGatherer* gatherer) const {
if (!fNonMSAAClip.isEmpty() && !fNonMSAAClip.fAnalyticClip.isEmpty()) {
const CircularRRectClip& analyticClip = fNonMSAAClip.fAnalyticClip;
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};
CircularRRectClipBlock::CircularRRectClipData data(

const AtlasClip& atlasClip = fNonMSAAClip.fAtlasClip;
skvx::float2 maskSize = atlasClip.fMaskBounds.size();
SkRect texMaskBounds = SkRect::MakeXYWH(atlasClip.fOutPos.x(), atlasClip.fOutPos.y(),
maskSize.x(), maskSize.y());
SkPoint texCoordOffset = {atlasClip.fOutPos.x() - atlasClip.fMaskBounds.left(),
atlasClip.fOutPos.y() - atlasClip.fMaskBounds.top()};

NonMSAAClipBlock::NonMSAAClipData data(
analyticClip.fBounds.makeOutset(0.5f).asSkRect(),
radiusPair,
analyticClip.edgeSelectRect());
analyticClip.edgeSelectRect(),
texCoordOffset,
texMaskBounds,
atlasClip.fAtlasTexture);
if (fClipShader) {
// For both an analytic clip and clip shader, we need to compose them together into
// a single clipping root node.
@@ -275,14 +286,14 @@ void PaintParams::handleClipping(const KeyContext& keyContext,
AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kModulate);
},
/* addSrcToKey= */ [&]() -> void {
CircularRRectClipBlock::AddBlock(keyContext, builder, gatherer, data);
NonMSAAClipBlock::AddBlock(keyContext, builder, gatherer, data);
},
/* addDstToKey= */ [&]() -> void {
AddToKey(keyContext, builder, gatherer, fClipShader.get());
});
} else {
// Without a clip shader, the analytic clip can be the clipping root node.
CircularRRectClipBlock::AddBlock(keyContext, builder, gatherer, data);
NonMSAAClipBlock::AddBlock(keyContext, builder, gatherer, data);
}
} else if (fClipShader) {
// Since there's no analytic clip, the clipping root node can be fClipShader directly.
20 changes: 12 additions & 8 deletions src/gpu/graphite/ShaderCodeDictionary.cpp
Original file line number Diff line number Diff line change
@@ -1163,22 +1163,26 @@ ShaderCodeDictionary::ShaderCodeDictionary(Layout layout)
/*uniforms=*/{}
};

fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCircularRRectClip] = {
/*name=*/"CircularRRectClip",
/*staticFn=*/"sk_circular_rrect_clip",
fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kAnalyticClip] = {
/*name=*/"AnalyticClip",
/*staticFn=*/"sk_analytic_clip",
SnippetRequirementFlags::kLocalCoords,
/*uniforms=*/{ { "rect", SkSLType::kFloat4 },
{ "radiusPlusHalf", SkSLType::kFloat2 },
{ "edgeSelect", SkSLType::kHalf4 } }
};

fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kAtlasClip] = {
/*name=*/"AtlasClip",
/*staticFn=*/"sk_atlas_clip",
fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kAnalyticAndAtlasClip] = {
/*name=*/"AnalyticAndAtlasClip",
/*staticFn=*/"sk_analytic_and_atlas_clip",
SnippetRequirementFlags::kLocalCoords,
/*uniforms=*/{ { "texCoordOffset", SkSLType::kHalf2 },
/*uniforms=*/{ { "rect", SkSLType::kFloat4 },
{ "radiusPlusHalf", SkSLType::kFloat2 },
{ "edgeSelect", SkSLType::kHalf4 },
{ "texCoordOffset", SkSLType::kHalf2 },
{ "maskBounds", SkSLType::kHalf4 },
{ "invAtlasSize", SkSLType::kFloat2 } }
{ "invAtlasSize", SkSLType::kFloat2 } },
/*texturesAndSamplers=*/{"atlasSampler"}
};

fBuiltInCodeSnippets[(int) BuiltInCodeSnippetID::kCompose] = {
13 changes: 7 additions & 6 deletions src/gpu/graphite/geom/NonMSAAClip.h
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ namespace skgpu::graphite {
/**
* Represents a rect or rrect clip with any non-rect corners having the same circular radii.
*/
struct CircularRRectClip {
struct AnalyticClip {
// Indicate which edges are adjacent to circular corners.
enum EdgeFlags {
kLeft_EdgeFlag = 0b0001,
@@ -26,10 +26,11 @@ struct CircularRRectClip {
kNone_EdgeFlag = 0b0000,
kAll_EdgeFlag = 0b1111,
};
Rect fBounds; // Bounds of clip
float fRadius = 0; // Circular radius, if any
// These defaults will produce no clip
Rect fBounds = { 0, 0, 0, 0 }; // Bounds of clip
float fRadius = 0; // Circular radius, if any
uint32_t fEdgeFlags = kNone_EdgeFlag;
bool fInverted = false;
bool fInverted = true;

bool isEmpty() const { return fBounds.isEmptyNegativeOrNaN(); }
SkRect edgeSelectRect() const {
@@ -55,8 +56,8 @@ struct AtlasClip {
* Combined non-MSAA clip structure
*/
struct NonMSAAClip {
CircularRRectClip fAnalyticClip;
AtlasClip fAtlasClip;
AnalyticClip fAnalyticClip;
AtlasClip fAtlasClip;

bool isEmpty() const { return fAnalyticClip.isEmpty() && fAtlasClip.isEmpty(); }
};
23 changes: 12 additions & 11 deletions src/sksl/generated/sksl_graphite_frag.minified.sksl
Original file line number Diff line number Diff line change
@@ -25,17 +25,18 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] =
" e,half4 f){if(d.w<0.)a=unpremul(a);else{half g=1.-d.w;half h=d.w*f.w;half i"
"=d.w-h;a.w=dot(half3(a.wx,1.),half3(g,i,h));}float3 g=float3(a.xyz);g=sign("
"g)*$k(abs(g),c,d.xyz);g=float3x3(b)*g;g=sign(g)*$k(abs(g),e,f.xyz);half h=f"
".w;a.xyz=half3(g)*max(a.w,h);return a;}$pure half4 sk_circular_rrect_clip(float2"
" a,float4 b,float2 c,half4 d){float2 e=abs(c.x).xx;float2 f=float2(d.xy)*(("
"b.xy+e)-a);float2 g=float2(d.zw)*(a-(b.zw-e));float2 h=max(max(f,g),0.);half"
" i=half(saturate(e.x*(1.-length(h*c.y))));half4 j=saturate(half4(half2(a-b."
"xy),half2(b.zw-a)));j=mix(j,half4(1.),d);half k=(((i*j.x)*j.y)*j.z)*j.w;k=c"
".x<0.?1.-k:k;return k.xxxx;}$pure half4 sk_atlas_clip(float2 a,half2 b,half4"
" c,float2 d,sampler2D e){float2 f=a+float2(b);float2 g=clamp(f,float2(c.xy)"
",float2(c.zw));return sample(e,g*d).x.xxxx;}$pure float $o(int a,float b,float"
" c,float d){switch(a){case 0:return clamp(b,c,d);case 1:{float e=d-c;return"
" mod(b-c,e)+c;}case 2:{float e=d-c;float g=2.*e;float h=mod(b-c,g);return mix"
"(h,g-h,step(e,h))+c;}default:return b;}}$pure half4 $p(float2 a,float2 b,sampler2D"
".w;a.xyz=half3(g)*max(a.w,h);return a;}$pure half4 sk_analytic_clip(float2 a"
",float4 b,float2 c,half4 d){float2 e=abs(c.x).xx;float2 f=float2(d.xy)*((b."
"xy+e)-a);float2 g=float2(d.zw)*(a-(b.zw-e));float2 h=max(max(f,g),0.);half i"
"=half(saturate(e.x*(1.-length(h*c.y))));half4 j=saturate(half4(half2(a-b.xy"
"),half2(b.zw-a)));j=mix(j,half4(1.),d);half k=(((i*j.x)*j.y)*j.z)*j.w;k=c.x"
"<0.?1.-k:k;return k.xxxx;}$pure half4 sk_analytic_and_atlas_clip(float2 a,float4"
" b,float2 c,half4 d,half2 e,half4 f,float2 g,sampler2D h){half4 i=sk_analytic_clip"
"(a,b,c,d);float2 j=a+float2(e);float2 k=clamp(j,float2(f.xy),float2(f.zw));"
"half l=sample(h,k*g).x;return i*l;}$pure float $o(int a,float b,float c,float"
" d){switch(a){case 0:return clamp(b,c,d);case 1:{float e=d-c;return mod(b-c"
",e)+c;}case 2:{float e=d-c;float g=2.*e;float h=mod(b-c,g);return mix(h,g-h"
",step(e,h))+c;}default:return b;}}$pure half4 $p(float2 a,float2 b,sampler2D"
" c){return sample(c,a*b);}$pure half4 $q(float2 a,float2 b,float4 c,int d,int"
" e,int f,float2 g,sampler2D h){if(d==3&&f==0){float i=floor(a.x)+.5;if(i<c."
"x||i>c.z)return half4(0.);}if(e==3&&f==0){float i=floor(a.y)+.5;if(i<c.y||i"
Loading

0 comments on commit 05830bf

Please sign in to comment.