stage version of vertices

This CL, just to limit its size/complexity, only handles
colors but not textures. Future CLs will cover everything.

Performance is pretty exciting. Its faster than the old code-path,
and when we fix a bug in pathutils to preserve opaqueness, it gets
a lot faster (8 -> 5)

Bug: skia:
Change-Id: I4113060e25fe25fe4e6a0ea59bd4fa5e33abc668
Reviewed-on: https://skia-review.googlesource.com/17276
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Mike Reed 2017-05-19 15:32:13 -04:00 committed by Skia Commit-Bot
parent 985febffa2
commit 0264095cfd
7 changed files with 3363 additions and 2773 deletions

View File

@ -205,5 +205,9 @@ SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint,
// Returns nullptr if no SkRasterPipeline blitter can be constructed for this paint.
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
SkArenaAlloc*);
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
const SkRasterPipeline& shaderPipeline,
bool shader_is_opaque, bool shader_wants_dither,
SkArenaAlloc*);
#endif

View File

@ -10,13 +10,19 @@
#include "SkColorShader.h"
#include "SkDraw.h"
#include "SkNx.h"
#include "SkPM4f.h"
#include "SkPM4fPriv.h"
#include "SkRasterClip.h"
#include "SkScan.h"
#include "SkShader.h"
#include "SkString.h"
#include "SkVertState.h"
#include "SkRasterPipeline.h"
#include "SkArenaAlloc.h"
#include "SkCoreBlitters.h"
#include "SkColorSpaceXform.h"
#include "SkColorSpace_Base.h"
struct Matrix43 {
float fMat[12]; // column major
@ -356,9 +362,67 @@ namespace {
return alloc->makeSkSp<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor));
}
} // anonymous ns
static bool update_tricolor_matrix(const SkMatrix& ctmInv,
const SkPoint pts[], const SkPM4f colors[],
int index0, int index1, int index2, Matrix43* result) {
SkMatrix m, im;
m.reset();
m.set(0, pts[index1].fX - pts[index0].fX);
m.set(1, pts[index2].fX - pts[index0].fX);
m.set(2, pts[index0].fX);
m.set(3, pts[index1].fY - pts[index0].fY);
m.set(4, pts[index2].fY - pts[index0].fY);
m.set(5, pts[index0].fY);
if (!m.invert(&im)) {
return false;
}
SkMatrix dstToUnit;
dstToUnit.setConcat(im, ctmInv);
Sk4f c0 = colors[index0].to4f(),
c1 = colors[index1].to4f(),
c2 = colors[index2].to4f();
Matrix43 colorm;
(c1 - c0).store(&colorm.fMat[0]);
(c2 - c0).store(&colorm.fMat[4]);
c0.store(&colorm.fMat[8]);
result->setConcat(colorm, dstToUnit);
return true;
}
static SkPM4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
SkArenaAlloc* alloc) {
SkPM4f* dst = alloc->makeArray<SkPM4f>(count);
if (!deviceCS) {
for (int i = 0; i < count; ++i) {
dst[i] = SkPM4f_from_SkColor(src[i], nullptr);
}
} else {
// For now, we want premul to happen on the colors before interplation. If we later want
// to apply it after the interp, pass kUnpremul here.
SkAlphaType alphaVerb = kPremul_SkAlphaType;
auto srcCS = SkColorSpace::MakeSRGB();
auto dstCS = as_CSB(deviceCS)->makeLinearGamma();
SkColorSpaceXform::New(srcCS.get(),
dstCS.get())->apply(SkColorSpaceXform::kRGBA_F32_ColorFormat, dst,
SkColorSpaceXform::kBGRA_8888_ColorFormat, src,
count, alphaVerb);
}
return dst;
}
static bool compute_is_opaque(const SkColor colors[], int count) {
uint32_t c = ~0;
for (int i = 0; i < count; ++i) {
c &= colors[i];
}
return SkColorGetA(c) == 0xFF;
}
void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
const SkPoint vertices[], const SkPoint textures[],
const SkColor colors[], SkBlendMode bmode,
@ -370,6 +434,10 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
return;
}
SkMatrix ctmInv;
if (!fMatrix->invert(&ctmInv)) {
return;
}
// transform out vertices into device coordinates
SkAutoSTMalloc<16, SkPoint> storage(count);
@ -387,6 +455,53 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
Thus for texture drawing, we need both texture[] and a shader.
*/
if (colors && !textures) {
char arenaStorage[4096];
SkArenaAlloc alloc(arenaStorage, sizeof(storage));
Matrix43 matrix43;
SkRasterPipeline shaderPipeline;
// Convert the SkColors into float colors. The conversion depends on some conditions:
// - If the pixmap has a dst colorspace, we have to be "color-correct".
// Do we map into dst-colorspace before or after we interpolate?
// - We have to decide when to apply per-color alpha (before or after we interpolate)
//
// For now, we will take a simple approach, but recognize this is just a start:
// - convert colors into dst colorspace before interpolation (matches gradients)
// - apply per-color alpha before interpolation (matches old version of vertices)
//
SkPM4f* dstColors = convert_colors(colors, count, fDst.colorSpace(), &alloc);
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
// the triangle, we do see occasional (slightly) out-of-range values, so we add these
// clamp stages. It would be nice to find a way to detect when these are not needed.
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,
is_opaque, wants_dither, &alloc);
SkASSERT(!blitter->isNullBlitter());
// setup our state and function pointer for iterating triangles
VertState state(count, indices, indexCount);
VertState::Proc vertProc = state.chooseProc(vmode);
while (vertProc(&state)) {
SkPoint tmp[] = {
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
};
if (update_tricolor_matrix(ctmInv, vertices, dstColors, state.f0, state.f1, state.f2,
&matrix43)) {
SkScan::FillTriangle(tmp, *fRC, blitter);
}
}
return;
}
auto triShader = sk_make_sp<SkTriColorShader>();
SkPaint p(paint);

