xref: /third_party/skia/gm/fwidth_squircle.cpp (revision cb93a386)
1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm/gm.h"
9#include "include/core/SkBlendMode.h"
10#include "include/core/SkCanvas.h"
11#include "include/core/SkColor.h"
12#include "include/core/SkMatrix.h"
13#include "include/core/SkPoint.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkRefCnt.h"
16#include "include/core/SkString.h"
17#include "include/gpu/GrRecordingContext.h"
18#include "include/private/GrTypesPriv.h"
19#include "src/core/SkCanvasPriv.h"
20#include "src/gpu/GrBuffer.h"
21#include "src/gpu/GrCaps.h"
22#include "src/gpu/GrDirectContextPriv.h"
23#include "src/gpu/GrGeometryProcessor.h"
24#include "src/gpu/GrGpuBuffer.h"
25#include "src/gpu/GrMemoryPool.h"
26#include "src/gpu/GrOpFlushState.h"
27#include "src/gpu/GrOpsRenderPass.h"
28#include "src/gpu/GrPipeline.h"
29#include "src/gpu/GrProcessor.h"
30#include "src/gpu/GrProcessorSet.h"
31#include "src/gpu/GrProgramInfo.h"
32#include "src/gpu/GrRecordingContextPriv.h"
33#include "src/gpu/GrResourceProvider.h"
34#include "src/gpu/GrShaderCaps.h"
35#include "src/gpu/GrShaderVar.h"
36#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
37#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
38#include "src/gpu/glsl/GrGLSLUniformHandler.h"
39#include "src/gpu/glsl/GrGLSLVarying.h"
40#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
41#include "src/gpu/ops/GrDrawOp.h"
42#include "src/gpu/ops/GrOp.h"
43#include "src/gpu/v1/SurfaceDrawContext_v1.h"
44#include "tools/gpu/ProxyUtils.h"
45
46#include <memory>
47#include <utility>
48
49class GrAppliedClip;
50
51/**
52 * This test ensures that fwidth() works properly on GPU configs by drawing a squircle.
53 */
54namespace {
55
56static constexpr GrGeometryProcessor::Attribute gVertex =
57        {"bboxcoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
58
59////////////////////////////////////////////////////////////////////////////////////////////////////
60// SkSL code.
61
62class FwidthSquircleTestProcessor : public GrGeometryProcessor {
63public:
64    static GrGeometryProcessor* Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix) {
65        return arena->make([&](void* ptr) {
66            return new (ptr) FwidthSquircleTestProcessor(viewMatrix);
67        });
68    }
69
70    const char* name() const override { return "FwidthSquircleTestProcessor"; }
71
72    void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
73
74    std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
75
76private:
77    FwidthSquircleTestProcessor(const SkMatrix& viewMatrix)
78            : GrGeometryProcessor(kFwidthSquircleTestProcessor_ClassID)
79            , fViewMatrix(viewMatrix) {
80        this->setVertexAttributes(&gVertex, 1);
81    }
82
83    const SkMatrix fViewMatrix;
84
85    using INHERITED = GrGeometryProcessor;
86};
87
88std::unique_ptr<GrGeometryProcessor::ProgramImpl> FwidthSquircleTestProcessor::makeProgramImpl(
89        const GrShaderCaps&) const {
90    class Impl : public ProgramImpl {
91    public:
92        void setData(const GrGLSLProgramDataManager& pdman,
93                     const GrShaderCaps&,
94                     const GrGeometryProcessor& geomProc) override {
95            const auto& proc = geomProc.cast<FwidthSquircleTestProcessor>();
96            pdman.setSkMatrix(fViewMatrixHandle, proc.fViewMatrix);
97        }
98
99    private:
100        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
101            const auto& proc = args.fGeomProc.cast<FwidthSquircleTestProcessor>();
102
103            auto* uniforms = args.fUniformHandler;
104            fViewMatrixHandle = uniforms->addUniform(nullptr,
105                                                     kVertex_GrShaderFlag,
106                                                     kFloat3x3_GrSLType,
107                                                     "viewmatrix");
108
109            auto* varyings = args.fVaryingHandler;
110            varyings->emitAttributes(proc);
111
112            GrGLSLVarying squircleCoord(kFloat2_GrSLType);
113            varyings->addVarying("bboxcoord", &squircleCoord);
114
115            auto* v = args.fVertBuilder;
116            v->codeAppendf("float2x2 R = float2x2(cos(.05), sin(.05), -sin(.05), cos(.05));");
117
118            v->codeAppendf("%s = bboxcoord * 1.25;", squircleCoord.vsOut());
119            v->codeAppendf("float3 vertexpos = float3(bboxcoord * 100 * R + 100, 1);");
120            v->codeAppendf("vertexpos = %s * vertexpos;",
121                           uniforms->getUniformCStr(fViewMatrixHandle));
122            gpArgs->fPositionVar.set(kFloat3_GrSLType, "vertexpos");
123
124            auto* f = args.fFragBuilder;
125            f->codeAppendf("float golden_ratio = 1.61803398875;");
126            f->codeAppendf("float pi = 3.141592653589793;");
127            f->codeAppendf("float x = abs(%s.x), y = abs(%s.y);",
128                           squircleCoord.fsIn(), squircleCoord.fsIn());
129
130            // Squircle function!
131            f->codeAppendf("float fn = half(pow(x, golden_ratio*pi) + "
132                           "pow(y, golden_ratio*pi) - 1);");
133            f->codeAppendf("float fnwidth = fwidth(fn);");
134            f->codeAppendf("fnwidth += 1e-10;");  // Guard against divide-by-zero.
135            f->codeAppendf("half coverage = clamp(half(.5 - fn/fnwidth), 0, 1);");
136
137            f->codeAppendf("half4 %s = half4(.51, .42, .71, 1) * .89;", args.fOutputColor);
138            f->codeAppendf("half4 %s = half4(coverage);", args.fOutputCoverage);
139        }
140
141        UniformHandle fViewMatrixHandle;
142    };
143
144    return std::make_unique<Impl>();
145}
146
147////////////////////////////////////////////////////////////////////////////////////////////////////
148// Draw Op.
149
150class FwidthSquircleTestOp : public GrDrawOp {
151public:
152    DEFINE_OP_CLASS_ID
153
154    static GrOp::Owner Make(GrRecordingContext* ctx, const SkMatrix& viewMatrix) {
155        return GrOp::Make<FwidthSquircleTestOp>(ctx, viewMatrix);
156    }
157
158private:
159    FwidthSquircleTestOp(const SkMatrix& viewMatrix)
160            : GrDrawOp(ClassID())
161            , fViewMatrix(viewMatrix) {
162        this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
163    }
164
165    const char* name() const override { return "FwidthSquircleTestOp"; }
166    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
167    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
168        return GrProcessorSet::EmptySetAnalysis();
169    }
170
171    GrProgramInfo* createProgramInfo(const GrCaps* caps,
172                                     SkArenaAlloc* arena,
173                                     const GrSurfaceProxyView& writeView,
174                                     bool usesMSAASurface,
175                                     GrAppliedClip&& appliedClip,
176                                     const GrDstProxyView& dstProxyView,
177                                     GrXferBarrierFlags renderPassXferBarriers,
178                                     GrLoadOp colorLoadOp) const {
179        GrGeometryProcessor* geomProc = FwidthSquircleTestProcessor::Make(arena, fViewMatrix);
180
181        return sk_gpu_test::CreateProgramInfo(caps, arena, writeView, usesMSAASurface,
182                                              std::move(appliedClip), dstProxyView,
183                                              geomProc, SkBlendMode::kSrcOver,
184                                              GrPrimitiveType::kTriangleStrip,
185                                              renderPassXferBarriers, colorLoadOp);
186    }
187
188    GrProgramInfo* createProgramInfo(GrOpFlushState* flushState) const {
189        return this->createProgramInfo(&flushState->caps(),
190                                       flushState->allocator(),
191                                       flushState->writeView(),
192                                       flushState->usesMSAASurface(),
193                                       flushState->detachAppliedClip(),
194                                       flushState->dstProxyView(),
195                                       flushState->renderPassBarriers(),
196                                       flushState->colorLoadOp());
197    }
198
199    void onPrePrepare(GrRecordingContext* context,
200                      const GrSurfaceProxyView& writeView,
201                      GrAppliedClip* clip,
202                      const GrDstProxyView& dstProxyView,
203                      GrXferBarrierFlags renderPassXferBarriers,
204                      GrLoadOp colorLoadOp) final {
205        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
206
207        // DMSAA is not supported on DDL.
208        bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
209
210        // This is equivalent to a GrOpFlushState::detachAppliedClip
211        GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip::Disabled();
212
213        fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, writeView,
214                                               usesMSAASurface, std::move(appliedClip),
215                                               dstProxyView, renderPassXferBarriers, colorLoadOp);
216
217        context->priv().recordProgramInfo(fProgramInfo);
218    }
219
220    void onPrepare(GrOpFlushState* flushState) final {
221        SkPoint vertices[4] = {
222            {-1, -1},
223            {+1, -1},
224            {-1, +1},
225            {+1, +1},
226        };
227        fVertexBuffer = flushState->resourceProvider()->createBuffer(
228                sizeof(vertices), GrGpuBufferType::kVertex, kStatic_GrAccessPattern, vertices);
229    }
230
231    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final {
232        if (!fVertexBuffer) {
233            return;
234        }
235
236        if (!fProgramInfo) {
237            fProgramInfo = this->createProgramInfo(flushState);
238        }
239
240        flushState->bindPipeline(*fProgramInfo, SkRect::MakeIWH(kWidth, kHeight));
241        flushState->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer));
242        flushState->draw(4, 0);
243
244    }
245
246    static const int kWidth = 200;
247    static const int kHeight = 200;
248
249    sk_sp<GrBuffer> fVertexBuffer;
250    const SkMatrix  fViewMatrix;
251
252    // The program info (and both the GrPipeline and GrGeometryProcessor it relies on), when
253    // allocated, are allocated in either the ddl-record-time or flush-time arena. It is the
254    // arena's job to free up their memory so we just have a bare programInfo pointer here. We
255    // don't even store the GrPipeline and GrGeometryProcessor pointers here bc they are
256    // guaranteed to have the same lifetime as the program info.
257    GrProgramInfo*  fProgramInfo = nullptr;
258
259    friend class ::GrOp; // for ctor
260
261    using INHERITED = GrDrawOp;
262};
263
264} // namespace
265
266////////////////////////////////////////////////////////////////////////////////////////////////////
267// Test.
268
269namespace skiagm {
270
271DEF_SIMPLE_GPU_GM_CAN_FAIL(fwidth_squircle, rContext, canvas, errorMsg, 200, 200) {
272    if (!rContext->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
273        *errorMsg = "Shader derivatives not supported.";
274        return DrawResult::kSkip;
275    }
276
277    auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
278    if (!sdc) {
279        *errorMsg = GM::kErrorMsg_DrawSkippedGpuOnly;
280        return DrawResult::kSkip;
281    }
282
283    // Draw the test directly to the frame buffer.
284    canvas->clear(SK_ColorWHITE);
285    sdc->addDrawOp(FwidthSquircleTestOp::Make(rContext, canvas->getTotalMatrix()));
286    return skiagm::DrawResult::kOk;
287}
288
289}  // namespace skiagm
290