blob: 7932fdc45804cdee5ccb6664e879af20b3107789 [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 Osman9a725dd2017-09-20 09:53:22 -040075 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip*,
76 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon9a036422017-07-13 17:04:43 -040077 // Set the color to unknown because the subclass may change the color later.
78 GrProcessorAnalysisColor gpColor;
79 gpColor.setToUnknown();
80 // We ignore the clip so pass this rather than the GrAppliedClip param.
81 static GrAppliedClip kNoClip;
Brian Osman9a725dd2017-09-20 09:53:22 -040082 return fHelper.xpRequiresDstTexture(caps, &kNoClip, dstIsClamped,
83 GrProcessorAnalysisCoverage::kNone, &gpColor);
Brian Salomon9a036422017-07-13 17:04:43 -040084 }
85
Robert Phillipseb35f4d2017-03-21 07:56:47 -040086protected:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040087 GrColor fColor;
88 bool fHasLocalRect;
89 GrQuad fLocalQuad;
90 SkRect fRect;
91
92private:
Robert Phillipseb35f4d2017-03-21 07:56:47 -040093 bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
94
Brian Salomon91326c32017-08-09 16:02:19 -040095 void onPrepareDraws(Target* target) override {
Robert Phillipseb35f4d2017-03-21 07:56:47 -040096 using namespace GrDefaultGeoProcFactory;
97
98 // The vertex attrib order is always pos, color, local coords.
99 static const int kColorOffset = sizeof(SkPoint);
100 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
101
102 sk_sp<GrGeometryProcessor> gp =
103 GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
104 Coverage::kSolid_Type,
105 fHasLocalRect ? LocalCoords::kHasExplicit_Type
106 : LocalCoords::kUnused_Type,
107 SkMatrix::I());
108 if (!gp) {
109 SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
110 return;
111 }
112
113 size_t vertexStride = gp->getVertexStride();
114
115 SkASSERT(fHasLocalRect
116 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
117 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
118
119 const GrBuffer* indexBuffer;
120 int firstIndex;
121 uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
122 if (!indices) {
123 SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
124 return;
125 }
126
127 const GrBuffer* vertexBuffer;
128 int firstVertex;
129 void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
130 if (!vertices) {
131 SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
132 return;
133 }
134
135 // Setup indices
136 indices[0] = 0;
137 indices[1] = 1;
138 indices[2] = 2;
Brian Salomon57caa662017-10-18 12:21:05 +0000139 indices[3] = 2;
140 indices[4] = 1;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400141 indices[5] = 3;
142
143 // Setup positions
144 SkPoint* position = (SkPoint*) vertices;
Brian Salomonec42e152018-05-18 12:52:22 -0400145 SkPointPriv::SetRectTriStrip(position, fRect, vertexStride);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400146
147 // Setup vertex colors
148 GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
149 for (int i = 0; i < 4; ++i) {
150 *color = fColor;
151 color = (GrColor*)((intptr_t)color + vertexStride);
152 }
153
154 // Setup local coords
155 if (fHasLocalRect) {
156 SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
157 for (int i = 0; i < 4; i++) {
158 *coords = fLocalQuad.point(i);
159 coords = (SkPoint*)((intptr_t) coords + vertexStride);
160 }
161 }
162
Chris Dalton3809bab2017-06-13 10:55:06 -0600163 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -0400164 mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3, GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -0600165 mesh.setVertexData(vertexBuffer, firstVertex);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400166
Brian Salomon9a036422017-07-13 17:04:43 -0400167 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400168 }
169
Brian Salomon9a036422017-07-13 17:04:43 -0400170 Helper fHelper;
171
172 typedef GrMeshDrawOp INHERITED;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400173};
174
Brian Salomon9a036422017-07-13 17:04:43 -0400175} // anonymous namespace
176
Brian Salomon9a036422017-07-13 17:04:43 -0400177static constexpr SkRect kEmptyRect = SkRect::MakeEmpty();
178
179namespace {
180
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400181/*
182 * Atlased ops just draw themselves as textured rects with the texture pixels being
183 * pulled out of the atlas. Their color is based on their ID.
184 */
185class AtlasedRectOp final : public NonAARectOp {
186public:
187 DEFINE_OP_CLASS_ID
188
189 ~AtlasedRectOp() override {
190 fID = -1;
191 }
192
193 const char* name() const override { return "AtlasedRectOp"; }
194
195 int id() const { return fID; }
196
Robert Phillips7c525e62018-06-12 10:11:12 -0400197 static std::unique_ptr<AtlasedRectOp> Make(GrContext* context,
198 GrPaint&& paint,
199 const SkRect& r,
200 int id) {
201 GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(context, std::move(paint),
202 r, id).release();
Brian Salomon9a036422017-07-13 17:04:43 -0400203 return std::unique_ptr<AtlasedRectOp>(static_cast<AtlasedRectOp*>(op));
204 }
205
206 // We set the initial color of the NonAARectOp based on the ID.
207 // Note that we force creation of a NonAARectOp that has local coords in anticipation of
208 // pulling from the atlas.
209 AtlasedRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r, int id)
210 : INHERITED(helperArgs, kColors[id], r, &kEmptyRect, ClassID())
211 , fID(id)
212 , fNext(nullptr) {
213 SkASSERT(fID < kMaxIDs);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400214 }
215
216 void setColor(GrColor color) { fColor = color; }
217 void setLocalRect(const SkRect& localRect) {
218 SkASSERT(fHasLocalRect); // This should've been created to anticipate this
Brian Salomona33b67c2018-05-17 10:42:14 -0400219 fLocalQuad = GrQuad(localRect);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400220 }
221
222 AtlasedRectOp* next() const { return fNext; }
223 void setNext(AtlasedRectOp* next) {
224 fNext = next;
225 }
226
227private:
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400228
229 static const int kMaxIDs = 9;
230 static const SkColor kColors[kMaxIDs];
231
232 int fID;
233 // The Atlased ops have an internal singly-linked list of ops that land in the same opList
234 AtlasedRectOp* fNext;
235
236 typedef NonAARectOp INHERITED;
237};
238
Brian Salomon9a036422017-07-13 17:04:43 -0400239} // anonymous namespace
240
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400241const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
242 GrColorPackRGBA(255, 0, 0, 255),
243 GrColorPackRGBA(0, 255, 0, 255),
244 GrColorPackRGBA(0, 0, 255, 255),
245 GrColorPackRGBA(0, 255, 255, 255),
246 GrColorPackRGBA(255, 0, 255, 255),
247 GrColorPackRGBA(255, 255, 0, 255),
248 GrColorPackRGBA(0, 0, 0, 255),
249 GrColorPackRGBA(128, 128, 128, 255),
250 GrColorPackRGBA(255, 255, 255, 255)
251};
252
253static const int kDrawnTileSize = 16;
254
255/*
256 * Rather than performing any rect packing, this atlaser just lays out constant-sized
257 * tiles in an Nx1 row
258 */
259static const int kAtlasTileSize = 2;
260
261/*
262 * This class aggregates the op information required for atlasing
263 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400264class AtlasObject final : public GrOnFlushCallbackObject {
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400265public:
266 AtlasObject() : fDone(false) { }
267
268 ~AtlasObject() override {
269 SkASSERT(fDone);
270 }
271
272 void markAsDone() {
273 fDone = true;
274 }
275
276 // Insert the new op in an internal singly-linked list for 'opListID'
277 void addOp(uint32_t opListID, AtlasedRectOp* op) {
278 LinkedListHeader* header = nullptr;
279 for (int i = 0; i < fOps.count(); ++i) {
280 if (opListID == fOps[i].fID) {
281 header = &(fOps[i]);
282 }
283 }
284
285 if (!header) {
286 fOps.push({opListID, nullptr});
287 header = &(fOps[fOps.count()-1]);
288 }
289
290 op->setNext(header->fHead);
291 header->fHead = op;
292 }
293
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500294 int numOps() const { return fOps.count(); }
295
296 // Get the fully lazy proxy that is backing the atlas. Its actual width isn't
297 // known until flush time.
298 sk_sp<GrTextureProxy> getAtlasProxy(GrProxyProvider* proxyProvider) {
299 if (fAtlasProxy) {
300 return fAtlasProxy;
301 }
302
303 fAtlasProxy = proxyProvider->createFullyLazyProxy(
304 [](GrResourceProvider* resourceProvider) {
305 if (!resourceProvider) {
306 return sk_sp<GrTexture>();
307 }
308
309 GrSurfaceDesc desc;
310 desc.fFlags = kRenderTarget_GrSurfaceFlag;
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500311 // TODO: until partial flushes in MDB lands we're stuck having
312 // all 9 atlas draws occur
313 desc.fWidth = 9 /*this->numOps()*/ * kAtlasTileSize;
314 desc.fHeight = kAtlasTileSize;
315 desc.fConfig = kRGBA_8888_GrPixelConfig;
316
317 return resourceProvider->createTexture(desc, SkBudgeted::kYes,
318 GrResourceProvider::kNoPendingIO_Flag);
319 },
320 GrProxyProvider::Renderable::kYes,
321 kBottomLeft_GrSurfaceOrigin,
322 kRGBA_8888_GrPixelConfig);
323 return fAtlasProxy;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400324 }
325
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400326 /*
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500327 * This callback creates the atlas and updates the AtlasedRectOps to read from it
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400328 */
Chris Daltonfe199b72017-05-05 11:26:15 -0400329 void preFlush(GrOnFlushResourceProvider* resourceProvider,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400330 const uint32_t* opListIDs, int numOpListIDs,
331 SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
332 SkASSERT(!results->count());
333
334 // Until MDB is landed we will most-likely only have one opList.
335 SkTDArray<LinkedListHeader*> lists;
336 for (int i = 0; i < numOpListIDs; ++i) {
337 if (LinkedListHeader* list = this->getList(opListIDs[i])) {
338 lists.push(list);
339 }
340 }
341
342 if (!lists.count()) {
343 return; // nothing to atlas
344 }
345
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500346 if (!resourceProvider->instatiateProxy(fAtlasProxy.get())) {
347 return;
348 }
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400349
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400350 // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
351 // there should either be two writes to clear it or no writes.
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500352 SkASSERT(9 == fAtlasProxy->getPendingReadCnt_TestOnly());
353 SkASSERT(2 == fAtlasProxy->getPendingWriteCnt_TestOnly() ||
354 0 == fAtlasProxy->getPendingWriteCnt_TestOnly());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400355 sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500356 fAtlasProxy,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400357 nullptr, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400358
Chris Dalton344e9032017-12-11 15:42:09 -0700359 // clear the atlas
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500360 rtc->clear(nullptr, 0x0, GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400361
362 int blocksInAtlas = 0;
363 for (int i = 0; i < lists.count(); ++i) {
364 for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
365 SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
366 kAtlasTileSize, kAtlasTileSize);
367
368 // For now, we avoid the resource buffer issues and just use clears
369#if 1
Chris Dalton344e9032017-12-11 15:42:09 -0700370 rtc->clear(&r, op->color(), GrRenderTargetContext::CanClearFullscreen::kNo);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400371#else
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400372 GrPaint paint;
Brian Salomon9a036422017-07-13 17:04:43 -0400373 paint.setColor4f(GrColor4f::FromGrColor(op->color()));
374 std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint),
375 SkRect::Make(r)));
376 rtc->priv().testingOnly_addDrawOp(std::move(drawOp));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400377#endif
378 blocksInAtlas++;
379
380 // Set the atlased Op's color to white (so we know we're not using it for
381 // the final draw).
382 op->setColor(0xFFFFFFFF);
383
384 // Set the atlased Op's localRect to point to where it landed in the atlas
385 op->setLocalRect(SkRect::Make(r));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400386 }
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
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400392 results->push_back(std::move(rtc));
393 }
394
395private:
396 typedef struct {
397 uint32_t fID;
398 AtlasedRectOp* fHead;
399 } LinkedListHeader;
400
401 LinkedListHeader* getList(uint32_t opListID) {
402 for (int i = 0; i < fOps.count(); ++i) {
403 if (opListID == fOps[i].fID) {
404 return &(fOps[i]);
405 }
406 }
407 return nullptr;
408 }
409
410 void clearOpsFor(LinkedListHeader* header) {
411 // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
412 // forget about them in the laziest way possible.
413 header->fHead = nullptr;
414 header->fID = 0; // invalid opList ID
415 }
416
417 // Each opList containing AtlasedRectOps gets its own internal singly-linked list
418 SkTDArray<LinkedListHeader> fOps;
419
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500420 // The fully lazy proxy for the atlas
421 sk_sp<GrTextureProxy> fAtlasProxy;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400422
423 // Set to true when the testing harness expects this object to be no longer used
424 bool fDone;
425};
426
427// This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
428static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500429 sk_sp<GrTextureProxy> atlasProxy) {
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500430 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400431 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400432 3*kDrawnTileSize,
433 kDrawnTileSize,
434 kRGBA_8888_GrPixelConfig,
435 nullptr));
436
Chris Dalton344e9032017-12-11 15:42:09 -0700437 rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255),
438 GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400439
440 for (int i = 0; i < 3; ++i) {
441 SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
442
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500443 auto fp = GrSimpleTextureEffect::Make(atlasProxy, SkMatrix::I());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400444 GrPaint paint;
445 paint.addColorFragmentProcessor(std::move(fp));
446 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Robert Phillips7c525e62018-06-12 10:11:12 -0400447 std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(context,
448 std::move(paint), r, start + i));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400449
450 AtlasedRectOp* sparePtr = op.get();
451
Brian Salomon9a036422017-07-13 17:04:43 -0400452 uint32_t opListID = rtc->priv().testingOnly_addDrawOp(std::move(op));
Chris Daltonf104fec2018-05-22 16:17:48 -0600453 SkASSERT(SK_InvalidUniqueID != opListID);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400454
455 object->addOp(opListID, sparePtr);
456 }
457
458 return rtc->asTextureProxyRef();
459}
460
461// Enable this if you want to debug the final draws w/o having the atlasCallback create the
462// atlas
463#if 0
Chris Dalton12658942017-10-05 19:45:25 -0600464#include "SkImageEncoder.h"
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400465#include "SkGrPriv.h"
Chris Dalton12658942017-10-05 19:45:25 -0600466#include "sk_tool_utils.h"
467
468static void save_bm(const SkBitmap& bm, const char name[]) {
469 bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
470 SkASSERT(result);
471}
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400472
473sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
474 SkBitmap bm;
475 bm.allocN32Pixels(18, 2, true);
476 bm.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 2, 2));
477 bm.erase(SK_ColorGREEN, SkIRect::MakeXYWH(2, 0, 2, 2));
478 bm.erase(SK_ColorBLUE, SkIRect::MakeXYWH(4, 0, 2, 2));
479 bm.erase(SK_ColorCYAN, SkIRect::MakeXYWH(6, 0, 2, 2));
480 bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
481 bm.erase(SK_ColorYELLOW, SkIRect::MakeXYWH(10, 0, 2, 2));
482 bm.erase(SK_ColorBLACK, SkIRect::MakeXYWH(12, 0, 2, 2));
483 bm.erase(SK_ColorGRAY, SkIRect::MakeXYWH(14, 0, 2, 2));
484 bm.erase(SK_ColorWHITE, SkIRect::MakeXYWH(16, 0, 2, 2));
485
486#if 1
487 save_bm(bm, "atlas-fake.png");
488#endif
489
Brian Osman2b23c4b2018-06-01 12:25:08 -0400490 GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info());
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400491 desc.fFlags |= kRenderTarget_GrSurfaceFlag;
492
493 sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
494 context->textureProvider(),
495 desc, SkBudgeted::kYes,
496 bm.getPixels(), bm.rowBytes());
497
498 return sk_ref_sp(tmp->asTextureProxy());
499}
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400500#endif
501
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500502
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400503static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
504 SkColor readback = bm.getColor(x, kDrawnTileSize/2);
505 REPORTER_ASSERT(reporter, expected == readback);
506 if (expected != readback) {
507 SkDebugf("Color mismatch: %x %x\n", expected, readback);
508 }
509}
510
511/*
512 * For the atlasing test we make a DAG that looks like:
513 *
514 * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8
515 * \ /
516 * \ /
517 * RT4
518 * We then flush RT4 and expect only ops 0-5 to be atlased together.
519 * Each op is just a solid colored rect so both the atlas and the final image should appear as:
520 * R G B C M Y
521 * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
522 *
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500523 * Note: until partial flushes in MDB lands, the atlas will actually have width= 9*kAtlasTileSize
524 * and look like:
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400525 * 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();
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500531 auto proxyProvider = context->contextPriv().proxyProvider();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400532
Chris Daltonfe199b72017-05-05 11:26:15 -0400533 AtlasObject object;
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400534
Chris Daltonfe199b72017-05-05 11:26:15 -0400535 context->contextPriv().addOnFlushCallbackObject(&object);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400536
537 sk_sp<GrTextureProxy> proxies[kNumProxies];
538 for (int i = 0; i < kNumProxies; ++i) {
Robert Phillips7d79e7b2018-02-14 11:09:57 -0500539 proxies[i] = make_upstream_image(context, &object, i*3,
540 object.getAtlasProxy(proxyProvider));
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400541 }
542
543 static const int kFinalWidth = 6*kDrawnTileSize;
544 static const int kFinalHeight = kDrawnTileSize;
545
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500546 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400547 SkBackingFit::kApprox,
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400548 kFinalWidth,
549 kFinalHeight,
550 kRGBA_8888_GrPixelConfig,
551 nullptr));
552
Chris Dalton344e9032017-12-11 15:42:09 -0700553 rtc->clear(nullptr, 0xFFFFFFFF, GrRenderTargetContext::CanClearFullscreen::kYes);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400554
555 // Note that this doesn't include the third texture proxy
556 for (int i = 0; i < kNumProxies-1; ++i) {
557 SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
558
559 SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
560
561 GrPaint paint;
Brian Osman2240be92017-10-18 13:15:13 -0400562 auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), t);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400563 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
564 paint.addColorFragmentProcessor(std::move(fp));
565
566 rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
567 }
568
Greg Daniela5cb7812017-06-16 09:45:32 -0400569 rtc->prepareForExternalIO(0, nullptr);
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400570
571 SkBitmap readBack;
572 readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
573
574 SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
575 readBack.rowBytes(), 0, 0);
576 SkASSERT(result);
577
Chris Daltonfe199b72017-05-05 11:26:15 -0400578 context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
579
580 object.markAsDone();
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400581
Robert Phillipseb35f4d2017-03-21 07:56:47 -0400582 int x = kDrawnTileSize/2;
583 test_color(reporter, readBack, x, SK_ColorRED);
584 x += kDrawnTileSize;
585 test_color(reporter, readBack, x, SK_ColorGREEN);
586 x += kDrawnTileSize;
587 test_color(reporter, readBack, x, SK_ColorBLUE);
588 x += kDrawnTileSize;
589 test_color(reporter, readBack, x, SK_ColorCYAN);
590 x += kDrawnTileSize;
591 test_color(reporter, readBack, x, SK_ColorMAGENTA);
592 x += kDrawnTileSize;
593 test_color(reporter, readBack, x, SK_ColorYELLOW);
594}