Batch multiple single NVPR draw paths to instanced draws
Batch multiple single NVPR draw paths to instanced draws. A draw path batch can be combined to other batch if the batches do not overlap and have same draw characteristics. Join the batches in linked list and flatten the list to a path list during draw time. Replace GrPathRendering::drawPath with GrPathRendering::drawPaths. Perf changes ARM, Shield TV device, Tegra X1 GPU: desk_chalkboard.skp_1 21.5ms -> 17.8ms 0.83x desk_mapsvg.skp_1 7.49ms -> 6.18ms 0.82x Others results are more volatile. GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1908433002 Review URL: https://codereview.chromium.org/1908433002
This commit is contained in:
parent
c4ce72fc15
commit
3c33c389e9
@ -36,6 +36,18 @@ public:
|
||||
const SkRect& getBounds() const { return fBounds; }
|
||||
|
||||
GrPathRendering::FillType getFillType() const { return fFillType; }
|
||||
|
||||
/**
|
||||
* Returns true if a path can be drawn in the same draw paths operation as the other
|
||||
* path. Should return true only when the condition holds transitively with all other paths in
|
||||
* the same group.
|
||||
* E.g.
|
||||
* canCombineDrawPathBatchWith(a) AND canCombineDrawPathBatchWith(b)
|
||||
* canCombineDrawPathBatchWith(a) AND canCombineDrawPathBatchWith(c)
|
||||
* implies
|
||||
* canCombineDrawPathBatchWith(b) AND canCombineDrawPathBatchWith(c)
|
||||
*/
|
||||
virtual bool canCombineDrawPathBatchWith(const GrPath& other) const = 0;
|
||||
#ifdef SK_DEBUG
|
||||
bool isEqualTo(const SkPath& path, const GrStrokeInfo& stroke) const;
|
||||
#endif
|
||||
|
@ -153,17 +153,6 @@ public:
|
||||
this->onStencilPath(args, path);
|
||||
}
|
||||
|
||||
void drawPath(const GrPipeline& pipeline,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrStencilSettings& stencil,
|
||||
const GrPath* path) {
|
||||
fGpu->handleDirtyContext();
|
||||
if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*fGpu->caps())) {
|
||||
fGpu->xferBarrier(pipeline.getRenderTarget(), barrierType);
|
||||
}
|
||||
this->onDrawPath(pipeline, primProc, stencil, path);
|
||||
}
|
||||
|
||||
void drawPaths(const GrPipeline& pipeline,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrStencilSettings& stencil,
|
||||
@ -184,15 +173,23 @@ public:
|
||||
transformValues, transformType, count);
|
||||
}
|
||||
|
||||
void drawPaths(const GrPipeline& pipeline,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrStencilSettings& stencil,
|
||||
const GrPath* const* paths,
|
||||
int count) {
|
||||
fGpu->handleDirtyContext();
|
||||
if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*fGpu->caps())) {
|
||||
fGpu->xferBarrier(pipeline.getRenderTarget(), barrierType);
|
||||
}
|
||||
this->onDrawPaths(pipeline, primProc, stencil, paths, count);
|
||||
}
|
||||
|
||||
protected:
|
||||
GrPathRendering(GrGpu* gpu)
|
||||
: fGpu(gpu) {
|
||||
}
|
||||
virtual void onStencilPath(const StencilPathArgs&, const GrPath*) = 0;
|
||||
virtual void onDrawPath(const GrPipeline&,
|
||||
const GrPrimitiveProcessor&,
|
||||
const GrStencilSettings&,
|
||||
const GrPath*) = 0;
|
||||
virtual void onDrawPaths(const GrPipeline&,
|
||||
const GrPrimitiveProcessor&,
|
||||
const GrStencilSettings&,
|
||||
@ -202,6 +199,11 @@ protected:
|
||||
const float transformValues[],
|
||||
PathTransformType,
|
||||
int count) = 0;
|
||||
virtual void onDrawPaths(const GrPipeline&,
|
||||
const GrPrimitiveProcessor&,
|
||||
const GrStencilSettings&,
|
||||
const GrPath* const*,
|
||||
int count) = 0;
|
||||
|
||||
GrGpu* fGpu;
|
||||
private:
|
||||
|
@ -28,7 +28,8 @@ class GrRenderTarget;
|
||||
* Batches are created when GrContext processes a draw call. Batches of the same subclass may be
|
||||
* merged using combineIfPossible. When two batches merge, one takes on the union of the data
|
||||
* and the other is left empty. The merged batch becomes responsible for drawing the data from both
|
||||
* the original batches.
|
||||
* the original batches. The merged patch may ref the other batch, if this is more efficient than
|
||||
* moving the data from the other batch to the merged batch.
|
||||
*
|
||||
* If there are any possible optimizations which might require knowing more about the full state of
|
||||
* the draw, ie whether or not the GrBatch is allowed to tweak alpha for coverage, then this
|
||||
|
@ -13,18 +13,84 @@ static void pre_translate_transform_values(const float* xforms,
|
||||
|
||||
SkString GrDrawPathBatch::dumpInfo() const {
|
||||
SkString string;
|
||||
string.printf("PATH: 0x%p", fPath.get());
|
||||
string.printf("Color: 0x%08x Fill: %x Path count: %d Paths: ", this->color(), this->fillType(),
|
||||
fTotalPathCount);
|
||||
const GrDrawPathBatch* batch = this;
|
||||
do {
|
||||
string.appendf("0x%p", batch->fPath.get());
|
||||
batch = batch->fNext.get();
|
||||
} while (batch);
|
||||
string.append("\n");
|
||||
return string;
|
||||
}
|
||||
|
||||
void GrDrawPathBatch::onDraw(GrBatchFlushState* state) {
|
||||
GrProgramDesc desc;
|
||||
bool GrDrawPathBatch::ListBoundsIntersects(const GrDrawPathBatch* a, const GrDrawPathBatch* b) {
|
||||
if (!SkRect::Intersects(a->fBounds, b->fBounds)) {
|
||||
return false;
|
||||
}
|
||||
if (!a->fNext && !b->fNext) {
|
||||
return true;
|
||||
}
|
||||
const GrDrawPathBatch* firstA = a;
|
||||
do {
|
||||
do {
|
||||
if (SkRect::Intersects(a->fPathBounds, b->fPathBounds)) {
|
||||
return true;
|
||||
}
|
||||
a = a->fNext.get();
|
||||
} while (a);
|
||||
a = firstA;
|
||||
b = b->fNext.get();
|
||||
} while (b);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GrDrawPathBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
|
||||
GrDrawPathBatch* that = t->cast<GrDrawPathBatch>();
|
||||
if (this->color() != that->color() ||
|
||||
!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
|
||||
return false;
|
||||
}
|
||||
if (!GrPipeline::AreEqual(*this->pipeline(), *that->pipeline(), false)) {
|
||||
return false;
|
||||
}
|
||||
if (that->fillType() != this->fillType() ||
|
||||
this->stencilSettings() != that->stencilSettings()) {
|
||||
return false;
|
||||
}
|
||||
if (ListBoundsIntersects(this, that)) {
|
||||
return false;
|
||||
}
|
||||
if (!fPath.get()->canCombineDrawPathBatchWith(*that->fPath.get())) {
|
||||
return false;
|
||||
}
|
||||
SkASSERT(!*fLastSlot);
|
||||
fLastSlot->reset(SkRef(that));
|
||||
fLastSlot = that->fLastSlot;
|
||||
fTotalPathCount += that->fTotalPathCount;
|
||||
this->joinBounds(that->fBounds);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrDrawPathBatch::onDraw(GrBatchFlushState* state) {
|
||||
SkAutoTUnref<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(),
|
||||
this->overrides(),
|
||||
this->viewMatrix()));
|
||||
state->gpu()->pathRendering()->drawPath(*this->pipeline(), *pathProc, this->stencilSettings(),
|
||||
fPath.get());
|
||||
if (fTotalPathCount > 1) {
|
||||
SkAutoSTMalloc<32, const GrPath*> paths(fTotalPathCount);
|
||||
GrDrawPathBatch* batch = this;
|
||||
int i = 0;
|
||||
do {
|
||||
paths[i++] = batch->fPath.get();
|
||||
batch = batch->fNext.get();
|
||||
} while (batch);
|
||||
state->gpu()->pathRendering()->drawPaths(*this->pipeline(), *pathProc,
|
||||
this->stencilSettings(), paths, fTotalPathCount);
|
||||
} else {
|
||||
const GrPath* path = fPath.get();
|
||||
state->gpu()->pathRendering()->drawPaths(*this->pipeline(), *pathProc,
|
||||
this->stencilSettings(), &path, 1);
|
||||
}
|
||||
}
|
||||
|
||||
SkString GrDrawPathRangeBatch::dumpInfo() const {
|
||||
|
@ -76,19 +76,26 @@ private:
|
||||
GrDrawPathBatch(const SkMatrix& viewMatrix, GrColor color, GrPathRendering::FillType fill,
|
||||
const GrPath* path)
|
||||
: INHERITED(ClassID(), viewMatrix, color, fill)
|
||||
, fPath(path) {
|
||||
, fPath(path)
|
||||
, fNext(nullptr)
|
||||
, fLastSlot(&fNext)
|
||||
, fTotalPathCount(1) {
|
||||
fBounds = path->getBounds();
|
||||
viewMatrix.mapRect(&fBounds);
|
||||
fPathBounds = fBounds;
|
||||
}
|
||||
|
||||
bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { return false; }
|
||||
static bool ListBoundsIntersects(const GrDrawPathBatch* a, const GrDrawPathBatch* b);
|
||||
bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override;
|
||||
|
||||
void onPrepare(GrBatchFlushState*) override {}
|
||||
|
||||
void onDraw(GrBatchFlushState* state) override;
|
||||
|
||||
GrPendingIOResource<const GrPath, kRead_GrIOType> fPath;
|
||||
|
||||
SkRect fPathBounds;
|
||||
sk_sp<GrDrawPathBatch> fNext; // Batch union is made with a linked list of batch nodes.
|
||||
sk_sp<GrDrawPathBatch>* fLastSlot; // Points to the fNext of the last batch in the batch list.
|
||||
int fTotalPathCount;
|
||||
typedef GrDrawPathBatchBase INHERITED;
|
||||
};
|
||||
|
||||
|
@ -333,6 +333,12 @@ GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& o
|
||||
this->registerWithCache(SkBudgeted::kYes);
|
||||
}
|
||||
|
||||
bool GrGLPath::canCombineDrawPathBatchWith(const GrPath& o) const {
|
||||
const GrGLPath* other = static_cast<const GrGLPath*>(&o);
|
||||
return fShouldStroke == other->fShouldStroke &&
|
||||
fShouldFill == other->fShouldFill;
|
||||
}
|
||||
|
||||
void GrGLPath::onRelease() {
|
||||
if (0 != fPathID) {
|
||||
static_cast<GrGLGpu*>(this->getGpu())->glPathRendering()->deletePaths(fPathID, 1);
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
|
||||
bool shouldStroke() const { return fShouldStroke; }
|
||||
bool shouldFill() const { return fShouldFill; }
|
||||
bool canCombineDrawPathBatchWith(const GrPath& other) const override;
|
||||
protected:
|
||||
void onRelease() override;
|
||||
void onAbandon() override;
|
||||
|
@ -145,37 +145,10 @@ void GrGLPathRendering::onStencilPath(const StencilPathArgs& args, const GrPath*
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLPathRendering::onDrawPath(const GrPipeline& pipeline,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrStencilSettings& stencil,
|
||||
const GrPath* path) {
|
||||
if (!this->gpu()->flushGLState(pipeline, primProc)) {
|
||||
return;
|
||||
}
|
||||
const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
|
||||
|
||||
this->flushPathStencilSettings(stencil);
|
||||
SkASSERT(!fHWPathStencilSettings.isTwoSided());
|
||||
|
||||
GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
|
||||
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
|
||||
|
||||
if (glPath->shouldStroke()) {
|
||||
if (glPath->shouldFill()) {
|
||||
GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
|
||||
}
|
||||
GL_CALL(StencilThenCoverStrokePath(glPath->pathID(), 0xffff, writeMask,
|
||||
GR_GL_BOUNDING_BOX));
|
||||
} else {
|
||||
GL_CALL(StencilThenCoverFillPath(glPath->pathID(), fillMode, writeMask,
|
||||
GR_GL_BOUNDING_BOX));
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLPathRendering::onDrawPaths(const GrPipeline& pipeline,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrStencilSettings& stencil, const GrPathRange* pathRange,
|
||||
const GrStencilSettings& stencil,
|
||||
const GrPathRange* pathRange,
|
||||
const void* indices, PathIndexType indexType,
|
||||
const float transformValues[], PathTransformType transformType,
|
||||
int count) {
|
||||
@ -215,6 +188,60 @@ void GrGLPathRendering::onDrawPaths(const GrPipeline& pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLPathRendering::onDrawPaths(const GrPipeline& pipeline,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrStencilSettings& stencil,
|
||||
const GrPath* const* paths,
|
||||
int count) {
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
if (!this->gpu()->flushGLState(pipeline, primProc)) {
|
||||
return;
|
||||
}
|
||||
this->flushPathStencilSettings(stencil);
|
||||
SkASSERT(!fHWPathStencilSettings.isTwoSided());
|
||||
|
||||
GrGLenum fillMode =
|
||||
gr_stencil_op_to_gl_path_rendering_fill_mode(
|
||||
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLint writeMask =
|
||||
fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
|
||||
const GrGLPath* path = static_cast<const GrGLPath*>(paths[0]);
|
||||
if (count > 1) {
|
||||
SkAutoSTMalloc<32, GrGLuint> indexStorage(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
indexStorage[i] = static_cast<const GrGLPath*>(paths[i])->pathID();
|
||||
}
|
||||
if (path->shouldStroke()) {
|
||||
if (path->shouldFill()) {
|
||||
GL_CALL(StencilFillPathInstanced(
|
||||
count, GR_GL_UNSIGNED_INT, indexStorage, 0,
|
||||
fillMode, writeMask, GR_GL_NONE, nullptr));
|
||||
}
|
||||
GL_CALL(StencilThenCoverStrokePathInstanced(
|
||||
count, GR_GL_UNSIGNED_INT, indexStorage, 0, 0xffff, writeMask,
|
||||
GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES, GR_GL_NONE, nullptr));
|
||||
} else {
|
||||
GL_CALL(StencilThenCoverFillPathInstanced(
|
||||
count, GR_GL_UNSIGNED_INT, indexStorage, 0,
|
||||
fillMode, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
|
||||
GR_GL_NONE, nullptr));
|
||||
}
|
||||
} else {
|
||||
if (path->shouldStroke()) {
|
||||
if (path->shouldFill()) {
|
||||
GL_CALL(StencilFillPath(path->pathID(), fillMode, writeMask));
|
||||
}
|
||||
GL_CALL(StencilThenCoverStrokePath(path->pathID(), 0xffff, writeMask,
|
||||
GR_GL_BOUNDING_BOX));
|
||||
} else {
|
||||
GL_CALL(StencilThenCoverFillPath(path->pathID(), fillMode, writeMask,
|
||||
GR_GL_BOUNDING_BOX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLPathRendering::setProgramPathFragmentInputTransform(GrGLuint program, GrGLint location,
|
||||
GrGLenum genMode, GrGLint components,
|
||||
const SkMatrix& matrix) {
|
||||
|
@ -65,10 +65,6 @@ public:
|
||||
|
||||
protected:
|
||||
void onStencilPath(const StencilPathArgs&, const GrPath*) override;
|
||||
void onDrawPath(const GrPipeline&,
|
||||
const GrPrimitiveProcessor&,
|
||||
const GrStencilSettings&,
|
||||
const GrPath*) override;
|
||||
void onDrawPaths(const GrPipeline&,
|
||||
const GrPrimitiveProcessor&,
|
||||
const GrStencilSettings&,
|
||||
@ -78,6 +74,11 @@ protected:
|
||||
const float transformValues[],
|
||||
PathTransformType,
|
||||
int count) override;
|
||||
void onDrawPaths(const GrPipeline&,
|
||||
const GrPrimitiveProcessor&,
|
||||
const GrStencilSettings&,
|
||||
const GrPath* const*,
|
||||
int count) override;
|
||||
private:
|
||||
/**
|
||||
* Mark certain functionality as not supported.
|
||||
|
Loading…
Reference in New Issue
Block a user