1 /* 2 * Copyright (C) 2011 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 "LegacyCACFLayerTreeHost.h" 28 29 #if USE(ACCELERATED_COMPOSITING) 30 31 #include "PlatformCALayer.h" 32 #include <QuartzCore/CABase.h> 33 #include <WebKitSystemInterface/WebKitSystemInterface.h> 34 35 #ifndef NDEBUG 36 #define D3D_DEBUG_INFO 37 #endif 38 39 #include <d3d9.h> 40 #include <d3dx9.h> 41 42 #pragma comment(lib, "d3d9") 43 #pragma comment(lib, "d3dx9") 44 45 using namespace std; 46 47 namespace WebCore { 48 49 static IDirect3D9* s_d3d = 0; 50 static IDirect3D9* d3d() 51 { 52 if (s_d3d) 53 return s_d3d; 54 55 if (!LoadLibrary(TEXT("d3d9.dll"))) 56 return 0; 57 58 s_d3d = Direct3DCreate9(D3D_SDK_VERSION); 59 60 return s_d3d; 61 } 62 63 static D3DPRESENT_PARAMETERS initialPresentationParameters() 64 { 65 D3DPRESENT_PARAMETERS parameters = {0}; 66 parameters.Windowed = TRUE; 67 parameters.SwapEffect = D3DSWAPEFFECT_COPY; 68 parameters.BackBufferCount = 1; 69 parameters.BackBufferFormat = D3DFMT_A8R8G8B8; 70 parameters.MultiSampleType = D3DMULTISAMPLE_NONE; 71 72 return parameters; 73 } 74 75 // FIXME: <rdar://6507851> Share this code with CoreAnimation. 76 static bool hardwareCapabilitiesIndicateCoreAnimationSupport(const D3DCAPS9& caps) 77 { 78 // CoreAnimation needs two or more texture units. 79 if (caps.MaxTextureBlendStages < 2) 80 return false; 81 82 // CoreAnimation needs non-power-of-two textures. 83 if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) 84 return false; 85 86 // CoreAnimation needs vertex shader 2.0 or greater. 87 if (D3DSHADER_VERSION_MAJOR(caps.VertexShaderVersion) < 2) 88 return false; 89 90 // CoreAnimation needs pixel shader 2.0 or greater. 91 if (D3DSHADER_VERSION_MAJOR(caps.PixelShaderVersion) < 2) 92 return false; 93 94 return true; 95 } 96 97 PassRefPtr<LegacyCACFLayerTreeHost> LegacyCACFLayerTreeHost::create() 98 { 99 return adoptRef(new LegacyCACFLayerTreeHost); 100 } 101 102 LegacyCACFLayerTreeHost::LegacyCACFLayerTreeHost() 103 : m_renderTimer(this, &LegacyCACFLayerTreeHost::renderTimerFired) 104 , m_context(wkCACFContextCreate()) 105 , m_mightBeAbleToCreateDeviceLater(true) 106 , m_mustResetLostDeviceBeforeRendering(false) 107 { 108 #ifndef NDEBUG 109 char* printTreeFlag = getenv("CA_PRINT_TREE"); 110 m_printTree = printTreeFlag && atoi(printTreeFlag); 111 #endif 112 } 113 114 LegacyCACFLayerTreeHost::~LegacyCACFLayerTreeHost() 115 { 116 wkCACFContextDestroy(m_context); 117 } 118 119 void LegacyCACFLayerTreeHost::initializeContext(void* userData, PlatformCALayer* layer) 120 { 121 wkCACFContextSetUserData(m_context, userData); 122 wkCACFContextSetLayer(m_context, layer->platformLayer()); 123 } 124 125 bool LegacyCACFLayerTreeHost::createRenderer() 126 { 127 if (m_d3dDevice || !m_mightBeAbleToCreateDeviceLater) 128 return m_d3dDevice; 129 130 m_mightBeAbleToCreateDeviceLater = false; 131 D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); 132 133 if (!d3d() || !::IsWindow(window())) 134 return false; 135 136 // D3D doesn't like to make back buffers for 0 size windows. We skirt this problem if we make the 137 // passed backbuffer width and height non-zero. The window will necessarily get set to a non-zero 138 // size eventually, and then the backbuffer size will get reset. 139 RECT rect; 140 GetClientRect(window(), &rect); 141 142 if (rect.left-rect.right == 0 || rect.bottom-rect.top == 0) { 143 parameters.BackBufferWidth = 1; 144 parameters.BackBufferHeight = 1; 145 } 146 147 D3DCAPS9 d3dCaps; 148 if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))) 149 return false; 150 151 DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE; 152 if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps) 153 behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; 154 else 155 behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; 156 157 COMPtr<IDirect3DDevice9> device; 158 if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window(), behaviorFlags, ¶meters, &device))) { 159 // In certain situations (e.g., shortly after waking from sleep), Direct3DCreate9() will 160 // return an IDirect3D9 for which IDirect3D9::CreateDevice will always fail. In case we 161 // have one of these bad IDirect3D9s, get rid of it so we'll fetch a new one the next time 162 // we want to call CreateDevice. 163 s_d3d->Release(); 164 s_d3d = 0; 165 166 // Even if we don't have a bad IDirect3D9, in certain situations (e.g., shortly after 167 // waking from sleep), CreateDevice will fail, but will later succeed if called again. 168 m_mightBeAbleToCreateDeviceLater = true; 169 170 return false; 171 } 172 173 // Now that we've created the IDirect3DDevice9 based on the capabilities we 174 // got from the IDirect3D9 global object, we requery the device for its 175 // actual capabilities. The capabilities returned by the device can 176 // sometimes be more complete, for example when using software vertex 177 // processing. 178 D3DCAPS9 deviceCaps; 179 if (FAILED(device->GetDeviceCaps(&deviceCaps))) 180 return false; 181 182 if (!hardwareCapabilitiesIndicateCoreAnimationSupport(deviceCaps)) 183 return false; 184 185 m_d3dDevice = device; 186 187 initD3DGeometry(); 188 189 wkCACFContextSetD3DDevice(m_context, m_d3dDevice.get()); 190 191 if (IsWindow(window())) { 192 rootLayer()->setBounds(bounds()); 193 flushContext(); 194 } 195 196 return true; 197 } 198 199 void LegacyCACFLayerTreeHost::destroyRenderer() 200 { 201 wkCACFContextSetLayer(m_context, 0); 202 203 wkCACFContextSetD3DDevice(m_context, 0); 204 m_d3dDevice = 0; 205 if (s_d3d) 206 s_d3d->Release(); 207 208 s_d3d = 0; 209 m_mightBeAbleToCreateDeviceLater = true; 210 211 CACFLayerTreeHost::destroyRenderer(); 212 } 213 214 void LegacyCACFLayerTreeHost::resize() 215 { 216 if (!m_d3dDevice) 217 return; 218 219 // Resetting the device might fail here. But that's OK, because if it does it we will attempt to 220 // reset the device the next time we try to render. 221 resetDevice(ChangedWindowSize); 222 223 if (rootLayer()) { 224 rootLayer()->setBounds(bounds()); 225 flushContext(); 226 } 227 } 228 229 void LegacyCACFLayerTreeHost::renderTimerFired(Timer<LegacyCACFLayerTreeHost>*) 230 { 231 paint(); 232 } 233 234 void LegacyCACFLayerTreeHost::paint() 235 { 236 createRenderer(); 237 if (!m_d3dDevice) { 238 if (m_mightBeAbleToCreateDeviceLater) 239 renderSoon(); 240 return; 241 } 242 243 CACFLayerTreeHost::paint(); 244 } 245 246 void LegacyCACFLayerTreeHost::render(const Vector<CGRect>& windowDirtyRects) 247 { 248 ASSERT(m_d3dDevice); 249 250 if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) { 251 // We can't reset the device right now. Try again soon. 252 renderSoon(); 253 return; 254 } 255 256 CGRect bounds = this->bounds(); 257 258 // Give the renderer some space to use. This needs to be valid until the 259 // wkCACFContextFinishUpdate() call below. 260 char space[4096]; 261 if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), CACurrentMediaTime(), bounds, windowDirtyRects.data(), windowDirtyRects.size())) 262 return; 263 264 HRESULT err = S_OK; 265 CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity(); 266 267 do { 268 // FIXME: don't need to clear dirty region if layer tree is opaque. 269 270 WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context); 271 if (!e) 272 break; 273 274 Vector<D3DRECT, 64> rects; 275 for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) { 276 D3DRECT rect; 277 rect.x1 = r->origin.x; 278 rect.x2 = rect.x1 + r->size.width; 279 rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height); 280 rect.y2 = rect.y1 + r->size.height; 281 282 rects.append(rect); 283 } 284 wkCACFUpdateRectEnumeratorRelease(e); 285 286 timeToNextRender = wkCACFContextGetNextUpdateTime(m_context); 287 288 if (rects.isEmpty()) 289 break; 290 291 m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0); 292 293 m_d3dDevice->BeginScene(); 294 wkCACFContextRenderUpdate(m_context); 295 m_d3dDevice->EndScene(); 296 297 err = m_d3dDevice->Present(0, 0, 0, 0); 298 299 if (err == D3DERR_DEVICELOST) { 300 wkCACFContextAddUpdateRect(m_context, bounds); 301 if (!resetDevice(LostDevice)) { 302 // We can't reset the device right now. Try again soon. 303 renderSoon(); 304 return; 305 } 306 } 307 } while (err == D3DERR_DEVICELOST); 308 309 wkCACFContextFinishUpdate(m_context); 310 311 #ifndef NDEBUG 312 if (m_printTree) 313 rootLayer()->printTree(); 314 #endif 315 316 // If timeToNextRender is not infinity, it means animations are running, so queue up to render again 317 if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity()) 318 renderSoon(); 319 } 320 321 void LegacyCACFLayerTreeHost::renderSoon() 322 { 323 if (!m_renderTimer.isActive()) 324 m_renderTimer.startOneShot(0); 325 } 326 327 void LegacyCACFLayerTreeHost::flushContext() 328 { 329 wkCACFContextFlush(m_context); 330 contextDidChange(); 331 } 332 333 void LegacyCACFLayerTreeHost::contextDidChange() 334 { 335 renderSoon(); 336 CACFLayerTreeHost::contextDidChange(); 337 } 338 339 CFTimeInterval LegacyCACFLayerTreeHost::lastCommitTime() const 340 { 341 return wkCACFContextGetLastCommitTime(m_context); 342 } 343 344 void LegacyCACFLayerTreeHost::initD3DGeometry() 345 { 346 ASSERT(m_d3dDevice); 347 348 CGRect bounds = this->bounds(); 349 350 float x0 = bounds.origin.x; 351 float y0 = bounds.origin.y; 352 float x1 = x0 + bounds.size.width; 353 float y1 = y0 + bounds.size.height; 354 355 D3DXMATRIXA16 projection; 356 D3DXMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f); 357 358 m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection); 359 } 360 361 bool LegacyCACFLayerTreeHost::resetDevice(ResetReason reason) 362 { 363 ASSERT(m_d3dDevice); 364 ASSERT(m_context); 365 366 HRESULT hr = m_d3dDevice->TestCooperativeLevel(); 367 368 if (hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR) { 369 // The device cannot be reset at this time. Try again soon. 370 m_mustResetLostDeviceBeforeRendering = true; 371 return false; 372 } 373 374 m_mustResetLostDeviceBeforeRendering = false; 375 376 if (reason == LostDevice && hr == D3D_OK) { 377 // The device wasn't lost after all. 378 return true; 379 } 380 381 // We can reset the device. 382 383 // We have to release the context's D3D resrouces whenever we reset the IDirect3DDevice9 in order to 384 // destroy any D3DPOOL_DEFAULT resources that Core Animation has allocated (e.g., textures used 385 // for mask layers). See <http://msdn.microsoft.com/en-us/library/bb174425(v=VS.85).aspx>. 386 wkCACFContextReleaseD3DResources(m_context); 387 388 D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); 389 hr = m_d3dDevice->Reset(¶meters); 390 391 // TestCooperativeLevel told us the device may be reset now, so we should 392 // not be told here that the device is lost. 393 ASSERT(hr != D3DERR_DEVICELOST); 394 395 initD3DGeometry(); 396 397 return true; 398 } 399 400 } // namespace WebCore 401 402 #endif // USE(ACCELERATED_COMPOSITING) 403