[graphite] Hook up software path renderer.

Added code to use software path renderer when Vello is not available and the multisample count is 1. This path is disabled for the moment due
to Dawn issues. Also fixes some bugs in the SoftwarePathAtlas.


Bug: b/291735945
Change-Id: I2c01c17becd52f09fcefbf390f9b84815c0c1c96
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/752359
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/graphite/AtlasProvider.cpp b/src/gpu/graphite/AtlasProvider.cpp
index 3167c73..3ba6569 100644
--- a/src/gpu/graphite/AtlasProvider.cpp
+++ b/src/gpu/graphite/AtlasProvider.cpp
@@ -16,11 +16,19 @@
 namespace skgpu::graphite {
 
 AtlasProvider::AtlasProvider(Recorder* recorder)
-        : fTextAtlasManager(std::make_unique<TextAtlasManager>(recorder)) {}
+        : fTextAtlasManager(std::make_unique<TextAtlasManager>(recorder)) {
+    // Disable for now.
+    //fPathAtlasFlags |= PathAtlasFlags::kSoftware;
+#ifdef SK_ENABLE_VELLO_SHADERS
+    if (recorder->priv().caps()->computeSupport()) {
+        fPathAtlasFlags |= PathAtlasFlag::kCompute;
+    }
+#endif  // SK_ENABLE_VELLO_SHADERS
+}
 
 std::unique_ptr<ComputePathAtlas> AtlasProvider::createComputePathAtlas(Recorder* recorder) const {
 #ifdef SK_ENABLE_VELLO_SHADERS
-    if (recorder->priv().caps()->computeSupport()) {
+    if (fPathAtlasFlags & PathAtlasFlag::kCompute) {
         return std::make_unique<VelloComputePathAtlas>();
     }
 #endif  // SK_ENABLE_VELLO_SHADERS
@@ -28,7 +36,10 @@
 }
 
 std::unique_ptr<SoftwarePathAtlas> AtlasProvider::createSoftwarePathAtlas() const {
-    return std::make_unique<SoftwarePathAtlas>();
+    if (fPathAtlasFlags & PathAtlasFlags::kSoftware) {
+        return std::make_unique<SoftwarePathAtlas>();
+    }
+    return nullptr;
 }
 
 sk_sp<TextureProxy> AtlasProvider::getAtlasTexture(Recorder* recorder,
diff --git a/src/gpu/graphite/AtlasProvider.h b/src/gpu/graphite/AtlasProvider.h
index f93ae97..beb8ab6 100644
--- a/src/gpu/graphite/AtlasProvider.h
+++ b/src/gpu/graphite/AtlasProvider.h
@@ -10,6 +10,8 @@
 
 #include "include/core/SkColorType.h"
 #include "include/core/SkRefCnt.h"
+#include "include/private/base/SkTo.h"
+#include "src/base/SkEnumBitMask.h"
 
 #include <memory>
 #include <unordered_map>
@@ -34,6 +36,20 @@
     // rendering. This TextAtlasManager is always available.
     TextAtlasManager* textAtlasManager() const { return fTextAtlasManager.get(); }
 
+    enum class PathAtlasFlags : unsigned {
+        kNone     = 0b000,
+        // ComputePathAtlas is supported
+        kCompute  = 0b001,
+        // SoftwarePathAtlas is supported
+        kSoftware = 0b010,
+    };
+    SK_DECL_BITMASK_OPS_FRIENDS(PathAtlasFlags);
+
+    // Returns whether a particular atlas type is available
+    bool isAvailable(PathAtlasFlags atlasType) {
+        return SkToBool(fPathAtlasFlags & atlasType);
+    }
+
     // Creates a new transient atlas handler that uses compute shaders to rasterize coverage masks
     // for path rendering. This method returns nullptr if compute shaders are not supported by the
     // owning Recorder's context.
@@ -59,8 +75,12 @@
     // allocations. For now our model is simple: all PathAtlases target the same texture and only
     // one of them will render to the texture during a given command submission.
     std::unordered_map<uint64_t, sk_sp<TextureProxy>> fTexturePool;
+
+    SkEnumBitMask<PathAtlasFlags> fPathAtlasFlags = PathAtlasFlags::kNone;
 };
 
+SK_MAKE_BITMASK_OPS(AtlasProvider::PathAtlasFlags)
+
 }  // namespace skgpu::graphite
 
 #endif  // skgpu_graphite_AtlasProvider_DEFINED
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index 2b12e59..8719a64 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -1204,19 +1204,25 @@
         return {renderers->analyticRRect(), nullptr};
     }
 
+    PathAtlas* pathAtlas = nullptr;
     // Prefer compute atlas draws if supported. This currently implicitly filters out clip draws as
     // they require MSAA. Eventually we may want to route clip shapes to the atlas as well but not
     // if hardware MSAA is required.
     // TODO(b/285195175): There may be reasons to prefer tessellation, e.g. if the shape is large
     // and hardware MSAA looks acceptable.
