• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &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 
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(&parameters);
451     initD3DGeometry();
452 }
453 
454 }
455 
456 #endif // USE(ACCELERATED_COMPOSITING)
457