summaryrefslogtreecommitdiff
path: root/widget/windows/WinCompositorWidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/WinCompositorWidget.cpp')
-rw-r--r--widget/windows/WinCompositorWidget.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/widget/windows/WinCompositorWidget.cpp b/widget/windows/WinCompositorWidget.cpp
new file mode 100644
index 0000000000..f660bd0192
--- /dev/null
+++ b/widget/windows/WinCompositorWidget.cpp
@@ -0,0 +1,329 @@
+/* -*- 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 "WinCompositorWidget.h"
+#include "gfxPrefs.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/widget/PlatformWidgetTypes.h"
+#include "nsWindow.h"
+#include "VsyncDispatcher.h"
+
+#include <ddraw.h>
+
+namespace mozilla {
+namespace widget {
+
+using namespace mozilla::gfx;
+
+WinCompositorWidget::WinCompositorWidget(const CompositorWidgetInitData& aInitData)
+ : mWidgetKey(aInitData.widgetKey()),
+ mWnd(reinterpret_cast<HWND>(aInitData.hWnd())),
+ mTransparencyMode(static_cast<nsTransparencyMode>(aInitData.transparencyMode())),
+ mMemoryDC(nullptr),
+ mCompositeDC(nullptr),
+ mLockedBackBufferData(nullptr)
+{
+ MOZ_ASSERT(mWnd && ::IsWindow(mWnd));
+
+ // mNotDeferEndRemoteDrawing is set on the main thread during init,
+ // but is only accessed after on the compositor thread.
+ mNotDeferEndRemoteDrawing = gfxPrefs::LayersCompositionFrameRate() == 0 ||
+ gfxPlatform::IsInLayoutAsapMode() ||
+ gfxPlatform::ForceSoftwareVsync();
+}
+
+void
+WinCompositorWidget::OnDestroyWindow()
+{
+ mTransparentSurface = nullptr;
+ mMemoryDC = nullptr;
+}
+
+bool
+WinCompositorWidget::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
+WinCompositorWidget::PostRender(WidgetRenderingContext* aContext)
+{
+ mPresentLock.Leave();
+}
+
+LayoutDeviceIntSize
+WinCompositorWidget::GetClientSize()
+{
+ RECT r;
+ if (!::GetClientRect(mWnd, &r)) {
+ return LayoutDeviceIntSize();
+ }
+ return LayoutDeviceIntSize(
+ r.right - r.left,
+ r.bottom - r.top);
+}
+
+already_AddRefed<gfx::DrawTarget>
+WinCompositorWidget::StartRemoteDrawing()
+{
+ MOZ_ASSERT(!mCompositeDC);
+
+ RefPtr<gfxASurface> 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;
+ }
+
+ MOZ_ASSERT(!mCompositeDC);
+ mCompositeDC = dc;
+
+ return mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(surf->CairoSurface(), size);
+}
+
+void
+WinCompositorWidget::EndRemoteDrawing()
+{
+ MOZ_ASSERT(!mLockedBackBufferData);
+
+ if (mTransparencyMode == eTransparencyTransparent) {
+ MOZ_ASSERT(mTransparentSurface);
+ RedrawTransparentWindow();
+ }
+ if (mCompositeDC) {
+ FreeWindowSurface(mCompositeDC);
+ }
+ mCompositeDC = nullptr;
+}
+
+bool
+WinCompositorWidget::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<int>(scanLine) > height / 2) {
+ // No need to defer.
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<gfx::DrawTarget>
+WinCompositorWidget::GetBackBufferDrawTarget(gfx::DrawTarget* aScreenTarget,
+ const LayoutDeviceIntRect& aRect,
+ const LayoutDeviceIntRect& aClearRect)
+{
+ MOZ_ASSERT(!mLockedBackBufferData);
+
+ RefPtr<gfx::DrawTarget> target =
+ CompositorWidget::GetBackBufferDrawTarget(aScreenTarget, aRect, aClearRect);
+ 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<gfx::DrawTarget> dataTarget =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ destData,
+ destSize,
+ destStride,
+ destFormat);
+ mLockedBackBufferData = destData;
+
+ return dataTarget.forget();
+}
+
+already_AddRefed<gfx::SourceSurface>
+WinCompositorWidget::EndBackBufferDrawing()
+{
+ if (mLockedBackBufferData) {
+ MOZ_ASSERT(mLastBackBuffer);
+ mLastBackBuffer->ReleaseBits(mLockedBackBufferData);
+ mLockedBackBufferData = nullptr;
+ }
+ return CompositorWidget::EndBackBufferDrawing();
+}
+
+bool
+WinCompositorWidget::InitCompositor(layers::Compositor* aCompositor)
+{
+ if (aCompositor->GetBackendType() == layers::LayersBackend::LAYERS_BASIC) {
+ DeviceManagerDx::Get()->InitializeDirectDraw();
+ }
+ return true;
+}
+
+uintptr_t
+WinCompositorWidget::GetWidgetKey()
+{
+ return mWidgetKey;
+}
+
+void
+WinCompositorWidget::EnterPresentLock()
+{
+ mPresentLock.Enter();
+}
+
+void
+WinCompositorWidget::LeavePresentLock()
+{
+ mPresentLock.Leave();
+}
+
+RefPtr<gfxASurface>
+WinCompositorWidget::EnsureTransparentSurface()
+{
+ MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);
+
+ IntSize size = GetClientSize().ToUnknownSize();
+ if (!mTransparentSurface || mTransparentSurface->GetSize() != size) {
+ mTransparentSurface = nullptr;
+ mMemoryDC = nullptr;
+ CreateTransparentSurface(size);
+ }
+
+ RefPtr<gfxASurface> surface = mTransparentSurface;
+ return surface.forget();
+}
+
+void
+WinCompositorWidget::CreateTransparentSurface(const gfx::IntSize& aSize)
+{
+ MOZ_ASSERT(!mTransparentSurface && !mMemoryDC);
+ RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(aSize, SurfaceFormat::A8R8G8B8_UINT32);
+ mTransparentSurface = surface;
+ mMemoryDC = surface->GetDC();
+}
+
+void
+WinCompositorWidget::UpdateTransparency(nsTransparencyMode aMode)
+{
+ if (mTransparencyMode == aMode) {
+ return;
+ }
+
+ mTransparencyMode = aMode;
+ mTransparentSurface = nullptr;
+ mMemoryDC = nullptr;
+
+ if (mTransparencyMode == eTransparencyTransparent) {
+ EnsureTransparentSurface();
+ }
+}
+
+void
+WinCompositorWidget::ClearTransparentWindow()
+{
+ if (!mTransparentSurface) {
+ return;
+ }
+
+ EnsureTransparentSurface();
+
+ IntSize size = mTransparentSurface->GetSize();
+ if (!size.IsEmpty()) {
+ RefPtr<DrawTarget> drawTarget =
+ gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface, size);
+ if (!drawTarget) {
+ return;
+ }
+ drawTarget->ClearRect(Rect(0, 0, size.width, size.height));
+ RedrawTransparentWindow();
+ }
+}
+
+bool
+WinCompositorWidget::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
+WinCompositorWidget::GetWindowSurface()
+{
+ return eTransparencyTransparent == mTransparencyMode
+ ? mMemoryDC
+ : ::GetDC(mWnd);
+}
+
+void
+WinCompositorWidget::FreeWindowSurface(HDC dc)
+{
+ if (eTransparencyTransparent != mTransparencyMode)
+ ::ReleaseDC(mWnd, dc);
+}
+
+} // namespace widget
+} // namespace mozilla