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