blob: c96f19709854e813632de12c6dec43b7a23270ba [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
Greg Daniela5cb7812017-06-16 09:45:32 -040010#include "GrBackendSemaphore.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040011#include "GrClip.h"
12#include "GrContextPriv.h"
13#include "GrDefaultGeoProcFactory.h"
Chris Daltonfe199b72017-05-05 11:26:15 -040014#include "GrOnFlushResourceProvider.h"
Robert Phillips0bd24dc2018-01-16 08:06:32 -050015#include "GrProxyProvider.h"
Robert Phillips7d79e7b2018-02-14 11:09:57 -050016#include "GrQuad.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040017#include "GrRenderTargetContextPriv.h"
18#include "GrResourceProvider.h"
Robert Phillips7d79e7b2018-02-14 11:09:57 -050019#include "GrTexture.h"
20
Mike Reed75ae4212018-01-23 11:24:08 -050021#include "SkBitmap.h"
Cary Clark74f623d2017-11-06 20:02:02 -050022#include "SkPointPriv.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040023#include "effects/GrSimpleTextureEffect.h"
Brian Salomon9a036422017-07-13 17:04:43 -040024#include "ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040025
Brian Salomon9a036422017-07-13 17:04:43 -040026namespace {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040027// This is a simplified mesh drawing op that can be used in the atlas generation test.
28// Please see AtlasedRectOp below.
Brian Salomon9a036422017-07-13 17:04:43 -040029class NonAARectOp : public GrMeshDrawOp {
30protected:
31 using Helper = GrSimpleMeshDrawOpHelper;
32
Robert Phillipseb35f4d2017-03-21 07:56:47 -040033public:
34 DEFINE_OP_CLASS_ID
Robert Phillipseb35f4d2017-03-21 07:56:47 -040035
36 // This creates an instance of a simple non-AA solid color rect-drawing Op
Robert Phillips7c525e62018-06-12 10:11:12 -040037 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
38 GrPaint&& paint,
39 const SkRect& r) {
40 return Helper::FactoryHelper<NonAARectOp>(context, std::move(paint), r, nullptr, ClassID());
Robert Phillipseb35f4d2017-03-21 07:56:47 -040041 }
42
43 // This creates an instance of a simple non-AA textured rect-drawing Op
Robert Phillips7c525e62018-06-12 10:11:12 -040044 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
45 GrPaint&& paint,
46 const SkRect& r,
47 const SkRect& local) {
48 return Helper::FactoryHelper<NonAARectOp>(context, std::move(paint), r, &local, ClassID());
Robert Phillipseb35f4d2017-03-21 07:56:47 -040049 }
50
51 GrColor color() const { return fColor; }
52
Brian Salomon9a036422017-07-13 17:04:43 -040053 NonAARectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r,
54 const SkRect* localRect, int32_t classID)
55 : INHERITED(classID)
56 , fColor(color)
57 , fHasLocalRect(SkToBool(localRect))
58 , fRect(r)
59 , fHelper(helperArgs, GrAAType::kNone) {
60 if (fHasLocalRect) {
Brian Salomona33b67c2018-05-17 10:42:14 -040061 fLocalQuad = GrQuad(*localRect);
Brian Salomon9a036422017-07-13 17:04:43 -040062 }
63 // Choose some conservative values for aa bloat and zero area.
64 this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
65 }
66
Robert Phillipsb493eeb2017-09-13 13:10:52 -040067 const char* name() const override { return "NonAARectOp"; }
68
Robert Phillipsf1748f52017-09-14 14:11:24 -040069 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -040070 fHelper.visitProxies(func);
71 }
72
Brian Salomon9a036422017-07-13 17:04:43 -040073 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
74
Brian Osman532b3f92018-07-11 10:02:07 -040075 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip*) override {
Brian Salomon9a036422017-07-13 17:04:43 -040076 // Set the color to unknown because the subclass may change the color later.
77 GrProcessorAnalysisColor gpColor;
78 gpColor.setToUnknown();
79 // We ignore the clip so pass this rather than the GrAppliedClip param.
80 static GrAppliedClip kNoClip;
Brian Osman532b3f92018-07-11 10:02:07 -040081 return fHelper.xpRequiresDstTexture(caps, &kNoClip, GrProcessorAnalysisCoverage::kNone,
82 &gpColor);
Brian Salomon9a036422017-07-13 17:04:43 -040083 }
84
Robert Phillipseb35f4d2017-03-21 07:56:47 -040085protected:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040086 GrColor fColor;
87 bool fHasLocalRect;
88 GrQuad fLocalQuad;
89 SkRect fRect;
90
91private:
Brian Salomon91326c32017-08-09 16:02:19 -040092 void onPrepareDraws(Target* target) override {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040093 using namespace GrDefaultGeoProcFactory;
94
95 // The vertex attrib order is always pos, color, local coords.
96 static const int kColorOffset = sizeof(SkPoint);
97 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
98
99 sk_sp<GrGeometryProcessor> gp =
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400100 GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
101 Color::kPremulGrColorAttribute_Type,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400102 Coverage::kSolid_Type,
103 fHasLocalRect ? LocalCoords::kHasExplicit_Type
104 : LocalCoords::kUnused_Type,
105 SkMatrix::I());
106 if (!gp) {
107 SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
108 return;
109 }
110
Brian Salomon92be2f72018-06-19 14:33:47 -0400111 size_t vertexStride = fHasLocalRect
112 ? sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
113 : sizeof(GrDefaultGeoProcFactory::PositionColorAttr);
114 SkASSERT(vertexStride == gp->debugOnly_vertexStride());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400115
116 const GrBuffer* indexBuffer;
117 int firstIndex;
118 uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
119 if (!indices) {
120 SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
121 return;
122 }
123
124 const GrBuffer* vertexBuffer;
125 int firstVertex;
126 void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
127 if (!vertices) {
128 SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
129 return;
130 }
131
132 // Setup indices
133 indices[0] = 0;
134 indices[1] = 1;
135 indices[2] = 2;
Brian Salomon57caa662017-10-18 12:21:05 +0000136 indices[3] = 2;
137 indices[4] = 1;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400138 indices[5] = 3;
139
140 // Setup positions
141 SkPoint* position = (SkPoint*) vertices;
Brian Salomonec42e152018-05-18 12:52:22 -0400142 SkPointPriv::SetRectTriStrip(position, fRect, vertexStride);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400143
144 // Setup vertex colors
145 GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
146 for (int i = 0; i < 4; ++i) {
147 *color = fColor;
148 color = (GrColor*)((intptr_t)color + vertexStride);
149 }
150
151 // Setup local coords
152 if (fHasLocalRect) {
153 SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
154 for (int i = 0; i < 4; i++) {
155 *coords = fLocalQuad.point(i);
156 coords = (SkPoint*)((intptr_t) coords + vertexStride);
157 }
158 }
159
Brian Salomon7eae3e02018-08-07 14:02:38 +0000160 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
161 mesh->setIndexed(indexBuffer, 6, firstIndex, 0, 3, GrPrimitiveRestart::kNo);
162 mesh->setVertexData(vertexBuffer, firstVertex);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400163
Brian Salomon49348902018-06-26 09:12:38 -0400164 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +0000165 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400166 }
167
Brian Salomon9a036422017-07-13 17:04:43 -0400168 Helper fHelper;
169
170 typedef GrMeshDrawOp INHERITED;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400171};
172
Brian Salomon9a036422017-07-13 17:04:43 -0400173} // anonymous namespace
174
Brian Salomon9a036422017-07-13 17:04:43 -0400175static constexpr SkRect kEmptyRect = SkRect::MakeEmpty();
176
177namespace {
178
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400179/*
180 * Atlased ops just draw themselves as textured rects with the texture pixels being
181 * pulled out of the atlas. Their color is based on their ID.
182 */
183class AtlasedRectOp final : public NonAARectOp {
184public:
185 DEFINE_OP_CLASS_ID
186
187 ~AtlasedRectOp() override {
188 fID = -1;
189 }
190
191 const char* name() const override { return "AtlasedRectOp"; }
192
193 int id() const { return fID; }
194
Robert Phillips7c525e62018-06-12 10:11:12 -0400195 static std::unique_ptr<AtlasedRectOp> Make(GrContext* context,
196 GrPaint&& paint,
197 const SkRect& r,
198 int id) {
199 GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(context, std::move(paint),
200 r, id).release();
Brian Salomon9a036422017-07-13 17:04:43 -0400201 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
Brian Salomona33b67c2018-05-17 10:42:14 -0400217 fLocalQuad = GrQuad(localRect);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400218 }
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) {
Mike Reed5edcd312018-08-08 11:23:41 -0400284 fOps.push_back({opListID, nullptr});
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400285 header = &(fOps[fOps.count()-1]);
286 }
287
288 op->setNext(header->fHead);
289 header->fHead = op;
290 }
291
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500292 int numOps() const { return fOps.count(); }
293
294 // Get the fully lazy proxy that is backing the atlas. Its actual width isn't
295 // known until flush time.
296 sk_sp<GrTextureProxy> getAtlasProxy(GrProxyProvider* proxyProvider) {
297 if (fAtlasProxy) {
298 return fAtlasProxy;
299 }
300
Chris Dalton4c458b12018-06-16 17:22:59 -0600301 fAtlasProxy = GrProxyProvider::MakeFullyLazyProxy(
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500302 [](GrResourceProvider* resourceProvider) {
303 if (!resourceProvider) {
304 return sk_sp<GrTexture>();
305 }
306
307 GrSurfaceDesc desc;
308 desc.fFlags = kRenderTarget_GrSurfaceFlag;
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500309 // TODO: until partial flushes in MDB lands we're stuck having
310 // all 9 atlas draws occur
311 desc.fWidth = 9 /*this->numOps()*/ * kAtlasTileSize;
312 desc.fHeight = kAtlasTileSize;
313 desc.fConfig = kRGBA_8888_GrPixelConfig;
314
315 return resourceProvider->createTexture(desc, SkBudgeted::kYes,
Chris Daltond004e0b2018-09-27 09:28:03 -0600316 GrResourceProvider::Flags::kNoPendingIO);
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500317 },
318 GrProxyProvider::Renderable::kYes,
319 kBottomLeft_GrSurfaceOrigin,
Chris Dalton4c458b12018-06-16 17:22:59 -0600320 kRGBA_8888_GrPixelConfig,
321 *proxyProvider->caps());
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500322 return fAtlasProxy;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400323 }
324
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400325 /*
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500326 * This callback creates the atlas and updates the AtlasedRectOps to read from it
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400327 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400328 void preFlush(GrOnFlushResourceProvider* resourceProvider,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400329 const uint32_t* opListIDs, int numOpListIDs,
330 SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
331 SkASSERT(!results->count());
332
333 // Until MDB is landed we will most-likely only have one opList.
334 SkTDArray<LinkedListHeader*> lists;
335 for (int i = 0; i < numOpListIDs; ++i) {
336 if (LinkedListHeader* list = this->getList(opListIDs[i])) {
Mike Reed5edcd312018-08-08 11:23:41 -0400337 lists.push_back(list);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400338 }
339 }
340
341 if (!lists.count()) {
342 return; // nothing to atlas
343 }
344
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500345 if (!resourceProvider->instatiateProxy(fAtlasProxy.get())) {
346 return;
347 }
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400348
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400349 // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
350 // there should either be two writes to clear it or no writes.
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500351 SkASSERT(9 == fAtlasProxy->getPendingReadCnt_TestOnly());
352 SkASSERT(2 == fAtlasProxy->getPendingWriteCnt_TestOnly() ||
353 0 == fAtlasProxy->getPendingWriteCnt_TestOnly());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400354 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500355 fAtlasProxy,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400356 nullptr, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400357
Chris Dalton344e9032017-12-11 15:42:09 -0700358 // clear the atlas
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500359 rtc->clear(nullptr, 0x0, GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400360
361 int blocksInAtlas = 0;
362 for (int i = 0; i < lists.count(); ++i) {
363 for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
364 SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
365 kAtlasTileSize, kAtlasTileSize);
366
367 // For now, we avoid the resource buffer issues and just use clears
368#if 1
Chris Dalton344e9032017-12-11 15:42:09 -0700369 rtc->clear(&r, op->color(), GrRenderTargetContext::CanClearFullscreen::kNo);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400370#else
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400371 GrPaint paint;
Brian Salomon9a036422017-07-13 17:04:43 -0400372 paint.setColor4f(GrColor4f::FromGrColor(op->color()));
373 std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint),
374 SkRect::Make(r)));
375 rtc->priv().testingOnly_addDrawOp(std::move(drawOp));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400376#endif
377 blocksInAtlas++;
378
379 // Set the atlased Op's color to white (so we know we're not using it for
380 // the final draw).
381 op->setColor(0xFFFFFFFF);
382
383 // Set the atlased Op's localRect to point to where it landed in the atlas
384 op->setLocalRect(SkRect::Make(r));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400385 }
386
387 // We've updated all these ops and we certainly don't want to process them again
388 this->clearOpsFor(lists[i]);
389 }
390
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400391 results->push_back(std::move(rtc));
392 }
393
394private:
395 typedef struct {
396 uint32_t fID;
397 AtlasedRectOp* fHead;
398 } LinkedListHeader;
399
400 LinkedListHeader* getList(uint32_t opListID) {
401 for (int i = 0; i < fOps.count(); ++i) {
402 if (opListID == fOps[i].fID) {
403 return &(fOps[i]);
404 }
405 }
406 return nullptr;
407 }
408
409 void clearOpsFor(LinkedListHeader* header) {
410 // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
411 // forget about them in the laziest way possible.
412 header->fHead = nullptr;
413 header->fID = 0; // invalid opList ID
414 }
415
416 // Each opList containing AtlasedRectOps gets its own internal singly-linked list
417 SkTDArray<LinkedListHeader> fOps;
418
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500419 // The fully lazy proxy for the atlas
420 sk_sp<GrTextureProxy> fAtlasProxy;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400421
422 // Set to true when the testing harness expects this object to be no longer used
423 bool fDone;
424};
425
426// This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
427static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500428 sk_sp<GrTextureProxy> atlasProxy) {
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500429 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400430 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400431 3*kDrawnTileSize,
432 kDrawnTileSize,
433 kRGBA_8888_GrPixelConfig,
434 nullptr));
435
Chris Dalton344e9032017-12-11 15:42:09 -0700436 rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255),
437 GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400438
439 for (int i = 0; i < 3; ++i) {
440 SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
441
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500442 auto fp = GrSimpleTextureEffect::Make(atlasProxy, SkMatrix::I());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400443 GrPaint paint;
444 paint.addColorFragmentProcessor(std::move(fp));
445 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Robert Phillips7c525e62018-06-12 10:11:12 -0400446 std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(context,
447 std::move(paint), r, start + i));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400448
449 AtlasedRectOp* sparePtr = op.get();
450
Brian Salomon9a036422017-07-13 17:04:43 -0400451 uint32_t opListID = rtc->priv().testingOnly_addDrawOp(std::move(op));
Chris Daltonf104fec2018-05-22 16:17:48 -0600452 SkASSERT(SK_InvalidUniqueID != opListID);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400453
454 object->addOp(opListID, sparePtr);
455 }
456
457 return rtc->asTextureProxyRef();
458}
459
460// Enable this if you want to debug the final draws w/o having the atlasCallback create the
461// atlas
462#if 0
Chris Dalton12658942017-10-05 19:45:25 -0600463#include "SkImageEncoder.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400464#include "SkGrPriv.h"
Chris Dalton12658942017-10-05 19:45:25 -0600465#include "sk_tool_utils.h"
466
467static void save_bm(const SkBitmap& bm, const char name[]) {
468 bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
469 SkASSERT(result);
470}
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400471
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
Brian Osman2b23c4b2018-06-01 12:25:08 -0400489 GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400490 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}
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400499#endif
500
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500501
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400502static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
503 SkColor readback = bm.getColor(x, kDrawnTileSize/2);
504 REPORTER_ASSERT(reporter, expected == readback);
505 if (expected != readback) {
506 SkDebugf("Color mismatch: %x %x\n", expected, readback);
507 }
508}
509
510/*
511 * For the atlasing test we make a DAG that looks like:
512 *
513 * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8
514 * \ /
515 * \ /
516 * RT4
517 * We then flush RT4 and expect only ops 0-5 to be atlased together.
518 * Each op is just a solid colored rect so both the atlas and the final image should appear as:
519 * R G B C M Y
520 * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
521 *
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500522 * Note: until partial flushes in MDB lands, the atlas will actually have width= 9*kAtlasTileSize
523 * and look like:
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400524 * R G B C M Y K Grey White
525 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400526DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400527 static const int kNumProxies = 3;
528
529 GrContext* context = ctxInfo.grContext();
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500530 auto proxyProvider = context->contextPriv().proxyProvider();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400531
Chris Daltonfe199b72017-05-05 11:26:15 -0400532 AtlasObject object;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400533
Chris Daltonfe199b72017-05-05 11:26:15 -0400534 context->contextPriv().addOnFlushCallbackObject(&object);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400535
536 sk_sp<GrTextureProxy> proxies[kNumProxies];
537 for (int i = 0; i < kNumProxies; ++i) {
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500538 proxies[i] = make_upstream_image(context, &object, i*3,
539 object.getAtlasProxy(proxyProvider));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400540 }
541
542 static const int kFinalWidth = 6*kDrawnTileSize;
543 static const int kFinalHeight = kDrawnTileSize;
544
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500545 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400546 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400547 kFinalWidth,
548 kFinalHeight,
549 kRGBA_8888_GrPixelConfig,
550 nullptr));
551
Chris Dalton344e9032017-12-11 15:42:09 -0700552 rtc->clear(nullptr, 0xFFFFFFFF, GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400553
554 // Note that this doesn't include the third texture proxy
555 for (int i = 0; i < kNumProxies-1; ++i) {
556 SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
557
558 SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
559
560 GrPaint paint;
Brian Osman2240be92017-10-18 13:15:13 -0400561 auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), t);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400562 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
563 paint.addColorFragmentProcessor(std::move(fp));
564
565 rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
566 }
567
Greg Daniela5cb7812017-06-16 09:45:32 -0400568 rtc->prepareForExternalIO(0, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400569
570 SkBitmap readBack;
571 readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
572
573 SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
574 readBack.rowBytes(), 0, 0);
575 SkASSERT(result);
576
Chris Daltonfe199b72017-05-05 11:26:15 -0400577 context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
578
579 object.markAsDone();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400580
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400581 int x = kDrawnTileSize/2;
582 test_color(reporter, readBack, x, SK_ColorRED);
583 x += kDrawnTileSize;
584 test_color(reporter, readBack, x, SK_ColorGREEN);
585 x += kDrawnTileSize;
586 test_color(reporter, readBack, x, SK_ColorBLUE);
587 x += kDrawnTileSize;
588 test_color(reporter, readBack, x, SK_ColorCYAN);
589 x += kDrawnTileSize;
590 test_color(reporter, readBack, x, SK_ColorMAGENTA);
591 x += kDrawnTileSize;
592 test_color(reporter, readBack, x, SK_ColorYELLOW);
593}