• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
winRectToCGRect(RECT rc)48 inline static CGRect winRectToCGRect(RECT rc)
49 {
50     return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top));
51 }
52 
winRectToCGRect(RECT rc,RECT relativeToRect)53 inline 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 
58 namespace WebCore {
59 
acceleratedCompositingAvailable()60 bool 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 
create()117 PassRefPtr<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 
CACFLayerTreeHost()128 CACFLayerTreeHost::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 
initialize()140 void 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 
~CACFLayerTreeHost()165 CACFLayerTreeHost::~CACFLayerTreeHost()
166 {
167     ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost");
168 }
169 
setWindow(HWND window)170 void 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 
rootLayer() const199 PlatformCALayer* CACFLayerTreeHost::rootLayer() const
200 {
201     return m_rootLayer.get();
202 }
203 
addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer)204 void CACFLayerTreeHost::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer)
205 {
206     m_pendingAnimatedLayers.add(layer);
207 }
208 
setRootChildLayer(PlatformCALayer * layer)209 void 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 
layerTreeDidChange()217 void 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 
destroyRenderer()232 void CACFLayerTreeHost::destroyRenderer()
233 {
234     m_rootLayer = 0;
235     m_rootChildLayer = 0;
236     LayerChangesFlusher::shared().cancelPendingFlush(this);
237 }
238 
getDirtyRects(HWND window,Vector<CGRect> & outRects)239 static 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 
paint()269 void CACFLayerTreeHost::paint()
270 {
271     Vector<CGRect> dirtyRects;
272     getDirtyRects(m_window, dirtyRects);
273     render(dirtyRects);
274 }
275 
flushPendingGraphicsLayerChangesSoon()276 void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon()
277 {
278     m_shouldFlushPendingGraphicsLayerChanges = true;
279     LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
280 }
281 
flushPendingLayerChangesNow()282 void 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 
contextDidChange()302 void CACFLayerTreeHost::contextDidChange()
303 {
304     // All pending animations will have been started with the flush. Fire the animationStarted calls.
305     notifyAnimationsStarted();
306 }
307 
notifyAnimationsStarted()308 void 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 
bounds() const322 CGRect 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