View File

@ -82,7 +82,7 @@
M(exclusion) M(hardlight) M(lighten) M(overlay) M(softlight) \
M(hue) M(saturation) M(color) M(luminosity) \
M(luminance_to_alpha) \
M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) \
M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) M(matrix_4x3) \
M(matrix_perspective) \
M(parametric_r) M(parametric_g) M(parametric_b) \
M(parametric_a) \

View File

@ -22,6 +22,10 @@ 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*);
SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
: fDst(dst)
@ -38,6 +42,9 @@ 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;
@ -85,48 +92,100 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
paint.getBlendMode(),
SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
SkBlendMode* blend = &blitter->fBlend;
SkPM4f* paintColor = &blitter->fPaintColor;
SkRasterPipeline* pipeline = &blitter->fShader;
SkShader* shader = paint.getShader();
SkShader* shader = paint.getShader();
SkColorFilter* colorFilter = paint.getColorFilter();
bool is_opaque = paintColor->a() == 1.0f,
is_constant = true,
wants_dither = false;
// TODO: Think more about under what conditions we dither:
// - if we're drawing anything into 565 and the user has asked us to dither, or
// - if we're drawing a gradient into 565 or 8888.
if ((paint.isDither() && dst.info().colorType() == kRGB_565_SkColorType) ||
(shader && shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType)) {
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;
}
}
bool is_opaque = paintColor->a() == 1.0f,
is_constant = blitter->fDitherCtx.rate == 0.0f;
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 = is_constant && shader->isConstant();
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;
}
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
const SkPaint& paint,
const SkMatrix& ctm,
const SkRasterPipeline& shaderPipeline,
bool is_opaque, bool wants_dither,
SkArenaAlloc* alloc) {
return SkRasterPipelineBlitter::Create(dst, paint, ctm, shaderPipeline,
is_opaque, wants_dither, alloc);
}
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()));
bool is_constant = false; // we figure a custom shaderPipeline is never constant
SkPM4f* paintColor = &blitter->fPaintColor;
SkRasterPipeline* pipeline = &blitter->fShader;
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;
}
blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
return blitter;
}
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();
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;
}
}
if (colorFilter) {
colorFilter->appendStages(pipeline, dst.colorSpace(), alloc, is_opaque);
colorFilter->appendStages(pipeline, fDst.colorSpace(), alloc, is_opaque);
is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
}
@ -147,14 +206,12 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
if (is_constant && *blend == SkBlendMode::kSrc) {
SkRasterPipeline p;
p.extend(*pipeline);
blitter->fDstPtr = &blitter->fMemsetColor;
blitter->append_store(&p);
fDstPtr = &fMemsetColor;
this->append_store(&p);
p.run(0,1);
blitter->fCanMemsetInBlitH = true;
fCanMemsetInBlitH = true;
}
return blitter;
}
void SkRasterPipelineBlitter::append_load_d(SkRasterPipeline* p) const {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1047,6 +1047,16 @@ STAGE(matrix_4x5) {
b = B;
a = A;
}
STAGE(matrix_4x3) {
auto m = (const float*)ctx;
auto X = r,
Y = g;
r = mad(X, m[0], mad(Y, m[4], m[ 8]));
g = mad(X, m[1], mad(Y, m[5], m[ 9]));
b = mad(X, m[2], mad(Y, m[6], m[10]));
a = mad(X, m[3], mad(Y, m[7], m[11]));
}
STAGE(matrix_perspective) {
// N.B. Unlike the other matrix_ stages, this matrix is row-major.
auto m = (const float*)ctx;