OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * |
| 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are |
| 6 * met: |
| 7 * |
| 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. |
| 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ |
| 30 |
| 31 #include "config.h" |
| 32 |
| 33 #include "RuntimeEnabledFeatures.h" |
| 34 #include "platform/SharedBuffer.h" |
| 35 #include "platform/image-decoders/ImageDecoder.h" |
| 36 #include "public/web/WebKit.h" |
| 37 #include "wtf/OwnPtr.h" |
| 38 #include "wtf/PassOwnPtr.h" |
| 39 |
| 40 #if OS(WINDOWS) |
| 41 #include <mmsystem.h> |
| 42 #include <time.h> |
| 43 #else |
| 44 #include <sys/time.h> |
| 45 #endif |
| 46 |
| 47 using namespace WebCore; |
| 48 |
| 49 #if OS(WINDOWS) |
| 50 |
| 51 // There's no real platform support herein, so adopt the win32 performance count
er from WTF. |
| 52 // http://trac.webkit.org/browser/trunk/Source/WTF/wtf/CurrentTime.cpp?rev=15243
8 |
| 53 |
| 54 static double lowResUTCTime() |
| 55 { |
| 56 FILETIME fileTime; |
| 57 GetSystemTimeAsFileTime(&fileTime); |
| 58 |
| 59 // As per Windows documentation for FILETIME, copy the resulting FILETIME st
ructure to a |
| 60 // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct ass
ignment can |
| 61 // prevent alignment faults on 64-bit Windows). |
| 62 ULARGE_INTEGER dateTime; |
| 63 memcpy(&dateTime, &fileTime, sizeof(dateTime)); |
| 64 |
| 65 // Number of 100 nanosecond between January 1, 1601 and January 1, 1970. |
| 66 static const ULONGLONG epochBias = 116444736000000000ULL; |
| 67 // Windows file times are in 100s of nanoseconds. |
| 68 static const double hundredsOfNanosecondsPerMillisecond = 10000; |
| 69 return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond
; |
| 70 } |
| 71 |
| 72 static LARGE_INTEGER qpcFrequency; |
| 73 static bool syncedTime; |
| 74 |
| 75 static double highResUpTime() |
| 76 { |
| 77 // We use QPC, but only after sanity checking its result, due to bugs: |
| 78 // http://support.microsoft.com/kb/274323 http://support.microsoft.com/kb/89
5980 |
| 79 // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("you can get diffe
rent results |
| 80 // on different processors due to bugs in the basic input/output system (BIO
S) or the |
| 81 // hardware abstraction layer (HAL)."). |
| 82 |
| 83 static LARGE_INTEGER qpcLast; |
| 84 static DWORD tickCountLast; |
| 85 static bool inited; |
| 86 |
| 87 LARGE_INTEGER qpc; |
| 88 QueryPerformanceCounter(&qpc); |
| 89 DWORD tickCount = GetTickCount(); |
| 90 |
| 91 if (inited) { |
| 92 __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFre
quency.QuadPart; |
| 93 __int64 tickCountElapsed; |
| 94 if (tickCount >= tickCountLast) { |
| 95 tickCountElapsed = (tickCount - tickCountLast); |
| 96 } else { |
| 97 __int64 tickCountLarge = tickCount + 0x100000000I64; |
| 98 tickCountElapsed = tickCountLarge - tickCountLast; |
| 99 } |
| 100 |
| 101 // Force a re-sync if QueryPerformanceCounter differs from GetTickCount(
) by more than |
| 102 // 500ms. (The 500ms value is from http://support.microsoft.com/kb/27432
3). |
| 103 __int64 diff = tickCountElapsed - qpcElapsed; |
| 104 if (diff > 500 || diff < -500) |
| 105 syncedTime = false; |
| 106 } else { |
| 107 inited = true; |
| 108 } |
| 109 |
| 110 qpcLast = qpc; |
| 111 tickCountLast = tickCount; |
| 112 |
| 113 return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart); |
| 114 } |
| 115 |
| 116 static bool qpcAvailable() |
| 117 { |
| 118 static bool available; |
| 119 static bool checked; |
| 120 |
| 121 if (checked) |
| 122 return available; |
| 123 |
| 124 available = QueryPerformanceFrequency(&qpcFrequency); |
| 125 checked = true; |
| 126 return available; |
| 127 } |
| 128 |
| 129 static double getCurrentTime() |
| 130 { |
| 131 // Use a combination of ftime and QueryPerformanceCounter. |
| 132 // ftime returns the information we want, but doesn't have sufficient resolu
tion. |
| 133 // QueryPerformanceCounter has high resolution, but is only usable to measur
e time intervals. |
| 134 // To combine them, we call ftime and QueryPerformanceCounter initially. Lat
er calls will |
| 135 // use QueryPerformanceCounter by itself, adding the delta to the saved ftim
e. |
| 136 // We periodically re-sync to correct for drift. |
| 137 static double syncLowResUTCTime; |
| 138 static double syncHighResUpTime; |
| 139 static double lastUTCTime; |
| 140 |
| 141 double lowResTime = lowResUTCTime(); |
| 142 if (!qpcAvailable()) |
| 143 return lowResTime / 1000.0; |
| 144 |
| 145 double highResTime = highResUpTime(); |
| 146 if (!syncedTime) { |
| 147 timeBeginPeriod(1); // increase time resolution around low-res time gett
er |
| 148 syncLowResUTCTime = lowResTime = lowResUTCTime(); |
| 149 timeEndPeriod(1); // restore time resolution |
| 150 syncHighResUpTime = highResTime; |
| 151 syncedTime = true; |
| 152 } |
| 153 |
| 154 double highResElapsed = highResTime - syncHighResUpTime; |
| 155 double utc = syncLowResUTCTime + highResElapsed; |
| 156 |
| 157 // Force a clock re-sync if we've drifted. |
| 158 double lowResElapsed = lowResTime - syncLowResUTCTime; |
| 159 const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-r
es accuracy |
| 160 if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec) |
| 161 syncedTime = false; |
| 162 |
| 163 // Make sure time doesn't run backwards (only correct if the difference is <
2 seconds, |
| 164 // since DST or clock changes could occur). |
| 165 const double backwardTimeLimit = 2000.0; |
| 166 if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit) |
| 167 return lastUTCTime / 1000.0; |
| 168 lastUTCTime = utc; |
| 169 return utc / 1000.0; |
| 170 } |
| 171 |
| 172 #else |
| 173 |
| 174 static double getCurrentTime() |
| 175 { |
| 176 struct timeval now; |
| 177 gettimeofday(&now, 0); |
| 178 return now.tv_sec + now.tv_usec / 1000000.0; |
| 179 } |
| 180 |
| 181 #endif |
| 182 |
| 183 static PassRefPtr<SharedBuffer> readFile(const char* fileName) |
| 184 { |
| 185 FILE* fp = fopen(fileName, "rb"); |
| 186 if (!fp) { |
| 187 fprintf(stderr, "Can't open %s\n", fileName); |
| 188 exit(1); |
| 189 } |
| 190 |
| 191 fseek(fp, 0, SEEK_END); |
| 192 size_t fileSize = ftell(fp); |
| 193 rewind(fp); |
| 194 |
| 195 OwnPtr<unsigned char[]> buffer = adoptArrayPtr(new unsigned char[fileSize]); |
| 196 fread(buffer.get(), 1, fileSize, fp); |
| 197 fclose(fp); |
| 198 |
| 199 return SharedBuffer::create(buffer.get(), fileSize); |
| 200 } |
| 201 |
| 202 static bool checkFrameDependencies(SharedBuffer* data, size_t& numIndependentFra
mes, float& avgNumDependentFrames, size_t& maxNumDependentFrames) |
| 203 { |
| 204 OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
| 205 ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnore
d); |
| 206 decoder->setData(data, true); |
| 207 |
| 208 int frameCount = decoder->frameCount(); |
| 209 numIndependentFrames = decoder->countIndependentFrames(); |
| 210 avgNumDependentFrames = decoder->countAverageDependentFrames(); |
| 211 maxNumDependentFrames = decoder->countMaxDependentFrames(); |
| 212 return !decoder->failed(); |
| 213 } |
| 214 |
| 215 static bool decodeImageDataInSequence(SharedBuffer* data) |
| 216 { |
| 217 OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
| 218 ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnore
d); |
| 219 decoder->setData(data, true); |
| 220 |
| 221 int frameCount = decoder->frameCount(); |
| 222 for (int i = 0; i < frameCount; ++i) { |
| 223 if (!decoder->frameBufferAtIndex(i)) |
| 224 break; |
| 225 } |
| 226 |
| 227 return !decoder->failed(); |
| 228 } |
| 229 |
| 230 static bool decodeImageDataWithSeeking(SharedBuffer* data) |
| 231 { |
| 232 // We decode 5 frames in sequence, then clear the frame buffer cache, |
| 233 // decode next 5 frames in sequence and so on. |
| 234 const size_t sequenceSize = 5; |
| 235 OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
| 236 ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnore
d); |
| 237 decoder->setData(data, true); |
| 238 |
| 239 int frameCount = decoder->frameCount(); |
| 240 for (int i = 0; i < frameCount; ++i) { |
| 241 if (!(i % sequenceSize)) |
| 242 decoder->clearCacheExceptFrame(kNotFound); |
| 243 if (!decoder->frameBufferAtIndex(i)) |
| 244 break; |
| 245 } |
| 246 |
| 247 return !decoder->failed(); |
| 248 } |
| 249 |
| 250 int main(int argc, char* argv[]) |
| 251 { |
| 252 if (argc < 2) { |
| 253 fprintf(stderr, "Usage: %s file [iterations]\n", argv[0]); |
| 254 exit(2); |
| 255 } |
| 256 |
| 257 size_t iterations = 1000; |
| 258 if (argc >= 3) { |
| 259 char* end = 0; |
| 260 iterations = strtol(argv[2], &end, 10); |
| 261 if (*end != '\0' || !iterations) { |
| 262 fprintf(stderr, "Second argument should be number of iterations. " |
| 263 "The default is 1000. You supplied %s\n", argv[2]); |
| 264 exit(2); |
| 265 } |
| 266 } |
| 267 |
| 268 class DummyWebKitPlatformSupport : public blink::Platform { |
| 269 public: |
| 270 const unsigned char* getTraceCategoryEnabledFlag(const char*) |
| 271 { return (const unsigned char *) "nope-none-nada"; } |
| 272 void cryptographicallyRandomValues(unsigned char*, size_t) { } |
| 273 }; |
| 274 |
| 275 blink::initializeWithoutV8(new DummyWebKitPlatformSupport()); |
| 276 WebCore::RuntimeEnabledFeatures::setAnimatedWebPEnabled(true); |
| 277 |
| 278 RefPtr<SharedBuffer> data = readFile(argv[1]); |
| 279 |
| 280 size_t numIndependentFrames; |
| 281 float avgNumDependentFrames; |
| 282 size_t maxNumDependentFrames; |
| 283 if (!checkFrameDependencies(data.get(), numIndependentFrames, avgNumDependen
tFrames, maxNumDependentFrames)) { |
| 284 fprintf(stderr, "Image decode failed during dependency check.\n"); |
| 285 exit(3); |
| 286 } |
| 287 |
| 288 double totalTime = 0.0; |
| 289 for (size_t i = 0; i < iterations; ++i) { |
| 290 double startTime = getCurrentTime(); |
| 291 bool decoded = decodeImageDataInSequence(data.get()); |
| 292 double elapsedTime = getCurrentTime() - startTime; |
| 293 totalTime += elapsedTime; |
| 294 if (!decoded) { |
| 295 fprintf(stderr, "Image decode failed during sequential decoding.\n")
; |
| 296 exit(3); |
| 297 } |
| 298 } |
| 299 double averageTimeSequential = totalTime / static_cast<double>(iterations); |
| 300 |
| 301 totalTime = 0.0; |
| 302 for (size_t i = 0; i < iterations; ++i) { |
| 303 double startTime = getCurrentTime(); |
| 304 bool decoded = decodeImageDataWithSeeking(data.get()); |
| 305 double elapsedTime = getCurrentTime() - startTime; |
| 306 totalTime += elapsedTime; |
| 307 if (!decoded) { |
| 308 fprintf(stderr, "Image decode failed during decoding with seeking.\n
"); |
| 309 exit(3); |
| 310 } |
| 311 } |
| 312 double averageTimeWithSeeking = totalTime / static_cast<double>(iterations); |
| 313 |
| 314 printf("%s %f %f %zu %f %zu\n", argv[1], averageTimeSequential, averageTimeW
ithSeeking, numIndependentFrames, avgNumDependentFrames, maxNumDependentFrames); |
| 315 return 0; |
| 316 } |
OLD | NEW |