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

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

Issue 1938103002: Reland 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: Layout tests leak fixed Created 4 years, 8 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 6d7c97bc792f6226a2281c4ed28272348fc48765..86acd77dab2b4b29382b64ffd2ec6394a39acbbb 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
@@ -26,6 +26,22 @@ const double SlackBeforeDeadline = 0.001; // a small slack period between deadli
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. This limit still needs to be short enough for
+// the latency to be negligible to the user.
+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)
{
return (deadlineSeconds - SlackBeforeDeadline - monotonicallyIncreasingTime() <= 0);
@@ -47,6 +63,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, const
ASSERT(m_data->length() == (unsigned) (size.height() * size.width() * 4));
m_encodedImage = adoptPtr(new Vector<unsigned char>());
m_pixelRowStride = size.width() * NumChannelsPng;
+ m_idleTaskStatus = IdleTaskNotSupported;
m_numRowsCompleted = 0;
}
@@ -56,15 +73,19 @@ CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator()
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());
- // 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");
- Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this));
+ m_idleTaskStatus = IdleTaskNotStarted;
+
+ 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 SameThreadTask(bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent, this, quality)), IdleTaskStartTimeoutDelay);
} else if (m_mimeType == "image/jpeg") {
Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::initiateJpegEncoding, this, quality));
} else {
@@ -77,39 +98,47 @@ void CanvasAsyncBlobCreator::initiateJpegEncoding(const double& quality)
{
m_jpegEncoderState = JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get());
if (!m_jpegEncoderState) {
- Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr));
+ this->createNullAndInvokeCallback();
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());
- m_pngEncoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get());
- if (!m_pngEncoderState) {
- Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr));
+ if (m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask) {
return;
}
- CanvasAsyncBlobCreator::idleEncodeRowsPng(deadlineSeconds);
-}
+ ASSERT(m_idleTaskStatus == IdleTaskNotStarted);
+ m_idleTaskStatus = IdleTaskStarted;
-void CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng()
-{
- ASSERT(isMainThread());
- Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind<double>(&CanvasAsyncBlobCreator::idleEncodeRowsPng, this));
+ if (!initializePngStruct()) {
+ m_idleTaskStatus = IdleTaskFailed;
+ return;
+ }
+ this->idleEncodeRowsPng(deadlineSeconds);
}
void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds)
{
ASSERT(isMainThread());
+ if (m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask) {
+ 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;
- CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng();
+ Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::idleEncodeRowsPng, this));
return;
}
PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get());
@@ -118,17 +147,50 @@ void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds)
m_numRowsCompleted = m_size.height();
PNGImageEncoder::finalizePng(m_pngEncoderState.get());
- if (isDeadlineNearOrPassed(deadlineSeconds))
- Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::createBlobAndCall, this));
- else
- this->createBlobAndCall();
+ m_idleTaskStatus = IdleTaskCompleted;
+
+ if (isDeadlineNearOrPassed(deadlineSeconds)) {
+ Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::createBlobAndInvokeCallback, this));
+ } else {
+ this->createBlobAndInvokeCallback();
+ }
}
-void CanvasAsyncBlobCreator::createBlobAndCall()
+void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread()
+{
+ ASSERT(m_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->createBlobAndInvokeCallback();
+
+ this->signalAlternativeCodePathFinishedForTesting();
+}
+
+void CanvasAsyncBlobCreator::createBlobAndInvokeCallback()
{
ASSERT(isMainThread());
Blob* resultBlob = Blob::create(m_encodedImage->data(), m_encodedImage->size(), m_mimeType);
Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, resultBlob));
+ // Since toBlob is done, timeout events are no longer needed. So we clear
+ // non-GC members to allow teardown of CanvasAsyncBlobCreator.
+ m_data.clear();
+ m_callback.clear();
+}
+
+void CanvasAsyncBlobCreator::createNullAndInvokeCallback()
+{
+ ASSERT(isMainThread());
+ Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr));
+ // Since toBlob is done (failed), timeout events are no longer needed. So we
+ // clear non-GC members to allow teardown of CanvasAsyncBlobCreator.
+ m_data.clear();
+ m_callback.clear();
}
void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality)
@@ -146,7 +208,62 @@ void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality)
return;
}
- Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::createBlobAndCall, AllowCrossThreadAccess(this)));
+ Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::createBlobAndInvokeCallback, AllowCrossThreadAccess(this)));
+}
+
+bool CanvasAsyncBlobCreator::initializePngStruct()
+{
+ m_pngEncoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get());
+ if (!m_pngEncoderState) {
+ this->createNullAndInvokeCallback();
+ return false;
+ }
+ return true;
+}
+
+
+void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality)
+{
+ if (m_idleTaskStatus == IdleTaskStarted) {
+ // Even if the task started quickly, we still want to ensure completion
+ this->postDelayedTaskToMainThread(BLINK_FROM_HERE, new SameThreadTask(bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent, this)), IdleTaskCompleteTimeoutDelay);
+ } else if (m_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 = IdleTaskSwitchedToMainThreadTask;
+ signalTaskSwitchInStartTimeoutEventForTesting();
+ if (initializePngStruct()) {
+ Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this));
+ } else {
+ // Failing in initialization of png struct
+ this->signalAlternativeCodePathFinishedForTesting();
+ }
+ } else {
+ ASSERT(m_idleTaskStatus == IdleTaskFailed || m_idleTaskStatus == IdleTaskCompleted);
+ this->signalAlternativeCodePathFinishedForTesting();
+ }
+
+}
+
+void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent()
+{
+ ASSERT(m_idleTaskStatus != IdleTaskNotStarted);
+
+ if (m_idleTaskStatus == IdleTaskStarted) {
+ // It has taken too long to complete for the idle task.
+ m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask;
+ signalTaskSwitchInCompleteTimeoutEventForTesting();
+ Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this));
+ } else {
+ ASSERT(m_idleTaskStatus == IdleTaskFailed || m_idleTaskStatus == IdleTaskCompleted);
+ this->signalAlternativeCodePathFinishedForTesting();
+ }
+}
+
+void CanvasAsyncBlobCreator::postDelayedTaskToMainThread(const WebTraceLocation& location, SameThreadTask* task, double delayMs)
+{
+ Platform::current()->mainThread()->getWebTaskRunner()->postDelayedTask(location, task, delayMs);
}
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698