/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "InProcessWinCompositorWidget.h" #include "mozilla/StaticPrefs_layers.h" #include "mozilla/gfx/DeviceManagerDx.h" #include "mozilla/gfx/Point.h" #include "mozilla/layers/Compositor.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/webrender/RenderThread.h" #include "mozilla/widget/PlatformWidgetTypes.h" #include "gfxPlatform.h" #include "HeadlessCompositorWidget.h" #include "HeadlessWidget.h" #include "nsWindow.h" #include "VsyncDispatcher.h" #include "WinCompositorWindowThread.h" #include "VRShMem.h" #include namespace mozilla { namespace widget { using namespace mozilla::gfx; using namespace mozilla; /* static */ RefPtr CompositorWidget::CreateLocal( const CompositorWidgetInitData& aInitData, const layers::CompositorOptions& aOptions, nsIWidget* aWidget) { if (aInitData.type() == CompositorWidgetInitData::THeadlessCompositorWidgetInitData) { return new HeadlessCompositorWidget( aInitData.get_HeadlessCompositorWidgetInitData(), aOptions, static_cast(aWidget)); } else { return new InProcessWinCompositorWidget( aInitData.get_WinCompositorWidgetInitData(), aOptions, static_cast(aWidget)); } } InProcessWinCompositorWidget::InProcessWinCompositorWidget( const WinCompositorWidgetInitData& aInitData, const layers::CompositorOptions& aOptions, nsWindow* aWindow) : WinCompositorWidget(aInitData, aOptions), mWindow(aWindow), mWnd(reinterpret_cast(aInitData.hWnd())), mTransparentSurfaceLock("mTransparentSurfaceLock"), mTransparencyMode(aInitData.transparencyMode()), mMemoryDC(nullptr), mCompositeDC(nullptr), mLockedBackBufferData(nullptr) { MOZ_ASSERT(mWindow); MOZ_ASSERT(mWnd && ::IsWindow(mWnd)); // mNotDeferEndRemoteDrawing is set on the main thread during init, // but is only accessed after on the compositor thread. mNotDeferEndRemoteDrawing = StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 || gfxPlatform::IsInLayoutAsapMode() || gfxPlatform::ForceSoftwareVsync(); } void InProcessWinCompositorWidget::OnDestroyWindow() { gfx::CriticalSectionAutoEnter presentLock(&mPresentLock); MutexAutoLock lock(mTransparentSurfaceLock); mTransparentSurface = nullptr; mMemoryDC = nullptr; } bool InProcessWinCompositorWidget::OnWindowResize( const LayoutDeviceIntSize& aSize) { return true; } void InProcessWinCompositorWidget::OnWindowModeChange(nsSizeMode aSizeMode) {} bool InProcessWinCompositorWidget::PreRender(WidgetRenderingContext* aContext) { // This can block waiting for WM_SETTEXT to finish // Using PreRender is unnecessarily pessimistic because // we technically only need to block during the present call // not all of compositor rendering mPresentLock.Enter(); return true; } void InProcessWinCompositorWidget::PostRender( WidgetRenderingContext* aContext) { mPresentLock.Leave(); } LayoutDeviceIntSize InProcessWinCompositorWidget::GetClientSize() { RECT r; if (!::GetClientRect(mWnd, &r)) { return LayoutDeviceIntSize(); } return LayoutDeviceIntSize(r.right - r.left, r.bottom - r.top); } already_AddRefed InProcessWinCompositorWidget::StartRemoteDrawing() { MutexAutoLock lock(mTransparentSurfaceLock); MOZ_ASSERT(!mCompositeDC); RefPtr surf; if (mTransparencyMode == eTransparencyTransparent) { surf = EnsureTransparentSurface(); } // Must call this after EnsureTransparentSurface(), since it could update // the DC. HDC dc = GetWindowSurface(); if (!surf) { if (!dc) { return nullptr; } uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 : gfxWindowsSurface::FLAG_IS_TRANSPARENT; surf = new gfxWindowsSurface(dc, flags); } IntSize size = surf->GetSize(); if (size.width <= 0 || size.height <= 0) { if (dc) { FreeWindowSurface(dc); } return nullptr; } RefPtr dt = mozilla::gfx::Factory::CreateDrawTargetForCairoSurface( surf->CairoSurface(), size); if (dt) { mCompositeDC = dc; } else { FreeWindowSurface(dc); } return dt.forget(); } void InProcessWinCompositorWidget::EndRemoteDrawing() { MOZ_ASSERT(!mLockedBackBufferData); if (mTransparencyMode == eTransparencyTransparent) { MOZ_ASSERT(mTransparentSurface); RedrawTransparentWindow(); } if (mCompositeDC) { FreeWindowSurface(mCompositeDC); } mCompositeDC = nullptr; } bool InProcessWinCompositorWidget::NeedsToDeferEndRemoteDrawing() { if (mNotDeferEndRemoteDrawing) { return false; } IDirectDraw7* ddraw = DeviceManagerDx::Get()->GetDirectDraw(); if (!ddraw) { return false; } DWORD scanLine = 0; int height = ::GetSystemMetrics(SM_CYSCREEN); HRESULT ret = ddraw->GetScanLine(&scanLine); if (ret == DDERR_VERTICALBLANKINPROGRESS) { scanLine = 0; } else if (ret != DD_OK) { return false; } // Check if there is a risk of tearing with GDI. if (static_cast(scanLine) > height / 2) { // No need to defer. return false; } return true; } already_AddRefed InProcessWinCompositorWidget::GetBackBufferDrawTarget( gfx::DrawTarget* aScreenTarget, const gfx::IntRect& aRect, bool* aOutIsCleared) { MOZ_ASSERT(!mLockedBackBufferData); RefPtr target = CompositorWidget::GetBackBufferDrawTarget( aScreenTarget, aRect, aOutIsCleared); if (!target) { return nullptr; } MOZ_ASSERT(target->GetBackendType() == BackendType::CAIRO); uint8_t* destData; IntSize destSize; int32_t destStride; SurfaceFormat destFormat; if (!target->LockBits(&destData, &destSize, &destStride, &destFormat)) { // LockBits is not supported. Use original DrawTarget. return target.forget(); } RefPtr dataTarget = Factory::CreateDrawTargetForData( BackendType::CAIRO, destData, destSize, destStride, destFormat); mLockedBackBufferData = destData; return dataTarget.forget(); } already_AddRefed InProcessWinCompositorWidget::EndBackBufferDrawing() { if (mLockedBackBufferData) { MOZ_ASSERT(mLastBackBuffer); mLastBackBuffer->ReleaseBits(mLockedBackBufferData); mLockedBackBufferData = nullptr; } return CompositorWidget::EndBackBufferDrawing(); } bool InProcessWinCompositorWidget::InitCompositor( layers::Compositor* aCompositor) { return true; } void InProcessWinCompositorWidget::EnterPresentLock() { mPresentLock.Enter(); } void InProcessWinCompositorWidget::LeavePresentLock() { mPresentLock.Leave(); } RefPtr InProcessWinCompositorWidget::EnsureTransparentSurface() { mTransparentSurfaceLock.AssertCurrentThreadOwns(); MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent); IntSize size = GetClientSize().ToUnknownSize(); if (!mTransparentSurface || mTransparentSurface->GetSize() != size) { mTransparentSurface = nullptr; mMemoryDC = nullptr; CreateTransparentSurface(size); } RefPtr surface = mTransparentSurface; return surface.forget(); } void InProcessWinCompositorWidget::CreateTransparentSurface( const gfx::IntSize& aSize) { mTransparentSurfaceLock.AssertCurrentThreadOwns(); MOZ_ASSERT(!mTransparentSurface && !mMemoryDC); RefPtr surface = new gfxWindowsSurface(aSize, SurfaceFormat::A8R8G8B8_UINT32); mTransparentSurface = surface; mMemoryDC = surface->GetDC(); } void InProcessWinCompositorWidget::UpdateTransparency( nsTransparencyMode aMode) { gfx::CriticalSectionAutoEnter presentLock(&mPresentLock); MutexAutoLock lock(mTransparentSurfaceLock); if (mTransparencyMode == aMode) { return; } mTransparencyMode = aMode; mTransparentSurface = nullptr; mMemoryDC = nullptr; if (mTransparencyMode == eTransparencyTransparent) { EnsureTransparentSurface(); } } void InProcessWinCompositorWidget::NotifyVisibilityUpdated( nsSizeMode aSizeMode, bool aIsFullyOccluded) { mSizeMode = aSizeMode; mIsFullyOccluded = aIsFullyOccluded; } nsSizeMode InProcessWinCompositorWidget::GetWindowSizeMode() const { nsSizeMode sizeMode = mSizeMode; return sizeMode; } bool InProcessWinCompositorWidget::GetWindowIsFullyOccluded() const { bool isFullyOccluded = mIsFullyOccluded; return isFullyOccluded; } bool InProcessWinCompositorWidget::HasGlass() const { MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread() || wr::RenderThread::IsInRenderThread()); return mTransparencyMode == eTransparencyBorderlessGlass; } void InProcessWinCompositorWidget::ClearTransparentWindow() { gfx::CriticalSectionAutoEnter presentLock(&mPresentLock); MutexAutoLock lock(mTransparentSurfaceLock); if (!mTransparentSurface) { return; } EnsureTransparentSurface(); IntSize size = mTransparentSurface->GetSize(); if (!size.IsEmpty()) { RefPtr drawTarget = gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface, size); if (!drawTarget) { return; } drawTarget->ClearRect(Rect(0, 0, size.width, size.height)); RedrawTransparentWindow(); } } bool InProcessWinCompositorWidget::RedrawTransparentWindow() { MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent); LayoutDeviceIntSize size = GetClientSize(); ::GdiFlush(); BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; SIZE winSize = {size.width, size.height}; POINT srcPos = {0, 0}; HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true); RECT winRect; ::GetWindowRect(hWnd, &winRect); // perform the alpha blend return !!::UpdateLayeredWindow(hWnd, nullptr, (POINT*)&winRect, &winSize, mMemoryDC, &srcPos, 0, &bf, ULW_ALPHA); } HDC InProcessWinCompositorWidget::GetWindowSurface() { return eTransparencyTransparent == mTransparencyMode ? mMemoryDC : ::GetDC(mWnd); } void InProcessWinCompositorWidget::FreeWindowSurface(HDC dc) { if (eTransparencyTransparent != mTransparencyMode) ::ReleaseDC(mWnd, dc); } bool InProcessWinCompositorWidget::IsHidden() const { return ::IsIconic(mWnd); } nsIWidget* InProcessWinCompositorWidget::RealWidget() { return mWindow; } void InProcessWinCompositorWidget::ObserveVsync(VsyncObserver* aObserver) { if (RefPtr cvd = mWindow->GetCompositorVsyncDispatcher()) { cvd->SetCompositorVsyncObserver(aObserver); } } } // namespace widget } // namespace mozilla