d358cbebd4
This CL adds a new type GrDstSampleType to say how we will sample the dst. We add tracking of the GrDstSampleType in the recording of GrOps and then during execution passing the information along to the GrPipeline. In general the tracking of GrDstSampleType is a global state of a GrOpsTask so it is kept separate fro the DstProxyView which is more specific to a single Op on the GrOpsTask. Bug: skia:10409 Change-Id: Ie843c31f2e48a887daf96cee99ed159b196cb545 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315645 Commit-Queue: Greg Daniel <egdaniel@google.com> Reviewed-by: Michael Ludwig <michaelludwig@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
508 lines
18 KiB
C++
508 lines
18 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/gm.h"
|
|
#include "include/core/SkBlendMode.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkMatrix.h"
|
|
#include "include/core/SkPaint.h"
|
|
#include "include/core/SkPoint.h"
|
|
#include "include/core/SkPoint3.h"
|
|
#include "include/core/SkRect.h"
|
|
#include "include/core/SkRefCnt.h"
|
|
#include "include/core/SkScalar.h"
|
|
#include "include/core/SkSize.h"
|
|
#include "include/core/SkString.h"
|
|
#include "include/core/SkTypes.h"
|
|
#include "include/gpu/GrRecordingContext.h"
|
|
#include "include/private/GrSharedEnums.h"
|
|
#include "include/private/GrTypesPriv.h"
|
|
#include "include/private/SkColorData.h"
|
|
#include "include/utils/SkRandom.h"
|
|
#include "src/core/SkGeometry.h"
|
|
#include "src/core/SkPointPriv.h"
|
|
#include "src/gpu/GrCaps.h"
|
|
#include "src/gpu/GrContextPriv.h"
|
|
#include "src/gpu/GrGeometryProcessor.h"
|
|
#include "src/gpu/GrMemoryPool.h"
|
|
#include "src/gpu/GrOpFlushState.h"
|
|
#include "src/gpu/GrOpsRenderPass.h"
|
|
#include "src/gpu/GrPaint.h"
|
|
#include "src/gpu/GrProcessorAnalysis.h"
|
|
#include "src/gpu/GrProcessorSet.h"
|
|
#include "src/gpu/GrProgramInfo.h"
|
|
#include "src/gpu/GrRecordingContextPriv.h"
|
|
#include "src/gpu/GrRenderTargetContext.h"
|
|
#include "src/gpu/GrRenderTargetContextPriv.h"
|
|
#include "src/gpu/GrUserStencilSettings.h"
|
|
#include "src/gpu/effects/GrBezierEffect.h"
|
|
#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
|
|
#include "src/gpu/geometry/GrPathUtils.h"
|
|
#include "src/gpu/ops/GrDrawOp.h"
|
|
#include "src/gpu/ops/GrMeshDrawOp.h"
|
|
#include "src/gpu/ops/GrOp.h"
|
|
#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
class GrAppliedClip;
|
|
|
|
namespace skiagm {
|
|
|
|
class BezierTestOp : public GrMeshDrawOp {
|
|
public:
|
|
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
|
|
|
|
GrProcessorSet::Analysis finalize(
|
|
const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
|
|
GrClampType clampType) override {
|
|
return fProcessorSet.finalize(
|
|
fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
|
|
&GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps, clampType, &fColor);
|
|
}
|
|
|
|
void visitProxies(const VisitProxyFunc& func) const override {
|
|
if (fProgramInfo) {
|
|
fProgramInfo->visitFPProxies(func);
|
|
} else {
|
|
fProcessorSet.visitProxies(func);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
BezierTestOp(const SkRect& rect, const SkPMColor4f& color, int32_t classID)
|
|
: INHERITED(classID)
|
|
, fRect(rect)
|
|
, fColor(color)
|
|
, fProcessorSet(SkBlendMode::kSrc) {
|
|
this->setBounds(rect, HasAABloat::kYes, IsHairline::kNo);
|
|
}
|
|
|
|
virtual GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) = 0;
|
|
|
|
GrProgramInfo* programInfo() override { return fProgramInfo; }
|
|
|
|
void onCreateProgramInfo(const GrCaps* caps,
|
|
SkArenaAlloc* arena,
|
|
const GrSurfaceProxyView* writeView,
|
|
GrAppliedClip&& appliedClip,
|
|
const GrXferProcessor::DstProxyView& dstProxyView,
|
|
GrXferBarrierFlags renderPassXferBarriers) override {
|
|
auto gp = this->makeGP(*caps, arena);
|
|
if (!gp) {
|
|
return;
|
|
}
|
|
|
|
GrPipeline::InputFlags flags = GrPipeline::InputFlags::kNone;
|
|
|
|
fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
|
|
std::move(appliedClip),
|
|
dstProxyView, gp,
|
|
std::move(fProcessorSet),
|
|
GrPrimitiveType::kTriangles,
|
|
renderPassXferBarriers,
|
|
flags);
|
|
}
|
|
|
|
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final {
|
|
if (!fProgramInfo) {
|
|
this->createProgramInfo(flushState);
|
|
}
|
|
|
|
if (!fProgramInfo) {
|
|
return;
|
|
}
|
|
|
|
flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
|
|
flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
|
|
flushState->drawMesh(*fMesh);
|
|
}
|
|
|
|
const SkRect& rect() const { return fRect; }
|
|
const SkPMColor4f& color() const { return fColor; }
|
|
|
|
protected:
|
|
GrSimpleMesh* fMesh = nullptr; // filled in by the derived classes
|
|
|
|
private:
|
|
SkRect fRect;
|
|
SkPMColor4f fColor;
|
|
GrProcessorSet fProcessorSet;
|
|
GrProgramInfo* fProgramInfo = nullptr;
|
|
|
|
using INHERITED = GrMeshDrawOp;
|
|
};
|
|
|
|
/**
|
|
* 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 final { return "BezierConicTestOp"; }
|
|
|
|
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
|
|
const SkRect& rect,
|
|
const SkPMColor4f& color,
|
|
const SkMatrix& klm) {
|
|
GrOpMemoryPool* pool = context->priv().opMemoryPool();
|
|
|
|
return pool->allocate<BezierConicTestOp>(rect, color, klm);
|
|
}
|
|
|
|
private:
|
|
friend class ::GrOpMemoryPool; // for ctor
|
|
|
|
BezierConicTestOp(const SkRect& rect, const SkPMColor4f& color, const SkMatrix& klm)
|
|
: INHERITED(rect, color, ClassID())
|
|
, fKLM(klm) {}
|
|
|
|
struct Vertex {
|
|
SkPoint fPosition;
|
|
float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
|
|
};
|
|
|
|
GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final {
|
|
auto tmp = GrConicEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(),
|
|
false);
|
|
if (!tmp) {
|
|
return nullptr;
|
|
}
|
|
SkASSERT(tmp->vertexStride() == sizeof(Vertex));
|
|
return tmp;
|
|
}
|
|
|
|
void onPrepareDraws(Target* target) final {
|
|
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));
|
|
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);
|
|
}
|
|
|
|
fMesh = helper.mesh();
|
|
}
|
|
|
|
SkMatrix fKLM;
|
|
|
|
static constexpr int kVertsPerCubic = 4;
|
|
static constexpr int kIndicesPerCubic = 6;
|
|
|
|
using INHERITED = BezierTestOp;
|
|
};
|
|
|
|
|
|
/**
|
|
* This GM directly exercises effects that draw Bezier curves in the GPU backend.
|
|
*/
|
|
class BezierConicEffects : public GpuGM {
|
|
public:
|
|
BezierConicEffects() {
|
|
this->setBGColor(0xFFFFFFFF);
|
|
}
|
|
|
|
protected:
|
|
static const int kNumConics = 10;
|
|
static const int kCellWidth = 128;
|
|
static const int kCellHeight = 128;
|
|
|
|
SkString onShortName() override {
|
|
return SkString("bezier_conic_effects");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(kCellWidth, kNumConics*kCellHeight);
|
|
}
|
|
|
|
void onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
|
|
SkCanvas* canvas) override {
|
|
|
|
const SkScalar w = kCellWidth, h = kCellHeight;
|
|
const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000);
|
|
|
|
const SkPoint baseControlPts[kNumConics][3] = {
|
|
{ { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } },
|
|
{ { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } },
|
|
{ { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } },
|
|
{ { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } },
|
|
{ { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } },
|
|
{ { 0.96f * w, 0.65f * h}, { 0.03f * w, 0.79f * h }, { 0.24f * w, 0.56f * h } },
|
|
{ { 0.57f * w, 0.12f * h}, { 0.33f * w, 0.67f * h }, { 0.59f * w, 0.33f * h } },
|
|
{ { 0.12f * w, 0.72f * h}, { 0.69f * w, 0.85f * h }, { 0.46f * w, 0.32f * h } },
|
|
{ { 0.27f * w, 0.49f * h}, { 0.41f * w, 0.02f * h }, { 0.11f * w, 0.42f * h } },
|
|
{ { 0.40f * w, 0.13f * h}, { 0.83f * w, 0.30f * h }, { 0.31f * w, 0.68f * h } },
|
|
};
|
|
const SkScalar weights[kNumConics] = { 0.62f, 0.01f, 0.95f, 1.48f, 0.37f,
|
|
0.66f, 0.15f, 0.14f, 0.61f, 1.4f };
|
|
|
|
SkPaint ctrlPtPaint;
|
|
ctrlPtPaint.setColor(SK_ColorRED);
|
|
|
|
SkPaint choppedPtPaint;
|
|
choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
|
|
|
|
SkPaint polyPaint;
|
|
polyPaint.setColor(0xffA0A0A0);
|
|
polyPaint.setStrokeWidth(0);
|
|
polyPaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
SkPaint boundsPaint;
|
|
boundsPaint.setColor(0xff808080);
|
|
boundsPaint.setStrokeWidth(0);
|
|
boundsPaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
|
|
for (int row = 0; row < kNumConics; ++row) {
|
|
SkScalar x = 0;
|
|
SkScalar y = row * h;
|
|
SkPoint controlPts[] = {
|
|
{x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY},
|
|
{x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY},
|
|
{x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY}
|
|
};
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
|
|
}
|
|
|
|
canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
|
|
|
|
SkConic dst[4];
|
|
SkMatrix klm;
|
|
int cnt = ChopConic(controlPts, dst, weights[row]);
|
|
GrPathUtils::getConicKLM(controlPts, weights[row], &klm);
|
|
|
|
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;
|
|
bounds.setBounds(pts, 3);
|
|
|
|
canvas->drawRect(bounds, boundsPaint);
|
|
|
|
std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(context, bounds,
|
|
kOpaqueBlack, klm);
|
|
renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
|
|
}
|
|
}
|
|
}
|
|
|
|
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.
|
|
static int SplitConic(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 SplitConic 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).
|
|
static int ChopConic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
|
|
SkConic dstTemp[2];
|
|
int conicCnt = SplitConic(src, dstTemp, weight);
|
|
if (2 == conicCnt) {
|
|
int conicCnt2 = SplitConic(dstTemp[0].fPts, dst, dstTemp[0].fW);
|
|
conicCnt = conicCnt2 + SplitConic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
|
|
} else {
|
|
dst[0] = dstTemp[0];
|
|
}
|
|
return conicCnt;
|
|
}
|
|
|
|
using INHERITED = GM;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class BezierQuadTestOp : public BezierTestOp {
|
|
public:
|
|
DEFINE_OP_CLASS_ID
|
|
const char* name() const override { return "BezierQuadTestOp"; }
|
|
|
|
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
|
|
const SkRect& rect,
|
|
const SkPMColor4f& color,
|
|
const GrPathUtils::QuadUVMatrix& devToUV) {
|
|
GrOpMemoryPool* pool = context->priv().opMemoryPool();
|
|
|
|
return pool->allocate<BezierQuadTestOp>(rect, color, devToUV);
|
|
}
|
|
|
|
private:
|
|
friend class ::GrOpMemoryPool; // for ctor
|
|
|
|
BezierQuadTestOp(const SkRect& rect, const SkPMColor4f& color,
|
|
const GrPathUtils::QuadUVMatrix& devToUV)
|
|
: INHERITED(rect, color, ClassID())
|
|
, fDevToUV(devToUV) {}
|
|
|
|
struct Vertex {
|
|
SkPoint fPosition;
|
|
float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
|
|
};
|
|
|
|
GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final {
|
|
auto tmp = GrQuadEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(),
|
|
false);
|
|
if (!tmp) {
|
|
return nullptr;
|
|
}
|
|
SkASSERT(tmp->vertexStride() == sizeof(Vertex));
|
|
return tmp;
|
|
}
|
|
|
|
void onPrepareDraws(Target* target) final {
|
|
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(verts, 4, sizeof(Vertex), sizeof(SkPoint));
|
|
|
|
fMesh = helper.mesh();
|
|
}
|
|
|
|
GrPathUtils::QuadUVMatrix fDevToUV;
|
|
|
|
static constexpr int kVertsPerCubic = 4;
|
|
static constexpr int kIndicesPerCubic = 6;
|
|
|
|
using INHERITED = BezierTestOp;
|
|
};
|
|
|
|
/**
|
|
* This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
|
|
*/
|
|
class BezierQuadEffects : public GpuGM {
|
|
public:
|
|
BezierQuadEffects() {
|
|
this->setBGColor(0xFFFFFFFF);
|
|
}
|
|
|
|
protected:
|
|
static const int kNumQuads = 5;
|
|
static const int kCellWidth = 128;
|
|
static const int kCellHeight = 128;
|
|
|
|
SkString onShortName() override {
|
|
return SkString("bezier_quad_effects");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(kCellWidth, kNumQuads*kCellHeight);
|
|
}
|
|
|
|
void onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
|
|
SkCanvas* canvas) override {
|
|
|
|
const SkScalar w = kCellWidth, h = kCellHeight;
|
|
const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000);
|
|
|
|
const SkPoint baseControlPts[kNumQuads][3] = {
|
|
{ { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } },
|
|
{ { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } },
|
|
{ { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } },
|
|
{ { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } },
|
|
{ { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } },
|
|
};
|
|
|
|
SkPaint ctrlPtPaint;
|
|
ctrlPtPaint.setColor(SK_ColorRED);
|
|
|
|
SkPaint choppedPtPaint;
|
|
choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
|
|
|
|
SkPaint polyPaint;
|
|
polyPaint.setColor(0xffA0A0A0);
|
|
polyPaint.setStrokeWidth(0);
|
|
polyPaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
SkPaint boundsPaint;
|
|
boundsPaint.setColor(0xff808080);
|
|
boundsPaint.setStrokeWidth(0);
|
|
boundsPaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
for (int row = 0; row < kNumQuads; ++row) {
|
|
SkScalar x = 0;
|
|
SkScalar y = row * h;
|
|
SkPoint controlPts[] = {
|
|
{x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY},
|
|
{x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY},
|
|
{x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY}
|
|
};
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
|
|
}
|
|
|
|
canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
|
|
|
|
SkPoint chopped[5];
|
|
int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
|
|
|
|
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.setBounds(pts, 3);
|
|
|
|
canvas->drawRect(bounds, boundsPaint);
|
|
|
|
GrPathUtils::QuadUVMatrix DevToUV(pts);
|
|
|
|
std::unique_ptr<GrDrawOp> op = BezierQuadTestOp::Make(context, bounds,
|
|
kOpaqueBlack, DevToUV);
|
|
renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
using INHERITED = GM;
|
|
};
|
|
|
|
DEF_GM(return new BezierConicEffects;)
|
|
DEF_GM(return new BezierQuadEffects;)
|
|
} // namespace skiagm
|