blob: 02dbdc301d23650045aa6c81c798f3d5501cca78 [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 QuadPerEdgeAA_DEFINED
#define QuadPerEdgeAA_DEFINED
#include "include/core/SkPoint.h"
#include "include/core/SkPoint3.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/gpu/BufferWriter.h"
#include "src/gpu/ganesh/GrBuffer.h"
#include "src/gpu/ganesh/GrColor.h"
#include "src/gpu/ganesh/GrGeometryProcessor.h"
#include "src/gpu/ganesh/GrSamplerState.h"
#include "src/gpu/ganesh/geometry/GrQuad.h"
#include "src/gpu/ganesh/geometry/GrQuadUtils.h"
#include "src/gpu/ganesh/ops/TextureOp.h"
class GrCaps;
class GrColorSpaceXform;
class GrMeshDrawTarget;
struct GrShaderCaps;
struct VertexWriter;
namespace skgpu::ganesh::QuadPerEdgeAA {
using Saturate = skgpu::ganesh::TextureOp::Saturate;
enum class CoverageMode { kNone, kWithPosition, kWithColor };
enum class Subset : bool { kNo = false, kYes = true };
enum class ColorType { kNone, kByte, kFloat, kLast = kFloat };
static const int kColorTypeCount = static_cast<int>(ColorType::kLast) + 1;
enum class IndexBufferOption {
kPictureFramed, // geometrically AA'd -> 8 verts/quad + an index buffer
kIndexedRects, // non-AA'd but indexed -> 4 verts/quad + an index buffer
kTriStrips, // non-AA'd -> 4 verts/quad but no index buffer
kLast = kTriStrips
};
static const int kIndexBufferOptionCount = static_cast<int>(IndexBufferOption::kLast) + 1;
IndexBufferOption CalcIndexBufferOption(GrAAType aa, int numQuads);
// Gets the minimum ColorType that can represent a color.
ColorType MinColorType(SkPMColor4f);
// Specifies the vertex configuration for an op that renders per-edge AA quads. The vertex
// order (when enabled) is device position, color, local position, subset, aa edge equations.
// This order matches the constructor argument order of VertexSpec and is the order that
// GPAttributes maintains. If hasLocalCoords is false, then the local quad type can be ignored.
struct VertexSpec {
public:
VertexSpec()
: fDeviceQuadType(0) // kAxisAligned
, fLocalQuadType(0) // kAxisAligned
, fIndexBufferOption(0) // kPictureFramed
, fHasLocalCoords(false)
, fColorType(0) // kNone
, fHasSubset(false)
, fUsesCoverageAA(false)
, fCompatibleWithCoverageAsAlpha(false)
, fRequiresGeometrySubset(false) {}
VertexSpec(GrQuad::Type deviceQuadType,
ColorType colorType,
GrQuad::Type localQuadType,
bool hasLocalCoords,
Subset subset,
GrAAType aa,
bool coverageAsAlpha,
IndexBufferOption indexBufferOption)
: fDeviceQuadType(static_cast<unsigned>(deviceQuadType))
, fLocalQuadType(static_cast<unsigned>(localQuadType))
, fIndexBufferOption(static_cast<unsigned>(indexBufferOption))
, fHasLocalCoords(hasLocalCoords)
, fColorType(static_cast<unsigned>(colorType))
, fHasSubset(static_cast<unsigned>(subset))
, fUsesCoverageAA(aa == GrAAType::kCoverage)
, fCompatibleWithCoverageAsAlpha(coverageAsAlpha)
, fRequiresGeometrySubset(aa == GrAAType::kCoverage &&
deviceQuadType > GrQuad::Type::kRectilinear) {}
GrQuad::Type deviceQuadType() const { return static_cast<GrQuad::Type>(fDeviceQuadType); }
GrQuad::Type localQuadType() const { return static_cast<GrQuad::Type>(fLocalQuadType); }
IndexBufferOption indexBufferOption() const {
return static_cast<IndexBufferOption>(fIndexBufferOption);
}
bool hasLocalCoords() const { return fHasLocalCoords; }
ColorType colorType() const { return static_cast<ColorType>(fColorType); }
bool hasVertexColors() const { return ColorType::kNone != this->colorType(); }
bool hasSubset() const { return fHasSubset; }
bool usesCoverageAA() const { return fUsesCoverageAA; }
bool compatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; }
bool requiresGeometrySubset() const { return fRequiresGeometrySubset; }
// Will always be 2 or 3
int deviceDimensionality() const;
// Will always be 0 if hasLocalCoords is false, otherwise will be 2 or 3
int localDimensionality() const;
int verticesPerQuad() const { return fUsesCoverageAA ? 8 : 4; }
CoverageMode coverageMode() const;
size_t vertexSize() const;
bool needsIndexBuffer() const {
return this->indexBufferOption() != IndexBufferOption::kTriStrips;
}
GrPrimitiveType primitiveType() const {
switch (this->indexBufferOption()) {
case IndexBufferOption::kPictureFramed:
return GrPrimitiveType::kTriangles;
case IndexBufferOption::kIndexedRects:
return GrPrimitiveType::kTriangles;
case IndexBufferOption::kTriStrips:
return GrPrimitiveType::kTriangleStrip;
}
SkUNREACHABLE;
}
private:
static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type doesn't fit in 2 bits");
static_assert(kColorTypeCount <= 4, "Color doesn't fit in 2 bits");
static_assert(kIndexBufferOptionCount <= 4, "IndexBufferOption doesn't fit in 2 bits");
unsigned fDeviceQuadType : 2;
unsigned fLocalQuadType : 2;
unsigned fIndexBufferOption : 2;
unsigned fHasLocalCoords : 1;
unsigned fColorType : 2;
unsigned fHasSubset : 1;
unsigned fUsesCoverageAA : 1;
unsigned fCompatibleWithCoverageAsAlpha : 1;
// The geometry subset serves to clip off pixels touched by quads with sharp corners that
// would otherwise exceed the miter limit for the AA-outset geometry.
unsigned fRequiresGeometrySubset : 1;
};
// A Tessellator is responsible for processing a series of device+local GrQuads into a VBO,
// as specified by a VertexSpec. This vertex data can then be processed by a GP created with
// MakeProcessor and/or MakeTexturedProcessor.
class Tessellator {
public:
explicit Tessellator(const VertexSpec& spec, char* vertices);
// Calculates (as needed) inset and outset geometry for anti-aliasing, and appends all
// necessary position and vertex attributes required by this Tessellator's VertexSpec into
// the 'vertices' the Tessellator was called with. The insetting and outsetting may
// damage the provided GrQuads (as this is intended to work with GrQuadBuffer::Iter).
// 'localQuad' can be null if the VertexSpec does not use local coords.
void append(GrQuad* deviceQuad, GrQuad* localQuad,
const SkPMColor4f& color, const SkRect& uvSubset, GrQuadAAFlags aaFlags);
SkDEBUGCODE(skgpu::BufferWriter::Mark vertexMark() const { return fVertexWriter.mark(); })
private:
// VertexSpec defines many unique ways to write vertex attributes, which can be handled
// generically by branching per-quad based on the VertexSpec. However, there are several
// specs that appear in the wild far more frequently, so they use explicit WriteQuadProcs
// that have no branches.
typedef void (*WriteQuadProc)(VertexWriter* vertices, const VertexSpec& spec,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomSubset, const SkRect& texSubset);
static WriteQuadProc GetWriteQuadProc(const VertexSpec& spec);
GrQuadUtils::TessellationHelper fAAHelper;
VertexSpec fVertexSpec;
VertexWriter fVertexWriter;
WriteQuadProc fWriteProc;
};
GrGeometryProcessor* MakeProcessor(SkArenaAlloc*, const VertexSpec&);
GrGeometryProcessor* MakeTexturedProcessor(SkArenaAlloc*,
const VertexSpec&,
const GrShaderCaps&,
const GrBackendFormat&,
GrSamplerState,
const skgpu::Swizzle&,
sk_sp<GrColorSpaceXform> textureColorSpaceXform,
Saturate);
// This method will return the correct index buffer for the specified indexBufferOption.
// It will, correctly, return nullptr if the indexBufferOption is kTriStrips.
sk_sp<const GrBuffer> GetIndexBuffer(GrMeshDrawTarget*, IndexBufferOption);
// What is the maximum number of quads allowed for the specified indexBuffer option?
int QuadLimit(IndexBufferOption);
// This method will issue the draw call on the provided GrOpsRenderPass, as specified by the
// indexing method in vertexSpec. It is up to the calling code to allocate, fill in, and bind a
// vertex buffer, and to acquire and bind the correct index buffer (if needed) with
// GrPrimitiveRestart::kNo.
//
// @param runningQuadCount the number of quads already stored in 'vertexBuffer' and
// 'indexBuffer' e.g., different GrMeshes have already been placed in
// the buffers to allow dynamic state changes.
// @param quadCount the number of quads that will be drawn by the provided 'mesh'.
// A subsequent ConfigureMesh call would the use
// 'runningQuadCount' + 'quadCount' for its new 'runningQuadCount'.
void IssueDraw(const GrCaps&, GrOpsRenderPass*, const VertexSpec&, int runningQuadCount,
int quadCount, int maxVerts, int absVertBufferOffset);
} // namespace skgpu::ganesh::QuadPerEdgeAA
#endif // QuadPerEdgeAA_DEFINED