blob: 706ce7a9d97529c7859eb7e568b229207e0fa6e1 [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 Phillips0bd24dc2018-01-16 08:06:32 -050017#include "GrProxyProvider.h"
Robert Phillips7d79e7b2018-02-14 11:09:57 -050018#include "GrQuad.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040019#include "GrRenderTargetContextPriv.h"
20#include "GrResourceProvider.h"
Robert Phillips7d79e7b2018-02-14 11:09:57 -050021#include "GrTexture.h"
22
Mike Reed75ae4212018-01-23 11:24:08 -050023#include "SkBitmap.h"
Cary Clark74f623d2017-11-06 20:02:02 -050024#include "SkPointPriv.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040025#include "effects/GrSimpleTextureEffect.h"
Brian Salomon9a036422017-07-13 17:04:43 -040026#include "ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -040027
Brian Salomon9a036422017-07-13 17:04:43 -040028namespace {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040029// This is a simplified mesh drawing op that can be used in the atlas generation test.
30// Please see AtlasedRectOp below.
Brian Salomon9a036422017-07-13 17:04:43 -040031class NonAARectOp : public GrMeshDrawOp {
32protected:
33 using Helper = GrSimpleMeshDrawOpHelper;
34
Robert Phillipseb35f4d2017-03-21 07:56:47 -040035public:
36 DEFINE_OP_CLASS_ID
Robert Phillipseb35f4d2017-03-21 07:56:47 -040037
38 // This creates an instance of a simple non-AA solid color rect-drawing Op
Brian Salomon9a036422017-07-13 17:04:43 -040039 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r) {
40 return Helper::FactoryHelper<NonAARectOp>(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
Brian Salomon9a036422017-07-13 17:04:43 -040044 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r, const SkRect& local) {
45 return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, &local, ClassID());
Robert Phillipseb35f4d2017-03-21 07:56:47 -040046 }
47
48 GrColor color() const { return fColor; }
49
Brian Salomon9a036422017-07-13 17:04:43 -040050 NonAARectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r,
51 const SkRect* localRect, int32_t classID)
52 : INHERITED(classID)
53 , fColor(color)
54 , fHasLocalRect(SkToBool(localRect))
55 , fRect(r)
56 , fHelper(helperArgs, GrAAType::kNone) {
57 if (fHasLocalRect) {
Brian Salomona33b67c2018-05-17 10:42:14 -040058 fLocalQuad = GrQuad(*localRect);
Brian Salomon9a036422017-07-13 17:04:43 -040059 }
60 // Choose some conservative values for aa bloat and zero area.
61 this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
62 }
63
Robert Phillipsb493eeb2017-09-13 13:10:52 -040064 const char* name() const override { return "NonAARectOp"; }
65
Robert Phillipsf1748f52017-09-14 14:11:24 -040066 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -040067 fHelper.visitProxies(func);
68 }
69
Brian Salomon9a036422017-07-13 17:04:43 -040070 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
71
Brian Osman9a725dd2017-09-20 09:53:22 -040072 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip*,
73 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon9a036422017-07-13 17:04:43 -040074 // Set the color to unknown because the subclass may change the color later.
75 GrProcessorAnalysisColor gpColor;
76 gpColor.setToUnknown();
77 // We ignore the clip so pass this rather than the GrAppliedClip param.
78 static GrAppliedClip kNoClip;
Brian Osman9a725dd2017-09-20 09:53:22 -040079 return fHelper.xpRequiresDstTexture(caps, &kNoClip, dstIsClamped,
80 GrProcessorAnalysisCoverage::kNone, &gpColor);
Brian Salomon9a036422017-07-13 17:04:43 -040081 }
82
Robert Phillipseb35f4d2017-03-21 07:56:47 -040083protected:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040084 GrColor fColor;
85 bool fHasLocalRect;
86 GrQuad fLocalQuad;
87 SkRect fRect;
88
89private:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040090 bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
91
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 =
100 GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
101 Coverage::kSolid_Type,
102 fHasLocalRect ? LocalCoords::kHasExplicit_Type
103 : LocalCoords::kUnused_Type,
104 SkMatrix::I());
105 if (!gp) {
106 SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
107 return;
108 }
109
110 size_t vertexStride = gp->getVertexStride();
111
112 SkASSERT(fHasLocalRect
113 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
114 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
115
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
Chris Dalton3809bab2017-06-13 10:55:06 -0600160 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -0600161 mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3);
162 mesh.setVertexData(vertexBuffer, firstVertex);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400163
Brian Salomon9a036422017-07-13 17:04:43 -0400164 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400165 }
166
Brian Salomon9a036422017-07-13 17:04:43 -0400167 Helper fHelper;
168
169 typedef GrMeshDrawOp INHERITED;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400170};
171
Brian Salomon9a036422017-07-13 17:04:43 -0400172} // anonymous namespace
173
Brian Salomon9a036422017-07-13 17:04:43 -0400174static constexpr SkRect kEmptyRect = SkRect::MakeEmpty();
175
176namespace {
177
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400178/*
179 * Atlased ops just draw themselves as textured rects with the texture pixels being
180 * pulled out of the atlas. Their color is based on their ID.
181 */
182class AtlasedRectOp final : public NonAARectOp {
183public:
184 DEFINE_OP_CLASS_ID
185
186 ~AtlasedRectOp() override {
187 fID = -1;
188 }
189
190 const char* name() const override { return "AtlasedRectOp"; }
191
192 int id() const { return fID; }
193
Brian Salomon9a036422017-07-13 17:04:43 -0400194 static std::unique_ptr<AtlasedRectOp> Make(GrPaint&& paint, const SkRect& r, int id) {
195 GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(std::move(paint), r, id).release();
196 return std::unique_ptr<AtlasedRectOp>(static_cast<AtlasedRectOp*>(op));
197 }
198
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 Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r, int id)
203 : INHERITED(helperArgs, kColors[id], r, &kEmptyRect, ClassID())
204 , fID(id)
205 , fNext(nullptr) {
206 SkASSERT(fID < kMaxIDs);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400207 }
208
209 void setColor(GrColor color) { fColor = color; }
210 void setLocalRect(const SkRect& localRect) {
211 SkASSERT(fHasLocalRect); // This should've been created to anticipate this
Brian Salomona33b67c2018-05-17 10:42:14 -0400212 fLocalQuad = GrQuad(localRect);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400213 }
214
215 AtlasedRectOp* next() const { return fNext; }
216 void setNext(AtlasedRectOp* next) {
217 fNext = next;
218 }
219
220private:
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400221
222 static const int kMaxIDs = 9;
223 static const SkColor kColors[kMaxIDs];
224
225 int fID;
226 // The Atlased ops have an internal singly-linked list of ops that land in the same opList
227 AtlasedRectOp* fNext;
228
229 typedef NonAARectOp INHERITED;
230};
231
Brian Salomon9a036422017-07-13 17:04:43 -0400232} // anonymous namespace
233
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400234const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
235 GrColorPackRGBA(255, 0, 0, 255),
236 GrColorPackRGBA(0, 255, 0, 255),
237 GrColorPackRGBA(0, 0, 255, 255),
238 GrColorPackRGBA(0, 255, 255, 255),
239 GrColorPackRGBA(255, 0, 255, 255),
240 GrColorPackRGBA(255, 255, 0, 255),
241 GrColorPackRGBA(0, 0, 0, 255),
242 GrColorPackRGBA(128, 128, 128, 255),
243 GrColorPackRGBA(255, 255, 255, 255)
244};
245
246static const int kDrawnTileSize = 16;
247
248/*
249 * Rather than performing any rect packing, this atlaser just lays out constant-sized
250 * tiles in an Nx1 row
251 */
252static const int kAtlasTileSize = 2;
253
254/*
255 * This class aggregates the op information required for atlasing
256 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400257class AtlasObject final : public GrOnFlushCallbackObject {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400258public:
259 AtlasObject() : fDone(false) { }
260
261 ~AtlasObject() override {
262 SkASSERT(fDone);
263 }
264
265 void markAsDone() {
266 fDone = true;
267 }
268
269 // Insert the new op in an internal singly-linked list for 'opListID'
270 void addOp(uint32_t opListID, AtlasedRectOp* op) {
271 LinkedListHeader* header = nullptr;
272 for (int i = 0; i < fOps.count(); ++i) {
273 if (opListID == fOps[i].fID) {
274 header = &(fOps[i]);
275 }
276 }
277
278 if (!header) {
279 fOps.push({opListID, nullptr});
280 header = &(fOps[fOps.count()-1]);
281 }
282
283 op->setNext(header->fHead);
284 header->fHead = op;
285 }
286
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500287 int numOps() const { return fOps.count(); }
288
289 // Get the fully lazy proxy that is backing the atlas. Its actual width isn't
290 // known until flush time.
291 sk_sp<GrTextureProxy> getAtlasProxy(GrProxyProvider* proxyProvider) {
292 if (fAtlasProxy) {
293 return fAtlasProxy;
294 }
295
296 fAtlasProxy = proxyProvider->createFullyLazyProxy(
297 [](GrResourceProvider* resourceProvider) {
298 if (!resourceProvider) {
299 return sk_sp<GrTexture>();
300 }
301
302 GrSurfaceDesc desc;
303 desc.fFlags = kRenderTarget_GrSurfaceFlag;
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500304 // TODO: until partial flushes in MDB lands we're stuck having
305 // all 9 atlas draws occur
306 desc.fWidth = 9 /*this->numOps()*/ * kAtlasTileSize;
307 desc.fHeight = kAtlasTileSize;
308 desc.fConfig = kRGBA_8888_GrPixelConfig;
309
310 return resourceProvider->createTexture(desc, SkBudgeted::kYes,
311 GrResourceProvider::kNoPendingIO_Flag);
312 },
313 GrProxyProvider::Renderable::kYes,
314 kBottomLeft_GrSurfaceOrigin,
315 kRGBA_8888_GrPixelConfig);
316 return fAtlasProxy;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400317 }
318
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400319 /*
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500320 * This callback creates the atlas and updates the AtlasedRectOps to read from it
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400321 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400322 void preFlush(GrOnFlushResourceProvider* resourceProvider,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400323 const uint32_t* opListIDs, int numOpListIDs,
324 SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
325 SkASSERT(!results->count());
326
327 // Until MDB is landed we will most-likely only have one opList.
328 SkTDArray<LinkedListHeader*> lists;
329 for (int i = 0; i < numOpListIDs; ++i) {
330 if (LinkedListHeader* list = this->getList(opListIDs[i])) {
331 lists.push(list);
332 }
333 }
334
335 if (!lists.count()) {
336 return; // nothing to atlas
337 }
338
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500339 if (!resourceProvider->instatiateProxy(fAtlasProxy.get())) {
340 return;
341 }
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400342
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400343 // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
344 // there should either be two writes to clear it or no writes.
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500345 SkASSERT(9 == fAtlasProxy->getPendingReadCnt_TestOnly());
346 SkASSERT(2 == fAtlasProxy->getPendingWriteCnt_TestOnly() ||
347 0 == fAtlasProxy->getPendingWriteCnt_TestOnly());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400348 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500349 fAtlasProxy,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400350 nullptr, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400351
Chris Dalton344e9032017-12-11 15:42:09 -0700352 // clear the atlas
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500353 rtc->clear(nullptr, 0x0, GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400354
355 int blocksInAtlas = 0;
356 for (int i = 0; i < lists.count(); ++i) {
357 for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
358 SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
359 kAtlasTileSize, kAtlasTileSize);
360
361 // For now, we avoid the resource buffer issues and just use clears
362#if 1
Chris Dalton344e9032017-12-11 15:42:09 -0700363 rtc->clear(&r, op->color(), GrRenderTargetContext::CanClearFullscreen::kNo);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400364#else
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400365 GrPaint paint;
Brian Salomon9a036422017-07-13 17:04:43 -0400366 paint.setColor4f(GrColor4f::FromGrColor(op->color()));
367 std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint),
368 SkRect::Make(r)));
369 rtc->priv().testingOnly_addDrawOp(std::move(drawOp));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400370#endif
371 blocksInAtlas++;
372
373 // Set the atlased Op's color to white (so we know we're not using it for
374 // the final draw).
375 op->setColor(0xFFFFFFFF);
376
377 // Set the atlased Op's localRect to point to where it landed in the atlas
378 op->setLocalRect(SkRect::Make(r));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400379 }
380
381 // We've updated all these ops and we certainly don't want to process them again
382 this->clearOpsFor(lists[i]);
383 }
384
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400385 results->push_back(std::move(rtc));
386 }
387
388private:
389 typedef struct {
390 uint32_t fID;
391 AtlasedRectOp* fHead;
392 } LinkedListHeader;
393
394 LinkedListHeader* getList(uint32_t opListID) {
395 for (int i = 0; i < fOps.count(); ++i) {
396 if (opListID == fOps[i].fID) {
397 return &(fOps[i]);
398 }
399 }
400 return nullptr;
401 }
402
403 void clearOpsFor(LinkedListHeader* header) {
404 // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
405 // forget about them in the laziest way possible.
406 header->fHead = nullptr;
407 header->fID = 0; // invalid opList ID
408 }
409
410 // Each opList containing AtlasedRectOps gets its own internal singly-linked list
411 SkTDArray<LinkedListHeader> fOps;
412
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500413 // The fully lazy proxy for the atlas
414 sk_sp<GrTextureProxy> fAtlasProxy;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400415
416 // Set to true when the testing harness expects this object to be no longer used
417 bool fDone;
418};
419
420// This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
421static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500422 sk_sp<GrTextureProxy> atlasProxy) {
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500423 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400424 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400425 3*kDrawnTileSize,
426 kDrawnTileSize,
427 kRGBA_8888_GrPixelConfig,
428 nullptr));
429
Chris Dalton344e9032017-12-11 15:42:09 -0700430 rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255),
431 GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400432
433 for (int i = 0; i < 3; ++i) {
434 SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
435
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500436 auto fp = GrSimpleTextureEffect::Make(atlasProxy, SkMatrix::I());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400437 GrPaint paint;
438 paint.addColorFragmentProcessor(std::move(fp));
439 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Brian Salomon9a036422017-07-13 17:04:43 -0400440 std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(std::move(paint), r, start + i));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400441
442 AtlasedRectOp* sparePtr = op.get();
443
Brian Salomon9a036422017-07-13 17:04:43 -0400444 uint32_t opListID = rtc->priv().testingOnly_addDrawOp(std::move(op));
Chris Daltonf104fec2018-05-22 16:17:48 -0600445 SkASSERT(SK_InvalidUniqueID != opListID);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400446
447 object->addOp(opListID, sparePtr);
448 }
449
450 return rtc->asTextureProxyRef();
451}
452
453// Enable this if you want to debug the final draws w/o having the atlasCallback create the
454// atlas
455#if 0
Chris Dalton12658942017-10-05 19:45:25 -0600456#include "SkImageEncoder.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400457#include "SkGrPriv.h"
Chris Dalton12658942017-10-05 19:45:25 -0600458#include "sk_tool_utils.h"
459
460static void save_bm(const SkBitmap& bm, const char name[]) {
461 bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
462 SkASSERT(result);
463}
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400464
465sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
466 SkBitmap bm;
467 bm.allocN32Pixels(18, 2, true);
468 bm.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 2, 2));
469 bm.erase(SK_ColorGREEN, SkIRect::MakeXYWH(2, 0, 2, 2));
470 bm.erase(SK_ColorBLUE, SkIRect::MakeXYWH(4, 0, 2, 2));
471 bm.erase(SK_ColorCYAN, SkIRect::MakeXYWH(6, 0, 2, 2));
472 bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
473 bm.erase(SK_ColorYELLOW, SkIRect::MakeXYWH(10, 0, 2, 2));
474 bm.erase(SK_ColorBLACK, SkIRect::MakeXYWH(12, 0, 2, 2));
475 bm.erase(SK_ColorGRAY, SkIRect::MakeXYWH(14, 0, 2, 2));
476 bm.erase(SK_ColorWHITE, SkIRect::MakeXYWH(16, 0, 2, 2));
477
478#if 1
479 save_bm(bm, "atlas-fake.png");
480#endif
481
482 GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
483 desc.fFlags |= kRenderTarget_GrSurfaceFlag;
484
485 sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
486 context->textureProvider(),
487 desc, SkBudgeted::kYes,
488 bm.getPixels(), bm.rowBytes());
489
490 return sk_ref_sp(tmp->asTextureProxy());
491}
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400492#endif
493
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500494
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400495static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
496 SkColor readback = bm.getColor(x, kDrawnTileSize/2);
497 REPORTER_ASSERT(reporter, expected == readback);
498 if (expected != readback) {
499 SkDebugf("Color mismatch: %x %x\n", expected, readback);
500 }
501}
502
503/*
504 * For the atlasing test we make a DAG that looks like:
505 *
506 * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8
507 * \ /
508 * \ /
509 * RT4
510 * We then flush RT4 and expect only ops 0-5 to be atlased together.
511 * Each op is just a solid colored rect so both the atlas and the final image should appear as:
512 * R G B C M Y
513 * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
514 *
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500515 * Note: until partial flushes in MDB lands, the atlas will actually have width= 9*kAtlasTileSize
516 * and look like:
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400517 * R G B C M Y K Grey White
518 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400519DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400520 static const int kNumProxies = 3;
521
522 GrContext* context = ctxInfo.grContext();
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500523 auto proxyProvider = context->contextPriv().proxyProvider();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400524
Chris Daltonfe199b72017-05-05 11:26:15 -0400525 AtlasObject object;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400526
Chris Daltonfe199b72017-05-05 11:26:15 -0400527 context->contextPriv().addOnFlushCallbackObject(&object);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400528
529 sk_sp<GrTextureProxy> proxies[kNumProxies];
530 for (int i = 0; i < kNumProxies; ++i) {
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500531 proxies[i] = make_upstream_image(context, &object, i*3,
532 object.getAtlasProxy(proxyProvider));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400533 }
534
535 static const int kFinalWidth = 6*kDrawnTileSize;
536 static const int kFinalHeight = kDrawnTileSize;
537
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500538 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400539 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400540 kFinalWidth,
541 kFinalHeight,
542 kRGBA_8888_GrPixelConfig,
543 nullptr));
544
Chris Dalton344e9032017-12-11 15:42:09 -0700545 rtc->clear(nullptr, 0xFFFFFFFF, GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400546
547 // Note that this doesn't include the third texture proxy
548 for (int i = 0; i < kNumProxies-1; ++i) {
549 SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
550
551 SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
552
553 GrPaint paint;
Brian Osman2240be92017-10-18 13:15:13 -0400554 auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), t);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400555 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
556 paint.addColorFragmentProcessor(std::move(fp));
557
558 rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
559 }
560
Greg Daniela5cb7812017-06-16 09:45:32 -0400561 rtc->prepareForExternalIO(0, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400562
563 SkBitmap readBack;
564 readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
565
566 SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
567 readBack.rowBytes(), 0, 0);
568 SkASSERT(result);
569
Chris Daltonfe199b72017-05-05 11:26:15 -0400570 context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
571
572 object.markAsDone();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400573
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400574 int x = kDrawnTileSize/2;
575 test_color(reporter, readBack, x, SK_ColorRED);
576 x += kDrawnTileSize;
577 test_color(reporter, readBack, x, SK_ColorGREEN);
578 x += kDrawnTileSize;
579 test_color(reporter, readBack, x, SK_ColorBLUE);
580 x += kDrawnTileSize;
581 test_color(reporter, readBack, x, SK_ColorCYAN);
582 x += kDrawnTileSize;
583 test_color(reporter, readBack, x, SK_ColorMAGENTA);
584 x += kDrawnTileSize;
585 test_color(reporter, readBack, x, SK_ColorYELLOW);
586}
587
588#endif