blob: 97e2c7a3d2b8e380451d9bb583590a2a9520c129 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrGrStrokePatchBuilder_DEFINED
#define GrGrStrokePatchBuilder_DEFINED
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
#include "include/private/SkTArray.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "src/gpu/tessellate/GrStrokeTessellateShader.h"
class SkStrokeRec;
// This is an RAII class that expands strokes into tessellation patches for consumption by
// GrStrokeTessellateShader. The provided GrMeshDrawOp::Target must not be used externally for the
// entire lifetime of this class. e.g.:
//
// void onPrepare(GrOpFlushState* target) {
// GrStrokePatchBuilder builder(target, &fMyPatchChunks, scale, count); // Locks target.
// for (...) {
// builder.addPath(path, stroke);
// }
// }
// ... target can now be used normally again.
// ... fMyPatchChunks now contains chunks that can be drawn during onExecute.
class GrStrokePatchBuilder {
public:
// We generate patch buffers in chunks. Normally there will only be one chunk, but in rare cases
// the first can run out of space if too many cubics needed to be subdivided.
struct PatchChunk {
sk_sp<const GrBuffer> fPatchBuffer;
int fPatchCount = 0;
int fBasePatch;
};
// Stores raw pointers to the provided target and patchChunkArray, which this class will use and
// push to as addPath is called. The caller is responsible to bind and draw each chunk that gets
// pushed to the array. (See GrStrokeTessellateShader.)
//
// All points are multiplied by 'matrixScale' before being written to the GPU buffer.
GrStrokePatchBuilder(GrMeshDrawOp::Target* target, SkTArray<PatchChunk>* patchChunkArray,
float matrixScale, int totalCombinedVerbCnt)
: fTarget(target)
, fPatchChunkArray(patchChunkArray)
, fMaxTessellationSegments(target->caps().shaderCaps()->maxTessellationSegments())
, fLinearizationIntolerance(matrixScale *
GrTessellationPathRenderer::kLinearizationIntolerance) {
// Pre-allocate at least enough vertex space for one stroke in 3 to split, and for 8 caps.
int strokePreallocCount = totalCombinedVerbCnt * 4/3;
int capPreallocCount = 8;
int joinPreallocCount = strokePreallocCount + capPreallocCount;
this->allocPatchChunkAtLeast(strokePreallocCount + capPreallocCount + joinPreallocCount);
}
// "Releases" the target to be used externally again by putting back any unused pre-allocated
// vertices.
~GrStrokePatchBuilder() {
fTarget->putBackVertices(fCurrChunkPatchCapacity - fPatchChunkArray->back().fPatchCount,
sizeof(GrStrokeTessellateShader::Patch));
}
void addPath(const SkPath&, const SkStrokeRec&);
private:
void allocPatchChunkAtLeast(int minPatchAllocCount);
GrStrokeTessellateShader::Patch* reservePatch();
void writeCubicSegment(float prevJoinType, const SkPoint pts[4], float cubicType);
void writeJoin(float joinType, const SkPoint& prevControlPoint, const SkPoint& anchorPoint,
const SkPoint& nextControlPoint);
void writeSquareCap(const SkPoint& endPoint, const SkPoint& controlPoint);
void writeCaps(SkPaint::Cap);
void moveTo(const SkPoint&);
void lineTo(float prevJoinType, const SkPoint&, const SkPoint&);
void quadraticTo(float prevJoinType, const SkPoint[3], int maxDepth = -1);
void cubicTo(float prevJoinType, const SkPoint[4], int maxDepth = -1, bool mightInflect = true);
void close(SkPaint::Cap);
// These are raw pointers whose lifetimes are controlled outside this class.
GrMeshDrawOp::Target* const fTarget;
SkTArray<PatchChunk>* const fPatchChunkArray;
const int fMaxTessellationSegments;
// GrTessellationPathRenderer::kIntolerance adjusted for the matrix scale.
const float fLinearizationIntolerance;
// Variables related to the patch chunk that we are currently filling.
int fCurrChunkPatchCapacity;
int fCurrChunkMinPatchAllocCount;
GrStrokeTessellateShader::Patch* fCurrChunkPatchData;
// Variables related to the path that we are currently iterating.
float fCurrStrokeRadius;
float fCurrStrokeJoinType; // See GrStrokeTessellateShader for join type definitions.
float fNumRadialSegmentsPerRad;
// These values contain worst-case numbers of parametric segments our hardware can support for
// the current stroke radius, in the event that there are also enough radial segments to rotate
// 180 and 360 degrees respectively. These are used for "quick accepts" that allow us to send
// almost all curves directly to the hardware without having to chop or think any further.
float fMaxParametricSegments180;
float fMaxParametricSegments360;
// Variables related to the specific contour that we are currently iterating.
bool fHasPreviousSegment = false;
SkPoint fCurrContourStartPoint;
SkPoint fCurrContourFirstControlPoint;
SkPoint fLastControlPoint;
SkPoint fCurrentPoint;
};
#endif