1/*
2 * Copyright 2016 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// This is a GPU-backend specific test. It relies on static intializers to work
9
10#include "SkTypes.h"
11#include "Test.h"
12
13#if SK_SUPPORT_GPU
14#include "GrContext.h"
15#include "GrGeometryProcessor.h"
16#include "GrGpu.h"
17#include "GrOpFlushState.h"
18#include "GrRenderTargetContext.h"
19#include "GrRenderTargetContextPriv.h"
20#include "SkString.h"
21#include "glsl/GrGLSLFragmentShaderBuilder.h"
22#include "glsl/GrGLSLGeometryProcessor.h"
23#include "glsl/GrGLSLVarying.h"
24#include "ops/GrMeshDrawOp.h"
25
26namespace {
27class Op : public GrMeshDrawOp {
28public:
29    DEFINE_OP_CLASS_ID
30
31    const char* name() const override { return "Dummy Op"; }
32
33    static std::unique_ptr<GrDrawOp> Make(int numAttribs) {
34        return std::unique_ptr<GrDrawOp>(new Op(numAttribs));
35    }
36
37    FixedFunctionFlags fixedFunctionFlags() const override {
38        return FixedFunctionFlags::kNone;
39    }
40
41    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
42        return RequiresDstTexture::kNo;
43    }
44
45private:
46    Op(int numAttribs) : INHERITED(ClassID()), fNumAttribs(numAttribs) {
47        this->setBounds(SkRect::MakeWH(1.f, 1.f), HasAABloat::kNo, IsZeroArea::kNo);
48    }
49
50    bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
51
52    void onPrepareDraws(Target* target) const override {
53        class GP : public GrGeometryProcessor {
54        public:
55            GP(int numAttribs) {
56                this->initClassID<GP>();
57                SkASSERT(numAttribs > 1);
58                for (auto i = 0; i < numAttribs; ++i) {
59                    fAttribNames.push_back().printf("attr%d", i);
60                }
61                for (auto i = 0; i < numAttribs; ++i) {
62                    this->addVertexAttrib(fAttribNames[i].c_str(), kVec2f_GrVertexAttribType);
63                }
64            }
65            const char* name() const override { return "Dummy GP"; }
66
67            GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
68                class GLSLGP : public GrGLSLGeometryProcessor {
69                public:
70                    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
71                        const GP& gp = args.fGP.cast<GP>();
72                        args.fVaryingHandler->emitAttributes(gp);
73                        this->setupPosition(args.fVertBuilder, gpArgs, gp.getAttrib(0).fName);
74                        GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
75                        fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputColor);
76                        fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
77                    }
78                    void setData(const GrGLSLProgramDataManager& pdman,
79                                 const GrPrimitiveProcessor& primProc,
80                                 FPCoordTransformIter&&) override {}
81                };
82                return new GLSLGP();
83            }
84            void getGLSLProcessorKey(const GrShaderCaps&,
85                                     GrProcessorKeyBuilder* builder) const override {
86                builder->add32(this->numAttribs());
87            }
88
89        private:
90            SkTArray<SkString> fAttribNames;
91        };
92        sk_sp<GrGeometryProcessor> gp(new GP(fNumAttribs));
93        QuadHelper helper;
94        size_t vertexStride = gp->getVertexStride();
95        SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
96        vertices->setRectFan(0.f, 0.f, 1.f, 1.f, vertexStride);
97        helper.recordDraw(target, gp.get(), target->makePipeline(0, &GrProcessorSet::EmptySet()));
98    }
99
100    int fNumAttribs;
101
102    typedef GrMeshDrawOp INHERITED;
103};
104}
105
106DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) {
107    GrContext* context = ctxInfo.grContext();
108
109    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
110                                                                     SkBackingFit::kApprox,
111                                                                     1, 1, kRGBA_8888_GrPixelConfig,
112                                                                     nullptr));
113    if (!renderTargetContext) {
114        ERRORF(reporter, "Could not create render target context.");
115        return;
116    }
117    int attribCnt = context->caps()->maxVertexAttributes();
118    if (!attribCnt) {
119        ERRORF(reporter, "No attributes allowed?!");
120        return;
121    }
122    context->flush();
123    context->resetGpuStats();
124#if GR_GPU_STATS
125    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0);
126    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0);
127#endif
128    GrPaint grPaint;
129    // This one should succeed.
130    renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt));
131    context->flush();
132#if GR_GPU_STATS
133    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1);
134    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0);
135#endif
136    context->resetGpuStats();
137    renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt + 1));
138    context->flush();
139#if GR_GPU_STATS
140    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0);
141    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1);
142#endif
143}
144#endif
145