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 initializers to work 9 10#include <memory> 11 12#include "include/core/SkTypes.h" 13#include "tests/Test.h" 14 15#include "include/core/SkString.h" 16#include "include/gpu/GrDirectContext.h" 17#include "src/core/SkPointPriv.h" 18#include "src/gpu/GrDirectContextPriv.h" 19#include "src/gpu/GrGeometryProcessor.h" 20#include "src/gpu/GrGpu.h" 21#include "src/gpu/GrMemoryPool.h" 22#include "src/gpu/GrOpFlushState.h" 23#include "src/gpu/GrProgramInfo.h" 24#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 25#include "src/gpu/glsl/GrGLSLVarying.h" 26#include "src/gpu/ops/GrMeshDrawOp.h" 27#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" 28#include "src/gpu/v1/SurfaceDrawContext_v1.h" 29 30namespace { 31class Op : public GrMeshDrawOp { 32public: 33 DEFINE_OP_CLASS_ID 34 35 const char* name() const override { return "Test Op"; } 36 37 static GrOp::Owner Make(GrRecordingContext* rContext, int numAttribs) { 38 return GrOp::Make<Op>(rContext, numAttribs); 39 } 40 41 FixedFunctionFlags fixedFunctionFlags() const override { 42 return FixedFunctionFlags::kNone; 43 } 44 45 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override { 46 return GrProcessorSet::EmptySetAnalysis(); 47 } 48 49private: 50 friend class ::GrOp; 51 52 Op(int numAttribs) : INHERITED(ClassID()), fNumAttribs(numAttribs) { 53 this->setBounds(SkRect::MakeWH(1.f, 1.f), HasAABloat::kNo, IsHairline::kNo); 54 } 55 56 GrProgramInfo* programInfo() override { return fProgramInfo; } 57 58 void onCreateProgramInfo(const GrCaps* caps, 59 SkArenaAlloc* arena, 60 const GrSurfaceProxyView& writeView, 61 bool usesMSAASurface, 62 GrAppliedClip&& appliedClip, 63 const GrDstProxyView& dstProxyView, 64 GrXferBarrierFlags renderPassXferBarriers, 65 GrLoadOp colorLoadOp) override { 66 class GP : public GrGeometryProcessor { 67 public: 68 static GrGeometryProcessor* Make(SkArenaAlloc* arena, int numAttribs) { 69 return arena->make([&](void* ptr) { 70 return new (ptr) GP(numAttribs); 71 }); 72 } 73 74 const char* name() const override { return "Test GP"; } 75 76 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override { 77 class Impl : public ProgramImpl { 78 public: 79 void setData(const GrGLSLProgramDataManager&, 80 const GrShaderCaps&, 81 const GrGeometryProcessor&) override {} 82 83 private: 84 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 85 const GP& gp = args.fGeomProc.cast<GP>(); 86 args.fVaryingHandler->emitAttributes(gp); 87 WriteOutputPosition(args.fVertBuilder, gpArgs, gp.fAttributes[0].name()); 88 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 89 fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputColor); 90 fragBuilder->codeAppendf("const half4 %s = half4(1);", 91 args.fOutputCoverage); 92 } 93 }; 94 95 return std::make_unique<Impl>(); 96 } 97 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* builder) const override { 98 builder->add32(fNumAttribs); 99 } 100 101 private: 102 GP(int numAttribs) : INHERITED(kGP_ClassID), fNumAttribs(numAttribs) { 103 SkASSERT(numAttribs > 1); 104 fAttribNames = std::make_unique<SkString[]>(numAttribs); 105 fAttributes = std::make_unique<Attribute[]>(numAttribs); 106 for (auto i = 0; i < numAttribs; ++i) { 107 fAttribNames[i].printf("attr%d", i); 108 // This gives us more of a mix of attribute types, and allows the 109 // component count to fit within the limits for iOS Metal. 110 if (i & 0x1) { 111 fAttributes[i] = {fAttribNames[i].c_str(), kFloat_GrVertexAttribType, 112 kFloat_GrSLType}; 113 } else { 114 fAttributes[i] = {fAttribNames[i].c_str(), kFloat2_GrVertexAttribType, 115 kFloat2_GrSLType}; 116 } 117 } 118 this->setVertexAttributes(fAttributes.get(), numAttribs); 119 } 120 121 int fNumAttribs; 122 std::unique_ptr<SkString[]> fAttribNames; 123 std::unique_ptr<Attribute[]> fAttributes; 124 125 using INHERITED = GrGeometryProcessor; 126 }; 127 128 GrGeometryProcessor* gp = GP::Make(arena, fNumAttribs); 129 130 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, 131 arena, 132 writeView, 133 usesMSAASurface, 134 std::move(appliedClip), 135 dstProxyView, 136 gp, 137 GrProcessorSet::MakeEmptySet(), 138 GrPrimitiveType::kTriangles, 139 renderPassXferBarriers, 140 colorLoadOp, 141 GrPipeline::InputFlags::kNone); 142 } 143 144 void onPrepareDraws(GrMeshDrawTarget* target) override { 145 if (!fProgramInfo) { 146 this->createProgramInfo(target); 147 } 148 149 size_t vertexStride = fProgramInfo->geomProc().vertexStride(); 150 QuadHelper helper(target, vertexStride, 1); 151 SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.vertices()); 152 SkPointPriv::SetRectTriStrip(vertices, 0.f, 0.f, 1.f, 1.f, vertexStride); 153 fMesh = helper.mesh(); 154 } 155 156 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { 157 if (!fProgramInfo || !fMesh) { 158 return; 159 } 160 161 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); 162 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline()); 163 flushState->drawMesh(*fMesh); 164 } 165 166 int fNumAttribs; 167 GrSimpleMesh* fMesh = nullptr; 168 GrProgramInfo* fProgramInfo = nullptr; 169 170 using INHERITED = GrMeshDrawOp; 171}; 172} // namespace 173 174DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) { 175 auto dContext = ctxInfo.directContext(); 176#if GR_GPU_STATS 177 GrGpu* gpu = dContext->priv().getGpu(); 178#endif 179 180 auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, 181 GrColorType::kRGBA_8888, 182 nullptr, 183 SkBackingFit::kApprox, 184 {1, 1}, 185 SkSurfaceProps()); 186 if (!sdc) { 187 ERRORF(reporter, "Could not create render target context."); 188 return; 189 } 190 int attribCnt = dContext->priv().caps()->maxVertexAttributes(); 191 if (!attribCnt) { 192 ERRORF(reporter, "No attributes allowed?!"); 193 return; 194 } 195 dContext->flushAndSubmit(); 196 dContext->priv().resetGpuStats(); 197#if GR_GPU_STATS 198 REPORTER_ASSERT(reporter, gpu->stats()->numDraws() == 0); 199 REPORTER_ASSERT(reporter, gpu->stats()->numFailedDraws() == 0); 200#endif 201 // Adding discard to appease vulkan validation warning about loading uninitialized data on draw 202 sdc->discard(); 203 204 GrPaint grPaint; 205 // This one should succeed. 206 sdc->addDrawOp(Op::Make(dContext, attribCnt)); 207 dContext->flushAndSubmit(); 208#if GR_GPU_STATS 209 REPORTER_ASSERT(reporter, gpu->stats()->numDraws() == 1); 210 REPORTER_ASSERT(reporter, gpu->stats()->numFailedDraws() == 0); 211#endif 212 dContext->priv().resetGpuStats(); 213 sdc->addDrawOp(Op::Make(dContext, attribCnt + 1)); 214 dContext->flushAndSubmit(); 215#if GR_GPU_STATS 216 REPORTER_ASSERT(reporter, gpu->stats()->numDraws() == 0); 217 REPORTER_ASSERT(reporter, gpu->stats()->numFailedDraws() == 1); 218#endif 219} 220