1/* 2 * Copyright 2015 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 "src/gpu/ops/LatticeOp.h" 9 10#include "include/core/SkBitmap.h" 11#include "include/core/SkRect.h" 12#include "src/core/SkLatticeIter.h" 13#include "src/core/SkMatrixPriv.h" 14#include "src/gpu/BufferWriter.h" 15#include "src/gpu/GrGeometryProcessor.h" 16#include "src/gpu/GrGpu.h" 17#include "src/gpu/GrOpFlushState.h" 18#include "src/gpu/GrProgramInfo.h" 19#include "src/gpu/GrResourceProvider.h" 20#include "src/gpu/GrResourceProviderPriv.h" 21#include "src/gpu/SkGr.h" 22#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h" 23#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 24#include "src/gpu/glsl/GrGLSLVarying.h" 25#include "src/gpu/ops/GrMeshDrawOp.h" 26#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" 27 28namespace skgpu::v1::LatticeOp { 29 30namespace { 31 32class LatticeGP : public GrGeometryProcessor { 33public: 34 static GrGeometryProcessor* Make(SkArenaAlloc* arena, 35 const GrSurfaceProxyView& view, 36 sk_sp<GrColorSpaceXform> csxf, 37 GrSamplerState::Filter filter, 38 bool wideColor) { 39 return arena->make([&](void* ptr) { 40 return new (ptr) LatticeGP(view, std::move(csxf), filter, wideColor); 41 }); 42 } 43 44 const char* name() const override { return "LatticeGP"; } 45 46 SkString getShaderDfxInfo() const override { 47 SkString format; 48 format.printf("ShaderDfx_LatticeGP_%d", GrColorSpaceXform::XformKey(fColorSpaceXform.get())); 49 return format; 50 } 51 52 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { 53 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get())); 54 } 55 56 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override { 57 class Impl : public ProgramImpl { 58 public: 59 void setData(const GrGLSLProgramDataManager& pdman, 60 const GrShaderCaps&, 61 const GrGeometryProcessor& geomProc) override { 62 const auto& latticeGP = geomProc.cast<LatticeGP>(); 63 fColorSpaceXformHelper.setData(pdman, latticeGP.fColorSpaceXform.get()); 64 } 65 66 private: 67 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 68 using Interpolation = GrGLSLVaryingHandler::Interpolation; 69 const auto& latticeGP = args.fGeomProc.cast<LatticeGP>(); 70 fColorSpaceXformHelper.emitCode(args.fUniformHandler, 71 latticeGP.fColorSpaceXform.get()); 72 73 args.fVaryingHandler->emitAttributes(latticeGP); 74 WriteOutputPosition(args.fVertBuilder, gpArgs, latticeGP.fInPosition.name()); 75 gpArgs->fLocalCoordVar = latticeGP.fInTextureCoords.asShaderVar(); 76 77 args.fFragBuilder->codeAppend("float2 textureCoords;"); 78 args.fVaryingHandler->addPassThroughAttribute( 79 latticeGP.fInTextureCoords.asShaderVar(), 80 "textureCoords"); 81 args.fFragBuilder->codeAppend("float4 textureDomain;"); 82 args.fVaryingHandler->addPassThroughAttribute( 83 latticeGP.fInTextureDomain.asShaderVar(), 84 "textureDomain", 85 Interpolation::kCanBeFlat); 86 args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor); 87 args.fVaryingHandler->addPassThroughAttribute(latticeGP.fInColor.asShaderVar(), 88 args.fOutputColor, 89 Interpolation::kCanBeFlat); 90 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor); 91 args.fFragBuilder->appendTextureLookupAndBlend( 92 args.fOutputColor, 93 SkBlendMode::kModulate, 94 args.fTexSamplers[0], 95 "clamp(textureCoords, textureDomain.xy, textureDomain.zw)", 96 &fColorSpaceXformHelper); 97 args.fFragBuilder->codeAppend(";"); 98 args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage); 99 } 100 101 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper; 102 }; 103 104 return std::make_unique<Impl>(); 105 } 106 107private: 108 LatticeGP(const GrSurfaceProxyView& view, sk_sp<GrColorSpaceXform> csxf, 109 GrSamplerState::Filter filter, bool wideColor) 110 : INHERITED(kLatticeGP_ClassID) 111 , fColorSpaceXform(std::move(csxf)) { 112 113 fSampler.reset(GrSamplerState(GrSamplerState::WrapMode::kClamp, filter), 114 view.proxy()->backendFormat(), view.swizzle()); 115 this->setTextureSamplerCnt(1); 116 fInPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; 117 fInTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; 118 fInTextureDomain = {"textureDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType}; 119 fInColor = MakeColorAttribute("color", wideColor); 120 this->setVertexAttributes(&fInPosition, 4); 121 } 122 123 const TextureSampler& onTextureSampler(int) const override { return fSampler; } 124 125 Attribute fInPosition; 126 Attribute fInTextureCoords; 127 Attribute fInTextureDomain; 128 Attribute fInColor; 129 130 sk_sp<GrColorSpaceXform> fColorSpaceXform; 131 TextureSampler fSampler; 132 133 using INHERITED = GrGeometryProcessor; 134}; 135 136class NonAALatticeOp final : public GrMeshDrawOp { 137private: 138 using Helper = GrSimpleMeshDrawOpHelper; 139 140public: 141 DEFINE_OP_CLASS_ID 142 143 static GrOp::Owner Make(GrRecordingContext* context, 144 GrPaint&& paint, 145 const SkMatrix& viewMatrix, 146 GrSurfaceProxyView view, 147 SkAlphaType alphaType, 148 sk_sp<GrColorSpaceXform> colorSpaceXForm, 149 GrSamplerState::Filter filter, 150 std::unique_ptr<SkLatticeIter> iter, 151 const SkRect& dst) { 152 SkASSERT(view.proxy()); 153 return Helper::FactoryHelper<NonAALatticeOp>(context, std::move(paint), viewMatrix, 154 std::move(view), alphaType, 155 std::move(colorSpaceXForm), filter, 156 std::move(iter), dst); 157 } 158 159 NonAALatticeOp(GrProcessorSet* processorSet, const SkPMColor4f& color, 160 const SkMatrix& viewMatrix, GrSurfaceProxyView view, 161 SkAlphaType alphaType, sk_sp<GrColorSpaceXform> colorSpaceXform, 162 GrSamplerState::Filter filter, std::unique_ptr<SkLatticeIter> iter, 163 const SkRect& dst) 164 : INHERITED(ClassID()) 165 , fHelper(processorSet, GrAAType::kNone) 166 , fView(std::move(view)) 167 , fAlphaType(alphaType) 168 , fColorSpaceXform(std::move(colorSpaceXform)) 169 , fFilter(filter) { 170 Patch& patch = fPatches.push_back(); 171 patch.fViewMatrix = viewMatrix; 172 patch.fColor = color; 173 patch.fIter = std::move(iter); 174 patch.fDst = dst; 175 176 // setup bounds 177 this->setTransformedBounds(patch.fDst, viewMatrix, HasAABloat::kNo, IsHairline::kNo); 178 } 179 180 const char* name() const override { return "NonAALatticeOp"; } 181 182 void visitProxies(const GrVisitProxyFunc& func) const override { 183 func(fView.proxy(), GrMipmapped::kNo); 184 if (fProgramInfo) { 185 fProgramInfo->visitFPProxies(func); 186 } else { 187 fHelper.visitProxies(func); 188 } 189 } 190 191 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 192 193 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip, 194 GrClampType clampType) override { 195 auto opaque = fPatches[0].fColor.isOpaque() && fAlphaType == kOpaque_SkAlphaType 196 ? GrProcessorAnalysisColor::Opaque::kYes 197 : GrProcessorAnalysisColor::Opaque::kNo; 198 auto analysisColor = GrProcessorAnalysisColor(opaque); 199 auto result = fHelper.finalizeProcessors(caps, clip, clampType, 200 GrProcessorAnalysisCoverage::kNone, 201 &analysisColor); 202 analysisColor.isConstant(&fPatches[0].fColor); 203 fWideColor = !fPatches[0].fColor.fitsInBytes(); 204 return result; 205 } 206 207private: 208 GrProgramInfo* programInfo() override { return fProgramInfo; } 209 210 void onCreateProgramInfo(const GrCaps* caps, 211 SkArenaAlloc* arena, 212 const GrSurfaceProxyView& writeView, 213 bool usesMSAASurface, 214 GrAppliedClip&& appliedClip, 215 const GrDstProxyView& dstProxyView, 216 GrXferBarrierFlags renderPassXferBarriers, 217 GrLoadOp colorLoadOp) override { 218 219 auto gp = LatticeGP::Make(arena, fView, fColorSpaceXform, fFilter, fWideColor); 220 if (!gp) { 221 return; 222 } 223 224 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView, 225 usesMSAASurface, 226 std::move(appliedClip), 227 dstProxyView, gp, 228 fHelper.detachProcessorSet(), 229 GrPrimitiveType::kTriangles, 230 renderPassXferBarriers, 231 colorLoadOp, 232 fHelper.pipelineFlags(), 233 &GrUserStencilSettings::kUnused); 234 } 235 236 void onPrepareDraws(GrMeshDrawTarget* target) override { 237 if (!fProgramInfo) { 238 this->createProgramInfo(target); 239 if (!fProgramInfo) { 240 return; 241 } 242 } 243 244 int patchCnt = fPatches.count(); 245 int numRects = 0; 246 for (int i = 0; i < patchCnt; i++) { 247 numRects += fPatches[i].fIter->numRectsToDraw(); 248 } 249 250 if (!numRects) { 251 return; 252 } 253 254 const size_t kVertexStride = fProgramInfo->geomProc().vertexStride(); 255 256 QuadHelper helper(target, kVertexStride, numRects); 257 258 VertexWriter vertices{helper.vertices()}; 259 if (!vertices) { 260 SkDebugf("Could not allocate vertices\n"); 261 return; 262 } 263 264 for (int i = 0; i < patchCnt; i++) { 265 const Patch& patch = fPatches[i]; 266 267 GrVertexColor patchColor(patch.fColor, fWideColor); 268 269 // Apply the view matrix here if it is scale-translate. Otherwise, we need to 270 // wait until we've created the dst rects. 271 bool isScaleTranslate = patch.fViewMatrix.isScaleTranslate(); 272 if (isScaleTranslate) { 273 patch.fIter->mapDstScaleTranslate(patch.fViewMatrix); 274 } 275 276 SkIRect srcR; 277 SkRect dstR; 278 Sk4f scales(1.f / fView.proxy()->width(), 1.f / fView.proxy()->height(), 279 1.f / fView.proxy()->width(), 1.f / fView.proxy()->height()); 280 static const Sk4f kDomainOffsets(0.5f, 0.5f, -0.5f, -0.5f); 281 static const Sk4f kFlipOffsets(0.f, 1.f, 0.f, 1.f); 282 static const Sk4f kFlipMuls(1.f, -1.f, 1.f, -1.f); 283 while (patch.fIter->next(&srcR, &dstR)) { 284 Sk4f coords(SkIntToScalar(srcR.fLeft), SkIntToScalar(srcR.fTop), 285 SkIntToScalar(srcR.fRight), SkIntToScalar(srcR.fBottom)); 286 Sk4f domain = coords + kDomainOffsets; 287 coords *= scales; 288 domain *= scales; 289 if (fView.origin() == kBottomLeft_GrSurfaceOrigin) { 290 coords = kFlipMuls * coords + kFlipOffsets; 291 domain = SkNx_shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets); 292 } 293 SkRect texDomain; 294 SkRect texCoords; 295 domain.store(&texDomain); 296 coords.store(&texCoords); 297 298 if (isScaleTranslate) { 299 vertices.writeQuad(VertexWriter::TriStripFromRect(dstR), 300 VertexWriter::TriStripFromRect(texCoords), 301 texDomain, 302 patchColor); 303 } else { 304 SkPoint mappedPts[4]; 305 patch.fViewMatrix.mapRectToQuad(mappedPts, dstR); 306 // In the above if statement, writeQuad writes the corners as: 307 // left-top, left-bottom, right-top, right-bottom. 308 // However, mapRectToQuad returns them in the order: 309 // left-top, right-top, right-bottom, left-bottom 310 // Thus we write out the vertices to match the writeQuad path. 311 vertices << mappedPts[0] 312 << SkPoint::Make(texCoords.fLeft, texCoords.fTop) 313 << texDomain 314 << patchColor; 315 vertices << mappedPts[3] 316 << SkPoint::Make(texCoords.fLeft, texCoords.fBottom) 317 << texDomain 318 << patchColor; 319 vertices << mappedPts[1] 320 << SkPoint::Make(texCoords.fRight, texCoords.fTop) 321 << texDomain 322 << patchColor; 323 vertices << mappedPts[2] 324 << SkPoint::Make(texCoords.fRight, texCoords.fBottom) 325 << texDomain 326 << patchColor; 327 } 328 } 329 } 330 331 fMesh = helper.mesh(); 332 } 333 334 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { 335 if (!fProgramInfo || !fMesh) { 336 return; 337 } 338 339 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); 340 flushState->bindTextures(fProgramInfo->geomProc(), 341 *fView.proxy(), 342 fProgramInfo->pipeline()); 343 flushState->drawMesh(*fMesh); 344 } 345 346 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override { 347 NonAALatticeOp* that = t->cast<NonAALatticeOp>(); 348 if (fView != that->fView) { 349 return CombineResult::kCannotCombine; 350 } 351 if (fFilter != that->fFilter) { 352 return CombineResult::kCannotCombine; 353 } 354 if (GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) { 355 return CombineResult::kCannotCombine; 356 } 357 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 358 return CombineResult::kCannotCombine; 359 } 360 361 fPatches.move_back_n(that->fPatches.count(), that->fPatches.begin()); 362 fWideColor |= that->fWideColor; 363 return CombineResult::kMerged; 364 } 365 366#if GR_TEST_UTILS 367 SkString onDumpInfo() const override { 368 SkString str; 369 370 for (int i = 0; i < fPatches.count(); ++i) { 371 str.appendf("%d: Color: 0x%08x Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 372 fPatches[i].fColor.toBytes_RGBA(), fPatches[i].fDst.fLeft, 373 fPatches[i].fDst.fTop, fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom); 374 } 375 376 str += fHelper.dumpInfo(); 377 return str; 378 } 379#endif 380 381 struct Patch { 382 SkMatrix fViewMatrix; 383 std::unique_ptr<SkLatticeIter> fIter; 384 SkRect fDst; 385 SkPMColor4f fColor; 386 }; 387 388 Helper fHelper; 389 SkSTArray<1, Patch, true> fPatches; 390 GrSurfaceProxyView fView; 391 SkAlphaType fAlphaType; 392 sk_sp<GrColorSpaceXform> fColorSpaceXform; 393 GrSamplerState::Filter fFilter; 394 bool fWideColor; 395 396 GrSimpleMesh* fMesh = nullptr; 397 GrProgramInfo* fProgramInfo = nullptr; 398 399 using INHERITED = GrMeshDrawOp; 400}; 401 402} // anonymous namespace 403 404GrOp::Owner MakeNonAA(GrRecordingContext* context, 405 GrPaint&& paint, 406 const SkMatrix& viewMatrix, 407 GrSurfaceProxyView view, 408 SkAlphaType alphaType, 409 sk_sp<GrColorSpaceXform> colorSpaceXform, 410 GrSamplerState::Filter filter, 411 std::unique_ptr<SkLatticeIter> iter, 412 const SkRect& dst) { 413 return NonAALatticeOp::Make(context, std::move(paint), viewMatrix, std::move(view), alphaType, 414 std::move(colorSpaceXform), filter, std::move(iter), dst); 415} 416 417} // namespace skgpu::v1::LatticeOp 418 419#if GR_TEST_UTILS 420#include "src/gpu/GrDrawOpTest.h" 421#include "src/gpu/GrProxyProvider.h" 422#include "src/gpu/GrRecordingContextPriv.h" 423 424/** Randomly divides subset into count divs. */ 425static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop, 426 SkRandom* random) { 427 // Rules for lattice divs: Must be strictly increasing and in the range 428 // [subsetStart, subsetStop. 429 // Not terribly efficient alg for generating random divs: 430 // 1 Start with minimum legal pixels between each div. 431 // 2) Randomly assign the remaining pixels of the subset to divs. 432 // 3) Convert from pixel counts to div offsets. 433 434 // 1) Initially each divs[i] represents the number of pixels between 435 // div i-1 and i. The initial div is allowed to be at subsetStart. There 436 // must be one pixel spacing between subsequent divs. 437 divs[0] = 0; 438 for (int i = 1; i < count; ++i) { 439 divs[i] = 1; 440 } 441 // 2) Assign the remaining subset pixels to fall 442 int subsetLength = subsetStop - subsetStart; 443 for (int i = 0; i < subsetLength - count; ++i) { 444 // +1 because count divs means count+1 intervals. 445 int entry = random->nextULessThan(count + 1); 446 // We don't have an entry to to store the count after the last div 447 if (entry < count) { 448 divs[entry]++; 449 } 450 } 451 // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset. 452 int offset = subsetStart; 453 for (int i = 0; i < count; ++i) { 454 divs[i] += offset; 455 offset = divs[i]; 456 } 457} 458 459GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) { 460 SkCanvas::Lattice lattice; 461 // We loop because our random lattice code can produce an invalid lattice in the case where 462 // there is a single div separator in both x and y and both are aligned with the left and top 463 // edge of the image subset, respectively. 464 std::unique_ptr<int[]> xdivs; 465 std::unique_ptr<int[]> ydivs; 466 std::unique_ptr<SkCanvas::Lattice::RectType[]> flags; 467 std::unique_ptr<SkColor[]> colors; 468 SkIRect subset; 469 SkISize dims; 470 dims.fWidth = random->nextRangeU(1, 1000); 471 dims.fHeight = random->nextRangeU(1, 1000); 472 GrSurfaceOrigin origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin 473 : kBottomLeft_GrSurfaceOrigin; 474 const GrBackendFormat format = 475 context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, 476 GrRenderable::kNo); 477 478 auto proxy = context->priv().proxyProvider()->createProxy(format, 479 dims, 480 GrRenderable::kNo, 481 1, 482 GrMipmapped::kNo, 483 SkBackingFit::kExact, 484 SkBudgeted::kYes, 485 GrProtected::kNo); 486 487 do { 488 if (random->nextBool()) { 489 subset.fLeft = random->nextULessThan(dims.fWidth); 490 subset.fRight = random->nextRangeU(subset.fLeft + 1, dims.fWidth); 491 subset.fTop = random->nextULessThan(dims.fHeight); 492 subset.fBottom = random->nextRangeU(subset.fTop + 1, dims.fHeight); 493 } else { 494 subset.setXYWH(0, 0, dims.fWidth, dims.fHeight); 495 } 496 // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with 497 // a non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds. 498 lattice.fBounds = ⊂ 499 lattice.fXCount = random->nextRangeU(1, subset.width()); 500 lattice.fYCount = random->nextRangeU(1, subset.height()); 501 xdivs.reset(new int[lattice.fXCount]); 502 ydivs.reset(new int[lattice.fYCount]); 503 init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random); 504 init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random); 505 lattice.fXDivs = xdivs.get(); 506 lattice.fYDivs = ydivs.get(); 507 bool hasFlags = random->nextBool(); 508 if (hasFlags) { 509 int n = (lattice.fXCount + 1) * (lattice.fYCount + 1); 510 flags.reset(new SkCanvas::Lattice::RectType[n]); 511 colors.reset(new SkColor[n]); 512 for (int i = 0; i < n; ++i) { 513 flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent 514 : SkCanvas::Lattice::kDefault; 515 } 516 lattice.fRectTypes = flags.get(); 517 lattice.fColors = colors.get(); 518 } else { 519 lattice.fRectTypes = nullptr; 520 lattice.fColors = nullptr; 521 } 522 } while (!SkLatticeIter::Valid(dims.fWidth, dims.fHeight, lattice)); 523 SkRect dst; 524 dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f); 525 dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f); 526 dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f); 527 dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f); 528 std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst)); 529 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 530 auto csxf = GrTest::TestColorXform(random); 531 GrSamplerState::Filter filter = 532 random->nextBool() ? GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kLinear; 533 534 GrSurfaceProxyView view( 535 std::move(proxy), origin, 536 context->priv().caps()->getReadSwizzle(format, GrColorType::kRGBA_8888)); 537 538 return skgpu::v1::LatticeOp::NonAALatticeOp::Make(context, std::move(paint), viewMatrix, 539 std::move(view), kPremul_SkAlphaType, 540 std::move(csxf), filter, std::move(iter), 541 dst); 542} 543 544#endif 545