refactor SkRasterPipelineBlitter
This refactors the factories so that the create-from-paint factory is a front-patch to the create-from-shader-pipeline factory. Feature-wise, we make the pre-baked shader pipeline responsible for modulating by paint alpha; the factory only adds when creating from the paint. We can fold the alpha into the colors in drawVertices, which makes it run a bit faster, dropping the need for a scale_1_float runtime stage. This causes a few invisible diffs on the "vertices" GM, but everything else draws the same. Change-Id: I3eeacc9aafbce2023ab18991bbb68c35645e9387 Reviewed-on: https://skia-review.googlesource.com/17395 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Mike Klein <mtklein@chromium.org>
This commit is contained in:
parent
1859f69d20
commit
b35cb3143e
@ -202,12 +202,15 @@ SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
|
||||
// Returns nullptr if no SkRasterPipeline blitter can be constructed for this paint.
|
||||
// Neither of these ever returns nullptr, but this first factory may return a SkNullBlitter.
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
SkArenaAlloc*);
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
// Use this if you've pre-baked a shader pipeline, including modulating with paint alpha.
|
||||
// This factory never returns an SkNullBlitter.
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool shader_is_opaque, bool shader_wants_dither,
|
||||
bool shader_is_opaque,
|
||||
bool shader_wants_dither,
|
||||
SkArenaAlloc*);
|
||||
|
||||
#endif
|
||||
|
@ -257,9 +257,9 @@ void SkTriColorShader::TriColorShaderContext::shadeSpan4f(int x, int y, SkPM4f d
|
||||
#ifndef SK_IGNORE_TO_STRING
|
||||
void SkTriColorShader::toString(SkString* str) const {
|
||||
str->append("SkTriColorShader: (");
|
||||
|
||||
|
||||
this->INHERITED::toString(str);
|
||||
|
||||
|
||||
str->append(")");
|
||||
}
|
||||
#endif
|
||||
@ -472,6 +472,17 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
|
||||
//
|
||||
SkPM4f* dstColors = convert_colors(colors, count, fDst.colorSpace(), &alloc);
|
||||
|
||||
bool is_opaque;
|
||||
if (paint.getAlpha() == 0xff) {
|
||||
is_opaque = compute_is_opaque(colors, count);
|
||||
} else {
|
||||
is_opaque = false;
|
||||
Sk4f alpha = paint.getAlpha() * (1/255.0f);
|
||||
for (int i = 0; i < count; i++) {
|
||||
(dstColors[i].to4f() * alpha).store(dstColors + i);
|
||||
}
|
||||
}
|
||||
|
||||
shaderPipeline.append(SkRasterPipeline::matrix_4x3, &matrix43);
|
||||
// In theory we should never need to clamp. However, either due to imprecision in our
|
||||
// matrix43, or the scan converter passing us pixel centers that in fact are not within
|
||||
@ -480,9 +491,8 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
|
||||
shaderPipeline.append(SkRasterPipeline::clamp_0);
|
||||
shaderPipeline.append(SkRasterPipeline::clamp_a);
|
||||
|
||||
bool is_opaque = compute_is_opaque(colors, count),
|
||||
wants_dither = paint.isDither();
|
||||
auto blitter = SkCreateRasterPipelineBlitter(fDst, paint, *fMatrix, shaderPipeline,
|
||||
bool wants_dither = paint.isDither();
|
||||
auto blitter = SkCreateRasterPipelineBlitter(fDst, paint, shaderPipeline,
|
||||
is_opaque, wants_dither, &alloc);
|
||||
SkASSERT(!blitter->isNullBlitter());
|
||||
|
||||
|
@ -20,17 +20,19 @@
|
||||
|
||||
class SkRasterPipelineBlitter : public SkBlitter {
|
||||
public:
|
||||
static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
SkArenaAlloc*);
|
||||
static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool wants_dither,
|
||||
SkArenaAlloc*);
|
||||
// Create using paint.getShader() or paint.getColor() if there is no shader.
|
||||
// If there's a shader, we will modulate the shader color by the paint alpha.
|
||||
static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*, const SkMatrix& ctm);
|
||||
|
||||
SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
|
||||
// Create using pre-built shader pipeline.
|
||||
// This pre-built pipeline must already have handled modulating with the paint alpha.
|
||||
static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool is_constant, bool wants_dither);
|
||||
|
||||
SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend)
|
||||
: fDst(dst)
|
||||
, fBlend(blend)
|
||||
, fPaintColor(paintColor)
|
||||
{}
|
||||
|
||||
void blitH (int x, int y, int w) override;
|
||||
@ -42,9 +44,6 @@ public:
|
||||
// blits using something like a SkRasterPipeline::runFew() method.
|
||||
|
||||
private:
|
||||
void finishBuilding(const SkPaint& paint, const SkMatrix& ctm, bool is_oapque,
|
||||
bool is_constant, bool wants_dither, SkArenaAlloc* alloc);
|
||||
|
||||
void append_load_d(SkRasterPipeline*) const;
|
||||
void append_blend (SkRasterPipeline*) const;
|
||||
void maybe_clamp (SkRasterPipeline*) const;
|
||||
@ -52,8 +51,7 @@ private:
|
||||
|
||||
SkPixmap fDst;
|
||||
SkBlendMode fBlend;
|
||||
SkPM4f fPaintColor;
|
||||
SkRasterPipeline fShader;
|
||||
SkRasterPipeline fColorPipeline;
|
||||
|
||||
// We may be able to specialize blitH() into a memset.
|
||||
bool fCanMemsetInBlitH = false;
|
||||
@ -80,138 +78,125 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix& ctm,
|
||||
SkArenaAlloc* alloc) {
|
||||
return SkRasterPipelineBlitter::Create(dst, paint, ctm, alloc);
|
||||
}
|
||||
|
||||
SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix& ctm,
|
||||
SkArenaAlloc* alloc) {
|
||||
auto blitter = alloc->make<SkRasterPipelineBlitter>(
|
||||
dst,
|
||||
paint.getBlendMode(),
|
||||
SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
|
||||
|
||||
SkPM4f* paintColor = &blitter->fPaintColor;
|
||||
SkRasterPipeline* pipeline = &blitter->fShader;
|
||||
SkShader* shader = paint.getShader();
|
||||
|
||||
bool is_opaque = paintColor->a() == 1.0f,
|
||||
is_constant = true,
|
||||
wants_dither = false;
|
||||
|
||||
if (shader) {
|
||||
pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
|
||||
if (!shader->appendStages(pipeline, dst.colorSpace(), alloc, ctm, paint)) {
|
||||
// When a shader fails to append stages, it means it has vetoed drawing entirely.
|
||||
return alloc->make<SkNullBlitter>();
|
||||
}
|
||||
|
||||
if (!is_opaque) {
|
||||
pipeline->append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
|
||||
}
|
||||
is_opaque = is_opaque && shader->isOpaque();
|
||||
is_constant = shader->isConstant();
|
||||
wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
|
||||
|
||||
} else {
|
||||
pipeline->append(SkRasterPipeline::constant_color, paintColor);
|
||||
}
|
||||
|
||||
blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
|
||||
return blitter;
|
||||
return SkRasterPipelineBlitter::Create(dst, paint, alloc, ctm);
|
||||
}
|
||||
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool wants_dither,
|
||||
bool is_opaque,
|
||||
bool wants_dither,
|
||||
SkArenaAlloc* alloc) {
|
||||
return SkRasterPipelineBlitter::Create(dst, paint, ctm, shaderPipeline,
|
||||
is_opaque, wants_dither, alloc);
|
||||
bool is_constant = false; // If this were the case, it'd be better to just set a paint color.
|
||||
return SkRasterPipelineBlitter::Create(dst, paint, alloc,
|
||||
shaderPipeline, is_opaque, is_constant, wants_dither);
|
||||
}
|
||||
|
||||
SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool wants_dither,
|
||||
SkArenaAlloc* alloc) {
|
||||
auto blitter = alloc->make<SkRasterPipelineBlitter>(
|
||||
dst,
|
||||
paint.getBlendMode(),
|
||||
SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
|
||||
SkArenaAlloc* alloc,
|
||||
const SkMatrix& ctm) {
|
||||
auto paintColor = alloc->make<SkPM4f>(SkPM4f_from_SkColor(paint.getColor(),
|
||||
dst.colorSpace()));
|
||||
if (auto shader = paint.getShader()) {
|
||||
SkRasterPipeline shaderPipeline;
|
||||
if (!shader->appendStages(&shaderPipeline, dst.colorSpace(), alloc, ctm, paint)) {
|
||||
// When a shader fails to append stages, it means it has vetoed drawing entirely.
|
||||
return alloc->make<SkNullBlitter>();
|
||||
}
|
||||
|
||||
bool is_constant = false; // we figure a custom shaderPipeline is never constant
|
||||
SkPM4f* paintColor = &blitter->fPaintColor;
|
||||
SkRasterPipeline* pipeline = &blitter->fShader;
|
||||
bool is_opaque = shader->isOpaque();
|
||||
if (paintColor->a() != 1.0f) {
|
||||
shaderPipeline.append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
|
||||
is_opaque = false;
|
||||
}
|
||||
|
||||
pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
|
||||
pipeline->extend(shaderPipeline);
|
||||
|
||||
if (paintColor->a() != 1.0f) {
|
||||
pipeline->append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
|
||||
is_opaque = false;
|
||||
bool is_constant = shader->isConstant();
|
||||
bool wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
|
||||
return Create(dst, paint, alloc, shaderPipeline, is_opaque, is_constant, wants_dither);
|
||||
}
|
||||
|
||||
blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
|
||||
return blitter;
|
||||
SkRasterPipeline shaderPipeline;
|
||||
shaderPipeline.append(SkRasterPipeline::constant_color, paintColor);
|
||||
bool is_opaque = paintColor->a() == 1.0f,
|
||||
is_constant = true,
|
||||
wants_dither = false;
|
||||
return Create(dst, paint, alloc, shaderPipeline, is_opaque, is_constant, wants_dither);
|
||||
}
|
||||
|
||||
void SkRasterPipelineBlitter::finishBuilding(const SkPaint& paint, const SkMatrix& ctm,
|
||||
bool is_opaque, bool is_constant, bool wants_dither,
|
||||
SkArenaAlloc* alloc) {
|
||||
SkBlendMode* blend = &fBlend;
|
||||
SkPM4f* paintColor = &fPaintColor;
|
||||
SkRasterPipeline* pipeline = &fShader;
|
||||
SkColorFilter* colorFilter = paint.getColorFilter();
|
||||
SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
SkArenaAlloc* alloc,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque,
|
||||
bool is_constant,
|
||||
bool wants_dither) {
|
||||
auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, paint.getBlendMode());
|
||||
|
||||
SkASSERT(fDitherCtx.rate == 0);
|
||||
if ((paint.isDither() && fDst.info().colorType() == kRGB_565_SkColorType) || wants_dither) {
|
||||
switch (fDst.info().colorType()) {
|
||||
case kRGB_565_SkColorType:
|
||||
fDitherCtx.rate = 1/63.0f;
|
||||
is_constant = false;
|
||||
break;
|
||||
case kRGBA_8888_SkColorType:
|
||||
case kBGRA_8888_SkColorType:
|
||||
fDitherCtx.rate = 1/255.0f;
|
||||
is_constant = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Our job in this factory is to fill out the blitter's color pipeline.
|
||||
// This is the common front of the full blit pipelines, each constructed lazily on first use.
|
||||
// The full blit pipelines handle reading and writing the dst, blending, coverage, dithering.
|
||||
auto colorPipeline = &blitter->fColorPipeline;
|
||||
|
||||
// Let's get the shader in first. If the shader's not constant, it'll need seeding with x,y.
|
||||
if (!is_constant) {
|
||||
colorPipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
|
||||
}
|
||||
colorPipeline->extend(shaderPipeline);
|
||||
|
||||
if (colorFilter) {
|
||||
colorFilter->appendStages(pipeline, fDst.colorSpace(), alloc, is_opaque);
|
||||
// If there's a color filter it comes next.
|
||||
if (auto colorFilter = paint.getColorFilter()) {
|
||||
colorFilter->appendStages(colorPipeline, dst.colorSpace(), alloc, is_opaque);
|
||||
is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
|
||||
}
|
||||
|
||||
// We'll dither if the shader wants to, or if we're drawing 565 and the paint wants to.
|
||||
// Not all formats make sense to dither (think, F16). We set their dither rate to zero.
|
||||
// We need to decide if we're going to dither now to keep is_constant accurate.
|
||||
if (wants_dither ||
|
||||
(paint.isDither() && dst.info().colorType() == kRGB_565_SkColorType)) {
|
||||
switch (dst.info().colorType()) {
|
||||
default: blitter->fDitherCtx.rate = 0.0f; break;
|
||||
case kRGB_565_SkColorType: blitter->fDitherCtx.rate = 1/63.0f; break;
|
||||
case kRGBA_8888_SkColorType:
|
||||
case kBGRA_8888_SkColorType: blitter->fDitherCtx.rate = 1/255.0f; break;
|
||||
}
|
||||
}
|
||||
is_constant = is_constant && (blitter->fDitherCtx.rate == 0.0f);
|
||||
|
||||
// We're logically done here. The code between here and return blitter is all optimization.
|
||||
|
||||
// A pipeline that's still constant here can collapse back into a constant color.
|
||||
if (is_constant) {
|
||||
pipeline->append(SkRasterPipeline::store_f32, &paintColor);
|
||||
pipeline->run(0,1);
|
||||
auto constantColor = alloc->make<SkPM4f>();
|
||||
colorPipeline->append(SkRasterPipeline::store_f32, &constantColor);
|
||||
colorPipeline->run(0,1);
|
||||
*colorPipeline = SkRasterPipeline();
|
||||
colorPipeline->append(SkRasterPipeline::constant_color, constantColor);
|
||||
|
||||
*pipeline = SkRasterPipeline();
|
||||
pipeline->append(SkRasterPipeline::constant_color, paintColor);
|
||||
|
||||
is_opaque = paintColor->a() == 1.0f;
|
||||
is_opaque = constantColor->a() == 1.0f;
|
||||
}
|
||||
|
||||
if (is_opaque && *blend == SkBlendMode::kSrcOver) {
|
||||
*blend = SkBlendMode::kSrc;
|
||||
// We can strength-reduce SrcOver into Src when opaque.
|
||||
if (is_opaque && blitter->fBlend == SkBlendMode::kSrcOver) {
|
||||
blitter->fBlend = SkBlendMode::kSrc;
|
||||
}
|
||||
|
||||
if (is_constant && *blend == SkBlendMode::kSrc) {
|
||||
// When we're drawing a constant color in Src mode, we can sometimes just memset.
|
||||
// (The previous two optimizations help find more opportunities for this one.)
|
||||
if (is_constant && blitter->fBlend == SkBlendMode::kSrc) {
|
||||
// Run our color pipeline all the way through to produce what we'd memset when we can.
|
||||
// Not all blits can memset, so we need to keep colorPipeline too.
|
||||
SkRasterPipeline p;
|
||||
p.extend(*pipeline);
|
||||
fDstPtr = &fMemsetColor;
|
||||
this->append_store(&p);
|
||||
p.extend(*colorPipeline);
|
||||
blitter->fDstPtr = &blitter->fMemsetColor;
|
||||
blitter->append_store(&p);
|
||||
p.run(0,1);
|
||||
|
||||
fCanMemsetInBlitH = true;
|
||||
blitter->fCanMemsetInBlitH = true;
|
||||
}
|
||||
|
||||
return blitter;
|
||||
}
|
||||
|
||||
void SkRasterPipelineBlitter::append_load_d(SkRasterPipeline* p) const {
|
||||
@ -282,7 +267,7 @@ void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
|
||||
|
||||
auto& p = fBlitH;
|
||||
if (p.empty()) {
|
||||
p.extend(fShader);
|
||||
p.extend(fColorPipeline);
|
||||
if (fBlend != SkBlendMode::kSrc) {
|
||||
this->append_load_d(&p);
|
||||
this->append_blend(&p);
|
||||
@ -296,7 +281,7 @@ void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
|
||||
void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
|
||||
auto& p = fBlitAntiH;
|
||||
if (p.empty()) {
|
||||
p.extend(fShader);
|
||||
p.extend(fColorPipeline);
|
||||
if (fBlend == SkBlendMode::kSrcOver) {
|
||||
p.append(SkRasterPipeline::scale_1_float, &fCurrentCoverage);
|
||||
this->append_load_d(&p);
|
||||
@ -334,7 +319,7 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
|
||||
|
||||
if (mask.fFormat == SkMask::kA8_Format && fBlitMaskA8.empty()) {
|
||||
auto& p = fBlitMaskA8;
|
||||
p.extend(fShader);
|
||||
p.extend(fColorPipeline);
|
||||
if (fBlend == SkBlendMode::kSrcOver) {
|
||||
p.append(SkRasterPipeline::scale_u8, &fMaskPtr);
|
||||
this->append_load_d(&p);
|
||||
@ -350,7 +335,7 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
|
||||
|
||||
if (mask.fFormat == SkMask::kLCD16_Format && fBlitMaskLCD16.empty()) {
|
||||
auto& p = fBlitMaskLCD16;
|
||||
p.extend(fShader);
|
||||
p.extend(fColorPipeline);
|
||||
this->append_load_d(&p);
|
||||
this->append_blend(&p);
|
||||
p.append(SkRasterPipeline::lerp_565, &fMaskPtr);
|
||||
|
Loading…
Reference in New Issue
Block a user