Bug 1186000 - Support screen mirroring to HDMI display on gonk r=mwu,mattwoodrow

This commit is contained in:
Sotaro Ikeda 2015-08-08 13:50:47 -07:00
parent be1f9cecbe
commit def035db89
12 changed files with 253 additions and 61 deletions

View File

@ -996,6 +996,9 @@ pref("gfx.canvas.max-size-for-skia-gl", -1);
// enable fence with readpixels for SurfaceStream
pref("gfx.gralloc.fence-with-readpixels", true);
// enable screen mirroring to external display
pref("gfx.screen-mirroring.enabled", true);
// The url of the page used to display network error details.
pref("b2g.neterror.url", "net_error.html");

View File

@ -55,11 +55,17 @@
#ifdef MOZ_WIDGET_ANDROID
#include <android/log.h>
#include "AndroidBridge.h"
#endif
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
#include "opengl/CompositorOGL.h"
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "ScopedGLHelpers.h"
#endif
#ifdef MOZ_WIDGET_GONK
#include "nsScreenManagerGonk.h"
#include "nsWindow.h"
#endif
#include "GeckoProfiler.h"
#include "TextRenderer.h" // for TextRenderer
@ -302,7 +308,7 @@ LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
ApplyOcclusionCulling(mRoot, opaque);
Render();
#ifdef MOZ_WIDGET_ANDROID
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
RenderToPresentationSurface();
#endif
mGeometryChanged = false;
@ -780,7 +786,7 @@ LayerManagerComposite::Render()
RecordFrame();
}
#ifdef MOZ_WIDGET_ANDROID
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
class ScopedCompositorProjMatrix {
public:
ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
@ -854,6 +860,7 @@ private:
void
LayerManagerComposite::RenderToPresentationSurface()
{
#ifdef MOZ_WIDGET_ANDROID
if (!AndroidBridge::Bridge()) {
return;
}
@ -886,17 +893,60 @@ LayerManagerComposite::RenderToPresentationSurface()
const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window);
#elif defined(MOZ_WIDGET_GONK)
CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get());
nsScreenGonk* screen = static_cast<nsWindow*>(mCompositor->GetWidget())->GetScreen();
if (!screen->IsPrimaryScreen()) {
// Only primary screen support mirroring
return;
}
nsWindow* mirrorScreenWidget = screen->GetMirroringWidget();
if (!mirrorScreenWidget) {
// No mirroring
return;
}
nsScreenGonk* mirrorScreen = mirrorScreenWidget->GetScreen();
if (!mirrorScreen->GetTopWindows().IsEmpty()) {
return;
}
EGLSurface surface = mirrorScreen->GetEGLSurface();
if (surface == LOCAL_EGL_NO_SURFACE) {
// Create GLContext
nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget);
mirrorScreenWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT,
reinterpret_cast<uintptr_t>(gl.get()));
surface = mirrorScreen->GetEGLSurface();
if (surface == LOCAL_EGL_NO_SURFACE) {
// Failed to create EGLSurface
return;
}
}
GLContext* gl = compositor->gl();
GLContextEGL* egl = GLContextEGL::Cast(gl);
const IntSize windowSize = mirrorScreen->GetNaturalBounds().Size();
#endif
if ((windowSize.width <= 0) || (windowSize.height <= 0)) {
return;
}
ScreenRotation rotation = compositor->GetScreenRotation();
const int actualWidth = windowSize.width;
const int actualHeight = windowSize.height;
const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize();
const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height);
const int pageWidth = originalSize.width;
const int pageHeight = originalSize.height;
int pageWidth = originalSize.width;
int pageHeight = originalSize.height;
if (rotation == ROTATION_90 || rotation == ROTATION_270) {
pageWidth = originalSize.height;
pageHeight = originalSize.width;
}
float scale = 1.0;
@ -910,42 +960,47 @@ LayerManagerComposite::RenderToPresentationSurface()
ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize);
const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0);
ScopedCompositorRenderOffset overrideRenderOffset(compositor, offset);
ScopedContextSurfaceOverride overrideSurface(egl, surface);
Matrix viewMatrix = ComputeTransformForRotation(originalRect,
rotation);
viewMatrix.Invert(); // unrotate
viewMatrix.PostScale(scale, scale);
viewMatrix.PostTranslate(offset.x, offset.y);
Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
mRoot->ComputeEffectiveTransforms(matrix);
nsIntRegion opaque;
ApplyOcclusionCulling(mRoot, opaque);
nsIntRegion invalid;
Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight);
Rect rect, actualBounds;
mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds);
// Override the projection matrix since the presentation frame buffer
// is probably not the same size as the device frame buffer. The override
// projection matrix also scales the content to fit into the presentation
// frame buffer.
Matrix viewMatrix;
viewMatrix.PreTranslate(-1.0, 1.0);
viewMatrix.PreScale((2.0f * scale) / (float)actualWidth, (2.0f * scale) / (float)actualHeight);
viewMatrix.PreScale(1.0f, -1.0f);
viewMatrix.PreTranslate((int)((float)offset.x / scale), offset.y);
Matrix4x4 projMatrix = Matrix4x4::From2D(viewMatrix);
ScopedCompositorProjMatrix overrideProjMatrix(compositor, projMatrix);
// The Java side of Fennec sets a scissor rect that accounts for
// chrome such as the URL bar. Override that so that the entire frame buffer
// is cleared.
ScopedScissorRect screen(egl, 0, 0, actualWidth, actualHeight);
ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
egl->fClearColor(0.0, 0.0, 0.0, 0.0);
egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
const IntRect clipRect = IntRect(0, 0, (int)(scale * pageWidth), actualHeight);
const IntRect clipRect = IntRect(0, 0, actualWidth, actualHeight);
RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect));
RootLayer()->RenderLayer(clipRect);
mCompositor->EndFrame();
mCompositor->SetDispAcquireFence(mRoot);
mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame()
#ifdef MOZ_WIDGET_GONK
nsRefPtr<Composer2D> composer2D;
composer2D = mCompositor->GetWidget()->GetComposer2D();
if (composer2D) {
composer2D->Render(mirrorScreenWidget);
}
#endif
}
#endif

