Home | History | Annotate | Download | only in win
      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