Make TessellationHelper resettable
Change-Id: I94d22edf13b8392e1d7b84367fd65701f99a0e54
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/255137
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index 4619d44..412867f 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -385,9 +385,16 @@
// TessellationHelper implementation
///////////////////////////////////////////////////////////////////////////////////////////////////
-TessellationHelper::TessellationHelper(const GrQuad& deviceQuad, const GrQuad* localQuad)
- : fDeviceType(deviceQuad.quadType())
- , fLocalType(localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned) {
+void TessellationHelper::reset(const GrQuad& deviceQuad, const GrQuad* localQuad) {
+ // Record basic state that isn't recorded on the Vertices struct itself
+ fDeviceType = deviceQuad.quadType();
+ fLocalType = localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned;
+
+ // Reset metadata validity
+ fOutsetRequestValid = false;
+ fEdgeEquationsValid = false;
+
+ // Set vertices to match the device and local quad
fOriginal.fX = deviceQuad.x4f();
fOriginal.fY = deviceQuad.y4f();
fOriginal.fW = deviceQuad.w4f();
@@ -431,10 +438,12 @@
// on thefInvSinTheta since it will approach infinity.
fEdgeVectors.fInvSinTheta = rsqrt(1.f - fEdgeVectors.fCosTheta * fEdgeVectors.fCosTheta);
}
+
+ fVerticesValid = true;
}
const TessellationHelper::EdgeEquations& TessellationHelper::getEdgeEquations() {
- if (!fEdgeEquations.fValid) {
+ if (!fEdgeEquationsValid) {
V4f dx = fEdgeVectors.fDX;
V4f dy = fEdgeVectors.fDY;
// Correct for bad edges by copying adjacent edge information into the bad component
@@ -453,7 +462,7 @@
fEdgeEquations.fC = c;
}
- fEdgeEquations.fValid = true;
+ fEdgeEquationsValid = true;
}
return fEdgeEquations;
}
@@ -465,7 +474,7 @@
SkASSERT(all(edgeDistances >= 0.f));
// Rebuild outset request if invalid or if the edge distances have changed.
- if (!fOutsetRequest.fValid || any(edgeDistances != fOutsetRequest.fEdgeDistances)) {
+ if (!fOutsetRequestValid || any(edgeDistances != fOutsetRequest.fEdgeDistances)) {
// Based on the edge distances, determine if it's acceptable to use fInvSinTheta to
// calculate the inset or outset geometry.
if (fDeviceType <= GrQuad::Type::kRectilinear) {
@@ -513,7 +522,7 @@
}
fOutsetRequest.fEdgeDistances = edgeDistances;
- fOutsetRequest.fValid = true;
+ fOutsetRequestValid = true;
}
return fOutsetRequest;
}
@@ -654,6 +663,28 @@
}
}
+void TessellationHelper::Vertices::asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType,
+ GrQuad* localOut, GrQuad::Type localType) const {
+ SkASSERT(deviceOut);
+ SkASSERT(fUVRCount == 0 || localOut);
+
+ fX.store(deviceOut->xs());
+ fY.store(deviceOut->ys());
+ if (deviceType == GrQuad::Type::kPerspective) {
+ fW.store(deviceOut->ws());
+ }
+ deviceOut->setQuadType(deviceType); // This sets ws == 1 when device type != perspective
+
+ if (fUVRCount > 0) {
+ fU.store(localOut->xs());
+ fV.store(localOut->ys());
+ if (fUVRCount == 3) {
+ fR.store(localOut->ws());
+ }
+ localOut->setQuadType(localType);
+ }
+}
+
V4f TessellationHelper::EdgeEquations::estimateCoverage(const V4f& x2d, const V4f& y2d) const {
// Calculate distance of the 4 inset points (px, py) to the 4 edges
V4f d0 = mad(fA[0], x2d, mad(fB[0], y2d, fC[0]));
@@ -799,9 +830,11 @@
V4f TessellationHelper::inset(const skvx::Vec<4, float>& edgeDistances,
GrQuad* deviceInset, GrQuad* localInset) {
+ SkASSERT(fVerticesValid);
+
Vertices inset = fOriginal;
int vertexCount = this->adjustVertices(edgeDistances, true, &inset);
- this->setQuads(inset, deviceInset, localInset);
+ inset.asGrQuads(deviceInset, fDeviceType, localInset, fLocalType);
if (vertexCount < 3) {
// The interior has less than a full pixel's area so estimate reduced coverage using
@@ -815,31 +848,11 @@
void TessellationHelper::outset(const skvx::Vec<4, float>& edgeDistances,
GrQuad* deviceOutset, GrQuad* localOutset) {
+ SkASSERT(fVerticesValid);
+
Vertices outset = fOriginal;
this->adjustVertices(edgeDistances, false, &outset);
- this->setQuads(outset, deviceOutset, localOutset);
-}
-
-void TessellationHelper::setQuads(const Vertices& vertices,
- GrQuad* deviceOut, GrQuad* localOut) const {
- SkASSERT(deviceOut);
- SkASSERT(vertices.fUVRCount == 0 || localOut);
-
- vertices.fX.store(deviceOut->xs());
- vertices.fY.store(deviceOut->ys());
- if (fDeviceType == GrQuad::Type::kPerspective) {
- vertices.fW.store(deviceOut->ws());
- }
- deviceOut->setQuadType(fDeviceType); // This sets ws == 1 when device type != perspective
-
- if (vertices.fUVRCount > 0) {
- vertices.fU.store(localOut->xs());
- vertices.fV.store(localOut->ys());
- if (vertices.fUVRCount == 3) {
- vertices.fR.store(localOut->ws());
- }
- localOut->setQuadType(fLocalType);
- }
+ outset.asGrQuads(deviceOutset, fDeviceType, localOutset, fLocalType);
}
}; // namespace GrQuadUtils
diff --git a/src/gpu/geometry/GrQuadUtils.h b/src/gpu/geometry/GrQuadUtils.h
index d18e9d2..58f6237 100644
--- a/src/gpu/geometry/GrQuadUtils.h
+++ b/src/gpu/geometry/GrQuadUtils.h
@@ -42,8 +42,9 @@
class TessellationHelper {
public:
- // Provide nullptr if there are no local coordinates to track
- TessellationHelper(const GrQuad& deviceQuad, const GrQuad* localQuad);
+ // Set the original device and (optional) local coordinates that are inset or outset
+ // by the requested edge distances. Use nullptr if there are no local coordinates to update.
+ void reset(const GrQuad& deviceQuad, const GrQuad* localQuad);
// Calculates a new quadrilateral with edges parallel to the original except that they
// have been moved inwards by edgeDistances (which should be positive). Distances are
@@ -94,6 +95,9 @@
void moveTo(const skvx::Vec<4, float>& x2d,
const skvx::Vec<4, float>& y2d,
const skvx::Vec<4, int32_t>& mask);
+
+ void asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType,
+ GrQuad* localOut, GrQuad::Type localType) const;
};
// NOTE: This struct is named 'EdgeVectors' because it holds a lot of cached calculations
@@ -117,8 +121,6 @@
struct EdgeEquations {
// a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
skvx::Vec<4, float> fA, fB, fC;
- // True if the field is up to date with the state of fOriginal+fAAFlags
- bool fValid = false;
skvx::Vec<4, float> estimateCoverage(const skvx::Vec<4, float>& x2d,
const skvx::Vec<4, float>& y2d) const;
@@ -134,11 +136,8 @@
// be because of the requested edge distances (collapse of inset, etc.)
bool fInsetDegenerate;
bool fOutsetDegenerate;
- // True if the field is up to date with the state of fOriginal+fAAFlags
- bool fValid = false;
};
- // Always valid
Vertices fOriginal;
EdgeVectors fEdgeVectors;
GrQuad::Type fDeviceType;
@@ -148,7 +147,13 @@
OutsetRequest fOutsetRequest;
EdgeEquations fEdgeEquations;
- void setQuads(const Vertices& vertices, GrQuad* deviceOut, GrQuad* localOut) const;
+ // Validity of Vertices/EdgeVectors (always true after first call to set()).
+ bool fVerticesValid = false;
+ // Validity of outset request (true after calling getOutsetRequest() until next set() call
+ // or next inset/outset() with different edge distances).
+ bool fOutsetRequestValid = false;
+ // Validity of edge equations (true after calling getEdgeEquations() until next set() call).
+ bool fEdgeEquationsValid = false;
// The requested edge distances must be positive so that they can be reused between inset
// and outset calls.
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 687d3a5..6833107 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -107,6 +107,7 @@
geomDomain.outset(0.5f, 0.5f); // account for AA expansion
}
+ GrQuadUtils::TessellationHelper helper;
if (aaFlags == GrQuadAAFlags::kNone) {
// Have to write the coverage AA vertex structure, but there's no math to be done for a
// non-aa quad batched into a coverage AA op.
@@ -118,8 +119,7 @@
// TODO(michaelludwig) - Update TessellateHelper to select processing functions based on
// the vertexspec once per op, and then burn through all quads with the selected
// function ptr.
- GrQuadUtils::TessellationHelper helper(deviceQuad,
- spec.hasLocalCoords() ? &localQuad : nullptr);
+ helper.reset(deviceQuad, spec.hasLocalCoords() ? &localQuad : nullptr);
// Edge inset/outset distance ordered LBTR, set to 0.5 for a half pixel if the AA flag
// is turned on, or 0.0 if the edge is not anti-aliased.