OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright | |
11 * notice, this list of conditions and the following disclaimer in the | |
12 * documentation and/or other materials provided with the distribution. | |
13 * | |
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 | |
28 #include "core/platform/graphics/Canvas2DLayerBridge.h" | |
29 | |
30 #include "GrContext.h" | |
31 #include "SkDevice.h" | |
32 #include "SkSurface.h" | |
33 #include "core/platform/graphics/Canvas2DLayerManager.h" | |
34 #include "core/platform/graphics/GraphicsLayer.h" | |
35 #include "core/platform/graphics/gpu/SharedGraphicsContext3D.h" | |
36 #include "platform/TraceEvent.h" | |
37 #include "public/platform/Platform.h" | |
38 #include "public/platform/WebCompositorSupport.h" | |
39 #include "public/platform/WebGraphicsContext3D.h" | |
40 | |
41 using blink::WebExternalTextureLayer; | |
42 using blink::WebGraphicsContext3D; | |
43 | |
44 namespace WebCore { | |
45 | |
46 void Canvas2DLayerBridgePtr::clear() | |
47 { | |
48 if (m_ptr) { | |
49 m_ptr->destroy(); | |
50 m_ptr.clear(); | |
51 } | |
52 } | |
53 | |
54 Canvas2DLayerBridgePtr& Canvas2DLayerBridgePtr::operator=(const PassRefPtr<Canva
s2DLayerBridge>& other) | |
55 { | |
56 clear(); | |
57 m_ptr = other; | |
58 return *this; | |
59 } | |
60 | |
61 static SkSurface* createSurface(GraphicsContext3D* context3D, const IntSize& siz
e, int msaaSampleCount) | |
62 { | |
63 ASSERT(!context3D->webContext()->isContextLost()); | |
64 GrContext* gr = context3D->grContext(); | |
65 if (!gr) | |
66 return 0; | |
67 gr->resetContext(); | |
68 SkImageInfo info; | |
69 info.fWidth = size.width(); | |
70 info.fHeight = size.height(); | |
71 info.fColorType = kPMColor_SkColorType; | |
72 info.fAlphaType = kPremul_SkAlphaType; | |
73 return SkSurface::NewRenderTarget(gr, info, msaaSampleCount); | |
74 } | |
75 | |
76 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(PassRefPtr<GraphicsC
ontext3D> context, const IntSize& size, OpacityMode opacityMode, int msaaSampleC
ount) | |
77 { | |
78 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation"); | |
79 SkAutoTUnref<SkSurface> surface(createSurface(context.get(), size, msaaSampl
eCount)); | |
80 if (!surface.get()) { | |
81 return PassRefPtr<Canvas2DLayerBridge>(); | |
82 } | |
83 RefPtr<SkDeferredCanvas> canvas = adoptRef(SkDeferredCanvas::Create(surface.
get())); | |
84 RefPtr<Canvas2DLayerBridge> layerBridge = adoptRef(new Canvas2DLayerBridge(c
ontext, canvas.release(), msaaSampleCount, opacityMode)); | |
85 return layerBridge.release(); | |
86 } | |
87 | |
88 Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context,
PassRefPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMod
e) | |
89 : m_canvas(canvas) | |
90 , m_context(context) | |
91 , m_msaaSampleCount(msaaSampleCount) | |
92 , m_bytesAllocated(0) | |
93 , m_didRecordDrawCommand(false) | |
94 , m_surfaceIsValid(true) | |
95 , m_framesPending(0) | |
96 , m_destructionInProgress(false) | |
97 , m_rateLimitingEnabled(false) | |
98 , m_next(0) | |
99 , m_prev(0) | |
100 , m_lastImageId(0) | |
101 { | |
102 ASSERT(m_canvas); | |
103 // Used by browser tests to detect the use of a Canvas2DLayerBridge. | |
104 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation"); | |
105 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createEx
ternalTextureLayer(this)); | |
106 m_layer->setOpaque(opacityMode == Opaque); | |
107 m_layer->setBlendBackgroundColor(opacityMode != Opaque); | |
108 GraphicsLayer::registerContentsLayer(m_layer->layer()); | |
109 m_layer->setRateLimitContext(m_rateLimitingEnabled); | |
110 m_canvas->setNotificationClient(this); | |
111 } | |
112 | |
113 Canvas2DLayerBridge::~Canvas2DLayerBridge() | |
114 { | |
115 ASSERT(m_destructionInProgress); | |
116 m_layer.clear(); | |
117 Vector<MailboxInfo>::iterator mailboxInfo; | |
118 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mai
lboxInfo++) { | |
119 ASSERT(mailboxInfo->m_status != MailboxInUse); | |
120 if (mailboxInfo->m_status == MailboxReleased) { | |
121 if (mailboxInfo->m_mailbox.syncPoint) { | |
122 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); | |
123 mailboxInfo->m_mailbox.syncPoint = 0; | |
124 } | |
125 // Invalidate texture state in case the compositor altered it since
the copy-on-write. | |
126 mailboxInfo->m_image->getTexture()->invalidateCachedState(); | |
127 } | |
128 } | |
129 m_mailboxes.clear(); | |
130 } | |
131 | |
132 void Canvas2DLayerBridge::destroy() | |
133 { | |
134 ASSERT(!m_destructionInProgress); | |
135 m_destructionInProgress = true; | |
136 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); | |
137 m_canvas->setNotificationClient(0); | |
138 m_layer->clearTexture(); | |
139 Canvas2DLayerManager::get().layerToBeDestroyed(this); | |
140 // Orphaning the layer is required to trigger the recration of a new layer | |
141 // in the case where destruction is caused by a canvas resize. Test: | |
142 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html | |
143 m_layer->layer()->removeFromParent(); | |
144 } | |
145 | |
146 void Canvas2DLayerBridge::limitPendingFrames() | |
147 { | |
148 ASSERT(!m_destructionInProgress); | |
149 if (m_didRecordDrawCommand) { | |
150 m_framesPending++; | |
151 m_didRecordDrawCommand = false; | |
152 if (m_framesPending > 1) { | |
153 // Turn on the rate limiter if this layer tends to accumulate a | |
154 // non-discardable multi-frame backlog of draw commands. | |
155 setRateLimitingEnabled(true); | |
156 } | |
157 if (m_rateLimitingEnabled) { | |
158 flush(); | |
159 } | |
160 } | |
161 } | |
162 | |
163 void Canvas2DLayerBridge::prepareForDraw() | |
164 { | |
165 ASSERT(!m_destructionInProgress); | |
166 ASSERT(m_layer); | |
167 if (!isValid()) { | |
168 if (m_canvas) { | |
169 // drop pending commands because there is no surface to draw to | |
170 m_canvas->silentFlush(); | |
171 } | |
172 return; | |
173 } | |
174 m_context->makeContextCurrent(); | |
175 } | |
176 | |
177 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAlloca
ted) | |
178 { | |
179 ASSERT(!m_destructionInProgress); | |
180 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated; | |
181 m_bytesAllocated = bytesAllocated; | |
182 Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, delta); | |
183 } | |
184 | |
185 size_t Canvas2DLayerBridge::storageAllocatedForRecording() | |
186 { | |
187 ASSERT(!m_destructionInProgress); | |
188 return m_canvas->storageAllocatedForRecording(); | |
189 } | |
190 | |
191 void Canvas2DLayerBridge::flushedDrawCommands() | |
192 { | |
193 ASSERT(!m_destructionInProgress); | |
194 storageAllocatedForRecordingChanged(storageAllocatedForRecording()); | |
195 m_framesPending = 0; | |
196 } | |
197 | |
198 void Canvas2DLayerBridge::skippedPendingDrawCommands() | |
199 { | |
200 ASSERT(!m_destructionInProgress); | |
201 // Stop triggering the rate limiter if SkDeferredCanvas is detecting | |
202 // and optimizing overdraw. | |
203 setRateLimitingEnabled(false); | |
204 flushedDrawCommands(); | |
205 } | |
206 | |
207 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled) | |
208 { | |
209 ASSERT(!m_destructionInProgress || !enabled); | |
210 if (m_rateLimitingEnabled != enabled) { | |
211 m_rateLimitingEnabled = enabled; | |
212 m_layer->setRateLimitContext(m_rateLimitingEnabled); | |
213 } | |
214 } | |
215 | |
216 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree) | |
217 { | |
218 ASSERT(!m_destructionInProgress); | |
219 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree); | |
220 if (bytesFreed) | |
221 Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr
_t)bytesFreed)); | |
222 m_bytesAllocated -= bytesFreed; | |
223 return bytesFreed; | |
224 } | |
225 | |
226 void Canvas2DLayerBridge::flush() | |
227 { | |
228 ASSERT(!m_destructionInProgress); | |
229 if (m_canvas->hasPendingCommands()) { | |
230 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush"); | |
231 m_canvas->flush(); | |
232 } | |
233 } | |
234 | |
235 WebGraphicsContext3D* Canvas2DLayerBridge::context() | |
236 { | |
237 // Check on m_layer is necessary because context() may be called during | |
238 // the destruction of m_layer | |
239 if (m_layer) { | |
240 isValid(); // To ensure rate limiter is disabled if context is lost. | |
241 } | |
242 return m_context->webContext(); | |
243 } | |
244 | |
245 bool Canvas2DLayerBridge::isValid() | |
246 { | |
247 ASSERT(m_layer); | |
248 if (m_destructionInProgress) | |
249 return false; | |
250 if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) { | |
251 // Attempt to recover. | |
252 m_layer->clearTexture(); | |
253 m_mailboxes.clear(); | |
254 RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get()
; | |
255 if (!sharedContext || sharedContext->webContext()->isContextLost()) { | |
256 m_surfaceIsValid = false; | |
257 } else { | |
258 m_context = sharedContext; | |
259 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevi
ce()->height()); | |
260 SkAutoTUnref<SkSurface> surface(createSurface(m_context.get(), size,
m_msaaSampleCount)); | |
261 if (surface.get()) { | |
262 m_canvas->setSurface(surface.get()); | |
263 m_surfaceIsValid = true; | |
264 // FIXME: draw sad canvas picture into new buffer crbug.com/2438
42 | |
265 } else { | |
266 // Surface allocation failed. Set m_surfaceIsValid to false to | |
267 // trigger subsequent retry. | |
268 m_surfaceIsValid = false; | |
269 } | |
270 } | |
271 } | |
272 if (!m_surfaceIsValid) | |
273 setRateLimitingEnabled(false); | |
274 return m_surfaceIsValid; | |
275 } | |
276 | |
277 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMa
ilbox, blink::WebExternalBitmap* bitmap) | |
278 { | |
279 if (bitmap) { | |
280 // Using accelerated 2d canvas with software renderer, which | |
281 // should only happen in tests that use fake graphics contexts. | |
282 // In this case, we do not care about producing any results for | |
283 // compositing. | |
284 m_canvas->silentFlush(); | |
285 return false; | |
286 } | |
287 if (!isValid()) | |
288 return false; | |
289 // Release to skia textures that were previouosly released by the | |
290 // compositor. We do this before acquiring the next snapshot in | |
291 // order to cap maximum gpu memory consumption. | |
292 m_context->makeContextCurrent(); | |
293 flush(); | |
294 Vector<MailboxInfo>::iterator mailboxInfo; | |
295 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mai
lboxInfo++) { | |
296 if (mailboxInfo->m_status == MailboxReleased) { | |
297 if (mailboxInfo->m_mailbox.syncPoint) { | |
298 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); | |
299 mailboxInfo->m_mailbox.syncPoint = 0; | |
300 } | |
301 // Invalidate texture state in case the compositor altered it since
the copy-on-write. | |
302 mailboxInfo->m_image->getTexture()->invalidateCachedState(); | |
303 mailboxInfo->m_image.reset(0); | |
304 mailboxInfo->m_status = MailboxAvailable; | |
305 } | |
306 } | |
307 SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot()); | |
308 // Early exit if canvas was not drawn to since last prepareMailbox | |
309 if (image->uniqueID() == m_lastImageId) | |
310 return false; | |
311 m_lastImageId = image->uniqueID(); | |
312 | |
313 mailboxInfo = createMailboxInfo(); | |
314 mailboxInfo->m_status = MailboxInUse; | |
315 mailboxInfo->m_image.swap(&image); | |
316 // Because of texture sharing with the compositor, we must invalidate | |
317 // the state cached in skia so that the deferred copy on write | |
318 // in SkSurface_Gpu does not make any false assumptions. | |
319 mailboxInfo->m_image->getTexture()->invalidateCachedState(); | |
320 | |
321 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0); | |
322 ASSERT(mailboxInfo->m_image.get()); | |
323 ASSERT(mailboxInfo->m_image->getTexture()); | |
324 | |
325 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, mailboxInfo->m_image->
getTexture()->getTextureHandle()); | |
326 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); | |
327 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); | |
328 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); | |
329 m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::T
EXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); | |
330 context()->produceTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, mailboxInfo
->m_mailbox.name); | |
331 context()->flush(); | |
332 mailboxInfo->m_mailbox.syncPoint = context()->insertSyncPoint(); | |
333 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); | |
334 // Because we are changing the texture binding without going through skia, | |
335 // we must dirty the context. | |
336 m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState); | |
337 | |
338 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has | |
339 // live mailboxes | |
340 ASSERT(!mailboxInfo->m_parentLayerBridge); | |
341 mailboxInfo->m_parentLayerBridge = this; | |
342 *outMailbox = mailboxInfo->m_mailbox; | |
343 return true; | |
344 } | |
345 | |
346 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() { | |
347 ASSERT(!m_destructionInProgress); | |
348 MailboxInfo* mailboxInfo; | |
349 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mai
lboxInfo++) { | |
350 if (mailboxInfo->m_status == MailboxAvailable) { | |
351 return mailboxInfo; | |
352 } | |
353 } | |
354 | |
355 // No available mailbox: create one. | |
356 m_mailboxes.grow(m_mailboxes.size() + 1); | |
357 mailboxInfo = &m_mailboxes.last(); | |
358 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name); | |
359 // Worst case, canvas is triple buffered. More than 3 active mailboxes | |
360 // means there is a problem. | |
361 // For the single-threaded case, this value needs to be at least | |
362 // kMaxSwapBuffersPending+1 (in render_widget.h). | |
363 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2. | |
364 // TODO(piman): fix this. | |
365 ASSERT(m_mailboxes.size() <= 4); | |
366 ASSERT(mailboxInfo < m_mailboxes.end()); | |
367 return mailboxInfo; | |
368 } | |
369 | |
370 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox
& mailbox) | |
371 { | |
372 Vector<MailboxInfo>::iterator mailboxInfo; | |
373 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mai
lboxInfo++) { | |
374 if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.na
me))) { | |
375 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint; | |
376 ASSERT(mailboxInfo->m_status == MailboxInUse); | |
377 mailboxInfo->m_status = MailboxReleased; | |
378 // Trigger Canvas2DLayerBridge self-destruction if this is the | |
379 // last live mailbox and the layer bridge is not externally | |
380 // referenced. | |
381 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this); | |
382 mailboxInfo->m_parentLayerBridge.clear(); | |
383 return; | |
384 } | |
385 } | |
386 } | |
387 | |
388 blink::WebLayer* Canvas2DLayerBridge::layer() | |
389 { | |
390 ASSERT(m_layer); | |
391 return m_layer->layer(); | |
392 } | |
393 | |
394 void Canvas2DLayerBridge::contextAcquired() | |
395 { | |
396 ASSERT(!m_destructionInProgress); | |
397 Canvas2DLayerManager::get().layerDidDraw(this); | |
398 m_didRecordDrawCommand = true; | |
399 } | |
400 | |
401 unsigned Canvas2DLayerBridge::backBufferTexture() | |
402 { | |
403 ASSERT(!m_destructionInProgress); | |
404 if (!isValid()) | |
405 return 0; | |
406 contextAcquired(); | |
407 m_canvas->flush(); | |
408 m_context->flush(); | |
409 GrRenderTarget* renderTarget = reinterpret_cast<GrRenderTarget*>(m_canvas->g
etDevice()->accessRenderTarget()); | |
410 if (renderTarget) { | |
411 return renderTarget->asTexture()->getTextureHandle(); | |
412 } | |
413 return 0; | |
414 } | |
415 | |
416 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) { | |
417 // This copy constructor should only be used for Vector reallocation | |
418 // Assuming 'other' is to be destroyed, we swap m_image ownership | |
419 // rather than do a refcount dance. | |
420 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox)); | |
421 m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image)); | |
422 m_status = other.m_status; | |
423 } | |
424 | |
425 } | |
OLD | NEW |