Chromium Code Reviews| Index: third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp |
| diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp |
| index e04ea2b304f96f0bc3e3c80e013ff43918bbac47..ad845e0c00c16bc9e2ec85307c9b8a5251c6ba6e 100644 |
| --- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp |
| +++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp |
| @@ -69,31 +69,45 @@ using namespace HTMLNames; |
| namespace { |
| // These values come from the WhatWG spec. |
| -const int DefaultWidth = 300; |
| -const int DefaultHeight = 150; |
| +const int kDefaultWidth = 300; |
|
Justin Novosad
2015/12/02 16:33:33
The k prefix is a skia thing. In Blink use Capital
danakj
2015/12/02 18:23:40
It's also a chromium thing, I didn't know blink ha
danakj
2015/12/02 18:31:09
FWIW I only see a blink-specific rule for enums: "
|
| +const int kDefaultHeight = 150; |
| // Firefox limits width/height to 32767 pixels, but slows down dramatically before it |
| // reaches that limit. We limit by area instead, giving us larger maximum dimensions, |
| // in exchange for a smaller maximum canvas size. |
| -const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels |
| +const int kMaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels |
| // In Skia, we will also limit width/height to 32767. |
| -const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels. |
| +const int kMaxSkiaDim = 32767; // Maximum width/height in CSS pixels. |
| + |
| +// We estimate the max limit of GPU allocated memory for canvases before Chrome becomes laggy by setting the |
| +// total allocated memory for accelerated canvases to be equivalent to memory used by 80 accelerated |
| +// canvases, each has a size of 1000*500 and 2d context. |
| +// Each such canvas occupies 6000000 = 1000 * 500 * 3 * 4 bytes, where 3 is the bufferCount in |
| +// updateExternallyAllocatedMemory() and 4 means four bytes per pixel per buffer. |
| +#if !OS(ANDROID) |
| +const int kMaxTotalExternallyAllocatedMemory = 6000000 * 80; |
| +#else |
| +// We estimate that the max limit for android phones is half of that for desktops based on local |
| +// experimental results on Android One; though a very slight lagginess on Android One is still expected, |
| +// other Android devices should work fine. |
| +const int kMaxTotalExternallyAllocatedMemory = 6000000 * 40; |
| +#endif |
| // A default value of quality argument for toDataURL and toBlob |
| // It is in an invalid range (outside 0.0 - 1.0) so that it will not be misinterpreted as a user-input value |
| -const int UndefinedQualityValue = -1.0; |
| +const int kUndefinedQualityValue = -1.0; |
| // Default image mime type for toDataURL and toBlob functions |
| -const char DefaultMimeType[] = "image/png"; |
| +const char kDefaultMimeType[] = "image/png"; |
| bool canCreateImageBuffer(const IntSize& size) |
| { |
| if (size.isEmpty()) |
| return false; |
| - if (size.width() * size.height() > MaxCanvasArea) |
| + if (size.width() * size.height() > kMaxCanvasArea) |
| return false; |
| - if (size.width() > MaxSkiaDim || size.height() > MaxSkiaDim) |
| + if (size.width() > kMaxSkiaDim || size.height() > kMaxSkiaDim) |
| return false; |
| return true; |
| } |
| @@ -111,7 +125,7 @@ PassRefPtr<Image> createTransparentImage(const IntSize& size) |
| inline HTMLCanvasElement::HTMLCanvasElement(Document& document) |
| : HTMLElement(canvasTag, document) |
| , DocumentVisibilityObserver(document) |
| - , m_size(DefaultWidth, DefaultHeight) |
| + , m_size(kDefaultWidth, kDefaultHeight) |
| , m_ignoreReset(false) |
| , m_externallyAllocatedMemory(0) |
| , m_originClean(true) |
| @@ -124,9 +138,13 @@ inline HTMLCanvasElement::HTMLCanvasElement(Document& document) |
| DEFINE_NODE_FACTORY(HTMLCanvasElement) |
| +intptr_t HTMLCanvasElement::s_totalMemoryForAcceleratedCanvases = 0; |
| + |
| HTMLCanvasElement::~HTMLCanvasElement() |
| { |
| v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory); |
| + if (m_imageBuffer && m_imageBuffer->isAccelerated()) |
| + updateTotalMemoryForAcceleratedCanvases(-m_externallyAllocatedMemory); |
| #if !ENABLE(OILPAN) |
| // Ensure these go away before the ImageBuffer. |
| m_context.clear(); |
| @@ -347,11 +365,11 @@ void HTMLCanvasElement::reset() |
| int w = getAttribute(widthAttr).toInt(&ok); |
| if (!ok || w < 0) |
| - w = DefaultWidth; |
| + w = kDefaultWidth; |
| int h = getAttribute(heightAttr).toInt(&ok); |
| if (!ok || h < 0) |
| - h = DefaultHeight; |
| + h = kDefaultHeight; |
| if (m_context && m_context->is2d()) |
| m_context->reset(); |
| @@ -451,14 +469,14 @@ String HTMLCanvasElement::toEncodingMimeType(const String& mimeType) |
| // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread). |
| if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType)) |
| - lowercaseMimeType = DefaultMimeType; |
| + lowercaseMimeType = kDefaultMimeType; |
| return lowercaseMimeType; |
| } |
| const AtomicString HTMLCanvasElement::imageSourceURL() const |
| { |
| - return AtomicString(toDataURLInternal(DefaultMimeType, 0, FrontBuffer)); |
| + return AtomicString(toDataURLInternal(kDefaultMimeType, 0, FrontBuffer)); |
| } |
| void HTMLCanvasElement::prepareSurfaceForPaintingIfNeeded() const |
| @@ -521,7 +539,7 @@ String HTMLCanvasElement::toDataURL(const String& mimeType, const ScriptValue& q |
| exceptionState.throwSecurityError("Tainted canvases may not be exported."); |
| return String(); |
| } |
| - double quality = UndefinedQualityValue; |
| + double quality = kUndefinedQualityValue; |
| if (!qualityArgument.isEmpty()) { |
| v8::Local<v8::Value> v8Value = qualityArgument.v8Value(); |
| if (v8Value->IsNumber()) { |
| @@ -544,7 +562,7 @@ void HTMLCanvasElement::toBlob(FileCallback* callback, const String& mimeType, c |
| return; |
| } |
| - double quality = UndefinedQualityValue; |
| + double quality = kUndefinedQualityValue; |
| if (!qualityArgument.isEmpty()) { |
| v8::Local<v8::Value> v8Value = qualityArgument.v8Value(); |
| if (v8Value->IsNumber()) { |
| @@ -561,7 +579,7 @@ void HTMLCanvasElement::toBlob(FileCallback* callback, const String& mimeType, c |
| RefPtr<DOMUint8ClampedArray> imageDataRef(imageData->data()); |
| RefPtr<CanvasAsyncBlobCreator> asyncCreatorRef = CanvasAsyncBlobCreator::create(imageDataRef.release(), encodingMimeType, imageData->size(), callback); |
| - if (Platform::current()->isThreadedCompositingEnabled() && (encodingMimeType == DefaultMimeType)) { |
| + if (Platform::current()->isThreadedCompositingEnabled() && (encodingMimeType == kDefaultMimeType)) { |
| asyncCreatorRef->scheduleAsyncBlobCreation(true); |
| } else { |
| asyncCreatorRef->scheduleAsyncBlobCreation(false, quality); |
| @@ -616,6 +634,12 @@ bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const |
| if (!Platform::current()->canAccelerate2dCanvas()) |
| return false; |
| + // When GPU allocated memory runs low (due to having created too many accelerated canvases), the |
| + // compositor starves and browser becomes laggy. Thus, we should stop allocating more GPU memory to |
| + // new canvases created when the current memory usage exceeds the threshold. |
| + if (s_totalMemoryForAcceleratedCanvases >= kMaxTotalExternallyAllocatedMemory) |
|
Justin Novosad
2015/12/02 16:33:34
Constant should be named in a way that reflects th
|
| + return false; |
| + |
| return true; |
| } |
| @@ -776,8 +800,25 @@ void HTMLCanvasElement::updateExternallyAllocatedMemory() const |
| externallyAllocatedMemory = std::numeric_limits<intptr_t>::max(); |
| // Subtracting two intptr_t that are known to be positive will never underflow. |
| - v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(externallyAllocatedMemory - m_externallyAllocatedMemory); |
| + intptr_t diffFromCurrAllocatedMemory = externallyAllocatedMemory - m_externallyAllocatedMemory; |
| + v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(diffFromCurrAllocatedMemory); |
| m_externallyAllocatedMemory = externallyAllocatedMemory; |
| + |
| + if (m_imageBuffer && m_imageBuffer->isAccelerated()) |
| + updateTotalMemoryForAcceleratedCanvases(diffFromCurrAllocatedMemory); |
|
Justin Novosad
2015/12/02 16:33:34
This is wrong. You need to track gpu allocated mem
|
| +} |
| + |
| +void HTMLCanvasElement::updateTotalMemoryForAcceleratedCanvases(intptr_t diffFromCurrAllocatedMemory) |
| +{ |
| + ASSERT(isMainThread()); |
| + |
| + Checked<intptr_t, RecordOverflow> checkedExternallyAllocatedMemory = s_totalMemoryForAcceleratedCanvases; |
|
Justin Novosad
2015/12/02 16:33:33
Don't use the term "ExternallyAllocated" here, it
|
| + checkedExternallyAllocatedMemory += diffFromCurrAllocatedMemory; |
| + intptr_t externallyAllocatedMemory; |
| + if (checkedExternallyAllocatedMemory.safeGet(externallyAllocatedMemory) == CheckedState::DidOverflow) |
| + externallyAllocatedMemory = std::numeric_limits<intptr_t>::max(); |
| + |
| + s_totalMemoryForAcceleratedCanvases = externallyAllocatedMemory; |
| } |
| SkCanvas* HTMLCanvasElement::drawingCanvas() const |