Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / widget / windows / InProcessWinCompositorWidget.cpp
blobde491b734b5e4e430f8691dea83ae5c37285e8fc
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"
19 #include "nsWindow.h"
20 #include "VsyncDispatcher.h"
21 #include "WinCompositorWindowThread.h"
22 #include "VRShMem.h"
24 #include <ddraw.h>
26 namespace mozilla::widget {
28 using namespace mozilla::gfx;
29 using namespace mozilla;
31 /* static */
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));
40 } else {
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),
51 mWindow(aWindow),
52 mWnd(reinterpret_cast<HWND>(aInitData.hWnd())),
53 mTransparentSurfaceLock("mTransparentSurfaceLock"),
54 mTransparencyMode(uint32_t(aInitData.transparencyMode())),
55 mMemoryDC(nullptr),
56 mCompositeDC(nullptr),
57 mLockedBackBufferData(nullptr) {
58 MOZ_ASSERT(mWindow);
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;
72 mMemoryDC = nullptr;
75 bool InProcessWinCompositorWidget::OnWindowResize(
76 const LayoutDeviceIntSize& aSize) {
77 return true;
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
87 mPresentLock.Enter();
88 return true;
91 void InProcessWinCompositorWidget::PostRender(
92 WidgetRenderingContext* aContext) {
93 mPresentLock.Leave();
96 LayoutDeviceIntSize InProcessWinCompositorWidget::GetClientSize() {
97 RECT r;
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
116 // the DC.
117 HDC dc = GetWindowSurface();
118 if (!surf) {
119 if (!dc) {
120 return nullptr;
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) {
130 if (dc) {
131 FreeWindowSurface(dc);
133 return nullptr;
136 RefPtr<DrawTarget> dt =
137 mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(
138 surf->CairoSurface(), size);
139 if (dt) {
140 mCompositeDC = dc;
141 } else {
142 FreeWindowSurface(dc);
145 return dt.forget();
148 void InProcessWinCompositorWidget::EndRemoteDrawing() {
149 MOZ_ASSERT(!mLockedBackBufferData);
151 if (TransparencyModeIs(TransparencyMode::Transparent)) {
152 MOZ_ASSERT(mTransparentSurface);
153 RedrawTransparentWindow();
155 if (mCompositeDC) {
156 FreeWindowSurface(mCompositeDC);
158 mCompositeDC = nullptr;
161 bool InProcessWinCompositorWidget::NeedsToDeferEndRemoteDrawing() {
162 if (mNotDeferEndRemoteDrawing) {
163 return false;
166 IDirectDraw7* ddraw = DeviceManagerDx::Get()->GetDirectDraw();
167 if (!ddraw) {
168 return false;
171 DWORD scanLine = 0;
172 int height = ::GetSystemMetrics(SM_CYSCREEN);
173 HRESULT ret = ddraw->GetScanLine(&scanLine);
174 if (ret == DDERR_VERTICALBLANKINPROGRESS) {
175 scanLine = 0;
176 } else if (ret != DD_OK) {
177 return false;
180 // Check if there is a risk of tearing with GDI.
181 if (static_cast<int>(scanLine) > height / 2) {
182 // No need to defer.
183 return false;
186 return true;
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);
197 if (!target) {
198 return nullptr;
201 MOZ_ASSERT(target->GetBackendType() == BackendType::CAIRO);
203 uint8_t* destData;
204 IntSize destSize;
205 int32_t destStride;
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) {
231 return true;
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;
245 mMemoryDC = 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)) {
267 return;
270 mTransparencyMode = uint32_t(aMode);
271 mTransparentSurface = nullptr;
272 mMemoryDC = 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;
287 return sizeMode;
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) {
299 return;
302 EnsureTransparentSurface();
304 IntSize size = mTransparentSurface->GetSize();
305 if (!size.IsEmpty()) {
306 RefPtr<DrawTarget> drawTarget =
307 gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface, size);
308 if (!drawTarget) {
309 return;
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();
321 ::GdiFlush();
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);
327 RECT winRect;
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
337 : ::GetDC(mWnd);
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
364 // directly.
365 ::SetParent(aCompositorWnd, aParentWnd);
366 mSetParentCompleted = true;
368 } // namespace mozilla::widget