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