| Index: third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
|
| diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
|
| index 2e1415f3d71a7ff11178f96465dce446042c4aa1..d0c082add4c2fb6da7454e845a3c9bd48435ef30 100644
|
| --- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
|
| +++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
|
| @@ -5,6 +5,7 @@
|
| #include "CanvasAsyncBlobCreator.h"
|
|
|
| #include "core/fileapi/Blob.h"
|
| +#include "platform/Task.h"
|
| #include "platform/ThreadSafeFunctional.h"
|
| #include "platform/graphics/ImageBuffer.h"
|
| #include "platform/heap/Handle.h"
|
| @@ -26,21 +27,6 @@
|
| const double SlackBeforeDeadline = 0.001; // a small slack period between deadline and current time for safety
|
| const int NumChannelsPng = 4;
|
| const int LongTaskImageSizeThreshold = 1000 * 1000; // The max image size we expect to encode in 14ms on Linux in PNG format
|
| -
|
| -// The encoding task is highly likely to switch from idle task to alternative
|
| -// code path when the startTimeoutDelay is set to be below 150ms. As we want the
|
| -// majority of encoding tasks to take the usual async idle task, we set a
|
| -// lenient limit -- 200ms here.
|
| -const double IdleTaskStartTimeoutDelay = 200.0;
|
| -// We should be more lenient on completion timeout delay to ensure that the
|
| -// switch from idle to main thread only happens to a minority of toBlob calls
|
| -#if !OS(ANDROID)
|
| -// Png image encoding on 4k by 4k canvas on Mac HDD takes 5.7+ seconds
|
| -const double IdleTaskCompleteTimeoutDelay = 6700.0;
|
| -#else
|
| -// Png image encoding on 4k by 4k canvas on Android One takes 9.0+ seconds
|
| -const double IdleTaskCompleteTimeoutDelay = 10000.0;
|
| -#endif
|
|
|
| bool isDeadlineNearOrPassed(double deadlineSeconds)
|
| {
|
| @@ -64,7 +50,6 @@
|
| ASSERT(m_data->length() == (unsigned) (size.height() * size.width() * 4));
|
| m_encodedImage = adoptPtr(new Vector<unsigned char>());
|
| m_pixelRowStride = size.width() * NumChannelsPng;
|
| - m_idleTaskStatus = IdleTaskStatus::Default;
|
| m_numRowsCompleted = 0;
|
| }
|
|
|
| @@ -74,23 +59,18 @@
|
|
|
| void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation(bool canUseIdlePeriodScheduling, double quality)
|
| {
|
| + // TODO: async blob creation should be supported in worker_pool threads as well. but right now blink does not have that
|
| ASSERT(isMainThread());
|
|
|
| // Make self-reference to keep this object alive until the final task completes
|
| m_selfRef = this;
|
|
|
| + // At the time being, progressive encoding is only applicable to png image format,
|
| + // and thus idle tasks scheduling can only be applied to png image format.
|
| + // TODO(xlai): Progressive encoding on jpeg and webp image formats (crbug.com/571398, crbug.com/571399)
|
| if (canUseIdlePeriodScheduling) {
|
| - // At the time being, progressive encoding is only applicable to png image format,
|
| - // and thus idle tasks scheduling can only be applied to png image format.
|
| - // TODO(xlai): Progressive encoding on jpeg and webp image formats (crbug.com/571398, crbug.com/571399)
|
| ASSERT(m_mimeType == "image/png");
|
| - m_idleTaskStatus = IdleTaskStatus::IdleTaskNotStarted;
|
| - m_alternativeSelfRef = this;
|
| -
|
| - this->scheduleInitiatePngEncoding();
|
| - // We post the below task to check if the above idle task isn't late.
|
| - // There's no risk of concurrency as both tasks are on main thread.
|
| - this->postDelayedTaskToMainThread(BLINK_FROM_HERE, new Task(bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent, this, quality)), IdleTaskStartTimeoutDelay);
|
| + Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this));
|
| } else if (m_mimeType == "image/jpeg") {
|
| Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::initiateJpegEncoding, this, quality));
|
| } else {
|
| @@ -104,55 +84,40 @@
|
| m_jpegEncoderState = JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get());
|
| if (!m_jpegEncoderState) {
|
| Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr));
|
| - clearSelfReference();
|
| + m_selfRef.clear();
|
| return;
|
| }
|
| BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTask : BackgroundTaskRunner::TaskSizeShortRunningTask;
|
| BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, AllowCrossThreadAccess(this), quality), taskSize);
|
| }
|
|
|
| -void CanvasAsyncBlobCreator::scheduleInitiatePngEncoding()
|
| -{
|
| - Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this));
|
| -}
|
| -
|
| void CanvasAsyncBlobCreator::initiatePngEncoding(double deadlineSeconds)
|
| {
|
| ASSERT(isMainThread());
|
| - if (m_idleTaskStatus == IdleTaskStatus::IdleTaskSwitchedToMainThreadTask) {
|
| - // Clear the selfRef as the encoding task has already been carried out
|
| - // on alternative code path.
|
| - clearSelfReference();
|
| + m_pngEncoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get());
|
| + if (!m_pngEncoderState) {
|
| + Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr));
|
| + m_selfRef.clear();
|
| return;
|
| }
|
|
|
| - ASSERT(m_idleTaskStatus == IdleTaskStatus::IdleTaskNotStarted);
|
| - m_idleTaskStatus = IdleTaskStatus::IdleTaskStarted;
|
| + CanvasAsyncBlobCreator::idleEncodeRowsPng(deadlineSeconds);
|
| +}
|
|
|
| - if (!initializePngStruct()) {
|
| - m_idleTaskStatus = IdleTaskStatus::IdleTaskFailed;
|
| - // Clears the selfRef as the idle task has failed.
|
| - clearSelfReference();
|
| - return;
|
| - }
|
| - this->idleEncodeRowsPng(deadlineSeconds);
|
| +void CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng()
|
| +{
|
| + ASSERT(isMainThread());
|
| + Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind<double>(&CanvasAsyncBlobCreator::idleEncodeRowsPng, this));
|
| }
|
|
|
| void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds)
|
| {
|
| ASSERT(isMainThread());
|
| - if (m_idleTaskStatus == IdleTaskStatus::IdleTaskSwitchedToMainThreadTask) {
|
| - // Clear the selfRef as the encoding task has already been carried out
|
| - // on alternative code path.
|
| - clearSelfReference();
|
| - return;
|
| - }
|
| -
|
| unsigned char* inputPixels = m_data->data() + m_pixelRowStride * m_numRowsCompleted;
|
| for (int y = m_numRowsCompleted; y < m_size.height(); ++y) {
|
| if (isDeadlineNearOrPassed(deadlineSeconds)) {
|
| m_numRowsCompleted = y;
|
| - Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::idleEncodeRowsPng, this));
|
| + CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng();
|
| return;
|
| }
|
| PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get());
|
| @@ -161,33 +126,11 @@
|
| m_numRowsCompleted = m_size.height();
|
| PNGImageEncoder::finalizePng(m_pngEncoderState.get());
|
|
|
| - m_idleTaskStatus = IdleTaskStatus::IdleTaskCompleted;
|
| -
|
| if (isDeadlineNearOrPassed(deadlineSeconds)) {
|
| Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::createBlobAndCall, this));
|
| } else {
|
| this->createBlobAndCall();
|
| }
|
| -
|
| - // Clears the selfRef as the idle task has come to a completion.
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::clearSelfReference, this));
|
| -}
|
| -
|
| -void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread()
|
| -{
|
| - ASSERT(m_idleTaskStatus == IdleTaskStatus::IdleTaskSwitchedToMainThreadTask);
|
| -
|
| - // Continue encoding from the last completed row
|
| - unsigned char* inputPixels = m_data->data() + m_pixelRowStride * m_numRowsCompleted;
|
| - for (int y = m_numRowsCompleted; y < m_size.height(); ++y) {
|
| - PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get());
|
| - inputPixels += m_pixelRowStride;
|
| - }
|
| - PNGImageEncoder::finalizePng(m_pngEncoderState.get());
|
| - this->createBlobAndCall();
|
| -
|
| - // Clears alternative selfRef as the alternative code path has completed.
|
| - this->clearAlternativeSelfReference();
|
| }
|
|
|
| void CanvasAsyncBlobCreator::createBlobAndCall()
|
| @@ -195,43 +138,20 @@
|
| ASSERT(isMainThread());
|
| Blob* resultBlob = Blob::create(m_encodedImage->data(), m_encodedImage->size(), m_mimeType);
|
| Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, resultBlob));
|
| -}
|
| -
|
| -void CanvasAsyncBlobCreator::createNullptrAndCall()
|
| -{
|
| - ASSERT(isMainThread());
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr));
|
| + clearSelfReference(); // self-destruct once job is done.
|
| }
|
|
|
| void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality)
|
| {
|
| ASSERT(!isMainThread());
|
|
|
| - bool encodeSuccess;
|
| if (m_mimeType == "image/jpeg") {
|
| JPEGImageEncoder::encodeWithPreInitializedState(m_jpegEncoderState.get(), m_data->data());
|
| - encodeSuccess = true;
|
| - } else {
|
| - encodeSuccess = ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get());
|
| + } else if (!ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get())) {
|
| + scheduleCreateNullptrAndCallOnMainThread();
|
| }
|
|
|
| - if (encodeSuccess) {
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::createBlobAndCall, AllowCrossThreadAccess(this)));
|
| - } else {
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&BlobCallback::handleEvent, m_callback.get(), nullptr));
|
| - }
|
| -
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::clearSelfReference, AllowCrossThreadAccess(this)));
|
| -}
|
| -
|
| -bool CanvasAsyncBlobCreator::initializePngStruct()
|
| -{
|
| - m_pngEncoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get());
|
| - if (!m_pngEncoderState) {
|
| - this->createNullptrAndCall();
|
| - return false;
|
| - }
|
| - return true;
|
| + scheduleCreateBlobAndCallOnMainThread();
|
| }
|
|
|
| void CanvasAsyncBlobCreator::clearSelfReference()
|
| @@ -242,55 +162,17 @@
|
| m_selfRef.clear();
|
| }
|
|
|
| -void CanvasAsyncBlobCreator::clearAlternativeSelfReference()
|
| +void CanvasAsyncBlobCreator::scheduleCreateBlobAndCallOnMainThread()
|
| {
|
| - ASSERT(isMainThread());
|
| - m_alternativeSelfRef.clear();
|
| + ASSERT(!isMainThread());
|
| + Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::createBlobAndCall, AllowCrossThreadAccess(this)));
|
| }
|
|
|
| -void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality)
|
| +void CanvasAsyncBlobCreator::scheduleCreateNullptrAndCallOnMainThread()
|
| {
|
| - if (m_idleTaskStatus == IdleTaskStatus::IdleTaskStarted) {
|
| - // Even if the task started quickly, we still want to ensure completion
|
| - this->postDelayedTaskToMainThread(BLINK_FROM_HERE, new Task(bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent, this)), IdleTaskCompleteTimeoutDelay);
|
| - } else if (m_idleTaskStatus == IdleTaskStatus::IdleTaskNotStarted) {
|
| - // If the idle task does not start after a delay threshold, we will
|
| - // force it to happen on main thread (even though it may cause more
|
| - // janks) to prevent toBlob being postponed forever in extreme cases.
|
| - m_idleTaskStatus = IdleTaskStatus::IdleTaskSwitchedToMainThreadTask;
|
| - signalTaskSwitchInStartTimeoutEventForTesting();
|
| - if (initializePngStruct()) {
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this));
|
| - } else {
|
| - this->clearAlternativeSelfReference(); // As it fails on alternative path
|
| - }
|
| - } else {
|
| - // No need to hold the alternativeSelfRef as the alternative code path
|
| - // will not occur for IdleTaskFailed and IdleTaskCompleted
|
| - this->clearAlternativeSelfReference();
|
| - }
|
| -
|
| -}
|
| -
|
| -void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent()
|
| -{
|
| - ASSERT(m_idleTaskStatus != IdleTaskStatus::IdleTaskNotStarted);
|
| -
|
| - if (m_idleTaskStatus == IdleTaskStatus::IdleTaskStarted) {
|
| - // It has taken too long to complete for the idle task.
|
| - m_idleTaskStatus = IdleTaskStatus::IdleTaskSwitchedToMainThreadTask;
|
| - signalTaskSwitchInCompleteTimeoutEventForTesting();
|
| - Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this));
|
| - } else {
|
| - // No need to hold the alternativeSelfRef as the alternative code path
|
| - // will not occur for IdleTaskFailed and IdleTaskCompleted
|
| - this->clearAlternativeSelfReference();
|
| - }
|
| -}
|
| -
|
| -void CanvasAsyncBlobCreator::postDelayedTaskToMainThread(const WebTraceLocation& location, Task* task, double delayMs)
|
| -{
|
| - Platform::current()->mainThread()->taskRunner()->postDelayedTask(location, task, delayMs);
|
| + ASSERT(!isMainThread());
|
| + Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&BlobCallback::handleEvent, m_callback.get(), nullptr));
|
| + Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::clearSelfReference, AllowCrossThreadAccess(this)));
|
| }
|
|
|
| } // namespace blink
|
|
|