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 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 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 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 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 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 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 165 CACFLayerTreeHost::~CACFLayerTreeHost() 166 { 167 ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost"); 168 } 169 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 199 PlatformCALayer* CACFLayerTreeHost::rootLayer() const 200 { 201 return m_rootLayer.get(); 202 } 203 204 void CACFLayerTreeHost::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer) 205 { 206 m_pendingAnimatedLayers.add(layer); 207 } 208 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 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 232 void CACFLayerTreeHost::destroyRenderer() 233 { 234 m_rootLayer = 0; 235 m_rootChildLayer = 0; 236 LayerChangesFlusher::shared().cancelPendingFlush(this); 237 } 238 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 269 void CACFLayerTreeHost::paint() 270 { 271 Vector<CGRect> dirtyRects; 272 getDirtyRects(m_window, dirtyRects); 273 render(dirtyRects); 274 } 275 276 void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon() 277 { 278 m_shouldFlushPendingGraphicsLayerChanges = true; 279 LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this); 280 } 281 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 302 void CACFLayerTreeHost::contextDidChange() 303 { 304 // All pending animations will have been started with the flush. Fire the animationStarted calls. 305 notifyAnimationsStarted(); 306 } 307 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 322 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