TestSurfaceCache.cpp (5905B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "gtest/gtest.h" 7 8 #include "Common.h" 9 #include "imgIContainer.h" 10 #include "ImageFactory.h" 11 #include "mozilla/gfx/2D.h" 12 #include "mozilla/RefPtr.h" 13 #include "mozilla/StaticPrefs_image.h" 14 #include "nsIInputStream.h" 15 #include "nsString.h" 16 #include "ProgressTracker.h" 17 18 using namespace mozilla; 19 using namespace mozilla::gfx; 20 using namespace mozilla::image; 21 22 class ImageSurfaceCache : public ::testing::Test { 23 protected: 24 AutoInitializeImageLib mInit; 25 }; 26 27 TEST_F(ImageSurfaceCache, Factor2) { 28 ImageTestCase testCase = GreenPNGTestCase(); 29 30 // Create an image. 31 RefPtr<Image> image = ImageFactory::CreateAnonymousImage( 32 nsDependentCString(testCase.mMimeType)); 33 ASSERT_TRUE(!image->HasError()); 34 35 nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); 36 ASSERT_TRUE(inputStream); 37 38 // Figure out how much data we have. 39 uint64_t length; 40 nsresult rv = inputStream->Available(&length); 41 ASSERT_NS_SUCCEEDED(rv); 42 43 // Ensures we meet the threshold for FLAG_SYNC_DECODE_IF_FAST to do sync 44 // decoding without the implications of FLAG_SYNC_DECODE. 45 ASSERT_LT(length, 46 static_cast<uint64_t>( 47 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup())); 48 49 // Write the data into the image. 50 rv = image->OnImageDataAvailable(nullptr, inputStream, 0, 51 static_cast<uint32_t>(length)); 52 ASSERT_NS_SUCCEEDED(rv); 53 54 // Let the image know we've sent all the data. 55 rv = image->OnImageDataComplete(nullptr, NS_OK, true); 56 ASSERT_NS_SUCCEEDED(rv); 57 58 RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); 59 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); 60 61 const uint32_t whichFrame = imgIContainer::FRAME_CURRENT; 62 63 // FLAG_SYNC_DECODE will make RasterImage::LookupFrame use 64 // SurfaceCache::Lookup to force an exact match lookup (and potential decode). 65 const uint32_t exactFlags = imgIContainer::FLAG_HIGH_QUALITY_SCALING | 66 imgIContainer::FLAG_SYNC_DECODE; 67 68 // If the data stream is small enough, as we assert above, 69 // FLAG_SYNC_DECODE_IF_FAST will allow us to decode sync, but avoid forcing 70 // SurfaceCache::Lookup. Instead it will use SurfaceCache::LookupBestMatch. 71 const uint32_t bestMatchFlags = imgIContainer::FLAG_HIGH_QUALITY_SCALING | 72 imgIContainer::FLAG_SYNC_DECODE_IF_FAST; 73 74 // We need the default threshold to be enabled (otherwise we should disable 75 // this test). 76 int32_t threshold = StaticPrefs::image_cache_factor2_threshold_surfaces(); 77 ASSERT_TRUE(threshold >= 0); 78 79 // We need to know what the native sizes are, otherwise factor of 2 mode will 80 // be disabled. 81 size_t nativeSizes = image->GetNativeSizesLength(); 82 ASSERT_EQ(nativeSizes, 1u); 83 84 // Threshold is the native size count and the pref threshold added together. 85 // Make sure the image is big enough that we can simply decrement and divide 86 // off the size as we please and not hit unexpected duplicates. 87 int32_t totalThreshold = static_cast<int32_t>(nativeSizes) + threshold; 88 ASSERT_TRUE(testCase.mSize.width > totalThreshold * 4); 89 90 // Request a bunch of slightly different sizes. We won't trip factor of 2 mode 91 // in this loop. 92 IntSize size = testCase.mSize; 93 for (int32_t i = 0; i <= totalThreshold; ++i) { 94 RefPtr<SourceSurface> surf = 95 image->GetFrameAtSize(size, whichFrame, bestMatchFlags); 96 ASSERT_TRUE(surf); 97 EXPECT_EQ(surf->GetSize(), size); 98 99 size.width -= 1; 100 size.height -= 1; 101 } 102 103 // Now let's ask for a new size. Despite this being sync, it will return 104 // the closest factor of 2 size we have and not the requested size. 105 RefPtr<SourceSurface> surf = 106 image->GetFrameAtSize(size, whichFrame, bestMatchFlags); 107 ASSERT_TRUE(surf); 108 109 EXPECT_EQ(surf->GetSize(), testCase.mSize); 110 111 // Now we should be in factor of 2 mode but unless we trigger a decode no 112 // pruning of the old sized surfaces should happen. 113 size = testCase.mSize; 114 for (int32_t i = 0; i < totalThreshold; ++i) { 115 RefPtr<SourceSurface> surf = 116 image->GetFrameAtSize(size, whichFrame, bestMatchFlags); 117 ASSERT_TRUE(surf); 118 EXPECT_EQ(surf->GetSize(), size); 119 120 size.width -= 1; 121 size.height -= 1; 122 } 123 124 // Now force an existing surface to be marked as explicit so that it 125 // won't get freed upon pruning (gets marked in the Lookup). 126 size.width += 1; 127 size.height += 1; 128 surf = image->GetFrameAtSize(size, whichFrame, exactFlags); 129 ASSERT_TRUE(surf); 130 EXPECT_EQ(surf->GetSize(), size); 131 132 // Now force a new decode to happen by getting a new factor of 2 size. 133 size.width = testCase.mSize.width / 2 - 1; 134 size.height = testCase.mSize.height / 2 - 1; 135 surf = image->GetFrameAtSize(size, whichFrame, bestMatchFlags); 136 ASSERT_TRUE(surf); 137 EXPECT_EQ(surf->GetSize().width, testCase.mSize.width / 2); 138 EXPECT_EQ(surf->GetSize().height, testCase.mSize.height / 2); 139 140 // The decode above would have forced a pruning to happen, so now if 141 // we request all of the sizes we used to have decoded, only the explicit 142 // size should have been kept. 143 size = testCase.mSize; 144 for (int32_t i = 0; i < totalThreshold - 1; ++i) { 145 RefPtr<SourceSurface> surf = 146 image->GetFrameAtSize(size, whichFrame, bestMatchFlags); 147 ASSERT_TRUE(surf); 148 EXPECT_EQ(surf->GetSize(), testCase.mSize); 149 150 size.width -= 1; 151 size.height -= 1; 152 } 153 154 // This lookup finds the surface that already existed that we later marked 155 // as explicit. It should still exist after pruning. 156 surf = image->GetFrameAtSize(size, whichFrame, bestMatchFlags); 157 ASSERT_TRUE(surf); 158 EXPECT_EQ(surf->GetSize(), size); 159 }