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 
     28 #if USE(ACCELERATED_COMPOSITING)
     29 
     30 #include "WKCACFLayerRenderer.h"
     31 
     32 #include "WKCACFContextFlusher.h"
     33 #include "WKCACFLayer.h"
     34 #include <CoreGraphics/CGSRegion.h>
     35 #include <QuartzCore/CACFContext.h>
     36 #include <QuartzCore/CARenderOGL.h>
     37 #include <QuartzCoreInterface/QuartzCoreInterface.h>
     38 #include <wtf/HashMap.h>
     39 #include <wtf/OwnArrayPtr.h>
     40 #include <wtf/StdLibExtras.h>
     41 #include <d3d9.h>
     42 #include <d3dx9.h>
     43 
     44 #pragma comment(lib, "d3d9")
     45 #pragma comment(lib, "d3dx9")
     46 #ifdef DEBUG_ALL
     47 #pragma comment(lib, "QuartzCore_debug")
     48 #else
     49 #pragma comment(lib, "QuartzCore")
     50 #endif
     51 
     52 static IDirect3D9* s_d3d = 0;
     53 static IDirect3D9* d3d()
     54 {
     55     if (s_d3d)
     56         return s_d3d;
     57 
     58     if (!LoadLibrary(TEXT("d3d9.dll")))
     59         return 0;
     60 
     61     s_d3d = Direct3DCreate9(D3D_SDK_VERSION);
     62 
     63     return s_d3d;
     64 }
     65 
     66 inline static CGRect winRectToCGRect(RECT rc)
     67 {
     68     return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top));
     69 }
     70 
     71 inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect)
     72 {
     73     return CGRectMake(rc.left, (relativeToRect.bottom-rc.bottom), (rc.right - rc.left), (rc.bottom - rc.top));
     74 }
     75 
     76 namespace WebCore {
     77 
     78 typedef HashMap<CACFContextRef, WKCACFLayerRenderer*> ContextToWindowMap;
     79 
     80 static ContextToWindowMap& windowsForContexts()
     81 {
     82     DEFINE_STATIC_LOCAL(ContextToWindowMap, map, ());
     83     return map;
     84 }
     85 
     86 static D3DPRESENT_PARAMETERS initialPresentationParameters()
     87 {
     88     D3DPRESENT_PARAMETERS parameters = {0};
     89     parameters.Windowed = TRUE;
     90     parameters.SwapEffect = D3DSWAPEFFECT_COPY;
     91     parameters.BackBufferCount = 1;
     92     parameters.BackBufferFormat = D3DFMT_A8R8G8B8;
     93     parameters.MultiSampleType = D3DMULTISAMPLE_NONE;
     94 
     95     return parameters;
     96 }
     97 
     98 bool WKCACFLayerRenderer::acceleratedCompositingAvailable()
     99 {
    100     static bool available;
    101     static bool tested;
    102 
    103     if (tested)
    104         return available;
    105 
    106     tested = true;
    107     HMODULE library = LoadLibrary(TEXT("d3d9.dll"));
    108     if (!library)
    109         return false;
    110 
    111     FreeLibrary(library);
    112     library = LoadLibrary(TEXT("QuartzCore.dll"));
    113     if (!library)
    114         return false;
    115 
    116     FreeLibrary(library);
    117     available = true;
    118     return available;
    119 }
    120 
    121 void WKCACFLayerRenderer::didFlushContext(CACFContextRef context)
    122 {
    123     WKCACFLayerRenderer* window = windowsForContexts().get(context);
    124     if (!window)
    125         return;
    126 
    127     window->renderSoon();
    128 }
    129 
    130 PassOwnPtr<WKCACFLayerRenderer> WKCACFLayerRenderer::create()
    131 {
    132     if (!acceleratedCompositingAvailable())
    133         return 0;
    134     return new WKCACFLayerRenderer;
    135 }
    136 
    137 WKCACFLayerRenderer::WKCACFLayerRenderer()
    138     : m_triedToCreateD3DRenderer(false)
    139     , m_renderContext(0)
    140     , m_renderer(0)
    141     , m_hostWindow(0)
    142     , m_renderTimer(this, &WKCACFLayerRenderer::renderTimerFired)
    143     , m_scrollFrame(0, 0, 1, 1) // Default to 1 to avoid 0 size frames
    144 {
    145 #ifndef NDEBUG
    146     char* printTreeFlag = getenv("CA_PRINT_TREE");
    147     m_printTree = printTreeFlag && atoi(printTreeFlag);
    148 #endif
    149 }
    150 
    151 WKCACFLayerRenderer::~WKCACFLayerRenderer()
    152 {
    153     destroyRenderer();
    154 }
    155 
    156 void WKCACFLayerRenderer::setScrollFrame(const IntRect& scrollFrame)
    157 {
    158     m_scrollFrame = scrollFrame;
    159     CGRect frameBounds = bounds();
    160     m_scrollLayer->setBounds(CGRectMake(0, 0, m_scrollFrame.width(), m_scrollFrame.height()));
    161     m_scrollLayer->setPosition(CGPointMake(0, frameBounds.size.height));
    162 
    163     if (m_rootChildLayer)
    164         m_rootChildLayer->setPosition(CGPointMake(-m_scrollFrame.x(), m_scrollFrame.height() + m_scrollFrame.y()));
    165 }
    166 
    167 void WKCACFLayerRenderer::setRootContents(CGImageRef image)
    168 {
    169     ASSERT(m_rootLayer);
    170     m_rootLayer->setContents(image);
    171     renderSoon();
    172 }
    173 
    174 void WKCACFLayerRenderer::setRootChildLayer(WebCore::PlatformLayer* layer)
    175 {
    176     if (!m_scrollLayer)
    177         return;
    178 
    179     m_scrollLayer->removeAllSublayers();
    180     m_rootChildLayer = layer;
    181     if (layer) {
    182         m_scrollLayer->addSublayer(layer);
    183 
    184         // Set the frame
    185         layer->setAnchorPoint(CGPointMake(0, 1));
    186         setScrollFrame(m_scrollFrame);
    187     }
    188 }
    189 
    190 void WKCACFLayerRenderer::setNeedsDisplay()
    191 {
    192     ASSERT(m_rootLayer);
    193     m_rootLayer->setNeedsDisplay();
    194     renderSoon();
    195 }
    196 
    197 void WKCACFLayerRenderer::createRenderer()
    198 {
    199     if (m_triedToCreateD3DRenderer)
    200         return;
    201 
    202     m_triedToCreateD3DRenderer = true;
    203     D3DPRESENT_PARAMETERS parameters = initialPresentationParameters();
    204 
    205     if (!d3d() || !::IsWindow(m_hostWindow))
    206         return;
    207 
    208     // D3D doesn't like to make back buffers for 0 size windows. We skirt this problem if we make the
    209     // passed backbuffer width and height non-zero. The window will necessarily get set to a non-zero
    210     // size eventually, and then the backbuffer size will get reset.
    211     RECT rect;
    212     GetClientRect(m_hostWindow, &rect);
    213 
    214     if (rect.left-rect.right == 0 || rect.bottom-rect.top == 0) {
    215         parameters.BackBufferWidth = 1;
    216         parameters.BackBufferHeight = 1;
    217     }
    218 
    219     if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hostWindow, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &parameters, &m_d3dDevice)))
    220         return;
    221 
    222     D3DXMATRIXA16 projection;
    223     D3DXMatrixOrthoOffCenterRH(&projection, rect.left, rect.right, rect.top, rect.bottom, -1.0f, 1.0f);
    224 
    225     m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection);
    226 
    227     m_context.adoptCF(CACFContextCreate(0));
    228     windowsForContexts().set(m_context.get(), this);
    229 
    230     m_renderContext = static_cast<CARenderContext*>(CACFContextGetRenderContext(m_context.get()));
    231     m_renderer = CARenderOGLNew(wkqcCARenderOGLCallbacks(wkqckCARenderDX9Callbacks), m_d3dDevice.get(), 0);
    232 
    233     // Create the root hierarchy
    234     m_rootLayer = WKCACFLayer::create(WKCACFLayer::Layer);
    235     m_rootLayer->setName("WKCACFLayerRenderer rootLayer");
    236     m_scrollLayer = WKCACFLayer::create(WKCACFLayer::Layer);
    237     m_scrollLayer->setName("WKCACFLayerRenderer scrollLayer");
    238 
    239     m_rootLayer->addSublayer(m_scrollLayer);
    240     m_scrollLayer->setMasksToBounds(true);
    241     m_scrollLayer->setAnchorPoint(CGPointMake(0, 1));
    242 
    243 #ifndef NDEBUG
    244     CGColorRef debugColor = createCGColor(Color(255, 0, 0, 204));
    245     m_rootLayer->setBackgroundColor(debugColor);
    246     CGColorRelease(debugColor);
    247 #endif
    248 
    249     if (IsWindow(m_hostWindow))
    250         m_rootLayer->setFrame(bounds());
    251 
    252     if (m_context)
    253         m_rootLayer->becomeRootLayerForContext(m_context.get());
    254 }
    255 
    256 void WKCACFLayerRenderer::destroyRenderer()
    257 {
    258     if (m_context) {
    259         windowsForContexts().remove(m_context.get());
    260         WKCACFContextFlusher::shared().removeContext(m_context.get());
    261     }
    262 
    263     if (m_renderer)
    264         CARenderOGLDestroy(m_renderer);
    265     m_renderer = 0;
    266     m_d3dDevice = 0;
    267     if (s_d3d)
    268         s_d3d->Release();
    269 
    270     s_d3d = 0;
    271     m_rootLayer = 0;
    272     m_scrollLayer = 0;
    273     m_rootChildLayer = 0;
    274 
    275     m_triedToCreateD3DRenderer = false;
    276 }
    277 
    278 void WKCACFLayerRenderer::resize()
    279 {
    280     if (!m_d3dDevice)
    281         return;
    282 
    283     resetDevice();
    284 
    285     if (m_rootLayer) {
    286         m_rootLayer->setFrame(bounds());
    287         WKCACFContextFlusher::shared().flushAllContexts();
    288         setScrollFrame(m_scrollFrame);
    289     }
    290 }
    291 
    292 static void getDirtyRects(HWND window, Vector<CGRect>& outRects)
    293 {
    294     ASSERT_ARG(outRects, outRects.isEmpty());
    295 
    296     RECT clientRect;
    297     if (!GetClientRect(window, &clientRect))
    298         return;
    299 
    300     HRGN region = CreateRectRgn(0, 0, 0, 0);
    301     int regionType = GetUpdateRgn(window, region, false);
    302     if (regionType != COMPLEXREGION) {
    303         RECT dirtyRect;
    304         if (GetUpdateRect(window, &dirtyRect, false))
    305             outRects.append(winRectToCGRect(dirtyRect, clientRect));
    306         return;
    307     }
    308 
    309     DWORD dataSize = GetRegionData(region, 0, 0);
    310     OwnArrayPtr<unsigned char> regionDataBuffer(new unsigned char[dataSize]);
    311     RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get());
    312     if (!GetRegionData(region, dataSize, regionData))
    313         return;
    314 
    315     outRects.resize(regionData->rdh.nCount);
    316 
    317     RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer);
    318     for (size_t i = 0; i < outRects.size(); ++i, ++rect)
    319         outRects[i] = winRectToCGRect(*rect, clientRect);
    320 
    321     DeleteObject(region);
    322 }
    323 
    324 void WKCACFLayerRenderer::renderTimerFired(Timer<WKCACFLayerRenderer>*)
    325 {
    326     paint();
    327 }
    328 
    329 void WKCACFLayerRenderer::paint()
    330 {
    331     if (!m_d3dDevice)
    332         return;
    333 
    334     Vector<CGRect> dirtyRects;
    335     getDirtyRects(m_hostWindow, dirtyRects);
    336     render(dirtyRects);
    337 }
    338 
    339 void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects)
    340 {
    341     ASSERT(m_d3dDevice);
    342 
    343     // Flush the root layer to the render tree.
    344     WKCACFContextFlusher::shared().flushAllContexts();
    345 
    346     CGRect bounds = this->bounds();
    347 
    348     CFTimeInterval t = CACurrentMediaTime();
    349 
    350     // Give the renderer some space to use. This needs to be valid until the
    351     // CARenderUpdateFinish() call below.
    352     char space[4096];
    353     CARenderUpdate* u = CARenderUpdateBegin(space, sizeof(space), t, 0, 0, &bounds);
    354     if (!u)
    355         return;
    356 
    357     CARenderContextLock(m_renderContext);
    358     CARenderUpdateAddContext(u, m_renderContext);
    359     CARenderContextUnlock(m_renderContext);
    360 
    361     for (size_t i = 0; i < dirtyRects.size(); ++i)
    362         CARenderUpdateAddRect(u, &dirtyRects[i]);
    363 
    364     HRESULT err = S_OK;
    365     do {
    366         CGSRegionObj rgn = CARenderUpdateCopyRegion(u);
    367 
    368         if (!rgn)
    369             break;
    370 
    371         // FIXME: don't need to clear dirty region if layer tree is opaque.
    372 
    373         Vector<D3DRECT, 64> rects;
    374         CGSRegionEnumeratorObj e = CGSRegionEnumerator(rgn);
    375         for (const CGRect* r = CGSNextRect(e); r; r = CGSNextRect(e)) {
    376             D3DRECT rect;
    377             rect.x1 = r->origin.x;
    378             rect.x2 = rect.x1 + r->size.width;
    379             rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height);
    380             rect.y2 = rect.y1 + r->size.height;
    381 
    382             rects.append(rect);
    383         }
    384         CGSReleaseRegionEnumerator(e);
    385         CGSReleaseRegion(rgn);
    386 
    387         if (rects.isEmpty())
    388             break;
    389 
    390         m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0);
    391 
    392         m_d3dDevice->BeginScene();
    393         CARenderOGLRender(m_renderer, u);
    394         m_d3dDevice->EndScene();
    395 
    396         err = m_d3dDevice->Present(0, 0, 0, 0);
    397 
    398         if (err == D3DERR_DEVICELOST) {
    399             // Lost device situation.
    400             CARenderOGLPurge(m_renderer);
    401             resetDevice();
    402             CARenderUpdateAddRect(u, &bounds);
    403         }
    404     } while (err == D3DERR_DEVICELOST);
    405 
    406     CARenderUpdateFinish(u);
    407 
    408 #ifndef NDEBUG
    409     if (m_printTree)
    410         m_rootLayer->printTree();
    411 #endif
    412 }
    413 
    414 void WKCACFLayerRenderer::renderSoon()
    415 {
    416     if (!m_renderTimer.isActive())
    417         m_renderTimer.startOneShot(0);
    418 }
    419 
    420 CGRect WKCACFLayerRenderer::bounds() const
    421 {
    422     RECT clientRect;
    423     GetClientRect(m_hostWindow, &clientRect);
    424 
    425     return winRectToCGRect(clientRect);
    426 }
    427 
    428 void WKCACFLayerRenderer::initD3DGeometry()
    429 {
    430     ASSERT(m_d3dDevice);
    431 
    432     CGRect bounds = this->bounds();
    433 
    434     float x0 = bounds.origin.x;
    435     float y0 = bounds.origin.y;
    436     float x1 = x0 + bounds.size.width;
    437     float y1 = y0 + bounds.size.height;
    438 
    439     D3DXMATRIXA16 projection;
    440     D3DXMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f);
    441 
    442     m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection);
    443 }
    444 
    445 void WKCACFLayerRenderer::resetDevice()
    446 {
    447     ASSERT(m_d3dDevice);
    448 
    449     D3DPRESENT_PARAMETERS parameters = initialPresentationParameters();
    450     m_d3dDevice->Reset(&parameters);
    451     initD3DGeometry();
    452 }
    453 
    454 }
    455 
    456 #endif // USE(ACCELERATED_COMPOSITING)
    457