CFTimeInterval LegacyCACFLayerTreeHost::lastCommitTime() const
{
    return wkCACFContextGetLastCommitTime(m_context);
}
void WKCACFLayerRenderer::render(const Vector<CGRect>& windowDirtyRects)
{
    ASSERT(m_d3dDevice);

    if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) {
        // We can't reset the device right now. Try again soon.
        renderSoon();
        return;
    }

    if (m_client && !m_client->shouldRender()) {
        renderSoon();
        return;
    }

    // Sync the layer if needed
    if (m_syncLayerChanges) {
        m_client->syncCompositingState();
        m_syncLayerChanges = false;
    }

    // Flush the root layer to the render tree.
    wkCACFContextFlush(m_context);

    // All pending animations will have been started with the flush. Fire the animationStarted calls
    double currentTime = WTF::currentTime();
    double currentMediaTime = CACurrentMediaTime();
    double t = currentTime + wkCACFContextGetLastCommitTime(m_context) - currentMediaTime;
    ASSERT(t <= currentTime);

    HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end();
    for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it) {
        PlatformCALayerClient* owner = (*it)->owner();
        owner->platformCALayerAnimationStarted(t);
    }

    m_pendingAnimatedLayers.clear();

    CGRect bounds = this->bounds();

    // Give the renderer some space to use. This needs to be valid until the
    // wkCACFContextFinishUpdate() call below.
    char space[4096];
    if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), currentMediaTime, bounds, windowDirtyRects.data(), windowDirtyRects.size()))
        return;

    HRESULT err = S_OK;
    CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity();

    do {
        // FIXME: don't need to clear dirty region if layer tree is opaque.

        WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context);
        if (!e)
            break;

        Vector<D3DRECT, 64> rects;
        for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) {
            D3DRECT rect;
            rect.x1 = r->origin.x;
            rect.x2 = rect.x1 + r->size.width;
            rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height);
            rect.y2 = rect.y1 + r->size.height;

            rects.append(rect);
        }
        wkCACFUpdateRectEnumeratorRelease(e);

        timeToNextRender = wkCACFContextGetNextUpdateTime(m_context);

        if (rects.isEmpty())
            break;

        m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0);

        m_d3dDevice->BeginScene();
        wkCACFContextRenderUpdate(m_context);
        m_d3dDevice->EndScene();

        err = m_d3dDevice->Present(0, 0, 0, 0);

        if (err == D3DERR_DEVICELOST) {
            wkCACFContextAddUpdateRect(m_context, bounds);
            if (!resetDevice(LostDevice)) {
                // We can't reset the device right now. Try again soon.
                renderSoon();
                return;
            }
        }
    } while (err == D3DERR_DEVICELOST);

    wkCACFContextFinishUpdate(m_context);

#ifndef NDEBUG
    if (m_printTree)
        m_rootLayer->printTree();
#endif

    // If timeToNextRender is not infinity, it means animations are running, so queue up to render again
    if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity())
        renderSoon();
}