blob: f8026e816a3ddf043b4d691296729c44543b4916 [file] [log] [blame]
Robert Phillipseb35f4d2017-03-21 07:56:47 -04001/*
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
8#include "Test.h"
9
10#if SK_SUPPORT_GPU
11
12#include "GrClip.h"
13#include "GrContextPriv.h"
14#include "GrDefaultGeoProcFactory.h"
Chris Daltonfe199b72017-05-05 11:26:15 -040015#include "GrOnFlushResourceProvider.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040016#include "GrRenderTargetContextPriv.h"
17#include "GrResourceProvider.h"
18#include "GrQuad.h"
19#include "effects/GrSimpleTextureEffect.h"
20#include "ops/GrTestMeshDrawOp.h"
21
22// This is a simplified mesh drawing op that can be used in the atlas generation test.
23// Please see AtlasedRectOp below.
Brian Salomond3ccb0a2017-04-03 10:38:00 -040024class NonAARectOp : public GrLegacyMeshDrawOp {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040025public:
26 DEFINE_OP_CLASS_ID
27 const char* name() const override { return "NonAARectOp"; }
28
29 // This creates an instance of a simple non-AA solid color rect-drawing Op
30 static std::unique_ptr<GrDrawOp> Make(const SkRect& r, GrColor color) {
31 return std::unique_ptr<GrDrawOp>(new NonAARectOp(ClassID(), r, color));
32 }
33
34 // This creates an instance of a simple non-AA textured rect-drawing Op
35 static std::unique_ptr<GrDrawOp> Make(const SkRect& r, GrColor color, const SkRect& local) {
36 return std::unique_ptr<GrDrawOp>(new NonAARectOp(ClassID(), r, color, local));
37 }
38
39 GrColor color() const { return fColor; }
40
41protected:
42 NonAARectOp(uint32_t classID, const SkRect& r, GrColor color)
43 : INHERITED(classID)
44 , fColor(color)
45 , fHasLocalRect(false)
46 , fRect(r) {
47 // Choose some conservative values for aa bloat and zero area.
48 this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
49 }
50
51 NonAARectOp(uint32_t classID, const SkRect& r, GrColor color, const SkRect& local)
52 : INHERITED(classID)
53 , fColor(color)
54 , fHasLocalRect(true)
55 , fLocalQuad(local)
56 , fRect(r) {
57 // Choose some conservative values for aa bloat and zero area.
58 this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
59 }
60
61 GrColor fColor;
62 bool fHasLocalRect;
63 GrQuad fLocalQuad;
64 SkRect fRect;
65
66private:
Brian Salomona811b122017-03-30 08:21:32 -040067 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
68 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -040069 color->setToUnknown();
Brian Salomona811b122017-03-30 08:21:32 -040070 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Robert Phillipseb35f4d2017-03-21 07:56:47 -040071 }
72
Brian Salomone7d30482017-03-29 12:09:15 -040073 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040074 optimizations.getOverrideColorIfSet(&fColor);
75 }
76
77 bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
78
79 void onPrepareDraws(Target* target) const override {
80 using namespace GrDefaultGeoProcFactory;
81
82 // The vertex attrib order is always pos, color, local coords.
83 static const int kColorOffset = sizeof(SkPoint);
84 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
85
86 sk_sp<GrGeometryProcessor> gp =
87 GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
88 Coverage::kSolid_Type,
89 fHasLocalRect ? LocalCoords::kHasExplicit_Type
90 : LocalCoords::kUnused_Type,
91 SkMatrix::I());
92 if (!gp) {
93 SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
94 return;
95 }
96
97 size_t vertexStride = gp->getVertexStride();
98
99 SkASSERT(fHasLocalRect
100 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
101 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
102
103 const GrBuffer* indexBuffer;
104 int firstIndex;
105 uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
106 if (!indices) {
107 SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
108 return;
109 }
110
111 const GrBuffer* vertexBuffer;
112 int firstVertex;
113 void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
114 if (!vertices) {
115 SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
116 return;
117 }
118
119 // Setup indices
120 indices[0] = 0;
121 indices[1] = 1;
122 indices[2] = 2;
123 indices[3] = 0;
124 indices[4] = 2;
125 indices[5] = 3;
126
127 // Setup positions
128 SkPoint* position = (SkPoint*) vertices;
129 position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride);
130
131 // Setup vertex colors
132 GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
133 for (int i = 0; i < 4; ++i) {
134 *color = fColor;
135 color = (GrColor*)((intptr_t)color + vertexStride);
136 }
137
138 // Setup local coords
139 if (fHasLocalRect) {
140 SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
141 for (int i = 0; i < 4; i++) {
142 *coords = fLocalQuad.point(i);
143 coords = (SkPoint*)((intptr_t) coords + vertexStride);
144 }
145 }
146
Chris Daltonbca46e22017-05-15 11:03:26 -0600147 GrMesh mesh(kTriangles_GrPrimitiveType);
Chris Dalton114a3c02017-05-26 15:17:19 -0600148 mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3);
149 mesh.setVertexData(vertexBuffer, firstVertex);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400150
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400151 target->draw(gp.get(), this->pipeline(), mesh);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400152 }
153
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400154 typedef GrLegacyMeshDrawOp INHERITED;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400155};
156
157#ifdef SK_DEBUG
158#include "SkImageEncoder.h"
159#include "sk_tool_utils.h"
160
161static void save_bm(const SkBitmap& bm, const char name[]) {
162 bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
163 SkASSERT(result);
164}
165#endif
166
167/*
168 * Atlased ops just draw themselves as textured rects with the texture pixels being
169 * pulled out of the atlas. Their color is based on their ID.
170 */
171class AtlasedRectOp final : public NonAARectOp {
172public:
173 DEFINE_OP_CLASS_ID
174
175 ~AtlasedRectOp() override {
176 fID = -1;
177 }
178
179 const char* name() const override { return "AtlasedRectOp"; }
180
181 int id() const { return fID; }
182
183 static std::unique_ptr<AtlasedRectOp> Make(const SkRect& r, int id) {
184 return std::unique_ptr<AtlasedRectOp>(new AtlasedRectOp(r, id));
185 }
186
187 void setColor(GrColor color) { fColor = color; }
188 void setLocalRect(const SkRect& localRect) {
189 SkASSERT(fHasLocalRect); // This should've been created to anticipate this
190 fLocalQuad.set(localRect);
191 }
192
193 AtlasedRectOp* next() const { return fNext; }
194 void setNext(AtlasedRectOp* next) {
195 fNext = next;
196 }
197
198private:
199 // We set the initial color of the NonAARectOp based on the ID.
200 // Note that we force creation of a NonAARectOp that has local coords in anticipation of
201 // pulling from the atlas.
202 AtlasedRectOp(const SkRect& r, int id)
203 : INHERITED(ClassID(), r, kColors[id], SkRect::MakeEmpty())
204 , fID(id)
205 , fNext(nullptr) {
206 SkASSERT(fID < kMaxIDs);
207 }
208
209 static const int kMaxIDs = 9;
210 static const SkColor kColors[kMaxIDs];
211
212 int fID;
213 // The Atlased ops have an internal singly-linked list of ops that land in the same opList
214 AtlasedRectOp* fNext;
215
216 typedef NonAARectOp INHERITED;
217};
218
219const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
220 GrColorPackRGBA(255, 0, 0, 255),
221 GrColorPackRGBA(0, 255, 0, 255),
222 GrColorPackRGBA(0, 0, 255, 255),
223 GrColorPackRGBA(0, 255, 255, 255),
224 GrColorPackRGBA(255, 0, 255, 255),
225 GrColorPackRGBA(255, 255, 0, 255),
226 GrColorPackRGBA(0, 0, 0, 255),
227 GrColorPackRGBA(128, 128, 128, 255),
228 GrColorPackRGBA(255, 255, 255, 255)
229};
230
231static const int kDrawnTileSize = 16;
232
233/*
234 * Rather than performing any rect packing, this atlaser just lays out constant-sized
235 * tiles in an Nx1 row
236 */
237static const int kAtlasTileSize = 2;
238
239/*
240 * This class aggregates the op information required for atlasing
241 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400242class AtlasObject final : public GrOnFlushCallbackObject {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400243public:
244 AtlasObject() : fDone(false) { }
245
246 ~AtlasObject() override {
247 SkASSERT(fDone);
248 }
249
250 void markAsDone() {
251 fDone = true;
252 }
253
254 // Insert the new op in an internal singly-linked list for 'opListID'
255 void addOp(uint32_t opListID, AtlasedRectOp* op) {
256 LinkedListHeader* header = nullptr;
257 for (int i = 0; i < fOps.count(); ++i) {
258 if (opListID == fOps[i].fID) {
259 header = &(fOps[i]);
260 }
261 }
262
263 if (!header) {
264 fOps.push({opListID, nullptr});
265 header = &(fOps[fOps.count()-1]);
266 }
267
268 op->setNext(header->fHead);
269 header->fHead = op;
270 }
271
272 // For the time being we need to pre-allocate the atlas.
273 void setAtlasDest(sk_sp<GrTextureProxy> atlasDest) {
274 fAtlasDest = atlasDest;
275 }
276
277 void saveRTC(sk_sp<GrRenderTargetContext> rtc) {
278 SkASSERT(!fRTC);
279 fRTC = rtc;
280 }
281
282#ifdef SK_DEBUG
283 void saveAtlasToDisk() {
284 SkBitmap readBack;
285 readBack.allocN32Pixels(fRTC->width(), fRTC->height());
286
287 bool result = fRTC->readPixels(readBack.info(),
288 readBack.getPixels(), readBack.rowBytes(), 0, 0);
289 SkASSERT(result);
290 save_bm(readBack, "atlas-real.png");
291 }
292#endif
293
294 /*
295 * This callback back creates the atlas and updates the AtlasedRectOps to read from it
296 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400297 void preFlush(GrOnFlushResourceProvider* resourceProvider,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400298 const uint32_t* opListIDs, int numOpListIDs,
299 SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
300 SkASSERT(!results->count());
301
302 // Until MDB is landed we will most-likely only have one opList.
303 SkTDArray<LinkedListHeader*> lists;
304 for (int i = 0; i < numOpListIDs; ++i) {
305 if (LinkedListHeader* list = this->getList(opListIDs[i])) {
306 lists.push(list);
307 }
308 }
309
310 if (!lists.count()) {
311 return; // nothing to atlas
312 }
313
314 // TODO: right now we have to pre-allocate the atlas bc the TextureSamplers need a
315 // hard GrTexture
316#if 0
317 GrSurfaceDesc desc;
318 desc.fFlags = kRenderTarget_GrSurfaceFlag;
319 desc.fWidth = this->numOps() * kAtlasTileSize;
320 desc.fHeight = kAtlasTileSize;
321 desc.fConfig = kRGBA_8888_GrPixelConfig;
322
323 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(desc,
324 nullptr,
325 nullptr);
326#else
327 // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
328 // there should either be two writes to clear it or no writes.
329 SkASSERT(9 == fAtlasDest->getPendingReadCnt_TestOnly());
330 SkASSERT(2 == fAtlasDest->getPendingWriteCnt_TestOnly() ||
331 0 == fAtlasDest->getPendingWriteCnt_TestOnly());
332 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
333 fAtlasDest,
334 nullptr, nullptr);
335#endif
336
337 rtc->clear(nullptr, 0xFFFFFFFF, true); // clear the atlas
338
339 int blocksInAtlas = 0;
340 for (int i = 0; i < lists.count(); ++i) {
341 for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
342 SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
343 kAtlasTileSize, kAtlasTileSize);
344
345 // For now, we avoid the resource buffer issues and just use clears
346#if 1
347 rtc->clear(&r, op->color(), false);
348#else
349 std::unique_ptr<GrDrawOp> drawOp(GrNonAARectOp::Make(SkRect::Make(r),
350 atlasedOp->color()));
351
352 GrPaint paint;
353 rtc->priv().testingOnly_addDrawOp(std::move(paint),
354 GrAAType::kNone,
355 std::move(drawOp));
356#endif
357 blocksInAtlas++;
358
359 // Set the atlased Op's color to white (so we know we're not using it for
360 // the final draw).
361 op->setColor(0xFFFFFFFF);
362
363 // Set the atlased Op's localRect to point to where it landed in the atlas
364 op->setLocalRect(SkRect::Make(r));
365
366 // TODO: we also need to set the op's GrSuperDeferredSimpleTextureEffect to point
367 // to the rtc's proxy!
368 }
369
370 // We've updated all these ops and we certainly don't want to process them again
371 this->clearOpsFor(lists[i]);
372 }
373
374 // Hide a ref to the RTC in AtlasData so we can check on it later
375 this->saveRTC(rtc);
376
377 results->push_back(std::move(rtc));
378 }
379
380private:
381 typedef struct {
382 uint32_t fID;
383 AtlasedRectOp* fHead;
384 } LinkedListHeader;
385
386 LinkedListHeader* getList(uint32_t opListID) {
387 for (int i = 0; i < fOps.count(); ++i) {
388 if (opListID == fOps[i].fID) {
389 return &(fOps[i]);
390 }
391 }
392 return nullptr;
393 }
394
395 void clearOpsFor(LinkedListHeader* header) {
396 // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
397 // forget about them in the laziest way possible.
398 header->fHead = nullptr;
399 header->fID = 0; // invalid opList ID
400 }
401
402 // Each opList containing AtlasedRectOps gets its own internal singly-linked list
403 SkTDArray<LinkedListHeader> fOps;
404
405 // The RTC used to create the atlas
406 sk_sp<GrRenderTargetContext> fRTC;
407
408 // For the time being we need to pre-allocate the atlas bc the TextureSamplers require
409 // a GrTexture
410 sk_sp<GrTextureProxy> fAtlasDest;
411
412 // Set to true when the testing harness expects this object to be no longer used
413 bool fDone;
414};
415
416// This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
417static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
418 sk_sp<GrTextureProxy> fakeAtlas) {
419
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400420 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
421 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400422 3*kDrawnTileSize,
423 kDrawnTileSize,
424 kRGBA_8888_GrPixelConfig,
425 nullptr));
426
427 rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255), true);
428
429 for (int i = 0; i < 3; ++i) {
430 SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
431
432 std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(r, start+i));
433
434 // TODO: here is the blocker for deferring creation of the atlas. The TextureSamplers
435 // created here currently require a hard GrTexture.
436 sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(context->resourceProvider(),
437 fakeAtlas,
438 nullptr, SkMatrix::I());
439
440 GrPaint paint;
441 paint.addColorFragmentProcessor(std::move(fp));
442 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
443
444 AtlasedRectOp* sparePtr = op.get();
445
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400446 uint32_t opListID = rtc->priv().testingOnly_addLegacyMeshDrawOp(
447 std::move(paint), GrAAType::kNone, std::move(op));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400448
449 object->addOp(opListID, sparePtr);
450 }
451
452 return rtc->asTextureProxyRef();
453}
454
455// Enable this if you want to debug the final draws w/o having the atlasCallback create the
456// atlas
457#if 0
458#include "SkGrPriv.h"
459
460sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
461 SkBitmap bm;
462 bm.allocN32Pixels(18, 2, true);
463 bm.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 2, 2));
464 bm.erase(SK_ColorGREEN, SkIRect::MakeXYWH(2, 0, 2, 2));
465 bm.erase(SK_ColorBLUE, SkIRect::MakeXYWH(4, 0, 2, 2));
466 bm.erase(SK_ColorCYAN, SkIRect::MakeXYWH(6, 0, 2, 2));
467 bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
468 bm.erase(SK_ColorYELLOW, SkIRect::MakeXYWH(10, 0, 2, 2));
469 bm.erase(SK_ColorBLACK, SkIRect::MakeXYWH(12, 0, 2, 2));
470 bm.erase(SK_ColorGRAY, SkIRect::MakeXYWH(14, 0, 2, 2));
471 bm.erase(SK_ColorWHITE, SkIRect::MakeXYWH(16, 0, 2, 2));
472
473#if 1
474 save_bm(bm, "atlas-fake.png");
475#endif
476
477 GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
478 desc.fFlags |= kRenderTarget_GrSurfaceFlag;
479
480 sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
481 context->textureProvider(),
482 desc, SkBudgeted::kYes,
483 bm.getPixels(), bm.rowBytes());
484
485 return sk_ref_sp(tmp->asTextureProxy());
486}
487#else
488// TODO: this is unfortunate and must be removed. We want the atlas to be created later.
489sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
490 GrSurfaceDesc desc;
491 desc.fFlags = kRenderTarget_GrSurfaceFlag;
492 desc.fConfig = kSkia8888_GrPixelConfig;
493 desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
494 desc.fWidth = 32;
495 desc.fHeight = 16;
496 sk_sp<GrSurfaceProxy> atlasDest = GrSurfaceProxy::MakeDeferred(
497 context->resourceProvider(),
498 desc, SkBackingFit::kExact,
499 SkBudgeted::kYes,
500 GrResourceProvider::kNoPendingIO_Flag);
501 return sk_ref_sp(atlasDest->asTextureProxy());
502}
503#endif
504
505static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
506 SkColor readback = bm.getColor(x, kDrawnTileSize/2);
507 REPORTER_ASSERT(reporter, expected == readback);
508 if (expected != readback) {
509 SkDebugf("Color mismatch: %x %x\n", expected, readback);
510 }
511}
512
513/*
514 * For the atlasing test we make a DAG that looks like:
515 *
516 * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8
517 * \ /
518 * \ /
519 * RT4
520 * We then flush RT4 and expect only ops 0-5 to be atlased together.
521 * Each op is just a solid colored rect so both the atlas and the final image should appear as:
522 * R G B C M Y
523 * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
524 *
525 * Note: until MDB lands, the atlas will actually have width= 9*kAtlasTileSize and look like:
526 * R G B C M Y K Grey White
527 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400528DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400529 static const int kNumProxies = 3;
530
531 GrContext* context = ctxInfo.grContext();
532
533 if (context->caps()->useDrawInsteadOfClear()) {
534 // TODO: fix the buffer issues so this can run on all devices
535 return;
536 }
537
Chris Daltonfe199b72017-05-05 11:26:15 -0400538 AtlasObject object;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400539
540 // For now (until we add a GrSuperDeferredSimpleTextureEffect), we create the final atlas
541 // proxy ahead of time.
542 sk_sp<GrTextureProxy> atlasDest = pre_create_atlas(context);
543
Chris Daltonfe199b72017-05-05 11:26:15 -0400544 object.setAtlasDest(atlasDest);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400545
Chris Daltonfe199b72017-05-05 11:26:15 -0400546 context->contextPriv().addOnFlushCallbackObject(&object);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400547
548 sk_sp<GrTextureProxy> proxies[kNumProxies];
549 for (int i = 0; i < kNumProxies; ++i) {
Chris Daltonfe199b72017-05-05 11:26:15 -0400550 proxies[i] = make_upstream_image(context, &object, i*3, atlasDest);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400551 }
552
553 static const int kFinalWidth = 6*kDrawnTileSize;
554 static const int kFinalHeight = kDrawnTileSize;
555
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400556 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
557 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400558 kFinalWidth,
559 kFinalHeight,
560 kRGBA_8888_GrPixelConfig,
561 nullptr));
562
563 rtc->clear(nullptr, 0xFFFFFFFF, true);
564
565 // Note that this doesn't include the third texture proxy
566 for (int i = 0; i < kNumProxies-1; ++i) {
567 SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
568
569 SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
570
571 GrPaint paint;
572 sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(context->resourceProvider(),
573 std::move(proxies[i]),
574 nullptr, t));
575 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
576 paint.addColorFragmentProcessor(std::move(fp));
577
578 rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
579 }
580
581 rtc->prepareForExternalIO();
582
583 SkBitmap readBack;
584 readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
585
586 SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
587 readBack.rowBytes(), 0, 0);
588 SkASSERT(result);
589
Chris Daltonfe199b72017-05-05 11:26:15 -0400590 context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
591
592 object.markAsDone();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400593
594#if 0
595 save_bm(readBack, "atlas-final-image.png");
596 data.saveAtlasToDisk();
597#endif
598
599 int x = kDrawnTileSize/2;
600 test_color(reporter, readBack, x, SK_ColorRED);
601 x += kDrawnTileSize;
602 test_color(reporter, readBack, x, SK_ColorGREEN);
603 x += kDrawnTileSize;
604 test_color(reporter, readBack, x, SK_ColorBLUE);
605 x += kDrawnTileSize;
606 test_color(reporter, readBack, x, SK_ColorCYAN);
607 x += kDrawnTileSize;
608 test_color(reporter, readBack, x, SK_ColorMAGENTA);
609 x += kDrawnTileSize;
610 test_color(reporter, readBack, x, SK_ColorYELLOW);
611}
612
613#endif