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 |