/* * Copyright 2021 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm/gm.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/gpu/GrRecordingContext.h" #include "src/core/SkCanvasPriv.h" #include "src/gpu/GrBuffer.h" #include "src/gpu/GrGeometryProcessor.h" #include "src/gpu/GrGpuBuffer.h" #include "src/gpu/GrOpFlushState.h" #include "src/gpu/GrProcessor.h" #include "src/gpu/GrProcessorSet.h" #include "src/gpu/GrProgramInfo.h" #include "src/gpu/GrResourceProvider.h" #include "src/gpu/GrShaderVar.h" #include "src/gpu/KeyBuilder.h" #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/ops/GrDrawOp.h" #include "src/gpu/ops/GrOp.h" #include "src/gpu/v1/SurfaceDrawContext_v1.h" #include "tools/gpu/ProxyUtils.h" #include #include class GrAppliedClip; class GrGLSLProgramDataManager; namespace { enum class AttrMode { kAuto, kManual, kWacky }; class AttributeTestProcessor : public GrGeometryProcessor { public: static GrGeometryProcessor* Make(SkArenaAlloc* arena, AttrMode mode) { return arena->make([&](void* ptr) { return new (ptr) AttributeTestProcessor(mode); }); } const char* name() const final { return "AttributeTestProcessor"; } void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const final { b->add32(static_cast(fMode)); } std::unique_ptr makeProgramImpl(const GrShaderCaps&) const final; private: AttributeTestProcessor(AttrMode mode) : GrGeometryProcessor(kAttributeTestProcessor_ClassID), fMode(mode) { switch (fMode) { case AttrMode::kAuto: fAttributes.emplace_back("pos", kFloat2_GrVertexAttribType, SkSLType::kFloat2); fAttributes.emplace_back("color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4); this->setVertexAttributesWithImplicitOffsets(fAttributes.data(), fAttributes.size()); break; case AttrMode::kManual: // Same result as kAuto but with explicitly specified offsets and stride. fAttributes.emplace_back("pos", kFloat2_GrVertexAttribType, SkSLType::kFloat2, 0); fAttributes.emplace_back("color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4, 8); this->setVertexAttributes(fAttributes.data(), fAttributes.size(), 12); break; case AttrMode::kWacky: // 0 thru 7 : float2 aliased to "pos0" and "pos1" // 8 thru 11: pad // 12 thru 15: unorm4 "color" // 16 thru 19: pad fAttributes.emplace_back("pos0", kFloat2_GrVertexAttribType, SkSLType::kFloat2, 0); fAttributes.emplace_back("pos1", kFloat2_GrVertexAttribType, SkSLType::kFloat2, 0); fAttributes.emplace_back("color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4, 12); this->setVertexAttributes(fAttributes.data(), fAttributes.size(), 20); break; } } const AttrMode fMode; std::vector fAttributes; using INHERITED = GrGeometryProcessor; }; std::unique_ptr AttributeTestProcessor::makeProgramImpl( const GrShaderCaps&) const { class Impl : public ProgramImpl { public: void setData(const GrGLSLProgramDataManager&, const GrShaderCaps&, const GrGeometryProcessor&) override {} private: void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const AttributeTestProcessor& proc = args.fGeomProc.cast(); args.fVaryingHandler->emitAttributes(proc); if (proc.fMode == AttrMode::kWacky) { args.fVertBuilder->codeAppend("float2 pos = pos0 + pos1;"); } args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor); args.fVaryingHandler->addPassThroughAttribute(GrShaderVar("color", SkSLType::kHalf4), args.fOutputColor); gpArgs->fPositionVar.set(SkSLType::kFloat2, "pos"); args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage); } }; return std::make_unique(); } class AttributeTestOp : public GrDrawOp { public: DEFINE_OP_CLASS_ID static GrOp::Owner Make(GrRecordingContext* context, AttrMode mode, const SkRect& r) { return GrOp::Make(context, mode, r); } private: AttributeTestOp(AttrMode mode, SkRect rect) : GrDrawOp(ClassID()), fMode(mode), fRect(rect) { this->setBounds(fRect, HasAABloat::kNo, IsHairline::kNo); } const char* name() const override { return "AttributeTestOp"; } FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override { return GrProcessorSet::EmptySetAnalysis(); } GrProgramInfo* createProgramInfo(const GrCaps* caps, SkArenaAlloc* arena, const GrSurfaceProxyView& writeView, bool usesMSAASurface, GrAppliedClip&& appliedClip, const GrDstProxyView& dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp) const { GrGeometryProcessor* geomProc = AttributeTestProcessor::Make(arena, fMode); return sk_gpu_test::CreateProgramInfo(caps, arena, writeView, usesMSAASurface, std::move(appliedClip), dstProxyView, geomProc, SkBlendMode::kSrcOver, GrPrimitiveType::kTriangleStrip, renderPassXferBarriers, colorLoadOp); } GrProgramInfo* createProgramInfo(GrOpFlushState* flushState) const { return this->createProgramInfo(&flushState->caps(), flushState->allocator(), flushState->writeView(), flushState->usesMSAASurface(), flushState->detachAppliedClip(), flushState->dstProxyView(), flushState->renderPassBarriers(), flushState->colorLoadOp()); } void onPrePrepare(GrRecordingContext* context, const GrSurfaceProxyView& writeView, GrAppliedClip* clip, const GrDstProxyView& dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp) final { SkArenaAlloc* arena = context->priv().recordTimeAllocator(); // DMSAA is not supported on DDL. bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1; // This is equivalent to a GrOpFlushState::detachAppliedClip GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip::Disabled(); fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, writeView, usesMSAASurface, std::move(appliedClip), dstProxyView, renderPassXferBarriers, colorLoadOp); context->priv().recordProgramInfo(fProgramInfo); } template void makeVB(GrOpFlushState* flushState, const SkRect rect) { V v[4]; v[0].p = {rect.left() , rect.top() }; v[1].p = {rect.right(), rect.top() }; v[2].p = {rect.left() , rect.bottom()}; v[3].p = {rect.right(), rect.bottom()}; v[0].color = SK_ColorRED; v[1].color = SK_ColorGREEN; v[2].color = SK_ColorYELLOW; v[3].color = SK_ColorMAGENTA; fVertexBuffer = flushState->resourceProvider()->createBuffer( sizeof(v), GrGpuBufferType::kVertex, kStatic_GrAccessPattern, v); } void onPrepare(GrOpFlushState* flushState) override { if (fMode == AttrMode::kWacky) { struct V { SkPoint p; uint32_t pad0; uint32_t color; uint32_t pad1; }; SkRect rect {fRect.fLeft/2.f, fRect.fTop/2.f, fRect.fRight/2.f, fRect.fBottom/2.f}; this->makeVB(flushState, rect); } else { struct V { SkPoint p; uint32_t color; }; this->makeVB(flushState, fRect); } } void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { if (!fVertexBuffer) { return; } if (!fProgramInfo) { fProgramInfo = this->createProgramInfo(flushState); } flushState->bindPipeline(*fProgramInfo, fRect); flushState->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer)); flushState->draw(4, 0); } sk_sp fVertexBuffer; const AttrMode fMode; const SkRect fRect; // The program info (and both the GrPipeline and GrGeometryProcessor it relies on), when // allocated, are allocated in either the ddl-record-time or flush-time arena. It is the // arena's job to free up their memory so we just have a bare programInfo pointer here. We // don't even store the GrPipeline and GrGeometryProcessor pointers here bc they are // guaranteed to have the same lifetime as the program info. GrProgramInfo* fProgramInfo = nullptr; friend class ::GrOp; // for ctor using INHERITED = GrDrawOp; }; } // namespace namespace skiagm { /** * This is a GPU-backend specific test that exercises explicit and implicit attribute offsets and * strides. */ class AttributesGM : public GpuGM { SkString onShortName() override { return SkString("attributes"); } SkISize onISize() override { return {120, 340}; } DrawResult onDraw(GrRecordingContext*, SkCanvas*, SkString* errorMsg) override; }; DrawResult AttributesGM::onDraw(GrRecordingContext* rc, SkCanvas* canvas, SkString* errorMsg) { auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas); if (!sdc) { *errorMsg = kErrorMsg_DrawSkippedGpuOnly; return DrawResult::kSkip; } sdc->clear(SK_PMColor4fBLACK); // Draw the test directly to the frame buffer. auto r = SkRect::MakeXYWH(10, 10, 100, 100); for (AttrMode m : {AttrMode::kAuto, AttrMode::kManual, AttrMode::kWacky}) { sdc->addDrawOp(AttributeTestOp::Make(rc, m, r)); r.offset(0, 110); } return DrawResult::kOk; } //////////////////////////////////////////////////////////////////////////////////////////////////// DEF_GM( return new AttributesGM(); ) } // namespace skiagm