/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkTypes.h" #if SK_SUPPORT_GPU #include "GrContextPriv.h" #include "Test.h" #include "text/GrAtlasGlyphCache.h" static const int kNumPlots = 2; static const int kPlotSize = 32; static const int kAtlasSize = kNumPlots * kPlotSize; int GrDrawOpAtlas::numAllocated_TestingOnly() const { int count = 0; for (uint32_t i = 0; i < this->maxPages(); ++i) { if (fProxies[i]->priv().isInstantiated()) { ++count; } } return count; } void EvictionFunc(GrDrawOpAtlas::AtlasID atlasID, void*) { SkASSERT(0); // The unit test shouldn't exercise this code path } static void check(skiatest::Reporter* r, GrDrawOpAtlas* atlas, uint32_t expectedActive, uint32_t expectedMax, int expectedAlloced) { REPORTER_ASSERT(r, expectedActive == atlas->numActivePages()); REPORTER_ASSERT(r, expectedMax == atlas->maxPages()); REPORTER_ASSERT(r, expectedAlloced == atlas->numAllocated_TestingOnly()); } class TestingUploadTarget : public GrDeferredUploadTarget { public: TestingUploadTarget() { } const GrTokenTracker* tokenTracker() final { return &fTokenTracker; } GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) final { SkASSERT(0); // this test shouldn't invoke this code path return fTokenTracker.nextDrawToken(); } virtual GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&& upload) final { return fTokenTracker.nextTokenToFlush(); } void issueDrawToken() { fTokenTracker.issueDrawToken(); } void flushToken() { fTokenTracker.flushToken(); } private: GrTokenTracker fTokenTracker; typedef GrDeferredUploadTarget INHERITED; }; static bool fill_plot(GrDrawOpAtlas* atlas, GrResourceProvider* resourceProvider, GrDeferredUploadTarget* target, GrDrawOpAtlas::AtlasID* atlasID, int alpha) { SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize); SkBitmap data; data.allocPixels(ii); data.eraseARGB(alpha, 0, 0, 0); SkIPoint16 loc; bool result = atlas->addToAtlas(resourceProvider, atlasID, target, kPlotSize, kPlotSize, data.getAddr(0, 0), &loc); return result; } DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DrawOpAtlas, reporter, ctxInfo) { auto context = ctxInfo.grContext(); auto proxyProvider = context->contextPriv().proxyProvider(); auto resourceProvider = context->contextPriv().resourceProvider(); auto drawingManager = context->contextPriv().drawingManager(); GrOnFlushResourceProvider onFlushResourceProvider(drawingManager); TestingUploadTarget uploadTarget; std::unique_ptr atlas = GrDrawOpAtlas::Make( proxyProvider, kAlpha_8_GrPixelConfig, kAtlasSize, kAtlasSize, kNumPlots, kNumPlots, GrDrawOpAtlas::AllowMultitexturing::kYes, EvictionFunc, nullptr); check(reporter, atlas.get(), 0, 4, 0); // Fill up the first level GrDrawOpAtlas::AtlasID atlasIDs[kNumPlots * kNumPlots]; for (int i = 0; i < kNumPlots * kNumPlots; ++i) { bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasIDs[i], i*32); REPORTER_ASSERT(reporter, result); check(reporter, atlas.get(), 1, 4, 1); } atlas->instantiate(&onFlushResourceProvider); check(reporter, atlas.get(), 1, 4, 1); // Force allocation of a second level GrDrawOpAtlas::AtlasID atlasID; bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasID, 4*32); REPORTER_ASSERT(reporter, result); check(reporter, atlas.get(), 2, 4, 2); // Simulate a lot of draws using only the first plot. The last texture should be compacted. for (int i = 0; i < 512; ++i) { atlas->setLastUseToken(atlasIDs[0], uploadTarget.tokenTracker()->nextDrawToken()); uploadTarget.issueDrawToken(); uploadTarget.flushToken(); atlas->compact(uploadTarget.tokenTracker()->nextTokenToFlush()); } check(reporter, atlas.get(), 1, 4, 1); } #endif