skia2/gm/beziereffects.cpp
Brian Osman f04fb3cacb Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.

The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.

Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.

Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.

Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-13 00:57:17 +00:00

475 lines
17 KiB
C++

/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// This test only works with the GPU backend.
#include "gm.h"
#include "sk_tool_utils.h"
#include "GrContext.h"
#include "GrMemoryPool.h"
#include "GrOpFlushState.h"
#include "GrPathUtils.h"
#include "GrRenderTargetContextPriv.h"
#include "SkColorPriv.h"
#include "SkGeometry.h"
#include "SkPoint3.h"
#include "SkPointPriv.h"
#include "effects/GrBezierEffect.h"
#include "ops/GrMeshDrawOp.h"
namespace skiagm {
class BezierTestOp : public GrMeshDrawOp {
public:
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
auto analysis = fProcessorSet.finalize(fColor, GrProcessorAnalysisCoverage::kSingleChannel,
clip, false, caps, &fColor);
return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
}
void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
fProcessorSet.visitProxies(func);
}
protected:
BezierTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect, const SkPMColor4f& color,
int32_t classID)
: INHERITED(classID)
, fRect(rect)
, fColor(color)
, fGeometryProcessor(std::move(gp))
, fProcessorSet(SkBlendMode::kSrc) {
this->setBounds(rect, HasAABloat::kYes, IsZeroArea::kNo);
}
Target::PipelineAndFixedDynamicState makePipeline(Target* target) {
return target->makePipeline(0, std::move(fProcessorSet), target->detachAppliedClip());
}
sk_sp<const GrGeometryProcessor> gp() const { return fGeometryProcessor; }
const SkRect& rect() const { return fRect; }
const SkPMColor4f& color() const { return fColor; }
private:
SkRect fRect;
SkPMColor4f fColor;
sk_sp<const GrGeometryProcessor> fGeometryProcessor;
GrProcessorSet fProcessorSet;
typedef GrMeshDrawOp INHERITED;
};
/**
* This GM directly exercises effects that draw Bezier curves in the GPU backend.
*/
class BezierConicTestOp : public BezierTestOp {
public:
DEFINE_OP_CLASS_ID
const char* name() const override { return "BezierConicTestOp"; }
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
sk_sp<const GrGeometryProcessor> gp,
const SkRect& rect,
const SkPMColor4f& color,
const SkMatrix& klm) {
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
return pool->allocate<BezierConicTestOp>(std::move(gp), rect, color, klm);
}
private:
friend class ::GrOpMemoryPool; // for ctor
BezierConicTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect,
const SkPMColor4f& color, const SkMatrix& klm)
: INHERITED(std::move(gp), rect, color, ClassID()), fKLM(klm) {}
struct Vertex {
SkPoint fPosition;
float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
};
void onPrepareDraws(Target* target) override {
SkASSERT(this->gp()->vertexStride() == sizeof(Vertex));
QuadHelper helper(target, sizeof(Vertex), 1);
Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices());
if (!verts) {
return;
}
SkRect rect = this->rect();
SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect.fLeft, rect.fTop, rect.fRight,
rect.fBottom, sizeof(Vertex));
for (int v = 0; v < 4; ++v) {
SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1);
}
auto pipe = this->makePipeline(target);
helper.recordDraw(target, this->gp(), pipe.fPipeline, pipe.fFixedDynamicState);
}
SkMatrix fKLM;
static constexpr int kVertsPerCubic = 4;
static constexpr int kIndicesPerCubic = 6;
typedef BezierTestOp INHERITED;
};
/**
* This GM directly exercises effects that draw Bezier curves in the GPU backend.
*/
class BezierConicEffects : public GM {
public:
BezierConicEffects() {
this->setBGColor(0xFFFFFFFF);
}
protected:
SkString onShortName() override {
return SkString("bezier_conic_effects");
}
SkISize onISize() override {
return SkISize::Make(800, 800);
}
void onDraw(SkCanvas* canvas) override {
GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
if (!renderTargetContext) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
return;
}
GrContext* context = canvas->getGrContext();
if (!context) {
return;
}
struct Vertex {
SkPoint fPosition;
float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
};
constexpr int kNumConics = 10;
SkRandom rand;
// Mult by 3 for each edge effect type
int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
int row = 0;
int col = 0;
SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000);
for (int i = 0; i < kNumConics; ++i) {
SkPoint baseControlPts[] = {
{rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
{rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
{rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
};
SkScalar weight = rand.nextRangeF(0.f, 2.f);
for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
sk_sp<GrGeometryProcessor> gp;
GrClipEdgeType et = (GrClipEdgeType)edgeType;
gp = GrConicEffect::Make(color, SkMatrix::I(), et, *context->contextPriv().caps(),
SkMatrix::I(), false);
if (!gp) {
continue;
}
SkScalar x = col * w;
SkScalar y = row * h;
SkPoint controlPts[] = {
{x + baseControlPts[0].fX, y + baseControlPts[0].fY},
{x + baseControlPts[1].fX, y + baseControlPts[1].fY},
{x + baseControlPts[2].fX, y + baseControlPts[2].fY}
};
SkConic dst[4];
SkMatrix klm;
int cnt = chop_conic(controlPts, dst, weight);
GrPathUtils::getConicKLM(controlPts, weight, &klm);
SkPaint ctrlPtPaint;
ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
for (int i = 0; i < 3; ++i) {
canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
}
SkPaint polyPaint;
polyPaint.setColor(0xffA0A0A0);
polyPaint.setStrokeWidth(0);
polyPaint.setStyle(SkPaint::kStroke_Style);
canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
SkPaint choppedPtPaint;
choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
for (int c = 0; c < cnt; ++c) {
SkPoint* pts = dst[c].fPts;
for (int i = 0; i < 3; ++i) {
canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
}
SkRect bounds;
//SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
//bounds.set(bPts, 2);
bounds.set(pts, 3);
SkPaint boundsPaint;
boundsPaint.setColor(0xff808080);
boundsPaint.setStrokeWidth(0);
boundsPaint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(bounds, boundsPaint);
std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(context, gp, bounds,
color, klm);
renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
}
++col;
if (numCols == col) {
col = 0;
++row;
}
}
}
}
private:
// Uses the max curvature function for quads to estimate
// where to chop the conic. If the max curvature is not
// found along the curve segment it will return 1 and
// dst[0] is the original conic. If it returns 2 the dst[0]
// and dst[1] are the two new conics.
int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
SkScalar t = SkFindQuadMaxCurvature(src);
if (t == 0 || t == 1) {
if (dst) {
dst[0].set(src, weight);
}
return 1;
} else {
if (dst) {
SkConic conic;
conic.set(src, weight);
if (!conic.chopAt(t, dst)) {
dst[0].set(src, weight);
return 1;
}
}
return 2;
}
}
// Calls split_conic on the entire conic and then once more on each subsection.
// Most cases will result in either 1 conic (chop point is not within t range)
// or 3 points (split once and then one subsection is split again).
int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
SkConic dstTemp[2];
int conicCnt = split_conic(src, dstTemp, weight);
if (2 == conicCnt) {
int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
} else {
dst[0] = dstTemp[0];
}
return conicCnt;
}
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
class BezierQuadTestOp : public BezierTestOp {
public:
DEFINE_OP_CLASS_ID
const char* name() const override { return "BezierQuadTestOp"; }
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
sk_sp<const GrGeometryProcessor> gp,
const SkRect& rect,
const SkPMColor4f& color,
const GrPathUtils::QuadUVMatrix& devToUV) {
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
return pool->allocate<BezierQuadTestOp>(std::move(gp), rect, color, devToUV);
}
private:
friend class ::GrOpMemoryPool; // for ctor
BezierQuadTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect,
const SkPMColor4f& color, const GrPathUtils::QuadUVMatrix& devToUV)
: INHERITED(std::move(gp), rect, color, ClassID()), fDevToUV(devToUV) {}
struct Vertex {
SkPoint fPosition;
float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
};
void onPrepareDraws(Target* target) override {
SkASSERT(this->gp()->vertexStride() == sizeof(Vertex));
QuadHelper helper(target, sizeof(Vertex), 1);
Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices());
if (!verts) {
return;
}
SkRect rect = this->rect();
SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex));
fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
auto pipe = this->makePipeline(target);
helper.recordDraw(target, this->gp(), pipe.fPipeline, pipe.fFixedDynamicState);
}
GrPathUtils::QuadUVMatrix fDevToUV;
static constexpr int kVertsPerCubic = 4;
static constexpr int kIndicesPerCubic = 6;
typedef BezierTestOp INHERITED;
};
/**
* This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
*/
class BezierQuadEffects : public GM {
public:
BezierQuadEffects() {
this->setBGColor(0xFFFFFFFF);
}
protected:
SkString onShortName() override {
return SkString("bezier_quad_effects");
}
SkISize onISize() override {
return SkISize::Make(800, 800);
}
void onDraw(SkCanvas* canvas) override {
GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
if (!renderTargetContext) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
return;
}
GrContext* context = canvas->getGrContext();
if (!context) {
return;
}
struct Vertex {
SkPoint fPosition;
float fUV[4]; // The last two values are ignored. The effect expects a vec4f.
};
constexpr int kNumQuads = 5;
SkRandom rand;
int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
int row = 0;
int col = 0;
SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000);
for (int i = 0; i < kNumQuads; ++i) {
SkPoint baseControlPts[] = {
{rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
{rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
{rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
};
for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
sk_sp<GrGeometryProcessor> gp;
GrClipEdgeType et = (GrClipEdgeType)edgeType;
gp = GrQuadEffect::Make(color, SkMatrix::I(), et, *context->contextPriv().caps(),
SkMatrix::I(), false);
if (!gp) {
continue;
}
SkScalar x = col * w;
SkScalar y = row * h;
SkPoint controlPts[] = {
{x + baseControlPts[0].fX, y + baseControlPts[0].fY},
{x + baseControlPts[1].fX, y + baseControlPts[1].fY},
{x + baseControlPts[2].fX, y + baseControlPts[2].fY}
};
SkPoint chopped[5];
int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
SkPaint ctrlPtPaint;
ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
for (int i = 0; i < 3; ++i) {
canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
}
SkPaint polyPaint;
polyPaint.setColor(0xffA0A0A0);
polyPaint.setStrokeWidth(0);
polyPaint.setStyle(SkPaint::kStroke_Style);
canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
SkPaint choppedPtPaint;
choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
for (int c = 0; c < cnt; ++c) {
SkPoint* pts = chopped + 2 * c;
for (int i = 0; i < 3; ++i) {
canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
}
SkRect bounds;
bounds.set(pts, 3);
SkPaint boundsPaint;
boundsPaint.setColor(0xff808080);
boundsPaint.setStrokeWidth(0);
boundsPaint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(bounds, boundsPaint);
GrPaint grPaint;
grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
GrPathUtils::QuadUVMatrix DevToUV(pts);
std::unique_ptr<GrDrawOp> op = BezierQuadTestOp::Make(context, gp,
bounds, color, DevToUV);
renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
}
++col;
if (numCols == col) {
col = 0;
++row;
}
}
}
}
private:
typedef GM INHERITED;
};
DEF_GM(return new BezierConicEffects;)
DEF_GM(return new BezierQuadEffects;)
}