void Canvas2DLayerBridge::didProcessTask() { TRACE_EVENT0("cc", "Canvas2DLayerBridge::didProcessTask"); ASSERT(m_isRegisteredTaskObserver); // If m_renderTaskProcessedForCurrentFrame is already set to true, // it means that rendering tasks are not synchronized with the compositor // (i.e. not using requestAnimationFrame), so we are at risk of posting // a multi-frame backlog to the GPU if (m_renderingTaskCompletedForCurrentFrame) { if (isAccelerated()) { flushGpu(); if (!m_rateLimiter) { m_rateLimiter = SharedContextRateLimiter::create(MaxCanvasAnimationBacklog); } } else { flush(); } } if (m_rateLimiter) { m_rateLimiter->tick(); } m_renderingTaskCompletedForCurrentFrame = true; unregisterTaskObserver(); }
double KeyframeAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); #if USE(ACCELERATED_COMPOSITING) if (t != 0 || preActive()) return t; // A return value of 0 means we need service. But if we only have accelerated animations we // only need service at the end of the transition HashSet<int>::const_iterator endProperties = m_keyframes.endProperties(); bool acceleratedPropertiesOnly = true; for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { if (!animationOfPropertyIsAccelerated(*it) || !isAccelerated()) { acceleratedPropertiesOnly = false; break; } } if (acceleratedPropertiesOnly) { bool isLooping; getTimeToNextEvent(t, isLooping); } #endif return t; }
bool Canvas2DLayerBridge::restoreSurface() { ASSERT(!m_destructionInProgress); if (m_destructionInProgress) return false; ASSERT(isAccelerated() && !m_surface); WebGraphicsContext3D* sharedContext = 0; m_layer->clearTexture(); m_contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider()); if (m_contextProvider) sharedContext = m_contextProvider->context3d(); if (sharedContext && !sharedContext->isContextLost()) { GrContext* grCtx = m_contextProvider->grContext(); bool surfaceIsAccelerated; RefPtr<SkSurface> surface(createSkSurface(grCtx, m_size, m_msaaSampleCount, m_opacityMode, &surfaceIsAccelerated)); // Current paradigm does support switching from accelerated to non-accelerated, which would be tricky // due to changes to the layer tree, which can only happen at specific times during the document lifecycle. // Therefore, we can only accept the restored surface if it is accelerated. if (surface.get() && surfaceIsAccelerated) { m_surface = surface.release(); // FIXME: draw sad canvas picture into new buffer crbug.com/243842 } } return m_surface; }
void Canvas2DLayerBridge::flushGpu() { TRACE_EVENT0("cc", "Canvas2DLayerBridge::flushGpu"); flush(); WebGraphicsContext3D* webContext = context(); if (isAccelerated() && webContext) webContext->flush(); }
bool PlatformContextSkia::isNativeFontRenderingAllowed() { #if USE(SKIA_TEXT) return false; #else if (isAccelerated()) return false; return skia::SupportsPlatformPaint(m_canvas); #endif }
void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailbox, bool lostResource) { ASSERT(isAccelerated()); bool contextLost = !m_surface || m_contextProvider->context3d()->isContextLost(); ASSERT(m_mailboxes.last().m_parentLayerBridge.get() == this); // Mailboxes are typically released in FIFO order, so we iterate // from the end of m_mailboxes. auto releasedMailboxInfo = m_mailboxes.end(); auto firstMailbox = m_mailboxes.begin(); while (true) { --releasedMailboxInfo; if (nameEquals(releasedMailboxInfo->m_mailbox, mailbox)) { break; } ASSERT(releasedMailboxInfo != firstMailbox); } if (!contextLost) { // Invalidate texture state in case the compositor altered it since the copy-on-write. if (releasedMailboxInfo->m_image) { if (mailbox.validSyncToken) { context()->waitSyncToken(mailbox.syncToken); } GrTexture* texture = releasedMailboxInfo->m_image->getTexture(); if (texture) { if (lostResource) { texture->abandon(); } else { texture->textureParamsModified(); } } } } RefPtr<Canvas2DLayerBridge> selfRef; if (m_destructionInProgress) { // To avoid memory use after free, take a scoped self-reference // to postpone destruction until the end of this function. selfRef = this; } // The destruction of 'releasedMailboxInfo' will: // 1) Release the self reference held by the mailboxInfo, which may trigger // the self-destruction of this Canvas2DLayerBridge // 2) Release the SkImage, which will return the texture to skia's scratch // texture pool. m_mailboxes.remove(releasedMailboxInfo); }
double ImplicitAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); #if USE(ACCELERATED_COMPOSITING) if (t != 0 || preActive()) return t; // A return value of 0 means we need service. But if this is an accelerated animation we // only need service at the end of the transition. if (animationOfPropertyIsAccelerated(m_animatingProperty) && isAccelerated()) { bool isLooping; getTimeToNextEvent(t, isLooping); } #endif return t; }
double KeyframeAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); if (t != 0 || preActive()) return t; // A return value of 0 means we need service. But if we only have accelerated animations we // only need service at the end of the transition bool acceleratedPropertiesOnly = true; for (auto propertyID : m_keyframes.properties()) { if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(propertyID) || !isAccelerated()) { acceleratedPropertiesOnly = false; break; } } if (acceleratedPropertiesOnly) { bool isLooping; getTimeToNextEvent(t, isLooping); } return t; }
bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox, WebExternalBitmap* bitmap) { ASSERT(isAccelerated()); if (m_destructionInProgress) { // It can be hit in the following sequence. // 1. Canvas draws something. // 2. The compositor begins the frame. // 3. Javascript makes a context be lost. // 4. Here. return false; } if (bitmap) { // Using accelerated 2d canvas with software renderer, which // should only happen in tests that use fake graphics contexts // or in Android WebView in software mode. In this case, we do // not care about producing any results for this canvas. skipQueuedDrawCommands(); m_lastImageId = 0; return false; } if (!checkSurfaceValid()) return false; WebGraphicsContext3D* webContext = context(); RefPtr<SkImage> image = newImageSnapshot(PreferAcceleration); // Early exit if canvas was not drawn to since last prepareMailbox GLenum filter = m_filterQuality == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR; if (image->uniqueID() == m_lastImageId && filter == m_lastFilter) return false; m_lastImageId = image->uniqueID(); m_lastFilter = filter; { MailboxInfo tmp; tmp.m_image = image; tmp.m_parentLayerBridge = this; m_mailboxes.prepend(tmp); } MailboxInfo& mailboxInfo = m_mailboxes.first(); mailboxInfo.m_mailbox.nearestNeighbor = filter == GL_NEAREST; GrContext* grContext = m_contextProvider->grContext(); if (!grContext) return true; // for testing: skip gl stuff when using a mock graphics context. // Need to flush skia's internal queue because texture is about to be accessed directly grContext->flush(); ASSERT(image->getTexture()); // Because of texture sharing with the compositor, we must invalidate // the state cached in skia so that the deferred copy on write // in SkSurface_Gpu does not make any false assumptions. mailboxInfo.m_image->getTexture()->textureParamsModified(); webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo.m_image->getTexture()->getTextureHandle()); webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Re-use the texture's existing mailbox, if there is one. if (image->getTexture()->getCustomData()) { ASSERT(image->getTexture()->getCustomData()->size() == sizeof(mailboxInfo.m_mailbox.name)); memcpy(&mailboxInfo.m_mailbox.name[0], image->getTexture()->getCustomData()->data(), sizeof(mailboxInfo.m_mailbox.name)); } else { context()->genMailboxCHROMIUM(mailboxInfo.m_mailbox.name); RefPtr<SkData> mailboxNameData = adoptRef(SkData::NewWithCopy(&mailboxInfo.m_mailbox.name[0], sizeof(mailboxInfo.m_mailbox.name))); image->getTexture()->setCustomData(mailboxNameData.get()); webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo.m_mailbox.name); } if (isHidden()) { // With hidden canvases, we release the SkImage immediately because // there is no need for animations to be double buffered. mailboxInfo.m_image.clear(); } else { // FIXME: We'd rather insert a syncpoint than perform a flush here, // but currentlythe canvas will flicker if we don't flush here. webContext->flush(); // mailboxInfo.m_mailbox.syncPoint = webContext->insertSyncPoint(); } webContext->bindTexture(GL_TEXTURE_2D, 0); // Because we are changing the texture binding without going through skia, // we must dirty the context. grContext->resetContext(kTextureBinding_GrGLBackendState); *outMailbox = mailboxInfo.m_mailbox; return true; }
void CanvasRenderingContext2D::drawTextInternal( const String& text, double x, double y, CanvasRenderingContext2DState::PaintType paintType, double* maxWidth) { // The style resolution required for fonts is not available in frame-less // documents. if (!canvas()->document().frame()) return; // accessFont needs the style to be up to date, but updating style can cause // script to run, (e.g. due to autofocus) which can free the canvas (set size // to 0, for example), so update style before grabbing the drawingCanvas. canvas()->document().updateStyleAndLayoutTreeForNode(canvas()); SkCanvas* c = drawingCanvas(); if (!c) return; if (!std::isfinite(x) || !std::isfinite(y)) return; if (maxWidth && (!std::isfinite(*maxWidth) || *maxWidth <= 0)) return; // Currently, SkPictureImageFilter does not support subpixel text // anti-aliasing, which is expected when !creationAttributes().alpha(), so we // need to fall out of display list mode when drawing text to an opaque // canvas. crbug.com/583809 if (!creationAttributes().alpha() && !isAccelerated()) canvas()->disableDeferral( DisableDeferralReasonSubPixelTextAntiAliasingSupport); const Font& font = accessFont(); font.getFontDescription().setSubpixelAscentDescent(true); const SimpleFontData* fontData = font.primaryFont(); DCHECK(fontData); if (!fontData) return; const FontMetrics& fontMetrics = fontData->getFontMetrics(); // FIXME: Need to turn off font smoothing. const ComputedStyle* computedStyle = 0; TextDirection direction = toTextDirection(state().getDirection(), canvas(), &computedStyle); bool isRTL = direction == RTL; bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false; TextRun textRun(text, 0, 0, TextRun::AllowTrailingExpansion, direction, override); textRun.setNormalizeSpace(true); // Draw the item text at the correct point. FloatPoint location(x, y + getFontBaseline(fontMetrics)); double fontWidth = font.width(textRun); bool useMaxWidth = (maxWidth && *maxWidth < fontWidth); double width = useMaxWidth ? *maxWidth : fontWidth; TextAlign align = state().getTextAlign(); if (align == StartTextAlign) align = isRTL ? RightTextAlign : LeftTextAlign; else if (align == EndTextAlign) align = isRTL ? LeftTextAlign : RightTextAlign; switch (align) { case CenterTextAlign: location.setX(location.x() - width / 2); break; case RightTextAlign: location.setX(location.x() - width); break; default: break; } // The slop built in to this mask rect matches the heuristic used in // FontCGWin.cpp for GDI text. TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(), width + fontMetrics.height(), fontMetrics.lineSpacing()); if (paintType == CanvasRenderingContext2DState::StrokePaintType) inflateStrokeRect(textRunPaintInfo.bounds); CanvasRenderingContext2DAutoRestoreSkCanvas stateRestorer(this); if (useMaxWidth) { drawingCanvas()->save(); drawingCanvas()->translate(location.x(), location.y()); // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) // still work. drawingCanvas()->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1); location = FloatPoint(); } draw( [&font, this, &textRunPaintInfo, &location]( SkCanvas* c, const SkPaint* paint) // draw lambda { font.drawBidiText(c, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady, cDeviceScaleFactor, *paint); }, [](const SkIRect& rect) // overdraw test lambda { return false; }, textRunPaintInfo.bounds, paintType); }