-    // TODO(b/280927548): Currently we assume `pathAtlas` is a GPU compute path atlas and select it
-    // if it's supported (this should be always nullptr if SK_ENABLE_VELLO_SHADERS isn't defined).
-    // This will likely need to provide more information about the PathAtlas' rendering algorithm
-    // when we support non-compute PathAtlases, which may factor into the renderer choice.
-    PathAtlas* pathAtlas = fDC->getOrCreatePathAtlas(fRecorder);
-    if (!requireMSAA && pathAtlas) {
+    AtlasProvider* atlasProvider = fRecorder->priv().atlasProvider();
+    if (atlasProvider->isAvailable(AtlasProvider::PathAtlasFlags::kCompute)) {
         // TODO: vello can't do correct strokes yet. Maybe this shouldn't get selected for stroke
         // renders until all stroke styles are supported?
+        pathAtlas = fDC->getComputePathAtlas(fRecorder);
+    // Only use CPU rendered paths when multisampling is disabled
+    // TODO: enable other uses of the software path renderer
+    } else if (fRecorder->priv().caps()->defaultMSAASamplesCount() <= 1 &&
+               atlasProvider->isAvailable(AtlasProvider::PathAtlasFlags::kSoftware)) {
+        pathAtlas = fDC->getSoftwarePathAtlas(fRecorder);
+    }
+    // We currently always use a coverage mask renderer if a `PathAtlas` is selected.
+    if (!requireMSAA && pathAtlas) {
         return {renderers->coverageMask(), pathAtlas};
     }
 
diff --git a/src/gpu/graphite/DrawContext.cpp b/src/gpu/graphite/DrawContext.cpp
index ca4708d..e6ce3e6 100644
--- a/src/gpu/graphite/DrawContext.cpp
+++ b/src/gpu/graphite/DrawContext.cpp
@@ -131,14 +131,20 @@
                                          std::move(condContext));
 }
 
-PathAtlas* DrawContext::getOrCreatePathAtlas(Recorder* recorder) {
-    // TODO: Determine whether to use SoftwarePathAtlas
+PathAtlas* DrawContext::getComputePathAtlas(Recorder* recorder) {
     if (!fComputePathAtlas) {
         fComputePathAtlas = recorder->priv().atlasProvider()->createComputePathAtlas(recorder);
     }
     return fComputePathAtlas.get();
 }
 
+PathAtlas* DrawContext::getSoftwarePathAtlas(Recorder* recorder) {
+    if (!fSoftwarePathAtlas) {
+        fSoftwarePathAtlas = recorder->priv().atlasProvider()->createSoftwarePathAtlas();
+    }
+    return fSoftwarePathAtlas.get();
+}
+
 void DrawContext::snapDrawPass(Recorder* recorder) {
     if (fPendingDraws->drawCount() == 0 && fPendingLoadOp != LoadOp::kClear) {
         return;
@@ -263,6 +269,7 @@
 sk_sp<Task> DrawContext::snapUploadTask(Recorder* recorder) {
     if (fSoftwarePathAtlas) {
         fSoftwarePathAtlas->recordUploads(this, recorder);
+        fSoftwarePathAtlas->reset();
     }
 
     if (!fPendingUploads || fPendingUploads->size() == 0) {
diff --git a/src/gpu/graphite/DrawContext.h b/src/gpu/graphite/DrawContext.h
index e0f3ebc..7f85e33 100644
--- a/src/gpu/graphite/DrawContext.h
+++ b/src/gpu/graphite/DrawContext.h
@@ -83,14 +83,15 @@
                       const SkIRect& dstRect,
                       std::unique_ptr<ConditionalUploadContext>);
 
-    // Returns the transient path atlas that accumulates coverage masks for atlas draws recorded to
-    // this SDC. The atlas gets created lazily upon request. Returns nullptr if atlas draws are not
-    // supported.
-    //
-    // TODO: Should this be explicit about how the atlas gets drawn (i.e. GPU compute vs CPU)?
-    // Currently this is assumed to use GPU compute atlas. Maybe the PathAtlas class should report
-    // its rendering algorithm to aid the renderer selection in `chooseRenderer`?
-    PathAtlas* getOrCreatePathAtlas(Recorder*);
+    // Returns the transient path atlas that uses compute to accumulate coverage masks for atlas
+    // draws recorded to this SDC. The atlas gets created lazily upon request. Returns nullptr
+    // if compute path generation is not supported.
+    PathAtlas* getComputePathAtlas(Recorder*);
+
+    // Returns the transient path atlas that uses CPU rendering to upload coverage masks for atlas
+    // draws recorded to this SDC. The atlas gets created lazily upon request. Returns nullptr
+    // if software path generation is not supported.
+    PathAtlas* getSoftwarePathAtlas(Recorder*);
 
     // Ends the current DrawList being accumulated by the SDC, converting it into an optimized and
     // immutable DrawPass. The DrawPass will be ordered after any other snapped DrawPasses or
diff --git a/src/gpu/graphite/PathAtlas.cpp b/src/gpu/graphite/PathAtlas.cpp
index 3729f7c..cbf8478 100644
--- a/src/gpu/graphite/PathAtlas.cpp
+++ b/src/gpu/graphite/PathAtlas.cpp
@@ -267,6 +267,10 @@
                                    atlasBounds.y() + 1 - deviceOffset.y());
     draw.fCTM = &translatedMatrix;
     SkPath path = shape.asPath();
+    if (path.isInverseFillType()) {
+        // The shader will handle the inverse fill in this case
+        path.toggleInverseFillType();
+    }
     draw.drawPathCoverage(path, paint);
 
     // Add atlasBounds to dirtyRect for later upload
diff --git a/src/gpu/graphite/UploadTask.h b/src/gpu/graphite/UploadTask.h
index 1bd3c0e..9063dc5 100644
--- a/src/gpu/graphite/UploadTask.h
+++ b/src/gpu/graphite/UploadTask.h
@@ -81,7 +81,7 @@
     UploadInstance& operator=(UploadInstance&&);
     ~UploadInstance();
 
-    bool isValid() const { return fBuffer != nullptr; }
+    bool isValid() const { return fBuffer != nullptr && fTextureProxy != nullptr; }
 
     bool prepareResources(ResourceProvider*);