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