bool InstancedRendering::Batch::onCombineIfPossible(GrBatch* other, const GrCaps& caps) { Batch* that = static_cast<Batch*>(other); SkASSERT(fInstancedRendering == that->fInstancedRendering); SkASSERT(fTailDraw); SkASSERT(that->fTailDraw); if (!BatchInfo::CanCombine(fInfo, that->fInfo) || !GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), that->bounds(), caps)) { return false; } BatchInfo combinedInfo = fInfo | that->fInfo; if (!combinedInfo.isSimpleRects()) { // This threshold was chosen with the "shapes_mixed" bench on a MacBook with Intel graphics. // There seems to be a wide range where it doesn't matter if we combine or not. What matters // is that the itty bitty rects combine with other shapes and the giant ones don't. constexpr SkScalar kMaxPixelsToGeneralizeRects = 256 * 256; if (fInfo.isSimpleRects() && fPixelLoad > kMaxPixelsToGeneralizeRects) { return false; } if (that->fInfo.isSimpleRects() && that->fPixelLoad > kMaxPixelsToGeneralizeRects) { return false; } } this->joinBounds(*that); fInfo = combinedInfo; fPixelLoad += that->fPixelLoad; // Adopt the other batch's draws. fNumDraws += that->fNumDraws; fNumChangesInGeometry += that->fNumChangesInGeometry; if (fTailDraw->fGeometry != that->fHeadDraw->fGeometry) { ++fNumChangesInGeometry; } fTailDraw->fNext = that->fHeadDraw; fTailDraw = that->fTailDraw; that->fHeadDraw = that->fTailDraw = nullptr; return true; }
InstancedRendering::Batch* InstancedRendering::recordShape(ShapeType type, const SkRect& bounds, const SkMatrix& viewMatrix, GrColor color, const SkRect& localRect, bool antialias, const GrInstancedPipelineInfo& info, bool* useHWAA) { SkASSERT(State::kRecordingDraws == fState); if (info.fIsRenderingToFloat && !fCanRenderToFloat) { return nullptr; } AntialiasMode antialiasMode; if (!this->selectAntialiasMode(viewMatrix, antialias, info, useHWAA, &antialiasMode)) { return nullptr; } Batch* batch = this->createBatch(); batch->fInfo.fAntialiasMode = antialiasMode; batch->fInfo.fShapeTypes = GetShapeFlag(type); batch->fInfo.fCannotDiscard = !info.fCanDiscard; Instance& instance = batch->getSingleInstance(); instance.fInfo = (int)type << kShapeType_InfoBit; Batch::HasAABloat aaBloat = (antialiasMode == AntialiasMode::kCoverage) ? Batch::HasAABloat::kYes : Batch::HasAABloat::kNo; Batch::IsZeroArea zeroArea = (bounds.isEmpty()) ? Batch::IsZeroArea::kYes : Batch::IsZeroArea::kNo; // The instanced shape renderer draws rectangles of [-1, -1, +1, +1], so we find the matrix that // will map this rectangle to the same device coordinates as "viewMatrix * bounds". float sx = 0.5f * bounds.width(); float sy = 0.5f * bounds.height(); float tx = sx + bounds.fLeft; float ty = sy + bounds.fTop; if (!viewMatrix.hasPerspective()) { float* m = instance.fShapeMatrix2x3; m[0] = viewMatrix.getScaleX() * sx; m[1] = viewMatrix.getSkewX() * sy; m[2] = viewMatrix.getTranslateX() + viewMatrix.getScaleX() * tx + viewMatrix.getSkewX() * ty; m[3] = viewMatrix.getSkewY() * sx; m[4] = viewMatrix.getScaleY() * sy; m[5] = viewMatrix.getTranslateY() + viewMatrix.getSkewY() * tx + viewMatrix.getScaleY() * ty; // Since 'm' is a 2x3 matrix that maps the rect [-1, +1] into the shape's device-space quad, // it's quite simple to find the bounding rectangle: float devBoundsHalfWidth = fabsf(m[0]) + fabsf(m[1]); float devBoundsHalfHeight = fabsf(m[3]) + fabsf(m[4]); SkRect batchBounds; batchBounds.fLeft = m[2] - devBoundsHalfWidth; batchBounds.fRight = m[2] + devBoundsHalfWidth; batchBounds.fTop = m[5] - devBoundsHalfHeight; batchBounds.fBottom = m[5] + devBoundsHalfHeight; batch->setBounds(batchBounds, aaBloat, zeroArea); // TODO: Is this worth the CPU overhead? batch->fInfo.fNonSquare = fabsf(devBoundsHalfHeight - devBoundsHalfWidth) > 0.5f || // Early out. fabs(m[0] * m[3] + m[1] * m[4]) > 1e-3f || // Skew? fabs(m[0] * m[0] + m[1] * m[1] - m[3] * m[3] - m[4] * m[4]) > 1e-2f; // Diff. lengths? } else { SkMatrix shapeMatrix(viewMatrix); shapeMatrix.preTranslate(tx, ty); shapeMatrix.preScale(sx, sy); instance.fInfo |= kPerspective_InfoFlag; float* m = instance.fShapeMatrix2x3; m[0] = SkScalarToFloat(shapeMatrix.getScaleX()); m[1] = SkScalarToFloat(shapeMatrix.getSkewX()); m[2] = SkScalarToFloat(shapeMatrix.getTranslateX()); m[3] = SkScalarToFloat(shapeMatrix.getSkewY()); m[4] = SkScalarToFloat(shapeMatrix.getScaleY()); m[5] = SkScalarToFloat(shapeMatrix.getTranslateY()); // Send the perspective column as a param. batch->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1], shapeMatrix[SkMatrix::kMPersp2]); batch->fInfo.fHasPerspective = true; batch->setBounds(bounds, aaBloat, zeroArea); batch->fInfo.fNonSquare = true; } instance.fColor = color; const float* rectAsFloats = localRect.asScalars(); // Ensure SkScalar == float. memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float)); batch->fPixelLoad = batch->bounds().height() * batch->bounds().width(); return batch; }