1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "WinCompositorWidget.h"
7 #include "gfxPrefs.h"
8 #include "mozilla/gfx/DeviceManagerDx.h"
9 #include "mozilla/gfx/Point.h"
10 #include "mozilla/layers/Compositor.h"
11 #include "mozilla/widget/PlatformWidgetTypes.h"
12 #include "nsWindow.h"
13 #include "VsyncDispatcher.h"
14 
15 #include <ddraw.h>
16 
17 namespace mozilla {
18 namespace widget {
19 
20 using namespace mozilla::gfx;
21 
WinCompositorWidget(const CompositorWidgetInitData & aInitData)22 WinCompositorWidget::WinCompositorWidget(const CompositorWidgetInitData& aInitData)
23  : mWidgetKey(aInitData.widgetKey()),
24    mWnd(reinterpret_cast<HWND>(aInitData.hWnd())),
25    mTransparencyMode(static_cast<nsTransparencyMode>(aInitData.transparencyMode())),
26    mMemoryDC(nullptr),
27    mCompositeDC(nullptr),
28    mLockedBackBufferData(nullptr)
29 {
30   MOZ_ASSERT(mWnd && ::IsWindow(mWnd));
31 
32   // mNotDeferEndRemoteDrawing is set on the main thread during init,
33   // but is only accessed after on the compositor thread.
34   mNotDeferEndRemoteDrawing = gfxPrefs::LayersCompositionFrameRate() == 0 ||
35                               gfxPlatform::IsInLayoutAsapMode() ||
36                               gfxPlatform::ForceSoftwareVsync();
37 }
38 
39 void
OnDestroyWindow()40 WinCompositorWidget::OnDestroyWindow()
41 {
42   mTransparentSurface = nullptr;
43   mMemoryDC = nullptr;
44 }
45 
46 bool
PreRender(WidgetRenderingContext * aContext)47 WinCompositorWidget::PreRender(WidgetRenderingContext* aContext)
48 {
49   // This can block waiting for WM_SETTEXT to finish
50   // Using PreRender is unnecessarily pessimistic because
51   // we technically only need to block during the present call
52   // not all of compositor rendering
53   mPresentLock.Enter();
54   return true;
55 }
56 
57 void
PostRender(WidgetRenderingContext * aContext)58 WinCompositorWidget::PostRender(WidgetRenderingContext* aContext)
59 {
60   mPresentLock.Leave();
61 }
62 
63 LayoutDeviceIntSize
GetClientSize()64 WinCompositorWidget::GetClientSize()
65 {
66   RECT r;
67   if (!::GetClientRect(mWnd, &r)) {
68     return LayoutDeviceIntSize();
69   }
70   return LayoutDeviceIntSize(
71     r.right - r.left,
72     r.bottom - r.top);
73 }
74 
75 already_AddRefed<gfx::DrawTarget>
StartRemoteDrawing()76 WinCompositorWidget::StartRemoteDrawing()
77 {
78   MOZ_ASSERT(!mCompositeDC);
79 
80   RefPtr<gfxASurface> surf;
81   if (mTransparencyMode == eTransparencyTransparent) {
82     surf = EnsureTransparentSurface();
83   }
84 
85   // Must call this after EnsureTransparentSurface(), since it could update
86   // the DC.
87   HDC dc = GetWindowSurface();
88   if (!surf) {
89     if (!dc) {
90       return nullptr;
91     }
92     uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 :
93         gfxWindowsSurface::FLAG_IS_TRANSPARENT;
94     surf = new gfxWindowsSurface(dc, flags);
95   }
96 
97   IntSize size = surf->GetSize();
98   if (size.width <= 0 || size.height <= 0) {
99     if (dc) {
100       FreeWindowSurface(dc);
101     }
102     return nullptr;
103   }
104 
105   MOZ_ASSERT(!mCompositeDC);
106   mCompositeDC = dc;
107 
108   return mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(surf->CairoSurface(), size);
109 }
110 
111 void
EndRemoteDrawing()112 WinCompositorWidget::EndRemoteDrawing()
113 {
114   MOZ_ASSERT(!mLockedBackBufferData);
115 
116   if (mTransparencyMode == eTransparencyTransparent) {
117     MOZ_ASSERT(mTransparentSurface);
118     RedrawTransparentWindow();
119   }
120   if (mCompositeDC) {
121     FreeWindowSurface(mCompositeDC);
122   }
123   mCompositeDC = nullptr;
124 }
125 
126 bool
NeedsToDeferEndRemoteDrawing()127 WinCompositorWidget::NeedsToDeferEndRemoteDrawing()
128 {
129   if(mNotDeferEndRemoteDrawing) {
130     return false;
131   }
132 
133   IDirectDraw7* ddraw = DeviceManagerDx::Get()->GetDirectDraw();
134   if (!ddraw) {
135     return false;
136   }
137 
138   DWORD scanLine = 0;
139   int height = ::GetSystemMetrics(SM_CYSCREEN);
140   HRESULT ret = ddraw->GetScanLine(&scanLine);
141   if (ret == DDERR_VERTICALBLANKINPROGRESS) {
142     scanLine = 0;
143   } else if (ret != DD_OK) {
144     return false;
145   }
146 
147   // Check if there is a risk of tearing with GDI.
148   if (static_cast<int>(scanLine) > height / 2) {
149     // No need to defer.
150     return false;
151   }
152 
153   return true;
154 }
155 
156 already_AddRefed<gfx::DrawTarget>
GetBackBufferDrawTarget(gfx::DrawTarget * aScreenTarget,const LayoutDeviceIntRect & aRect,const LayoutDeviceIntRect & aClearRect)157 WinCompositorWidget::GetBackBufferDrawTarget(gfx::DrawTarget* aScreenTarget,
158                                              const LayoutDeviceIntRect& aRect,
159                                              const LayoutDeviceIntRect& aClearRect)
160 {
161   MOZ_ASSERT(!mLockedBackBufferData);
162 
163   RefPtr<gfx::DrawTarget> target =
164     CompositorWidget::GetBackBufferDrawTarget(aScreenTarget, aRect, aClearRect);
165   if (!target) {
166     return nullptr;
167   }
168 
169   MOZ_ASSERT(target->GetBackendType() == BackendType::CAIRO);
170 
171   uint8_t* destData;
172   IntSize destSize;
173   int32_t destStride;
174   SurfaceFormat destFormat;
175   if (!target->LockBits(&destData, &destSize, &destStride, &destFormat)) {
176     // LockBits is not supported. Use original DrawTarget.
177     return target.forget();
178   }
179 
180   RefPtr<gfx::DrawTarget> dataTarget =
181     Factory::CreateDrawTargetForData(BackendType::CAIRO,
182                                      destData,
183                                      destSize,
184                                      destStride,
185                                      destFormat);
186   mLockedBackBufferData = destData;
187 
188   return dataTarget.forget();
189 }
190 
191 already_AddRefed<gfx::SourceSurface>
EndBackBufferDrawing()192 WinCompositorWidget::EndBackBufferDrawing()
193 {
194   if (mLockedBackBufferData) {
195     MOZ_ASSERT(mLastBackBuffer);
196     mLastBackBuffer->ReleaseBits(mLockedBackBufferData);
197     mLockedBackBufferData = nullptr;
198   }
199   return CompositorWidget::EndBackBufferDrawing();
200 }
201 
202 bool
InitCompositor(layers::Compositor * aCompositor)203 WinCompositorWidget::InitCompositor(layers::Compositor* aCompositor)
204 {
205   if (aCompositor->GetBackendType() == layers::LayersBackend::LAYERS_BASIC) {
206     DeviceManagerDx::Get()->InitializeDirectDraw();
207   }
208   return true;
209 }
210 
211 uintptr_t
GetWidgetKey()212 WinCompositorWidget::GetWidgetKey()
213 {
214   return mWidgetKey;
215 }
216 
217 void
EnterPresentLock()218 WinCompositorWidget::EnterPresentLock()
219 {
220   mPresentLock.Enter();
221 }
222 
223 void
LeavePresentLock()224 WinCompositorWidget::LeavePresentLock()
225 {
226   mPresentLock.Leave();
227 }
228 
229 RefPtr<gfxASurface>
EnsureTransparentSurface()230 WinCompositorWidget::EnsureTransparentSurface()
231 {
232   MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);
233 
234   IntSize size = GetClientSize().ToUnknownSize();
235   if (!mTransparentSurface || mTransparentSurface->GetSize() != size) {
236     mTransparentSurface = nullptr;
237     mMemoryDC = nullptr;
238     CreateTransparentSurface(size);
239   }
240 
241   RefPtr<gfxASurface> surface = mTransparentSurface;
242   return surface.forget();
243 }
244 
245 void
CreateTransparentSurface(const gfx::IntSize & aSize)246 WinCompositorWidget::CreateTransparentSurface(const gfx::IntSize& aSize)
247 {
248   MOZ_ASSERT(!mTransparentSurface && !mMemoryDC);
249   RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(aSize, SurfaceFormat::A8R8G8B8_UINT32);
250   mTransparentSurface = surface;
251   mMemoryDC = surface->GetDC();
252 }
253 
254 void
UpdateTransparency(nsTransparencyMode aMode)255 WinCompositorWidget::UpdateTransparency(nsTransparencyMode aMode)
256 {
257   if (mTransparencyMode == aMode) {
258     return;
259   }
260 
261   mTransparencyMode = aMode;
262   mTransparentSurface = nullptr;
263   mMemoryDC = nullptr;
264 
265   if (mTransparencyMode == eTransparencyTransparent) {
266     EnsureTransparentSurface();
267   }
268 }
269 
270 void
ClearTransparentWindow()271 WinCompositorWidget::ClearTransparentWindow()
272 {
273   if (!mTransparentSurface) {
274     return;
275   }
276 
277   EnsureTransparentSurface();
278 
279   IntSize size = mTransparentSurface->GetSize();
280   if (!size.IsEmpty()) {
281     RefPtr<DrawTarget> drawTarget =
282       gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface, size);
283     if (!drawTarget) {
284       return;
285     }
286     drawTarget->ClearRect(Rect(0, 0, size.width, size.height));
287     RedrawTransparentWindow();
288   }
289 }
290 
291 bool
RedrawTransparentWindow()292 WinCompositorWidget::RedrawTransparentWindow()
293 {
294   MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);
295 
296   LayoutDeviceIntSize size = GetClientSize();
297 
298   ::GdiFlush();
299 
300   BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
301   SIZE winSize = { size.width, size.height };
302   POINT srcPos = { 0, 0 };
303   HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
304   RECT winRect;
305   ::GetWindowRect(hWnd, &winRect);
306 
307   // perform the alpha blend
308   return !!::UpdateLayeredWindow(
309     hWnd, nullptr, (POINT*)&winRect, &winSize, mMemoryDC,
310     &srcPos, 0, &bf, ULW_ALPHA);
311 }
312 
313 HDC
GetWindowSurface()314 WinCompositorWidget::GetWindowSurface()
315 {
316   return eTransparencyTransparent == mTransparencyMode
317          ? mMemoryDC
318          : ::GetDC(mWnd);
319 }
320 
321 void
FreeWindowSurface(HDC dc)322 WinCompositorWidget::FreeWindowSurface(HDC dc)
323 {
324   if (eTransparencyTransparent != mTransparencyMode)
325     ::ReleaseDC(mWnd, dc);
326 }
327 
328 } // namespace widget
329 } // namespace mozilla
330