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