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 #include "bench/Benchmark.h" 9 10 #include "include/core/SkString.h" 11 #include "include/gpu/ganesh/GrDirectContext.h" 12 #include "src/base/SkHalf.h" 13 #include "src/base/SkRandom.h" 14 #include "src/core/SkColorSpacePriv.h" 15 #include "src/gpu/KeyBuilder.h" 16 #include "src/gpu/ganesh/GrCaps.h" 17 #include "src/gpu/ganesh/GrDirectContextPriv.h" 18 #include "src/gpu/ganesh/GrGeometryProcessor.h" 19 #include "src/gpu/ganesh/GrMemoryPool.h" 20 #include "src/gpu/ganesh/GrMeshDrawTarget.h" 21 #include "src/gpu/ganesh/GrOpFlushState.h" 22 #include "src/gpu/ganesh/GrProgramInfo.h" 23 #include "src/gpu/ganesh/GrSimpleMesh.h" 24 #include "src/gpu/ganesh/SkGr.h" 25 #include "src/gpu/ganesh/SurfaceDrawContext.h" 26 #include "src/gpu/ganesh/glsl/GrGLSLColorSpaceXformHelper.h" 27 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h" 28 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h" 29 #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h" 30 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h" 31 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h" 32 33 namespace { 34 35 enum Mode { 36 kBaseline_Mode, // Do the wrong thing, but quickly. 37 kFloat_Mode, // Transform colors on CPU, use float4 attributes. 38 kHalf_Mode, // Transform colors on CPU, use half4 attributes. 39 kShader_Mode, // Use ubyte4 attributes, transform colors on GPU (vertex shader). 40 }; 41 42 class GP : public GrGeometryProcessor { 43 public: Make(SkArenaAlloc * arena,Mode mode,sk_sp<GrColorSpaceXform> colorSpaceXform)44 static GrGeometryProcessor* Make(SkArenaAlloc* arena, Mode mode, 45 sk_sp<GrColorSpaceXform> colorSpaceXform) { 46 return arena->make([&](void* ptr) { 47 return new (ptr) GP(mode, std::move(colorSpaceXform)); 48 }); 49 } 50 name() const51 const char* name() const override { return "VertexColorXformGP"; } 52 makeProgramImpl(const GrShaderCaps &) const53 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override { 54 class Impl : public ProgramImpl { 55 public: 56 void setData(const GrGLSLProgramDataManager& pdman, 57 const GrShaderCaps&, 58 const GrGeometryProcessor& geomProc) override { 59 const GP& gp = geomProc.cast<GP>(); 60 fColorSpaceHelper.setData(pdman, gp.fColorSpaceXform.get()); 61 } 62 63 private: 64 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 65 const GP& gp = args.fGeomProc.cast<GP>(); 66 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 67 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 68 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 69 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 70 71 varyingHandler->emitAttributes(gp); 72 73 // Setup color 74 GrGLSLVarying varying(SkSLType::kHalf4); 75 varyingHandler->addVarying("color", &varying); 76 vertBuilder->codeAppendf("half4 color = %s;", gp.fInColor.name()); 77 78 if (kShader_Mode == gp.fMode) { 79 fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(), 80 kVertex_GrShaderFlag); 81 SkString xformedColor; 82 vertBuilder->appendColorGamutXform(&xformedColor, "color", &fColorSpaceHelper); 83 vertBuilder->codeAppendf("color = %s;", xformedColor.c_str()); 84 vertBuilder->codeAppend("color = half4(color.rgb * color.a, color.a);"); 85 } 86 87 vertBuilder->codeAppendf("%s = color;", varying.vsOut()); 88 fragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, varying.fsIn()); 89 90 // Position 91 WriteOutputPosition(args.fVertBuilder, gpArgs, gp.fInPosition.name()); 92 93 // Coverage 94 fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage); 95 } 96 97 GrGLSLColorSpaceXformHelper fColorSpaceHelper; 98 }; 99 100 return std::make_unique<Impl>(); 101 } 102 addToKey(const GrShaderCaps &,skgpu::KeyBuilder * b) const103 void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const override { 104 b->add32(fMode); 105 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get())); 106 } 107 108 private: GP(Mode mode,sk_sp<GrColorSpaceXform> colorSpaceXform)109 GP(Mode mode, sk_sp<GrColorSpaceXform> colorSpaceXform) 110 : INHERITED(kVertexColorSpaceBenchGP_ClassID) 111 , fMode(mode) 112 , fColorSpaceXform(std::move(colorSpaceXform)) { 113 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, SkSLType::kFloat2}; 114 switch (fMode) { 115 case kBaseline_Mode: 116 case kShader_Mode: 117 fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4}; 118 break; 119 case kFloat_Mode: 120 fInColor = {"inColor", kFloat4_GrVertexAttribType, SkSLType::kHalf4}; 121 break; 122 case kHalf_Mode: 123 fInColor = {"inColor", kHalf4_GrVertexAttribType, SkSLType::kHalf4}; 124 break; 125 } 126 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 2); 127 } 128 129 Mode fMode; 130 sk_sp<GrColorSpaceXform> fColorSpaceXform; 131 132 Attribute fInPosition; 133 Attribute fInColor; 134 135 using INHERITED = GrGeometryProcessor; 136 }; 137 138 class Op : public GrMeshDrawOp { 139 public: 140 DEFINE_OP_CLASS_ID 141 name() const142 const char* name() const override { return "VertColorXformOp"; } 143 Op(GrColor color)144 Op(GrColor color) 145 : INHERITED(ClassID()) 146 , fMode(kBaseline_Mode) 147 , fColor(color) { 148 this->setBounds(SkRect::MakeWH(100.f, 100.f), HasAABloat::kNo, IsHairline::kNo); 149 } 150 Op(const SkColor4f & color4f,Mode mode)151 Op(const SkColor4f& color4f, Mode mode) 152 : INHERITED(ClassID()) 153 , fMode(mode) 154 , fColor4f(color4f) { 155 SkASSERT(kFloat_Mode == fMode || kHalf_Mode == mode); 156 this->setBounds(SkRect::MakeWH(100.f, 100.f), HasAABloat::kNo, IsHairline::kNo); 157 } 158 Op(GrColor color,sk_sp<GrColorSpaceXform> colorSpaceXform)159 Op(GrColor color, sk_sp<GrColorSpaceXform> colorSpaceXform) 160 : INHERITED(ClassID()) 161 , fMode(kShader_Mode) 162 , fColor(color) 163 , fColorSpaceXform(std::move(colorSpaceXform)) { 164 this->setBounds(SkRect::MakeWH(100.f, 100.f), HasAABloat::kNo, IsHairline::kNo); 165 } 166 fixedFunctionFlags() const167 FixedFunctionFlags fixedFunctionFlags() const override { 168 return FixedFunctionFlags::kNone; 169 } 170 finalize(const GrCaps &,const GrAppliedClip *,GrClampType)171 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override { 172 return GrProcessorSet::EmptySetAnalysis(); 173 } 174 175 private: 176 friend class ::GrMemoryPool; 177 programInfo()178 GrProgramInfo* programInfo() override { return fProgramInfo; } 179 onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)180 void onCreateProgramInfo(const GrCaps* caps, 181 SkArenaAlloc* arena, 182 const GrSurfaceProxyView& writeView, 183 bool usesMSAASurface, 184 GrAppliedClip&& appliedClip, 185 const GrDstProxyView& dstProxyView, 186 GrXferBarrierFlags renderPassXferBarriers, 187 GrLoadOp colorLoadOp) override { 188 GrGeometryProcessor* gp = GP::Make(arena, fMode, fColorSpaceXform); 189 190 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, 191 arena, 192 writeView, 193 usesMSAASurface, 194 std::move(appliedClip), 195 dstProxyView, 196 gp, 197 GrProcessorSet::MakeEmptySet(), 198 GrPrimitiveType::kTriangleStrip, 199 renderPassXferBarriers, 200 colorLoadOp, 201 GrPipeline::InputFlags::kNone); 202 } 203 onPrepareDraws(GrMeshDrawTarget * target)204 void onPrepareDraws(GrMeshDrawTarget* target) override { 205 if (!fProgramInfo) { 206 this->createProgramInfo(target); 207 } 208 209 size_t vertexStride = fProgramInfo->geomProc().vertexStride(); 210 const int kVertexCount = 1024; 211 sk_sp<const GrBuffer> vertexBuffer; 212 int firstVertex = 0; 213 void* verts = target->makeVertexSpace(vertexStride, kVertexCount, &vertexBuffer, 214 &firstVertex); 215 if (!verts) { 216 return; 217 } 218 219 const float dx = 100.0f / kVertexCount; 220 if (kFloat_Mode == fMode) { 221 struct V { 222 SkPoint fPos; 223 SkColor4f fColor; 224 }; 225 SkASSERT(sizeof(V) == vertexStride); 226 V* v = (V*)verts; 227 for (int i = 0; i < kVertexCount; i += 2) { 228 v[i + 0].fPos.set(dx * i, 0.0f); 229 v[i + 0].fColor = fColor4f; 230 v[i + 1].fPos.set(dx * i, 100.0f); 231 v[i + 1].fColor = fColor4f; 232 } 233 } else if (kHalf_Mode == fMode) { 234 struct V { 235 SkPoint fPos; 236 uint64_t fColor; 237 }; 238 SkASSERT(sizeof(V) == vertexStride); 239 uint64_t color; 240 to_half(skvx::float4::Load(&fColor4f)).store(&color); 241 V* v = (V*)verts; 242 for (int i = 0; i < kVertexCount; i += 2) { 243 v[i + 0].fPos.set(dx * i, 0.0f); 244 v[i + 0].fColor = color; 245 v[i + 1].fPos.set(dx * i, 100.0f); 246 v[i + 1].fColor = color; 247 } 248 } else { 249 struct V { 250 SkPoint fPos; 251 GrColor fColor; 252 }; 253 SkASSERT(sizeof(V) == vertexStride); 254 V* v = (V*)verts; 255 for (int i = 0; i < kVertexCount; i += 2) { 256 v[i + 0].fPos.set(dx * i, 0.0f); 257 v[i + 0].fColor = fColor; 258 v[i + 1].fPos.set(dx * i, 100.0f); 259 v[i + 1].fColor = fColor; 260 } 261 } 262 263 fMesh = target->allocMesh(); 264 fMesh->set(std::move(vertexBuffer), kVertexCount, firstVertex); 265 } 266 onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)267 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { 268 if (!fProgramInfo || !fMesh) { 269 return; 270 } 271 272 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); 273 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline()); 274 flushState->drawMesh(*fMesh); 275 } 276 277 Mode fMode; 278 GrColor fColor; 279 SkColor4f fColor4f; 280 sk_sp<GrColorSpaceXform> fColorSpaceXform; 281 282 GrSimpleMesh* fMesh = nullptr; 283 GrProgramInfo* fProgramInfo = nullptr; 284 285 using INHERITED = GrMeshDrawOp; 286 }; 287 } // namespace 288 289 class VertexColorSpaceBench : public Benchmark { 290 public: VertexColorSpaceBench(Mode mode,const char * name)291 VertexColorSpaceBench(Mode mode, const char* name) : fMode(mode) { 292 fName = "vertexcolorspace"; 293 fName.appendf("_%s", name); 294 } 295 isSuitableFor(Backend backend)296 bool isSuitableFor(Backend backend) override { return Backend::kGanesh == backend; } onGetName()297 const char* onGetName() override { return fName.c_str(); } 298 onDraw(int loops,SkCanvas * canvas)299 void onDraw(int loops, SkCanvas* canvas) override { 300 auto context = canvas->recordingContext()->asDirectContext(); 301 SkASSERT(context); 302 303 if (kHalf_Mode == fMode && 304 !context->priv().caps()->halfFloatVertexAttributeSupport()) { 305 return; 306 } 307 308 auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, 309 SkNamedGamut::kDisplayP3); 310 auto xform = GrColorSpaceXform::Make(sk_srgb_singleton(), kUnpremul_SkAlphaType, 311 p3.get(), kUnpremul_SkAlphaType); 312 313 SkRandom r; 314 const int kDrawsPerLoop = 32; 315 316 for (int i = 0; i < loops; ++i) { 317 auto sdc = 318 skgpu::ganesh::SurfaceDrawContext::Make(context, 319 GrColorType::kRGBA_8888, 320 p3, 321 SkBackingFit::kApprox, 322 {100, 100}, 323 SkSurfaceProps(), 324 /*label=*/"DrawVertexColorSpaceBench"); 325 SkASSERT(sdc); 326 327 for (int j = 0; j < kDrawsPerLoop; ++j) { 328 SkColor c = r.nextU(); 329 GrOp::Owner op = nullptr; 330 GrRecordingContext* rContext = canvas->recordingContext(); 331 switch (fMode) { 332 case kBaseline_Mode: 333 op = GrOp::Make<Op>(rContext, SkColorToPremulGrColor(c)); 334 break; 335 case kShader_Mode: 336 op = GrOp::Make<Op>(rContext, SkColorToUnpremulGrColor(c), xform); 337 break; 338 case kHalf_Mode: 339 case kFloat_Mode: { 340 SkColor4f c4f = SkColor4f::FromColor(c); 341 c4f = xform->apply(c4f); 342 op = GrOp::Make<Op>(rContext, c4f, fMode); 343 } 344 } 345 sdc->addDrawOp(std::move(op)); 346 } 347 348 context->flushAndSubmit(); 349 } 350 } 351 352 private: 353 SkString fName; 354 Mode fMode; 355 356 using INHERITED = Benchmark; 357 }; 358 359 DEF_BENCH(return new VertexColorSpaceBench(kBaseline_Mode, "baseline")); 360 DEF_BENCH(return new VertexColorSpaceBench(kFloat_Mode, "float")); 361 DEF_BENCH(return new VertexColorSpaceBench(kHalf_Mode, "half")); 362 DEF_BENCH(return new VertexColorSpaceBench(kShader_Mode, "shader")); 363