Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Unified Diff: third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp

Issue 1732913002: Revert of toBlob: If idle image encoding is postponed for too long, switch to main thread (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698