Index: Source/web/ImageDecodeBench.cpp |
diff --git a/Source/web/ImageDecodeBench.cpp b/Source/web/ImageDecodeBench.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f7d4b1edc07ef195984bcc057b81f56f4685ceb0 |
--- /dev/null |
+++ b/Source/web/ImageDecodeBench.cpp |
@@ -0,0 +1,316 @@ |
+/* |
+ * Copyright (C) 2013 Google Inc. All rights reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+#include "config.h" |
+ |
+#include "RuntimeEnabledFeatures.h" |
+#include "platform/SharedBuffer.h" |
+#include "platform/image-decoders/ImageDecoder.h" |
+#include "public/web/WebKit.h" |
+#include "wtf/OwnPtr.h" |
+#include "wtf/PassOwnPtr.h" |
+ |
+#if OS(WINDOWS) |
+#include <mmsystem.h> |
+#include <time.h> |
+#else |
+#include <sys/time.h> |
+#endif |
+ |
+using namespace WebCore; |
+ |
+#if OS(WINDOWS) |
+ |
+// There's no real platform support herein, so adopt the win32 performance counter from WTF. |
+// http://trac.webkit.org/browser/trunk/Source/WTF/wtf/CurrentTime.cpp?rev=152438 |
+ |
+static double lowResUTCTime() |
+{ |
+ FILETIME fileTime; |
+ GetSystemTimeAsFileTime(&fileTime); |
+ |
+ // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a |
+ // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can |
+ // prevent alignment faults on 64-bit Windows). |
+ ULARGE_INTEGER dateTime; |
+ memcpy(&dateTime, &fileTime, sizeof(dateTime)); |
+ |
+ // Number of 100 nanosecond between January 1, 1601 and January 1, 1970. |
+ static const ULONGLONG epochBias = 116444736000000000ULL; |
+ // Windows file times are in 100s of nanoseconds. |
+ static const double hundredsOfNanosecondsPerMillisecond = 10000; |
+ return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond; |
+} |
+ |
+static LARGE_INTEGER qpcFrequency; |
+static bool syncedTime; |
+ |
+static double highResUpTime() |
+{ |
+ // We use QPC, but only after sanity checking its result, due to bugs: |
+ // http://support.microsoft.com/kb/274323 http://support.microsoft.com/kb/895980 |
+ // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("you can get different results |
+ // on different processors due to bugs in the basic input/output system (BIOS) or the |
+ // hardware abstraction layer (HAL)."). |
+ |
+ static LARGE_INTEGER qpcLast; |
+ static DWORD tickCountLast; |
+ static bool inited; |
+ |
+ LARGE_INTEGER qpc; |
+ QueryPerformanceCounter(&qpc); |
+ DWORD tickCount = GetTickCount(); |
+ |
+ if (inited) { |
+ __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart; |
+ __int64 tickCountElapsed; |
+ if (tickCount >= tickCountLast) { |
+ tickCountElapsed = (tickCount - tickCountLast); |
+ } else { |
+ __int64 tickCountLarge = tickCount + 0x100000000I64; |
+ tickCountElapsed = tickCountLarge - tickCountLast; |
+ } |
+ |
+ // Force a re-sync if QueryPerformanceCounter differs from GetTickCount() by more than |
+ // 500ms. (The 500ms value is from http://support.microsoft.com/kb/274323). |
+ __int64 diff = tickCountElapsed - qpcElapsed; |
+ if (diff > 500 || diff < -500) |
+ syncedTime = false; |
+ } else { |
+ inited = true; |
+ } |
+ |
+ qpcLast = qpc; |
+ tickCountLast = tickCount; |
+ |
+ return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart); |
+} |
+ |
+static bool qpcAvailable() |
+{ |
+ static bool available; |
+ static bool checked; |
+ |
+ if (checked) |
+ return available; |
+ |
+ available = QueryPerformanceFrequency(&qpcFrequency); |
+ checked = true; |
+ return available; |
+} |
+ |
+static double getCurrentTime() |
+{ |
+ // Use a combination of ftime and QueryPerformanceCounter. |
+ // ftime returns the information we want, but doesn't have sufficient resolution. |
+ // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals. |
+ // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will |
+ // use QueryPerformanceCounter by itself, adding the delta to the saved ftime. |
+ // We periodically re-sync to correct for drift. |
+ static double syncLowResUTCTime; |
+ static double syncHighResUpTime; |
+ static double lastUTCTime; |
+ |
+ double lowResTime = lowResUTCTime(); |
+ if (!qpcAvailable()) |
+ return lowResTime / 1000.0; |
+ |
+ double highResTime = highResUpTime(); |
+ if (!syncedTime) { |
+ timeBeginPeriod(1); // increase time resolution around low-res time getter |
+ syncLowResUTCTime = lowResTime = lowResUTCTime(); |
+ timeEndPeriod(1); // restore time resolution |
+ syncHighResUpTime = highResTime; |
+ syncedTime = true; |
+ } |
+ |
+ double highResElapsed = highResTime - syncHighResUpTime; |
+ double utc = syncLowResUTCTime + highResElapsed; |
+ |
+ // Force a clock re-sync if we've drifted. |
+ double lowResElapsed = lowResTime - syncLowResUTCTime; |
+ const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy |
+ if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec) |
+ syncedTime = false; |
+ |
+ // Make sure time doesn't run backwards (only correct if the difference is < 2 seconds, |
+ // since DST or clock changes could occur). |
+ const double backwardTimeLimit = 2000.0; |
+ if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit) |
+ return lastUTCTime / 1000.0; |
+ lastUTCTime = utc; |
+ return utc / 1000.0; |
+} |
+ |
+#else |
+ |
+static double getCurrentTime() |
+{ |
+ struct timeval now; |
+ gettimeofday(&now, 0); |
+ return now.tv_sec + now.tv_usec / 1000000.0; |
+} |
+ |
+#endif |
+ |
+static PassRefPtr<SharedBuffer> readFile(const char* fileName) |
+{ |
+ FILE* fp = fopen(fileName, "rb"); |
+ if (!fp) { |
+ fprintf(stderr, "Can't open %s\n", fileName); |
+ exit(1); |
+ } |
+ |
+ fseek(fp, 0, SEEK_END); |
+ size_t fileSize = ftell(fp); |
+ rewind(fp); |
+ |
+ OwnPtr<unsigned char[]> buffer = adoptArrayPtr(new unsigned char[fileSize]); |
+ fread(buffer.get(), 1, fileSize, fp); |
+ fclose(fp); |
+ |
+ return SharedBuffer::create(buffer.get(), fileSize); |
+} |
+ |
+static bool checkFrameDependencies(SharedBuffer* data, size_t& numIndependentFrames, float& avgNumDependentFrames, size_t& maxNumDependentFrames) |
+{ |
+ OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
+ ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnored); |
+ decoder->setData(data, true); |
+ |
+ int frameCount = decoder->frameCount(); |
+ numIndependentFrames = decoder->countIndependentFrames(); |
+ avgNumDependentFrames = decoder->countAverageDependentFrames(); |
+ maxNumDependentFrames = decoder->countMaxDependentFrames(); |
+ return !decoder->failed(); |
+} |
+ |
+static bool decodeImageDataInSequence(SharedBuffer* data) |
+{ |
+ OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
+ ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnored); |
+ decoder->setData(data, true); |
+ |
+ int frameCount = decoder->frameCount(); |
+ for (int i = 0; i < frameCount; ++i) { |
+ if (!decoder->frameBufferAtIndex(i)) |
+ break; |
+ } |
+ |
+ return !decoder->failed(); |
+} |
+ |
+static bool decodeImageDataWithSeeking(SharedBuffer* data) |
+{ |
+ // We decode 5 frames in sequence, then clear the frame buffer cache, |
+ // decode next 5 frames in sequence and so on. |
+ const size_t sequenceSize = 5; |
+ OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
+ ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnored); |
+ decoder->setData(data, true); |
+ |
+ int frameCount = decoder->frameCount(); |
+ for (int i = 0; i < frameCount; ++i) { |
+ if (!(i % sequenceSize)) |
+ decoder->clearCacheExceptFrame(kNotFound); |
+ if (!decoder->frameBufferAtIndex(i)) |
+ break; |
+ } |
+ |
+ return !decoder->failed(); |
+} |
+ |
+int main(int argc, char* argv[]) |
+{ |
+ if (argc < 2) { |
+ fprintf(stderr, "Usage: %s file [iterations]\n", argv[0]); |
+ exit(2); |
+ } |
+ |
+ size_t iterations = 1000; |
+ if (argc >= 3) { |
+ char* end = 0; |
+ iterations = strtol(argv[2], &end, 10); |
+ if (*end != '\0' || !iterations) { |
+ fprintf(stderr, "Second argument should be number of iterations. " |
+ "The default is 1000. You supplied %s\n", argv[2]); |
+ exit(2); |
+ } |
+ } |
+ |
+ class DummyWebKitPlatformSupport : public blink::Platform { |
+ public: |
+ const unsigned char* getTraceCategoryEnabledFlag(const char*) |
+ { return (const unsigned char *) "nope-none-nada"; } |
+ void cryptographicallyRandomValues(unsigned char*, size_t) { } |
+ }; |
+ |
+ blink::initializeWithoutV8(new DummyWebKitPlatformSupport()); |
+ WebCore::RuntimeEnabledFeatures::setAnimatedWebPEnabled(true); |
+ |
+ RefPtr<SharedBuffer> data = readFile(argv[1]); |
+ |
+ size_t numIndependentFrames; |
+ float avgNumDependentFrames; |
+ size_t maxNumDependentFrames; |
+ if (!checkFrameDependencies(data.get(), numIndependentFrames, avgNumDependentFrames, maxNumDependentFrames)) { |
+ fprintf(stderr, "Image decode failed during dependency check.\n"); |
+ exit(3); |
+ } |
+ |
+ double totalTime = 0.0; |
+ for (size_t i = 0; i < iterations; ++i) { |
+ double startTime = getCurrentTime(); |
+ bool decoded = decodeImageDataInSequence(data.get()); |
+ double elapsedTime = getCurrentTime() - startTime; |
+ totalTime += elapsedTime; |
+ if (!decoded) { |
+ fprintf(stderr, "Image decode failed during sequential decoding.\n"); |
+ exit(3); |
+ } |
+ } |
+ double averageTimeSequential = totalTime / static_cast<double>(iterations); |
+ |
+ totalTime = 0.0; |
+ for (size_t i = 0; i < iterations; ++i) { |
+ double startTime = getCurrentTime(); |
+ bool decoded = decodeImageDataWithSeeking(data.get()); |
+ double elapsedTime = getCurrentTime() - startTime; |
+ totalTime += elapsedTime; |
+ if (!decoded) { |
+ fprintf(stderr, "Image decode failed during decoding with seeking.\n"); |
+ exit(3); |
+ } |
+ } |
+ double averageTimeWithSeeking = totalTime / static_cast<double>(iterations); |
+ |
+ printf("%s %f %f %zu %f %zu\n", argv[1], averageTimeSequential, averageTimeWithSeeking, numIndependentFrames, avgNumDependentFrames, maxNumDependentFrames); |
+ return 0; |
+} |