blob: e47a76bb52405e97df5c2436602e821ea931095a [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"
Brian Salomon9a036422017-07-13 17:04:43 -040021#include "ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040022
Brian Salomon9a036422017-07-13 17:04:43 -040023namespace {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040024// This is a simplified mesh drawing op that can be used in the atlas generation test.
25// Please see AtlasedRectOp below.
Brian Salomon9a036422017-07-13 17:04:43 -040026class NonAARectOp : public GrMeshDrawOp {
27protected:
28 using Helper = GrSimpleMeshDrawOpHelper;
29
Robert Phillipseb35f4d2017-03-21 07:56:47 -040030public:
31 DEFINE_OP_CLASS_ID
Robert Phillipseb35f4d2017-03-21 07:56:47 -040032
33 // This creates an instance of a simple non-AA solid color rect-drawing Op
Brian Salomon9a036422017-07-13 17:04:43 -040034 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r) {
35 return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, nullptr, ClassID());
Robert Phillipseb35f4d2017-03-21 07:56:47 -040036 }
37
38 // This creates an instance of a simple non-AA textured rect-drawing Op
Brian Salomon9a036422017-07-13 17:04:43 -040039 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r, const SkRect& local) {
40 return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, &local, ClassID());
Robert Phillipseb35f4d2017-03-21 07:56:47 -040041 }
42
43 GrColor color() const { return fColor; }
44
Brian Salomon9a036422017-07-13 17:04:43 -040045 NonAARectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r,
46 const SkRect* localRect, int32_t classID)
47 : INHERITED(classID)
48 , fColor(color)
49 , fHasLocalRect(SkToBool(localRect))
50 , fRect(r)
51 , fHelper(helperArgs, GrAAType::kNone) {
52 if (fHasLocalRect) {
53 fLocalQuad.set(*localRect);
54 }
55 // Choose some conservative values for aa bloat and zero area.
56 this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
57 }
58
Robert Phillipsb493eeb2017-09-13 13:10:52 -040059 const char* name() const override { return "NonAARectOp"; }
60
Robert Phillipsf1748f52017-09-14 14:11:24 -040061 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -040062 fHelper.visitProxies(func);
63 }
64
Brian Salomon9a036422017-07-13 17:04:43 -040065 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
66
Brian Osman9a725dd2017-09-20 09:53:22 -040067 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip*,
68 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon9a036422017-07-13 17:04:43 -040069 // Set the color to unknown because the subclass may change the color later.
70 GrProcessorAnalysisColor gpColor;
71 gpColor.setToUnknown();
72 // We ignore the clip so pass this rather than the GrAppliedClip param.
73 static GrAppliedClip kNoClip;
Brian Osman9a725dd2017-09-20 09:53:22 -040074 return fHelper.xpRequiresDstTexture(caps, &kNoClip, dstIsClamped,
75 GrProcessorAnalysisCoverage::kNone, &gpColor);
Brian Salomon9a036422017-07-13 17:04:43 -040076 }
77
Robert Phillipseb35f4d2017-03-21 07:56:47 -040078protected:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040079 GrColor fColor;
80 bool fHasLocalRect;
81 GrQuad fLocalQuad;
82 SkRect fRect;
83
84private:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040085 bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
86
Brian Salomon91326c32017-08-09 16:02:19 -040087 void onPrepareDraws(Target* target) override {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040088 using namespace GrDefaultGeoProcFactory;
89
90 // The vertex attrib order is always pos, color, local coords.
91 static const int kColorOffset = sizeof(SkPoint);
92 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
93
94 sk_sp<GrGeometryProcessor> gp =
95 GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
96 Coverage::kSolid_Type,
97 fHasLocalRect ? LocalCoords::kHasExplicit_Type
98 : LocalCoords::kUnused_Type,
99 SkMatrix::I());
100 if (!gp) {
101 SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
102 return;
103 }
104
105 size_t vertexStride = gp->getVertexStride();
106
107 SkASSERT(fHasLocalRect
108 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
109 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
110
111 const GrBuffer* indexBuffer;
112 int firstIndex;
113 uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
114 if (!indices) {
115 SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
116 return;
117 }
118
119 const GrBuffer* vertexBuffer;
120 int firstVertex;
121 void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
122 if (!vertices) {
123 SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
124 return;
125 }
126
127 // Setup indices
128 indices[0] = 0;
129 indices[1] = 1;
130 indices[2] = 2;
131 indices[3] = 0;
132 indices[4] = 2;
133 indices[5] = 3;
134
135 // Setup positions
136 SkPoint* position = (SkPoint*) vertices;
137 position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride);
138
139 // Setup vertex colors
140 GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
141 for (int i = 0; i < 4; ++i) {
142 *color = fColor;
143 color = (GrColor*)((intptr_t)color + vertexStride);
144 }
145
146 // Setup local coords
147 if (fHasLocalRect) {
148 SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
149 for (int i = 0; i < 4; i++) {
150 *coords = fLocalQuad.point(i);
151 coords = (SkPoint*)((intptr_t) coords + vertexStride);
152 }
153 }
154
Chris Dalton3809bab2017-06-13 10:55:06 -0600155 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -0600156 mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3);
157 mesh.setVertexData(vertexBuffer, firstVertex);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400158
Brian Salomon9a036422017-07-13 17:04:43 -0400159 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400160 }
161
Brian Salomon9a036422017-07-13 17:04:43 -0400162 Helper fHelper;
163
164 typedef GrMeshDrawOp INHERITED;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400165};
166
Brian Salomon9a036422017-07-13 17:04:43 -0400167} // anonymous namespace
168
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400169#ifdef SK_DEBUG
170#include "SkImageEncoder.h"
171#include "sk_tool_utils.h"
172
173static void save_bm(const SkBitmap& bm, const char name[]) {
174 bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
175 SkASSERT(result);
176}
177#endif
178
Brian Salomon9a036422017-07-13 17:04:43 -0400179static constexpr SkRect kEmptyRect = SkRect::MakeEmpty();
180
181namespace {
182
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400183/*
184 * Atlased ops just draw themselves as textured rects with the texture pixels being
185 * pulled out of the atlas. Their color is based on their ID.
186 */
187class AtlasedRectOp final : public NonAARectOp {
188public:
189 DEFINE_OP_CLASS_ID
190
191 ~AtlasedRectOp() override {
192 fID = -1;
193 }
194
195 const char* name() const override { return "AtlasedRectOp"; }
196
197 int id() const { return fID; }
198
Brian Salomon9a036422017-07-13 17:04:43 -0400199 static std::unique_ptr<AtlasedRectOp> Make(GrPaint&& paint, const SkRect& r, int id) {
200 GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(std::move(paint), r, id).release();
201 return std::unique_ptr<AtlasedRectOp>(static_cast<AtlasedRectOp*>(op));
202 }
203
204 // We set the initial color of the NonAARectOp based on the ID.
205 // Note that we force creation of a NonAARectOp that has local coords in anticipation of
206 // pulling from the atlas.
207 AtlasedRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r, int id)
208 : INHERITED(helperArgs, kColors[id], r, &kEmptyRect, ClassID())
209 , fID(id)
210 , fNext(nullptr) {
211 SkASSERT(fID < kMaxIDs);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400212 }
213
214 void setColor(GrColor color) { fColor = color; }
215 void setLocalRect(const SkRect& localRect) {
216 SkASSERT(fHasLocalRect); // This should've been created to anticipate this
217 fLocalQuad.set(localRect);
218 }
219
220 AtlasedRectOp* next() const { return fNext; }
221 void setNext(AtlasedRectOp* next) {
222 fNext = next;
223 }
224
225private:
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400226
227 static const int kMaxIDs = 9;
228 static const SkColor kColors[kMaxIDs];
229
230 int fID;
231 // The Atlased ops have an internal singly-linked list of ops that land in the same opList
232 AtlasedRectOp* fNext;
233
234 typedef NonAARectOp INHERITED;
235};
236
Brian Salomon9a036422017-07-13 17:04:43 -0400237} // anonymous namespace
238
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400239const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
240 GrColorPackRGBA(255, 0, 0, 255),
241 GrColorPackRGBA(0, 255, 0, 255),
242 GrColorPackRGBA(0, 0, 255, 255),
243 GrColorPackRGBA(0, 255, 255, 255),
244 GrColorPackRGBA(255, 0, 255, 255),
245 GrColorPackRGBA(255, 255, 0, 255),
246 GrColorPackRGBA(0, 0, 0, 255),
247 GrColorPackRGBA(128, 128, 128, 255),
248 GrColorPackRGBA(255, 255, 255, 255)
249};
250
251static const int kDrawnTileSize = 16;
252
253/*
254 * Rather than performing any rect packing, this atlaser just lays out constant-sized
255 * tiles in an Nx1 row
256 */
257static const int kAtlasTileSize = 2;
258
259/*
260 * This class aggregates the op information required for atlasing
261 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400262class AtlasObject final : public GrOnFlushCallbackObject {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400263public:
264 AtlasObject() : fDone(false) { }
265
266 ~AtlasObject() override {
267 SkASSERT(fDone);
268 }
269
270 void markAsDone() {
271 fDone = true;
272 }
273
274 // Insert the new op in an internal singly-linked list for 'opListID'
275 void addOp(uint32_t opListID, AtlasedRectOp* op) {
276 LinkedListHeader* header = nullptr;
277 for (int i = 0; i < fOps.count(); ++i) {
278 if (opListID == fOps[i].fID) {
279 header = &(fOps[i]);
280 }
281 }
282
283 if (!header) {
284 fOps.push({opListID, nullptr});
285 header = &(fOps[fOps.count()-1]);
286 }
287
288 op->setNext(header->fHead);
289 header->fHead = op;
290 }
291
292 // For the time being we need to pre-allocate the atlas.
293 void setAtlasDest(sk_sp<GrTextureProxy> atlasDest) {
294 fAtlasDest = atlasDest;
295 }
296
297 void saveRTC(sk_sp<GrRenderTargetContext> rtc) {
298 SkASSERT(!fRTC);
299 fRTC = rtc;
300 }
301
302#ifdef SK_DEBUG
303 void saveAtlasToDisk() {
304 SkBitmap readBack;
305 readBack.allocN32Pixels(fRTC->width(), fRTC->height());
306
307 bool result = fRTC->readPixels(readBack.info(),
308 readBack.getPixels(), readBack.rowBytes(), 0, 0);
309 SkASSERT(result);
310 save_bm(readBack, "atlas-real.png");
311 }
312#endif
313
314 /*
315 * This callback back creates the atlas and updates the AtlasedRectOps to read from it
316 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400317 void preFlush(GrOnFlushResourceProvider* resourceProvider,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400318 const uint32_t* opListIDs, int numOpListIDs,
319 SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
320 SkASSERT(!results->count());
321
322 // Until MDB is landed we will most-likely only have one opList.
323 SkTDArray<LinkedListHeader*> lists;
324 for (int i = 0; i < numOpListIDs; ++i) {
325 if (LinkedListHeader* list = this->getList(opListIDs[i])) {
326 lists.push(list);
327 }
328 }
329
330 if (!lists.count()) {
331 return; // nothing to atlas
332 }
333
334 // TODO: right now we have to pre-allocate the atlas bc the TextureSamplers need a
335 // hard GrTexture
336#if 0
337 GrSurfaceDesc desc;
338 desc.fFlags = kRenderTarget_GrSurfaceFlag;
339 desc.fWidth = this->numOps() * kAtlasTileSize;
340 desc.fHeight = kAtlasTileSize;
341 desc.fConfig = kRGBA_8888_GrPixelConfig;
342
343 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(desc,
344 nullptr,
345 nullptr);
346#else
347 // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
348 // there should either be two writes to clear it or no writes.
349 SkASSERT(9 == fAtlasDest->getPendingReadCnt_TestOnly());
350 SkASSERT(2 == fAtlasDest->getPendingWriteCnt_TestOnly() ||
351 0 == fAtlasDest->getPendingWriteCnt_TestOnly());
352 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
353 fAtlasDest,
354 nullptr, nullptr);
355#endif
356
357 rtc->clear(nullptr, 0xFFFFFFFF, true); // clear the atlas
358
359 int blocksInAtlas = 0;
360 for (int i = 0; i < lists.count(); ++i) {
361 for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
362 SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
363 kAtlasTileSize, kAtlasTileSize);
364
365 // For now, we avoid the resource buffer issues and just use clears
366#if 1
367 rtc->clear(&r, op->color(), false);
368#else
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400369 GrPaint paint;
Brian Salomon9a036422017-07-13 17:04:43 -0400370 paint.setColor4f(GrColor4f::FromGrColor(op->color()));
371 std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint),
372 SkRect::Make(r)));
373 rtc->priv().testingOnly_addDrawOp(std::move(drawOp));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400374#endif
375 blocksInAtlas++;
376
377 // Set the atlased Op's color to white (so we know we're not using it for
378 // the final draw).
379 op->setColor(0xFFFFFFFF);
380
381 // Set the atlased Op's localRect to point to where it landed in the atlas
382 op->setLocalRect(SkRect::Make(r));
383
384 // TODO: we also need to set the op's GrSuperDeferredSimpleTextureEffect to point
385 // to the rtc's proxy!
386 }
387
388 // We've updated all these ops and we certainly don't want to process them again
389 this->clearOpsFor(lists[i]);
390 }
391
392 // Hide a ref to the RTC in AtlasData so we can check on it later
393 this->saveRTC(rtc);
394
395 results->push_back(std::move(rtc));
396 }
397
398private:
399 typedef struct {
400 uint32_t fID;
401 AtlasedRectOp* fHead;
402 } LinkedListHeader;
403
404 LinkedListHeader* getList(uint32_t opListID) {
405 for (int i = 0; i < fOps.count(); ++i) {
406 if (opListID == fOps[i].fID) {
407 return &(fOps[i]);
408 }
409 }
410 return nullptr;
411 }
412
413 void clearOpsFor(LinkedListHeader* header) {
414 // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
415 // forget about them in the laziest way possible.
416 header->fHead = nullptr;
417 header->fID = 0; // invalid opList ID
418 }
419
420 // Each opList containing AtlasedRectOps gets its own internal singly-linked list
421 SkTDArray<LinkedListHeader> fOps;
422
423 // The RTC used to create the atlas
424 sk_sp<GrRenderTargetContext> fRTC;
425
426 // For the time being we need to pre-allocate the atlas bc the TextureSamplers require
427 // a GrTexture
428 sk_sp<GrTextureProxy> fAtlasDest;
429
430 // Set to true when the testing harness expects this object to be no longer used
431 bool fDone;
432};
433
434// This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
435static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
436 sk_sp<GrTextureProxy> fakeAtlas) {
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400437 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
438 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400439 3*kDrawnTileSize,
440 kDrawnTileSize,
441 kRGBA_8888_GrPixelConfig,
442 nullptr));
443
444 rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255), true);
445
446 for (int i = 0; i < 3; ++i) {
447 SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
448
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400449 // TODO: here is the blocker for deferring creation of the atlas. The TextureSamplers
450 // created here currently require a hard GrTexture.
Brian Salomonaff329b2017-08-11 09:40:37 -0400451 auto fp = GrSimpleTextureEffect::Make(fakeAtlas, nullptr, SkMatrix::I());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400452 GrPaint paint;
453 paint.addColorFragmentProcessor(std::move(fp));
454 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Brian Salomon9a036422017-07-13 17:04:43 -0400455 std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(std::move(paint), r, start + i));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400456
457 AtlasedRectOp* sparePtr = op.get();
458
Brian Salomon9a036422017-07-13 17:04:43 -0400459 uint32_t opListID = rtc->priv().testingOnly_addDrawOp(std::move(op));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400460
461 object->addOp(opListID, sparePtr);
462 }
463
464 return rtc->asTextureProxyRef();
465}
466
467// Enable this if you want to debug the final draws w/o having the atlasCallback create the
468// atlas
469#if 0
470#include "SkGrPriv.h"
471
472sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
473 SkBitmap bm;
474 bm.allocN32Pixels(18, 2, true);
475 bm.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 2, 2));
476 bm.erase(SK_ColorGREEN, SkIRect::MakeXYWH(2, 0, 2, 2));
477 bm.erase(SK_ColorBLUE, SkIRect::MakeXYWH(4, 0, 2, 2));
478 bm.erase(SK_ColorCYAN, SkIRect::MakeXYWH(6, 0, 2, 2));
479 bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
480 bm.erase(SK_ColorYELLOW, SkIRect::MakeXYWH(10, 0, 2, 2));
481 bm.erase(SK_ColorBLACK, SkIRect::MakeXYWH(12, 0, 2, 2));
482 bm.erase(SK_ColorGRAY, SkIRect::MakeXYWH(14, 0, 2, 2));
483 bm.erase(SK_ColorWHITE, SkIRect::MakeXYWH(16, 0, 2, 2));
484
485#if 1
486 save_bm(bm, "atlas-fake.png");
487#endif
488
489 GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
490 desc.fFlags |= kRenderTarget_GrSurfaceFlag;
491
492 sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
493 context->textureProvider(),
494 desc, SkBudgeted::kYes,
495 bm.getPixels(), bm.rowBytes());
496
497 return sk_ref_sp(tmp->asTextureProxy());
498}
499#else
500// TODO: this is unfortunate and must be removed. We want the atlas to be created later.
501sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
502 GrSurfaceDesc desc;
503 desc.fFlags = kRenderTarget_GrSurfaceFlag;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400504 desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
505 desc.fWidth = 32;
506 desc.fHeight = 16;
Robert Phillips16d8ec62017-07-27 16:16:25 -0400507 desc.fConfig = kSkia8888_GrPixelConfig;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400508 sk_sp<GrSurfaceProxy> atlasDest = GrSurfaceProxy::MakeDeferred(
509 context->resourceProvider(),
510 desc, SkBackingFit::kExact,
511 SkBudgeted::kYes,
512 GrResourceProvider::kNoPendingIO_Flag);
513 return sk_ref_sp(atlasDest->asTextureProxy());
514}
515#endif
516
517static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
518 SkColor readback = bm.getColor(x, kDrawnTileSize/2);
519 REPORTER_ASSERT(reporter, expected == readback);
520 if (expected != readback) {
521 SkDebugf("Color mismatch: %x %x\n", expected, readback);
522 }
523}
524
525/*
526 * For the atlasing test we make a DAG that looks like:
527 *
528 * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8
529 * \ /
530 * \ /
531 * RT4
532 * We then flush RT4 and expect only ops 0-5 to be atlased together.
533 * Each op is just a solid colored rect so both the atlas and the final image should appear as:
534 * R G B C M Y
535 * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
536 *
537 * Note: until MDB lands, the atlas will actually have width= 9*kAtlasTileSize and look like:
538 * R G B C M Y K Grey White
539 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400540DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400541 static const int kNumProxies = 3;
542
543 GrContext* context = ctxInfo.grContext();
544
545 if (context->caps()->useDrawInsteadOfClear()) {
546 // TODO: fix the buffer issues so this can run on all devices
547 return;
548 }
549
Chris Daltonfe199b72017-05-05 11:26:15 -0400550 AtlasObject object;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400551
552 // For now (until we add a GrSuperDeferredSimpleTextureEffect), we create the final atlas
553 // proxy ahead of time.
554 sk_sp<GrTextureProxy> atlasDest = pre_create_atlas(context);
555
Chris Daltonfe199b72017-05-05 11:26:15 -0400556 object.setAtlasDest(atlasDest);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400557
Chris Daltonfe199b72017-05-05 11:26:15 -0400558 context->contextPriv().addOnFlushCallbackObject(&object);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400559
560 sk_sp<GrTextureProxy> proxies[kNumProxies];
561 for (int i = 0; i < kNumProxies; ++i) {
Chris Daltonfe199b72017-05-05 11:26:15 -0400562 proxies[i] = make_upstream_image(context, &object, i*3, atlasDest);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400563 }
564
565 static const int kFinalWidth = 6*kDrawnTileSize;
566 static const int kFinalHeight = kDrawnTileSize;
567
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400568 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
569 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400570 kFinalWidth,
571 kFinalHeight,
572 kRGBA_8888_GrPixelConfig,
573 nullptr));
574
575 rtc->clear(nullptr, 0xFFFFFFFF, true);
576
577 // Note that this doesn't include the third texture proxy
578 for (int i = 0; i < kNumProxies-1; ++i) {
579 SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
580
581 SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
582
583 GrPaint paint;
Brian Salomonaff329b2017-08-11 09:40:37 -0400584 auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), nullptr, t);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400585 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
586 paint.addColorFragmentProcessor(std::move(fp));
587
588 rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
589 }
590
Greg Daniela5cb7812017-06-16 09:45:32 -0400591 rtc->prepareForExternalIO(0, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400592
593 SkBitmap readBack;
594 readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
595
596 SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
597 readBack.rowBytes(), 0, 0);
598 SkASSERT(result);
599
Chris Daltonfe199b72017-05-05 11:26:15 -0400600 context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
601
602 object.markAsDone();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400603
604#if 0
605 save_bm(readBack, "atlas-final-image.png");
606 data.saveAtlasToDisk();
607#endif
608
609 int x = kDrawnTileSize/2;
610 test_color(reporter, readBack, x, SK_ColorRED);
611 x += kDrawnTileSize;
612 test_color(reporter, readBack, x, SK_ColorGREEN);
613 x += kDrawnTileSize;
614 test_color(reporter, readBack, x, SK_ColorBLUE);
615 x += kDrawnTileSize;
616 test_color(reporter, readBack, x, SK_ColorCYAN);
617 x += kDrawnTileSize;
618 test_color(reporter, readBack, x, SK_ColorMAGENTA);
619 x += kDrawnTileSize;
620 test_color(reporter, readBack, x, SK_ColorYELLOW);
621}
622
623#endif