draw vertices: fast triangles using a shader
Introduce a new call to SkShaderBase to return an SkUpdatableShader. An SkUpdatableShader is both a SkShaderBase and has an update() method. If the onUpdatableShader call returns !nullptr, then the shader's specialized updatable shader is used. Otherwise, the shader is wrapped in a TexCoordShader. This scheme allows Shaders to have specialized updaters, but also adds updaters for shaders that don't need specialization. orig this bench 771µs 779µs verts_textures_colors 217µs 335µs verts_textures 275µs 380µs verts_textures_persp Bug=skia:11822 Change-Id: I11ba344eba96188c5631228cd5e91c5d834ea3d6 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/429417 Commit-Queue: Herb Derby <herb@google.com> Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
parent
17eaf62160
commit
5992f9eb43
@ -458,6 +458,8 @@ skia_core_sources = [
|
||||
"$_src/shaders/SkLocalMatrixShader.h",
|
||||
"$_src/shaders/SkShader.cpp",
|
||||
"$_src/shaders/SkShaderBase.h",
|
||||
"$_src/shaders/SkTransformShader.cpp",
|
||||
"$_src/shaders/SkTransformShader.h",
|
||||
|
||||
# private
|
||||
"$_include/private/SkChecksum.h",
|
||||
|
@ -255,6 +255,7 @@ static void fill_triangle_3(const VertState& state, SkBlitter* blitter, const Sk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_triangle(const VertState& state, SkBlitter* blitter, const SkRasterClip& rc,
|
||||
const SkPoint dev2[], const SkPoint3 dev3[]) {
|
||||
if (dev3) {
|
||||
@ -308,27 +309,39 @@ void SkDraw::drawFixedVertices(const SkVertices* vertices, SkBlendMode blendMode
|
||||
|
||||
VertState state(vertexCount, indices, indexCount);
|
||||
VertState::Proc vertProc = state.chooseProc(info.mode());
|
||||
SkMatrix ctm = fMatrixProvider->localToDevice();
|
||||
// No colors are changing and no texture coordinates are changing, so no updates between
|
||||
// triangles are needed. Use SkVM to blit the triangles.
|
||||
if (gUseSkVMBlitter && !colors && (!texCoords || texCoords == positions)) {
|
||||
if (auto blitter = SkVMBlitter::Make(
|
||||
fDst, paint, *fMatrixProvider, outerAlloc, this->fRC->clipShader())) {
|
||||
while (vertProc(&state)) {
|
||||
fill_triangle(state, blitter, *fRC, dev2, dev3);
|
||||
if (gUseSkVMBlitter && !colors) {
|
||||
if (!texCoords || texCoords == positions) {
|
||||
if (auto blitter = SkVMBlitter::Make(
|
||||
fDst, paint, *fMatrixProvider, outerAlloc, this->fRC->clipShader())) {
|
||||
while (vertProc(&state)) {
|
||||
fill_triangle(state, blitter, *fRC, dev2, dev3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
auto texCoordShader = as_SB(shader)->updatableShader(outerAlloc);
|
||||
SkPaint shaderPaint{paint};
|
||||
shaderPaint.setShader(sk_ref_sp(texCoordShader));
|
||||
if (auto blitter = SkVMBlitter::Make(
|
||||
fDst, shaderPaint, *fMatrixProvider, outerAlloc, this->fRC->clipShader())) {
|
||||
auto updater = [&](skvm::Uniforms* uniforms) {
|
||||
SkMatrix localM;
|
||||
return texture_to_matrix(state, positions, texCoords, &localM) &&
|
||||
texCoordShader->update(SkMatrix::Concat(ctm, localM), uniforms);
|
||||
};
|
||||
while (vertProc(&state)) {
|
||||
if (blitter->updateUniforms(updater)) {
|
||||
fill_triangle(state, blitter, *fRC, dev2, dev3);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to know if we have perspective or not, so we can know what stage(s) we will need,
|
||||
and how to prep our "uniforms" before each triangle in the tricolorshader.
|
||||
|
||||
We could just check the matrix on each triangle to decide, but we have to be sure to always
|
||||
make the same decision, since we create 1 or 2 stages only once for the entire patch.
|
||||
|
||||
To be safe, we just make that determination here, and pass it into the tricolorshader.
|
||||
*/
|
||||
SkMatrix ctm = fMatrixProvider->localToDevice();
|
||||
const bool usePerspective = ctm.hasPerspective();
|
||||
|
||||
SkTriColorShader* triShader = nullptr;
|
||||
|
@ -556,8 +556,7 @@ SkVMBlitter::SkVMBlitter(const SkPixmap& device,
|
||||
bool* ok)
|
||||
: fDevice(device), fSprite(sprite ? *sprite : SkPixmap{})
|
||||
, fSpriteOffset(spriteOffset)
|
||||
, fUniforms(skvm::Ptr{0}
|
||||
, kBlitterUniformsCount)
|
||||
, fUniforms(skvm::Ptr{0}, kBlitterUniformsCount)
|
||||
, fParams(EffectiveParams(device, sprite, paint, matrices, std::move(clip)))
|
||||
, fKey(CacheKey(fParams, &fUniforms, &fAlloc, ok)) {}
|
||||
|
||||
|
@ -36,6 +36,11 @@ public:
|
||||
|
||||
~SkVMBlitter() override;
|
||||
|
||||
template<typename Updater>
|
||||
bool updateUniforms(Updater updater) {
|
||||
return updater(&fUniforms);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Coverage { Full, UniformF, MaskA8, MaskLCD16, Mask3D };
|
||||
struct Key {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "src/image/SkImage_Base.h"
|
||||
#include "src/shaders/SkBitmapProcShader.h"
|
||||
#include "src/shaders/SkEmptyShader.h"
|
||||
#include "src/shaders/SkTransformShader.h"
|
||||
|
||||
SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
|
||||
#if 0
|
||||
@ -691,11 +692,43 @@ SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) co
|
||||
return this->doStages(rec, updater) ? updater : nullptr;
|
||||
}
|
||||
|
||||
skvm::Color SkImageShader::onProgram(skvm::Builder* p,
|
||||
class SkImageShader::TransformShader : public SkTransformShader {
|
||||
public:
|
||||
explicit TransformShader(const SkImageShader& shader)
|
||||
: SkTransformShader{shader}
|
||||
, fImageShader{shader} {}
|
||||
|
||||
skvm::Color onProgram(skvm::Builder* b,
|
||||
skvm::Coord device, skvm::Coord local, skvm::Color color,
|
||||
const SkMatrixProvider& matrices, const SkMatrix* localM,
|
||||
const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
|
||||
return fImageShader.makeProgram(
|
||||
b, device, local, color, matrices, localM, dst, uniforms, this, alloc);
|
||||
}
|
||||
|
||||
private:
|
||||
const SkImageShader& fImageShader;
|
||||
};
|
||||
|
||||
SkUpdatableShader* SkImageShader::onUpdatableShader(SkArenaAlloc* alloc) const {
|
||||
return alloc->make<TransformShader>(*this);
|
||||
}
|
||||
|
||||
skvm::Color SkImageShader::onProgram(skvm::Builder* b,
|
||||
skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
|
||||
const SkMatrixProvider& matrices, const SkMatrix* localM,
|
||||
const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
|
||||
return this->makeProgram(
|
||||
b, device, origLocal, paint, matrices, localM, dst, uniforms, nullptr, alloc);
|
||||
}
|
||||
|
||||
skvm::Color SkImageShader::makeProgram(
|
||||
skvm::Builder* p, skvm::Coord device, skvm::Coord origLocal, skvm::Color paint,
|
||||
const SkMatrixProvider& matrices, const SkMatrix* localM, const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, const TransformShader* coordShader, SkArenaAlloc* alloc) const {
|
||||
|
||||
SkMatrix baseInv;
|
||||
if (!this->computeTotalInverse(matrices.localToDevice(), localM, &baseInv)) {
|
||||
return {};
|
||||
@ -725,7 +758,12 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p,
|
||||
lower = &lowerPixmap;
|
||||
}
|
||||
|
||||
skvm::Coord upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms);
|
||||
skvm::Coord upperLocal;
|
||||
if (coordShader != nullptr) {
|
||||
upperLocal = coordShader->applyMatrix(p, upperInv, origLocal, uniforms);
|
||||
} else {
|
||||
upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms);
|
||||
}
|
||||
|
||||
// We can exploit image opacity to skip work unpacking alpha channels.
|
||||
const bool input_is_opaque = SkAlphaTypeIsOpaque(upper.alphaType())
|
||||
|
@ -51,10 +51,18 @@ private:
|
||||
bool onAppendStages(const SkStageRec&) const override;
|
||||
SkStageUpdater* onAppendUpdatableStages(const SkStageRec&) const override;
|
||||
|
||||
SkUpdatableShader* onUpdatableShader(SkArenaAlloc* alloc) const override;
|
||||
|
||||
skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
|
||||
const SkMatrixProvider&, const SkMatrix* localM, const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc*) const override;
|
||||
|
||||
class TransformShader;
|
||||
skvm::Color makeProgram(
|
||||
skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
|
||||
const SkMatrixProvider&, const SkMatrix* localM, const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, const TransformShader* coordShader, SkArenaAlloc*) const;
|
||||
|
||||
bool doStages(const SkStageRec&, SkImageStageUpdater* = nullptr) const;
|
||||
|
||||
sk_sp<SkImage> fImage;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "src/shaders/SkImageShader.h"
|
||||
#include "src/shaders/SkPictureShader.h"
|
||||
#include "src/shaders/SkShaderBase.h"
|
||||
#include "src/shaders/SkTransformShader.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "src/gpu/GrFragmentProcessor.h"
|
||||
@ -135,6 +136,18 @@ sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkUpdatableShader* SkShaderBase::updatableShader(SkArenaAlloc* alloc) const {
|
||||
if (auto updatable = this->onUpdatableShader(alloc)) {
|
||||
return updatable;
|
||||
}
|
||||
|
||||
return alloc->make<SkTransformShader>(*as_SB(this));
|
||||
}
|
||||
|
||||
SkUpdatableShader* SkShaderBase::onUpdatableShader(SkArenaAlloc* alloc) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkShaders::Empty() { return sk_make_sp<SkEmptyShader>(); }
|
||||
sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
|
||||
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
virtual bool SK_WARN_UNUSED_RESULT update(const SkMatrix& ctm) = 0;
|
||||
};
|
||||
|
||||
class SkUpdatableShader;
|
||||
|
||||
class SkShaderBase : public SkShader {
|
||||
public:
|
||||
~SkShaderBase() override;
|
||||
@ -211,6 +213,9 @@ public:
|
||||
*/
|
||||
virtual sk_sp<SkShader> makeAsALocalMatrixShader(SkMatrix* localMatrix) const;
|
||||
|
||||
SkUpdatableShader* updatableShader(SkArenaAlloc* alloc) const;
|
||||
virtual SkUpdatableShader* onUpdatableShader(SkArenaAlloc* alloc) const;
|
||||
|
||||
SkStageUpdater* appendUpdatableStages(const SkStageRec& rec) const {
|
||||
return this->onAppendUpdatableStages(rec);
|
||||
}
|
||||
@ -259,6 +264,11 @@ private:
|
||||
using INHERITED = SkShader;
|
||||
};
|
||||
|
||||
class SkUpdatableShader : public SkShaderBase {
|
||||
public:
|
||||
virtual bool update(const SkMatrix& ctm, skvm::Uniforms* u) const = 0;
|
||||
};
|
||||
|
||||
inline SkShaderBase* as_SB(SkShader* shader) {
|
||||
return static_cast<SkShaderBase*>(shader);
|
||||
}
|
||||
|
63
src/shaders/SkTransformShader.cpp
Normal file
63
src/shaders/SkTransformShader.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/core/SkMatrixProvider.h"
|
||||
#include "src/shaders/SkTransformShader.h"
|
||||
|
||||
SkTransformShader::SkTransformShader(const SkShaderBase& shader) : fShader{shader} {}
|
||||
|
||||
skvm::Color SkTransformShader::onProgram(skvm::Builder* b,
|
||||
skvm::Coord device, skvm::Coord local, skvm::Color color,
|
||||
const SkMatrixProvider& matrices, const SkMatrix* localM,
|
||||
const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
|
||||
skvm::Coord newLocal = this->applyMatrix(b, matrices.localToDevice(), local, uniforms);
|
||||
SkSimpleMatrixProvider matrixProvider{SkMatrix::I()};
|
||||
return fShader.program(
|
||||
b, device, newLocal, color, matrixProvider, localM, dst, uniforms, alloc);
|
||||
}
|
||||
|
||||
skvm::Coord SkTransformShader::applyMatrix(
|
||||
skvm::Builder* b, const SkMatrix& matrix, skvm::Coord local,
|
||||
skvm::Uniforms* uniforms) const {
|
||||
|
||||
fMatrix = uniforms->pushPtr(&fMatrixStorage);
|
||||
|
||||
skvm::F32 x = local.x,
|
||||
y = local.y;
|
||||
|
||||
auto dot = [&,x,y](int row) {
|
||||
return b->mad(x, b->arrayF(fMatrix, 3*row+0),
|
||||
b->mad(y, b->arrayF(fMatrix, 3*row+1),
|
||||
b->arrayF(fMatrix, 3*row+2)));
|
||||
};
|
||||
|
||||
x = dot(0);
|
||||
y = dot(1);
|
||||
if (matrix.hasPerspective() || fShader.getLocalMatrix().hasPerspective()) {
|
||||
x = x * (1.0f / dot(2));
|
||||
y = y * (1.0f / dot(2));
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
bool SkTransformShader::update(const SkMatrix& ctm, skvm::Uniforms* u) const {
|
||||
SkMatrix matrix;
|
||||
if (this->computeTotalInverse(ctm, nullptr, &matrix)) {
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
fMatrixStorage[i] = matrix[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkTransformShader::onAppendStages(const SkStageRec& rec) const {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
47
src/shaders/SkTransformShader.h
Normal file
47
src/shaders/SkTransformShader.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkTextCoordShader_DEFINED
|
||||
#define SkTextCoordShader_DEFINED
|
||||
|
||||
#include "src/core/SkVM.h"
|
||||
#include "src/shaders/SkShaderBase.h"
|
||||
|
||||
// SkTransformShader allows the transform used by the shader to change without regenerating the
|
||||
// jitted code. This supports the drawVertices call to change the mapping as the texture
|
||||
// coordinates associated with each vertex change with each new triangle.
|
||||
class SkTransformShader : public SkUpdatableShader {
|
||||
public:
|
||||
explicit SkTransformShader(const SkShaderBase& shader);
|
||||
|
||||
// Adds instructions to use the mapping stored in the uniforms represented by fMatrix. After
|
||||
// generating a new skvm::Coord, it passes the mapped coordinates to fShader's onProgram
|
||||
// along with the identity matrix.
|
||||
skvm::Color onProgram(skvm::Builder* b,
|
||||
skvm::Coord device, skvm::Coord local, skvm::Color color,
|
||||
const SkMatrixProvider& matrices, const SkMatrix* localM,
|
||||
const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override;
|
||||
|
||||
// Add code to calculate a new coordinate given local using the mapping in fMatrix.
|
||||
skvm::Coord applyMatrix(
|
||||
skvm::Builder* b, const SkMatrix& matrix, skvm::Coord local,
|
||||
skvm::Uniforms* uniforms) const;
|
||||
|
||||
// Change the values represented by the uniforms in fMatrix.
|
||||
bool update(const SkMatrix& ctm, skvm::Uniforms* u) const override;
|
||||
bool onAppendStages(const SkStageRec& rec) const override;
|
||||
|
||||
private:
|
||||
// For serialization. This will never be called.
|
||||
Factory getFactory() const override { return nullptr; }
|
||||
const char* getTypeName() const override { return nullptr; }
|
||||
|
||||
const SkShaderBase& fShader;
|
||||
mutable SkScalar fMatrixStorage[9];
|
||||
mutable skvm::Uniform fMatrix;
|
||||
};
|
||||
#endif //SkTextCoordShader_DEFINED
|
Loading…
Reference in New Issue
Block a user