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