View File

@ -301,7 +301,7 @@ private:
* Render the current layer tree to the active target.
*/
void Render();
#ifdef MOZ_WIDGET_ANDROID
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
void RenderToPresentationSurface();
#endif

View File

@ -263,6 +263,7 @@ private:
DECL_GFX_PREF(Once, "gfx.vsync.hw-vsync.enabled", HardwareVsyncEnabled, bool, false);
DECL_GFX_PREF(Once, "gfx.vsync.refreshdriver", VsyncAlignedRefreshDriver, bool, false);
DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs", WorkAroundDriverBugs, bool, true);
DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled", ScreenMirroringEnabled, bool, false);
DECL_GFX_PREF(Live, "gl.msaa-level", MSAALevel, uint32_t, 2);
DECL_GFX_PREF(Live, "gl.require-hardware", RequireHardwareGL, bool, false);

View File

@ -751,6 +751,9 @@ HwcComposer2D::Render(nsIWidget* aWidget)
mList->hwLayers[mList->numHwLayers - 1].handle = dispSurface->lastHandle;
mList->hwLayers[mList->numHwLayers - 1].acquireFenceFd = dispSurface->GetPrevDispAcquireFd();
} else {
// Update screen rect to handle a case that TryRenderWithHwc() is not called.
mScreenRect = screen->GetNaturalBounds();
mList->flags = HWC_GEOMETRY_CHANGED;
mList->numHwLayers = 2;
mList->hwLayers[0].hints = 0;
@ -773,7 +776,8 @@ HwcComposer2D::Prepare(buffer_handle_t dispHandle, int fence, nsScreenGonk* scre
if (mPrepared) {
LOGE("Multiple hwc prepare calls!");
}
mHal->Prepare(mList, screen->GetDisplayType(), dispHandle, fence);
hwc_rect_t dispRect = {0, 0, mScreenRect.width, mScreenRect.height};
mHal->Prepare(mList, screen->GetDisplayType(), dispRect, dispHandle, fence);
mPrepared = true;
}

View File

@ -32,8 +32,6 @@ HwcHAL::HwcHAL()
printf_stderr("HwcHAL Error: Cannot load hwcomposer");
return;
}
GetHwcAttributes();
}
HwcHAL::~HwcHAL()
@ -79,6 +77,7 @@ HwcHAL::ResetHwc()
int
HwcHAL::Prepare(HwcList *aList,
uint32_t aDisp,
hwc_rect_t aDispRect,
buffer_handle_t aHandle,
int aFenceFd)
{
@ -103,8 +102,8 @@ HwcHAL::Prepare(HwcList *aList,
aList->hwLayers[idx].handle = aHandle;
aList->hwLayers[idx].blending = HWC_BLENDING_PREMULT;
aList->hwLayers[idx].compositionType = HWC_FRAMEBUFFER_TARGET;
SetCrop(aList->hwLayers[idx], mHwcRect);
aList->hwLayers[idx].displayFrame = mHwcRect;
SetCrop(aList->hwLayers[idx], aDispRect);
aList->hwLayers[idx].displayFrame = aDispRect;
aList->hwLayers[idx].visibleRegionScreen.numRects = 1;
aList->hwLayers[idx].visibleRegionScreen.rects = &aList->hwLayers[idx].displayFrame;
aList->hwLayers[idx].acquireFenceFd = aFenceFd;
@ -195,20 +194,6 @@ HwcHAL::RegisterHwcEventCallback(const HwcHALProcs_t &aProcs)
#endif
}
void
HwcHAL::GetHwcAttributes()
{
int32_t values[2];
const uint32_t attrs[] = {
HWC_DISPLAY_WIDTH,
HWC_DISPLAY_HEIGHT,
HWC_DISPLAY_NO_ATTRIBUTE
};
mHwc->getDisplayAttributes(mHwc, 0, 0, attrs, values);
mHwcRect = {0, 0, values[0], values[1]};
}
uint32_t
HwcHAL::GetAPIVersion() const
{

View File

@ -31,8 +31,6 @@ public:
virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
virtual const hwc_rect_t GetHwcRect() const override { return mHwcRect; }
virtual void SetEGLInfo(hwc_display_t aDpy,
hwc_surface_t aSur) override { }
@ -45,6 +43,7 @@ public:
virtual int Prepare(HwcList *aList,
uint32_t aDisp,
hwc_rect_t aDispRect,
buffer_handle_t aHandle,
int aFenceFd) override;
@ -60,13 +59,10 @@ public:
virtual bool RegisterHwcEventCallback(const HwcHALProcs_t &aProcs) override;
private:
void GetHwcAttributes();
uint32_t GetAPIVersion() const;
private:
HwcDevice *mHwc = nullptr;
hwc_rect_t mHwcRect = {0};
};
} // namespace mozilla

