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, ¶meters, &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(¶meters); 451 initD3DGeometry(); 452 } 453 454 } 455 456 #endif // USE(ACCELERATED_COMPOSITING) 457