From 895274391db8df7357334aec260edca2e1735626 Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Fri, 16 Dec 2016 09:52:16 -0500 Subject: move src/gpu/batches -> src/gpu/ops Change-Id: I6410eae41f051ce38bef6f38d670924c3483c325 Reviewed-on: https://skia-review.googlesource.com/6163 Commit-Queue: Brian Salomon Reviewed-by: Brian Osman --- src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp | 407 ++++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp (limited to 'src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp') diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp new file mode 100644 index 0000000000..c2473535f7 --- /dev/null +++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp @@ -0,0 +1,407 @@ +/* + * 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 "GrAALinearizingConvexPathRenderer.h" + +#include "GrAAConvexTessellator.h" +#include "GrBatchTest.h" +#include "GrContext.h" +#include "GrDefaultGeoProcFactory.h" +#include "GrGeometryProcessor.h" +#include "GrInvariantOutput.h" +#include "GrOpFlushState.h" +#include "GrPathUtils.h" +#include "GrPipelineBuilder.h" +#include "GrProcessor.h" +#include "GrStyle.h" +#include "SkGeometry.h" +#include "SkPathPriv.h" +#include "SkString.h" +#include "SkTraceEvent.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "ops/GrMeshDrawOp.h" + +static const int DEFAULT_BUFFER_SIZE = 100; + +// The thicker the stroke, the harder it is to produce high-quality results using tessellation. For +// the time being, we simply drop back to software rendering above this stroke width. +static const SkScalar kMaxStrokeWidth = 20.0; + +GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() { +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { + if (GrAAType::kCoverage != args.fAAType) { + return false; + } + if (!args.fShape->knownToBeConvex()) { + return false; + } + if (args.fShape->style().pathEffect()) { + return false; + } + if (args.fShape->inverseFilled()) { + return false; + } + const SkStrokeRec& stroke = args.fShape->style().strokeRec(); + + if (stroke.getStyle() == SkStrokeRec::kStroke_Style || + stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) { + if (!args.fViewMatrix->isSimilarity()) { + return false; + } + SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth(); + if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) { + return false; + } + return strokeWidth <= kMaxStrokeWidth && + args.fShape->knownToBeClosed() && + stroke.getJoin() != SkPaint::Join::kRound_Join; + } + return stroke.getStyle() == SkStrokeRec::kFill_Style; +} + +// extract the result vertices and indices from the GrAAConvexTessellator +static void extract_verts(const GrAAConvexTessellator& tess, + void* vertices, + size_t vertexStride, + GrColor color, + uint16_t firstIndex, + uint16_t* idxs, + bool tweakAlphaForCoverage) { + intptr_t verts = reinterpret_cast(vertices); + + for (int i = 0; i < tess.numPts(); ++i) { + *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i); + } + + // Make 'verts' point to the colors + verts += sizeof(SkPoint); + for (int i = 0; i < tess.numPts(); ++i) { + if (tweakAlphaForCoverage) { + SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255); + unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i)); + GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); + *reinterpret_cast(verts + i * vertexStride) = scaledColor; + } else { + *reinterpret_cast(verts + i * vertexStride) = color; + *reinterpret_cast(verts + i * vertexStride + sizeof(GrColor)) = + tess.coverage(i); + } + } + + for (int i = 0; i < tess.numIndices(); ++i) { + idxs[i] = tess.index(i) + firstIndex; + } +} + +static sk_sp create_fill_gp(bool tweakAlphaForCoverage, + const SkMatrix& viewMatrix, + bool usesLocalCoords, + bool coverageIgnored) { + using namespace GrDefaultGeoProcFactory; + + Color color(Color::kAttribute_Type); + Coverage::Type coverageType; + // TODO remove coverage if coverage is ignored + /*if (coverageIgnored) { + coverageType = Coverage::kNone_Type; + } else*/ if (tweakAlphaForCoverage) { + coverageType = Coverage::kSolid_Type; + } else { + coverageType = Coverage::kAttribute_Type; + } + Coverage coverage(coverageType); + LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type : + LocalCoords::kUnused_Type); + return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix); +} + +class AAFlatteningConvexPathOp final : public GrMeshDrawOp { +public: + DEFINE_OP_CLASS_ID + static sk_sp Make(GrColor color, + const SkMatrix& viewMatrix, + const SkPath& path, + SkScalar strokeWidth, + SkStrokeRec::Style style, + SkPaint::Join join, + SkScalar miterLimit) { + return sk_sp(new AAFlatteningConvexPathOp(color, viewMatrix, path, strokeWidth, + style, join, miterLimit)); + } + + const char* name() const override { return "AAFlatteningConvexPathOp"; } + + SkString dumpInfo() const override { + SkString string; + for (const auto& path : fPaths) { + string.appendf( + "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, " + "MiterLimit: %.2f\n", + path.fColor, path.fStrokeWidth, path.fStyle, path.fJoin, path.fMiterLimit); + } + string.append(DumpPipelineInfo(*this->pipeline())); + string.append(INHERITED::dumpInfo()); + return string; + } + + void computePipelineOptimizations(GrInitInvariantOutput* color, + GrInitInvariantOutput* coverage, + GrBatchToXPOverrides* overrides) const override { + // When this is called there is only one path. + color->setKnownFourComponents(fPaths[0].fColor); + coverage->setUnknownSingleComponent(); + } + +private: + AAFlatteningConvexPathOp(GrColor color, + const SkMatrix& viewMatrix, + const SkPath& path, + SkScalar strokeWidth, + SkStrokeRec::Style style, + SkPaint::Join join, + SkScalar miterLimit) + : INHERITED(ClassID()) { + fPaths.emplace_back( + PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit}); + + // compute bounds + SkRect bounds = path.getBounds(); + SkScalar w = strokeWidth; + if (w > 0) { + w /= 2; + // If the half stroke width is < 1 then we effectively fallback to bevel joins. + if (SkPaint::kMiter_Join == join && w > 1.f) { + w *= miterLimit; + } + bounds.outset(w, w); + } + this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo); + } + + void initBatchTracker(const GrXPOverridesForBatch& overrides) override { + // Handle any color overrides + if (!overrides.readsColor()) { + fPaths[0].fColor = GrColor_ILLEGAL; + } + overrides.getOverrideColorIfSet(&fPaths[0].fColor); + + // setup batch properties + fColor = fPaths[0].fColor; + fUsesLocalCoords = overrides.readsLocalCoords(); + fCoverageIgnored = !overrides.readsCoverage(); + fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage(); + } + + void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount, + size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const { + if (vertexCount == 0 || indexCount == 0) { + return; + } + const GrBuffer* vertexBuffer; + GrMesh mesh; + int firstVertex; + void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, + &firstVertex); + if (!verts) { + SkDebugf("Could not allocate vertices\n"); + return; + } + memcpy(verts, vertices, vertexCount * vertexStride); + + const GrBuffer* indexBuffer; + int firstIndex; + uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); + if (!idxs) { + SkDebugf("Could not allocate indices\n"); + return; + } + memcpy(idxs, indices, indexCount * sizeof(uint16_t)); + mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex, + firstIndex, vertexCount, indexCount); + target->draw(gp, mesh); + } + + void onPrepareDraws(Target* target) const override { + bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); + + // Setup GrGeometryProcessor + sk_sp gp(create_fill_gp(canTweakAlphaForCoverage, + this->viewMatrix(), + this->usesLocalCoords(), + this->coverageIgnored())); + if (!gp) { + SkDebugf("Couldn't create a GrGeometryProcessor\n"); + return; + } + + size_t vertexStride = gp->getVertexStride(); + + SkASSERT(canTweakAlphaForCoverage ? + vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) : + vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); + + int instanceCount = fPaths.count(); + + int vertexCount = 0; + int indexCount = 0; + int maxVertices = DEFAULT_BUFFER_SIZE; + int maxIndices = DEFAULT_BUFFER_SIZE; + uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride); + uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t)); + for (int i = 0; i < instanceCount; i++) { + const PathData& args = fPaths[i]; + GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth, + args.fJoin, args.fMiterLimit); + + if (!tess.tessellate(args.fViewMatrix, args.fPath)) { + continue; + } + + int currentIndices = tess.numIndices(); + SkASSERT(currentIndices <= UINT16_MAX); + if (indexCount + currentIndices > UINT16_MAX) { + // if we added the current instance, we would overflow the indices we can store in a + // uint16_t. Draw what we've got so far and reset. + this->draw(target, gp.get(), + vertexCount, vertexStride, vertices, indexCount, indices); + vertexCount = 0; + indexCount = 0; + } + int currentVertices = tess.numPts(); + if (vertexCount + currentVertices > maxVertices) { + maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2); + vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride); + } + if (indexCount + currentIndices > maxIndices) { + maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2); + indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t)); + } + + extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor, + vertexCount, indices + indexCount, canTweakAlphaForCoverage); + vertexCount += currentVertices; + indexCount += currentIndices; + } + this->draw(target, gp.get(), vertexCount, vertexStride, vertices, indexCount, indices); + sk_free(vertices); + sk_free(indices); + } + + bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { + AAFlatteningConvexPathOp* that = t->cast(); + if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), + that->bounds(), caps)) { + return false; + } + + SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); + if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { + return false; + } + + // In the event of two ops, one who can tweak, one who cannot, we just fall back to not + // tweaking + if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { + fCanTweakAlphaForCoverage = false; + } + + fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin()); + this->joinBounds(*that); + return true; + } + + GrColor color() const { return fColor; } + bool usesLocalCoords() const { return fUsesLocalCoords; } + bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; } + const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; } + bool coverageIgnored() const { return fCoverageIgnored; } + + struct PathData { + GrColor fColor; + SkMatrix fViewMatrix; + SkPath fPath; + SkScalar fStrokeWidth; + SkStrokeRec::Style fStyle; + SkPaint::Join fJoin; + SkScalar fMiterLimit; + }; + + GrColor fColor; + bool fUsesLocalCoords; + bool fCoverageIgnored; + bool fCanTweakAlphaForCoverage; + SkSTArray<1, PathData, true> fPaths; + + typedef GrMeshDrawOp INHERITED; +}; + +bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), + "GrAALinearizingConvexPathRenderer::onDrawPath"); + SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled()); + SkASSERT(!args.fShape->isEmpty()); + SkASSERT(!args.fShape->style().pathEffect()); + + SkPath path; + args.fShape->asPath(&path); + bool fill = args.fShape->style().isSimpleFill(); + const SkStrokeRec& stroke = args.fShape->style().strokeRec(); + SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth(); + SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin(); + SkScalar miterLimit = stroke.getMiter(); + + sk_sp op = + AAFlatteningConvexPathOp::Make(args.fPaint->getColor(), *args.fViewMatrix, path, + strokeWidth, stroke.getStyle(), join, miterLimit); + + GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fAAType); + pipelineBuilder.setUserStencil(args.fUserStencilSettings); + + args.fRenderTargetContext->addDrawOp(pipelineBuilder, *args.fClip, std::move(op)); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef GR_TEST_UTILS + +DRAW_BATCH_TEST_DEFINE(AAFlatteningConvexPathOp) { + GrColor color = GrRandomColor(random); + SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); + SkPath path = GrTest::TestPathConvex(random); + + SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style, + SkStrokeRec::kStroke_Style, + SkStrokeRec::kStrokeAndFill_Style }; + + SkStrokeRec::Style style = styles[random->nextU() % 3]; + + SkScalar strokeWidth = -1.f; + SkPaint::Join join = SkPaint::kMiter_Join; + SkScalar miterLimit = 0.5f; + + if (SkStrokeRec::kFill_Style != style) { + strokeWidth = random->nextRangeF(1.0f, 10.0f); + if (random->nextBool()) { + join = SkPaint::kMiter_Join; + } else { + join = SkPaint::kBevel_Join; + } + miterLimit = random->nextRangeF(0.5f, 2.0f); + } + + return AAFlatteningConvexPathOp::Make(color, viewMatrix, path, strokeWidth, style, join, + miterLimit) + .release(); +} + +#endif -- cgit v1.2.3