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