aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkMatrix.h22
-rw-r--r--src/gpu/GrDrawState.h19
-rw-r--r--src/gpu/gl/GrGpuGLShaders.cpp8
-rw-r--r--tests/MatrixTest.cpp58
4 files changed, 91 insertions, 16 deletions
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index df3275ec4b..3c3d105805 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -483,20 +483,28 @@ public:
*/
bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
-#ifdef SK_SCALAR_IS_FIXED
- friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
- return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0;
+ /** Efficient comparison of two matrices. It distinguishes between zero and
+ * negative zero. It will return false when the sign of zero values is the
+ * only difference between the two matrices. It considers NaN values to be
+ * equal to themselves. So a matrix full of NaNs is "cheap equal" to
+ * another matrix full of NaNs iff the NaN values are bitwise identical
+ * while according to strict the strict == test a matrix with a NaN value
+ * is equal to nothing, including itself.
+ */
+ bool cheapEqualTo(const SkMatrix& m) const {
+ return 0 == memcmp(fMat, m.fMat, sizeof(fMat));
}
- friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
- return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
+#ifdef SK_SCALAR_IS_FIXED
+ friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
+ return a->cheapEquals(b);
}
#else
- friend bool operator==(const SkMatrix& a, const SkMatrix& b);
+ friend bool operator==(const SkMatrix& a, const SkMatrix& b);
+#endif
friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
return !(a == b);
}
-#endif
enum {
// flatten/unflatten will never return a value larger than this
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index c0ff42fc69..92ee2cbaad 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -85,8 +85,8 @@ struct GrDrawState {
// are tightly packed
GrAssert(kMemsetSize + sizeof(fColor) + sizeof(fCoverage) +
sizeof(fFirstCoverageStage) + sizeof(fColorFilterMode) +
- sizeof(fSrcBlend) + sizeof(fDstBlend) + sizeof(GrMatrix) ==
- reinterpret_cast<uintptr_t>(&fEdgeAANumEdges) -
+ sizeof(fSrcBlend) + sizeof(fDstBlend) ==
+ reinterpret_cast<uintptr_t>(&fViewMatrix) -
reinterpret_cast<uintptr_t>(this));
fEdgeAANumEdges = 0;
@@ -740,7 +740,13 @@ struct GrDrawState {
// Most stages are usually not used, so conditionals here
// reduce the expected number of bytes touched by 50%.
bool operator ==(const GrDrawState& s) const {
- if (memcmp(this, &s, this->leadingBytes())) return false;
+ if (memcmp(this, &s, this->leadingBytes())) {
+ return false;
+ }
+
+ if (!s.fViewMatrix.cheapEqualTo(fViewMatrix)) {
+ return false;
+ }
for (int i = 0; i < kNumStages; i++) {
if (fTextures[i] &&
@@ -766,6 +772,8 @@ struct GrDrawState {
GrDrawState& operator =(const GrDrawState& s) {
memcpy(this, &s, this->leadingBytes());
+ fViewMatrix = s.fViewMatrix;
+
for (int i = 0; i < kNumStages; i++) {
if (s.fTextures[i]) {
memcpy(&this->fSamplerStates[i], &s.fSamplerStates[i],
@@ -799,9 +807,10 @@ private:
SkXfermode::Mode fColorFilterMode;
GrBlendCoeff fSrcBlend;
GrBlendCoeff fDstBlend;
- GrMatrix fViewMatrix;
// @}
+ GrMatrix fViewMatrix;
+
// @{ Data for GrTesselatedPathRenderer
// TODO: currently ignored in copying & comparison for performance.
// Must be considered if GrTesselatedPathRenderer is being used.
@@ -820,7 +829,7 @@ private:
// TODO: ignores GrTesselatedPathRenderer data structures. We don't
// have a compile-time flag that lets us know if it's being used, and
// checking at runtime seems to cost 5% performance.
- return (size_t) ((unsigned char*)&fEdgeAANumEdges -
+ return (size_t) ((unsigned char*)&fViewMatrix -
(unsigned char*)&fBlendConstant);
}
diff --git a/src/gpu/gl/GrGpuGLShaders.cpp b/src/gpu/gl/GrGpuGLShaders.cpp
index cb156f7ae4..ec2d4e2fc8 100644
--- a/src/gpu/gl/GrGpuGLShaders.cpp
+++ b/src/gpu/gl/GrGpuGLShaders.cpp
@@ -409,7 +409,7 @@ void GrGpuGLShaders::onResetContext() {
void GrGpuGLShaders::flushViewMatrix() {
const GrMatrix& vm = this->getDrawState().getViewMatrix();
- if (GrGpuGLShaders::getHWViewMatrix() != vm) {
+ if (!GrGpuGLShaders::getHWViewMatrix().cheapEqualTo(vm)) {
const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
GrAssert(NULL != rt);
@@ -492,11 +492,13 @@ void GrGpuGLShaders::flushTextureMatrix(int s) {
const GrGLTexture* texture =
static_cast<const GrGLTexture*>(drawState.getTexture(s));
if (NULL != texture) {
+ const GrMatrix& hwMatrix = this->getHWSamplerMatrix(s);
+ const GrMatrix& samplerMatrix = drawState.getSampler(s).getMatrix();
if (GrGLProgram::kUnusedUniform != uni &&
(((1 << s) & fDirtyFlags.fTextureChangedMask) ||
- this->getHWSamplerMatrix(s) != drawState.getSampler(s).getMatrix())) {
+ !hwMatrix.cheapEqualTo(samplerMatrix))) {
- GrMatrix m = drawState.getSampler(s).getMatrix();
+ GrMatrix m = samplerMatrix;
GrSamplerState::SampleMode mode =
drawState.getSampler(s).getSampleMode();
AdjustTextureMatrix(texture, mode, &m);
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index c9a696c548..316e670df3 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -32,6 +32,49 @@ static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
return true;
}
+static bool are_equal(skiatest::Reporter* reporter,
+ const SkMatrix& a,
+ const SkMatrix& b) {
+ bool equal = a == b;
+ bool cheapEqual = a.cheapEqualTo(b);
+ if (equal != cheapEqual) {
+#if SK_SCALAR_IS_FLOAT
+ if (equal) {
+ bool foundZeroSignDiff = false;
+ for (int i = 0; i < 9; ++i) {
+ float aVal = a.get(i);
+ float bVal = b.get(i);
+ int aValI = *reinterpret_cast<int*>(&aVal);
+ int bValI = *reinterpret_cast<int*>(&bVal);
+ if (0 == aVal && 0 == bVal && aValI != bValI) {
+ foundZeroSignDiff = true;
+ } else {
+ REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
+ }
+ }
+ REPORTER_ASSERT(reporter, foundZeroSignDiff);
+ } else {
+ bool foundNaN = false;
+ for (int i = 0; i < 9; ++i) {
+ float aVal = a.get(i);
+ float bVal = b.get(i);
+ int aValI = *reinterpret_cast<int*>(&aVal);
+ int bValI = *reinterpret_cast<int*>(&bVal);
+ if (sk_float_isnan(aVal) && aValI == bValI) {
+ foundNaN = true;
+ } else {
+ REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
+ }
+ }
+ REPORTER_ASSERT(reporter, foundNaN);
+ }
+#else
+ REPORTER_ASSERT(reporter, false);
+#endif
+ }
+ return equal;
+}
+
static bool is_identity(const SkMatrix& m) {
SkMatrix identity;
identity.reset();
@@ -49,7 +92,7 @@ static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
SkMatrix m2;
uint32_t size3 = m2.unflatten(buffer);
REPORTER_ASSERT(reporter, size1 == size2);
- REPORTER_ASSERT(reporter, m == m2);
+ REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
char buffer2[SkMatrix::kMaxFlattenSize + 100];
size3 = m2.flatten(buffer2);
@@ -237,6 +280,19 @@ void TestMatrix(skiatest::Reporter* reporter) {
mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
REPORTER_ASSERT(reporter, !mat.asAffine(affine));
+ SkMatrix mat2;
+ mat2.reset();
+ mat.reset();
+ SkScalar zero = 0;
+ mat.set(SkMatrix::kMSkewX, -zero);
+ REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
+
+ mat2.reset();
+ mat.reset();
+ mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
+ mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
+ REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
+
test_matrix_max_stretch(reporter);
}