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;
d3d()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
winRectToCGRect(RECT rc)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
winRectToCGRect(RECT rc,RECT relativeToRect)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
windowsForContexts()80 static ContextToWindowMap& windowsForContexts()
81 {
82 DEFINE_STATIC_LOCAL(ContextToWindowMap, map, ());
83 return map;
84 }
85
initialPresentationParameters()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
acceleratedCompositingAvailable()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
didFlushContext(CACFContextRef context)121 void WKCACFLayerRenderer::didFlushContext(CACFContextRef context)
122 {
123 WKCACFLayerRenderer* window = windowsForContexts().get(context);
124 if (!window)
125 return;
126
127 window->renderSoon();
128 }
129
create()130 PassOwnPtr<WKCACFLayerRenderer> WKCACFLayerRenderer::create()
131 {
132 if (!acceleratedCompositingAvailable())
133 return 0;
134 return new WKCACFLayerRenderer;
135 }
136
WKCACFLayerRenderer()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
~WKCACFLayerRenderer()151 WKCACFLayerRenderer::~WKCACFLayerRenderer()
152 {
153 destroyRenderer();
154 }
155
setScrollFrame(const IntRect & scrollFrame)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
setRootContents(CGImageRef image)167 void WKCACFLayerRenderer::setRootContents(CGImageRef image)
168 {
169 ASSERT(m_rootLayer);
170 m_rootLayer->setContents(image);
171 renderSoon();
172 }
173
setRootChildLayer(WebCore::PlatformLayer * layer)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
setNeedsDisplay()190 void WKCACFLayerRenderer::setNeedsDisplay()
191 {
192 ASSERT(m_rootLayer);
193 m_rootLayer->setNeedsDisplay();
194 renderSoon();
195 }
196
createRenderer()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
destroyRenderer()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
resize()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
getDirtyRects(HWND window,Vector<CGRect> & outRects)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
renderTimerFired(Timer<WKCACFLayerRenderer> *)324 void WKCACFLayerRenderer::renderTimerFired(Timer<WKCACFLayerRenderer>*)
325 {
326 paint();
327 }
328
paint()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
render(const Vector<CGRect> & dirtyRects)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
renderSoon()414 void WKCACFLayerRenderer::renderSoon()
415 {
416 if (!m_renderTimer.isActive())
417 m_renderTimer.startOneShot(0);
418 }
419
bounds() const420 CGRect WKCACFLayerRenderer::bounds() const
421 {
422 RECT clientRect;
423 GetClientRect(m_hostWindow, &clientRect);
424
425 return winRectToCGRect(clientRect);
426 }
427
initD3DGeometry()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
resetDevice()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