| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/ccpr/GrCCPerFlushResources.h" |
| |
| #include "include/gpu/GrRecordingContext.h" |
| #include "src/gpu/GrFixedClip.h" |
| #include "src/gpu/GrMemoryPool.h" |
| #include "src/gpu/GrOnFlushResourceProvider.h" |
| #include "src/gpu/GrRecordingContextPriv.h" |
| #include "src/gpu/GrSurfaceDrawContext.h" |
| #include "src/gpu/geometry/GrStyledShape.h" |
| #include "src/gpu/ops/GrFillRectOp.h" |
| |
| GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP, |
| const GrCCAtlas::Specs& specs) |
| : fAtlasSpecs(specs) { |
| } |
| |
| std::unique_ptr<GrCCAtlas> GrCCPerFlushResources::renderDeviceSpacePathInAtlas( |
| GrOnFlushResourceProvider* onFlushRP, const SkIRect& clipIBounds, const SkPath& devPath, |
| const SkIRect& devPathIBounds, GrFillRule fillRule, SkIVector* devToAtlasOffset) { |
| SkASSERT(!devPath.isEmpty()); |
| GrScissorTest enableScissorInAtlas; |
| SkIRect clippedPathIBounds; |
| if (clipIBounds.contains(devPathIBounds)) { |
| clippedPathIBounds = devPathIBounds; |
| enableScissorInAtlas = GrScissorTest::kDisabled; |
| } else { |
| SkAssertResult(clippedPathIBounds.intersect(clipIBounds, devPathIBounds)); |
| enableScissorInAtlas = GrScissorTest::kEnabled; |
| } |
| |
| auto retiredAtlas = this->placeRenderedPathInAtlas(onFlushRP, clippedPathIBounds, |
| enableScissorInAtlas, devToAtlasOffset); |
| |
| SkMatrix atlasMatrix = SkMatrix::Translate(devToAtlasOffset->fX, devToAtlasOffset->fY); |
| this->enqueueRenderedPath(devPath, fillRule, clippedPathIBounds, atlasMatrix, |
| enableScissorInAtlas, *devToAtlasOffset); |
| |
| return retiredAtlas; |
| } |
| |
| std::unique_ptr<GrCCAtlas> GrCCPerFlushResources::placeRenderedPathInAtlas( |
| GrOnFlushResourceProvider* onFlushRP, const SkIRect& clippedPathIBounds, |
| GrScissorTest scissorTest, SkIVector* devToAtlasOffset) { |
| std::unique_ptr<GrCCAtlas> retiredAtlas; |
| SkIPoint16 location; |
| if (!fAtlas || |
| !fAtlas->addRect(clippedPathIBounds.width(), clippedPathIBounds.height(), &location)) { |
| // The retired atlas is out of room and can't grow any bigger. |
| if (fAtlas) { |
| this->flushRenderedPaths(onFlushRP); |
| retiredAtlas = std::move(fAtlas); |
| } |
| fAtlas = std::make_unique<GrCCAtlas>(fAtlasSpecs, *onFlushRP->caps()); |
| SkASSERT(clippedPathIBounds.width() <= fAtlasSpecs.fMinWidth); |
| SkASSERT(clippedPathIBounds.height() <= fAtlasSpecs.fMinHeight); |
| SkAssertResult(fAtlas->addRect(clippedPathIBounds.width(), clippedPathIBounds.height(), |
| &location)); |
| } |
| devToAtlasOffset->set(location.x() - clippedPathIBounds.left(), |
| location.y() - clippedPathIBounds.top()); |
| return retiredAtlas; |
| } |
| |
| void GrCCPerFlushResources::enqueueRenderedPath(const SkPath& path, GrFillRule fillRule, |
| const SkIRect& clippedDevIBounds, |
| const SkMatrix& pathToAtlasMatrix, |
| GrScissorTest enableScissorInAtlas, |
| SkIVector devToAtlasOffset) { |
| SkPath* atlasPath; |
| if (enableScissorInAtlas == GrScissorTest::kDisabled) { |
| atlasPath = &fAtlasPaths[(int)fillRule].fUberPath; |
| } else { |
| auto& [scissoredPath, scissor] = fAtlasPaths[(int)fillRule].fScissoredPaths.push_back(); |
| scissor = clippedDevIBounds.makeOffset(devToAtlasOffset); |
| atlasPath = &scissoredPath; |
| } |
| auto origin = clippedDevIBounds.topLeft() + devToAtlasOffset; |
| atlasPath->moveTo(origin.fX, origin.fY); // Implicit moveTo(0,0). |
| atlasPath->addPath(path, pathToAtlasMatrix); |
| } |
| |
| static void draw_stencil_to_coverage(GrOnFlushResourceProvider* onFlushRP, |
| GrSurfaceDrawContext* surfaceDrawContext, SkRect&& rect) { |
| auto aaType = GrAAType::kMSAA; |
| auto fillRectFlags = GrSimpleMeshDrawOpHelper::InputFlags::kNone; |
| |
| // This will be the final op in the surfaceDrawContext. So if Ganesh is planning to discard the |
| // stencil values anyway, then we might not actually need to reset the stencil values back to 0. |
| bool mustResetStencil = !onFlushRP->caps()->discardStencilValuesAfterRenderPass(); |
| |
| if (surfaceDrawContext->numSamples() == 1) { |
| // We are mixed sampled. We need to either enable conservative raster (preferred) or disable |
| // MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for the cover |
| // geometry, the stencil test is still multisampled and will still produce smooth results.) |
| if (onFlushRP->caps()->conservativeRasterSupport()) { |
| fillRectFlags |= GrSimpleMeshDrawOpHelper::InputFlags::kConservativeRaster; |
| } else { |
| aaType = GrAAType::kNone; |
| } |
| mustResetStencil = true; |
| } |
| |
| const GrUserStencilSettings* stencil; |
| if (mustResetStencil) { |
| constexpr static GrUserStencilSettings kTestAndResetStencil( |
| GrUserStencilSettings::StaticInit< |
| 0x0000, |
| GrUserStencilTest::kNotEqual, |
| 0xffff, |
| GrUserStencilOp::kZero, |
| GrUserStencilOp::kKeep, |
| 0xffff>()); |
| |
| // Outset the cover rect in case there are T-junctions in the path bounds. |
| rect.outset(1, 1); |
| stencil = &kTestAndResetStencil; |
| } else { |
| constexpr static GrUserStencilSettings kTestStencil( |
| GrUserStencilSettings::StaticInit< |
| 0x0000, |
| GrUserStencilTest::kNotEqual, |
| 0xffff, |
| GrUserStencilOp::kKeep, |
| GrUserStencilOp::kKeep, |
| 0xffff>()); |
| |
| stencil = &kTestStencil; |
| } |
| |
| GrPaint paint; |
| paint.setColor4f(SK_PMColor4fWHITE); |
| GrQuad coverQuad(rect); |
| DrawQuad drawQuad{coverQuad, coverQuad, GrQuadAAFlags::kAll}; |
| auto coverOp = GrFillRectOp::Make(surfaceDrawContext->recordingContext(), std::move(paint), |
| aaType, &drawQuad, stencil, fillRectFlags); |
| surfaceDrawContext->addDrawOp(nullptr, std::move(coverOp)); |
| } |
| |
| void GrCCPerFlushResources::flushRenderedPaths(GrOnFlushResourceProvider* onFlushRP) { |
| SkASSERT(fAtlas); |
| auto surfaceDrawContext = fAtlas->instantiate(onFlushRP); |
| if (!surfaceDrawContext) { |
| for (int i = 0; i < (int)SK_ARRAY_COUNT(fAtlasPaths); ++i) { |
| fAtlasPaths[i].fUberPath.reset(); |
| fAtlasPaths[i].fScissoredPaths.reset(); |
| } |
| return; |
| } |
| |
| for (int i = 0; i < (int)SK_ARRAY_COUNT(fAtlasPaths); ++i) { |
| SkPathFillType fillType = (i == (int)GrFillRule::kNonzero) ? SkPathFillType::kWinding |
| : SkPathFillType::kEvenOdd; |
| SkPath& uberPath = fAtlasPaths[i].fUberPath; |
| if (!uberPath.isEmpty()) { |
| uberPath.setIsVolatile(true); |
| uberPath.setFillType(fillType); |
| surfaceDrawContext->stencilPath(nullptr, GrAA::kYes, SkMatrix::I(), uberPath); |
| uberPath.reset(); |
| } |
| for (auto& [scissoredPath, scissor] : fAtlasPaths[i].fScissoredPaths) { |
| GrFixedClip fixedClip( |
| surfaceDrawContext->asRenderTargetProxy()->backingStoreDimensions(), scissor); |
| scissoredPath.setIsVolatile(true); |
| scissoredPath.setFillType(fillType); |
| surfaceDrawContext->stencilPath(&fixedClip, GrAA::kYes, SkMatrix::I(), scissoredPath); |
| } |
| fAtlasPaths[i].fScissoredPaths.reset(); |
| } |
| |
| draw_stencil_to_coverage(onFlushRP, surfaceDrawContext.get(), |
| SkRect::MakeSize(SkSize::Make(fAtlas->drawBounds()))); |
| |
| if (surfaceDrawContext->asSurfaceProxy()->requiresManualMSAAResolve()) { |
| onFlushRP->addTextureResolveTask(sk_ref_sp(surfaceDrawContext->asTextureProxy()), |
| GrSurfaceProxy::ResolveFlags::kMSAA); |
| } |
| } |
| |
| std::unique_ptr<GrCCAtlas> GrCCPerFlushResources::finalize(GrOnFlushResourceProvider* onFlushRP) { |
| if (fAtlas) { |
| this->flushRenderedPaths(onFlushRP); |
| } |
| #ifdef SK_DEBUG |
| // These paths should have been rendered and reset to empty by this point. |
| for (size_t i = 0; i < SK_ARRAY_COUNT(fAtlasPaths); ++i) { |
| SkASSERT(fAtlasPaths[i].fUberPath.isEmpty()); |
| SkASSERT(fAtlasPaths[i].fScissoredPaths.empty()); |
| } |
| #endif |
| return std::move(fAtlas); |
| } |