#include "public/platform/WebGraphicsContext3DProvider.h"
#include "wtf/RefCountedLeakCounter.h"
-using blink::WebExternalTextureLayer;
-using blink::WebGraphicsContext3D;
-
namespace {
enum {
InvalidMailboxIndex = -1,
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, canvas2DLayerBridgeInstanceCounter, ("Canvas2DLayerBridge"));
}
-namespace WebCore {
+namespace blink {
static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0)
{
if (!gr)
return nullptr;
gr->resetContext();
- SkImageInfo info;
- info.fWidth = size.width();
- info.fHeight = size.height();
- info.fColorType = kPMColor_SkColorType;
- info.fAlphaType = kPremul_SkAlphaType;
+ SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
return adoptRef(SkSurface::NewRenderTarget(gr, info, msaaSampleCount));
}
PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
{
TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
- OwnPtr<blink::WebGraphicsContext3DProvider> contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
+ OwnPtr<WebGraphicsContext3DProvider> contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
if (!contextProvider)
return nullptr;
RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount));
return nullptr;
RefPtr<Canvas2DLayerBridge> layerBridge;
OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
- layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), canvas.release(), msaaSampleCount, opacityMode));
+ layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), canvas.release(), surface.release(), msaaSampleCount, opacityMode));
return layerBridge.release();
}
-Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<blink::WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
+Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, PassRefPtr<SkSurface> surface, int msaaSampleCount, OpacityMode opacityMode)
: m_canvas(canvas)
+ , m_surface(surface)
, m_contextProvider(contextProvider)
, m_imageBuffer(0)
, m_msaaSampleCount(msaaSampleCount)
, m_releasedMailboxInfoIndex(InvalidMailboxIndex)
{
ASSERT(m_canvas);
+ ASSERT(m_surface);
ASSERT(m_contextProvider);
// Used by browser tests to detect the use of a Canvas2DLayerBridge.
TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
- m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
+ m_layer = adoptPtr(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
m_layer->setOpaque(opacityMode == Opaque);
m_layer->setBlendBackgroundColor(opacityMode != Opaque);
GraphicsLayer::registerContentsLayer(m_layer->layer());
ASSERT(!Canvas2DLayerManager::get().isInList(this));
m_layer.clear();
freeReleasedMailbox();
-#if ASSERT_ENABLED
+#if ENABLE(ASSERT)
Vector<MailboxInfo>::iterator mailboxInfo;
for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
ASSERT(mailboxInfo->m_status != MailboxInUse);
m_destructionInProgress = true;
GraphicsLayer::unregisterContentsLayer(m_layer->layer());
m_canvas->setNotificationClient(0);
+ m_surface.clear();
m_canvas.clear();
m_layer->clearTexture();
// Orphaning the layer is required to trigger the recration of a new layer
}
}
+void Canvas2DLayerBridge::willAccessPixels()
+{
+ // A readback operation may alter the texture parameters, which may affect
+ // the compositor's behavior. Therefore, we must trigger copy-on-write
+ // even though we are not technically writing to the texture, only to its
+ // parameters.
+ m_surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+}
+
void Canvas2DLayerBridge::freeTransientResources()
{
ASSERT(!m_destructionInProgress);
+ if (!m_isSurfaceValid)
+ return;
freeReleasedMailbox();
flush();
freeMemoryIfPossible(bytesAllocated());
}
return;
}
- context()->makeContextCurrent();
}
void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
void Canvas2DLayerBridge::freeReleasedMailbox()
{
- if (m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid)
+ if (!m_isSurfaceValid || m_contextProvider->context3d()->isContextLost())
return;
MailboxInfo* mailboxInfo = releasedMailboxInfo();
if (!mailboxInfo)
Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
}
-blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
+WebGraphicsContext3D* Canvas2DLayerBridge::context()
{
// Check on m_layer is necessary because context() may be called during
// the destruction of m_layer
if (m_layer && !m_destructionInProgress)
checkSurfaceValid(); // To ensure rate limiter is disabled if context is lost.
- return m_contextProvider->context3d();
+ return m_contextProvider ? m_contextProvider->context3d() : 0;
}
bool Canvas2DLayerBridge::checkSurfaceValid()
return false;
if (m_contextProvider->context3d()->isContextLost()) {
m_isSurfaceValid = false;
+ m_surface.clear();
if (m_imageBuffer)
m_imageBuffer->notifySurfaceInvalid();
setRateLimitingEnabled(false);
return false;
ASSERT(m_layer && !m_isSurfaceValid);
- blink::WebGraphicsContext3D* sharedContext = 0;
+ WebGraphicsContext3D* sharedContext = 0;
// We must clear the mailboxes before calling m_layer->clearTexture() to prevent
// re-entry via mailboxReleased from operating on defunct GrContext objects.
m_mailboxes.clear();
m_releasedMailboxInfoIndex = InvalidMailboxIndex;
m_layer->clearTexture();
- m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
+ m_contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
if (m_contextProvider)
sharedContext = m_contextProvider->context3d();
IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
if (surface.get()) {
- m_canvas->setSurface(surface.get());
+ m_surface = surface.release();
+ m_canvas->setSurface(m_surface.get());
m_isSurfaceValid = true;
// FIXME: draw sad canvas picture into new buffer crbug.com/243842
}
return m_isSurfaceValid;
}
-bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
+bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox, WebExternalBitmap* bitmap)
{
if (m_destructionInProgress) {
// It can be hit in the following sequence.
if (!checkSurfaceValid())
return false;
- blink::WebGraphicsContext3D* webContext = context();
+ WebGraphicsContext3D* webContext = context();
// Release to skia textures that were previouosly released by the
// compositor. We do this before acquiring the next snapshot in
// order to cap maximum gpu memory consumption.
- webContext->makeContextCurrent();
flush();
RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
ASSERT(mailboxInfo->m_image.get());
+
+ // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
+ // live mailboxes
+ ASSERT(!mailboxInfo->m_parentLayerBridge);
+ mailboxInfo->m_parentLayerBridge = this;
+ *outMailbox = mailboxInfo->m_mailbox;
+
+ GrContext* grContext = m_contextProvider->grContext();
+ if (!grContext)
+ return true; // for testing: skip gl stuff when using a mock graphics context.
+
ASSERT(mailboxInfo->m_image->getTexture());
// Because of texture sharing with the compositor, we must invalidate
webContext->bindTexture(GL_TEXTURE_2D, 0);
// Because we are changing the texture binding without going through skia,
// we must dirty the context.
- m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
+ grContext->resetContext(kTextureBinding_GrGLBackendState);
- // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
- // live mailboxes
- ASSERT(!mailboxInfo->m_parentLayerBridge);
- mailboxInfo->m_parentLayerBridge = this;
- *outMailbox = mailboxInfo->m_mailbox;
return true;
}
return mailboxInfo;
}
-void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
+void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailbox, bool lostResource)
{
freeReleasedMailbox(); // Never have more than one mailbox in the released state.
+ bool contextLost = !m_isSurfaceValid || m_contextProvider->context3d()->isContextLost();
Vector<MailboxInfo>::iterator mailboxInfo;
for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
if (nameEquals(mailboxInfo->m_mailbox, mailbox)) {
mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
ASSERT(mailboxInfo->m_status == MailboxInUse);
- mailboxInfo->m_status = MailboxReleased;
+ ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
+
+ if (contextLost) {
+ // No need to clean up the mailbox resource, but make sure the
+ // mailbox can also be reusable once the context is restored.
+ mailboxInfo->m_status = MailboxAvailable;
+ m_releasedMailboxInfoIndex = InvalidMailboxIndex;
+ Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
+ } else if (lostResource) {
+ // In case of the resource is lost, we need to delete the backing
+ // texture and remove the mailbox from list to avoid reusing it
+ // in future.
+ if (mailboxInfo->m_image) {
+ GrTexture* texture = mailboxInfo->m_image->getTexture();
+ if (texture) {
+ texture->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
+ texture->textureParamsModified();
+ }
+ mailboxInfo->m_image.clear();
+ }
+ if (m_destructionInProgress) {
+ mailboxInfo->m_status = MailboxAvailable; // To satisfy assert in destructor
+
+ // The following line may trigger self destruction. We do not care about
+ // not cleaning up m_mailboxes during destruction sequence because
+ // mailboxes will not be recycled after this point. Calling remove()
+ // could trigger a memory use after free, so we just clear the self
+ // reference to be safe, and we let the Canvas2DLayerBridge destructor
+ // take care of freeing m_mailboxes.
+ mailboxInfo->m_parentLayerBridge.clear();
+ } else {
+ size_t i = mailboxInfo - m_mailboxes.begin();
+ m_mailboxes.remove(i); // indirectly clears mailboxInfo->m_parentLayerBridge
+ Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
+ }
+ // mailboxInfo is not valid from this point, so we return immediately.
+ return;
+ } else {
+ mailboxInfo->m_status = MailboxReleased;
+ m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
+ m_framesSinceMailboxRelease = 0;
+ if (isHidden()) {
+ freeReleasedMailbox();
+ } else {
+ ASSERT(!m_destructionInProgress);
+ Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
+ }
+ }
// Trigger Canvas2DLayerBridge self-destruction if this is the
// last live mailbox and the layer bridge is not externally
// referenced.
- m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
- m_framesSinceMailboxRelease = 0;
- if (isHidden()) {
- freeReleasedMailbox();
- } else {
- ASSERT(!m_destructionInProgress);
- Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
- }
- ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
mailboxInfo->m_parentLayerBridge.clear();
return;
}
}
}
-blink::WebLayer* Canvas2DLayerBridge::layer() const
+WebLayer* Canvas2DLayerBridge::layer() const
{
ASSERT(!m_destructionInProgress);
ASSERT(m_layer);
return m_layer->layer();
}
-void Canvas2DLayerBridge::willUse()
+void Canvas2DLayerBridge::finalizeFrame(const FloatRect &dirtyRect)
{
ASSERT(!m_destructionInProgress);
Canvas2DLayerManager::get().layerDidDraw(this);
+ m_layer->layer()->invalidateRect(dirtyRect);
m_didRecordDrawCommand = true;
}
ASSERT(!m_destructionInProgress);
if (!checkSurfaceValid())
return 0;
- willUse();
m_canvas->flush();
context()->flush();
GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
// This copy constructor should only be used for Vector reallocation
- // Assuming 'other' is to be destroyed, we transfer m_image ownership
- // rather than do a refcount dance.
+ // Assuming 'other' is to be destroyed, we transfer m_image and
+ // m_parentLayerBridge ownership rather than do a refcount dance.
memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
+ m_parentLayerBridge = const_cast<MailboxInfo*>(&other)->m_parentLayerBridge.release();
m_status = other.m_status;
}
-}
+} // namespace blink