View File

@ -85,9 +85,6 @@ public:
// Check if mHwc exists
virtual bool HasHwc() const = 0;
// Get HwcRect
virtual const hwc_rect_t GetHwcRect() const = 0;
// Set EGL info (only ICS need this info)
virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
hwc_surface_t aEGLSurface) = 0;
@ -105,6 +102,7 @@ public:
// HwcDevice prepare
virtual int Prepare(HwcList *aList,
uint32_t aDisp,
hwc_rect_t aDispRect,
buffer_handle_t aHandle,
int aFenceFd) = 0;

View File

@ -86,6 +86,7 @@ HwcICS::ResetHwc()
int
HwcICS::Prepare(HwcList *aList,
uint32_t aDisp,
hwc_rect_t aDispRect,
buffer_handle_t aHandle,
int aFenceFd)
{

View File

@ -31,8 +31,6 @@ public:
virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
virtual const hwc_rect_t GetHwcRect() const override { return {0}; }
virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
hwc_surface_t aEGLSurface) override;
@ -45,6 +43,7 @@ public:
virtual int Prepare(HwcList *aList,
uint32_t aDisp,
hwc_rect_t aDispRect,
buffer_handle_t aHandle,
int aFenceFd) override;

View File

@ -121,6 +121,7 @@ nsScreenGonk::nsScreenGonk(uint32_t aId,
#if ANDROID_VERSION >= 17
, mDisplaySurface(aNativeData.mDisplaySurface)
#endif
, mIsMirroring(false)
, mDisplayType(aDisplayType)
, mEGLDisplay(EGL_NO_DISPLAY)
, mEGLSurface(EGL_NO_SURFACE)
@ -143,8 +144,22 @@ nsScreenGonk::nsScreenGonk(uint32_t aId,
mColorDepth = SurfaceFormatToColorDepth(mSurfaceFormat);
}
static void
ReleaseGLContextSync(mozilla::gl::GLContext* aGLContext) {
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
aGLContext->Release();
}
nsScreenGonk::~nsScreenGonk()
{
// Release GLContext on compositor thread
if (mGLContext) {
CompositorParent::CompositorLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(&ReleaseGLContextSync,
mGLContext.forget().take()));
mGLContext = nullptr;
}
}
bool
@ -359,7 +374,8 @@ nsScreenGonk::GetDisplayType()
}
void
nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay, hwc_surface_t aSurface,
nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay,
hwc_surface_t aSurface,
gl::GLContext* aGLContext)
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
@ -378,10 +394,116 @@ nsScreenGonk::GetEGLDisplay()
hwc_surface_t
nsScreenGonk::GetEGLSurface()
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
return mEGLSurface;
}
static void
UpdateMirroringWidgetSync(nsScreenGonk* aScreen, nsWindow* aWindow) {
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
already_AddRefed<nsWindow> window(aWindow);
aScreen->UpdateMirroringWidget(window);
}
bool
nsScreenGonk::EnableMirroring()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsPrimaryScreen());
nsRefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen();
NS_ENSURE_TRUE(primaryScreen, false);
bool ret = primaryScreen->SetMirroringScreen(this);
NS_ENSURE_TRUE(ret, false);
// Create a widget for mirroring
nsWidgetInitData initData;
initData.mScreenId = mId;
nsRefPtr<nsWindow> window = new nsWindow();
window->Create(nullptr, nullptr, mNaturalBounds, &initData);
MOZ_ASSERT(static_cast<nsWindow*>(window)->GetScreen() == this);
// Update mMirroringWidget on compositor thread
CompositorParent::CompositorLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(&UpdateMirroringWidgetSync,
primaryScreen,
window.forget().take()));
mIsMirroring = true;
return true;
}
bool
nsScreenGonk::DisableMirroring()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsPrimaryScreen());
mIsMirroring = false;
nsRefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen();
NS_ENSURE_TRUE(primaryScreen, false);
bool ret = primaryScreen->ClearMirroringScreen(this);
NS_ENSURE_TRUE(ret, false);
// Update mMirroringWidget on compositor thread
CompositorParent::CompositorLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(&UpdateMirroringWidgetSync,
primaryScreen,
nullptr));
return true;
}
bool
nsScreenGonk::SetMirroringScreen(nsScreenGonk* aScreen)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsPrimaryScreen());
if (mMirroringScreen) {
return false;
}
mMirroringScreen = mMirroringScreen;
return true;
}
bool
nsScreenGonk::ClearMirroringScreen(nsScreenGonk* aScreen)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsPrimaryScreen());
if (mMirroringScreen != aScreen) {
return false;
}
mMirroringScreen = nullptr;
return true;
}
void
nsScreenGonk::UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow)
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
MOZ_ASSERT(IsPrimaryScreen());
if (mMirroringWidget) {
nsCOMPtr<nsIWidget> widget = mMirroringWidget.forget();
NS_ReleaseOnMainThread(widget);
}
mMirroringWidget = aWindow;
}
nsWindow*
nsScreenGonk::GetMirroringWidget()
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
MOZ_ASSERT(IsPrimaryScreen());
return mMirroringWidget;
}
NS_IMPL_ISUPPORTS(nsScreenManagerGonk, nsIScreenManager)
nsScreenManagerGonk::nsScreenManagerGonk()
@ -611,7 +733,7 @@ NotifyDisplayChange(uint32_t aId, bool aConnected)
nsresult
nsScreenManagerGonk::AddScreen(GonkDisplay::DisplayType aDisplayType,
android::IGraphicBufferProducer* aProducer)
android::IGraphicBufferProducer* aSink)
{
MOZ_ASSERT(NS_IsMainThread());
@ -622,13 +744,19 @@ nsScreenManagerGonk::AddScreen(GonkDisplay::DisplayType aDisplayType,
NS_ENSURE_TRUE(!IsScreenConnected(id), NS_ERROR_FAILURE);
GonkDisplay::NativeData nativeData =
GetGonkDisplay()->GetNativeData(aDisplayType, aProducer);
GetGonkDisplay()->GetNativeData(aDisplayType, aSink);
nsScreenGonk* screen = new nsScreenGonk(id, aDisplayType, nativeData);
mScreens.AppendElement(screen);
NotifyDisplayChange(id, true);
// By default, non primary screen does mirroring.
if (aDisplayType != GonkDisplay::DISPLAY_PRIMARY &&
gfxPrefs::ScreenMirroringEnabled()) {
screen->EnableMirroring();
}
return NS_OK;
}
@ -645,6 +773,9 @@ nsScreenManagerGonk::RemoveScreen(GonkDisplay::DisplayType aDisplayType)
for (size_t i = 0; i < mScreens.Length(); i++) {
if (mScreens[i]->GetId() == screenId) {
if (mScreens[i]->IsMirroring()) {
mScreens[i]->DisableMirroring();
}
mScreens.RemoveElementAt(i);
break;
}

View File

@ -17,14 +17,16 @@
#ifndef nsScreenManagerGonk_h___
#define nsScreenManagerGonk_h___
#include "mozilla/Hal.h"
#include "cutils/properties.h"
#include "hardware/hwcomposer.h"
#include "libdisplay/GonkDisplay.h"
#include "mozilla/Hal.h"
#include "mozilla/Mutex.h"
#include "nsBaseScreen.h"
#include "nsCOMPtr.h"
#include "nsIScreenManager.h"
#include "nsProxyRelease.h"
#include <android/native_window.h>
@ -87,11 +89,25 @@ public:
return mTopWindows;
}
// Set EGL info of primary display. Used for BLIT Composition.
// Non-primary screen only
bool EnableMirroring();
bool DisableMirroring();
bool IsMirroring()
{
return mIsMirroring;
}
// Primary screen only
bool SetMirroringScreen(nsScreenGonk* aScreen);
bool ClearMirroringScreen(nsScreenGonk* aScreen);
// Called only on compositor thread
void SetEGLInfo(hwc_display_t aDisplay, hwc_surface_t aSurface,
mozilla::gl::GLContext* aGLContext);
hwc_display_t GetEGLDisplay();
hwc_surface_t GetEGLSurface();
void UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow); // Primary screen only
nsWindow* GetMirroringWidget(); // Primary screen only
protected:
uint32_t mId;
@ -107,12 +123,15 @@ protected:
#if ANDROID_VERSION >= 17
android::sp<android::DisplaySurface> mDisplaySurface;
#endif
bool mIsMirroring; // Non-primary screen only
nsRefPtr<nsScreenGonk> mMirroringScreen; // Primary screen only
// Accessed and updated only on compositor thread
GonkDisplay::DisplayType mDisplayType;
hwc_display_t mEGLDisplay;
hwc_surface_t mEGLSurface;
mozilla::gl::GLContext* mGLContext;
nsRefPtr<mozilla::gl::GLContext> mGLContext;
nsRefPtr<nsWindow> mMirroringWidget; // Primary screen only
};
class nsScreenManagerGonk final : public nsIScreenManager
@ -133,7 +152,7 @@ public:
void DisplayEnabled(bool aEnabled);
nsresult AddScreen(GonkDisplay::DisplayType aDisplayType,
android::IGraphicBufferProducer* aProducer = nullptr);
android::IGraphicBufferProducer* aSink = nullptr);
nsresult RemoveScreen(GonkDisplay::DisplayType aDisplayType);