OnFlushCallbackTest.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. * Copyright 2017 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. #include "tests/Test.h"
  8. #include "include/core/SkBitmap.h"
  9. #include "include/gpu/GrBackendSemaphore.h"
  10. #include "include/gpu/GrTexture.h"
  11. #include "src/core/SkPointPriv.h"
  12. #include "src/gpu/GrClip.h"
  13. #include "src/gpu/GrContextPriv.h"
  14. #include "src/gpu/GrDefaultGeoProcFactory.h"
  15. #include "src/gpu/GrOnFlushResourceProvider.h"
  16. #include "src/gpu/GrProxyProvider.h"
  17. #include "src/gpu/GrRenderTargetContextPriv.h"
  18. #include "src/gpu/GrResourceProvider.h"
  19. #include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
  20. #include "src/gpu/geometry/GrQuad.h"
  21. #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
  22. namespace {
  23. // This is a simplified mesh drawing op that can be used in the atlas generation test.
  24. // Please see AtlasedRectOp below.
  25. class NonAARectOp : public GrMeshDrawOp {
  26. protected:
  27. using Helper = GrSimpleMeshDrawOpHelper;
  28. public:
  29. DEFINE_OP_CLASS_ID
  30. // This creates an instance of a simple non-AA solid color rect-drawing Op
  31. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  32. GrPaint&& paint,
  33. const SkRect& r) {
  34. return Helper::FactoryHelper<NonAARectOp>(context, std::move(paint), r, nullptr, ClassID());
  35. }
  36. // This creates an instance of a simple non-AA textured rect-drawing Op
  37. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  38. GrPaint&& paint,
  39. const SkRect& r,
  40. const SkRect& local) {
  41. return Helper::FactoryHelper<NonAARectOp>(context, std::move(paint), r, &local, ClassID());
  42. }
  43. const SkPMColor4f& color() const { return fColor; }
  44. NonAARectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkRect& r,
  45. const SkRect* localRect, int32_t classID)
  46. : INHERITED(classID)
  47. , fColor(color)
  48. , fHasLocalRect(SkToBool(localRect))
  49. , fRect(r)
  50. , fHelper(helperArgs, GrAAType::kNone) {
  51. if (fHasLocalRect) {
  52. fLocalQuad = GrQuad(*localRect);
  53. }
  54. // Choose some conservative values for aa bloat and zero area.
  55. this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
  56. }
  57. const char* name() const override { return "NonAARectOp"; }
  58. void visitProxies(const VisitProxyFunc& func) const override {
  59. fHelper.visitProxies(func);
  60. }
  61. FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
  62. GrProcessorSet::Analysis finalize(
  63. const GrCaps& caps, const GrAppliedClip*, bool hasMixedSampledCoverage,
  64. GrClampType clampType) override {
  65. // Set the color to unknown because the subclass may change the color later.
  66. GrProcessorAnalysisColor gpColor;
  67. gpColor.setToUnknown();
  68. // We ignore the clip so pass this rather than the GrAppliedClip param.
  69. static GrAppliedClip kNoClip;
  70. return fHelper.finalizeProcessors(caps, &kNoClip, hasMixedSampledCoverage, clampType,
  71. GrProcessorAnalysisCoverage::kNone, &gpColor);
  72. }
  73. protected:
  74. SkPMColor4f fColor;
  75. bool fHasLocalRect;
  76. GrQuad fLocalQuad;
  77. SkRect fRect;
  78. private:
  79. void onPrepareDraws(Target* target) override {
  80. using namespace GrDefaultGeoProcFactory;
  81. // The vertex attrib order is always pos, color, local coords.
  82. static const int kColorOffset = sizeof(SkPoint);
  83. static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
  84. sk_sp<GrGeometryProcessor> gp =
  85. GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
  86. Color::kPremulGrColorAttribute_Type,
  87. Coverage::kSolid_Type,
  88. fHasLocalRect ? LocalCoords::kHasExplicit_Type
  89. : LocalCoords::kUnused_Type,
  90. SkMatrix::I());
  91. if (!gp) {
  92. SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
  93. return;
  94. }
  95. size_t vertexStride = gp->vertexStride();
  96. sk_sp<const GrBuffer> indexBuffer;
  97. int firstIndex;
  98. uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
  99. if (!indices) {
  100. SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
  101. return;
  102. }
  103. sk_sp<const GrBuffer> vertexBuffer;
  104. int firstVertex;
  105. void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
  106. if (!vertices) {
  107. SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
  108. return;
  109. }
  110. // Setup indices
  111. indices[0] = 0;
  112. indices[1] = 1;
  113. indices[2] = 2;
  114. indices[3] = 2;
  115. indices[4] = 1;
  116. indices[5] = 3;
  117. // Setup positions
  118. SkPoint* position = (SkPoint*) vertices;
  119. SkPointPriv::SetRectTriStrip(position, fRect, vertexStride);
  120. // Setup vertex colors
  121. GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
  122. for (int i = 0; i < 4; ++i) {
  123. *color = fColor.toBytes_RGBA();
  124. color = (GrColor*)((intptr_t)color + vertexStride);
  125. }
  126. // Setup local coords
  127. if (fHasLocalRect) {
  128. SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
  129. for (int i = 0; i < 4; i++) {
  130. *coords = fLocalQuad.point(i);
  131. coords = (SkPoint*)((intptr_t) coords + vertexStride);
  132. }
  133. }
  134. GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
  135. mesh->setIndexed(indexBuffer, 6, firstIndex, 0, 3, GrPrimitiveRestart::kNo);
  136. mesh->setVertexData(vertexBuffer, firstVertex);
  137. target->recordDraw(std::move(gp), mesh);
  138. }
  139. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  140. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  141. }
  142. Helper fHelper;
  143. typedef GrMeshDrawOp INHERITED;
  144. };
  145. } // anonymous namespace
  146. static constexpr SkRect kEmptyRect = SkRect::MakeEmpty();
  147. namespace {
  148. /*
  149. * Atlased ops just draw themselves as textured rects with the texture pixels being
  150. * pulled out of the atlas. Their color is based on their ID.
  151. */
  152. class AtlasedRectOp final : public NonAARectOp {
  153. public:
  154. DEFINE_OP_CLASS_ID
  155. ~AtlasedRectOp() override {
  156. fID = -1;
  157. }
  158. const char* name() const override { return "AtlasedRectOp"; }
  159. int id() const { return fID; }
  160. static std::unique_ptr<AtlasedRectOp> Make(GrContext* context,
  161. GrPaint&& paint,
  162. const SkRect& r,
  163. int id) {
  164. GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(context, std::move(paint),
  165. r, id).release();
  166. return std::unique_ptr<AtlasedRectOp>(static_cast<AtlasedRectOp*>(op));
  167. }
  168. // We set the initial color of the NonAARectOp based on the ID.
  169. // Note that we force creation of a NonAARectOp that has local coords in anticipation of
  170. // pulling from the atlas.
  171. AtlasedRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, const SkRect& r,
  172. int id)
  173. : INHERITED(helperArgs, SkPMColor4f::FromBytes_RGBA(kColors[id]), r, &kEmptyRect,
  174. ClassID())
  175. , fID(id)
  176. , fNext(nullptr) {
  177. SkASSERT(fID < kMaxIDs);
  178. }
  179. void setColor(const SkPMColor4f& color) { fColor = color; }
  180. void setLocalRect(const SkRect& localRect) {
  181. SkASSERT(fHasLocalRect); // This should've been created to anticipate this
  182. fLocalQuad = GrQuad(localRect);
  183. }
  184. AtlasedRectOp* next() const { return fNext; }
  185. void setNext(AtlasedRectOp* next) {
  186. fNext = next;
  187. }
  188. private:
  189. static const int kMaxIDs = 9;
  190. static const GrColor kColors[kMaxIDs];
  191. int fID;
  192. // The Atlased ops have an internal singly-linked list of ops that land in the same opList
  193. AtlasedRectOp* fNext;
  194. typedef NonAARectOp INHERITED;
  195. };
  196. } // anonymous namespace
  197. const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
  198. GrColorPackRGBA(255, 0, 0, 255),
  199. GrColorPackRGBA(0, 255, 0, 255),
  200. GrColorPackRGBA(0, 0, 255, 255),
  201. GrColorPackRGBA(0, 255, 255, 255),
  202. GrColorPackRGBA(255, 0, 255, 255),
  203. GrColorPackRGBA(255, 255, 0, 255),
  204. GrColorPackRGBA(0, 0, 0, 255),
  205. GrColorPackRGBA(128, 128, 128, 255),
  206. GrColorPackRGBA(255, 255, 255, 255)
  207. };
  208. static const int kDrawnTileSize = 16;
  209. /*
  210. * Rather than performing any rect packing, this atlaser just lays out constant-sized
  211. * tiles in an Nx1 row
  212. */
  213. static const int kAtlasTileSize = 2;
  214. /*
  215. * This class aggregates the op information required for atlasing
  216. */
  217. class AtlasObject final : public GrOnFlushCallbackObject {
  218. public:
  219. AtlasObject() : fDone(false) { }
  220. ~AtlasObject() override {
  221. SkASSERT(fDone);
  222. }
  223. void markAsDone() {
  224. fDone = true;
  225. }
  226. // Insert the new op in an internal singly-linked list for 'opListID'
  227. void addOp(uint32_t opListID, AtlasedRectOp* op) {
  228. LinkedListHeader* header = nullptr;
  229. for (int i = 0; i < fOps.count(); ++i) {
  230. if (opListID == fOps[i].fID) {
  231. header = &(fOps[i]);
  232. }
  233. }
  234. if (!header) {
  235. fOps.push_back({opListID, nullptr});
  236. header = &(fOps[fOps.count()-1]);
  237. }
  238. op->setNext(header->fHead);
  239. header->fHead = op;
  240. }
  241. int numOps() const { return fOps.count(); }
  242. // Get the fully lazy proxy that is backing the atlas. Its actual width isn't
  243. // known until flush time.
  244. sk_sp<GrTextureProxy> getAtlasProxy(GrProxyProvider* proxyProvider, const GrCaps* caps) {
  245. if (fAtlasProxy) {
  246. return fAtlasProxy;
  247. }
  248. const GrBackendFormat format = caps->getBackendFormatFromColorType(GrColorType::kRGBA_8888);
  249. fAtlasProxy = GrProxyProvider::MakeFullyLazyProxy(
  250. [](GrResourceProvider* resourceProvider)
  251. -> GrSurfaceProxy::LazyInstantiationResult {
  252. GrSurfaceDesc desc;
  253. // TODO: until partial flushes in MDB lands we're stuck having
  254. // all 9 atlas draws occur
  255. desc.fWidth = 9 /*this->numOps()*/ * kAtlasTileSize;
  256. desc.fHeight = kAtlasTileSize;
  257. desc.fConfig = kRGBA_8888_GrPixelConfig;
  258. auto texture = resourceProvider->createTexture(
  259. desc, GrRenderable::kYes, 1, SkBudgeted::kYes, GrProtected::kNo,
  260. GrResourceProvider::Flags::kNoPendingIO);
  261. return std::move(texture);
  262. },
  263. format,
  264. GrRenderable::kYes,
  265. 1,
  266. GrProtected::kNo,
  267. kBottomLeft_GrSurfaceOrigin,
  268. kRGBA_8888_GrPixelConfig,
  269. *proxyProvider->caps());
  270. fAtlasProxy->priv().setIgnoredByResourceAllocator();
  271. return fAtlasProxy;
  272. }
  273. /*
  274. * This callback creates the atlas and updates the AtlasedRectOps to read from it
  275. */
  276. void preFlush(GrOnFlushResourceProvider* resourceProvider,
  277. const uint32_t* opListIDs, int numOpListIDs,
  278. SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
  279. SkASSERT(!results->count());
  280. // Until MDB is landed we will most-likely only have one opList.
  281. SkTDArray<LinkedListHeader*> lists;
  282. for (int i = 0; i < numOpListIDs; ++i) {
  283. if (LinkedListHeader* list = this->getList(opListIDs[i])) {
  284. lists.push_back(list);
  285. }
  286. }
  287. if (!lists.count()) {
  288. return; // nothing to atlas
  289. }
  290. if (!resourceProvider->instatiateProxy(fAtlasProxy.get())) {
  291. return;
  292. }
  293. // At this point 'fAtlasProxy' should be instantiated and have:
  294. // 1 ref from the 'fAtlasProxy' sk_sp
  295. // 9 refs from the 9 AtlasedRectOps
  296. SkASSERT(10 == fAtlasProxy->priv().getProxyRefCnt());
  297. // The backing GrSurface should have only 1 though bc there is only one proxy
  298. SkASSERT(1 == fAtlasProxy->testingOnly_getBackingRefCnt());
  299. sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
  300. fAtlasProxy, GrColorType::kRGBA_8888, nullptr, nullptr);
  301. // clear the atlas
  302. rtc->clear(nullptr, SK_PMColor4fTRANSPARENT,
  303. GrRenderTargetContext::CanClearFullscreen::kYes);
  304. int blocksInAtlas = 0;
  305. for (int i = 0; i < lists.count(); ++i) {
  306. for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
  307. SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
  308. kAtlasTileSize, kAtlasTileSize);
  309. // For now, we avoid the resource buffer issues and just use clears
  310. #if 1
  311. rtc->clear(&r, op->color(), GrRenderTargetContext::CanClearFullscreen::kNo);
  312. #else
  313. GrPaint paint;
  314. paint.setColor4f(op->color());
  315. std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint),
  316. SkRect::Make(r)));
  317. rtc->priv().testingOnly_addDrawOp(std::move(drawOp));
  318. #endif
  319. blocksInAtlas++;
  320. // Set the atlased Op's color to white (so we know we're not using it for
  321. // the final draw).
  322. op->setColor(SK_PMColor4fWHITE);
  323. // Set the atlased Op's localRect to point to where it landed in the atlas
  324. op->setLocalRect(SkRect::Make(r));
  325. }
  326. // We've updated all these ops and we certainly don't want to process them again
  327. this->clearOpsFor(lists[i]);
  328. }
  329. results->push_back(std::move(rtc));
  330. }
  331. private:
  332. typedef struct {
  333. uint32_t fID;
  334. AtlasedRectOp* fHead;
  335. } LinkedListHeader;
  336. LinkedListHeader* getList(uint32_t opListID) {
  337. for (int i = 0; i < fOps.count(); ++i) {
  338. if (opListID == fOps[i].fID) {
  339. return &(fOps[i]);
  340. }
  341. }
  342. return nullptr;
  343. }
  344. void clearOpsFor(LinkedListHeader* header) {
  345. // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
  346. // forget about them in the laziest way possible.
  347. header->fHead = nullptr;
  348. header->fID = 0; // invalid opList ID
  349. }
  350. // Each opList containing AtlasedRectOps gets its own internal singly-linked list
  351. SkTDArray<LinkedListHeader> fOps;
  352. // The fully lazy proxy for the atlas
  353. sk_sp<GrTextureProxy> fAtlasProxy;
  354. // Set to true when the testing harness expects this object to be no longer used
  355. bool fDone;
  356. };
  357. // This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
  358. static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
  359. sk_sp<GrTextureProxy> atlasProxy) {
  360. sk_sp<GrRenderTargetContext> rtc(
  361. context->priv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
  362. 3*kDrawnTileSize,
  363. kDrawnTileSize,
  364. GrColorType::kRGBA_8888,
  365. nullptr));
  366. rtc->clear(nullptr, { 1, 0, 0, 1 }, GrRenderTargetContext::CanClearFullscreen::kYes);
  367. for (int i = 0; i < 3; ++i) {
  368. SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
  369. auto fp = GrSimpleTextureEffect::Make(atlasProxy, SkMatrix::I());
  370. GrPaint paint;
  371. paint.addColorFragmentProcessor(std::move(fp));
  372. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  373. std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(context,
  374. std::move(paint), r, start + i));
  375. AtlasedRectOp* sparePtr = op.get();
  376. uint32_t opListID;
  377. rtc->priv().testingOnly_addDrawOp(GrNoClip(), std::move(op),
  378. [&opListID](GrOp* op, uint32_t id) { opListID = id; });
  379. SkASSERT(SK_InvalidUniqueID != opListID);
  380. object->addOp(opListID, sparePtr);
  381. }
  382. return rtc->asTextureProxyRef();
  383. }
  384. // Enable this if you want to debug the final draws w/o having the atlasCallback create the
  385. // atlas
  386. #if 0
  387. #include "SkGrPriv.h"
  388. #include "include/core/SkImageEncoder.h"
  389. #include "tools/ToolUtils.h"
  390. static void save_bm(const SkBitmap& bm, const char name[]) {
  391. bool result = ToolUtils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
  392. SkASSERT(result);
  393. }
  394. sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
  395. SkBitmap bm;
  396. bm.allocN32Pixels(18, 2, true);
  397. bm.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 2, 2));
  398. bm.erase(SK_ColorGREEN, SkIRect::MakeXYWH(2, 0, 2, 2));
  399. bm.erase(SK_ColorBLUE, SkIRect::MakeXYWH(4, 0, 2, 2));
  400. bm.erase(SK_ColorCYAN, SkIRect::MakeXYWH(6, 0, 2, 2));
  401. bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
  402. bm.erase(SK_ColorYELLOW, SkIRect::MakeXYWH(10, 0, 2, 2));
  403. bm.erase(SK_ColorBLACK, SkIRect::MakeXYWH(12, 0, 2, 2));
  404. bm.erase(SK_ColorGRAY, SkIRect::MakeXYWH(14, 0, 2, 2));
  405. bm.erase(SK_ColorWHITE, SkIRect::MakeXYWH(16, 0, 2, 2));
  406. #if 1
  407. save_bm(bm, "atlas-fake.png");
  408. #endif
  409. GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info());
  410. desc.fFlags |= kRenderTarget_GrSurfaceFlag;
  411. sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
  412. context->textureProvider(),
  413. desc, SkBudgeted::kYes,
  414. bm.getPixels(), bm.rowBytes());
  415. return sk_ref_sp(tmp->asTextureProxy());
  416. }
  417. #endif
  418. static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
  419. SkColor readback = bm.getColor(x, kDrawnTileSize/2);
  420. REPORTER_ASSERT(reporter, expected == readback);
  421. if (expected != readback) {
  422. SkDebugf("Color mismatch: %x %x\n", expected, readback);
  423. }
  424. }
  425. /*
  426. * For the atlasing test we make a DAG that looks like:
  427. *
  428. * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8
  429. * \ /
  430. * \ /
  431. * RT4
  432. * We then flush RT4 and expect only ops 0-5 to be atlased together.
  433. * Each op is just a solid colored rect so both the atlas and the final image should appear as:
  434. * R G B C M Y
  435. * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
  436. *
  437. * Note: until partial flushes in MDB lands, the atlas will actually have width= 9*kAtlasTileSize
  438. * and look like:
  439. * R G B C M Y K Grey White
  440. */
  441. DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
  442. static const int kNumProxies = 3;
  443. GrContext* context = ctxInfo.grContext();
  444. auto proxyProvider = context->priv().proxyProvider();
  445. AtlasObject object;
  446. context->priv().addOnFlushCallbackObject(&object);
  447. sk_sp<GrTextureProxy> proxies[kNumProxies];
  448. for (int i = 0; i < kNumProxies; ++i) {
  449. proxies[i] = make_upstream_image(context, &object, i*3,
  450. object.getAtlasProxy(proxyProvider,
  451. context->priv().caps()));
  452. }
  453. static const int kFinalWidth = 6*kDrawnTileSize;
  454. static const int kFinalHeight = kDrawnTileSize;
  455. sk_sp<GrRenderTargetContext> rtc(
  456. context->priv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
  457. kFinalWidth,
  458. kFinalHeight,
  459. GrColorType::kRGBA_8888,
  460. nullptr));
  461. rtc->clear(nullptr, SK_PMColor4fWHITE, GrRenderTargetContext::CanClearFullscreen::kYes);
  462. // Note that this doesn't include the third texture proxy
  463. for (int i = 0; i < kNumProxies-1; ++i) {
  464. SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
  465. SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
  466. GrPaint paint;
  467. auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), t);
  468. paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
  469. paint.addColorFragmentProcessor(std::move(fp));
  470. rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
  471. }
  472. rtc->flush(SkSurface::BackendSurfaceAccess::kNoAccess, GrFlushInfo());
  473. SkBitmap readBack;
  474. readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
  475. SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
  476. readBack.rowBytes(), {0, 0});
  477. SkASSERT(result);
  478. context->priv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
  479. object.markAsDone();
  480. int x = kDrawnTileSize/2;
  481. test_color(reporter, readBack, x, SK_ColorRED);
  482. x += kDrawnTileSize;
  483. test_color(reporter, readBack, x, SK_ColorGREEN);
  484. x += kDrawnTileSize;
  485. test_color(reporter, readBack, x, SK_ColorBLUE);
  486. x += kDrawnTileSize;
  487. test_color(reporter, readBack, x, SK_ColorCYAN);
  488. x += kDrawnTileSize;
  489. test_color(reporter, readBack, x, SK_ColorMAGENTA);
  490. x += kDrawnTileSize;
  491. test_color(reporter, readBack, x, SK_ColorYELLOW);
  492. }