[graphite] Consolidate ordering constraints into DrawOrder type
Bug: skia:12466 Change-Id: I734c3a9595948dfb74f8ec72684183fe743bfefb Reviewed-on: https://skia-review.googlesource.com/c/skia/+/462883 Auto-Submit: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Jim Van Verth <jvanverth@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
649e3c2f98
commit
04bff8ba6e
@ -45,12 +45,6 @@ enum class Protected : bool {
|
||||
kYes = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* An ordinal number that allows draw commands to be re-ordered so long as when they are executed,
|
||||
* the read/writes to the color|depth|stencil attachments respect the original painter's order.
|
||||
*/
|
||||
using CompressedPaintersOrder = uint16_t;
|
||||
|
||||
} // namespace skgpu
|
||||
|
||||
#endif // skgpu_GraphiteTypes_DEFINED
|
||||
|
@ -50,8 +50,8 @@ Device::Device(sk_sp<Recorder> recorder, sk_sp<DrawContext> dc)
|
||||
, fRecorder(std::move(recorder))
|
||||
, fDC(std::move(dc))
|
||||
, fColorDepthBoundsManager(std::make_unique<NaiveBoundsManager>())
|
||||
, fMaxPaintOrder(0)
|
||||
, fMaxZ(0)
|
||||
, fCurrentDepth(DrawOrder::kClearDepth)
|
||||
, fMaxStencilIndex(DrawOrder::kUnassigned)
|
||||
, fDrawsOverlap(false) {
|
||||
SkASSERT(SkToBool(fDC) && SkToBool(fRecorder));
|
||||
}
|
||||
@ -151,6 +151,8 @@ void Device::drawShape(const Shape& shape,
|
||||
Transform localToDevice(this->localToDevice44());
|
||||
if (!localToDevice.valid()) {
|
||||
// If the transform is not invertible or not finite then drawing isn't well defined.
|
||||
// TBD: This warning should go through the general purpose graphite logging system
|
||||
SkDebugf("[graphite] WARNING - Skipping draw with non-invertible/non-finite transform.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -190,61 +192,72 @@ void Device::drawShape(const Shape& shape,
|
||||
SkASSERT(!SkToBool(paint.getPathEffect()) || (flags & DrawFlags::kIgnorePathEffect));
|
||||
SkASSERT(!SkToBool(paint.getMaskFilter()) || (flags & DrawFlags::kIgnoreMaskFilter));
|
||||
|
||||
uint16_t drawZ = fMaxZ + 1;
|
||||
ClipResult clip = this->applyClipToDraw(localToDevice, shape, style, drawZ);
|
||||
DrawOrder order(fCurrentDepth.next());
|
||||
ClipResult clip = this->applyClipToDraw(localToDevice, shape, style, order.depth());
|
||||
if (clip.fDrawBounds.isEmptyNegativeOrNaN()) {
|
||||
// Clipped out, so don't record anything
|
||||
return;
|
||||
}
|
||||
|
||||
// A draw's order always depends on the clips that must be drawn before it
|
||||
order.dependsOnPaintersOrder(clip.fOrder);
|
||||
|
||||
auto blendMode = paint.asBlendMode();
|
||||
PaintParams shading{paint.getColor4f(),
|
||||
blendMode.has_value() ? *blendMode : SkBlendMode::kSrcOver,
|
||||
paint.refShader()};
|
||||
|
||||
// If a draw is opaque, its ordering only depends on clipping; otherwise it must be drawn after
|
||||
// the most recent draw it intersects with, in order to blend correctly.
|
||||
// If a draw is not opaque, it must be drawn after the most recent draw it intersects with in
|
||||
// order to blend correctly. We always query the most recent draw (even when opaque) because it
|
||||
// also lets Device easily track whether or not there are any overlapping draws.
|
||||
const bool opaque = is_opaque(shading);
|
||||
CompressedPaintersOrder prevDrawOrder =
|
||||
CompressedPaintersOrder prevDraw =
|
||||
fColorDepthBoundsManager->getMostRecentDraw(clip.fDrawBounds);
|
||||
CompressedPaintersOrder drawOrder =
|
||||
1 + (opaque ? clip.fOrder : std::max(clip.fOrder, prevDrawOrder));
|
||||
if (!opaque) {
|
||||
order.dependsOnPaintersOrder(prevDraw);
|
||||
}
|
||||
|
||||
SkStrokeRec::Style styleType = style.getStyle();
|
||||
const SkStrokeRec::Style styleType = style.getStyle();
|
||||
if (styleType == SkStrokeRec::kStroke_Style ||
|
||||
styleType == SkStrokeRec::kHairline_Style ||
|
||||
styleType == SkStrokeRec::kStrokeAndFill_Style) {
|
||||
// TODO: If DC supports stroked primitives, Device could choose one of those based on shape
|
||||
StrokeParams stroke(style.getWidth(), style.getMiter(), style.getJoin(), style.getCap());
|
||||
fDC->strokePath(localToDevice, shape, stroke, clip.fScissor,
|
||||
drawOrder, drawZ, &shading);
|
||||
fDC->strokePath(localToDevice, shape, stroke, clip.fScissor, order, &shading);
|
||||
}
|
||||
if (styleType == SkStrokeRec::kFill_Style ||
|
||||
styleType == SkStrokeRec::kStrokeAndFill_Style) {
|
||||
// TODO: If DC supports filled primitives, Device could choose one of those based on shape
|
||||
if (shape.convex()) {
|
||||
fDC->fillConvexPath(localToDevice, shape, clip.fScissor,
|
||||
drawOrder, drawZ, &shading);
|
||||
} else {
|
||||
// FIXME must determine stencil order; a separate bounds manager? a rect tree? defer?
|
||||
fDC->stencilAndFillPath(localToDevice, shape, clip.fScissor,
|
||||
drawOrder, 0, drawZ, &shading);
|
||||
}
|
||||
|
||||
// TODO: Route all filled shapes to stencil-and-cover for the sprint; convex will draw
|
||||
// correctly but uses an unnecessary stencil step.
|
||||
// if (shape.convex()) {
|
||||
// fDC->fillConvexPath(localToDevice, shape, clip.fScissor, order, &shading);
|
||||
// } else {
|
||||
order.dependsOnStencil(fMaxStencilIndex.next());
|
||||
fDC->stencilAndFillPath(localToDevice, shape, clip.fScissor, order, &shading);
|
||||
// }
|
||||
}
|
||||
|
||||
// Record the painters order and Z used for this draw
|
||||
// Record the painters order and depth used for this draw
|
||||
const bool fullyOpaque = opaque && shape.isRect() &&
|
||||
localToDevice.type() <= Transform::Type::kRectStaysRect;
|
||||
fColorDepthBoundsManager->recordDraw(shape.bounds(), drawOrder, drawZ, fullyOpaque);
|
||||
fMaxPaintOrder = std::max(fMaxPaintOrder, drawOrder);
|
||||
fMaxZ = drawZ;
|
||||
fDrawsOverlap |= (prevDrawOrder != 0);
|
||||
fColorDepthBoundsManager->recordDraw(shape.bounds(),
|
||||
order.paintOrder(),
|
||||
order.depth(),
|
||||
fullyOpaque);
|
||||
|
||||
fCurrentDepth = order.depth();
|
||||
if (order.stencilIndex() != DrawOrder::kUnassigned) {
|
||||
fMaxStencilIndex = std::max(fMaxStencilIndex, order.stencilIndex());
|
||||
}
|
||||
fDrawsOverlap |= (prevDraw != DrawOrder::kNoIntersection);
|
||||
}
|
||||
|
||||
Device::ClipResult Device::applyClipToDraw(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkStrokeRec& style,
|
||||
uint16_t z) {
|
||||
PaintersDepth z) {
|
||||
SkIRect scissor = this->devClipBounds();
|
||||
|
||||
Rect drawBounds = shape.bounds();
|
||||
@ -262,11 +275,11 @@ Device::ClipResult Device::applyClipToDraw(const Transform& localToDevice,
|
||||
drawBounds.intersect(SkRect::Make(scissor));
|
||||
if (drawBounds.isEmptyNegativeOrNaN()) {
|
||||
// Trivially clipped out, so return now
|
||||
return {scissor, drawBounds, 0};
|
||||
return {scissor, drawBounds, DrawOrder::kNoIntersection};
|
||||
}
|
||||
|
||||
// TODO: iterate the clip stack and accumulate draw bounds into clip usage
|
||||
return {scissor, drawBounds, 0};
|
||||
return {scissor, drawBounds, DrawOrder::kNoIntersection};
|
||||
}
|
||||
|
||||
sk_sp<SkSpecialImage> Device::makeSpecial(const SkBitmap&) {
|
||||
|
@ -8,10 +8,10 @@
|
||||
#ifndef skgpu_Device_DEFINED
|
||||
#define skgpu_Device_DEFINED
|
||||
|
||||
#include "experimental/graphite/include/private/GraphiteTypesPriv.h"
|
||||
|
||||
#include "src/core/SkDevice.h"
|
||||
|
||||
#include "experimental/graphite/include/private/GraphiteTypesPriv.h"
|
||||
#include "experimental/graphite/src/DrawOrder.h"
|
||||
#include "experimental/graphite/src/geom/Rect.h"
|
||||
|
||||
class SkStrokeRec;
|
||||
@ -25,9 +25,6 @@ class Recorder;
|
||||
class Shape;
|
||||
class Transform;
|
||||
|
||||
struct PaintParams;
|
||||
struct StrokeParams;
|
||||
|
||||
class Device final : public SkBaseDevice {
|
||||
public:
|
||||
~Device() override;
|
||||
@ -155,7 +152,7 @@ private:
|
||||
//
|
||||
// This also records the draw's bounds to any clip elements that affect it so that they are
|
||||
// recorded when popped off the stack, or making an image snapshot of the Device.
|
||||
ClipResult applyClipToDraw(const Transform&, const Shape&, const SkStrokeRec&, uint16_t z);
|
||||
ClipResult applyClipToDraw(const Transform&, const Shape&, const SkStrokeRec&, PaintersDepth z);
|
||||
|
||||
sk_sp<Recorder> fRecorder;
|
||||
sk_sp<DrawContext> fDC;
|
||||
@ -163,11 +160,13 @@ private:
|
||||
// Tracks accumulated intersections for ordering dependent use of the color and depth attachment
|
||||
// (i.e. depth-based clipping, and transparent blending)
|
||||
std::unique_ptr<BoundsManager> fColorDepthBoundsManager;
|
||||
// The max recorded painters order sent to the DrawContext, needed to know how many available
|
||||
// values are left before overrunning 16-bit limit and forcing a reset.
|
||||
CompressedPaintersOrder fMaxPaintOrder;
|
||||
|
||||
// The max depth value sent to the DrawContext, incremented so each draw has a unique value.
|
||||
uint16_t fMaxZ;
|
||||
PaintersDepth fCurrentDepth;
|
||||
// TODO: Temporary way to assign stencil IDs for draws, but since each draw gets its own
|
||||
// value, it prevents the ability for draw steps to be re-arranged into blocks of stencil then
|
||||
// covers. However, it does ensure stenciling is correct until we wire up the intersection tree
|
||||
DisjointStencilIndex fMaxStencilIndex;
|
||||
|
||||
bool fDrawsOverlap;
|
||||
};
|
||||
|
@ -36,31 +36,26 @@ DrawContext::~DrawContext() {
|
||||
void DrawContext::stencilAndFillPath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
CompressedPaintersOrder stencilOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder order,
|
||||
const PaintParams* paint) {
|
||||
fPendingDraws->stencilAndFillPath(localToDevice, shape, scissor, colorDepthOrder, stencilOrder,
|
||||
depth, paint);
|
||||
fPendingDraws->stencilAndFillPath(localToDevice, shape, scissor, order,paint);
|
||||
}
|
||||
|
||||
void DrawContext::fillConvexPath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder order,
|
||||
const PaintParams* paint) {
|
||||
fPendingDraws->fillConvexPath(localToDevice, shape, scissor, colorDepthOrder, depth, paint);
|
||||
fPendingDraws->fillConvexPath(localToDevice, shape, scissor, order, paint);
|
||||
}
|
||||
|
||||
void DrawContext::strokePath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const StrokeParams& stroke,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder order,
|
||||
const PaintParams* paint) {
|
||||
fPendingDraws->strokePath(localToDevice, shape, stroke, scissor, colorDepthOrder, depth, paint);
|
||||
fPendingDraws->strokePath(localToDevice, shape, stroke, scissor, order, paint);
|
||||
}
|
||||
|
||||
void DrawContext::snapDrawPass(const BoundsManager* occlusionCuller) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
|
||||
#include "experimental/graphite/include/GraphiteTypes.h"
|
||||
#include "experimental/graphite/src/DrawOrder.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -45,24 +45,20 @@ public:
|
||||
void stencilAndFillPath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
CompressedPaintersOrder stencilOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder order,
|
||||
const PaintParams* paint);
|
||||
|
||||
void fillConvexPath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder order,
|
||||
const PaintParams* paint);
|
||||
|
||||
void strokePath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const StrokeParams& stroke,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder order,
|
||||
const PaintParams* paint);
|
||||
|
||||
// Ends the current DrawList being accumulated by the SDC, converting it into an optimized and
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkShader.h"
|
||||
|
||||
#include "experimental/graphite/include/GraphiteTypes.h"
|
||||
#include "experimental/graphite/src/DrawOrder.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@ -61,12 +61,6 @@ struct StrokeParams;
|
||||
*/
|
||||
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
|
||||
// transform from the Device changes at the same or slower rate as draw commands, it may make
|
||||
// sense for it to compute these dependent values and provide them here. Storing the scale
|
||||
// factor per draw command is low overhead, but unsure about storing 2 matrices per command.
|
||||
|
||||
// NOTE: All path rendering functions, e.g. [fill|stroke|...]Path() that take a Shape
|
||||
// draw using the same underlying techniques regardless of the shape's type. If a Shape has
|
||||
// a type matching a simpler primitive technique or coverage AA, the caller must explicitly
|
||||
@ -78,9 +72,7 @@ public:
|
||||
void stencilAndFillPath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkIRect& scissor, // TBD: expand this to one xformed rrect clip?
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
CompressedPaintersOrder stencilOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder ordering,
|
||||
const PaintParams* paint) {
|
||||
// TODO: Implement this and assert localToDevice.valid()
|
||||
}
|
||||
@ -88,8 +80,7 @@ public:
|
||||
void fillConvexPath(const Transform& localToDevice,
|
||||
const Shape& shape,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder ordering,
|
||||
const PaintParams* paint) {
|
||||
// TODO: Implement this and assert localToDevice.valid()
|
||||
}
|
||||
@ -98,8 +89,7 @@ public:
|
||||
const Shape& shape,
|
||||
const StrokeParams& stroke,
|
||||
const SkIRect& scissor,
|
||||
CompressedPaintersOrder colorDepthOrder,
|
||||
uint16_t depth,
|
||||
DrawOrder ordering,
|
||||
const PaintParams* paint) {
|
||||
// TODO: Implement this and assert localToDevice.valid()
|
||||
}
|
||||
@ -112,17 +102,6 @@ public:
|
||||
|
||||
int count() const { return 0; }
|
||||
|
||||
// TBD: Figure out preparation/flush APIs and/or how to iterate the draw commands. These will
|
||||
// be responsible for sorting by sort z and shading state, doing occlusion culling, and possibly
|
||||
// merging compatible, consecutive remaining commands. It can also easily track if there are
|
||||
// remaining depth-only draws or complex path draws that would trigger DMSAA. I[ml] can see this
|
||||
// all being internal to DrawCommandList, or being supported by accessors and iterators with the
|
||||
// rest of the logic stored in SDC. It is also unknown at this time how much conversion the
|
||||
// PaintParams will need to go through (vs. just building a key) in order to do state sorting.
|
||||
|
||||
// TBD: Any value in de-duplicating paint params/programs during accumulation or being able
|
||||
// to query the set of required programs for a given command list? Any time query or flush time?
|
||||
|
||||
private:
|
||||
// TODO: actually implement this, probably stl for now but will likely need something that
|
||||
// is allocation efficient. Should also explore having 1 vector of commands, vs. parallel
|
||||
|
163
experimental/graphite/src/DrawOrder.h
Normal file
163
experimental/graphite/src/DrawOrder.h
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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_DrawOrder_DEFINED
|
||||
#define skgpu_DrawOrder_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
namespace skgpu {
|
||||
|
||||
// Helper to encapsulate an unsigned number and enforce that it can only be used to create a
|
||||
// monotonic sequence. The template argument 'Sequence' is used to define different sequences
|
||||
// enforced by the compiler. For simplicity, and current needs within Graphite, it's assumed the
|
||||
// entire sequence can be indexed by uint16_t.
|
||||
template<typename Sequence>
|
||||
class MonotonicValue {
|
||||
public:
|
||||
static constexpr MonotonicValue First() { return 0; }
|
||||
static constexpr MonotonicValue Last() { return 0xffff; }
|
||||
|
||||
MonotonicValue() = default;
|
||||
MonotonicValue(const MonotonicValue& o) = default;
|
||||
|
||||
MonotonicValue& operator=(const MonotonicValue& o) = default;
|
||||
|
||||
bool operator< (MonotonicValue o) const { return fIndex < o.fIndex; }
|
||||
bool operator<=(MonotonicValue o) const { return fIndex <= o.fIndex; }
|
||||
|
||||
bool operator> (MonotonicValue o) const { return fIndex > o.fIndex; }
|
||||
bool operator>=(MonotonicValue o) const { return fIndex >= o.fIndex; }
|
||||
|
||||
bool operator==(MonotonicValue o) const { return fIndex == o.fIndex; }
|
||||
bool operator!=(MonotonicValue o) const { return fIndex != o.fIndex; }
|
||||
|
||||
uint16_t bits() const { return fIndex; }
|
||||
|
||||
// Get the next value in the sequence after this one
|
||||
MonotonicValue next() const { return fIndex + 1; }
|
||||
|
||||
private:
|
||||
constexpr MonotonicValue(uint16_t index) : fIndex(index) {}
|
||||
|
||||
uint16_t fIndex = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* CompressedPaintersOrder is an ordinal number that allows draw commands to be re-ordered so long
|
||||
* as when they are executed, the read/writes to the color|depth attachments respect the original
|
||||
* painter's order. Logical draws with the same CompressedPaintersOrder can be assumed to be
|
||||
* executed in any order, however that may have been determined (e.g. BoundsManager or relying on
|
||||
* a depth test during rasterization).
|
||||
*/
|
||||
struct CompressedPaintersOrderSequence {};
|
||||
using CompressedPaintersOrder = MonotonicValue<CompressedPaintersOrderSequence>;
|
||||
|
||||
/**
|
||||
* Each DisjointStencilIndex specifies an implicit set of non-overlapping draws. Assuming that two
|
||||
* draws have the same CompressedPaintersOrder and the same DisjointStencilIndex, their substeps
|
||||
* for multi-pass rendering (stencil-then-cover, etc.) can be intermingled with each other and
|
||||
* produce the same results as if each draw's substeps were executed in order before moving on to
|
||||
* the next draw's.
|
||||
*
|
||||
* Ordering within a set can be entirely arbitrary (i.e. all stencil steps can go before all cover
|
||||
* steps). Ordering between sets is also arbitrary since all draws share the same
|
||||
* CompressedPaintersOrder, so long as one set is entirely drawn before the next.
|
||||
*
|
||||
* Two draws that have different CompressedPaintersOrders but the same DisjointStencilIndex are
|
||||
* unrelated, they may or may not overlap. The painters order scopes the disjoint sets.
|
||||
*/
|
||||
struct DisjointStencilIndexSequence {};
|
||||
using DisjointStencilIndex = MonotonicValue<DisjointStencilIndexSequence>;
|
||||
|
||||
/**
|
||||
* Every draw has an associated depth value. The value is constant across the entire draw and is
|
||||
* not related to any varying Z coordinate induced by a 4x4 transform. The painter's depth is stored
|
||||
* in the depth attachment and the GREATER depth test is used to reject or accept pixels/samples
|
||||
* relative to what has already been rendered into the depth attachment. This allows draws that do
|
||||
* not depend on the previous color to be radically re-ordered relative to their original painter's
|
||||
* order while producing correct results.
|
||||
*/
|
||||
struct PaintersDepthSequence {};
|
||||
using PaintersDepth = MonotonicValue<PaintersDepthSequence>;
|
||||
|
||||
/**
|
||||
* DrawOrder aggregates the three separate sequences that Graphite uses to re-order draws and their
|
||||
* substeps as much as possible while preserving the painter's order semantics of the Skia API.
|
||||
*
|
||||
* To build the full DrawOrder for a draw, start with its assigned PaintersDepth (i.e. the original
|
||||
* painter's order of the draw call). From there, the DrawOrder can be updated to reflect
|
||||
* dependencies on previous draws, either from depth-only clip draws or because the draw is
|
||||
* transparent and must blend with the previous color values. Lastly, once the
|
||||
* CompressedPaintersOrder is finalized, the DrawOrder can be updated to reflect whether or not
|
||||
* the draw will involve the stencil buffer--and if so, specify the disjoint stencil set it
|
||||
* belongs to.
|
||||
*
|
||||
* The original and effective order that draws are executed in is defined by the PaintersDepth.
|
||||
* However, the actual execution order is defined by first the CompressedPaintersOrder and then
|
||||
* the DisjointStencilIndex. This means that draws with much higher depths can be executed earlier
|
||||
* if painter's order compression allows for it.
|
||||
*
|
||||
*
|
||||
*FIXME Integrate this?
|
||||
* This reduces to a vertex
|
||||
* coloring problem on the intersection graph formed by the commands and how their bounds
|
||||
* overlap, followed by ordering by pipeline description and uniform data. General vertex
|
||||
* coloring is NP-complete so DrawPass uses a greedy algorithm where the order it "colors" the
|
||||
* vertices is based on the ordering constraints for the color+depth buffer and optionally the
|
||||
* stencil buffer (stored in fColorDepthIndex and fStencilIndex respectively). skgpu::Device
|
||||
* determines the ordering on-the-fly by using BoundsManager to approximate intersections as
|
||||
* draw commands are recorded. It is possible to issue draws to Skia that produce pathologic
|
||||
* orderings using this method, but it strikes a reasonable balance between finding a near
|
||||
* optimal ordering that respects painter's order and is very efficient to compute.
|
||||
*
|
||||
*/
|
||||
class DrawOrder {
|
||||
public:
|
||||
// The first PaintersDepth is reserved for clearing the depth attachment; any draw using this
|
||||
// depth will always fail the depth test.
|
||||
static constexpr PaintersDepth kClearDepth = PaintersDepth::First();
|
||||
// The first CompressedPaintersOrder is reserved to indicate there is no previous draw that
|
||||
// must come before a draw.
|
||||
static constexpr CompressedPaintersOrder kNoIntersection = CompressedPaintersOrder::First();
|
||||
// The first DisjointStencilIndex is reserved to indicate an unassigned stencil set.
|
||||
static constexpr DisjointStencilIndex kUnassigned = DisjointStencilIndex::First();
|
||||
|
||||
explicit DrawOrder(PaintersDepth originalOrder)
|
||||
: fPaintOrder(kNoIntersection)
|
||||
, fStencilIndex(kUnassigned)
|
||||
, fDepth(originalOrder) {}
|
||||
|
||||
CompressedPaintersOrder paintOrder() const { return fPaintOrder; }
|
||||
DisjointStencilIndex stencilIndex() const { return fStencilIndex; }
|
||||
PaintersDepth depth() const { return fDepth; }
|
||||
|
||||
DrawOrder& dependsOnPaintersOrder(CompressedPaintersOrder prevDraw) {
|
||||
// A draw must be ordered after all previous draws that it depends on
|
||||
CompressedPaintersOrder next = prevDraw.next();
|
||||
if (fPaintOrder < next) {
|
||||
fPaintOrder = next;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
DrawOrder& dependsOnStencil(DisjointStencilIndex disjointSet) {
|
||||
// Stencil usage should only be set once
|
||||
SkASSERT(fStencilIndex == kUnassigned);
|
||||
fStencilIndex = disjointSet;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
CompressedPaintersOrder fPaintOrder;
|
||||
DisjointStencilIndex fStencilIndex;
|
||||
PaintersDepth fDepth;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
||||
#endif // skgpu_DrawOrder_DEFINED
|
@ -68,8 +68,8 @@ struct SortKey {
|
||||
int pipelineIndex,
|
||||
uint16_t geomDataHash,
|
||||
uint32_t shadingDataHash)
|
||||
: fPipelineKey{colorDepthOrder,
|
||||
stencilOrder,
|
||||
: fPipelineKey{colorDepthOrder.bits(),
|
||||
stencilOrder.bits(),
|
||||
static_cast<uint16_t>(drawStage),
|
||||
static_cast<uint32_t>(pipelineIndex)}
|
||||
, fDataHash{geomDataHash,
|
||||
@ -87,16 +87,6 @@ struct SortKey {
|
||||
|
||||
DrawStage stage() const { return static_cast<DrawStage>(fPipelineKey.fDrawStage); }
|
||||
|
||||
// Exposed for inspection, but generally the painters ordering isn't needed after sorting
|
||||
// since draws can be merged with different values as long as they have the same pipeline and
|
||||
// their sorted ordering is preserved within the pipeline.
|
||||
CompressedPaintersOrder colorDepthOrder() const {
|
||||
return static_cast<CompressedPaintersOrder>(fPipelineKey.fColorDepthOrder);
|
||||
}
|
||||
CompressedPaintersOrder stencilOrder() const {
|
||||
return static_cast<CompressedPaintersOrder>(fPipelineKey.fStencilOrder);
|
||||
}
|
||||
|
||||
// These are exposed to help optimize detecting when new uniforms need to be bound.
|
||||
// Differing hashes definitely represent different uniform bindings, but identical hashes
|
||||
// require a complete comparison.
|
||||
|
@ -8,7 +8,7 @@
|
||||
#ifndef skgpu_geom_BoundsManager_DEFINED
|
||||
#define skgpu_geom_BoundsManager_DEFINED
|
||||
|
||||
#include "experimental/graphite/include/GraphiteTypes.h"
|
||||
#include "experimental/graphite/src/DrawOrder.h"
|
||||
#include "experimental/graphite/src/geom/Rect.h"
|
||||
|
||||
#include "src/core/SkTBlockList.h"
|
||||
@ -34,9 +34,11 @@ public:
|
||||
|
||||
virtual CompressedPaintersOrder getMostRecentDraw(const Rect& bounds) const = 0;
|
||||
|
||||
virtual bool isOccluded(const Rect& bounds, uint16_t z) const = 0;
|
||||
virtual bool isOccluded(const Rect& bounds, PaintersDepth z) const = 0;
|
||||
|
||||
virtual void recordDraw(const Rect& bounds, CompressedPaintersOrder order, uint16_t z,
|
||||
virtual void recordDraw(const Rect& bounds,
|
||||
CompressedPaintersOrder order,
|
||||
PaintersDepth z,
|
||||
bool fullyOpaque=false) = 0;
|
||||
};
|
||||
|
||||
@ -53,17 +55,17 @@ public:
|
||||
return fLatestDraw;
|
||||
}
|
||||
|
||||
bool isOccluded(const Rect& bounds, uint16_t z) const override { return false; }
|
||||
bool isOccluded(const Rect& bounds, PaintersDepth z) const override { return false; }
|
||||
|
||||
void recordDraw(const Rect& bounds, CompressedPaintersOrder order, uint16_t z,
|
||||
void recordDraw(const Rect& bounds, CompressedPaintersOrder order, PaintersDepth z,
|
||||
bool fullyOpaque=false) override {
|
||||
if (order > fLatestDraw) {
|
||||
if (fLatestDraw < order) {
|
||||
fLatestDraw = order;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CompressedPaintersOrder fLatestDraw = 0;
|
||||
CompressedPaintersOrder fLatestDraw = CompressedPaintersOrder::First();
|
||||
};
|
||||
|
||||
// A BoundsManager that tracks every draw and can exactly determine all queries
|
||||
@ -73,26 +75,26 @@ public:
|
||||
~BruteForceBoundsManager() override {}
|
||||
|
||||
CompressedPaintersOrder getMostRecentDraw(const Rect& bounds) const override {
|
||||
CompressedPaintersOrder max = 0;
|
||||
CompressedPaintersOrder max = CompressedPaintersOrder::First();
|
||||
for (const Record& r : fRects.items()) {
|
||||
if (r.fOrder > max && r.fBounds.intersects(bounds)) {
|
||||
if (max < r.fOrder && r.fBounds.intersects(bounds)) {
|
||||
max = r.fOrder;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
bool isOccluded(const Rect& bounds, uint16_t z) const override {
|
||||
bool isOccluded(const Rect& bounds, PaintersDepth z) const override {
|
||||
// Iterate in reverse since the records were likely recorded in increasing Z
|
||||
for (const Record& r : fRects.ritems()) {
|
||||
if (r.fOpaque && r.fZ >= z && r.fBounds.contains(bounds)) {
|
||||
if (r.fOpaque && z < r.fZ && r.fBounds.contains(bounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void recordDraw(const Rect& bounds, CompressedPaintersOrder order, uint16_t z,
|
||||
void recordDraw(const Rect& bounds, CompressedPaintersOrder order, PaintersDepth z,
|
||||
bool fullyOpaque=false) override {
|
||||
fRects.push_back({bounds, order, z, fullyOpaque});
|
||||
}
|
||||
@ -101,7 +103,7 @@ private:
|
||||
struct Record {
|
||||
Rect fBounds;
|
||||
CompressedPaintersOrder fOrder;
|
||||
uint16_t fZ;
|
||||
PaintersDepth fZ;
|
||||
bool fOpaque;
|
||||
};
|
||||
|
||||
|
@ -33,6 +33,7 @@ skia_graphite_sources = [
|
||||
"$_src/DrawContext.cpp",
|
||||
"$_src/DrawContext.h",
|
||||
"$_src/DrawList.h",
|
||||
"$_src/DrawOrder.h",
|
||||
"$_src/DrawPass.cpp",
|
||||
"$_src/DrawPass.h",
|
||||
"$_src/Gpu.cpp",
|
||||
|
Loading…
Reference in New Issue
Block a user