CACFLayerTreeHost.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
1/*
2 * Copyright (C) 2009 Apple 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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CACFLayerTreeHost.h"
28
29#if USE(ACCELERATED_COMPOSITING)
30
31#include "CACFLayerTreeHostClient.h"
32#include "LayerChangesFlusher.h"
33#include "LegacyCACFLayerTreeHost.h"
34#include "PlatformCALayer.h"
35#include "WKCACFViewLayerTreeHost.h"
36#include "WebCoreInstanceHandle.h"
37#include <limits.h>
38#include <QuartzCore/CABase.h>
39#include <wtf/CurrentTime.h>
40#include <wtf/OwnArrayPtr.h>
41
42#ifdef DEBUG_ALL
43#pragma comment(lib, "QuartzCore_debug")
44#else
45#pragma comment(lib, "QuartzCore")
46#endif
47
48inline static CGRect winRectToCGRect(RECT rc)
49{
50    return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top));
51}
52
53inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect)
54{
55    return CGRectMake(rc.left, (relativeToRect.bottom-rc.bottom), (rc.right - rc.left), (rc.bottom - rc.top));
56}
57
58namespace WebCore {
59
60bool CACFLayerTreeHost::acceleratedCompositingAvailable()
61{
62    static bool available;
63    static bool tested;
64
65    if (tested)
66        return available;
67
68    tested = true;
69
70    // Initialize available to true since this function will be called from a
71    // propagation within createRenderer(). We want to be able to return true
72    // when that happens so that the test can continue.
73    available = true;
74
75    HMODULE library = LoadLibrary(TEXT("d3d9.dll"));
76    if (!library) {
77        available = false;
78        return available;
79    }
80
81    FreeLibrary(library);
82#ifdef DEBUG_ALL
83    library = LoadLibrary(TEXT("QuartzCore_debug.dll"));
84#else
85    library = LoadLibrary(TEXT("QuartzCore.dll"));
86#endif
87    if (!library) {
88        available = false;
89        return available;
90    }
91
92    FreeLibrary(library);
93
94    // Make a dummy HWND.
95    WNDCLASSEX wcex = { 0 };
96    wcex.cbSize = sizeof(WNDCLASSEX);
97    wcex.lpfnWndProc = DefWindowProc;
98    wcex.hInstance = WebCore::instanceHandle();
99    wcex.lpszClassName = L"CoreAnimationTesterWindowClass";
100    ::RegisterClassEx(&wcex);
101    HWND testWindow = ::CreateWindow(L"CoreAnimationTesterWindowClass", L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 20, 20, 0, 0, 0, 0);
102
103    if (!testWindow) {
104        available = false;
105        return available;
106    }
107
108    RefPtr<CACFLayerTreeHost> host = CACFLayerTreeHost::create();
109    host->setWindow(testWindow);
110    available = host->createRenderer();
111    host->setWindow(0);
112    ::DestroyWindow(testWindow);
113
114    return available;
115}
116
117PassRefPtr<CACFLayerTreeHost> CACFLayerTreeHost::create()
118{
119    if (!acceleratedCompositingAvailable())
120        return 0;
121    RefPtr<CACFLayerTreeHost> host = WKCACFViewLayerTreeHost::create();
122    if (!host)
123        host = LegacyCACFLayerTreeHost::create();
124    host->initialize();
125    return host.release();
126}
127
128CACFLayerTreeHost::CACFLayerTreeHost()
129    : m_client(0)
130    , m_rootLayer(PlatformCALayer::create(PlatformCALayer::LayerTypeRootLayer, 0))
131    , m_window(0)
132    , m_shouldFlushPendingGraphicsLayerChanges(false)
133    , m_isFlushingLayerChanges(false)
134#if !ASSERT_DISABLED
135    , m_state(WindowNotSet)
136#endif
137{
138}
139
140void CACFLayerTreeHost::initialize()
141{
142    // Point the CACFContext to this
143    initializeContext(this, m_rootLayer.get());
144
145    // Under the root layer, we have a clipping layer to clip the content,
146    // that contains a scroll layer that we use for scrolling the content.
147    // The root layer is the size of the client area of the window.
148    // The clipping layer is the size of the WebView client area (window less the scrollbars).
149    // The scroll layer is the size of the root child layer.
150    // Resizing the window will change the bounds of the rootLayer and the clip layer and will not
151    // cause any repositioning.
152    // Scrolling will affect only the position of the scroll layer without affecting the bounds.
153
154    m_rootLayer->setName("CACFLayerTreeHost rootLayer");
155    m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
156    m_rootLayer->setGeometryFlipped(true);
157
158#ifndef NDEBUG
159    CGColorRef debugColor = CGColorCreateGenericRGB(1, 0, 0, 0.8);
160    m_rootLayer->setBackgroundColor(debugColor);
161    CGColorRelease(debugColor);
162#endif
163}
164
165CACFLayerTreeHost::~CACFLayerTreeHost()
166{
167    ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost");
168}
169
170void CACFLayerTreeHost::setWindow(HWND window)
171{
172    if (window == m_window)
173        return;
174
175#if !ASSERT_DISABLED
176    switch (m_state) {
177    case WindowNotSet:
178        ASSERT_ARG(window, window);
179        ASSERT(!m_window);
180        m_state = WindowSet;
181        break;
182    case WindowSet:
183        ASSERT_ARG(window, !window);
184        ASSERT(m_window);
185        m_state = WindowCleared;
186        break;
187    case WindowCleared:
188        ASSERT_NOT_REACHED();
189        break;
190    }
191#endif
192
193    if (m_window)
194        destroyRenderer();
195
196    m_window = window;
197}
198
199PlatformCALayer* CACFLayerTreeHost::rootLayer() const
200{
201    return m_rootLayer.get();
202}
203
204void CACFLayerTreeHost::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer)
205{
206    m_pendingAnimatedLayers.add(layer);
207}
208
209void CACFLayerTreeHost::setRootChildLayer(PlatformCALayer* layer)
210{
211    m_rootLayer->removeAllSublayers();
212    m_rootChildLayer = layer;
213    if (m_rootChildLayer)
214        m_rootLayer->appendSublayer(m_rootChildLayer.get());
215}
216
217void CACFLayerTreeHost::layerTreeDidChange()
218{
219    if (m_isFlushingLayerChanges) {
220        // The layer tree is changing as a result of flushing GraphicsLayer changes to their
221        // underlying PlatformCALayers. We'll flush those changes to the context as part of that
222        // process, so there's no need to schedule another flush here.
223        return;
224    }
225
226    // The layer tree is changing as a result of someone modifying a PlatformCALayer that doesn't
227    // have a corresponding GraphicsLayer. Schedule a flush since we won't schedule one through the
228    // normal GraphicsLayer mechanisms.
229    LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
230}
231
232void CACFLayerTreeHost::destroyRenderer()
233{
234    m_rootLayer = 0;
235    m_rootChildLayer = 0;
236    LayerChangesFlusher::shared().cancelPendingFlush(this);
237}
238
239static void getDirtyRects(HWND window, Vector<CGRect>& outRects)
240{
241    ASSERT_ARG(outRects, outRects.isEmpty());
242
243    RECT clientRect;
244    if (!GetClientRect(window, &clientRect))
245        return;
246
247    OwnPtr<HRGN> region(CreateRectRgn(0, 0, 0, 0));
248    int regionType = GetUpdateRgn(window, region.get(), false);
249    if (regionType != COMPLEXREGION) {
250        RECT dirtyRect;
251        if (GetUpdateRect(window, &dirtyRect, false))
252            outRects.append(winRectToCGRect(dirtyRect, clientRect));
253        return;
254    }
255
256    DWORD dataSize = GetRegionData(region.get(), 0, 0);
257    OwnArrayPtr<unsigned char> regionDataBuffer = adoptArrayPtr(new unsigned char[dataSize]);
258    RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get());
259    if (!GetRegionData(region.get(), dataSize, regionData))
260        return;
261
262    outRects.resize(regionData->rdh.nCount);
263
264    RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer);
265    for (size_t i = 0; i < outRects.size(); ++i, ++rect)
266        outRects[i] = winRectToCGRect(*rect, clientRect);
267}
268
269void CACFLayerTreeHost::paint()
270{
271    Vector<CGRect> dirtyRects;
272    getDirtyRects(m_window, dirtyRects);
273    render(dirtyRects);
274}
275
276void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon()
277{
278    m_shouldFlushPendingGraphicsLayerChanges = true;
279    LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
280}
281
282void CACFLayerTreeHost::flushPendingLayerChangesNow()
283{
284    // Calling out to the client could cause our last reference to go away.
285    RefPtr<CACFLayerTreeHost> protector(this);
286
287    m_isFlushingLayerChanges = true;
288
289    // Flush changes stored up in GraphicsLayers to their underlying PlatformCALayers, if
290    // requested.
291    if (m_client && m_shouldFlushPendingGraphicsLayerChanges) {
292        m_shouldFlushPendingGraphicsLayerChanges = false;
293        m_client->flushPendingGraphicsLayerChanges();
294    }
295
296    // Flush changes stored up in PlatformCALayers to the context so they will be rendered.
297    flushContext();
298
299    m_isFlushingLayerChanges = false;
300}
301
302void CACFLayerTreeHost::contextDidChange()
303{
304    // All pending animations will have been started with the flush. Fire the animationStarted calls.
305    notifyAnimationsStarted();
306}
307
308void CACFLayerTreeHost::notifyAnimationsStarted()
309{
310    // Send currentTime to the pending animations. This function is called by CACF in a callback
311    // which occurs after the drawInContext calls. So currentTime is very close to the time
312    // the animations actually start
313    double currentTime = WTF::currentTime();
314
315    HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end();
316    for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it)
317        (*it)->animationStarted(currentTime);
318
319    m_pendingAnimatedLayers.clear();
320}
321
322CGRect CACFLayerTreeHost::bounds() const
323{
324    RECT clientRect;
325    GetClientRect(m_window, &clientRect);
326
327    return winRectToCGRect(clientRect);
328}
329
330}
331
332#endif // USE(ACCELERATED_COMPOSITING)
333