/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrTInstanceBatch_DEFINED
#define GrTInstanceBatch_DEFINED
#include "GrVertexBatch.h"
#include "GrBatchFlushState.h"
/**
* GrTInstanceBatch is an optional template to help with writing batches
* To use this template, The 'Impl' must define the following statics:
* A Geometry struct
*
* static const int kVertsPerInstance
* static const int kIndicesPerInstance
*
* const char* Name()
*
* void InvariantOutputCoverage(GrInitInvariantOutput* out)
*
* void SetBounds(const Geometry& seedGeometry, SkRect* outBounds)
*
* void UpdateBoundsAfterAppend(const Geometry& lastGeometry, SkRect* currentBounds)
*
* bool CanCombine(const Geometry& mine, const Geometry& theirs,
* const GrXPOverridesForBatch&)
*
* const GrGeometryProcessor* CreateGP(const Geometry& seedGeometry,
* const GrXPOverridesForBatch& overrides)
*
* const GrIndexBuffer* GetIndexBuffer(GrResourceProvider*)
*
* Tesselate(intptr_t vertices, size_t vertexStride, const Geometry& geo,
* const GrXPOverridesForBatch& overrides)
*/
template <typename Impl>
class GrTInstanceBatch : public GrVertexBatch {
public:
DEFINE_BATCH_CLASS_ID
typedef typename Impl::Geometry Geometry;
static GrTInstanceBatch* Create() { return new GrTInstanceBatch; }
const char* name() const override { return Impl::Name(); }
SkString dumpInfo() const override {
SkString str;
for (int i = 0; i < fGeoData.count(); ++i) {
str.append(Impl::DumpInfo(fGeoData[i], i));
}
str.append(INHERITED::dumpInfo());
return str;
}
void computePipelineOptimizations(GrInitInvariantOutput* color,
GrInitInvariantOutput* coverage,
GrBatchToXPOverrides* overrides) const override {
// When this is called on a batch, there is only one geometry bundle
color->setKnownFourComponents(fGeoData[0].fColor);
Impl::InitInvariantOutputCoverage(coverage);
}
void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
fOverrides = overrides;
}
SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
// After seeding, the client should call init() so the Batch can initialize itself
void init() {
const Geometry& geo = fGeoData[0];
Impl::SetBounds(geo, &fBounds);
}
void updateBoundsAfterAppend() {
const Geometry& geo = fGeoData.back();
Impl::UpdateBoundsAfterAppend(geo, &fBounds);
}
private:
GrTInstanceBatch() : INHERITED(ClassID()) {}
void onPrepareDraws(Target* target) const override {
SkAutoTUnref<const GrGeometryProcessor> gp(Impl::CreateGP(this->seedGeometry(),
fOverrides));
if (!gp) {
SkDebugf("Couldn't create GrGeometryProcessor\n");
return;
}
target->initDraw(gp, this->pipeline());
size_t vertexStride = gp->getVertexStride();
int instanceCount = fGeoData.count();
SkAutoTUnref<const GrIndexBuffer> indexBuffer(
Impl::GetIndexBuffer(target->resourceProvider()));
InstancedHelper helper;
void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
indexBuffer, Impl::kVertsPerInstance,
Impl::kIndicesPerInstance, instanceCount);
if (!vertices || !indexBuffer) {
SkDebugf("Could not allocate vertices\n");
return;
}
for (int i = 0; i < instanceCount; i++) {
intptr_t verts = reinterpret_cast<intptr_t>(vertices) +
i * Impl::kVertsPerInstance * vertexStride;
Impl::Tesselate(verts, vertexStride, fGeoData[i], fOverrides);
}
helper.recordDraw(target);
}
const Geometry& seedGeometry() const { return fGeoData[0]; }
bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
GrTInstanceBatch* that = t->cast<GrTInstanceBatch>();
if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
that->bounds(), caps)) {
return false;
}
if (!Impl::CanCombine(this->seedGeometry(), that->seedGeometry(), fOverrides)) {
return false;
}
// In the event of two batches, one who can tweak, one who cannot, we just fall back to
// not tweaking
if (fOverrides.canTweakAlphaForCoverage() && !that->fOverrides.canTweakAlphaForCoverage()) {
fOverrides = that->fOverrides;
}
fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
this->joinBounds(that->bounds());
return true;
}
GrXPOverridesForBatch fOverrides;
SkSTArray<1, Geometry, true> fGeoData;
typedef GrVertexBatch INHERITED;
};
#endif