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/. */
6 #include "InProcessWinCompositorWidget.h"
8 #include "mozilla/StaticPrefs_layers.h"
9 #include "mozilla/gfx/DeviceManagerDx.h"
10 #include "mozilla/gfx/Point.h"
11 #include "mozilla/layers/Compositor.h"
12 #include "mozilla/layers/CompositorThread.h"
13 #include "mozilla/webrender/RenderThread.h"
14 #include "mozilla/widget/PlatformWidgetTypes.h"
15 #include "gfxPlatform.h"
16 #include "HeadlessCompositorWidget.h"
17 #include "HeadlessWidget.h"
18 #include "nsIWidget.h"
20 #include "VsyncDispatcher.h"
21 #include "WinCompositorWindowThread.h"
26 namespace mozilla::widget
{
28 using namespace mozilla::gfx
;
29 using namespace mozilla
;
32 RefPtr
<CompositorWidget
> CompositorWidget::CreateLocal(
33 const CompositorWidgetInitData
& aInitData
,
34 const layers::CompositorOptions
& aOptions
, nsIWidget
* aWidget
) {
35 if (aInitData
.type() ==
36 CompositorWidgetInitData::THeadlessCompositorWidgetInitData
) {
37 return new HeadlessCompositorWidget(
38 aInitData
.get_HeadlessCompositorWidgetInitData(), aOptions
,
39 static_cast<HeadlessWidget
*>(aWidget
));
41 return new InProcessWinCompositorWidget(
42 aInitData
.get_WinCompositorWidgetInitData(), aOptions
,
43 static_cast<nsWindow
*>(aWidget
));
47 InProcessWinCompositorWidget::InProcessWinCompositorWidget(
48 const WinCompositorWidgetInitData
& aInitData
,
49 const layers::CompositorOptions
& aOptions
, nsWindow
* aWindow
)
50 : WinCompositorWidget(aInitData
, aOptions
),
52 mWnd(reinterpret_cast<HWND
>(aInitData
.hWnd())),
53 mTransparentSurfaceLock("mTransparentSurfaceLock"),
54 mTransparencyMode(uint32_t(aInitData
.transparencyMode())),
56 mCompositeDC(nullptr),
57 mLockedBackBufferData(nullptr) {
59 MOZ_ASSERT(mWnd
&& ::IsWindow(mWnd
));
61 // mNotDeferEndRemoteDrawing is set on the main thread during init,
62 // but is only accessed after on the compositor thread.
63 mNotDeferEndRemoteDrawing
=
64 StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
65 gfxPlatform::IsInLayoutAsapMode() || gfxPlatform::ForceSoftwareVsync();
68 void InProcessWinCompositorWidget::OnDestroyWindow() {
69 gfx::CriticalSectionAutoEnter
presentLock(&mPresentLock
);
70 MutexAutoLock
lock(mTransparentSurfaceLock
);
71 mTransparentSurface
= nullptr;
75 bool InProcessWinCompositorWidget::OnWindowResize(
76 const LayoutDeviceIntSize
& aSize
) {
80 void InProcessWinCompositorWidget::OnWindowModeChange(nsSizeMode aSizeMode
) {}
82 bool InProcessWinCompositorWidget::PreRender(WidgetRenderingContext
* aContext
) {
83 // This can block waiting for WM_SETTEXT to finish
84 // Using PreRender is unnecessarily pessimistic because
85 // we technically only need to block during the present call
86 // not all of compositor rendering
91 void InProcessWinCompositorWidget::PostRender(
92 WidgetRenderingContext
* aContext
) {
96 LayoutDeviceIntSize
InProcessWinCompositorWidget::GetClientSize() {
98 if (!::GetClientRect(mWnd
, &r
)) {
99 return LayoutDeviceIntSize();
101 return LayoutDeviceIntSize(r
.right
- r
.left
, r
.bottom
- r
.top
);
104 already_AddRefed
<gfx::DrawTarget
>
105 InProcessWinCompositorWidget::StartRemoteDrawing() {
106 MutexAutoLock
lock(mTransparentSurfaceLock
);
108 MOZ_ASSERT(!mCompositeDC
);
110 RefPtr
<gfxASurface
> surf
;
111 if (TransparencyModeIs(TransparencyMode::Transparent
)) {
112 surf
= EnsureTransparentSurface();
115 // Must call this after EnsureTransparentSurface(), since it could update
117 HDC dc
= GetWindowSurface();
122 uint32_t flags
= TransparencyModeIs(TransparencyMode::Opaque
)
124 : gfxWindowsSurface::FLAG_IS_TRANSPARENT
;
125 surf
= new gfxWindowsSurface(dc
, flags
);
128 IntSize size
= surf
->GetSize();
129 if (size
.width
<= 0 || size
.height
<= 0) {
131 FreeWindowSurface(dc
);
136 RefPtr
<DrawTarget
> dt
=
137 mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(
138 surf
->CairoSurface(), size
);
142 FreeWindowSurface(dc
);
148 void InProcessWinCompositorWidget::EndRemoteDrawing() {
149 MOZ_ASSERT(!mLockedBackBufferData
);
151 if (TransparencyModeIs(TransparencyMode::Transparent
)) {
152 MOZ_ASSERT(mTransparentSurface
);
153 RedrawTransparentWindow();
156 FreeWindowSurface(mCompositeDC
);
158 mCompositeDC
= nullptr;
161 bool InProcessWinCompositorWidget::NeedsToDeferEndRemoteDrawing() {
162 if (mNotDeferEndRemoteDrawing
) {
166 IDirectDraw7
* ddraw
= DeviceManagerDx::Get()->GetDirectDraw();
172 int height
= ::GetSystemMetrics(SM_CYSCREEN
);
173 HRESULT ret
= ddraw
->GetScanLine(&scanLine
);
174 if (ret
== DDERR_VERTICALBLANKINPROGRESS
) {
176 } else if (ret
!= DD_OK
) {
180 // Check if there is a risk of tearing with GDI.
181 if (static_cast<int>(scanLine
) > height
/ 2) {
189 already_AddRefed
<gfx::DrawTarget
>
190 InProcessWinCompositorWidget::GetBackBufferDrawTarget(
191 gfx::DrawTarget
* aScreenTarget
, const gfx::IntRect
& aRect
,
192 bool* aOutIsCleared
) {
193 MOZ_ASSERT(!mLockedBackBufferData
);
195 RefPtr
<gfx::DrawTarget
> target
= CompositorWidget::GetBackBufferDrawTarget(
196 aScreenTarget
, aRect
, aOutIsCleared
);
201 MOZ_ASSERT(target
->GetBackendType() == BackendType::CAIRO
);
206 SurfaceFormat destFormat
;
207 if (!target
->LockBits(&destData
, &destSize
, &destStride
, &destFormat
)) {
208 // LockBits is not supported. Use original DrawTarget.
209 return target
.forget();
212 RefPtr
<gfx::DrawTarget
> dataTarget
= Factory::CreateDrawTargetForData(
213 BackendType::CAIRO
, destData
, destSize
, destStride
, destFormat
);
214 mLockedBackBufferData
= destData
;
216 return dataTarget
.forget();
219 already_AddRefed
<gfx::SourceSurface
>
220 InProcessWinCompositorWidget::EndBackBufferDrawing() {
221 if (mLockedBackBufferData
) {
222 MOZ_ASSERT(mLastBackBuffer
);
223 mLastBackBuffer
->ReleaseBits(mLockedBackBufferData
);
224 mLockedBackBufferData
= nullptr;
226 return CompositorWidget::EndBackBufferDrawing();
229 bool InProcessWinCompositorWidget::InitCompositor(
230 layers::Compositor
* aCompositor
) {
234 void InProcessWinCompositorWidget::EnterPresentLock() { mPresentLock
.Enter(); }
236 void InProcessWinCompositorWidget::LeavePresentLock() { mPresentLock
.Leave(); }
238 RefPtr
<gfxASurface
> InProcessWinCompositorWidget::EnsureTransparentSurface() {
239 mTransparentSurfaceLock
.AssertCurrentThreadOwns();
240 MOZ_ASSERT(TransparencyModeIs(TransparencyMode::Transparent
));
242 IntSize size
= GetClientSize().ToUnknownSize();
243 if (!mTransparentSurface
|| mTransparentSurface
->GetSize() != size
) {
244 mTransparentSurface
= nullptr;
246 CreateTransparentSurface(size
);
249 RefPtr
<gfxASurface
> surface
= mTransparentSurface
;
250 return surface
.forget();
253 void InProcessWinCompositorWidget::CreateTransparentSurface(
254 const gfx::IntSize
& aSize
) {
255 mTransparentSurfaceLock
.AssertCurrentThreadOwns();
256 MOZ_ASSERT(!mTransparentSurface
&& !mMemoryDC
);
257 RefPtr
<gfxWindowsSurface
> surface
=
258 new gfxWindowsSurface(aSize
, SurfaceFormat::A8R8G8B8_UINT32
);
259 mTransparentSurface
= surface
;
260 mMemoryDC
= surface
->GetDC();
263 void InProcessWinCompositorWidget::UpdateTransparency(TransparencyMode aMode
) {
264 gfx::CriticalSectionAutoEnter
presentLock(&mPresentLock
);
265 MutexAutoLock
lock(mTransparentSurfaceLock
);
266 if (TransparencyModeIs(aMode
)) {
270 mTransparencyMode
= uint32_t(aMode
);
271 mTransparentSurface
= nullptr;
274 if (aMode
== TransparencyMode::Transparent
) {
275 EnsureTransparentSurface();
279 void InProcessWinCompositorWidget::NotifyVisibilityUpdated(
280 nsSizeMode aSizeMode
, bool aIsFullyOccluded
) {
281 mSizeMode
= aSizeMode
;
282 mIsFullyOccluded
= aIsFullyOccluded
;
285 nsSizeMode
InProcessWinCompositorWidget::GetWindowSizeMode() const {
286 nsSizeMode sizeMode
= mSizeMode
;
290 bool InProcessWinCompositorWidget::GetWindowIsFullyOccluded() const {
291 bool isFullyOccluded
= mIsFullyOccluded
;
292 return isFullyOccluded
;
295 void InProcessWinCompositorWidget::ClearTransparentWindow() {
296 gfx::CriticalSectionAutoEnter
presentLock(&mPresentLock
);
297 MutexAutoLock
lock(mTransparentSurfaceLock
);
298 if (!mTransparentSurface
) {
302 EnsureTransparentSurface();
304 IntSize size
= mTransparentSurface
->GetSize();
305 if (!size
.IsEmpty()) {
306 RefPtr
<DrawTarget
> drawTarget
=
307 gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface
, size
);
311 drawTarget
->ClearRect(Rect(0, 0, size
.width
, size
.height
));
312 RedrawTransparentWindow();
316 bool InProcessWinCompositorWidget::RedrawTransparentWindow() {
317 MOZ_ASSERT(TransparencyModeIs(TransparencyMode::Transparent
));
319 LayoutDeviceIntSize size
= GetClientSize();
323 BLENDFUNCTION bf
= {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
324 SIZE winSize
= {size
.width
, size
.height
};
325 POINT srcPos
= {0, 0};
326 HWND hWnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
328 ::GetWindowRect(hWnd
, &winRect
);
330 // perform the alpha blend
331 return !!::UpdateLayeredWindow(hWnd
, nullptr, (POINT
*)&winRect
, &winSize
,
332 mMemoryDC
, &srcPos
, 0, &bf
, ULW_ALPHA
);
335 HDC
InProcessWinCompositorWidget::GetWindowSurface() {
336 return TransparencyModeIs(TransparencyMode::Transparent
) ? mMemoryDC
340 void InProcessWinCompositorWidget::FreeWindowSurface(HDC dc
) {
341 if (!TransparencyModeIs(TransparencyMode::Transparent
)) {
342 ::ReleaseDC(mWnd
, dc
);
346 bool InProcessWinCompositorWidget::IsHidden() const { return ::IsIconic(mWnd
); }
348 nsIWidget
* InProcessWinCompositorWidget::RealWidget() { return mWindow
; }
350 void InProcessWinCompositorWidget::ObserveVsync(VsyncObserver
* aObserver
) {
351 if (RefPtr
<CompositorVsyncDispatcher
> cvd
=
352 mWindow
->GetCompositorVsyncDispatcher()) {
353 cvd
->SetCompositorVsyncObserver(aObserver
);
357 void InProcessWinCompositorWidget::UpdateCompositorWnd(
358 const HWND aCompositorWnd
, const HWND aParentWnd
) {
359 MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
360 MOZ_ASSERT(aCompositorWnd
&& aParentWnd
);
361 MOZ_ASSERT(aParentWnd
== mWnd
);
363 // Since we're in the parent process anyway, we can just call SetParent
365 ::SetParent(aCompositorWnd
, aParentWnd
);
366 mSetParentCompleted
= true;
368 } // namespace mozilla::widget