[graphite] Flesh out Device/SDC a bit more
Renames SDCTask -> RenderPassTask and adds comments describing its
purpose. Has SDC use a DrawList and expose similar drawing functions.
Stubs out all Device methods that will eventually need to be supported,
but groups them into in-scope and out-of-scope sections. Implements the
drawing functions in terms of drawPath() and has drawPath() invoke the
SDC functions (with untested support for path effects, hairline, etc.).
Bug: skia:12466
Change-Id: Id661fc987aeb35f0dfa536c0cb0b6210742cf32c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/452233
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/experimental/graphite/src/Device.cpp b/experimental/graphite/src/Device.cpp
index b6226ac..43b89f5 100644
--- a/experimental/graphite/src/Device.cpp
+++ b/experimental/graphite/src/Device.cpp
@@ -8,8 +8,17 @@
#include "experimental/graphite/src/Device.h"
#include "experimental/graphite/include/SkStuff.h"
+#include "experimental/graphite/src/DrawList.h"
#include "experimental/graphite/src/SurfaceDrawContext.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
+#include "include/core/SkPathEffect.h"
+#include "include/core/SkStrokeRec.h"
+
+#include "src/core/SkMatrixPriv.h"
+#include "src/core/SkPaintPriv.h"
+
namespace skgpu {
sk_sp<Device> Device::Make(const SkImageInfo& ii) {
@@ -22,8 +31,16 @@
}
Device::Device(sk_sp<SurfaceDrawContext> sdc)
- : SkBaseDevice(sdc->imageInfo(), SkSurfaceProps())
- , fSDC(std::move(sdc)) {
+ : SkBaseDevice(sdc->imageInfo(), SkSurfaceProps())
+ , fSDC(std::move(sdc)) {
+ SkASSERT(SkToBool(sdc));
+}
+
+SkBaseDevice* Device::onCreateDevice(const CreateInfo& info, const SkPaint*) {
+ // TODO: Inspect the paint and create info to determine if there's anything that has to be
+ // modified to support inline subpasses.
+ // TODO: onCreateDevice really should return sk_sp<SkBaseDevice>...
+ return Make(info.fInfo).release();
}
sk_sp<SkSurface> Device::makeSurface(const SkImageInfo& ii, const SkSurfaceProps& /* props */) {
@@ -31,9 +48,158 @@
}
bool Device::onReadPixels(const SkPixmap& pm, int x, int y) {
+ // TODO: If we're reading back pixels we need to push deferred clip draws and snap off the draw
+ // task so we can have a read back task added to the graph, the same as will be done if the
+ // Device has a snapped special image or is drawn into another device directly.
// TODO: actually do a read back
pm.erase(SK_ColorGREEN);
return true;
}
+void Device::drawPaint(const SkPaint& paint) {
+ SkRect deviceBounds = SkRect::Make(this->devClipBounds());
+ SkM44 devToLocal;
+ if (!this->localToDevice44().invert(&devToLocal)) {
+ // TBD: This matches legacy behavior for drawPaint() that requires local coords, although
+ // v1 handles arbitrary transforms when the paint is solid color because it just fills the
+ // device bounds directly. In the new world it might be nice to have non-invertible
+ // transforms formalized (i.e. no drawing ever, handled at SkCanvas level possibly?)
+ return;
+ }
+ SkRect localCoveringBounds = SkMatrixPriv::MapRect(devToLocal, deviceBounds);
+ this->drawRect(localCoveringBounds, paint);
+}
+
+void Device::drawRect(const SkRect& r, const SkPaint& paint) {
+ // TODO: If the SDC primitive is a rrect (and no simpler), this can be wasted effort since
+ // SkCanvas checks SkRRects for being a rect and reduces it, only for Device to rebuild it
+ // It would be less effort if we can skip the validation of SkRRect ctors here.
+ // TBD: For now rects are paths too, but they may become an SDC primitive
+ this->drawPath(SkPath::Rect(r), paint, /*pathIsMutable=*/true);
+}
+
+void Device::drawOval(const SkRect& oval, const SkPaint& paint) {
+ // TODO: This has wasted effort from the SkCanvas level since it instead converts rrects that
+ // happen to be ovals into this, only for us to go right back to rrect.
+
+ // Ovals are always a simplification of round rects
+ this->drawRRect(SkRRect::MakeOval(oval), paint);
+}
+
+void Device::drawRRect(const SkRRect& rr, const SkPaint& paint) {
+ // TBD: If the SDC has a rrect primitive, this won't need to be converted to a path
+ this->drawPath(SkPath::RRect(rr), paint, /*pathIsMutable=*/true);
+}
+
+void Device::drawPoints(SkCanvas::PointMode mode, size_t count,
+ const SkPoint* points, const SkPaint& paint) {
+ // TODO: I'm [ml] not sure either CPU or GPU backend really has a fast path for this that
+ // isn't captured by drawOval and drawLine, so could easily be moved into SkCanvas.
+ if (mode == SkCanvas::kPoints_PointMode) {
+ SkPaint filled = paint;
+ filled.setStyle(SkPaint::kFill_Style);
+ float radius = 0.5f * paint.getStrokeWidth();
+ for (size_t i = 0; i < count; ++i) {
+ SkRect cap = SkRect::MakeLTRB(points[i].fX - radius, points[i].fY - radius,
+ points[i].fX + radius, points[i].fY + radius);
+ if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
+ this->drawOval(cap, filled);
+ } else {
+ this->drawRect(cap, filled);
+ }
+ }
+ } else {
+ size_t inc = (mode == SkCanvas::kLines_PointMode) ? 2 : 1;
+ SkPathBuilder builder;
+ for (size_t i = 0; i < count; i += inc) {
+ builder.moveTo(points[i]);
+ builder.lineTo(points[i + 1]);
+ this->drawPath(builder.detach(), paint, /*pathIsMutable=*/true);
+ }
+ }
+}
+
+void Device::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
+ // Heavy weight paint options like path effects, mask filters, and stroke-and-fill style are
+ // applied on the CPU by generating a new path and recursing on drawPath().
+ if (paint.getPathEffect()) {
+ // Apply the path effect before anything else
+ // TODO: If asADash() returns true and the base path matches the dashing fast path, then
+ // that should be detected now as well.
+ // TODO: This logic is also a candidate for moving to SkCanvas if SkDevice exposes a faster
+ // dash path.
+
+ // Strip off path effect
+ SkPaint noPE = paint;
+ noPE.setPathEffect(nullptr);
+
+ float scaleFactor = SkPaintPriv::ComputeResScaleForStroking(this->localToDevice());
+ SkStrokeRec stroke(paint, scaleFactor);
+ SkPath dst;
+ if (paint.getPathEffect()->filterPath(&dst, path, &stroke,
+ nullptr, this->localToDevice())) {
+ // Adjust paint style to reflect modifications to stroke rec
+ stroke.applyToPaint(&noPE);
+ this->drawPath(dst, noPE, /*pathIsMutable=*/true);
+ return;
+ } else {
+ // TBD: This warning should go through the general purpose graphite logging system
+ SkDebugf("[graphite] WARNING - Path effect failed to apply, drawing original path.\n");
+ this->drawPath(path, noPE, pathIsMutable);
+ return;
+ }
+ }
+
+ // TODO: Handle mask filters, ignored for now but would be applied at this point. My[ml]
+ // thinking is that if there's a mask filter we call a helper function with the path and the
+ // paint, which returns a coverage mask. Then we do a rectangular draw sampling the mask and
+ // handling the rest of the paint's shading. I don't think that's really any different from
+ // the way it is right now. (not 100% sure, but this may also be a reasonable approach for CPU
+ // so could make SkCanvas handle all path effects, image filters, and mask filters and Devices
+ // only need to handle shaders, color filters, and blenders).
+ if (paint.getMaskFilter()) {
+ return;
+ }
+
+ // Resolve stroke-and-fill -> fill, and hairline -> stroke since the SDC only supports stroke
+ // or fill for path rendering.
+ if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
+ // TODO: Could const-cast path when pathIsMutable is true, might not be worth complexity...
+ SkPath simplified;
+ SkPaint styledPaint = paint;
+ if (paint.getFillPath(path, &simplified, nullptr, this->localToDevice())) {
+ styledPaint.setStyle(SkPaint::kFill_Style);
+ } else {
+ styledPaint.setStyle(SkPaint::kStroke_Style);
+ styledPaint.setStrokeWidth(0.f);
+ }
+ this->drawPath(simplified, styledPaint, /*pathIsMutable=*/true);
+ return;
+ }
+
+ // TODO: Implement clipping and z determination
+ SkIRect scissor = this->devClipBounds();
+
+ auto blendMode = paint.asBlendMode();
+ PaintParams shading{paint.getColor4f(),
+ blendMode.has_value() ? *blendMode : SkBlendMode::kSrcOver,
+ paint.refShader()};
+ if (paint.getStyle() == SkPaint::kStroke_Style) {
+ StrokeParams stroke{paint.getStrokeWidth(), paint.getStrokeMiter(),
+ paint.getStrokeJoin(), paint.getStrokeCap()};
+ if (paint.getStrokeWidth() <= 0.f) {
+ // Handle hairlines by transforming the control points into device space and drawing
+ // that path with a stroke width of 1 and the identity matrix
+ // FIXME: This doesn't work if the shading requires local coords...
+ SkPath devicePath = path.makeTransform(this->localToDevice());
+ stroke.fWidth = 1.f;
+ fSDC->strokePath(SkM44(), devicePath, stroke, scissor, 0, 0, &shading);
+ } else {
+ fSDC->strokePath(this->localToDevice44(), path, stroke, scissor, 0, 0, &shading);
+ }
+ } else {
+ fSDC->fillPath(this->localToDevice44(), path, scissor, 0, 0, &shading);
+ }
+}
+
} // namespace skgpu
diff --git a/experimental/graphite/src/Device.h b/experimental/graphite/src/Device.h
index cbe1ea1..d09535c 100644
--- a/experimental/graphite/src/Device.h
+++ b/experimental/graphite/src/Device.h
@@ -19,31 +19,85 @@
static sk_sp<Device> Make(const SkImageInfo&);
protected:
+ // Clipping
+ void onSave() override {}
+ void onRestore() override {}
+
bool onClipIsAA() const override { return false; }
bool onClipIsWideOpen() const override { return false; }
- void onAsRgnClip(SkRegion*) const override { }
ClipType onGetClipType() const override { return ClipType::kEmpty; }
SkIRect onDevClipBounds() const override { return {}; }
- void drawPaint(const SkPaint& paint) override {}
- void drawPoints(SkCanvas::PointMode mode, size_t count,
- const SkPoint[], const SkPaint& paint) override {}
- void drawRect(const SkRect& r, const SkPaint& paint) override {}
- void drawOval(const SkRect& oval, const SkPaint& paint) override {}
- void drawRRect(const SkRRect& rr, const SkPaint& paint) override {}
- void drawPath(const SkPath& path,
- const SkPaint& paint,
- bool pathIsMutable = false) override {}
+ void onClipRect(const SkRect& rect, SkClipOp, bool aa) override {}
+ void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) override {}
+ void onClipPath(const SkPath& path, SkClipOp, bool aa) override {}
+ // Drawing
+ void drawPaint(const SkPaint& paint) override;
+ void drawRect(const SkRect& r, const SkPaint& paint) override;
+ void drawOval(const SkRect& oval, const SkPaint& paint) override;
+ void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
+ void drawPoints(SkCanvas::PointMode mode, size_t count,
+ const SkPoint[], const SkPaint& paint) override;
+ void drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable = false) override;
+
+ // No need to specialize drawDRRect, drawArc, drawRegion, drawPatch as the default impls all
+ // route to drawPath, drawRect, or drawVertices as desired.
+
+ // Pixel management
+ sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+ SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
+
+ bool onReadPixels(const SkPixmap&, int x, int y) override;
+
+ /*
+ * TODO: These functions are not in scope to be implemented yet, but will need to be. Call them
+ * out explicitly so it's easy to keep tabs on how close feature-complete actually is.
+ */
+
+ void onAsRgnClip(SkRegion*) const override {}
+ void onClipShader(sk_sp<SkShader>) override {}
+ void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override {}
+ void onReplaceClip(const SkIRect& rect) override {}
+
+ bool onWritePixels(const SkPixmap&, int x, int y) override { return false; }
+
+ // TODO: This will likely be implemented with the same primitive building block that drawRect
+ // and drawRRect will rely on.
+ void drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
+ SkCanvas::QuadAAFlags aaFlags, const SkColor4f& color,
+ SkBlendMode mode) override {}
+
+ void drawEdgeAAImageSet(const SkCanvas::ImageSetEntry[], int count,
+ const SkPoint dstClips[], const SkMatrix preViewMatrices[],
+ const SkSamplingOptions&, const SkPaint&,
+ SkCanvas::SrcRectConstraint) override {}
+
+ // TODO: These image drawing APIs can likely be implemented with the same primitive building
+ // block that drawEdgeAAImageSet will use.
void drawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
const SkSamplingOptions&, const SkPaint&,
SkCanvas::SrcRectConstraint) override {}
+ void drawImageLattice(const SkImage*, const SkCanvas::Lattice&,
+ const SkRect& dst, SkFilterMode, const SkPaint&) override {}
+ void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
+ const SkColor[], int count, SkBlendMode, const SkSamplingOptions&,
+ const SkPaint&) override {}
+ void drawDrawable(SkDrawable*, const SkMatrix*, SkCanvas*) override {}
void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override {}
+ void drawShadow(const SkPath&, const SkDrawShadowRec&) override {}
void onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) override {}
- sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps&) override;
- bool onReadPixels(const SkPixmap&, int x, int y) override;
+ void drawDevice(SkBaseDevice*, const SkSamplingOptions&, const SkPaint&) override {}
+ void drawSpecial(SkSpecialImage*, const SkMatrix& localToDevice,
+ const SkSamplingOptions&, const SkPaint&) override {}
+
+ sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override { return nullptr; }
+ sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override { return nullptr; }
+ sk_sp<SkSpecialImage> snapSpecial(const SkIRect& subset, bool forceCopy = false) override {
+ return nullptr;
+ }
private:
Device(sk_sp<SurfaceDrawContext>);
diff --git a/experimental/graphite/src/DrawCommandList.h b/experimental/graphite/src/DrawList.h
similarity index 99%
rename from experimental/graphite/src/DrawCommandList.h
rename to experimental/graphite/src/DrawList.h
index d09edeb..16cbd03 100644
--- a/experimental/graphite/src/DrawCommandList.h
+++ b/experimental/graphite/src/DrawList.h
@@ -54,7 +54,7 @@
* it to. Draw-specific simplification, style application, and advanced clipping should be handled
* at a higher layer.
*/
-class DrawCommandList {
+class DrawList {
public:
// TBD: Do we always need the inverse deviceToLocal matrix? If not the entire matrix, do we need
// some other matrix-dependent value (e.g. scale factor) frequently? Since the localToDevice
diff --git a/experimental/graphite/src/RenderPassTask.cpp b/experimental/graphite/src/RenderPassTask.cpp
new file mode 100644
index 0000000..ca906b4
--- /dev/null
+++ b/experimental/graphite/src/RenderPassTask.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "experimental/graphite/src/RenderPassTask.h"
+
+#include "experimental/graphite/src/DrawList.h"
+
+namespace skgpu {
+
+sk_sp<RenderPassTask> RenderPassTask::Make(sk_sp<Task> prior, std::unique_ptr<DrawList> cmds) {
+ (void) prior; // unused for now, might be newTask.addDependency(prior)?
+ return sk_sp<RenderPassTask>(new RenderPassTask(std::move(cmds)));
+}
+
+RenderPassTask::RenderPassTask(std::unique_ptr<DrawList> cmds) : fCmds(std::move(cmds)) {}
+RenderPassTask::~RenderPassTask() = default;
+
+} // namespace skgpu
diff --git a/experimental/graphite/src/RenderPassTask.h b/experimental/graphite/src/RenderPassTask.h
new file mode 100644
index 0000000..7904138
--- /dev/null
+++ b/experimental/graphite/src/RenderPassTask.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skgpu_RenderPassTask_DEFINED
+#define skgpu_RenderPassTask_DEFINED
+
+#include "experimental/graphite/src/Task.h"
+
+namespace skgpu {
+
+class DrawList;
+
+/**
+ * RenderPassTask handles preparing and recording DrawLists into a single render pass within a
+ * command buffer. If the backend supports subpasses, and the DrawLists/surfaces are compatible, a
+ * RenderPassTask can execute multiple DrawLists across different surfaces as subpasses nested
+ * within a single render pass. If there is no such support, a RenderPassTask is one-to-one with a
+ * "render pass" to specific surface.
+ */
+class RenderPassTask final : public Task {
+public:
+ // TODO: 'prior' isn't actually used yet but is here to represent the dependency between a
+ // series of RenderPassTasks.
+ // TODO: RenderPassTask also needs to know the surface the commads are sent to (might not need
+ // to be explicit if there's a render pass list that collects {draw lists + surface}).
+ static sk_sp<RenderPassTask> Make(sk_sp<Task> prior, std::unique_ptr<DrawList> cmds);
+
+ ~RenderPassTask() override;
+
+private:
+ RenderPassTask(std::unique_ptr<DrawList> cmds);
+
+ // TODO: Seems very likely that the RenderPassTask will store an optimized/immutable
+ // representation derived from a DrawList and not directly a DrawList.
+ std::unique_ptr<DrawList> fCmds;
+};
+
+} // namespace skgpu
+
+#endif // skgpu_RenderPassTask_DEFINED
diff --git a/experimental/graphite/src/SDCTask.cpp b/experimental/graphite/src/SDCTask.cpp
deleted file mode 100644
index 95a19a0..0000000
--- a/experimental/graphite/src/SDCTask.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "experimental/graphite/src/SDCTask.h"
-
-namespace skgpu {
-
-sk_sp<SDCTask> SDCTask::Make() {
- return sk_sp<SDCTask>(new SDCTask());
-}
-
-SDCTask::SDCTask() {}
-SDCTask::~SDCTask() {}
-
-} // namespace skgpu
diff --git a/experimental/graphite/src/SDCTask.h b/experimental/graphite/src/SDCTask.h
deleted file mode 100644
index 35a0429..0000000
--- a/experimental/graphite/src/SDCTask.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef skgpu_SDCTask_DEFINED
-#define skgpu_SDCTask_DEFINED
-
-#include "experimental/graphite/src/Task.h"
-
-namespace skgpu {
-
-class SDCTask final : public Task {
-public:
- static sk_sp<SDCTask> Make();
-
- ~SDCTask() override;
-
-private:
- SDCTask();
-};
-
-} // namespace skgpu
-
-#endif // skgpu_SDCTask_DEFINED
diff --git a/experimental/graphite/src/SurfaceDrawContext.cpp b/experimental/graphite/src/SurfaceDrawContext.cpp
index bfb7a93..e10e517 100644
--- a/experimental/graphite/src/SurfaceDrawContext.cpp
+++ b/experimental/graphite/src/SurfaceDrawContext.cpp
@@ -7,24 +7,56 @@
#include "experimental/graphite/src/SurfaceDrawContext.h"
-#include "experimental/graphite/src/SDCTask.h"
+#include "experimental/graphite/src/DrawList.h"
+#include "experimental/graphite/src/RenderPassTask.h"
namespace skgpu {
sk_sp<SurfaceDrawContext> SurfaceDrawContext::Make(const SkImageInfo& ii) {
- sk_sp<SDCTask> task = SDCTask::Make();
- if (!task) {
- return nullptr;
- }
-
- return sk_sp<SurfaceDrawContext>(new SurfaceDrawContext(ii, std::move(task)));
+ return sk_sp<SurfaceDrawContext>(new SurfaceDrawContext(ii));
}
-SurfaceDrawContext::SurfaceDrawContext(const SkImageInfo& ii, sk_sp<SDCTask> task)
- : fImageInfo(ii)
- , fTask(std::move(task)) {
+SurfaceDrawContext::SurfaceDrawContext(const SkImageInfo& ii)
+ : fImageInfo(ii)
+ , fPendingDraws(std::make_unique<DrawList>())
+ , fTail(nullptr) {
+ // TBD - Will probably want DrawLists (and its internal commands) to come from an arena
+ // that the SDC manages.
}
-SurfaceDrawContext::~SurfaceDrawContext() {}
+SurfaceDrawContext::~SurfaceDrawContext() {
+ // If the SDC is destroyed and there are pending commands, they won't be drawn. Maybe that's ok
+ // but for now consider it a bug for not calling snapDrawTask().
+ SkASSERT(fPendingDraws->count() == 0);
+}
+
+void SurfaceDrawContext::fillPath(const SkM44& localToDevice,
+ const SkPath& path,
+ const SkIRect& scissor,
+ uint16_t sortZ,
+ uint16_t testZ,
+ const PaintParams* paint) {
+ fPendingDraws->fillPath(localToDevice, path, scissor, sortZ, testZ, paint);
+}
+
+void SurfaceDrawContext::strokePath(const SkM44& localToDevice,
+ const SkPath& path,
+ const StrokeParams& stroke,
+ const SkIRect& scissor,
+ uint16_t sortZ,
+ uint16_t testZ,
+ const PaintParams* paint) {
+ fPendingDraws->strokePath(localToDevice, path, stroke, scissor, sortZ, testZ, paint);
+}
+
+void SurfaceDrawContext::snapDrawPass(const BoundsManager* occlusionCuller) {
+ // TODO: actually sort, cull, and merge the DL for the DrawPass
+ (void) occlusionCuller;
+ // TODO: eventually the RenderPassTask will be handled later since it might take over
+ // multiple DrawPasses from the SDC.
+ auto task = RenderPassTask::Make(std::move(fTail), std::move(fPendingDraws));
+ fTail = task;
+ fPendingDraws = std::make_unique<DrawList>();
+}
} // namespace skgpu
diff --git a/experimental/graphite/src/SurfaceDrawContext.h b/experimental/graphite/src/SurfaceDrawContext.h
index f1160af..c0f7a7c 100644
--- a/experimental/graphite/src/SurfaceDrawContext.h
+++ b/experimental/graphite/src/SurfaceDrawContext.h
@@ -11,10 +11,22 @@
#include "include/core/SkImageInfo.h"
#include "include/core/SkRefCnt.h"
+class SkPath;
+class SkM44;
+
namespace skgpu {
-class SDCTask;
+class BoundsManager;
+class DrawList;
+class Task;
+struct PaintParams;
+struct StrokeParams;
+
+/**
+ * SurfaceDrawContext records draw commands into a specific Surface, via a general task graph
+ * representing GPU work and their inter-dependencies.
+ */
class SurfaceDrawContext final : public SkRefCnt {
public:
static sk_sp<SurfaceDrawContext> Make(const SkImageInfo&);
@@ -23,14 +35,56 @@
const SkImageInfo& imageInfo() { return fImageInfo; }
-private:
- SurfaceDrawContext(const SkImageInfo&, sk_sp<SDCTask>);
+ // TODO: need color/depth clearing functions (so DCL will probably need those too)
- SkImageInfo fImageInfo;
- sk_sp<SDCTask> fTask;
+ void fillPath(const SkM44& localToDevice,
+ const SkPath& path,
+ const SkIRect& scissor,
+ uint16_t sortZ,
+ uint16_t testZ,
+ const PaintParams* paint);
+
+ void strokePath(const SkM44& localToDevice,
+ const SkPath& path,
+ const StrokeParams& stroke,
+ const SkIRect& scissor,
+ uint16_t sortZ,
+ uint16_t testZ,
+ const PaintParams* paint);
+
+ // 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
+ // appended DrawPasses from a child SDC. A new DrawList is started to record subsequent drawing
+ // operations.
+
+ // If 'occlusionCuller' is null, then culling is skipped when converting the DrawList into a
+ // DrawPass.
+ // TBD - should this also return the task so the caller can point to it with its own
+ // dependencies? Or will that be mostly automatic based on draws and proxy refs?
+ void snapDrawPass(const BoundsManager* occlusionCuller);
+
+private:
+ SurfaceDrawContext(const SkImageInfo&);
+
+ SkImageInfo fImageInfo;
+
+ // TODO: After discussing the possibility of sub renderpasses and memoryless saved layers,
+ // an SDC ought to hold on to a list of {DrawList, Proxy} that represent the pending subpasses.
+ // A saveLayer can be then be drawn by (1) a regular render pass, followed by a regular texture
+ // sample in the parent's SDC, (2) a sub pass where the layer's draw list targets a
+ // "memory-less" 2nd surface w/in a regular render pass for the parent, or (3) fully eliding the
+ // layer by appending it's draw list directly to the parent's draw list. This will need to be
+ // represented at this high API level because it requires decisions to be made by Device when
+ // creating the SkDevice for the new layer, and during the restore.
+ std::unique_ptr<DrawList> fPendingDraws;
+
+ // TBD - Does the SDC even need to hold on to its tail task? Or when it finalizes its current
+ // DCL into a DrawTask it can send that back to the Recorder as part of a larger task graph?
+ // The only question then would be how to track the dependencies of that DrawTask since it would
+ // depend on the prior DrawTask and the SDC's surface view.
+ sk_sp<Task> fTail;
};
} // namespace skgpu
#endif // skgpu_SurfaceDrawContext_DEFINED
-
diff --git a/gn/graphite.gni b/gn/graphite.gni
index a88c529..305a7cd 100644
--- a/gn/graphite.gni
+++ b/gn/graphite.gni
@@ -20,15 +20,15 @@
"$_src/Context.cpp",
"$_src/Device.cpp",
"$_src/Device.h",
- "$_src/DrawCommandList.h",
+ "$_src/DrawList.h",
"$_src/Gpu.cpp",
"$_src/Gpu.h",
"$_src/Image_Graphite.cpp",
"$_src/Image_Graphite.h",
+ "$_src/RenderPassTask.cpp",
+ "$_src/RenderPassTask.h",
"$_src/ResourceProvider.cpp",
"$_src/ResourceProvider.h",
- "$_src/SDCTask.cpp",
- "$_src/SDCTask.h",
"$_src/SkStuff.cpp",
"$_src/SurfaceDrawContext.cpp",
"$_src/SurfaceDrawContext.h",