Add perspective support to GrTextureOp.
Bug: skia:
Change-Id: Idea4ffae37dc2c2f339af60a2b74ded476091758
Reviewed-on: https://skia-review.googlesource.com/127600
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 91a6927..5b7a30d 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -38,18 +38,24 @@
*/
class TextureGeometryProcessor : public GrGeometryProcessor {
public:
- struct Vertex {
- SkPoint fPosition;
+ template <typename P> struct Vertex {
+ static constexpr GrAA kAA = GrAA::kNo;
+ static constexpr bool kIsMultiTexture = false;
+ using Position = P;
+ P fPosition;
SkPoint fTextureCoords;
GrColor fColor;
};
- struct AAVertex : public Vertex {
+ template <typename P> struct AAVertex : Vertex<P> {
+ static constexpr GrAA kAA = GrAA::kYes;
SkPoint3 fEdges[4];
};
- struct MultiTextureVertex : Vertex {
+ template <typename P> struct MultiTextureVertex : Vertex<P> {
+ static constexpr bool kIsMultiTexture = true;
int fTextureIdx;
};
- struct AAMultiTextureVertex : MultiTextureVertex {
+ template <typename P> struct AAMultiTextureVertex : MultiTextureVertex<P> {
+ static constexpr GrAA kAA = GrAA::kYes;
SkPoint3 fEdges[4];
};
@@ -68,15 +74,16 @@
static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
- const GrSamplerState::Filter filters[],
+ bool perspective, const GrSamplerState::Filter filters[],
const GrShaderCaps& caps) {
// We use placement new to avoid always allocating space for kMaxTextures TextureSampler
// instances.
int samplerCnt = NumSamplersToUse(proxyCnt, caps);
size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
void* mem = GrGeometryProcessor::operator new(size);
- return sk_sp<TextureGeometryProcessor>(new (mem) TextureGeometryProcessor(
- proxies, proxyCnt, samplerCnt, std::move(csxf), coverageAA, filters, caps));
+ return sk_sp<TextureGeometryProcessor>(
+ new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt, std::move(csxf),
+ coverageAA, perspective, filters, caps));
}
~TextureGeometryProcessor() override {
@@ -90,7 +97,9 @@
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
- b->add32(static_cast<uint32_t>(this->usesCoverageEdgeAA()));
+ uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
+ x |= kFloat3_GrVertexAttribType == fPositions.fType ? 0 : 2;
+ b->add32(x);
}
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
@@ -111,9 +120,12 @@
const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
fColorSpaceXformHelper.emitCode(
args.fUniformHandler, textureGP.fColorSpaceXform.get());
- args.fVaryingHandler->setNoPerspective();
+ if (kFloat2_GrVertexAttribType == textureGP.fPositions.fType) {
+ args.fVaryingHandler->setNoPerspective();
+ }
args.fVaryingHandler->emitAttributes(textureGP);
- this->writeOutputPosition(args.fVertBuilder, gpArgs, textureGP.fPositions.fName);
+ gpArgs->fPositionVar = textureGP.fPositions.asShaderVar();
+
this->emitTransforms(args.fVertBuilder,
args.fVaryingHandler,
args.fUniformHandler,
@@ -154,21 +166,38 @@
args.fFragBuilder->codeAppend(";");
if (textureGP.usesCoverageEdgeAA()) {
const char* aaDistName = nullptr;
- // When interpolation is innacurate we perform the evaluation of the edge
+ bool mulByFragCoordW = false;
+ // When interpolation is inaccurate we perform the evaluation of the edge
// equations in the fragment shader rather than interpolating values computed
// in the vertex shader.
if (!args.fShaderCaps->interpolantsAreInaccurate()) {
GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
GrGLSLVarying::Scope::kVertToFrag);
- args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
- args.fVertBuilder->codeAppendf(
- R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
- dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
- dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
- dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
- aaDistVarying.vsOut(), textureGP.fPositions.fName,
- textureGP.fPositions.fName, textureGP.fPositions.fName,
- textureGP.fPositions.fName);
+ if (kFloat3_GrVertexAttribType == textureGP.fPositions.fType) {
+ args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
+ // The distance from edge equation e to homogenous point p=sk_Position
+ // is e.x*p.x/p.wx + e.y*p.y/p.w + e.z. However, we want screen space
+ // interpolation of this distance. We can do this by multiplying the
+ // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
+ // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
+ args.fVertBuilder->codeAppendf(
+ R"(%s = float4(dot(aaEdge0, %s), dot(aaEdge1, %s),
+ dot(aaEdge2, %s), dot(aaEdge3, %s));)",
+ aaDistVarying.vsOut(), textureGP.fPositions.fName,
+ textureGP.fPositions.fName, textureGP.fPositions.fName,
+ textureGP.fPositions.fName);
+ mulByFragCoordW = true;
+ } else {
+ args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
+ args.fVertBuilder->codeAppendf(
+ R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
+ dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
+ dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
+ dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
+ aaDistVarying.vsOut(), textureGP.fPositions.fName,
+ textureGP.fPositions.fName, textureGP.fPositions.fName,
+ textureGP.fPositions.fName);
+ }
aaDistName = aaDistVarying.fsIn();
} else {
GrGLSLVarying aaEdgeVarying[4]{
@@ -199,6 +228,9 @@
args.fFragBuilder->codeAppendf(
"float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
aaDistName, aaDistName, aaDistName, aaDistName);
+ if (mulByFragCoordW) {
+ args.fFragBuilder->codeAppend("mindist *= sk_FragCoord.w;");
+ }
args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
args.fOutputCoverage);
} else {
@@ -229,7 +261,7 @@
}
TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
- sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
+ sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
const GrSamplerState::Filter filters[], const GrShaderCaps& caps)
: INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
@@ -242,7 +274,11 @@
this->addTextureSampler(&fSamplers[i]);
}
- fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
+ if (perspective) {
+ fPositions = this->addVertexAttrib("position", kFloat3_GrVertexAttribType);
+ } else {
+ fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
+ }
fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
@@ -322,27 +358,37 @@
namespace {
// This is a class soley so it can be partially specialized (functions cannot be).
-template<GrAA, typename Vertex> class VertexAAHandler;
+template <typename Vertex, GrAA AA = Vertex::kAA, typename Position = typename Vertex::Position>
+class VertexAAHandler;
-template<typename Vertex> class VertexAAHandler<GrAA::kNo, Vertex> {
+template<typename Vertex> class VertexAAHandler<Vertex, GrAA::kNo, SkPoint> {
public:
- static void AssignPositionsAndTexCoords(Vertex* vertices, const GrQuad& quad,
+ static void AssignPositionsAndTexCoords(Vertex* vertices, const GrPerspQuad& quad,
const SkRect& texRect) {
- vertices[0].fPosition = quad.point(0);
- vertices[0].fTextureCoords = {texRect.fLeft, texRect.fTop};
- vertices[1].fPosition = quad.point(1);
- vertices[1].fTextureCoords = {texRect.fLeft, texRect.fBottom};
- vertices[2].fPosition = quad.point(2);
- vertices[2].fTextureCoords = {texRect.fRight, texRect.fTop};
- vertices[3].fPosition = quad.point(3);
- vertices[3].fTextureCoords = {texRect.fRight, texRect.fBottom};
+ SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
+ SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(Vertex));
+ for (int i = 0; i < 4; ++i) {
+ vertices[i].fPosition = {quad.x(i), quad.y(i)};
+ }
}
};
-template<typename Vertex> class VertexAAHandler<GrAA::kYes, Vertex> {
+template<typename Vertex> class VertexAAHandler<Vertex, GrAA::kNo, SkPoint3> {
public:
- static void AssignPositionsAndTexCoords(Vertex* vertices, const GrQuad& quad,
+ static void AssignPositionsAndTexCoords(Vertex* vertices, const GrPerspQuad& quad,
const SkRect& texRect) {
+ SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(Vertex));
+ for (int i = 0; i < 4; ++i) {
+ vertices[i].fPosition = quad.point(i);
+ }
+ }
+};
+
+template<typename Vertex> class VertexAAHandler<Vertex, GrAA::kYes, SkPoint> {
+public:
+ static void AssignPositionsAndTexCoords(Vertex* vertices, const GrPerspQuad& quad,
+ const SkRect& texRect) {
+ SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
auto x = quad.x4f();
auto y = quad.y4f();
Sk4f a, b, c;
@@ -359,7 +405,7 @@
}
private:
- static void AssignTexCoords(Vertex* vertices, const GrQuad& quad, const SkRect& tex) {
+ static void AssignTexCoords(Vertex* vertices, const GrPerspQuad& quad, const SkRect& tex) {
SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
quad.y(0), quad.y(1), quad.y(2),
1.f, 1.f, 1.f);
@@ -377,14 +423,73 @@
}
};
-template <typename Vertex, bool IsMultiTex> struct TexIdAssigner;
+template<typename Vertex> class VertexAAHandler<Vertex, GrAA::kYes, SkPoint3> {
+public:
+ static void AssignPositionsAndTexCoords(Vertex* vertices, const GrPerspQuad& quad,
+ const SkRect& texRect) {
+ auto x = quad.x4f();
+ auto y = quad.y4f();
+ auto iw = quad.iw4f();
+ x *= iw;
+ y *= iw;
+
+ // Get an equation for w from device space coords.
+ SkMatrix P;
+ P.setAll(x[0], y[0], 1, x[1], y[1], 1, x[2], y[2], 1);
+ SkAssertResult(P.invert(&P));
+ SkPoint3 weq{quad.w(0), quad.w(1), quad.w(2)};
+ P.mapHomogeneousPoints(&weq, &weq, 1);
+
+ Sk4f a, b, c;
+ compute_quad_edges_and_outset_vertices(&x, &y, &a, &b, &c);
+
+ // Compute new w values for the output vertices;
+ auto w = Sk4f(weq.fX) * x + Sk4f(weq.fY) * y + Sk4f(weq.fZ);
+ x *= w;
+ y *= w;
+
+ for (int i = 0; i < 4; ++i) {
+ vertices[i].fPosition = {x[i], y[i], w[i]};
+ for (int j = 0; j < 4; ++j) {
+ vertices[i].fEdges[j] = {a[j], b[j], c[j]};
+ }
+ }
+
+ AssignTexCoords(vertices, quad, texRect);
+ }
+
+private:
+ static void AssignTexCoords(Vertex* vertices, const GrPerspQuad& quad, const SkRect& tex) {
+ SkMatrix q = SkMatrix::MakeAll(quad.x(0), quad.x(1), quad.x(2),
+ quad.y(0), quad.y(1), quad.y(2),
+ quad.w(0), quad.w(1), quad.w(2));
+ SkMatrix qinv;
+ if (!q.invert(&qinv)) {
+ return;
+ }
+ SkMatrix t = SkMatrix::MakeAll(tex.fLeft, tex.fLeft, tex.fRight,
+ tex.fTop, tex.fBottom, tex.fTop,
+ 1.f, 1.f, 1.f);
+ SkMatrix map;
+ map.setConcat(t, qinv);
+ SkPoint3 tempTexCoords[4];
+ SkMatrixPriv::MapHomogeneousPointsWithStride(map, tempTexCoords, sizeof(SkPoint3),
+ &vertices[0].fPosition, sizeof(Vertex), 4);
+ for (int i = 0; i < 4; ++i) {
+ auto invW = 1.f / tempTexCoords[i].fZ;
+ vertices[i].fTextureCoords.fX = tempTexCoords[i].fX * invW;
+ vertices[i].fTextureCoords.fY = tempTexCoords[i].fY * invW;
+ }
+ }
+};
+
+template <typename Vertex, bool MT = Vertex::kIsMultiTexture> struct TexIdAssigner;
template <typename Vertex> struct TexIdAssigner<Vertex, true> {
static void Assign(Vertex* vertices, int textureIdx) {
- vertices[0].fTextureIdx = textureIdx;
- vertices[1].fTextureIdx = textureIdx;
- vertices[2].fTextureIdx = textureIdx;
- vertices[3].fTextureIdx = textureIdx;
+ for (int i = 0; i < 4; ++i) {
+ vertices[i].fTextureIdx = textureIdx;
+ }
}
};
@@ -393,8 +498,8 @@
};
} // anonymous namespace
-template <typename Vertex, bool IsMultiTex, GrAA AA>
-static void tessellate_quad(const GrQuad& devQuad, const SkRect& srcRect, GrColor color,
+template <typename Vertex>
+static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color,
GrSurfaceOrigin origin, Vertex* vertices, SkScalar iw, SkScalar ih,
int textureIdx) {
SkRect texRect = {
@@ -407,12 +512,12 @@
texRect.fTop = 1.f - texRect.fTop;
texRect.fBottom = 1.f - texRect.fBottom;
}
- VertexAAHandler<AA, Vertex>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
+ VertexAAHandler<Vertex>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
vertices[0].fColor = color;
vertices[1].fColor = color;
vertices[2].fColor = color;
vertices[3].fColor = color;
- TexIdAssigner<Vertex, IsMultiTex>::Assign(vertices, textureIdx);
+ TexIdAssigner<Vertex>::Assign(vertices, textureIdx);
}
/**
* Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
@@ -524,8 +629,10 @@
draw.fSrcRect = srcRect;
draw.fTextureIdx = 0;
draw.fColor = color;
- draw.fQuad = GrQuad(dstRect, viewMatrix);
- SkRect bounds = draw.fQuad.bounds();
+ fPerspective = viewMatrix.hasPerspective();
+ SkRect bounds;
+ draw.fQuad = GrPerspQuad(dstRect, viewMatrix);
+ bounds = draw.fQuad.bounds();
this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds);
@@ -543,9 +650,9 @@
}
bool coverageAA = GrAAType::kCoverage == this->aaType();
- sk_sp<GrGeometryProcessor> gp =
- TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform),
- coverageAA, filters, *target->caps().shaderCaps());
+ sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
+ proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
+ filters, *target->caps().shaderCaps());
GrPipeline::InitArgs args;
args.fProxy = target->proxy();
args.fCaps = &target->caps();
@@ -568,59 +675,52 @@
SkDebugf("Could not allocate vertices\n");
return;
}
+
+// Generic lambda in C++14?
+#define TESS_VERTS(Vertex) \
+ SkASSERT(gp->getVertexStride() == sizeof(Vertex)); \
+ auto vertices = static_cast<Vertex*>(vdata); \
+ for (const auto& draw : fDraws) { \
+ auto origin = proxies[draw.fTextureIdx]->origin(); \
+ tessellate_quad<Vertex>(draw.fQuad, draw.fSrcRect, draw.fColor, origin, vertices, \
+ iw[draw.fTextureIdx], ih[draw.fTextureIdx], draw.fTextureIdx); \
+ vertices += 4; \
+ }
+
+ float iw[kMaxTextures];
+ float ih[kMaxTextures];
+ for (int t = 0; t < fProxyCnt; ++t) {
+ const auto* texture = proxies[t]->priv().peekTexture();
+ iw[t] = 1.f / texture->width();
+ ih[t] = 1.f / texture->height();
+ }
+
if (1 == fProxyCnt) {
- GrSurfaceOrigin origin = proxies[0]->origin();
- GrTexture* texture = proxies[0]->priv().peekTexture();
- float iw = 1.f / texture->width();
- float ih = 1.f / texture->height();
if (coverageAA) {
- SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::AAVertex));
- auto vertices = static_cast<TextureGeometryProcessor::AAVertex*>(vdata);
- for (int i = 0; i < fDraws.count(); ++i) {
- tessellate_quad<TextureGeometryProcessor::AAVertex, false, GrAA::kYes>(
- fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin,
- vertices + 4 * i, iw, ih, 0);
+ if (fPerspective) {
+ TESS_VERTS(TextureGeometryProcessor::AAVertex<SkPoint3>)
+ } else {
+ TESS_VERTS(TextureGeometryProcessor::AAVertex<SkPoint>)
}
} else {
- SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
- auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
- for (int i = 0; i < fDraws.count(); ++i) {
- tessellate_quad<TextureGeometryProcessor::Vertex, false, GrAA::kNo>(
- fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin,
- vertices + 4 * i, iw, ih, 0);
+ if (fPerspective) {
+ TESS_VERTS(TextureGeometryProcessor::Vertex<SkPoint3>)
+ } else {
+ TESS_VERTS(TextureGeometryProcessor::Vertex<SkPoint>)
}
}
} else {
- GrTexture* textures[kMaxTextures];
- float iw[kMaxTextures];
- float ih[kMaxTextures];
- for (int t = 0; t < fProxyCnt; ++t) {
- textures[t] = proxies[t]->priv().peekTexture();
- iw[t] = 1.f / textures[t]->width();
- ih[t] = 1.f / textures[t]->height();
- }
if (coverageAA) {
- SkASSERT(gp->getVertexStride() ==
- sizeof(TextureGeometryProcessor::AAMultiTextureVertex));
- auto vertices = static_cast<TextureGeometryProcessor::AAMultiTextureVertex*>(vdata);
- for (int i = 0; i < fDraws.count(); ++i) {
- auto tidx = fDraws[i].fTextureIdx;
- GrSurfaceOrigin origin = proxies[tidx]->origin();
- tessellate_quad<TextureGeometryProcessor::AAMultiTextureVertex, true,
- GrAA::kYes>(fDraws[i].fQuad, fDraws[i].fSrcRect,
- fDraws[i].fColor, origin, vertices + 4 * i,
- iw[tidx], ih[tidx], tidx);
+ if (fPerspective) {
+ TESS_VERTS(TextureGeometryProcessor::AAMultiTextureVertex<SkPoint3>)
+ } else {
+ TESS_VERTS(TextureGeometryProcessor::AAMultiTextureVertex<SkPoint>)
}
} else {
- SkASSERT(gp->getVertexStride() ==
- sizeof(TextureGeometryProcessor::MultiTextureVertex));
- auto vertices = static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
- for (int i = 0; i < fDraws.count(); ++i) {
- auto tidx = fDraws[i].fTextureIdx;
- GrSurfaceOrigin origin = proxies[tidx]->origin();
- tessellate_quad<TextureGeometryProcessor::MultiTextureVertex, true, GrAA::kNo>(
- fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin,
- vertices + 4 * i, iw[tidx], ih[tidx], tidx);
+ if (fPerspective) {
+ TESS_VERTS(TextureGeometryProcessor::MultiTextureVertex<SkPoint3>)
+ } else {
+ TESS_VERTS(TextureGeometryProcessor::MultiTextureVertex<SkPoint>)
}
}
}
@@ -704,6 +804,7 @@
}
this->joinBounds(*that);
fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea);
+ fPerspective |= that->fPerspective;
return true;
}
@@ -779,7 +880,7 @@
struct Draw {
SkRect fSrcRect;
int fTextureIdx;
- GrQuad fQuad;
+ GrPerspQuad fQuad;
GrColor fColor;
};
SkSTArray<1, Draw, true> fDraws;
@@ -795,6 +896,7 @@
GrSamplerState::Filter fFilter0;
uint8_t fProxyCnt;
unsigned fAAType : 2;
+ unsigned fPerspective : 1;
// Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
unsigned fFinalized : 1;
unsigned fAllowSRGBInputs : 1;
@@ -813,7 +915,6 @@
GrColor color, const SkRect& srcRect, const SkRect& dstRect,
GrAAType aaType, const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs) {
- SkASSERT(!viewMatrix.hasPerspective());
return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, aaType, viewMatrix,
std::move(csxf), allowSRGBInputs);
}