From 6a639040bc3ee5369d86c9cb5b9047208beb5206 Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Wed, 14 Dec 2016 11:08:17 -0500 Subject: Update rect ops to use "op" in their name and return sk_sp. Change-Id: I757c33d1cd17a7a7dda858f0fc5ab1094e3c2472 Reviewed-on: https://skia-review.googlesource.com/5985 Reviewed-by: Brian Osman Commit-Queue: Brian Salomon --- src/gpu/batches/GrAAFillRectOp.cpp | 408 +++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 src/gpu/batches/GrAAFillRectOp.cpp (limited to 'src/gpu/batches/GrAAFillRectOp.cpp') diff --git a/src/gpu/batches/GrAAFillRectOp.cpp b/src/gpu/batches/GrAAFillRectOp.cpp new file mode 100644 index 0000000000..9f7b0e0205 --- /dev/null +++ b/src/gpu/batches/GrAAFillRectOp.cpp @@ -0,0 +1,408 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrAAFillRectOp.h" + +#include "GrColor.h" +#include "GrDefaultGeoProcFactory.h" +#include "GrMeshDrawOp.h" +#include "GrOpFlushState.h" +#include "GrResourceKey.h" +#include "GrResourceProvider.h" +#include "GrTypes.h" +#include "SkMatrix.h" +#include "SkRect.h" + +GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey); + +static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) { + pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride); +} + +static const int kNumAAFillRectsInIndexBuffer = 256; +static const int kVertsPerAAFillRect = 8; +static const int kIndicesPerAAFillRect = 30; + +const GrBuffer* get_index_buffer(GrResourceProvider* resourceProvider) { + GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey); + + // clang-format off + static const uint16_t gFillAARectIdx[] = { + 0, 1, 5, 5, 4, 0, + 1, 2, 6, 6, 5, 1, + 2, 3, 7, 7, 6, 2, + 3, 0, 4, 4, 7, 3, + 4, 5, 6, 6, 7, 4, + }; + // clang-format on + + GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect); + return resourceProvider->findOrCreateInstancedIndexBuffer( + gFillAARectIdx, kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, + kVertsPerAAFillRect, gAAFillRectIndexBufferKey); +} + +static void generate_aa_fill_rect_geometry(intptr_t verts, + size_t vertexStride, + GrColor color, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkRect& devRect, + const GrXPOverridesForBatch& overrides, + const SkMatrix* localMatrix) { + SkPoint* fan0Pos = reinterpret_cast(verts); + SkPoint* fan1Pos = reinterpret_cast(verts + 4 * vertexStride); + + SkScalar inset; + + if (viewMatrix.rectStaysRect()) { + inset = SkMinScalar(devRect.width(), SK_Scalar1); + inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); + + set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); + set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); + } else { + // compute transformed (1, 0) and (0, 1) vectors + SkVector vec[2] = {{viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY]}, + {viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY]}}; + + SkScalar len1 = SkPoint::Normalize(&vec[0]); + vec[0].scale(SK_ScalarHalf); + SkScalar len2 = SkPoint::Normalize(&vec[1]); + vec[1].scale(SK_ScalarHalf); + + inset = SkMinScalar(len1 * rect.width(), SK_Scalar1); + inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height()); + + // create the rotated rect + fan0Pos->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); + viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4); + + // Now create the inset points and then outset the original + // rotated points + + // TL + *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = + *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; + *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; + // BL + *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = + *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; + *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; + // BR + *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = + *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; + *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; + // TR + *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = + *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; + *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; + } + + if (localMatrix) { + SkMatrix invViewMatrix; + if (!viewMatrix.invert(&invViewMatrix)) { + SkDebugf("View matrix is non-invertible, local coords will be wrong."); + invViewMatrix = SkMatrix::I(); + } + SkMatrix localCoordMatrix; + localCoordMatrix.setConcat(*localMatrix, invViewMatrix); + SkPoint* fan0Loc = reinterpret_cast(verts + sizeof(SkPoint) + sizeof(GrColor)); + localCoordMatrix.mapPointsWithStride(fan0Loc, fan0Pos, vertexStride, 8); + } + + bool tweakAlphaForCoverage = overrides.canTweakAlphaForCoverage(); + + // Make verts point to vertex color and then set all the color and coverage vertex attrs + // values. + verts += sizeof(SkPoint); + + // The coverage offset is always the last vertex attribute + intptr_t coverageOffset = vertexStride - sizeof(GrColor) - sizeof(SkPoint); + for (int i = 0; i < 4; ++i) { + if (tweakAlphaForCoverage) { + *reinterpret_cast(verts + i * vertexStride) = 0; + } else { + *reinterpret_cast(verts + i * vertexStride) = color; + *reinterpret_cast(verts + i * vertexStride + coverageOffset) = 0; + } + } + + int scale; + if (inset < SK_ScalarHalf) { + scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); + SkASSERT(scale >= 0 && scale <= 255); + } else { + scale = 0xff; + } + + verts += 4 * vertexStride; + + float innerCoverage = GrNormalizeByteToFloat(scale); + GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); + + for (int i = 0; i < 4; ++i) { + if (tweakAlphaForCoverage) { + *reinterpret_cast(verts + i * vertexStride) = scaledColor; + } else { + *reinterpret_cast(verts + i * vertexStride) = color; + *reinterpret_cast(verts + i * vertexStride + coverageOffset) = innerCoverage; + } + } +} + +class AAFillRectOp final: public GrMeshDrawOp { +public: + DEFINE_OP_CLASS_ID + + AAFillRectOp(GrColor color, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkRect& devRect, + const SkMatrix* localMatrix) + : INHERITED(ClassID()) { + if (localMatrix) { + void* mem = fRectData.push_back_n(sizeof(RectWithLocalMatrixInfo)); + new (mem) RectWithLocalMatrixInfo(color, viewMatrix, rect, devRect, *localMatrix); + } else { + void* mem = fRectData.push_back_n(sizeof(RectInfo)); + new (mem) RectInfo(color, viewMatrix, rect, devRect); + } + IsZeroArea zeroArea = + (!rect.width() || !rect.height()) ? IsZeroArea::kYes : IsZeroArea::kNo; + this->setBounds(devRect, HasAABloat::kYes, zeroArea); + fRectCnt = 1; + } + + const char* name() const override { return "AAFillRectOp"; } + + SkString dumpInfo() const override { + SkString str; + str.appendf("# batched: %d\n", fRectCnt); + const RectInfo* info = this->first(); + for (int i = 0; i < fRectCnt; ++i) { + const SkRect& rect = info->rect(); + str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, + info->color(), rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); + info = this->next(info); + } + str.append(DumpPipelineInfo(*this->pipeline())); + str.append(INHERITED::dumpInfo()); + return str; + } + + void computePipelineOptimizations(GrInitInvariantOutput* color, + GrInitInvariantOutput* coverage, + GrBatchToXPOverrides* overrides) const override { + // When this is called there is only one rect + color->setKnownFourComponents(this->first()->color()); + coverage->setUnknownSingleComponent(); + } + + void initBatchTracker(const GrXPOverridesForBatch& overrides) override { + GrColor color; + if (overrides.getOverrideColorIfSet(&color)) { + this->first()->setColor(color); + } + fOverrides = overrides; + } + +private: + void onPrepareDraws(Target* target) const override { + bool needLocalCoords = fOverrides.readsLocalCoords(); + using namespace GrDefaultGeoProcFactory; + + Color color(Color::kAttribute_Type); + Coverage::Type coverageType; + if (fOverrides.canTweakAlphaForCoverage()) { + coverageType = Coverage::kSolid_Type; + } else { + coverageType = Coverage::kAttribute_Type; + } + Coverage coverage(coverageType); + LocalCoords lc = + needLocalCoords ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type; + sk_sp gp = + GrDefaultGeoProcFactory::Make(color, coverage, lc, SkMatrix::I()); + if (!gp) { + SkDebugf("Couldn't create GrGeometryProcessor\n"); + return; + } + + size_t vertexStride = gp->getVertexStride(); + + sk_sp indexBuffer(get_index_buffer(target->resourceProvider())); + InstancedHelper helper; + void* vertices = + helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(), + kVertsPerAAFillRect, kIndicesPerAAFillRect, fRectCnt); + if (!vertices || !indexBuffer) { + SkDebugf("Could not allocate vertices\n"); + return; + } + + const RectInfo* info = this->first(); + const SkMatrix* localMatrix = nullptr; + for (int i = 0; i < fRectCnt; i++) { + intptr_t verts = + reinterpret_cast(vertices) + i * kVertsPerAAFillRect * vertexStride; + if (needLocalCoords) { + if (info->hasLocalMatrix()) { + localMatrix = &static_cast(info)->localMatrix(); + } else { + localMatrix = &SkMatrix::I(); + } + } + generate_aa_fill_rect_geometry(verts, vertexStride, info->color(), info->viewMatrix(), + info->rect(), info->devRect(), fOverrides, localMatrix); + info = this->next(info); + } + helper.recordDraw(target, gp.get()); + } + + bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { + AAFillRectOp* that = t->cast(); + if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), + that->bounds(), caps)) { + return false; + } + + // In the event of two ops, one who can tweak, one who cannot, we just fall back to not + // tweaking. + if (fOverrides.canTweakAlphaForCoverage() && !that->fOverrides.canTweakAlphaForCoverage()) { + fOverrides = that->fOverrides; + } + + fRectData.push_back_n(that->fRectData.count(), that->fRectData.begin()); + fRectCnt += that->fRectCnt; + this->joinBounds(*that); + return true; + } + + struct RectInfo { + public: + RectInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, + const SkRect& devRect) + : RectInfo(color, viewMatrix, rect, devRect, HasLocalMatrix::kNo) {} + bool hasLocalMatrix() const { return HasLocalMatrix::kYes == fHasLocalMatrix; } + GrColor color() const { return fColor; } + const SkMatrix& viewMatrix() const { return fViewMatrix; } + const SkRect& rect() const { return fRect; } + const SkRect& devRect() const { return fDevRect; } + + void setColor(GrColor color) { fColor = color; } + + protected: + enum class HasLocalMatrix : uint32_t { kNo, kYes }; + + RectInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, + const SkRect& devRect, HasLocalMatrix hasLM) + : fHasLocalMatrix(hasLM) + , fColor(color) + , fViewMatrix(viewMatrix) + , fRect(rect) + , fDevRect(devRect) {} + + HasLocalMatrix fHasLocalMatrix; + GrColor fColor; + SkMatrix fViewMatrix; + SkRect fRect; + SkRect fDevRect; + }; + + struct RectWithLocalMatrixInfo : public RectInfo { + public: + RectWithLocalMatrixInfo(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, + const SkRect& devRect, const SkMatrix& localMatrix) + : RectInfo(color, viewMatrix, rect, devRect, HasLocalMatrix::kYes) + , fLocalMatrix(localMatrix) {} + const SkMatrix& localMatrix() const { return fLocalMatrix; } + + private: + SkMatrix fLocalMatrix; + }; + + RectInfo* first() { return reinterpret_cast(fRectData.begin()); } + const RectInfo* first() const { return reinterpret_cast(fRectData.begin()); } + const RectInfo* next(const RectInfo* prev) const { + intptr_t next = + reinterpret_cast(prev) + + (prev->hasLocalMatrix() ? sizeof(RectWithLocalMatrixInfo) : sizeof(RectInfo)); + return reinterpret_cast(next); + } + + GrXPOverridesForBatch fOverrides; + SkSTArray<4 * sizeof(RectWithLocalMatrixInfo), uint8_t, true> fRectData; + int fRectCnt; + + typedef GrMeshDrawOp INHERITED; +}; + +namespace GrAAFillRectOp { + +sk_sp Make(GrColor color, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkRect& devRect) { + return sk_sp(new AAFillRectOp(color, viewMatrix, rect, devRect, nullptr)); +} + +sk_sp Make(GrColor color, + const SkMatrix& viewMatrix, + const SkMatrix& localMatrix, + const SkRect& rect, + const SkRect& devRect) { + return sk_sp(new AAFillRectOp(color, viewMatrix, rect, devRect, &localMatrix)); +} + +sk_sp Make(GrColor color, + const SkMatrix& viewMatrix, + const SkMatrix& localMatrix, + const SkRect& rect) { + SkRect devRect; + viewMatrix.mapRect(&devRect, rect); + return Make(color, viewMatrix, localMatrix, rect, devRect); +} + +sk_sp MakeWithLocalRect(GrColor color, + const SkMatrix& viewMatrix, + const SkRect& rect, + const SkRect& localRect) { + SkRect devRect; + viewMatrix.mapRect(&devRect, rect); + SkMatrix localMatrix; + if (!localMatrix.setRectToRect(rect, localRect, SkMatrix::kFill_ScaleToFit)) { + return nullptr; + } + return Make(color, viewMatrix, localMatrix, rect, devRect); +} +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef GR_TEST_UTILS + +#include "GrBatchTest.h" + +DRAW_BATCH_TEST_DEFINE(AAFillRectOp) { + GrColor color = GrRandomColor(random); + SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); + SkRect rect = GrTest::TestRect(random); + SkRect devRect = GrTest::TestRect(random); + return GrAAFillRectOp::Make(color, viewMatrix, rect, devRect).release(); +} + +DRAW_BATCH_TEST_DEFINE(AAFillRectOpLocalMatrix) { + GrColor color = GrRandomColor(random); + SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); + SkMatrix localMatrix = GrTest::TestMatrix(random); + SkRect rect = GrTest::TestRect(random); + SkRect devRect = GrTest::TestRect(random); + return GrAAFillRectOp::Make(color, viewMatrix, localMatrix, rect, devRect).release(); +} + +#endif -- cgit v1.2.3