| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "GrDrawPathBatch.h" | |
| #include "GrRenderTargetPriv.h" | |
| static void pre_translate_transform_values(const float* xforms, | |
| GrPathRendering::PathTransformType type, int count, | |
| SkScalar x, SkScalar y, float* dst); | |
| void GrDrawPathBatchBase::onPrepare(GrBatchFlushState*) { | |
| const GrRenderTargetPriv& rtPriv = this->pipeline()->getRenderTarget()->renderTargetPriv(); | |
| fStencilPassSettings.reset(GrPathRendering::GetStencilPassSettings(fFillType), | |
| this->pipeline()->hasStencilClip(), rtPriv.numStencilBits()); | |
| } | |
| SkString GrDrawPathBatch::dumpInfo() const { | |
| SkString string; | |
| string.printf("PATH: 0x%p", fPath.get()); | |
| string.append(INHERITED::dumpInfo()); | |
| return string; | |
| } | |
| void GrDrawPathBatch::onDraw(GrBatchFlushState* state) { | |
| GrProgramDesc desc; | |
| SkAutoTUnref<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(), | |
| this->overrides(), | |
| this->viewMatrix())); | |
| state->gpu()->pathRendering()->drawPath(*this->pipeline(), *pathProc, | |
| this->stencilPassSettings(), fPath.get()); | |
| } | |
| SkString GrDrawPathRangeBatch::dumpInfo() const { | |
| SkString string; | |
| string.printf("RANGE: 0x%p COUNTS: [", fPathRange.get()); | |
| for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) { | |
| string.appendf("%d, ", iter.get()->fInstanceData->count()); | |
| } | |
| string.remove(string.size() - 2, 2); | |
| string.append("]"); | |
| string.append(INHERITED::dumpInfo()); | |
| return string; | |
| } | |
| GrDrawPathRangeBatch::GrDrawPathRangeBatch(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, | |
| SkScalar y, GrColor color, | |
| GrPathRendering::FillType fill, GrPathRange* range, | |
| const InstanceData* instanceData, const SkRect& bounds) | |
| : INHERITED(ClassID(), viewMatrix, color, fill) | |
| , fPathRange(range) | |
| , fTotalPathCount(instanceData->count()) | |
| , fScale(scale) { | |
| fDraws.addToHead()->set(instanceData, x, y); | |
| this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); | |
| } | |
| bool GrDrawPathRangeBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { | |
| GrDrawPathRangeBatch* that = t->cast<GrDrawPathRangeBatch>(); | |
| if (this->fPathRange.get() != that->fPathRange.get() || | |
| this->transformType() != that->transformType() || | |
| this->fScale != that->fScale || | |
| this->color() != that->color() || | |
| !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { | |
| return false; | |
| } | |
| if (!GrPipeline::AreEqual(*this->pipeline(), *that->pipeline(), false)) { | |
| return false; | |
| } | |
| switch (fDraws.head()->fInstanceData->transformType()) { | |
| case GrPathRendering::kNone_PathTransformType: | |
| if (this->fDraws.head()->fX != that->fDraws.head()->fX || | |
| this->fDraws.head()->fY != that->fDraws.head()->fY) { | |
| return false; | |
| } | |
| break; | |
| case GrPathRendering::kTranslateX_PathTransformType: | |
| if (this->fDraws.head()->fY != that->fDraws.head()->fY) { | |
| return false; | |
| } | |
| break; | |
| case GrPathRendering::kTranslateY_PathTransformType: | |
| if (this->fDraws.head()->fX != that->fDraws.head()->fX) { | |
| return false; | |
| } | |
| break; | |
| default: break; | |
| } | |
| // TODO: Check some other things here. (winding, opaque, pathProc color, vm, ...) | |
| // Try to combine this call with the previous DrawPaths. We do this by stenciling all the | |
| // paths together and then covering them in a single pass. This is not equivalent to two | |
| // separate draw calls, so we can only do it if there is no blending (no overlap would also | |
| // work). Note that it's also possible for overlapping paths to cancel each other's winding | |
| // numbers, and we only partially account for this by not allowing even/odd paths to be | |
| // combined. (Glyphs in the same font tend to wind the same direction so it works out OK.) | |
| if (GrPathRendering::kWinding_FillType != this->fillType() || | |
| GrPathRendering::kWinding_FillType != that->fillType() || | |
| this->overrides().willColorBlendWithDst()) { | |
| return false; | |
| } | |
| SkASSERT(!that->overrides().willColorBlendWithDst()); | |
| fTotalPathCount += that->fTotalPathCount; | |
| while (Draw* head = that->fDraws.head()) { | |
| Draw* draw = fDraws.addToTail(); | |
| draw->fInstanceData.reset(head->fInstanceData.release()); | |
| draw->fX = head->fX; | |
| draw->fY = head->fY; | |
| that->fDraws.popHead(); | |
| } | |
| this->joinBounds(*that); | |
| return true; | |
| } | |
| void GrDrawPathRangeBatch::onDraw(GrBatchFlushState* state) { | |
| const Draw& head = *fDraws.head(); | |
| SkMatrix drawMatrix(this->viewMatrix()); | |
| drawMatrix.preScale(fScale, fScale); | |
| drawMatrix.preTranslate(head.fX, head.fY); | |
| SkMatrix localMatrix; | |
| localMatrix.setScale(fScale, fScale); | |
| localMatrix.preTranslate(head.fX, head.fY); | |
| SkAutoTUnref<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(), | |
| this->overrides(), | |
| drawMatrix, | |
| localMatrix)); | |
| if (fDraws.count() == 1) { | |
| const InstanceData& instances = *head.fInstanceData; | |
| state->gpu()->pathRendering()->drawPaths(*this->pipeline(), | |
| *pathProc, | |
| this->stencilPassSettings(), | |
| fPathRange.get(), | |
| instances.indices(), | |
| GrPathRange::kU16_PathIndexType, | |
| instances.transformValues(), | |
| instances.transformType(), | |
| instances.count()); | |
| } else { | |
| int floatsPerTransform = GrPathRendering::PathTransformSize(this->transformType()); | |
| SkAutoSTMalloc<4096, float> transformStorage(floatsPerTransform * fTotalPathCount); | |
| SkAutoSTMalloc<2048, uint16_t> indexStorage(fTotalPathCount); | |
| int idx = 0; | |
| for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) { | |
| const Draw& draw = *iter.get(); | |
| const InstanceData& instances = *draw.fInstanceData; | |
| memcpy(&indexStorage[idx], instances.indices(), instances.count() * sizeof(uint16_t)); | |
| pre_translate_transform_values(instances.transformValues(), this->transformType(), | |
| instances.count(), | |
| draw.fX - head.fX, draw.fY - head.fY, | |
| &transformStorage[floatsPerTransform * idx]); | |
| idx += instances.count(); | |
| // TODO: Support mismatched transform types if we start using more types other than 2D. | |
| SkASSERT(instances.transformType() == this->transformType()); | |
| } | |
| SkASSERT(idx == fTotalPathCount); | |
| state->gpu()->pathRendering()->drawPaths(*this->pipeline(), | |
| *pathProc, | |
| this->stencilPassSettings(), | |
| fPathRange.get(), | |
| indexStorage, | |
| GrPathRange::kU16_PathIndexType, | |
| transformStorage, | |
| this->transformType(), | |
| fTotalPathCount); | |
| } | |
| } | |
| inline void pre_translate_transform_values(const float* xforms, | |
| GrPathRendering::PathTransformType type, int count, | |
| SkScalar x, SkScalar y, float* dst) { | |
| if (0 == x && 0 == y) { | |
| memcpy(dst, xforms, count * GrPathRendering::PathTransformSize(type) * sizeof(float)); | |
| return; | |
| } | |
| switch (type) { | |
| case GrPathRendering::kNone_PathTransformType: | |
| SkFAIL("Cannot pre-translate kNone_PathTransformType."); | |
| break; | |
| case GrPathRendering::kTranslateX_PathTransformType: | |
| SkASSERT(0 == y); | |
| for (int i = 0; i < count; i++) { | |
| dst[i] = xforms[i] + x; | |
| } | |
| break; | |
| case GrPathRendering::kTranslateY_PathTransformType: | |
| SkASSERT(0 == x); | |
| for (int i = 0; i < count; i++) { | |
| dst[i] = xforms[i] + y; | |
| } | |
| break; | |
| case GrPathRendering::kTranslate_PathTransformType: | |
| for (int i = 0; i < 2 * count; i += 2) { | |
| dst[i] = xforms[i] + x; | |
| dst[i + 1] = xforms[i + 1] + y; | |
| } | |
| break; | |
| case GrPathRendering::kAffine_PathTransformType: | |
| for (int i = 0; i < 6 * count; i += 6) { | |
| dst[i] = xforms[i]; | |
| dst[i + 1] = xforms[i + 1]; | |
| dst[i + 2] = xforms[i] * x + xforms[i + 1] * y + xforms[i + 2]; | |
| dst[i + 3] = xforms[i + 3]; | |
| dst[i + 4] = xforms[i + 4]; | |
| dst[i + 5] = xforms[i + 3] * x + xforms[i + 4] * y + xforms[i + 5]; | |
| } | |
| break; | |
| default: | |
| SkFAIL("Unknown transform type."); | |
| break; | |
| } | |
| } |