author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 30 Dec 2015 12:00:03 +0100 | |
changeset 277884 | c690c50b2b543b420803e8192d6e08e06b20e0a3 |
parent 277883 | 115a945b9f8d7ae38ec089a1f20a4cb73adab4b0 (current diff) |
parent 277822 | 46da19b556726f08ff6e9e38b007f5f42a4f6aeb (diff) |
child 277885 | b493cf33851fc44f9fef8179bc88969f10df3ff6 |
child 277971 | 626d2a7e3a4af515f9c0b611c59fd5128f08328c |
child 277985 | 718d15422bb35afd886fc4350e487bef967fcf7e |
push id | 69628 |
push user | cbook@mozilla.com |
push date | Wed, 30 Dec 2015 11:16:09 +0000 |
treeherder | mozilla-inbound@b493cf33851f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 46.0a1 |
first release with | nightly linux32
c690c50b2b54
/
46.0a1
/
20151230030234
/
files
nightly linux64
c690c50b2b54
/
46.0a1
/
20151230030234
/
files
nightly mac
c690c50b2b54
/
46.0a1
/
20151230030234
/
files
nightly win32
c690c50b2b54
/
46.0a1
/
20151230030234
/
files
nightly win64
c690c50b2b54
/
46.0a1
/
20151230030234
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
46.0a1
/
20151230030234
/
pushlog to previous
nightly linux64
46.0a1
/
20151230030234
/
pushlog to previous
nightly mac
46.0a1
/
20151230030234
/
pushlog to previous
nightly win32
46.0a1
/
20151230030234
/
pushlog to previous
nightly win64
46.0a1
/
20151230030234
/
pushlog to previous
|
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -5532,42 +5532,50 @@ let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils); let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom; let canvas = this._dndCanvas ? this._dndCanvas : document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); canvas.mozOpaque = true; canvas.width = 160 * scale; canvas.height = 90 * scale; let toDrag; + let dragImageOffset = -16; if (gMultiProcessBrowser) { var context = canvas.getContext('2d'); context.fillStyle = "white"; context.fillRect(0, 0, canvas.width, canvas.height); // Create a panel to use it in setDragImage // which will tell xul to render a panel that follows // the pointer while a dnd session is on. if (!this._dndPanel) { this._dndCanvas = canvas; this._dndPanel = document.createElement("panel"); this._dndPanel.setAttribute("type", "drag"); - this._dndPanel.appendChild(canvas); + let wrapper = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); + wrapper.style.width = "160px"; + wrapper.style.height = "90px"; + wrapper.appendChild(canvas); + canvas.style.width = "100%"; + canvas.style.height = "100%"; + this._dndPanel.appendChild(wrapper); document.documentElement.appendChild(this._dndPanel); } // PageThumb is async with e10s but that's fine // since we can update the panel during the dnd. PageThumbs.captureToCanvas(browser, canvas); toDrag = this._dndPanel; } else { // For the non e10s case we can just use PageThumbs // sync. No need for xul magic, the native dnd will // be fine, so let's use the canvas for setDragImage. PageThumbs.captureToCanvas(browser, canvas); toDrag = canvas; + dragImageOffset = dragImageOffset * scale; } - dt.setDragImage(toDrag, -16 * scale, -16 * scale); + dt.setDragImage(toDrag, dragImageOffset, dragImageOffset); // _dragData.offsetX/Y give the coordinates that the mouse should be // positioned relative to the corner of the new window created upon // dragend such that the mouse appears to have the same position // relative to the corner of the dragged tab. function clientX(ele) { return ele.getBoundingClientRect().left; }
--- a/configure.in +++ b/configure.in @@ -529,16 +529,20 @@ case "$target" in # -Wv:18 disables all warnings introduced after VS2013 # See http://blogs.msdn.com/b/vcblog/archive/2014/11/12/improvements-to-warnings-in-the-c-compiler.aspx CFLAGS="$CFLAGS -Wv:18" CXXFLAGS="$CXXFLAGS -Wv:18" # -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146) CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-" + # Disable C++11 thread-safe statics due to crashes on XP (bug 1204752) + # See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics + CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-" + # https://connect.microsoft.com/VisualStudio/feedback/details/888527/warnings-on-dbghelp-h # for dbghelp.h, imagehlp.h, and shobj.h # C4091: 'typedef ': ignored on left of '' when no variable is declared CFLAGS="$CFLAGS -wd4091" CXXFLAGS="$CXXFLAGS -wd4091" else AC_MSG_ERROR([This version (${_CC_MAJOR_VERSION}.${_CC_MINOR_VERSION}.${_CC_BUILD_VERSION}) of the MSVC compiler is unsupported. You must install Visual C++ 2013 Update 3 or newer in order to build.
--- a/docshell/base/nsAboutRedirector.cpp +++ b/docshell/base/nsAboutRedirector.cpp @@ -95,20 +95,22 @@ static RedirEntry kRedirMap[] = { nsIAboutModule::URI_MUST_LOAD_IN_CHILD }, { "serviceworkers", "chrome://global/content/aboutServiceWorkers.xhtml", nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT }, +#ifndef ANDROID { "profiles", "chrome://global/content/aboutProfiles.xhtml", nsIAboutModule::ALLOW_SCRIPT }, +#endif // about:srcdoc is unresolvable by specification. It is included here // because the security manager would disallow srcdoc iframes otherwise. { "srcdoc", "about:blank", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::HIDE_FROM_ABOUTABOUT | nsIAboutModule::URI_CAN_LOAD_IN_CHILD },
--- a/docshell/build/nsDocShellModule.cpp +++ b/docshell/build/nsDocShellModule.cpp @@ -175,17 +175,19 @@ const mozilla::Module::ContractIDEntry k { NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, #ifdef NIGHTLY_BUILD { NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, #endif { NS_ABOUT_MODULE_CONTRACTID_PREFIX "plugins", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, +#ifndef ANDROID { NS_ABOUT_MODULE_CONTRACTID_PREFIX "profiles", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, +#endif { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID }, { NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID }, { NS_HANDLERSERVICE_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY }, { NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
--- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -215,16 +215,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN( NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedResolveResults) #ifdef MOZ_EME NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager) #endif NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDevicesPromises) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator) void Navigator::Invalidate() { @@ -355,16 +356,18 @@ Navigator::Invalidate() mMediaKeySystemAccessManager->Shutdown(); mMediaKeySystemAccessManager = nullptr; } #endif if (mDeviceStorageAreaListener) { mDeviceStorageAreaListener = nullptr; } + + mVRGetDevicesPromises.Clear(); } //***************************************************************************** // Navigator::nsIDOMNavigator //***************************************************************************** NS_IMETHODIMP Navigator::GetUserAgent(nsAString& aUserAgent) @@ -1934,26 +1937,45 @@ Navigator::GetVRDevices(ErrorResult& aRv } nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow); RefPtr<Promise> p = Promise::Create(go, aRv); if (aRv.Failed()) { return nullptr; } + // We pass ourself to RefreshVRDevices, so NotifyVRDevicesUpdated will + // be called asynchronously, resolving the promises in mVRGetDevicesPromises. + if (!VRDevice::RefreshVRDevices(this)) { + p->MaybeReject(NS_ERROR_FAILURE); + return p.forget(); + } + + mVRGetDevicesPromises.AppendElement(p); + return p.forget(); +} + +void +Navigator::NotifyVRDevicesUpdated() +{ + // Synchronize the VR devices and resolve the promises in + // mVRGetDevicesPromises nsGlobalWindow* win = static_cast<nsGlobalWindow*>(mWindow.get()); nsTArray<RefPtr<VRDevice>> vrDevs; - if (!win->GetVRDevices(vrDevs)) { - p->MaybeReject(NS_ERROR_FAILURE); + if (win->UpdateVRDevices(vrDevs)) { + for (auto p: mVRGetDevicesPromises) { + p->MaybeResolve(vrDevs); + } } else { - p->MaybeResolve(vrDevs); + for (auto p: mVRGetDevicesPromises) { + p->MaybeReject(NS_ERROR_FAILURE); + } } - - return p.forget(); + mVRGetDevicesPromises.Clear(); } //***************************************************************************** // Navigator::nsIMozNavigatorNetwork //***************************************************************************** NS_IMETHODIMP Navigator::GetProperties(nsINetworkProperties** aProperties)
--- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -263,16 +263,17 @@ public: #endif #ifdef MOZ_B2G_RIL MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv); #endif // MOZ_B2G_RIL #ifdef MOZ_GAMEPAD void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv); #endif // MOZ_GAMEPAD already_AddRefed<Promise> GetVRDevices(ErrorResult& aRv); + void NotifyVRDevicesUpdated(); #ifdef MOZ_B2G_FM FMRadio* GetMozFMRadio(ErrorResult& aRv); #endif #ifdef MOZ_B2G_BT bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv); #endif // MOZ_B2G_BT #ifdef MOZ_TIME_MANAGER time::TimeManager* GetMozTime(ErrorResult& aRv); @@ -409,14 +410,16 @@ private: RefPtr<Presentation> mPresentation; // Hashtable for saving cached objects DoResolve created, so we don't create // the object twice if asked for it twice, whether due to use of "delete" or // due to Xrays. We could probably use a nsJSThingHashtable here, but then // we'd need to figure out exactly how to trace that, and that seems to be // rocket science. :( nsInterfaceHashtable<nsStringHashKey, nsISupports> mCachedResolveResults; + + nsTArray<RefPtr<Promise> > mVRGetDevicesPromises; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_Navigator_h
--- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -2117,27 +2117,16 @@ public: mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY, EmptyCString()); } return true; } - bool Suspend(JSContext* aCx) override - { - { - MutexAutoLock lock(mWebSocketImpl->mMutex); - mWebSocketImpl->mWorkerShuttingDown = true; - } - - mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY); - return true; - } - private: WebSocketImpl* mWebSocketImpl; }; } // namespace void WebSocketImpl::AddRefObject()
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -11437,20 +11437,20 @@ nsresult nsDocument::RemoteFrameFullscre nsresult nsDocument::RemoteFrameFullscreenReverted() { RestorePreviousFullScreenState(); return NS_OK; } static void -ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *) +ReleaseVRDeviceProxyRef(void *, nsIAtom*, void *aPropertyValue, void *) { if (aPropertyValue) { - static_cast<gfx::VRHMDInfo*>(aPropertyValue)->Release(); + static_cast<gfx::VRDeviceProxy*>(aPropertyValue)->Release(); } } bool nsDocument::FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome) { NS_ASSERTION(aElement, @@ -11759,19 +11759,19 @@ nsDocument::ApplyFullscreen(const Fullsc nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this); // If a document is already in fullscreen, then unlock the mouse pointer // before setting a new document to fullscreen UnlockPointer(); // Process options -- in this case, just HMD if (aRequest.mVRHMDDevice) { - RefPtr<gfx::VRHMDInfo> hmdRef = aRequest.mVRHMDDevice; + RefPtr<gfx::VRDeviceProxy> hmdRef = aRequest.mVRHMDDevice; elem->SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(), - ReleaseHMDInfoRef, true); + ReleaseVRDeviceProxyRef, true); } // Set the full-screen element. This sets the full-screen style on the // element, and the full-screen-ancestor styles on ancestors of the element // in this document. DebugOnly<bool> x = FullScreenStackPush(elem); NS_ASSERTION(x, "Full-screen state of requesting doc should always change!"); // Set the iframe fullscreen flag.
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -106,17 +106,17 @@ struct FullscreenRequest : public Linked Element* GetElement() const { return mElement; } nsDocument* GetDocument() const { return mDocument; } private: RefPtr<Element> mElement; RefPtr<nsDocument> mDocument; public: - RefPtr<gfx::VRHMDInfo> mVRHMDDevice; + RefPtr<gfx::VRDeviceProxy> mVRHMDDevice; // This value should be true if the fullscreen request is // originated from chrome code. bool mIsCallerChrome = false; // This value denotes whether we should trigger a NewOrigin event if // requesting fullscreen in its document causes the origin which is // fullscreen to change. We may want *not* to trigger that event if // we're calling RequestFullScreen() as part of a continuation of a // request in a subdocument in different process, whereupon the caller
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1172,18 +1172,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW #endif #ifdef MOZ_B2G mNetworkUploadObserverEnabled(false), mNetworkDownloadObserverEnabled(false), #endif mCleanedUp(false), mDialogAbuseCount(0), mAreDialogsEnabled(true), - mCanSkipCCGeneration(0), - mVRDevicesInitialized(false) + mCanSkipCCGeneration(0) { AssertIsOnMainThread(); nsLayoutStatics::AddRef(); // Initialize the PRCList (this). PR_INIT_CLIST(this); @@ -4277,17 +4276,17 @@ nsGlobalWindow::IsShowModalDialogEnabled static bool sIsDisabled; static const char sShowModalDialogPref[] = "dom.disable_window_showModalDialog"; if (!sAddedPrefCache) { Preferences::AddBoolVarCache(&sIsDisabled, sShowModalDialogPref, false); sAddedPrefCache = true; } - return !sIsDisabled; + return !sIsDisabled && !XRE_IsContentProcess(); } nsIDOMOfflineResourceList* nsGlobalWindow::GetApplicationCache(ErrorResult& aError) { MOZ_RELEASE_ASSERT(IsInnerWindow()); if (!mApplicationCache) { @@ -5959,17 +5958,17 @@ FullscreenTransitionTask::Observer::Obse obs->RemoveObserver(this, kPaintedTopic); mTask->mTimer = nullptr; mTask->Run(); } return NS_OK; } static bool -MakeWidgetFullscreen(nsGlobalWindow* aWindow, gfx::VRHMDInfo* aHMD, +MakeWidgetFullscreen(nsGlobalWindow* aWindow, gfx::VRDeviceProxy* aHMD, nsPIDOMWindow::FullscreenReason aReason, bool aFullscreen) { nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget(); if (!widget) { return false; } FullscreenTransitionDuration duration; @@ -5992,17 +5991,17 @@ MakeWidgetFullscreen(nsGlobalWindow* aWi task->Run(); } return true; } nsresult nsGlobalWindow::SetFullscreenInternal(FullscreenReason aReason, bool aFullScreen, - gfx::VRHMDInfo* aHMD) + gfx::VRDeviceProxy* aHMD) { MOZ_ASSERT(IsOuterWindow()); MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(), "Requires safe to run script as it " "may call FinishDOMFullscreenChange"); NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); @@ -8785,17 +8784,17 @@ nsGlobalWindow::ShowModalDialogOuter(con const nsAString& aOptions, ErrorResult& aError) { MOZ_RELEASE_ASSERT(IsOuterWindow()); if (mDoc) { mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog); } - if (!IsShowModalDialogEnabled() || XRE_IsContentProcess()) { + if (!IsShowModalDialogEnabled()) { aError.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } Telemetry::Accumulate(Telemetry::DOM_WINDOW_SHOWMODALDIALOG_USED, true); RefPtr<DialogValueHolder> argHolder = new DialogValueHolder(nsContentUtils::SubjectPrincipal(), aArgument); @@ -12936,29 +12935,21 @@ nsGlobalWindow::SyncGamepadState() for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) { gamepadsvc->SyncGamepadState(iter.Key(), iter.UserData()); } } } #endif // MOZ_GAMEPAD bool -nsGlobalWindow::GetVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices) -{ - FORWARD_TO_INNER(GetVRDevices, (aDevices), false); - - if (!mVRDevicesInitialized) { - bool ok = mozilla::dom::VRDevice::CreateAllKnownVRDevices(ToSupports(this), mVRDevices); - if (!ok) { - mVRDevices.Clear(); - return false; - } - } - - mVRDevicesInitialized = true; +nsGlobalWindow::UpdateVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices) +{ + FORWARD_TO_INNER(UpdateVRDevices, (aDevices), false); + + VRDevice::UpdateVRDevices(mVRDevices, ToSupports(this)); aDevices = mVRDevices; return true; } // nsGlobalChromeWindow implementation NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -124,17 +124,17 @@ class WindowOrientationObserver; namespace cache { class CacheStorage; } // namespace cache namespace indexedDB { class IDBFactory; } // namespace indexedDB } // namespace dom namespace gfx { -class VRHMDInfo; +class VRDeviceProxy; } // namespace gfx } // namespace mozilla extern already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow, mozilla::dom::Function& aFunction, const mozilla::dom::Sequence<JS::Value>& aArguments, mozilla::ErrorResult& aError); @@ -466,17 +466,17 @@ public: virtual void RefreshCompartmentPrincipal() override; // For accessing protected field mFullScreen friend class FullscreenTransitionTask; // Outer windows only. virtual nsresult SetFullscreenInternal( FullscreenReason aReason, bool aIsFullscreen, - mozilla::gfx::VRHMDInfo *aHMD = nullptr) override final; + mozilla::gfx::VRDeviceProxy *aHMD = nullptr) override final; virtual void FinishFullscreenChange(bool aIsFullscreen) override final; void SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen, nsIWidget* aWidget, nsIScreen* aScreen); bool FullScreen() const; // Inner windows only. virtual void SetHasGamepadEventListener(bool aHasGamepad = true) override; @@ -755,18 +755,18 @@ public: void SyncGamepadState(); #endif // Inner windows only. // Enable/disable updates for gamepad input. void EnableGamepadUpdates(); void DisableGamepadUpdates(); - // Get the VR devices for this window, initializing if necessary - bool GetVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices); + // Update the VR devices for this window + bool UpdateVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices); #define EVENT(name_, id_, type_, struct_) \ mozilla::dom::EventHandlerNonNull* GetOn##name_() \ { \ mozilla::EventListenerManager* elm = GetExistingListenerManager(); \ return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString()) \ : nullptr; \ } \ @@ -1843,22 +1843,18 @@ protected: #ifdef MOZ_WEBSPEECH // mSpeechSynthesis is only used on inner windows. RefPtr<mozilla::dom::SpeechSynthesis> mSpeechSynthesis; #endif // This is the CC generation the last time we called CanSkip. uint32_t mCanSkipCCGeneration; - // Did VR get initialized for this window? - bool mVRDevicesInitialized; // The VRDevies for this window nsTArray<RefPtr<mozilla::dom::VRDevice>> mVRDevices; - // Any attached HMD when fullscreen - RefPtr<mozilla::gfx::VRHMDInfo> mVRHMDInfo; friend class nsDOMScriptableHelper; friend class nsDOMWindowUtils; friend class mozilla::dom::PostMessageEvent; friend class DesktopNotification; static WindowByIdTable* sWindowsById; static bool sWarnedAboutWindowInternal;
--- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -97,20 +97,16 @@ class SVGAttrAnimationRuleProcessor; template<typename> class OwningNonNull; namespace css { class Loader; class ImageLoader; class Rule; } // namespace css -namespace gfx { -class VRHMDInfo; -} // namespace gfx - namespace dom { class AnonymousContent; class Attr; class BoxObject; class CDATASection; class Comment; struct CustomElementDefinition; class DocumentFragment;
--- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -36,17 +36,17 @@ struct nsTimeout; namespace mozilla { namespace dom { class AudioContext; class Element; class ServiceWorkerRegistrationMainThread; } // namespace dom namespace gfx { -class VRHMDInfo; +class VRDeviceProxy; } // namespace gfx } // namespace mozilla // Popup control state enum. The values in this enum must go from most // permissive to least permissive so that it's safe to push state in // all situations. Pushing popup state onto the stack never makes the // current popup state less permissive (see // nsGlobalWindow::PushPopupControlState()). @@ -506,17 +506,17 @@ public: * * If aHMD is not null, the window is made full screen on the given VR HMD * device instead of its currrent display. * * Outer windows only. */ virtual nsresult SetFullscreenInternal( FullscreenReason aReason, bool aIsFullscreen, - mozilla::gfx::VRHMDInfo *aHMD = nullptr) = 0; + mozilla::gfx::VRDeviceProxy *aHMD = nullptr) = 0; /** * This function should be called when the fullscreen state is flipped. * If no widget is involved the fullscreen change, this method is called * by SetFullscreenInternal, otherwise, it is called when the widget * finishes its change to or from fullscreen. * * @param aIsFullscreen indicates whether the widget is in fullscreen.
--- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -1306,17 +1306,17 @@ public: uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE; if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE) flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; if (!mPixelStore_PremultiplyAlpha) flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA; - gfx::DrawTarget* idealDrawTarget = nullptr; // Don't care for now. + RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr; // Don't care for now. return nsLayoutUtils::SurfaceFromElement(elem, flags, idealDrawTarget); } protected: // Returns false if `object` is null or not valid. template<class ObjectType> bool ValidateObject(const char* info, ObjectType* object);
--- a/dom/events/Touch.cpp +++ b/dom/events/Touch.cpp @@ -2,25 +2,47 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/dom/Touch.h" #include "mozilla/dom/EventTarget.h" -#include "mozilla/dom/TouchBinding.h" #include "mozilla/dom/TouchEvent.h" #include "nsGlobalWindow.h" #include "nsContentUtils.h" #include "nsIContent.h" namespace mozilla { namespace dom { +// static +already_AddRefed<Touch> +Touch::Constructor(const GlobalObject& aGlobal, + const TouchInit& aParam, + ErrorResult& aRv) +{ + // Annoyingly many parameters, make sure the ordering is the same as in the + // Touch constructor. + RefPtr<Touch> touch = new Touch(aParam.mTarget, + aParam.mIdentifier, + aParam.mPageX, + aParam.mPageY, + aParam.mScreenX, + aParam.mScreenY, + aParam.mClientX, + aParam.mClientY, + aParam.mRadiusX, + aParam.mRadiusY, + aParam.mRotationAngle, + aParam.mForce); + return touch.forget(); +} + Touch::Touch(EventTarget* aTarget, int32_t aIdentifier, int32_t aPageX, int32_t aPageY, int32_t aScreenX, int32_t aScreenY, int32_t aClientX, int32_t aClientY,
--- a/dom/events/Touch.h +++ b/dom/events/Touch.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_Touch_h_ #define mozilla_dom_Touch_h_ #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/MouseEvents.h" +#include "mozilla/dom/TouchBinding.h" #include "nsWrapperCache.h" #include "nsAutoPtr.h" #include "Units.h" class nsPresContext; namespace mozilla { namespace dom { @@ -23,16 +24,20 @@ class EventTarget; class Touch final : public nsISupports , public nsWrapperCache , public WidgetPointerHelper { public: static bool PrefEnabled(JSContext* aCx, JSObject* aGlobal); + static already_AddRefed<Touch> Constructor(const GlobalObject& aGlobal, + const TouchInit& aParam, + ErrorResult& aRv); + Touch(EventTarget* aTarget, int32_t aIdentifier, int32_t aPageX, int32_t aPageY, int32_t aScreenX, int32_t aScreenY, int32_t aClientX, int32_t aClientY,
--- a/dom/events/TouchEvent.cpp +++ b/dom/events/TouchEvent.cpp @@ -202,16 +202,49 @@ TouchEvent::PrefEnabled(JSContext* aCx, } } if (prefValue) { nsContentUtils::InitializeTouchEventTable(); } return prefValue; } +// static +already_AddRefed<Event> +TouchEvent::Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const TouchEventInit& aParam, + ErrorResult& aRv) +{ + nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); + RefPtr<TouchEvent> e = new TouchEvent(t, nullptr, nullptr); + bool trusted = e->Init(t); + RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches); + RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches); + RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches); + e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, + aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey, + aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches, + changedTouches); + e->SetTrusted(trusted); + return e.forget(); +} + + +already_AddRefed<TouchList> +TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches) +{ + RefPtr<TouchList> list = new TouchList(GetParentObject()); + size_t len = aTouches.Length(); + for (size_t i = 0; i < len; ++i) { + list->Append(aTouches[i]); + } + return list.forget(); +} + bool TouchEvent::AltKey() { return mEvent->AsTouchEvent()->IsAlt(); } bool TouchEvent::MetaKey()
--- a/dom/events/TouchEvent.h +++ b/dom/events/TouchEvent.h @@ -90,16 +90,19 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TouchEvent, UIEvent) virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override { return TouchEventBinding::Wrap(aCx, this, aGivenProto); } + already_AddRefed<TouchList> + CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches); + TouchList* Touches(); TouchList* TargetTouches(); TouchList* ChangedTouches(); bool AltKey(); bool MetaKey(); bool CtrlKey(); bool ShiftKey(); @@ -115,16 +118,21 @@ public: bool aMetaKey, TouchList* aTouches, TouchList* aTargetTouches, TouchList* aChangedTouches); static bool PrefEnabled(JSContext* aCx = nullptr, JSObject* aGlobal = nullptr); + static already_AddRefed<Event> Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const TouchEventInit& aParam, + ErrorResult& aRv); + protected: ~TouchEvent() {} RefPtr<TouchList> mTouches; RefPtr<TouchList> mTargetTouches; RefPtr<TouchList> mChangedTouches; };
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3168,27 +3168,17 @@ void HTMLMediaElement::UpdateSrcMediaStr stream->AddListener(mMediaStreamSizeListener); mWatchManager.Watch(*mMediaStreamListener, &HTMLMediaElement::UpdateReadyStateInternal); stream->AddAudioOutput(this); SetVolumeInternal(); - #ifdef MOZ_WIDGET_GONK - bool bUseOverlayImage = mSrcStream->AsDOMHwMediaStream() != nullptr; - #else - bool bUseOverlayImage = false; - #endif - VideoFrameContainer* container; - if (bUseOverlayImage) { - container = GetOverlayImageVideoFrameContainer(); - } else { - container = GetVideoFrameContainer(); - } + VideoFrameContainer* container = GetVideoFrameContainer(); if (container) { stream->AddVideoOutput(container); } } else { if (stream) { mSrcStreamPausedCurrentTime = CurrentTime(); stream->RemoveListener(mMediaStreamListener); @@ -4025,32 +4015,16 @@ VideoFrameContainer* HTMLMediaElement::G } mVideoFrameContainer = new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS)); return mVideoFrameContainer; } -VideoFrameContainer* HTMLMediaElement::GetOverlayImageVideoFrameContainer() -{ - if (mVideoFrameContainer) - return mVideoFrameContainer; - - // Only video frames need an image container. - if (!IsVideo()) { - return nullptr; - } - - mVideoFrameContainer = - new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY)); - - return mVideoFrameContainer; -} - nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName) { LOG_EVENT(LogLevel::Debug, ("%p Dispatching event %s", this, NS_ConvertUTF16toUTF8(aName).get())); // Save events that occur while in the bfcache. These will be dispatched // if the page comes out of the bfcache. if (mEventDeliveryPaused) { @@ -4377,16 +4351,19 @@ void HTMLMediaElement::NotifyAddedSource mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) { QueueSelectResourceTask(); } // A load was paused in the resource selection algorithm, waiting for // a new source child to be added, resume the resource selection algorithm. if (mLoadWaitStatus == WAITING_FOR_SOURCE) { + // Rest the flag so we don't queue multiple LoadFromSourceTask() when + // multiple <source> are attached in an event loop. + mLoadWaitStatus = NOT_WAITING; QueueLoadFromSourceTask(); } } nsIContent* HTMLMediaElement::GetNextSource() { nsCOMPtr<nsIDOMNode> thisDomNode = do_QueryObject(this);
--- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -213,19 +213,16 @@ public: // Called by the media decoder to indicate whether the media cache has // suspended the channel. virtual void NotifySuspendedByCache(bool aIsSuspended) final override; virtual bool IsActive() const final override; virtual bool IsHidden() const final override; - // In order to create overlayImageContainer to support DOMHwMediaStream. - VideoFrameContainer* GetOverlayImageVideoFrameContainer(); - // Called by the media decoder and the video frame to get the // ImageContainer containing the video data. B2G_ACL_EXPORT virtual VideoFrameContainer* GetVideoFrameContainer() final override; layers::ImageContainer* GetImageContainer(); // Dispatch events virtual nsresult DispatchAsyncEvent(const nsAString& aName) final override;
--- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -597,8 +597,9 @@ skip-if = buildapp == 'b2g' # bug 112901 [test_img_complete.html] [test_viewport_resize.html] [test_extapp.html] [test_image_clone_load.html] [test_bug1203668.html] [test_bug1166138.html] [test_filepicker_default_directory.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' +[test_bug1233598.html]
new file mode 100644 --- /dev/null +++ b/dom/html/test/test_bug1233598.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1233598 +--> +<head> + <title>Test for Bug 1233598</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233598">Mozilla Bug 1233598</a> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1233598 **/ + +var i; // must be out here to trigger the leak + +function runTest() +{ + i = document.createElement("input"); + i.setAttribute("type", "file"); + i.getFilesAndDirectories(); // returns a promise + ok(true, "Are we leaking?"); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({"set":[["dom.input.dirpicker", true]]}, runTest); + +</script> +</pre> +</body> +</html>
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -194,16 +194,17 @@ #include "mozilla/dom/voicemail/VoicemailIPCService.h" #include "mozilla/net/NeckoMessageUtils.h" #include "mozilla/widget/PuppetBidiKeyboard.h" #include "mozilla/RemoteSpellCheckEngineChild.h" #include "GMPServiceChild.h" #include "GMPDecoderModule.h" #include "gfxPlatform.h" #include "nscore.h" // for NS_FREE_PERMANENT_DATA +#include "VRManagerChild.h" using namespace mozilla; using namespace mozilla::docshell; using namespace mozilla::dom::bluetooth; using namespace mozilla::dom::cellbroadcast; using namespace mozilla::dom::devicestorage; using namespace mozilla::dom::icc; using namespace mozilla::dom::ipc; @@ -1255,16 +1256,23 @@ ContentChild::AllocPSharedBufferManagerC PImageBridgeChild* ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess); } +gfx::PVRManagerChild* +ContentChild::AllocPVRManagerChild(Transport* aTransport, + ProcessId aOtherProcess) +{ + return gfx::VRManagerChild::StartUpInChildProcess(aTransport, aOtherProcess); +} + PBackgroundChild* ContentChild::AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess) { return BackgroundChild::Alloc(aTransport, aOtherProcess); } PProcessHangMonitorChild*
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -143,16 +143,20 @@ public: PImageBridgeChild* AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) override; PProcessHangMonitorChild* AllocPProcessHangMonitorChild(Transport* aTransport, ProcessId aOtherProcess) override; + PVRManagerChild* + AllocPVRManagerChild(Transport* aTransport, + ProcessId aOtherProcess) override; + virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override; PBackgroundChild* AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess) override; virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId, const IPCTabContext& aContext,
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -263,16 +263,18 @@ using namespace mozilla::system; #include "nsIProfiler.h" #include "nsIProfileSaveEvent.h" #endif #ifdef MOZ_GAMEPAD #include "mozilla/dom/GamepadMonitoring.h" #endif +#include "VRManagerParent.h" // for VRManagerParent + static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); #if defined(XP_WIN) // e10s forced enable pref, defined in nsAppRunner.cpp extern const char* kForceEnableE10sPref; #endif using base::ChildPrivileges; @@ -2634,16 +2636,19 @@ ContentParent::InitInternal(ProcessPrior // on demand.) bool useOffMainThreadCompositing = !!CompositorParent::CompositorLoop(); if (useOffMainThreadCompositing) { DebugOnly<bool> opened = PCompositor::Open(this); MOZ_ASSERT(opened); opened = PImageBridge::Open(this); MOZ_ASSERT(opened); + + opened = gfx::PVRManager::Open(this); + MOZ_ASSERT(opened); } #ifdef MOZ_WIDGET_GONK DebugOnly<bool> opened = PSharedBufferManager::Open(this); MOZ_ASSERT(opened); #endif } if (gAppData) { @@ -3406,16 +3411,23 @@ ContentParent::AllocPGMPServiceParent(mo PCompositorParent* ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return CompositorParent::Create(aTransport, aOtherProcess); } +gfx::PVRManagerParent* +ContentParent::AllocPVRManagerParent(Transport* aTransport, + ProcessId aOtherProcess) +{ + return gfx::VRManagerParent::CreateCrossProcess(aTransport, aOtherProcess); +} + PImageBridgeParent* ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return ImageBridgeParent::Create(aTransport, aOtherProcess); } PBackgroundParent*
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -607,16 +607,20 @@ private: PBackgroundParent* AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess) override; PProcessHangMonitorParent* AllocPProcessHangMonitorParent(Transport* aTransport, ProcessId aOtherProcess) override; + PVRManagerParent* + AllocPVRManagerParent(Transport* aTransport, + ProcessId aOtherProcess) override; + virtual bool RecvGetProcessAttributes(ContentParentId* aCpId, bool* aIsForApp, bool* aIsForBrowser) override; virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline, bool* aIsConnected, bool* aIsLangRTL, InfallibleTArray<nsString>* dictionaries, ClipboardCapabilities* clipboardCaps,
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -49,16 +49,17 @@ include protocol PStorage; include protocol PTelephony; include protocol PTestShell; include protocol PVoicemail; include protocol PJavaScript; include protocol PRemoteSpellcheckEngine; include protocol PWebBrowserPersistDocument; include protocol PWebrtcGlobal; include protocol PPresentation; +include protocol PVRManager; include DOMTypes; include JavaScriptTypes; include InputStreamParams; include PTabContext; include URIParams; include PluginTypes; include ProtocolTypes; include PBackgroundSharedTypes; @@ -446,16 +447,17 @@ prio(normal upto urgent) sync protocol P { parent spawns PPluginModule; parent opens PCompositor; parent opens PProcessHangMonitor; parent opens PSharedBufferManager; parent opens PImageBridge; parent opens PGMPService; + parent opens PVRManager; child opens PBackground; manages PBlob; manages PBluetooth; manages PBrowser; manages PCellBroadcast; manages PContentPermissionRequest; manages PCrashReporter;
--- a/dom/media/DOMMediaStream.cpp +++ b/dom/media/DOMMediaStream.cpp @@ -1038,17 +1038,17 @@ DOMAudioNodeMediaStream::CreateTrackUnio RefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aNode); stream->InitTrackUnionStream(aWindow, aGraph); return stream.forget(); } DOMHwMediaStream::DOMHwMediaStream() { #ifdef MOZ_WIDGET_GONK - mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY); + mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS); mOverlayImage = mImageContainer->CreateOverlayImage(); nsAutoTArray<ImageContainer::NonOwningImage,1> images; images.AppendElement(ImageContainer::NonOwningImage(mOverlayImage)); mImageContainer->SetCurrentImages(images); #endif } DOMHwMediaStream::~DOMHwMediaStream()
--- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -2567,16 +2567,17 @@ ProcessedMediaStream::AllocateInputPort( } virtual void RunDuringShutdown() { Run(); } RefPtr<MediaInputPort> mPort; }; + MOZ_ASSERT(aStream->GraphImpl() == GraphImpl()); MOZ_ASSERT(aTrackID != TRACK_NONE && aTrackID != TRACK_INVALID, "Only TRACK_ANY and explicit ID are allowed"); RefPtr<MediaInputPort> port = new MediaInputPort(aStream, aTrackID, this, aInputNumber, aOutputNumber); port->SetGraphImpl(GraphImpl()); GraphImpl()->AppendMessage(new Message(port)); return port.forget(); }
--- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -360,37 +360,33 @@ AudioContext::CreateMediaElementSource(H return nullptr; } RefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv, mDestination->Stream()->Graph()); if (aRv.Failed()) { return nullptr; } - RefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode = - new MediaElementAudioSourceNode(this, stream); - return mediaElementAudioSourceNode.forget(); + return MediaElementAudioSourceNode::Create(this, stream, aRv); } already_AddRefed<MediaStreamAudioSourceNode> AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream, ErrorResult& aRv) { if (mIsOffline) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } if (CheckClosed(aRv)) { return nullptr; } - RefPtr<MediaStreamAudioSourceNode> mediaStreamAudioSourceNode = - new MediaStreamAudioSourceNode(this, &aMediaStream); - return mediaStreamAudioSourceNode.forget(); + return MediaStreamAudioSourceNode::Create(this, &aMediaStream, aRv); } already_AddRefed<GainNode> AudioContext::CreateGain(ErrorResult& aRv) { if (CheckClosed(aRv)) { return nullptr; }
--- a/dom/media/webaudio/AudioDestinationNode.h +++ b/dom/media/webaudio/AudioDestinationNode.h @@ -56,17 +56,16 @@ public: void Suspend(); void Resume(); void StartRendering(Promise* aPromise); void OfflineShutdown(); AudioChannel MozAudioChannelType() const; - void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv); virtual void NotifyMainThreadStreamFinished() override; void FireOfflineCompletionEvent(); // An amount that should be added to the MediaStream's current time to // get the AudioContext.currentTime. StreamTime ExtraCurrentTime(); @@ -86,16 +85,17 @@ public: void InputMuted(bool aInputMuted); void ResolvePromise(AudioBuffer* aRenderedBuffer); protected: virtual ~AudioDestinationNode(); private: + void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv); bool CheckAudioChannelPermissions(AudioChannel aValue); void SetCanPlay(float aVolume, bool aMuted); void NotifyStableState(); void ScheduleStableStateNotification(); SelfReference<AudioDestinationNode> mOfflineRenderingRef;
--- a/dom/media/webaudio/AudioNode.cpp +++ b/dom/media/webaudio/AudioNode.cpp @@ -49,34 +49,29 @@ AudioNode::AudioNode(AudioContext* aCont ChannelInterpretation aChannelInterpretation) : DOMEventTargetHelper(aContext->GetParentObject()) , mContext(aContext) , mChannelCount(aChannelCount) , mChannelCountMode(aChannelCountMode) , mChannelInterpretation(aChannelInterpretation) , mId(gId++) , mPassThrough(false) -#ifdef DEBUG - , mDemiseNotified(false) -#endif { MOZ_ASSERT(aContext); DOMEventTargetHelper::BindToOwner(aContext->GetParentObject()); aContext->RegisterNode(this); } AudioNode::~AudioNode() { MOZ_ASSERT(mInputNodes.IsEmpty()); MOZ_ASSERT(mOutputNodes.IsEmpty()); MOZ_ASSERT(mOutputParams.IsEmpty()); -#ifdef DEBUG - MOZ_ASSERT(mDemiseNotified, + MOZ_ASSERT(!mStream, "The webaudio-node-demise notification must have been sent"); -#endif if (mContext) { mContext->UnregisterNode(this); } } size_t AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { @@ -385,19 +380,16 @@ AudioNode::DestroyMediaStream() mStream = nullptr; nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (obs) { nsAutoString id; id.AppendPrintf("%u", mId); obs->NotifyObservers(nullptr, "webaudio-node-demise", id.get()); } -#ifdef DEBUG - mDemiseNotified = true; -#endif } } void AudioNode::RemoveOutputParam(AudioParam* aParam) { mOutputParams.RemoveElement(aParam); }
--- a/dom/media/webaudio/AudioNode.h +++ b/dom/media/webaudio/AudioNode.h @@ -251,19 +251,14 @@ private: nsTArray<RefPtr<AudioParam> > mOutputParams; uint32_t mChannelCount; ChannelCountMode mChannelCountMode; ChannelInterpretation mChannelInterpretation; const uint32_t mId; // Whether the node just passes through its input. This is a devtools API that // only works for some node types. bool mPassThrough; -#ifdef DEBUG - // In debug builds, check to make sure that the node demise notification has - // been properly sent before the node is destroyed. - bool mDemiseNotified; -#endif }; } // namespace dom } // namespace mozilla #endif
--- a/dom/media/webaudio/MediaElementAudioSourceNode.cpp +++ b/dom/media/webaudio/MediaElementAudioSourceNode.cpp @@ -5,20 +5,34 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MediaElementAudioSourceNode.h" #include "mozilla/dom/MediaElementAudioSourceNodeBinding.h" namespace mozilla { namespace dom { -MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext, - DOMMediaStream* aStream) - : MediaStreamAudioSourceNode(aContext, aStream) +MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext) + : MediaStreamAudioSourceNode(aContext) +{ +} + +/* static */ already_AddRefed<MediaElementAudioSourceNode> +MediaElementAudioSourceNode::Create(AudioContext* aContext, + DOMMediaStream* aStream, ErrorResult& aRv) { + RefPtr<MediaElementAudioSourceNode> node = + new MediaElementAudioSourceNode(aContext); + + node->Init(aStream, aRv); + if (aRv.Failed()) { + return nullptr; + } + + return node.forget(); } JSObject* MediaElementAudioSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return MediaElementAudioSourceNodeBinding::Wrap(aCx, this, aGivenProto); }
--- a/dom/media/webaudio/MediaElementAudioSourceNode.h +++ b/dom/media/webaudio/MediaElementAudioSourceNode.h @@ -10,28 +10,30 @@ #include "MediaStreamAudioSourceNode.h" namespace mozilla { namespace dom { class MediaElementAudioSourceNode final : public MediaStreamAudioSourceNode { public: - MediaElementAudioSourceNode(AudioContext* aContext, - DOMMediaStream* aStream); + static already_AddRefed<MediaElementAudioSourceNode> + Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual const char* NodeType() const override { return "MediaElementAudioSourceNode"; } virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } +private: + explicit MediaElementAudioSourceNode(AudioContext* aContext); }; } // namespace dom } // namespace mozilla #endif
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp +++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp @@ -26,28 +26,55 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode) NS_INTERFACE_MAP_END_INHERITING(AudioNode) NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode) NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode) -MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext, - DOMMediaStream* aMediaStream) +MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext) : AudioNode(aContext, 2, ChannelCountMode::Max, - ChannelInterpretation::Speakers), - mInputStream(aMediaStream) + ChannelInterpretation::Speakers) +{ +} + +/* static */ already_AddRefed<MediaStreamAudioSourceNode> +MediaStreamAudioSourceNode::Create(AudioContext* aContext, + DOMMediaStream* aStream, ErrorResult& aRv) { + RefPtr<MediaStreamAudioSourceNode> node = + new MediaStreamAudioSourceNode(aContext); + + node->Init(aStream, aRv); + if (aRv.Failed()) { + return nullptr; + } + + return node.forget(); +} + +void +MediaStreamAudioSourceNode::Init(DOMMediaStream* aMediaStream, ErrorResult& aRv) +{ + MOZ_ASSERT(aMediaStream); + MediaStream* inputStream = aMediaStream->GetPlaybackStream(); + MediaStreamGraph* graph = Context()->Graph(); + if (NS_WARN_IF(graph != inputStream->Graph())) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + + mInputStream = aMediaStream; AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this); - mStream = AudioNodeExternalInputStream::Create(aContext->Graph(), engine); + mStream = AudioNodeExternalInputStream::Create(graph, engine); ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get()); - mInputPort = outputStream->AllocateInputPort(aMediaStream->GetPlaybackStream()); + mInputPort = outputStream->AllocateInputPort(inputStream); mInputStream->AddConsumerToKeepAlive(static_cast<nsIDOMEventTarget*>(this)); PrincipalChanged(mInputStream); // trigger enabling/disabling of the connector mInputStream->AddPrincipalChangeObserver(this); } MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() {
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h +++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h @@ -39,18 +39,18 @@ public: private: bool mEnabled; }; class MediaStreamAudioSourceNode : public AudioNode, public DOMMediaStream::PrincipalChangeObserver { public: - MediaStreamAudioSourceNode(AudioContext* aContext, - DOMMediaStream* aMediaStream); + static already_AddRefed<MediaStreamAudioSourceNode> + Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode) virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual void DestroyMediaStream() override; @@ -62,16 +62,18 @@ public: } virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override; virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override; virtual void PrincipalChanged(DOMMediaStream* aMediaStream) override; protected: + explicit MediaStreamAudioSourceNode(AudioContext* aContext); + void Init(DOMMediaStream* aMediaStream, ErrorResult& aRv); virtual ~MediaStreamAudioSourceNode(); private: RefPtr<MediaInputPort> mInputPort; RefPtr<DOMMediaStream> mInputStream; }; } // namespace dom
--- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -2421,27 +2421,31 @@ nsPluginHost::FindPluginsInContent(bool nsTArray<PluginTag> plugins; uint32_t parentEpoch; if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &parentEpoch) || NS_FAILED(rv)) { return NS_ERROR_NOT_AVAILABLE; } if (parentEpoch != ChromeEpochForContent()) { - SetChromeEpochForContent(parentEpoch); *aPluginsChanged = true; if (!aCreatePluginList) { return NS_OK; } + // Don't do this if aCreatePluginList is false. Otherwise, when we actually + // want to create the list, we'll come back here and do nothing. + SetChromeEpochForContent(parentEpoch); + for (size_t i = 0; i < plugins.Length(); i++) { PluginTag& tag = plugins[i]; // Don't add the same plugin again. - if (PluginWithId(tag.id())) { + if (nsPluginTag* existing = PluginWithId(tag.id())) { + UpdateInMemoryPluginInfo(existing); continue; } nsPluginTag *pluginTag = new nsPluginTag(tag.id(), tag.name().get(), tag.description().get(), tag.filename().get(), "", // aFullPath @@ -2694,24 +2698,19 @@ nsPluginHost::FindPluginsForContent(uint tag->FileName(), tag->Version(), tag->mLastModifiedTime, tag->IsFromExtension())); } return NS_OK; } -// This function is not relevant for fake plugins. void -nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) +nsPluginHost::UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag) { - MOZ_ASSERT(XRE_IsParentProcess()); - - ReadPluginInfo(); - WritePluginInfo(); NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext); NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext); if (!aPluginTag) { return; } // Update types with category manager @@ -2732,16 +2731,30 @@ nsPluginHost::UpdatePluginInfo(nsPluginT } nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService(); if (obsService) obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr); } +// This function is not relevant for fake plugins. +void +nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + + ReadPluginInfo(); + WritePluginInfo(); + + IncrementChromeEpoch(); + + UpdateInMemoryPluginInfo(aPluginTag); +} + /* static */ bool nsPluginHost::IsTypeWhitelisted(const char *aMimeType) { nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist); if (!whitelist.Length()) { return true; } nsDependentCString wrap(aMimeType);
--- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -358,16 +358,18 @@ private: // To be used by the chrome process; returns the current epoch. uint32_t ChromeEpoch(); // To be used by the content process to get/set the last observed epoch value // from the chrome process. uint32_t ChromeEpochForContent(); void SetChromeEpochForContent(uint32_t aEpoch); + void UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag); + // On certain platforms, we only want to load certain plugins. This function // centralizes loading rules. bool ShouldAddPlugin(nsPluginTag* aPluginTag); RefPtr<nsPluginTag> mPlugins; RefPtr<nsPluginTag> mCachedPlugins; RefPtr<nsInvalidPluginTag> mInvalidPlugins;
--- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -571,17 +571,18 @@ nsPluginTag::IsClicktoplay() NS_IMETHODIMP nsPluginTag::GetClicktoplay(bool *aClicktoplay) { *aClicktoplay = IsClicktoplay(); return NS_OK; } NS_IMETHODIMP -nsPluginTag::GetEnabledState(uint32_t *aEnabledState) { +nsPluginTag::GetEnabledState(uint32_t *aEnabledState) +{ int32_t enabledState; nsresult rv = Preferences::GetInt(GetStatePrefNameForPlugin(this).get(), &enabledState); if (NS_SUCCEEDED(rv) && enabledState >= nsIPluginTag::STATE_DISABLED && enabledState <= nsIPluginTag::STATE_ENABLED) { *aEnabledState = (uint32_t)enabledState; return rv; @@ -596,17 +597,18 @@ nsPluginTag::GetEnabledState(uint32_t *a *aEnabledState = (uint32_t)enabledState; return NS_OK; } return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsPluginTag::SetEnabledState(uint32_t aEnabledState) { +nsPluginTag::SetEnabledState(uint32_t aEnabledState) +{ if (aEnabledState >= ePluginState_MaxValue) return NS_ERROR_ILLEGAL_VALUE; uint32_t oldState = nsIPluginTag::STATE_DISABLED; GetEnabledState(&oldState); if (oldState != aEnabledState) { Preferences::SetInt(GetStatePrefNameForPlugin(this).get(), aEnabledState); if (RefPtr<nsPluginHost> host = nsPluginHost::GetInst()) { host->UpdatePluginInfo(this);
--- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -51,18 +51,16 @@ skip-if = ((buildapp == 'mulet' || build [test_bug504220.html] [test_bug628069_1.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug628069_2.html] [test_bug631440.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug653364.html] [test_bug861217.html] -[test_bug1077002.html] -run-if = e10s [test_clientRects.html] [test_clipboard_events.html] skip-if = e10s || buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined) [test_consoleAPI.html] [test_DOMMatrix.html] [test_domWindowUtils.html] [test_domWindowUtils_scrollXY.html] [test_domWindowUtils_scrollbarSize.html] @@ -96,16 +94,18 @@ skip-if = buildapp == 'b2g' || buildapp [test_resource_timing.html] skip-if = buildapp == 'b2g' || buildapp == 'mulet' [test_resource_timing_cross_origin.html] skip-if = buildapp == 'b2g' || buildapp == 'mulet' [test_performance_now.html] [test_srcset_pref.html] [test_showModalDialog.html] skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog) +[test_showModalDialog_e10s.html] +run-if = e10s [test_stylesheetPI.html] [test_vibrator.html] skip-if = toolkit == 'android' #CRASH_SUTAGENT [test_windowProperties.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_windowedhistoryframes.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_navigation_timing.html]
rename from dom/tests/mochitest/general/test_bug1077002.html rename to dom/tests/mochitest/general/test_showModalDialog_e10s.html --- a/dom/tests/mochitest/general/test_bug1077002.html +++ b/dom/tests/mochitest/general/test_showModalDialog_e10s.html @@ -1,36 +1,32 @@ <!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1077002 --> <head> - <title>Test for Bug 1077002</title> + <title>Test for showModalDialog unavailability in e10s</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1077002">Mozilla Bug 1077002</a> <p id="display"></p> <div id="content"> <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> <div id="status" style="display: none"></div> </div> <pre id="test"> <script type="application/javascript;version=1.7"> -/** Test for Bug 1077002 **/ +/** Test for showModalDialog unavailability in e10s **/ -try { - // NB: This test runs in e10s only. In e10s showModalDialog should only - // ever throw NS_ERROR_NOT_AVAILABLE. - window.showModalDialog("http://example.org"); -} catch (e) { - is(e.name, "NS_ERROR_NOT_AVAILABLE", "throw the correct error message"); -} +// NB: This test runs in e10s only. In e10s showModalDialog should not +// exist. +ok(!window.showModalDialog, "showModalDialog should not exist"); </script> </pre> </body> </html>
--- a/dom/vr/VRDevice.cpp +++ b/dom/vr/VRDevice.cpp @@ -5,24 +5,67 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsWrapperCache.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/VRDeviceBinding.h" #include "mozilla/dom/ElementBinding.h" #include "mozilla/dom/VRDevice.h" +#include "Navigator.h" #include "gfxVR.h" +#include "VRDeviceProxy.h" +#include "VRManagerChild.h" #include "nsIFrame.h" using namespace mozilla::gfx; namespace mozilla { namespace dom { +/*static*/ bool +VRDevice::RefreshVRDevices(dom::Navigator* aNavigator) +{ + gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); + return vm && vm->RefreshVRDevicesWithCallback(aNavigator); +} + +/*static*/ void +VRDevice::UpdateVRDevices(nsTArray<RefPtr<VRDevice>>& aDevices, nsISupports* aParent) +{ + nsTArray<RefPtr<VRDevice>> devices; + + gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); + nsTArray<RefPtr<gfx::VRDeviceProxy>> proxyDevices; + if (vm && vm->GetVRDevices(proxyDevices)) { + for (size_t i = 0; i < proxyDevices.Length(); i++) { + RefPtr<gfx::VRDeviceProxy> proxyDevice = proxyDevices[i]; + bool isNewDevice = true; + for (size_t j = 0; j < aDevices.Length(); j++) { + if (aDevices[j]->GetHMD()->GetDeviceInfo() == proxyDevice->GetDeviceInfo()) { + devices.AppendElement(aDevices[j]); + isNewDevice = false; + } + } + + if (isNewDevice) { + gfx::VRStateValidFlags sensorBits = proxyDevice->GetDeviceInfo().GetSupportedSensorStateBits(); + devices.AppendElement(new HMDInfoVRDevice(aParent, proxyDevice)); + if (sensorBits & (gfx::VRStateValidFlags::State_Position | + gfx::VRStateValidFlags::State_Orientation)) + { + devices.AppendElement(new HMDPositionVRDevice(aParent, proxyDevice)); + } + } + } + } + + aDevices = devices; +} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfViewReadOnly, mParent) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfViewReadOnly, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfViewReadOnly, Release) JSObject* VRFieldOfViewReadOnly::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return VRFieldOfViewReadOnlyBinding::Wrap(aCx, this, aGivenProto); @@ -128,21 +171,21 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRP NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRPositionState, Release) VRPositionState::VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState) : mParent(aParent) , mVRState(aState) { mTimeStamp = aState.timestamp; - if (aState.flags & gfx::VRHMDInfo::State_Position) { + if (aState.flags & gfx::VRStateValidFlags::State_Position) { mPosition = new DOMPoint(mParent, aState.position[0], aState.position[1], aState.position[2], 0.0); } - if (aState.flags & gfx::VRHMDInfo::State_Orientation) { + if (aState.flags & gfx::VRStateValidFlags::State_Orientation) { mOrientation = new DOMPoint(mParent, aState.orientation[0], aState.orientation[1], aState.orientation[2], aState.orientation[3]); } } DOMPoint* VRPositionState::GetLinearVelocity() { if (!mLinearVelocity) { @@ -201,164 +244,124 @@ HMDVRDevice::WrapObject(JSContext* aCx, } /* virtual */ JSObject* PositionSensorVRDevice::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return PositionSensorVRDeviceBinding::Wrap(aCx, this, aGivenProto); } -namespace { - -class HMDInfoVRDevice : public HMDVRDevice +HMDInfoVRDevice::HMDInfoVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD) + : HMDVRDevice(aParent, aHMD) { -public: - HMDInfoVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD) - : HMDVRDevice(aParent, aHMD) - { - uint64_t hmdid = aHMD->GetDeviceIndex() << 8; - uint64_t devid = hmdid | 0x00; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor + MOZ_COUNT_CTOR_INHERITED(HMDInfoVRDevice, HMDVRDevice); + uint64_t hmdid = aHMD->GetDeviceInfo().GetDeviceID() << 8; + uint64_t devid = hmdid | 0x00; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor + + mHWID.Truncate(); + mHWID.AppendPrintf("0x%llx", hmdid); + + mDeviceId.Truncate(); + mDeviceId.AppendPrintf("0x%llx", devid); + + mDeviceName.Truncate(); + mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceInfo().GetDeviceName())); + mDeviceName.AppendLiteral(" (HMD)"); + + mValid = true; +} - mHWID.Truncate(); - mHWID.AppendPrintf("0x%llx", hmdid); - - mDeviceId.Truncate(); - mDeviceId.AppendPrintf("0x%llx", devid); +HMDInfoVRDevice::~HMDInfoVRDevice() +{ + MOZ_COUNT_DTOR_INHERITED(HMDInfoVRDevice, HMDVRDevice); +} - mDeviceName.Truncate(); - mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceName())); - mDeviceName.AppendLiteral(" (HMD)"); +/* If a field of view that is set to all 0's is passed in, + * the recommended field of view for that eye is used. + */ +void +HMDInfoVRDevice::SetFieldOfView(const VRFieldOfViewInit& aLeftFOV, + const VRFieldOfViewInit& aRightFOV, + double zNear, double zFar) +{ + gfx::VRFieldOfView left = gfx::VRFieldOfView(aLeftFOV.mUpDegrees, aLeftFOV.mRightDegrees, + aLeftFOV.mDownDegrees, aLeftFOV.mLeftDegrees); + gfx::VRFieldOfView right = gfx::VRFieldOfView(aRightFOV.mUpDegrees, aRightFOV.mRightDegrees, + aRightFOV.mDownDegrees, aRightFOV.mLeftDegrees); - mValid = true; + if (left.IsZero()) { + left = mHMD->GetDeviceInfo().GetRecommendedEyeFOV(VRDeviceInfo::Eye_Left); } - virtual ~HMDInfoVRDevice() { } - - /* If a field of view that is set to all 0's is passed in, - * the recommended field of view for that eye is used. - */ - virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV, - const VRFieldOfViewInit& aRightFOV, - double zNear, double zFar) override - { - gfx::VRFieldOfView left = gfx::VRFieldOfView(aLeftFOV.mUpDegrees, aLeftFOV.mRightDegrees, - aLeftFOV.mDownDegrees, aLeftFOV.mLeftDegrees); - gfx::VRFieldOfView right = gfx::VRFieldOfView(aRightFOV.mUpDegrees, aRightFOV.mRightDegrees, - aRightFOV.mDownDegrees, aRightFOV.mLeftDegrees); - - if (left.IsZero()) - left = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Left); - if (right.IsZero()) - right = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Right); - - mHMD->SetFOV(left, right, zNear, zFar); - } - - virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) override - { - gfx::IntSize sz(mHMD->SuggestedEyeResolution()); - gfx::VRHMDInfo::Eye eye = aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right; - RefPtr<VREyeParameters> params = - new VREyeParameters(mParent, - gfx::VRFieldOfView(15, 15, 15, 15), // XXX min? - mHMD->GetMaximumEyeFOV(eye), - mHMD->GetRecommendedEyeFOV(eye), - mHMD->GetEyeTranslation(eye), - mHMD->GetEyeFOV(eye), - gfx::IntRect((aEye == VREye::Left) ? 0 : sz.width, 0, sz.width, sz.height)); - return params.forget(); + if (right.IsZero()) { + right = mHMD->GetDeviceInfo().GetRecommendedEyeFOV(VRDeviceInfo::Eye_Right); } -protected: -}; - -class HMDPositionVRDevice : public PositionSensorVRDevice -{ -public: - HMDPositionVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD) - : PositionSensorVRDevice(aParent) - , mHMD(aHMD) - , mTracking(false) - { - - uint64_t hmdid = aHMD->GetDeviceIndex() << 8; - uint64_t devid = hmdid | 0x01; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor - - mHWID.Truncate(); - mHWID.AppendPrintf("0x%llx", hmdid); - - mDeviceId.Truncate(); - mDeviceId.AppendPrintf("0x%llx", devid); + mHMD->SetFOV(left, right, zNear, zFar); +} - mDeviceName.Truncate(); - mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceName())); - mDeviceName.AppendLiteral(" (Sensor)"); - - mValid = true; - } +already_AddRefed<VREyeParameters> HMDInfoVRDevice::GetEyeParameters(VREye aEye) +{ + gfx::IntSize sz(mHMD->GetDeviceInfo().SuggestedEyeResolution()); + gfx::VRDeviceInfo::Eye eye = aEye == VREye::Left ? gfx::VRDeviceInfo::Eye_Left : gfx::VRDeviceInfo::Eye_Right; + RefPtr<VREyeParameters> params = + new VREyeParameters(mParent, + gfx::VRFieldOfView(15, 15, 15, 15), // XXX min? + mHMD->GetDeviceInfo().GetMaximumEyeFOV(eye), + mHMD->GetDeviceInfo().GetRecommendedEyeFOV(eye), + mHMD->GetDeviceInfo().GetEyeTranslation(eye), + mHMD->GetDeviceInfo().GetEyeFOV(eye), + gfx::IntRect((aEye == VREye::Left) ? 0 : sz.width, 0, sz.width, sz.height)); + return params.forget(); +} - ~HMDPositionVRDevice() - { - if (mTracking) { - mHMD->StopSensorTracking(); - } - } +HMDPositionVRDevice::HMDPositionVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD) + : PositionSensorVRDevice(aParent, aHMD) +{ + MOZ_COUNT_CTOR_INHERITED(HMDPositionVRDevice, PositionSensorVRDevice); - virtual already_AddRefed<VRPositionState> GetState() override - { - if (!mTracking) { - mHMD->StartSensorTracking(); - mTracking = true; - } + uint64_t hmdid = aHMD->GetDeviceInfo().GetDeviceID() << 8; + uint64_t devid = hmdid | 0x01; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor - gfx::VRHMDSensorState state = mHMD->GetSensorState(); - RefPtr<VRPositionState> obj = new VRPositionState(mParent, state); + mHWID.Truncate(); + mHWID.AppendPrintf("0x%llx", hmdid); + + mDeviceId.Truncate(); + mDeviceId.AppendPrintf("0x%llx", devid); - return obj.forget(); - } - - virtual already_AddRefed<VRPositionState> GetImmediateState() override - { - if (!mTracking) { - mHMD->StartSensorTracking(); - mTracking = true; - } + mDeviceName.Truncate(); + mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceInfo().GetDeviceName())); + mDeviceName.AppendLiteral(" (Sensor)"); - gfx::VRHMDSensorState state = mHMD->GetSensorState(); - RefPtr<VRPositionState> obj = new VRPositionState(mParent, state); + mValid = true; +} - return obj.forget(); - } - - virtual void ResetSensor() override - { - mHMD->ZeroSensor(); - } +HMDPositionVRDevice::~HMDPositionVRDevice() +{ + MOZ_COUNT_DTOR_INHERITED(HMDPositionVRDevice, PositionSensorVRDevice); +} -protected: - RefPtr<gfx::VRHMDInfo> mHMD; - bool mTracking; -}; +already_AddRefed<VRPositionState> +HMDPositionVRDevice::GetState() +{ + gfx::VRHMDSensorState state = mHMD->GetSensorState(); + RefPtr<VRPositionState> obj = new VRPositionState(mParent, state); -} // namespace - -bool -VRDevice::CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<RefPtr<VRDevice>>& aDevices) -{ - nsTArray<RefPtr<gfx::VRHMDInfo>> hmds; - gfx::VRHMDManager::GetAllHMDs(hmds); + return obj.forget(); +} - for (size_t i = 0; i < hmds.Length(); ++i) { - uint32_t sensorBits = hmds[i]->GetSupportedSensorStateBits(); - aDevices.AppendElement(new HMDInfoVRDevice(aParent, hmds[i])); +already_AddRefed<VRPositionState> +HMDPositionVRDevice::GetImmediateState() +{ + gfx::VRHMDSensorState state = mHMD->GetSensorState(); + RefPtr<VRPositionState> obj = new VRPositionState(mParent, state); - if (sensorBits & - (gfx::VRHMDInfo::State_Position | gfx::VRHMDInfo::State_Orientation)) - { - aDevices.AppendElement(new HMDPositionVRDevice(aParent, hmds[i])); - } - } + return obj.forget(); +} - return true; +void +HMDPositionVRDevice::ResetSensor() +{ + mHMD->ZeroSensor(); } } // namespace dom } // namespace mozilla
--- a/dom/vr/VRDevice.h +++ b/dom/vr/VRDevice.h @@ -16,19 +16,21 @@ #include "mozilla/dom/DOMRect.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsTArray.h" #include "nsWrapperCache.h" #include "gfxVR.h" +#include "VRDeviceProxy.h" namespace mozilla { namespace dom { +class Navigator; class VRFieldOfViewReadOnly : public nsWrapperCache { public: VRFieldOfViewReadOnly(nsISupports* aParent, double aUpDegrees, double aRightDegrees, double aDownDegrees, double aLeftDegrees) : mParent(aParent) @@ -172,59 +174,68 @@ protected: RefPtr<VRFieldOfView> mCurFOV; RefPtr<DOMRect> mRenderRect; }; class VRDevice : public nsISupports, public nsWrapperCache { public: - // create new VRDevice objects for all known underlying gfx::vr devices - static bool CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<RefPtr<VRDevice>>& aDevices); -public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(VRDevice) void GetHardwareUnitId(nsAString& aHWID) const { aHWID = mHWID; } void GetDeviceId(nsAString& aDeviceId) const { aDeviceId = mDeviceId; } void GetDeviceName(nsAString& aDeviceName) const { aDeviceName = mDeviceName; } bool IsValid() { return mValid; } - virtual void Shutdown() { } - nsISupports* GetParentObject() const { return mParent; } enum VRDeviceType { HMD, PositionSensor }; VRDeviceType GetType() const { return mType; } + static bool RefreshVRDevices(dom::Navigator* aNavigator); + static void UpdateVRDevices(nsTArray<RefPtr<VRDevice> >& aDevices, + nsISupports* aParent); + + gfx::VRDeviceProxy *GetHMD() { + return mHMD; + } + protected: - VRDevice(nsISupports* aParent, VRDeviceType aType) + VRDevice(nsISupports* aParent, + gfx::VRDeviceProxy* aHMD, + VRDeviceType aType) : mParent(aParent) + , mHMD(aHMD) , mType(aType) , mValid(false) { + MOZ_COUNT_CTOR(VRDevice); mHWID.AssignLiteral("uknown"); mDeviceId.AssignLiteral("unknown"); mDeviceName.AssignLiteral("unknown"); } - virtual ~VRDevice() { - Shutdown(); + virtual ~VRDevice() + { + MOZ_COUNT_DTOR(VRDevice); } nsCOMPtr<nsISupports> mParent; + RefPtr<gfx::VRDeviceProxy> mHMD; nsString mHWID; nsString mDeviceId; nsString mDeviceName; VRDeviceType mType; bool mValid; }; @@ -235,44 +246,72 @@ public: virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) = 0; virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV, const VRFieldOfViewInit& aRightFOV, double zNear, double zFar) = 0; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - gfx::VRHMDInfo *GetHMD() { return mHMD.get(); } +protected: + HMDVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD) + : VRDevice(aParent, aHMD, VRDevice::HMD) + { + MOZ_COUNT_CTOR_INHERITED(HMDVRDevice, VRDevice); + } + + virtual ~HMDVRDevice() + { + MOZ_COUNT_DTOR_INHERITED(HMDVRDevice, VRDevice); + } +}; -protected: - HMDVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD) - : VRDevice(aParent, VRDevice::HMD) - , mHMD(aHMD) - { } +class HMDInfoVRDevice : public HMDVRDevice +{ +public: + HMDInfoVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD); + virtual ~HMDInfoVRDevice(); - virtual ~HMDVRDevice() { } - - RefPtr<gfx::VRHMDInfo> mHMD; + /* If a field of view that is set to all 0's is passed in, + * the recommended field of view for that eye is used. + */ + virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV, + const VRFieldOfViewInit& aRightFOV, + double zNear, double zFar) override; + virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) override; }; class PositionSensorVRDevice : public VRDevice { public: virtual already_AddRefed<VRPositionState> GetState() = 0; - virtual already_AddRefed<VRPositionState> GetImmediateState() = 0; - virtual void ResetSensor() = 0; - virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; protected: - explicit PositionSensorVRDevice(nsISupports* aParent) - : VRDevice(aParent, VRDevice::PositionSensor) - { } + explicit PositionSensorVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD) + : VRDevice(aParent, aHMD, VRDevice::PositionSensor) + { + MOZ_COUNT_CTOR_INHERITED(PositionSensorVRDevice, VRDevice); + } - virtual ~PositionSensorVRDevice() { } + virtual ~PositionSensorVRDevice() + { + MOZ_COUNT_DTOR_INHERITED(PositionSensorVRDevice, VRDevice); + } +}; + +class HMDPositionVRDevice : public PositionSensorVRDevice +{ +public: + HMDPositionVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD); + ~HMDPositionVRDevice(); + + virtual already_AddRefed<VRPositionState> GetState() override; + virtual already_AddRefed<VRPositionState> GetImmediateState() override; + virtual void ResetSensor() override; }; } // namespace dom } // namespace mozilla #endif
--- a/dom/webidl/Touch.webidl +++ b/dom/webidl/Touch.webidl @@ -5,17 +5,33 @@ * * The origin of this IDL file is * http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. */ -[Func="mozilla::dom::Touch::PrefEnabled"] +dictionary TouchInit { + required long identifier; + required EventTarget target; + long clientX = 0; + long clientY = 0; + long screenX = 0; + long screenY = 0; + long pageX = 0; + long pageY = 0; + float radiusX = 0; + float radiusY = 0; + float rotationAngle = 0; + float force = 0; +}; + +[Constructor(TouchInit touchInitDict), + Func="mozilla::dom::Touch::PrefEnabled"] interface Touch { readonly attribute long identifier; readonly attribute EventTarget? target; readonly attribute long screenX; readonly attribute long screenY; readonly attribute long clientX; readonly attribute long clientY; readonly attribute long pageX;
--- a/dom/webidl/TouchEvent.webidl +++ b/dom/webidl/TouchEvent.webidl @@ -1,15 +1,22 @@ /* -*- Mode: IDL; 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/. */ -[Func="mozilla::dom::TouchEvent::PrefEnabled"] +dictionary TouchEventInit : EventModifierInit { + sequence<Touch> touches = []; + sequence<Touch> targetTouches = []; + sequence<Touch> changedTouches = []; +}; + +[Constructor(DOMString type, optional TouchEventInit eventInitDict), + Func="mozilla::dom::TouchEvent::PrefEnabled"] interface TouchEvent : UIEvent { readonly attribute TouchList touches; readonly attribute TouchList targetTouches; readonly attribute TouchList changedTouches; readonly attribute boolean altKey; readonly attribute boolean metaKey; readonly attribute boolean ctrlKey;
--- a/dom/workers/WorkerFeature.h +++ b/dom/workers/WorkerFeature.h @@ -69,17 +69,14 @@ enum Status Dead }; class WorkerFeature { public: virtual ~WorkerFeature() { } - virtual bool Suspend(JSContext* aCx) { return true; } - virtual bool Resume(JSContext* aCx) { return true; } - virtual bool Notify(JSContext* aCx, Status aStatus) = 0; }; END_WORKERS_NAMESPACE #endif /* mozilla_dom_workers_workerfeature_h__ */
--- a/gfx/gl/GLContextProviderCGL.mm +++ b/gfx/gl/GLContextProviderCGL.mm @@ -12,16 +12,20 @@ #include "gfxFailure.h" #include "gfxPrefs.h" #include "prenv.h" #include "GeckoProfiler.h" #include "mozilla/gfx/MacIOSurface.h" #include <OpenGL/OpenGL.h> +// When running inside a VM, creating an accelerated OpenGL context usually +// fails. Uncomment this line to emulate that behavior. +// #define EMULATE_VM + namespace mozilla { namespace gl { using namespace mozilla::gfx; class CGLLibrary { public: @@ -169,22 +173,33 @@ GLContextCGL::SwapBuffers() already_AddRefed<GLContext> GLContextProviderCGL::CreateWrappingExisting(void*, void*) { return nullptr; } static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = { + NSOpenGLPFAAllowOfflineRenderers, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = { NSOpenGLPFAAccelerated, NSOpenGLPFAAllowOfflineRenderers, 0 }; static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = { + NSOpenGLPFAAllowOfflineRenderers, + NSOpenGLPFADoubleBuffer, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel[] = { NSOpenGLPFAAccelerated, NSOpenGLPFAAllowOfflineRenderers, NSOpenGLPFADoubleBuffer, 0 }; static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = { 0 @@ -220,27 +235,33 @@ CreateWithFormat(const NSOpenGLPixelForm shareContext:nullptr]; [format release]; return context; } already_AddRefed<GLContext> -GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget) +GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated) { if (!sCGLLibrary.EnsureInitialized()) { return nullptr; } +#ifdef EMULATE_VM + if (aForceAccelerated) { + return nullptr; + } +#endif + const NSOpenGLPixelFormatAttribute* attribs; if (sCGLLibrary.UseDoubleBufferedWindows()) { - attribs = kAttribs_doubleBuffered; + attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered; } else { - attribs = kAttribs_singleBuffered; + attribs = aForceAccelerated ? kAttribs_singleBuffered_accel : kAttribs_singleBuffered; } NSOpenGLContext* context = CreateWithFormat(attribs); if (!context) { return nullptr; } // make the context transparent GLint opaque = 0;
--- a/gfx/gl/GLContextProviderEAGL.mm +++ b/gfx/gl/GLContextProviderEAGL.mm @@ -206,17 +206,17 @@ CreateEAGLContext(bool aOffscreen, GLCon glContext = nullptr; return nullptr; } return glContext.forget(); } already_AddRefed<GLContext> -GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget) +GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated) { RefPtr<GLContext> glContext = CreateEAGLContext(false, GetGlobalContextEAGL()); if (!glContext) { return nullptr; } if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) { return nullptr;
--- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -744,17 +744,17 @@ GLContextProviderEGL::CreateWrappingExis return glContext.forget(); } return nullptr; } already_AddRefed<GLContext> -GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) +GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated) { if (!sEGLLibrary.EnsureInitialized()) { MOZ_CRASH("GFX: Failed to load EGL library 3!\n"); return nullptr; } bool doubleBuffered = true;
--- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -1047,17 +1047,17 @@ GLContextProviderGLX::CreateWrappingExis return glContext.forget(); } return nullptr; } already_AddRefed<GLContext> -GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget) +GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated) { if (!sGLXLibrary.EnsureInitialized()) { return nullptr; } // Currently, we take whatever Visual the window already has, and // try to create an fbconfig for that visual. This isn't // necessarily what we want in the long run; an fbconfig may not
--- a/gfx/gl/GLContextProviderImpl.h +++ b/gfx/gl/GLContextProviderImpl.h @@ -30,21 +30,22 @@ public: * The GetSharedContext() method will return non-null if sharing * was successful. * * Note: a context created for a widget /must not/ hold a strong * reference to the widget; otherwise a cycle can be created through * a GL layer manager. * * @param aWidget Widget whose surface to create a context for + * @param aForceAccelerated true if only accelerated contexts are allowed * * @return Context to use for the window */ static already_AddRefed<GLContext> - CreateForWindow(nsIWidget* widget); + CreateForWindow(nsIWidget* widget, bool aForceAccelerated); /** * Create a context for offscreen rendering. The target of this * context should be treated as opaque -- it might be a FBO, or a * pbuffer, or some other construct. Users of this GLContext * should bind framebuffer 0 directly to use this offscreen buffer. * * The offscreen context returned by this method will always have
--- a/gfx/gl/GLContextProviderNull.cpp +++ b/gfx/gl/GLContextProviderNull.cpp @@ -4,17 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GLContextProvider.h" namespace mozilla { namespace gl { already_AddRefed<GLContext> -GLContextProviderNull::CreateForWindow(nsIWidget*) +GLContextProviderNull::CreateForWindow(nsIWidget*, bool aForceAccelerated) { return nullptr; } already_AddRefed<GLContext> GLContextProviderNull::CreateWrappingExisting(void*, void*) { return nullptr;
--- a/gfx/gl/GLContextProviderWGL.cpp +++ b/gfx/gl/GLContextProviderWGL.cpp @@ -433,17 +433,17 @@ GetGlobalContextWGL() already_AddRefed<GLContext> GLContextProviderWGL::CreateWrappingExisting(void*, void*) { return nullptr; } already_AddRefed<GLContext> -GLContextProviderWGL::CreateForWindow(nsIWidget *aWidget) +GLContextProviderWGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated) { if (!sWGLLib.EnsureInitialized()) { return nullptr; } /** * We need to make sure we call SetPixelFormat -after- calling * EnsureInitialized, otherwise it can load/unload the dll and
--- a/gfx/layers/CompositorTypes.h +++ b/gfx/layers/CompositorTypes.h @@ -135,17 +135,16 @@ enum class EffectTypes : uint8_t { /** * How the Compositable should manage textures. */ enum class CompositableType : uint8_t { UNKNOWN, CONTENT_TILED, // tiled painted layer IMAGE, // image with single buffering - IMAGE_OVERLAY, // image without buffer IMAGE_BRIDGE, // ImageBridge protocol CONTENT_SINGLE, // painted layer interface, single buffering CONTENT_DOUBLE, // painted layer interface, double buffering COUNT }; #ifdef XP_WIN typedef void* SyncHandle;
--- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -129,21 +129,16 @@ ImageContainer::ImageContainer(Mode flag switch (flag) { case SYNCHRONOUS: break; case ASYNCHRONOUS: mIPDLChild = new ImageContainerChild(this); mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this).take(); MOZ_ASSERT(mImageClient); break; - case ASYNCHRONOUS_OVERLAY: - mIPDLChild = new ImageContainerChild(this); - mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY, this).take(); - MOZ_ASSERT(mImageClient); - break; default: MOZ_ASSERT(false, "This flag is invalid."); break; } } } ImageContainer::~ImageContainer() @@ -174,23 +169,18 @@ ImageContainer::CreateSharedRGBImage() return new SharedRGBImage(mImageClient); } #ifdef MOZ_WIDGET_GONK RefPtr<OverlayImage> ImageContainer::CreateOverlayImage() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) { - // If this ImageContainer is async but the image type mismatch, fix it here - if (ImageBridgeChild::IsCreated()) { - ImageBridgeChild::DispatchReleaseImageClient(mImageClient); - mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient( - CompositableType::IMAGE_OVERLAY, this).take(); - } + if (!mImageClient || !mImageClient->AsImageClientSingle()) { + return nullptr; } return new OverlayImage(); } #endif void ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages) {
--- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -345,17 +345,17 @@ protected: * updates the shared state to point to the new image and the old image * is immediately released (not true in Normal or Asynchronous modes). */ class ImageContainer final : public SupportsWeakPtr<ImageContainer> { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer) public: MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer) - enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01, ASYNCHRONOUS_OVERLAY = 0x02 }; + enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01 }; explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS); typedef uint32_t FrameID; typedef uint32_t ProducerID; RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
--- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1112,17 +1112,18 @@ ContainerLayer::ContainerLayer(LayerMana mInheritedXScale(1.0f), mInheritedYScale(1.0f), mPresShellResolution(1.0f), mScaleToResolution(false), mUseIntermediateSurface(false), mSupportsComponentAlphaChildren(false), mMayHaveReadbackChild(false), mChildrenChanged(false), - mEventRegionsOverride(EventRegionsOverride::NoOverride) + mEventRegionsOverride(EventRegionsOverride::NoOverride), + mVRDeviceID(0) { MOZ_COUNT_CTOR(ContainerLayer); mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT } ContainerLayer::~ContainerLayer() { MOZ_COUNT_DTOR(ContainerLayer); @@ -1274,17 +1275,17 @@ ContainerLayer::RepositionChild(Layer* a void ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale, mInheritedXScale, mInheritedYScale, mPresShellResolution, mScaleToResolution, mEventRegionsOverride, - reinterpret_cast<uint64_t>(mHMDInfo.get())); + mVRDeviceID); } bool ContainerLayer::Creates3DContextWithExtendingChildren() { if (Extend3DContext()) { return false; } @@ -2138,18 +2139,18 @@ ContainerLayer::PrintInfo(std::stringstr aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get(); } if (mEventRegionsOverride & EventRegionsOverride::ForceDispatchToContent) { aStream << " [force-dtc]"; } if (mEventRegionsOverride & EventRegionsOverride::ForceEmptyHitRegion) { aStream << " [force-ehr]"; } - if (mHMDInfo) { - aStream << nsPrintfCString(" [hmd=%p]", mHMDInfo.get()).get(); + if (mVRDeviceID) { + aStream << nsPrintfCString(" [hmd=%lu]", mVRDeviceID).get(); } } void ContainerLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) { Layer::DumpPacket(aPacket, aParent); // Get this layer data
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -2124,18 +2124,22 @@ public: EventRegionsOverride GetEventRegionsOverride() const { return mEventRegionsOverride; } /** * VR */ - void SetVRHMDInfo(gfx::VRHMDInfo* aHMD) { mHMDInfo = aHMD; } - gfx::VRHMDInfo* GetVRHMDInfo() { return mHMDInfo; } + void SetVRDeviceID(uint32_t aVRDeviceID) { + mVRDeviceID = aVRDeviceID; + } + uint32_t GetVRDeviceID() { + return mVRDeviceID; + } /** * Replace the current effective transform with the given one, * returning the old one. This is currently added as a hack for VR * rendering, and might go away if we find a better way to do this. * If you think you have a need for this method, talk with * vlad/mstange/mwoodrow first. */ @@ -2201,17 +2205,17 @@ protected: bool mScaleToResolution; bool mUseIntermediateSurface; bool mSupportsComponentAlphaChildren; bool mMayHaveReadbackChild; // This is updated by ComputeDifferences. This will be true if we need to invalidate // the intermediate surface. bool mChildrenChanged; EventRegionsOverride mEventRegionsOverride; - RefPtr<gfx::VRHMDInfo> mHMDInfo; + uint32_t mVRDeviceID; }; /** * A Layer which just renders a solid color in its visible region. It actually * can fill any area that contains the visible region, so if you need to * restrict the area filled, set a clip region on this layer. */ class ColorLayer : public Layer {
--- a/gfx/layers/client/ClientImageLayer.cpp +++ b/gfx/layers/client/ClientImageLayer.cpp @@ -102,24 +102,16 @@ protected: if (mContainer->IsAsync()) { mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE; return mImageClientTypeContainer; } AutoLockImage autoLock(mContainer); -#ifdef MOZ_WIDGET_GONK - if (autoLock.HasImage() && - autoLock.GetImage()->GetFormat() == ImageFormat::OVERLAY_IMAGE) { - mImageClientTypeContainer = CompositableType::IMAGE_OVERLAY; - return mImageClientTypeContainer; - } -#endif - mImageClientTypeContainer = autoLock.HasImage() ? CompositableType::IMAGE : CompositableType::UNKNOWN; return mImageClientTypeContainer; } RefPtr<ImageClient> mImageClient; CompositableType mImageClientTypeContainer; };
--- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -50,21 +50,16 @@ ImageClient::CreateImageClient(Composita result = new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE); break; case CompositableType::IMAGE_BRIDGE: result = new ImageClientBridge(aForwarder, aFlags); break; case CompositableType::UNKNOWN: result = nullptr; break; -#ifdef MOZ_WIDGET_GONK - case CompositableType::IMAGE_OVERLAY: - result = new ImageClientOverlay(aForwarder, aFlags); - break; -#endif default: MOZ_CRASH("GFX: unhandled program type image"); } NS_ASSERTION(result, "Failed to create ImageClient"); return result.forget(); } @@ -149,16 +144,31 @@ ImageClientSingle::UpdateImage(ImageCont return true; } nsTArray<Buffer> newBuffers; nsAutoTArray<CompositableForwarder::TimedTextureClient,4> textures; for (auto& img : images) { Image* image = img.mImage; + +#ifdef MOZ_WIDGET_GONK + if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) { + OverlayImage* overlayImage = static_cast<OverlayImage*>(image); + uint32_t overlayId = overlayImage->GetOverlayId(); + gfx::IntSize size = overlayImage->GetSize(); + + OverlaySource source; + source.handle() = OverlayHandle(overlayId); + source.size() = size; + GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect()); + continue; + } +#endif + RefPtr<TextureClient> texture = image->GetTextureClient(this); for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) { if (mBuffers[i].mImageSerial == image->GetSerial()) { if (texture) { MOZ_ASSERT(texture == mBuffers[i].mTextureClient); } else { texture = mBuffers[i].mTextureClient; @@ -311,45 +321,10 @@ ImageClientBridge::UpdateImage(ImageCont if (mAsyncContainerID == aContainer->GetAsyncContainerID()) { return true; } mAsyncContainerID = aContainer->GetAsyncContainerID(); static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer); return true; } -#ifdef MOZ_WIDGET_GONK -ImageClientOverlay::ImageClientOverlay(CompositableForwarder* aFwd, - TextureFlags aFlags) - : ImageClient(aFwd, aFlags, CompositableType::IMAGE_OVERLAY) -{ -} - -bool -ImageClientOverlay::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) -{ - AutoLockImage autoLock(aContainer); - - Image *image = autoLock.GetImage(); - if (!image) { - return false; - } - - if (mLastUpdateGenerationCounter == (uint32_t)image->GetSerial()) { - return true; - } - mLastUpdateGenerationCounter = (uint32_t)image->GetSerial(); - - if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) { - OverlayImage* overlayImage = static_cast<OverlayImage*>(image); - uint32_t overlayId = overlayImage->GetOverlayId(); - gfx::IntSize size = overlayImage->GetSize(); - - OverlaySource source; - source.handle() = OverlayHandle(overlayId); - source.size() = size; - GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect()); - } - return true; -} -#endif } // namespace layers } // namespace mozilla
--- a/gfx/layers/client/ImageClient.h +++ b/gfx/layers/client/ImageClient.h @@ -135,34 +135,12 @@ public: { MOZ_ASSERT(!aChild, "ImageClientBridge should not have IPDL actor"); } protected: uint64_t mAsyncContainerID; }; -#ifdef MOZ_WIDGET_GONK -/** - * And ImageClient to handle opaque video stream. - * Such video stream does not upload new Image for each frame. - * Gecko have no way to get the buffer content from the Image, since the Image - * does not contain the real buffer. - * It need special hardware to display the Image - */ -class ImageClientOverlay : public ImageClient -{ -public: - ImageClientOverlay(CompositableForwarder* aFwd, - TextureFlags aFlags); - - virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags); - TextureInfo GetTextureInfo() const override - { - return TextureInfo(CompositableType::IMAGE_OVERLAY); - } -}; -#endif - } // namespace layers } // namespace mozilla #endif
--- a/gfx/layers/composite/CompositableHost.cpp +++ b/gfx/layers/composite/CompositableHost.cpp @@ -197,21 +197,16 @@ CompositableHost::Create(const TextureIn NS_ERROR("Cannot create an image bridge compositable this way"); break; case CompositableType::CONTENT_TILED: result = new TiledContentHost(aTextureInfo); break; case CompositableType::IMAGE: result = new ImageHost(aTextureInfo); break; -#ifdef MOZ_WIDGET_GONK - case CompositableType::IMAGE_OVERLAY: - result = new ImageHostOverlay(aTextureInfo); - break; -#endif case CompositableType::CONTENT_SINGLE: result = new ContentHostSingleBuffered(aTextureInfo); break; case CompositableType::CONTENT_DOUBLE: result = new ContentHostDoubleBuffered(aTextureInfo); break; default: NS_ERROR("Unknown CompositableType");
--- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -27,16 +27,17 @@ #include "mozilla/RefPtr.h" // for nsRefPtr #include "nsDebug.h" // for NS_ASSERTION #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE #include "nsRegion.h" // for nsIntRegion #include "nsTArray.h" // for nsAutoTArray #include "TextRenderer.h" // for TextRenderer #include <vector> +#include "VRManager.h" // for VRManager #include "GeckoProfiler.h" // for GeckoProfiler #ifdef MOZ_ENABLE_PROFILER_SPS #include "ProfilerMarkers.h" // for ProfilerMarkers #endif #define CULLING_LOG(...) // #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__) @@ -133,28 +134,28 @@ struct PreparedLayer RenderTargetIntRect mClipRect; }; template<class ContainerT> void ContainerRenderVR(ContainerT* aContainer, LayerManagerComposite* aManager, const gfx::IntRect& aClipRect, - gfx::VRHMDInfo* aHMD) + RefPtr<gfx::VRHMDInfo> aHMD) { RefPtr<CompositingRenderTarget> surface; Compositor* compositor = aManager->GetCompositor(); RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget(); float opacity = aContainer->GetEffectiveOpacity(); // The size of each individual eye surface - gfx::IntSize eyeResolution = aHMD->SuggestedEyeResolution(); + gfx::IntSize eyeResolution = aHMD->GetDeviceInfo().SuggestedEyeResolution(); gfx::IntRect eyeRect[2]; eyeRect[0] = gfx::IntRect(0, 0, eyeResolution.width, eyeResolution.height); eyeRect[1] = gfx::IntRect(eyeResolution.width, 0, eyeResolution.width, eyeResolution.height); // The intermediate surface size; we're going to assume that we're not going to run // into max texture size limits gfx::IntRect surfaceRect = gfx::IntRect(0, 0, eyeResolution.width * 2, eyeResolution.height); @@ -343,17 +344,17 @@ struct PreparedData template<class ContainerT> void ContainerPrepare(ContainerT* aContainer, LayerManagerComposite* aManager, const RenderTargetIntRect& aClipRect) { aContainer->mPrepared = MakeUnique<PreparedData>(); aContainer->mPrepared->mNeedsSurfaceCopy = false; - gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo(); + RefPtr<gfx::VRHMDInfo> hmdInfo = gfx::VRManager::Get()->GetDevice(aContainer->GetVRDeviceID()); if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) { // we're not going to do anything here; instead, we'll do it all in ContainerRender. // XXX fix this; we can win with the same optimizations. Specifically, we // want to render thebes layers only once and then composite the intermeidate surfaces // with different transforms twice. return; } @@ -688,17 +689,17 @@ RenderIntermediate(ContainerT* aContaine template<class ContainerT> void ContainerRender(ContainerT* aContainer, LayerManagerComposite* aManager, const gfx::IntRect& aClipRect) { MOZ_ASSERT(aContainer->mPrepared); - gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo(); + RefPtr<gfx::VRHMDInfo> hmdInfo = gfx::VRManager::Get()->GetDevice(aContainer->GetVRDeviceID()); if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) { ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo); aContainer->mPrepared = nullptr; return; } if (aContainer->UseIntermediateSurface()) { RefPtr<CompositingRenderTarget> surface;
--- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -127,16 +127,31 @@ ImageHost::RemoveTextureHost(TextureHost for (int32_t i = mImages.Length() - 1; i >= 0; --i) { if (mImages[i].mFrontBuffer == aTexture) { aTexture->UnbindTextureSource(); mImages.RemoveElementAt(i); } } } +void +ImageHost::UseOverlaySource(OverlaySource aOverlay, + const gfx::IntRect& aPictureRect) +{ + if ((aOverlay.handle().type() == OverlayHandle::Tint32_t) && + aOverlay.handle().get_int32_t() != INVALID_OVERLAY) { + if (!mImageHostOverlay) { + mImageHostOverlay = new ImageHostOverlay(); + } + mImageHostOverlay->UseOverlaySource(aOverlay, aPictureRect); + } else { + mImageHostOverlay = nullptr; + } +} + static TimeStamp GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias) { switch (aBias) { case ImageHost::BIAS_NEGATIVE: return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS); case ImageHost::BIAS_POSITIVE: return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS); @@ -259,16 +274,31 @@ ImageHost::Composite(LayerComposite* aLa const nsIntRegion* aVisibleRegion) { if (!GetCompositor()) { // should only happen when a tab is dragged to another window and // async-video is still sending frames but we haven't attached the // set the new compositor yet. return; } + + if (mImageHostOverlay) { + mImageHostOverlay->Composite(GetCompositor(), + mFlashCounter, + aLayer, + aEffectChain, + aOpacity, + aTransform, + aFilter, + aClipRect, + aVisibleRegion); + mBias = BIAS_NONE; + return; + } + int imageIndex = ChooseImageIndex(); if (imageIndex < 0) { return; } if (uint32_t(imageIndex) + 1 < mImages.Length()) { GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS)); } @@ -423,16 +453,20 @@ ImageHost::PrintInfo(std::stringstream& nsAutoCString pfx(aPrefix); pfx += " "; for (auto& img : mImages) { aStream << "\n"; img.mFrontBuffer->PrintInfo(aStream, pfx.get()); AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]"); } + + if (mImageHostOverlay) { + mImageHostOverlay->PrintInfo(aStream, aPrefix); + } } void ImageHost::Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml) { for (auto& img : mImages) { @@ -442,26 +476,34 @@ ImageHost::Dump(std::stringstream& aStre DumpTextureHost(aStream, img.mFrontBuffer); aStream << (aDumpHtml ? " </li></ul> " : " "); } } LayerRenderState ImageHost::GetRenderState() { + if (mImageHostOverlay) { + return mImageHostOverlay->GetRenderState(); + } + TimedImage* img = ChooseImage(); if (img) { return img->mFrontBuffer->GetRenderState(); } return LayerRenderState(); } already_AddRefed<gfx::DataSourceSurface> ImageHost::GetAsSurface() { + if (mImageHostOverlay) { + return nullptr; + } + TimedImage* img = ChooseImage(); if (img) { return img->mFrontBuffer->GetAsSurface(); } return nullptr; } bool @@ -488,16 +530,20 @@ ImageHost::Unlock() img->mFrontBuffer->Unlock(); } mLocked = false; } IntSize ImageHost::GetImageSize() const { + if (mImageHostOverlay) { + return mImageHostOverlay->GetImageSize(); + } + const TimedImage* img = ChooseImage(); if (img) { return IntSize(img->mPictureRect.width, img->mPictureRect.height); } return IntSize(); } already_AddRefed<TexturedEffect> @@ -529,63 +575,65 @@ ImageHost::SetImageContainer(ImageContai mImageContainer->mImageHosts.RemoveElement(this); } mImageContainer = aImageContainer; if (mImageContainer) { mImageContainer->mImageHosts.AppendElement(this); } } -#ifdef MOZ_WIDGET_GONK -ImageHostOverlay::ImageHostOverlay(const TextureInfo& aTextureInfo) - : CompositableHost(aTextureInfo) +ImageHostOverlay::ImageHostOverlay() { + MOZ_COUNT_CTOR(ImageHostOverlay); } ImageHostOverlay::~ImageHostOverlay() { + MOZ_COUNT_DTOR(ImageHostOverlay); } void -ImageHostOverlay::Composite(LayerComposite* aLayer, +ImageHostOverlay::Composite(Compositor* aCompositor, + uint32_t aFlashCounter, + LayerComposite* aLayer, EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, const gfx::Filter& aFilter, const gfx::Rect& aClipRect, const nsIntRegion* aVisibleRegion) { - if (!GetCompositor()) { + if (mOverlay.handle().type() == OverlayHandle::Tnull_t) { return; } - if (mOverlay.handle().type() == OverlayHandle::Tnull_t) - return; Color hollow(0.0f, 0.0f, 0.0f, 0.0f); aEffectChain.mPrimaryEffect = new EffectSolidColor(hollow); aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(CompositionOp::OP_SOURCE); gfx::Rect rect; gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height); rect.SetRect(mPictureRect.x, mPictureRect.y, mPictureRect.width, mPictureRect.height); - mCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); - mCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE, - rect, aClipRect, aTransform, mFlashCounter); + aCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); + aCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE, + rect, aClipRect, aTransform, aFlashCounter); } LayerRenderState ImageHostOverlay::GetRenderState() { LayerRenderState state; +#ifdef MOZ_WIDGET_GONK if (mOverlay.handle().type() == OverlayHandle::Tint32_t) { state.SetOverlayId(mOverlay.handle().get_int32_t()); } +#endif return state; } void ImageHostOverlay::UseOverlaySource(OverlaySource aOverlay, const nsIntRect& aPictureRect) { mOverlay = aOverlay; @@ -597,22 +645,21 @@ ImageHostOverlay::GetImageSize() const { return IntSize(mPictureRect.width, mPictureRect.height); } void ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix) { aStream << aPrefix; - aStream << nsPrintfCString("ImageHost (0x%p)", this).get(); + aStream << nsPrintfCString("ImageHostOverlay (0x%p)", this).get(); AppendToString(aStream, mPictureRect, " [picture-rect=", "]"); if (mOverlay.handle().type() == OverlayHandle::Tint32_t) { nsAutoCString pfx(aPrefix); pfx += " "; aStream << nsPrintfCString("Overlay: %d", mOverlay.handle().get_int32_t()).get(); } } -#endif } // namespace layers } // namespace mozilla
--- a/gfx/layers/composite/ImageHost.h +++ b/gfx/layers/composite/ImageHost.h @@ -25,16 +25,17 @@ #include "nscore.h" // for nsACString namespace mozilla { namespace layers { class Compositor; struct EffectChain; class ImageContainerParent; +class ImageHostOverlay; /** * ImageHost. Works with ImageClientSingle and ImageClientBuffered */ class ImageHost : public CompositableHost { public: explicit ImageHost(const TextureInfo& aTextureInfo); @@ -49,16 +50,19 @@ public: const gfx::Filter& aFilter, const gfx::Rect& aClipRect, const nsIntRegion* aVisibleRegion = nullptr) override; virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override; virtual void RemoveTextureHost(TextureHost* aTexture) override; + virtual void UseOverlaySource(OverlaySource aOverlay, + const gfx::IntRect& aPictureRect) override; + virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override; virtual void Attach(Layer* aLayer, Compositor* aCompositor, AttachFlags aFlags = NO_FLAGS) override; virtual void SetCompositor(Compositor* aCompositor) override; @@ -132,45 +136,46 @@ protected: int32_t mLastFrameID; int32_t mLastProducerID; /** * Bias to apply to the next frame. */ Bias mBias; bool mLocked; + + RefPtr<ImageHostOverlay> mImageHostOverlay; }; -#ifdef MOZ_WIDGET_GONK - /** - * ImageHostOverlay works with ImageClientOverlay + * ImageHostOverlay handles OverlaySource compositing */ -class ImageHostOverlay : public CompositableHost { +class ImageHostOverlay { +protected: + virtual ~ImageHostOverlay(); + public: - ImageHostOverlay(const TextureInfo& aTextureInfo); - ~ImageHostOverlay(); + NS_INLINE_DECL_REFCOUNTING(ImageHostOverlay) + ImageHostOverlay(); - virtual CompositableType GetType() { return mTextureInfo.mCompositableType; } - - virtual void Composite(LayerComposite* aLayer, + virtual void Composite(Compositor* aCompositor, + uint32_t aFlashCounter, + LayerComposite* aLayer, EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, const gfx::Filter& aFilter, const gfx::Rect& aClipRect, - const nsIntRegion* aVisibleRegion = nullptr) override; - virtual LayerRenderState GetRenderState() override; + const nsIntRegion* aVisibleRegion); + virtual LayerRenderState GetRenderState(); virtual void UseOverlaySource(OverlaySource aOverlay, - const gfx::IntRect& aPictureRect) override; - virtual gfx::IntSize GetImageSize() const override; + const gfx::IntRect& aPictureRect); + virtual gfx::IntSize GetImageSize() const; virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix); protected: gfx::IntRect mPictureRect; OverlaySource mOverlay; }; -#endif - } // namespace layers } // namespace mozilla #endif
--- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -45,17 +45,16 @@ ImageLayerComposite::~ImageLayerComposit CleanupResources(); } bool ImageLayerComposite::SetCompositableHost(CompositableHost* aHost) { switch (aHost->GetType()) { case CompositableType::IMAGE: - case CompositableType::IMAGE_OVERLAY: mImageHost = aHost; return true; default: return false; } } void
--- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -1037,17 +1037,17 @@ LayerManagerComposite::RenderToPresentat nsScreenGonk* mirrorScreen = mirrorScreenWidget->GetScreen(); if (!mirrorScreen->GetTopWindows().IsEmpty()) { return; } EGLSurface surface = mirrorScreen->GetEGLSurface(); if (surface == LOCAL_EGL_NO_SURFACE) { // Create GLContext - RefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget); + RefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget, false); 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; } }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -671,17 +671,17 @@ CompositorD3D11::DrawVRDistortion(const HRESULT hr; EffectVRDistortion* vrEffect = static_cast<EffectVRDistortion*>(aEffectChain.mPrimaryEffect.get()); TextureSourceD3D11* source = vrEffect->mTexture->AsSourceD3D11(); VRHMDInfo* hmdInfo = vrEffect->mHMD; - VRHMDType hmdType = hmdInfo->GetType(); + VRHMDType hmdType = hmdInfo->GetDeviceInfo().GetType(); if (!mAttachments->mVRDistortionVS[hmdType] || !mAttachments->mVRDistortionPS[hmdType]) { NS_WARNING("No VS/PS for hmd type for VR distortion!"); return; }
--- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -837,16 +837,17 @@ bool DXGITextureHostD3D11::Lock() { if (!GetDevice()) { NS_WARNING("trying to lock a TextureHost without a D3D device"); return false; } if (!mTextureSource) { if (!mTexture && !OpenSharedHandle()) { + gfxWindowsPlatform::GetPlatform()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE); return false; } mTextureSource = new DataTextureSourceD3D11(mFormat, mCompositor, mTexture); } mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp +++ b/gfx/layers/ipc/CompositableTransactionParent.cpp @@ -212,17 +212,16 @@ CompositableParentManager::ReceiveCompos ScheduleComposition(op); } break; } #ifdef MOZ_WIDGET_GONK case CompositableOperation::TOpUseOverlaySource: { const OpUseOverlaySource& op = aEdit.get_OpUseOverlaySource(); CompositableHost* compositable = AsCompositable(op); - MOZ_ASSERT(compositable->GetType() == CompositableType::IMAGE_OVERLAY, "Invalid operation!"); if (!ValidatePictureRect(op.overlay().size(), op.picture())) { return false; } compositable->UseOverlaySource(op.overlay(), op.picture()); break; } #endif default: {
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -23,16 +23,17 @@ #endif #include "gfxPrefs.h" // for gfxPrefs #include "mozilla/AutoRestore.h" // for AutoRestore #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown #include "mozilla/DebugOnly.h" // for DebugOnly #include "mozilla/gfx/2D.h" // for DrawTarget #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/gfx/Rect.h" // for IntSize +#include "VRManager.h" // for VRManager #include "mozilla/ipc/Transport.h" // for Transport #include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager #include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager #include "mozilla/layers/AsyncCompositionManager.h" #include "mozilla/layers/BasicCompositor.h" // for BasicCompositor #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/CompositorLRU.h" // for CompositorLRU #include "mozilla/layers/CompositorOGL.h" // for CompositorOGL @@ -74,16 +75,22 @@ #ifdef MOZ_WIDGET_GONK #include "GeckoTouchDispatcher.h" #endif #include "LayerScope.h" namespace mozilla { + +namespace gfx { +// See VRManagerChild.cpp +void ReleaseVRManagerParentSingleton(); +} // namespace gfx + namespace layers { using namespace mozilla::ipc; using namespace mozilla::gfx; using namespace std; using base::ProcessId; using base::Thread; @@ -421,16 +428,17 @@ CompositorVsyncScheduler::Composite(Time { MOZ_ASSERT(CompositorParent::IsInCompositorThread()); { MonitorAutoLock lock(mCurrentCompositeTaskMonitor); mCurrentCompositeTask = nullptr; } DispatchTouchEvents(aVsyncTimestamp); + DispatchVREvents(aVsyncTimestamp); if (mNeedsComposite || mAsapScheduling) { mNeedsComposite = 0; mLastCompose = aVsyncTimestamp; ComposeToTarget(nullptr); mVsyncNotificationsSkipped = 0; TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp; @@ -492,30 +500,40 @@ CompositorVsyncScheduler::UnobserveVsync void CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp) { #ifdef MOZ_WIDGET_GONK GeckoTouchDispatcher::GetInstance()->NotifyVsync(aVsyncTimestamp); #endif } +void +CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(CompositorParent::IsInCompositorThread()); + + VRManager* vm = VRManager::Get(); + vm->NotifyVsync(aVsyncTimestamp); +} + void CompositorParent::StartUp() { MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); sCompositorThreadHolder = new CompositorThreadHolder(); } void CompositorParent::ShutDown() { MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!"); ReleaseImageBridgeParentSingleton(); + ReleaseVRManagerParentSingleton(); MediaSystemResourceService::Shutdown(); sCompositorThreadHolder = nullptr; // No locking is needed around sFinishedCompositorShutDown because it is only // ever accessed on the main thread. while (!sFinishedCompositorShutDown) { NS_ProcessNextEvent(nullptr, true);
--- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -129,16 +129,17 @@ public: private: virtual ~CompositorVsyncScheduler(); void NotifyCompositeTaskExecuted(); void ObserveVsync(); void UnobserveVsync(); void DispatchTouchEvents(TimeStamp aVsyncTimestamp); + void DispatchVREvents(TimeStamp aVsyncTimestamp); void CancelCurrentSetNeedsCompositeTask(); class Observer final : public VsyncObserver { public: explicit Observer(CompositorVsyncScheduler* aOwner); virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override; void Destroy(); @@ -290,17 +291,18 @@ public: /** * Returns a pointer to the compositor corresponding to the given ID. */ static CompositorParent* GetCompositor(uint64_t id); /** * Returns the compositor thread's message loop. * - * This message loop is used by CompositorParent and ImageBridgeParent. + * This message loop is used by CompositorParent, ImageBridgeParent, + * and VRManagerParent */ static MessageLoop* CompositorLoop(); /** * Creates the compositor thread and the global compositor map. */ static void StartUp();
--- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -44,20 +44,16 @@ #include "mozilla/layers/TextureHost.h" #include "mozilla/layers/AsyncCompositionManager.h" typedef std::vector<mozilla::layers::EditReply> EditReplyVector; using mozilla::layout::RenderFrameParent; namespace mozilla { -namespace gfx { -class VRHMDInfo; -} // namespace gfx - namespace layers { class PGrallocBufferParent; //-------------------------------------------------- // Convenience accessors static ShadowLayerParent* cast(const PLayerParent* in) @@ -397,22 +393,18 @@ LayerTransactionParent::RecvUpdate(Infal const ContainerLayerAttributes& attrs = specific.get_ContainerLayerAttributes(); containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale()); containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale()); containerLayer->SetScaleToResolution(attrs.scaleToResolution(), attrs.presShellResolution()); containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride()); - if (attrs.hmdInfo()) { - if (!IsSameProcess()) { - NS_WARNING("VR layers currently not supported with cross-process compositing"); - return false; - } - containerLayer->SetVRHMDInfo(reinterpret_cast<mozilla::gfx::VRHMDInfo*>(attrs.hmdInfo())); + if (attrs.hmdDeviceID()) { + containerLayer->SetVRDeviceID(attrs.hmdDeviceID()); } break; } case Specific::TColorLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] color layer")); ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite();
--- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -243,20 +243,17 @@ struct PaintedLayerAttributes { struct ContainerLayerAttributes { float preXScale; float preYScale; float inheritedXScale; float inheritedYScale; float presShellResolution; bool scaleToResolution; EventRegionsOverride eventRegionsOverride; - // This is a bare pointer; LayerTransactionParent::RecvUpdate prevents this - // from being used when !IsSameProcess(), but we should make this truly - // cross process at some point by passing the HMDConfig - uint64_t hmdInfo; + uint32_t hmdDeviceID; }; struct ColorLayerAttributes { LayerColor color; IntRect bounds; }; struct CanvasLayerAttributes { Filter filter; IntRect bounds; }; struct RefLayerAttributes { int64_t id; // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override // explicitly here. EventRegionsOverride eventRegionsOverride;
--- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -102,32 +102,33 @@ CompositorOGL::CreateContext() if (widgetOpenGLContext) { GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext); return already_AddRefed<GLContext>(alreadyRefed); } #ifdef XP_WIN if (gfxEnv::LayersPreferEGL()) { printf_stderr("Trying GL layers...\n"); - context = gl::GLContextProviderEGL::CreateForWindow(mWidget); + context = gl::GLContextProviderEGL::CreateForWindow(mWidget, false); } #endif // Allow to create offscreen GL context for main Layer Manager if (!context && gfxEnv::LayersPreferOffscreen()) { SurfaceCaps caps = SurfaceCaps::ForRGB(); caps.preserve = false; caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == gfxImageFormat::RGB16_565; context = GLContextProvider::CreateOffscreen(mSurfaceSize, caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE); } if (!context) { - context = gl::GLContextProvider::CreateForWindow(mWidget); + context = gl::GLContextProvider::CreateForWindow(mWidget, + gfxPlatform::GetPlatform()->RequiresAcceleratedGLContextForCompositorOGL()); } if (!context) { NS_WARNING("Failed to create CompositorOGL context"); } #ifdef MOZ_WIDGET_GONK mWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT,
--- a/gfx/skia/generate_mozbuild.py +++ b/gfx/skia/generate_mozbuild.py @@ -111,18 +111,16 @@ if CONFIG['GNU_CXX']: ] if CONFIG['CLANG_CXX']: CXXFLAGS += [ '-Wno-implicit-fallthrough', '-Wno-inconsistent-missing-override', '-Wno-macro-redefined', '-Wno-unused-private-field', ] - # work around inline function linking bug with template arguments - SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions'] else: CXXFLAGS += [ '-Wno-logical-op', '-Wno-maybe-uninitialized', ] if CONFIG['CPU_ARCH'] == 'arm': SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
--- a/gfx/skia/moz.build +++ b/gfx/skia/moz.build @@ -722,18 +722,16 @@ if CONFIG['GNU_CXX']: ] if CONFIG['CLANG_CXX']: CXXFLAGS += [ '-Wno-implicit-fallthrough', '-Wno-inconsistent-missing-override', '-Wno-macro-redefined', '-Wno-unused-private-field', ] - # work around inline function linking bug with template arguments - SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions'] else: CXXFLAGS += [ '-Wno-logical-op', '-Wno-maybe-uninitialized', ] if CONFIG['CPU_ARCH'] == 'arm': SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
deleted file mode 100644 --- a/gfx/tests/crashtests/358732-1.xhtml +++ /dev/null @@ -1,22 +0,0 @@ -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<title> -Testcase bug 358732 - Crash in _moz_cairo_win32_scaled_font_select_font, part 2 -</title> -<style> - svg, #div{border: 10px solid black;} -</style> - -</head> -<body> -This should not crash Mozilla within 5 seconds - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="position:absolute;left:0px;top:50px;"><g id="g"> -<foreignObject id="rotate" x="0" y="0" width="800" height="800"> - <iframe xmlns="http://www.w3.org/1999/xhtml" style="width:800px; height: 800px;" src="358732-iframe.html"></iframe> -</foreignObject> -</g></svg> - - - -</body> -</html>
deleted file mode 100644 --- a/gfx/tests/crashtests/358732-2.svg +++ /dev/null @@ -1,94 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:mathml="http://www.w3.org/1998/Math/MathML" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:xforms="http://www.w3.org/2002/xforms">ع<svg:foreignObject x="0" y="0" width="100%" height="100%"></svg:foreignObject> -<html:script><![CDATA[ -/*template*/ -var doc = document; -if (document.getElementById('content')) - doc = document.getElementById('content').contentDocument; - -function addfirstletter(){ -var x=doc.createElementNS('http://www.w3.org/1999/xhtml','style'); -x.innerHTML='\ -*::first-letter {float: right; text-transform: uppercase; background-color:red; font-size:600%;}\ -'; -doc.documentElement.appendChild(x); -} - -setTimeout(addfirstletter,200); - -/*template*/ - -/*template*/ -var doc = document; -if (document.getElementById('content')) - doc = document.getElementById('content').contentDocument; - -function addfirstline(){ -var x=doc.createElementNS('http://www.w3.org/1999/xhtml','style'); -x.innerHTML='\ -*::first-line { text-transform: uppercase; background-color:green; font-size:110%; height: 110%;}\ -*::after { content:"anonymous text"; float:right;border:3px solid black;text-transform: uppercase;height: 90%;}\ -*::before { content:"before text"; float:right;border:3px solid black;font-size: 10px;width:80%;}\ -'; -doc.documentElement.appendChild(x); -} -setTimeout(addfirstline,200); - -/*template*/ -/*template*/ -var doc = document; -if (document.getElementById('content')) - doc = document.getElementById('content').contentDocument; - -var timers=0; -function doe(aObj, aNested, aCurrentTimer){ -var temp =0; -for (var i in aObj) { -try { -if (i == 'ordinal') - continue; -if (typeof aObj[i] == 'object') { - if (aNested >= 1000 || aObj[i] == window.location || aOb[i] == doc.documentElement.boxObject) - continue; - setTimeout(doe,500, aObj[i], ++aNested, timers); - timers++; - if (aOb[i] == doc.documentElement.boxObject.firstChild) { - alert(i); - continue; - }; -} -if (i == 'textContent' || i == 'innerHTML') - continue; -} -catch(e){} -try { - //if (temp == 68 && aNested == 21 && aCurrentTimer >= 116) { - // alert(i + '-'+ aObj[i]); - // return; - // } - -if (typeof aObj[i] == 'function') { -if (i =='removeChild' || i == 'getBoxObjectFor' || i == 'enableRollup') -aObj[i](doc.documentElement); -} -else { - aObj[i]= 'on'; -} - temp+=1; -} -catch (e) { - -} -} -} - -setTimeout(doe,0, doc.documentElement, 300); - -/*template*/ - -]]></html:script> -</svg> \ No newline at end of file
deleted file mode 100644 --- a/gfx/tests/crashtests/358732-3.html +++ /dev/null @@ -1,23 +0,0 @@ -<html> -<head> -<title>Testcase 3 - Bug 358732 – Crash in _moz_cairo_win32_scaled_font_select_font, part 2</title> -<script id="script"> -var i=0; -function doe() { -var navigator1 = SpecialPowers.wrap(top).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation); -var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell); -var docviewer = docShell.contentViewer.QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer); -docviewer.textZoom=i; -i=i+0.2; -if (i>10) - i = 0; -} -setInterval(doe, 50); - -</script> -</head> -<body> -text⊹𒏺뱬𓕢𐡗␣㤫<sup>⊹𒏺뱬𓕢𐡗␣㤫 -text -</body> -</html>
deleted file mode 100644 --- a/gfx/tests/crashtests/358732-iframe.html +++ /dev/null @@ -1,37 +0,0 @@ -<object>᚛᚛᚛᚛᚛᚛᚛᚛᚛᚛ -᚛᚛᚛᚛᚛ -᚛᚛᚛᚛᚛᚛᚛᚛᚛᚛ -᚛᚛᚛᚛ -<i>᚛᚛᚛᚛᚛᚛᚛᚛᚛᚛ - -<script> -var doc = document; -if (document.getElementById('content')) { - doc = document.getElementById('content').contentDocument; -} - -var docviewer; -function do_onload() { -var navigator1 = SpecialPowers.wrap(parent).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation); -var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell); -docviewer = docShell.contentViewer; - -setTimeout(doe,500, 0.2); -} -do_onload(); - - - -function doe(i) { -netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - docviewer.textZoom += i; - -if (docviewer.textZoom >=4) - i = -0.2; - -if (docviewer.textZoom <=0) - i = 0.2; -window.status = docviewer.textZoom; -setTimeout(doe, 100, i); -} -</script>
--- a/gfx/tests/crashtests/crashtests.list +++ b/gfx/tests/crashtests/crashtests.list @@ -7,19 +7,16 @@ load 248518-1.html load 306649-1.xml load 306902-1.xml load 333861-1.html load 334735-1.html load 345576-1.html load 345629-1.html load 348462-1.html load 348462-2.html -skip load 358732-1.xhtml # Bug 1226751 -load 358732-2.svg -load 358732-3.html load 366643.html load 369688-1.html load 369947-1.html load 372094-1.xhtml load 376627-1.html load 377231-1.html load 377232-1.xhtml load 377461-1.xhtml
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -14,17 +14,16 @@ #include "mozilla/Logging.h" #include "mozilla/Services.h" #include "prprf.h" #include "gfxPlatform.h" #include "gfxPrefs.h" #include "gfxEnv.h" #include "gfxTextRun.h" -#include "gfxVR.h" #ifdef XP_WIN #include <process.h> #define getpid _getpid #else #include <unistd.h> #endif @@ -122,16 +121,18 @@ class mozilla::gl::SkiaGLGlue : public G #include "nsAlgorithm.h" #include "nsIGfxInfo.h" #include "nsIXULRuntime.h" #include "VsyncSource.h" #include "SoftwareVsyncSource.h" #include "nscore.h" // for NS_FREE_PERMANENT_DATA #include "mozilla/dom/ContentChild.h" +#include "gfxVR.h" +#include "VRManagerChild.h" namespace mozilla { namespace layers { #ifdef MOZ_WIDGET_GONK void InitGralloc(); #endif void ShutdownTileCache(); } // namespace layers @@ -440,18 +441,17 @@ gfxPlatform::gfxPlatform() mSkiaGlue = nullptr; uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA); uint32_t contentMask = BackendTypeBit(BackendType::CAIRO); InitBackendPrefs(canvasMask, BackendType::CAIRO, contentMask, BackendType::CAIRO); mTotalSystemMemory = mozilla::hal::GetTotalSystemMemory(); - // give HMDs a chance to be initialized very early on - VRHMDManager::ManagerInit(); + VRManager::ManagerInit(); } gfxPlatform* gfxPlatform::GetPlatform() { if (!gPlatform) { Init(); } @@ -730,48 +730,49 @@ gfxPlatform::InitLayersIPC() if (XRE_IsParentProcess()) { mozilla::layers::CompositorParent::StartUp(); #ifdef MOZ_WIDGET_GONK SharedBufferManagerChild::StartUp(); #endif mozilla::layers::ImageBridgeChild::StartUp(); + gfx::VRManagerChild::StartUpSameProcess(); } } /* static */ void gfxPlatform::ShutdownLayersIPC() { if (!sLayersIPCIsUp) { return; } sLayersIPCIsUp = false; if (XRE_IsParentProcess()) { // This must happen after the shutdown of media and widgets, which // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification. + gfx::VRManagerChild::ShutDown(); layers::ImageBridgeChild::ShutDown(); #ifdef MOZ_WIDGET_GONK layers::SharedBufferManagerChild::ShutDown(); #endif layers::CompositorParent::ShutDown(); - } + } else if (XRE_GetProcessType() == GeckoProcessType_Content) { + gfx::VRManagerChild::ShutDown(); + } } gfxPlatform::~gfxPlatform() { mScreenReferenceSurface = nullptr; mScreenReferenceDrawTarget = nullptr; - // Clean up any VR stuff - VRHMDManager::ManagerDestroy(); - // The cairo folks think we should only clean up in debug builds, // but we're generally in the habit of trying to shut down as // cleanly as possible even in production code, so call this // cairo_debug_* function unconditionally. // // because cairo can assert and thus crash on shutdown, don't do this in release builds #ifdef NS_FREE_PERMANENT_DATA #ifdef USE_SKIA @@ -2241,19 +2242,17 @@ gfxPlatform::GetAcceleratedCompositorBac } void gfxPlatform::GetCompositorBackends(bool useAcceleration, nsTArray<mozilla::layers::LayersBackend>& aBackends) { if (useAcceleration) { GetAcceleratedCompositorBackends(aBackends); } - if (SupportsBasicCompositor()) { - aBackends.AppendElement(LayersBackend::LAYERS_BASIC); - } + aBackends.AppendElement(LayersBackend::LAYERS_BASIC); } void gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend) { if (mCompositorBackend == aBackend) { return; }
--- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -127,19 +127,25 @@ enum class DeviceResetReason { OK = 0, HUNG, REMOVED, RESET, DRIVER_ERROR, INVALID_CALL, OUT_OF_MEMORY, + FORCED_RESET, UNKNOWN }; +enum class ForcedDeviceResetReason +{ + OPENSHAREDHANDLE = 0 +}; + class gfxPlatform { friend class SRGBOverrideObserver; public: typedef mozilla::gfx::Color Color; typedef mozilla::gfx::DataSourceSurface DataSourceSurface; typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::IntSize IntSize; @@ -639,36 +645,37 @@ public: // devices. Currently this is only used on Windows. virtual void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut); // Plugin async drawing support. virtual bool SupportsPluginDirectBitmapDrawing() { return false; } + // Some platforms don't support CompositorOGL in an unaccelerated OpenGL + // context. These platforms should return true here. + virtual bool RequiresAcceleratedGLContextForCompositorOGL() const { + return false; + } + protected: gfxPlatform(); virtual ~gfxPlatform(); /** * Initialized hardware vsync based on each platform. */ virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource(); // Returns whether or not layers should be accelerated by default on this platform. virtual bool AccelerateLayersByDefault(); // Returns a prioritized list of available compositor backends for acceleration. virtual void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends); - // Returns whether or not the basic compositor is supported. - virtual bool SupportsBasicCompositor() const { - return true; - } - /** * Initialise the preferred and fallback canvas backends * aBackendBitmask specifies the backends which are acceptable to the caller. * The backend used is determined by aBackendBitmask and the order specified * by the gfx.canvas.azure.backends pref. */ void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault, uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault);
--- a/gfx/thebes/gfxPlatformMac.h +++ b/gfx/thebes/gfxPlatformMac.h @@ -78,19 +78,21 @@ public: virtual bool CanRenderContentToDataSurface() const override { return true; } virtual bool SupportsApzWheelInput() const override { return true; } - bool SupportsBasicCompositor() const override { - // At the moment, BasicCompositor is broken on mac. - return false; + bool RequiresAcceleratedGLContextForCompositorOGL() const override { + // On OS X in a VM, unaccelerated CompositorOGL shows black flashes, so we + // require accelerated GL for CompositorOGL but allow unaccelerated GL for + // BasicCompositor. + return true; } bool UseAcceleratedCanvas(); virtual bool UseProgressivePaint() override; virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override; // lower threshold on font anti-aliasing
--- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -550,16 +550,25 @@ gfxWindowsPlatform::UpdateRenderMode() UpdateBackendPrefs(); if (didReset) { mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8); } } +void +gfxWindowsPlatform::ForceDeviceReset(ForcedDeviceResetReason aReason) +{ + Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason)); + + mDeviceResetReason = DeviceResetReason::FORCED_RESET; + mHasDeviceReset = true; +} + mozilla::gfx::BackendType gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers) { if (aLayers == LayersBackend::LAYERS_D3D11) { return gfxPlatform::GetDefaultContentBackend(); } // If we're not accelerated with D3D11, never use D2D.
--- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -145,16 +145,21 @@ public: /** * Updates render mode with relation to the current preferences and * available devices. */ void UpdateRenderMode(); /** + * Forces all GPU resources to be recreated on the next frame. + */ + void ForceDeviceReset(ForcedDeviceResetReason aReason); + + /** * Verifies a D2D device is present and working, will attempt to create one * it is non-functional or non-existant. * * \param aAttemptForce Attempt to force D2D cairo device creation by using * cairo device creation routines. */ void VerifyD2DDevice(bool aAttemptForce);
new file mode 100644 --- /dev/null +++ b/gfx/vr/VRDeviceProxy.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 20; 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 <math.h> + +#include "prlink.h" +#include "prmem.h" +#include "prenv.h" +#include "gfxPrefs.h" +#include "nsString.h" +#include "mozilla/Preferences.h" +#include "mozilla/unused.h" +#include "nsServiceManagerUtils.h" +#include "nsIScreenManager.h" + + +#ifdef XP_WIN +#include "../layers/d3d11/CompositorD3D11.h" +#endif + +#include "VRDeviceProxy.h" +#include "VRManagerChild.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +VRDeviceProxy::VRDeviceProxy(const VRDeviceUpdate& aDeviceUpdate) + : mDeviceInfo(aDeviceUpdate.mDeviceInfo) + , mSensorState(aDeviceUpdate.mSensorState) +{ + MOZ_COUNT_CTOR(VRDeviceProxy); + + if (mDeviceInfo.mScreenRect.width && mDeviceInfo.mScreenRect.height) { + if (mDeviceInfo.mIsFakeScreen) { + mScreen = MakeFakeScreen(mDeviceInfo.mScreenRect); + } else { + nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); + if (screenmgr) { + screenmgr->ScreenForRect(mDeviceInfo.mScreenRect.x, mDeviceInfo.mScreenRect.y, + mDeviceInfo.mScreenRect.width, mDeviceInfo.mScreenRect.height, + getter_AddRefs(mScreen)); + } + } +#ifdef DEBUG + printf_stderr("VR DEVICE SCREEN: %d %d %d %d\n", + mDeviceInfo.mScreenRect.x, mDeviceInfo.mScreenRect.y, + mDeviceInfo.mScreenRect.width, mDeviceInfo.mScreenRect.height); +#endif + } +} + +VRDeviceProxy::~VRDeviceProxy() { + MOZ_COUNT_DTOR(VRDeviceProxy); +} + +void +VRDeviceProxy::UpdateDeviceInfo(const VRDeviceUpdate& aDeviceUpdate) +{ + mDeviceInfo = aDeviceUpdate.mDeviceInfo; + mSensorState = aDeviceUpdate.mSensorState; +} + +bool +VRDeviceProxy::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, + double zNear, double zFar) +{ + VRManagerChild *vm = VRManagerChild::Get(); + vm->SendSetFOV(mDeviceInfo.mDeviceID, aFOVLeft, aFOVRight, zNear, zFar); + return true; +} + +void +VRDeviceProxy::ZeroSensor() +{ + VRManagerChild *vm = VRManagerChild::Get(); + vm->SendResetSensor(mDeviceInfo.mDeviceID); +} + +VRHMDSensorState +VRDeviceProxy::GetSensorState(double timeOffset) +{ + VRManagerChild *vm = VRManagerChild::Get(); + Unused << vm->SendKeepSensorTracking(mDeviceInfo.mDeviceID); + return mSensorState; +} + +void +VRDeviceProxy::UpdateSensorState(const VRHMDSensorState& aSensorState) +{ + mSensorState = aSensorState; +} + +// Dummy nsIScreen implementation, for when we just need to specify a size +class FakeScreen : public nsIScreen +{ +public: + explicit FakeScreen(const IntRect& aScreenRect) + : mScreenRect(aScreenRect) + { } + + NS_DECL_ISUPPORTS + + NS_IMETHOD GetRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { + *l = mScreenRect.x; + *t = mScreenRect.y; + *w = mScreenRect.width; + *h = mScreenRect.height; + return NS_OK; + } + NS_IMETHOD GetAvailRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { + return GetRect(l, t, w, h); + } + NS_IMETHOD GetRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { + return GetRect(l, t, w, h); + } + NS_IMETHOD GetAvailRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { + return GetAvailRect(l, t, w, h); + } + + NS_IMETHOD GetId(uint32_t* aId) override { *aId = (uint32_t)-1; return NS_OK; } + NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override { *aPixelDepth = 24; return NS_OK; } + NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override { *aColorDepth = 24; return NS_OK; } + + NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; } + NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; } + NS_IMETHOD GetRotation(uint32_t* aRotation) override { + *aRotation = nsIScreen::ROTATION_0_DEG; + return NS_OK; + } + NS_IMETHOD SetRotation(uint32_t aRotation) override { return NS_ERROR_NOT_AVAILABLE; } + NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override { + *aContentsScaleFactor = 1.0; + return NS_OK; + } + +protected: + virtual ~FakeScreen() {} + + IntRect mScreenRect; +}; + +NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen) + + +/* static */ already_AddRefed<nsIScreen> +VRDeviceProxy::MakeFakeScreen(const IntRect& aScreenRect) +{ + nsCOMPtr<nsIScreen> screen = new FakeScreen(aScreenRect); + return screen.forget(); +} +
new file mode 100644 --- /dev/null +++ b/gfx/vr/VRDeviceProxy.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#ifndef GFX_VR_PROXY_H +#define GFX_VR_PROXY_H + +#include "nsIScreen.h" +#include "nsCOMPtr.h" +#include "mozilla/RefPtr.h" + +#include "gfxVR.h" + +namespace mozilla { +namespace gfx { + +class VRManagerChild; + +class VRDeviceProxy +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRDeviceProxy) + + explicit VRDeviceProxy(const VRDeviceUpdate& aDeviceUpdate); + + void UpdateDeviceInfo(const VRDeviceUpdate& aDeviceUpdate); + void UpdateSensorState(const VRHMDSensorState& aSensorState); + + const VRDeviceInfo& GetDeviceInfo() const { return mDeviceInfo; } + virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0); + + bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, + double zNear, double zFar); + + virtual void ZeroSensor(); + + + // The nsIScreen that represents this device + nsIScreen* GetScreen() { return mScreen; } + +protected: + virtual ~VRDeviceProxy(); + + VRDeviceInfo mDeviceInfo; + VRHMDSensorState mSensorState; + + nsCOMPtr<nsIScreen> mScreen; + + static already_AddRefed<nsIScreen> MakeFakeScreen(const IntRect& aScreenRect); + +}; + +} // namespace gfx +} // namespace mozilla + +#endif /* GFX_VR_PROXY_H */
new file mode 100644 --- /dev/null +++ b/gfx/vr/VRDeviceProxyOrientationFallBack.cpp @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 20; 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 <math.h> +#include "VRDeviceProxyOrientationFallBack.h" +#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientationInternal +#include "mozilla/Hal.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +// 1/sqrt(2) (aka sqrt(2)/2) +#ifndef M_SQRT1_2 +# define M_SQRT1_2 0.70710678118654752440 +#endif + +#ifdef ANDROID +#include <android/log.h> +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoVR" , ## args) +#else +#define LOG(...) do { } while(0) +#endif + +namespace { +// some utility functions + +// This remaps axes in the given matrix to a new configuration based on the +// screen orientation. Similar to what Android SensorManager.remapCoordinateSystem +// does, except only for a fixed number of transforms that we need. +Matrix4x4 +RemapMatrixForOrientation(dom::ScreenOrientationInternal screenConfig, const Matrix4x4& aMatrix) +{ + Matrix4x4 out; + const float *in = &aMatrix._11; + float *o = &out._11; + + if (screenConfig == dom::eScreenOrientation_LandscapePrimary) { + // remap X,Y -> Y,-X + o[0] = -in[1]; o[1] = in[0]; o[2] = in[2]; + o[4] = -in[5]; o[5] = in[4]; o[6] = in[6]; + o[8] = -in[9]; o[9] = in[8]; o[10] = in[10]; + } else if (screenConfig == dom::eScreenOrientation_LandscapeSecondary) { + // remap X,Y -> -Y,X + o[0] = in[1]; o[1] = -in[0]; o[2] = in[2]; + o[4] = in[5]; o[5] = -in[4]; o[6] = in[6]; + o[8] = in[9]; o[9] = -in[8]; o[10] = in[10]; + } else if (screenConfig == dom::eScreenOrientation_PortraitPrimary) { + out = aMatrix; + } else if (screenConfig == dom::eScreenOrientation_PortraitSecondary) { + // remap X,Y -> X,-Z + o[0] = in[0]; o[1] = in[2]; o[2] = -in[1]; + o[4] = in[4]; o[5] = in[6]; o[6] = -in[5]; + o[8] = in[8]; o[9] = in[10]; o[10] = -in[9]; + } else { + MOZ_ASSERT(0, "gfxVRCardboard::RemapMatrixForOrientation invalid screenConfig"); + } + + return out; +} + +} // namespace + +namespace mozilla { +namespace gfx { + +VRDeviceProxyOrientationFallBack::VRDeviceProxyOrientationFallBack(const VRDeviceUpdate& aDeviceUpdate) + : VRDeviceProxy(aDeviceUpdate) + , mOrient(dom::eScreenOrientation_PortraitPrimary) + , mTracking(false) +{ + MOZ_COUNT_CTOR_INHERITED(VRDeviceProxyOrientationFallBack, VRDeviceProxy); +} + +VRDeviceProxyOrientationFallBack::~VRDeviceProxyOrientationFallBack() +{ + StopSensorTracking(); + MOZ_COUNT_DTOR_INHERITED(VRDeviceProxyOrientationFallBack, VRDeviceProxy); +} + +void +VRDeviceProxyOrientationFallBack::StartSensorTracking() +{ + if (!mTracking) { + // it's never been started before; initialize observers and + // initial state. + + hal::ScreenConfiguration sconfig; + hal::GetCurrentScreenConfiguration(&sconfig); + this->Notify(sconfig); + + hal::RegisterSensorObserver(hal::SENSOR_GAME_ROTATION_VECTOR, this); + hal::RegisterScreenConfigurationObserver(this); + + mSensorState.Clear(); + + mTracking = true; + } +} + +void +VRDeviceProxyOrientationFallBack::StopSensorTracking() +{ + if (mTracking) { + hal::UnregisterScreenConfigurationObserver(this); + hal::UnregisterSensorObserver(hal::SENSOR_GAME_ROTATION_VECTOR, this); + mTracking = false; + } +} + +// Android sends us events that have a 90-degree rotation about +// the x axis compared to what we want (phone flat vs. phone held in front of the eyes). +// Correct for this by applying a transform to undo this rotation. +void +VRDeviceProxyOrientationFallBack::Notify(const hal::ScreenConfiguration& config) +{ + mOrient = config.orientation(); + + if (mOrient == dom::eScreenOrientation_LandscapePrimary) { + mScreenTransform = Quaternion(-0.5f, 0.5f, 0.5f, 0.5f); + } else if (mOrient == dom::eScreenOrientation_LandscapeSecondary) { + mScreenTransform = Quaternion(-0.5f, -0.5f, -0.5f, 0.5f); + } else if (mOrient == dom::eScreenOrientation_PortraitPrimary) { + mScreenTransform = Quaternion((float) -M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2); + } else if (mOrient == dom::eScreenOrientation_PortraitSecondary) { + // Currently, PortraitSecondary event doesn't be triggered. + mScreenTransform = Quaternion((float) M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2); + } +} + +void +VRDeviceProxyOrientationFallBack::Notify(const hal::SensorData& data) +{ + if (data.sensor() != hal::SENSOR_GAME_ROTATION_VECTOR) + return; + + const nsTArray<float>& sensorValues = data.values(); + + // This is super chatty + //LOG("HMDInfoCardboard::Notify %f %f %f %f\n", sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]); + + mSavedLastSensor.Set(sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]); + mSavedLastSensorTime = data.timestamp(); + mNeedsSensorCompute = true; +} + +void +VRDeviceProxyOrientationFallBack::ZeroSensor() +{ + mSensorZeroInverse = mSavedLastSensor; + mSensorZeroInverse.Invert(); +} + +void +VRDeviceProxyOrientationFallBack::ComputeStateFromLastSensor() +{ + if (!mNeedsSensorCompute) + return; + + // apply the zero orientation + Quaternion q = mSensorZeroInverse * mSavedLastSensor; + + // make a matrix from the quat + Matrix4x4 qm; + qm.SetRotationFromQuaternion(q); + + // remap the coordinate space, based on the orientation + Matrix4x4 qmRemapped = RemapMatrixForOrientation(mOrient, qm); + + // turn it back into a quat + q.SetFromRotationMatrix(qmRemapped); + + // apply adjustment based on what's been done to the screen and the original zero + // position of the base coordinate space + q = mScreenTransform * q; + + mSensorState.flags |= VRStateValidFlags::State_Orientation; + mSensorState.orientation[0] = q.x; + mSensorState.orientation[1] = q.y; + mSensorState.orientation[2] = q.z; + mSensorState.orientation[3] = q.w; + + mSensorState.timestamp = mSavedLastSensorTime / 1000000.0; + + mNeedsSensorCompute = false; +} + +VRHMDSensorState +VRDeviceProxyOrientationFallBack::GetSensorState(double timeOffset) +{ + StartSensorTracking(); + ComputeStateFromLastSensor(); + return mSensorState; +} + +} // namespace gfx +} // namespace mozilla +
new file mode 100644 --- /dev/null +++ b/gfx/vr/VRDeviceProxyOrientationFallBack.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#ifndef GFX_VR_PROXY_ORIENTATION_FALLBACK_H +#define GFX_VR_PROXY_ORIENTATION_FALLBACK_H + +#include "VRDeviceProxy.h" + +#include "mozilla/HalSensor.h" +#include "mozilla/HalScreenConfiguration.h" + +namespace mozilla { +namespace gfx { + +class VRDeviceProxyOrientationFallBack : public VRDeviceProxy + , public hal::ISensorObserver + , public hal::ScreenConfigurationObserver +{ +public: + + explicit VRDeviceProxyOrientationFallBack(const VRDeviceUpdate& aDeviceUpdate); + + virtual void ZeroSensor() override; + virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) override; + + // ISensorObserver interface + void Notify(const hal::SensorData& SensorData) override; + // ScreenConfigurationObserver interface + void Notify(const hal::ScreenConfiguration& ScreenConfiguration) override; + + +protected: + virtual ~VRDeviceProxyOrientationFallBack(); + + void StartSensorTracking(); + void StopSensorTracking(); + void ComputeStateFromLastSensor(); + + uint32_t mOrient; + Quaternion mScreenTransform; + Quaternion mSensorZeroInverse; + Quaternion mSavedLastSensor; + double mSavedLastSensorTime; + bool mNeedsSensorCompute; // if we need to compute the state from mSavedLastSensor + + bool mTracking; +}; + +} // namespace gfx +} // namespace mozilla + +#endif /* GFX_VR_PROXY_ORIENTATION_FALLBACK_H */ \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/VRManager.cpp @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 20; 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 "VRManager.h" +#include "VRManagerParent.h" +#include "gfxVR.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/VRDevice.h" +#include "mozilla/unused.h" + +#include "gfxPrefs.h" +#include "gfxVR.h" +#if defined(XP_WIN) +#include "gfxVROculus.h" +#endif +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) +#include "gfxVROculus050.h" +#endif +#include "gfxVRCardboard.h" + + +namespace mozilla { +namespace gfx { + +static StaticRefPtr<VRManager> sVRManagerSingleton; + +/*static*/ void +VRManager::ManagerInit() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sVRManagerSingleton == nullptr) { + sVRManagerSingleton = new VRManager(); + ClearOnShutdown(&sVRManagerSingleton); + } +} + +VRManager::VRManager() + : mInitialized(false) +{ + MOZ_COUNT_CTOR(VRManager); + MOZ_ASSERT(sVRManagerSingleton == nullptr); + + RefPtr<VRHMDManager> mgr; + + // we'll only load the 0.5.0 oculus runtime if + // the >= 0.6.0 one failed to load; otherwise + // we might initialize oculus twice + bool useOculus050 = true; + Unused << useOculus050; + +#if defined(XP_WIN) + mgr = VRHMDManagerOculus::Create(); + if (mgr) { + useOculus050 = false; + mManagers.AppendElement(mgr); + } +#endif + +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) + if (useOculus050) { + mgr = VRHMDManagerOculus050::Create(); + if (mgr) { + mManagers.AppendElement(mgr); + } + } +#endif + + mgr = VRHMDManagerCardboard::Create(); + if (mgr) { + mManagers.AppendElement(mgr); + } +} + +VRManager::~VRManager() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mInitialized); + MOZ_COUNT_DTOR(VRManager); +} + +void +VRManager::Destroy() +{ + for (uint32_t i = 0; i < mManagers.Length(); ++i) { + mManagers[i]->Destroy(); + } + mInitialized = false; +} + +void +VRManager::Init() +{ + for (uint32_t i = 0; i < mManagers.Length(); ++i) { + mManagers[i]->Init(); + } + mInitialized = true; +} + +/* static */VRManager* +VRManager::Get() +{ + MOZ_ASSERT(sVRManagerSingleton != nullptr); + + return sVRManagerSingleton; +} + +void +VRManager::AddVRManagerParent(VRManagerParent* aVRManagerParent) +{ + if (mVRManagerParents.IsEmpty()) { + Init(); + } + mVRManagerParents.PutEntry(aVRManagerParent); +} + +void +VRManager::RemoveVRManagerParent(VRManagerParent* aVRManagerParent) +{ + mVRManagerParents.RemoveEntry(aVRManagerParent); + if (mVRManagerParents.IsEmpty()) { + Destroy(); + } +} + +void +VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp) +{ + for (auto iter = mVRDevices.Iter(); !iter.Done(); iter.Next()) { + gfx::VRHMDInfo* device = iter.UserData(); + device->NotifyVsync(aVsyncTimestamp); + } + DispatchVRDeviceSensorUpdate(); +} + +void +VRManager::RefreshVRDevices() +{ + nsTArray<RefPtr<gfx::VRHMDInfo> > devices; + + for (uint32_t i = 0; i < mManagers.Length(); ++i) { + mManagers[i]->GetHMDs(devices); + } + + bool deviceInfoChanged = false; + + if (devices.Length() != mVRDevices.Count()) { + deviceInfoChanged = true; + } + + for (const auto& device: devices) { + RefPtr<VRHMDInfo> oldDevice = GetDevice(device->GetDeviceInfo().GetDeviceID()); + if (oldDevice == nullptr) { + deviceInfoChanged = true; + break; + } + if (oldDevice->GetDeviceInfo() != device->GetDeviceInfo()) { + deviceInfoChanged = true; + break; + } + } + + if (deviceInfoChanged) { + mVRDevices.Clear(); + for (const auto& device: devices) { + mVRDevices.Put(device->GetDeviceInfo().GetDeviceID(), device); + } + } + + DispatchVRDeviceInfoUpdate(); +} + +void +VRManager::DispatchVRDeviceInfoUpdate() +{ + nsTArray<VRDeviceUpdate> update; + for (auto iter = mVRDevices.Iter(); !iter.Done(); iter.Next()) { + gfx::VRHMDInfo* device = iter.UserData(); + update.AppendElement(VRDeviceUpdate(device->GetDeviceInfo(), + device->GetSensorState())); + } + + for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) { + Unused << iter.Get()->GetKey()->SendUpdateDeviceInfo(update); + } +} + +void +VRManager::DispatchVRDeviceSensorUpdate() +{ + nsTArray<VRSensorUpdate> update; + + for (auto iter = mVRDevices.Iter(); !iter.Done(); iter.Next()) { + gfx::VRHMDInfo* device = iter.UserData(); + if (!device->GetDeviceInfo().GetUseMainThreadOrientation()) { + update.AppendElement(VRSensorUpdate(device->GetDeviceInfo().GetDeviceID(), + device->GetSensorState())); + } + } + if (update.Length() > 0) { + for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) { + Unused << iter.Get()->GetKey()->SendUpdateDeviceSensors(update); + } + } +} + +RefPtr<gfx::VRHMDInfo> +VRManager::GetDevice(const uint32_t& aDeviceID) +{ + RefPtr<gfx::VRHMDInfo> device; + if (mVRDevices.Get(aDeviceID, getter_AddRefs(device))) { + return device; + } + return nullptr; +} + +} // namespace gfx +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/gfx/vr/VRManager.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +#ifndef GFX_VR_MANAGER_H +#define GFX_VR_MANAGER_H + +#include "nsAutoPtr.h" +#include "nsRefPtrHashtable.h" +#include "nsTArray.h" +#include "nsTHashtable.h" +#include "mozilla/TimeStamp.h" +#include "gfxVR.h" + +namespace mozilla { +namespace gfx { + +class VRManagerParent; +class VRHMDInfo; + +class VRManager +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRManager) + +public: + static void ManagerInit(); + static VRManager* Get(); + + void AddVRManagerParent(VRManagerParent* aVRManagerParent); + void RemoveVRManagerParent(VRManagerParent* aVRManagerParent); + + void NotifyVsync(const TimeStamp& aVsyncTimestamp); + void RefreshVRDevices(); + RefPtr<gfx::VRHMDInfo> GetDevice(const uint32_t& aDeviceID); + +protected: + VRManager(); + ~VRManager(); + +private: + + void Init(); + void Destroy(); + + void DispatchVRDeviceInfoUpdate(); + void DispatchVRDeviceSensorUpdate(); + + typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet; + VRManagerParentSet mVRManagerParents; + + typedef nsTArray<RefPtr<VRHMDManager>> VRHMDManagerArray; + VRHMDManagerArray mManagers; + + typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRHMDInfo> VRHMDInfoHashMap; + VRHMDInfoHashMap mVRDevices; + + Atomic<bool> mInitialized; + +}; + +} // namespace gfx +} // namespace mozilla + +#endif // GFX_VR_MANAGER_H
--- a/gfx/vr/gfxVR.cpp +++ b/gfx/vr/gfxVR.cpp @@ -15,169 +15,48 @@ #if defined(XP_WIN) #include "gfxVROculus.h" #endif #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) #include "gfxVROculus050.h" #endif #include "gfxVRCardboard.h" -#include "nsServiceManagerUtils.h" -#include "nsIScreenManager.h" - #include "mozilla/unused.h" #include "mozilla/layers/Compositor.h" #include "mozilla/layers/TextureHost.h" #ifndef M_PI # define M_PI 3.14159265358979323846 #endif using namespace mozilla; using namespace mozilla::gfx; -// Dummy nsIScreen implementation, for when we just need to specify a size -class FakeScreen : public nsIScreen -{ -public: - explicit FakeScreen(const IntRect& aScreenRect) - : mScreenRect(aScreenRect) - { } - - NS_DECL_ISUPPORTS - - NS_IMETHOD GetRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { - *l = mScreenRect.x; - *t = mScreenRect.y; - *w = mScreenRect.width; - *h = mScreenRect.height; - return NS_OK; - } - NS_IMETHOD GetAvailRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { - return GetRect(l, t, w, h); - } - NS_IMETHOD GetRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { - return GetRect(l, t, w, h); - } - NS_IMETHOD GetAvailRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override { - return GetAvailRect(l, t, w, h); - } +Atomic<uint32_t> VRHMDManager::sDeviceBase(0); - NS_IMETHOD GetId(uint32_t* aId) override { *aId = (uint32_t)-1; return NS_OK; } - NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override { *aPixelDepth = 24; return NS_OK; } - NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override { *aColorDepth = 24; return NS_OK; } - - NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; } - NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; } - NS_IMETHOD GetRotation(uint32_t* aRotation) override { - *aRotation = nsIScreen::ROTATION_0_DEG; - return NS_OK; - } - NS_IMETHOD SetRotation(uint32_t aRotation) override { return NS_ERROR_NOT_AVAILABLE; } - NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override { - *aContentsScaleFactor = 1.0; - return NS_OK; - } - -protected: - virtual ~FakeScreen() {} - - IntRect mScreenRect; -}; - -NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen) - -VRHMDInfo::VRHMDInfo(VRHMDType aType) - : mType(aType) +VRHMDInfo::VRHMDInfo(VRHMDType aType, bool aUseMainThreadOrientation) { MOZ_COUNT_CTOR(VRHMDInfo); - - mDeviceIndex = VRHMDManager::AllocateDeviceIndex(); - mDeviceName.AssignLiteral("Unknown Device"); + mDeviceInfo.mType = aType; + mDeviceInfo.mDeviceID = VRHMDManager::AllocateDeviceID(); + mDeviceInfo.mUseMainThreadOrientation = aUseMainThreadOrientation; } - -VRHMDManager::VRHMDManagerArray *VRHMDManager::sManagers = nullptr; -Atomic<uint32_t> VRHMDManager::sDeviceBase(0); - -/* static */ void -VRHMDManager::ManagerInit() +VRHMDInfo::~VRHMDInfo() { - if (sManagers) - return; - - sManagers = new VRHMDManagerArray(); - - RefPtr<VRHMDManager> mgr; - - // we'll only load the 0.5.0 oculus runtime if - // the >= 0.6.0 one failed to load; otherwise - // we might initialize oculus twice - bool useOculus050 = true; - Unused << useOculus050; - -#if defined(XP_WIN) - mgr = new VRHMDManagerOculus(); - if (mgr->PlatformInit()) { - useOculus050 = false; - sManagers->AppendElement(mgr); - } -#endif - -#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) - if (useOculus050) { - mgr = new VRHMDManagerOculus050(); - if (mgr->PlatformInit()) - sManagers->AppendElement(mgr); - } -#endif - - mgr = new VRHMDManagerCardboard(); - if (mgr->PlatformInit()) - sManagers->AppendElement(mgr); -} - -/* static */ void -VRHMDManager::ManagerDestroy() -{ - if (!sManagers) - return; - - for (uint32_t i = 0; i < sManagers->Length(); ++i) { - (*sManagers)[i]->Destroy(); - } - - delete sManagers; - sManagers = nullptr; -} - -/* static */ void -VRHMDManager::GetAllHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) -{ - if (!sManagers) - return; - - for (uint32_t i = 0; i < sManagers->Length(); ++i) { - (*sManagers)[i]->GetHMDs(aHMDResult); - } + MOZ_COUNT_DTOR(VRHMDInfo); } /* static */ uint32_t -VRHMDManager::AllocateDeviceIndex() +VRHMDManager::AllocateDeviceID() { return ++sDeviceBase; } -/* static */ already_AddRefed<nsIScreen> -VRHMDManager::MakeFakeScreen(int32_t x, int32_t y, uint32_t width, uint32_t height) -{ - nsCOMPtr<nsIScreen> screen = new FakeScreen(IntRect(x, y, width, height)); - return screen.forget(); -} - VRHMDRenderingSupport::RenderTargetSet::RenderTargetSet() : currentRenderTarget(0) { } VRHMDRenderingSupport::RenderTargetSet::~RenderTargetSet() { }
--- a/gfx/vr/gfxVR.h +++ b/gfx/vr/gfxVR.h @@ -2,40 +2,50 @@ * 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/. */ #ifndef GFX_VR_H #define GFX_VR_H #include "nsTArray.h" -#include "nsIScreen.h" #include "nsString.h" #include "nsCOMPtr.h" #include "mozilla/RefPtr.h" #include "mozilla/gfx/2D.h" +#include "mozilla/Atomics.h" #include "mozilla/EnumeratedArray.h" -#include "mozilla/Atomics.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/TypedEnumBits.h" namespace mozilla { namespace layers { class Compositor; class CompositingRenderTarget; } namespace gfx { enum class VRHMDType : uint16_t { Oculus, Cardboard, Oculus050, NumHMDTypes }; +enum class VRStateValidFlags : uint16_t { + State_None = 0, + State_Position = 1 << 1, + State_Orientation = 1 << 2, + // State_All used for validity checking during IPC serialization + State_All = (1 << 3) - 1 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRStateValidFlags) + struct VRFieldOfView { VRFieldOfView() {} VRFieldOfView(double up, double right, double down, double left) : upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left) {} bool operator==(const VRFieldOfView& other) const { return other.upDegrees == upDegrees && @@ -58,47 +68,138 @@ struct VRFieldOfView { Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar, bool rightHanded); double upDegrees; double rightDegrees; double downDegrees; double leftDegrees; }; -// 12 floats per vertex. Position, tex coordinates -// for each channel, and 4 generic attributes -struct VRDistortionConstants { - float eyeToSourceScaleAndOffset[4]; - float destinationScaleAndOffset[4]; -}; - struct VRDistortionVertex { float values[12]; }; struct VRDistortionMesh { nsTArray<VRDistortionVertex> mVertices; nsTArray<uint16_t> mIndices; }; +// 12 floats per vertex. Position, tex coordinates +// for each channel, and 4 generic attributes +struct VRDistortionConstants { + float eyeToSourceScaleAndOffset[4]; + float destinationScaleAndOffset[4]; +}; + +struct VRDeviceInfo +{ + VRHMDType GetType() const { return mType; } + uint32_t GetDeviceID() const { return mDeviceID; } + const nsCString& GetDeviceName() const { return mDeviceName; } + VRStateValidFlags GetSupportedSensorStateBits() const { return mSupportedSensorBits; } + const VRFieldOfView& GetRecommendedEyeFOV(uint32_t whichEye) const { return mRecommendedEyeFOV[whichEye]; } + const VRFieldOfView& GetMaximumEyeFOV(uint32_t whichEye) const { return mMaximumEyeFOV[whichEye]; } + + const IntSize& SuggestedEyeResolution() const { return mEyeResolution; } + const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; } + const Matrix4x4& GetEyeProjectionMatrix(uint32_t whichEye) const { return mEyeProjectionMatrix[whichEye]; } + const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const { return mEyeFOV[whichEye]; } + bool GetUseMainThreadOrientation() const { return mUseMainThreadOrientation; } + + enum Eye { + Eye_Left, + Eye_Right, + NumEyes + }; + + uint32_t mDeviceID; + VRHMDType mType; + nsCString mDeviceName; + VRStateValidFlags mSupportedSensorBits; + VRFieldOfView mMaximumEyeFOV[VRDeviceInfo::NumEyes]; + VRFieldOfView mRecommendedEyeFOV[VRDeviceInfo::NumEyes]; + VRFieldOfView mEyeFOV[VRDeviceInfo::NumEyes]; + Point3D mEyeTranslation[VRDeviceInfo::NumEyes]; + Matrix4x4 mEyeProjectionMatrix[VRDeviceInfo::NumEyes]; + /* Suggested resolution for rendering a single eye. + * Assumption is that left/right rendering will be 2x of this size. + * XXX fix this for vertical displays + */ + IntSize mEyeResolution; + IntRect mScreenRect; + + bool mIsFakeScreen; + bool mUseMainThreadOrientation; + + + + bool operator==(const VRDeviceInfo& other) const { + return mType == other.mType && + mDeviceID == other.mDeviceID && + mDeviceName == other.mDeviceName && + mSupportedSensorBits == other.mSupportedSensorBits && + mEyeResolution == other.mEyeResolution && + mScreenRect == other.mScreenRect && + mIsFakeScreen == other.mIsFakeScreen && + mUseMainThreadOrientation == other.mUseMainThreadOrientation && + mMaximumEyeFOV[0] == other.mMaximumEyeFOV[0] && + mMaximumEyeFOV[1] == other.mMaximumEyeFOV[1] && + mRecommendedEyeFOV[0] == other.mRecommendedEyeFOV[0] && + mRecommendedEyeFOV[1] == other.mRecommendedEyeFOV[1] && + mEyeFOV[0] == other.mEyeFOV[0] && + mEyeFOV[1] == other.mEyeFOV[1] && + mEyeTranslation[0] == other.mEyeTranslation[0] && + mEyeTranslation[1] == other.mEyeTranslation[1] && + mEyeProjectionMatrix[0] == other.mEyeProjectionMatrix[0] && + mEyeProjectionMatrix[1] == other.mEyeProjectionMatrix[1]; + } + + bool operator!=(const VRDeviceInfo& other) const { + return !(*this == other); + } +}; + + + struct VRHMDSensorState { double timestamp; - uint32_t flags; + VRStateValidFlags flags; float orientation[4]; float position[3]; float angularVelocity[3]; float angularAcceleration[3]; float linearVelocity[3]; float linearAcceleration[3]; void Clear() { memset(this, 0, sizeof(VRHMDSensorState)); } }; +struct VRSensorUpdate { + VRSensorUpdate() { }; // Required for ipdl binding + VRSensorUpdate(uint32_t aDeviceID, const VRHMDSensorState& aSensorState) + : mDeviceID(aDeviceID) + , mSensorState(aSensorState) { }; + + uint32_t mDeviceID; + VRHMDSensorState mSensorState; +}; + +struct VRDeviceUpdate { + VRDeviceUpdate() { }; // Required for ipdl binding + VRDeviceUpdate(const VRDeviceInfo& aDeviceInfo, + const VRHMDSensorState& aSensorState) + : mDeviceInfo(aDeviceInfo) + , mSensorState(aSensorState) { }; + + VRDeviceInfo mDeviceInfo; + VRHMDSensorState mSensorState; +}; + /* A pure data struct that can be used to see if * the configuration of one HMDInfo matches another; for rendering purposes, * really asking "can the rendering details of this one be used for the other" */ struct VRHMDConfiguration { VRHMDConfiguration() : hmdType(VRHMDType::NumHMDTypes) {} bool operator==(const VRHMDConfiguration& other) const { @@ -144,116 +245,65 @@ public: virtual already_AddRefed<RenderTargetSet> CreateRenderTargetSet(layers::Compositor *aCompositor, const IntSize& aSize) = 0; virtual void DestroyRenderTargetSet(RenderTargetSet *aRTSet) = 0; virtual void SubmitFrame(RenderTargetSet *aRTSet) = 0; protected: VRHMDRenderingSupport() { } }; class VRHMDInfo { -public: - enum Eye { - Eye_Left, - Eye_Right, - NumEyes - }; - - enum StateValidFlags { - State_Position = 1 << 1, - State_Orientation = 1 << 2 - }; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDInfo) - VRHMDType GetType() const { return mType; } - uint32_t GetDeviceIndex() const { return mDeviceIndex; } - const nsCString& GetDeviceName() const { return mDeviceName; } - - virtual const VRFieldOfView& GetRecommendedEyeFOV(uint32_t whichEye) { return mRecommendedEyeFOV[whichEye]; } - virtual const VRFieldOfView& GetMaximumEyeFOV(uint32_t whichEye) { return mMaximumEyeFOV[whichEye]; } - const VRHMDConfiguration& GetConfiguration() const { return mConfiguration; } + const VRDeviceInfo& GetDeviceInfo() const { return mDeviceInfo; } /* set the FOV for this HMD unit; this triggers a computation of all the remaining bits. Returns false if it fails */ virtual bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, double zNear, double zFar) = 0; - const VRFieldOfView& GetEyeFOV(uint32_t whichEye) { return mEyeFOV[whichEye]; } - /* Suggested resolution for rendering a single eye. - * Assumption is that left/right rendering will be 2x of this size. - * XXX fix this for vertical displays - */ - const IntSize& SuggestedEyeResolution() const { return mEyeResolution; } - const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; } - const Matrix4x4& GetEyeProjectionMatrix(uint32_t whichEye) const { return mEyeProjectionMatrix[whichEye]; } - - virtual uint32_t GetSupportedSensorStateBits() { return mSupportedSensorBits; } - virtual bool StartSensorTracking() = 0; + virtual bool KeepSensorTracking() = 0; + virtual void NotifyVsync(const TimeStamp& aVsyncTimestamp) = 0; virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) = 0; - virtual void StopSensorTracking() = 0; virtual void ZeroSensor() = 0; - // if rendering is offloaded virtual VRHMDRenderingSupport *GetRenderingSupport() { return nullptr; } // distortion mesh stuff; we should implement renderingsupport for this virtual void FillDistortionConstants(uint32_t whichEye, const IntSize& textureSize, // the full size of the texture const IntRect& eyeViewport, // the viewport within the texture for the current eye const Size& destViewport, // the size of the destination viewport const Rect& destRect, // the rectangle within the dest viewport that this should be rendered VRDistortionConstants& values) = 0; - virtual const VRDistortionMesh& GetDistortionMesh(uint32_t whichEye) const { return mDistortionMesh[whichEye]; } - - // The nsIScreen that represents this device - virtual nsIScreen* GetScreen() { return mScreen; } + const VRDistortionMesh& GetDistortionMesh(uint32_t whichEye) const { return mDistortionMesh[whichEye]; } +protected: + explicit VRHMDInfo(VRHMDType aType, bool aUseMainThreadOrientation); + virtual ~VRHMDInfo(); -protected: - explicit VRHMDInfo(VRHMDType aType); - virtual ~VRHMDInfo() { MOZ_COUNT_DTOR(VRHMDInfo); } - - VRHMDType mType; VRHMDConfiguration mConfiguration; - uint32_t mDeviceIndex; - nsCString mDeviceName; - - VRFieldOfView mEyeFOV[NumEyes]; - IntSize mEyeResolution; - Point3D mEyeTranslation[NumEyes]; - Matrix4x4 mEyeProjectionMatrix[NumEyes]; - VRDistortionMesh mDistortionMesh[NumEyes]; - uint32_t mSupportedSensorBits; - - VRFieldOfView mRecommendedEyeFOV[NumEyes]; - VRFieldOfView mMaximumEyeFOV[NumEyes]; - - nsCOMPtr<nsIScreen> mScreen; + VRDeviceInfo mDeviceInfo; + VRDistortionMesh mDistortionMesh[VRDeviceInfo::NumEyes]; }; class VRHMDManager { public: - static void ManagerInit(); - static void ManagerDestroy(); - static void GetAllHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult); - static uint32_t AllocateDeviceIndex(); - static already_AddRefed<nsIScreen> MakeFakeScreen(int32_t x, int32_t y, uint32_t width, uint32_t height); + static uint32_t AllocateDeviceID(); protected: - typedef nsTArray<RefPtr<VRHMDManager>> VRHMDManagerArray; - static VRHMDManagerArray *sManagers; static Atomic<uint32_t> sDeviceBase; + public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDManager) - virtual bool PlatformInit() = 0; virtual bool Init() = 0; virtual void Destroy() = 0; virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) = 0; protected: VRHMDManager() { } virtual ~VRHMDManager() { } };
--- a/gfx/vr/gfxVRCardboard.cpp +++ b/gfx/vr/gfxVRCardboard.cpp @@ -5,251 +5,105 @@ #include <math.h> #include "prlink.h" #include "prmem.h" #include "prenv.h" #include "gfxPrefs.h" #include "nsString.h" -#include "mozilla/dom/ScreenOrientation.h" #include "mozilla/Preferences.h" #include "mozilla/Hal.h" #include "gfxVRCardboard.h" #include "nsServiceManagerUtils.h" #include "nsIScreenManager.h" -#ifdef ANDROID -#include <android/log.h> -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoVR" , ## args) -#else -#define LOG(...) do { } while(0) -#endif - -// 1/sqrt(2) (aka sqrt(2)/2) -#ifndef M_SQRT1_2 -# define M_SQRT1_2 0.70710678118654752440 -#endif - using namespace mozilla::dom; using namespace mozilla::gfx; using namespace mozilla::gfx::impl; -namespace { -// some utility functions - -// This remaps axes in the given matrix to a new configuration based on the -// screen orientation. Similar to what Android SensorManager.remapCoordinateSystem -// does, except only for a fixed number of transforms that we need. -Matrix4x4 -RemapMatrixForOrientation(ScreenOrientationInternal screenConfig, const Matrix4x4& aMatrix) -{ - Matrix4x4 out; - const float *in = &aMatrix._11; - float *o = &out._11; - - if (screenConfig == eScreenOrientation_LandscapePrimary) { - // remap X,Y -> Y,-X - o[0] = -in[1]; o[1] = in[0]; o[2] = in[2]; - o[4] = -in[5]; o[5] = in[4]; o[6] = in[6]; - o[8] = -in[9]; o[9] = in[8]; o[10] = in[10]; - } else if (screenConfig == eScreenOrientation_LandscapeSecondary) { - // remap X,Y -> -Y,X - o[0] = in[1]; o[1] = -in[0]; o[2] = in[2]; - o[4] = in[5]; o[5] = -in[4]; o[6] = in[6]; - o[8] = in[9]; o[9] = -in[8]; o[10] = in[10]; - } else if (screenConfig == eScreenOrientation_PortraitPrimary) { - out = aMatrix; - } else if (screenConfig == eScreenOrientation_PortraitSecondary) { - // remap X,Y -> X,-Z - o[0] = in[0]; o[1] = in[2]; o[2] = -in[1]; - o[4] = in[4]; o[5] = in[6]; o[6] = -in[5]; - o[8] = in[8]; o[9] = in[10]; o[10] = -in[9]; - } else { - MOZ_ASSERT(0, "gfxVRCardboard::RemapMatrixForOrientation invalid screenConfig"); - } - - return out; -} - -} // namespace - HMDInfoCardboard::HMDInfoCardboard() - : VRHMDInfo(VRHMDType::Cardboard) - , mStartCount(0) - , mOrient(eScreenOrientation_PortraitPrimary) + : VRHMDInfo(VRHMDType::Cardboard, true) { MOZ_ASSERT(sizeof(HMDInfoCardboard::DistortionVertex) == sizeof(VRDistortionVertex), "HMDInfoCardboard::DistortionVertex must match the size of VRDistortionVertex"); MOZ_COUNT_CTOR_INHERITED(HMDInfoCardboard, VRHMDInfo); - mDeviceName.AssignLiteral("Phone Sensor (Cardboard) HMD"); - - mSupportedSensorBits = State_Orientation; - - mRecommendedEyeFOV[Eye_Left] = VRFieldOfView(45.0, 45.0, 45.0, 45.0); - mRecommendedEyeFOV[Eye_Right] = VRFieldOfView(45.0, 45.0, 45.0, 45.0); - - mMaximumEyeFOV[Eye_Left] = VRFieldOfView(45.0, 45.0, 45.0, 45.0); - mMaximumEyeFOV[Eye_Right] = VRFieldOfView(45.0, 45.0, 45.0, 45.0); + mDeviceInfo.mDeviceName.AssignLiteral("Phone Sensor (Cardboard) HMD"); - SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0); + mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_Orientation; -#if 1 - int32_t xcoord = 0; - if (PR_GetEnv("FAKE_CARDBOARD_SCREEN")) { - const char *env = PR_GetEnv("FAKE_CARDBOARD_SCREEN"); - nsresult err; - xcoord = nsCString(env).ToInteger(&err); - if (err != NS_OK) xcoord = 0; - } - mScreen = VRHMDManager::MakeFakeScreen(xcoord, 0, 1920, 1080); -#endif + mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0); + mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0); -} + mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0); + mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0); -bool -HMDInfoCardboard::StartSensorTracking() -{ - LOG("HMDInfoCardboard::StartSensorTracking %d\n", mStartCount); - if (mStartCount == 0) { - // it's never been started before; initialize observers and - // initial state. + SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0); - mozilla::hal::ScreenConfiguration sconfig; - mozilla::hal::GetCurrentScreenConfiguration(&sconfig); - this->Notify(sconfig); - - mozilla::hal::RegisterSensorObserver(mozilla::hal::SENSOR_GAME_ROTATION_VECTOR, this); - mozilla::hal::RegisterScreenConfigurationObserver(this); - - mLastSensorState.Clear(); - } - - mStartCount++; - return true; + mDeviceInfo.mScreenRect.x = 0; + mDeviceInfo.mScreenRect.y = 0; + mDeviceInfo.mScreenRect.width = 1920; + mDeviceInfo.mScreenRect.height = 1080; + mDeviceInfo.mIsFakeScreen = true; } -// Android sends us events that have a 90-degree rotation about -// the x axis compared to what we want (phone flat vs. phone held in front of the eyes). -// Correct for this by applying a transform to undo this rotation. -void -HMDInfoCardboard::Notify(const mozilla::hal::ScreenConfiguration& config) -{ - mOrient = config.orientation(); - - if (mOrient == eScreenOrientation_LandscapePrimary) { - mScreenTransform = Quaternion(-0.5f, 0.5f, 0.5f, 0.5f); - } else if (mOrient == eScreenOrientation_LandscapeSecondary) { - mScreenTransform = Quaternion(-0.5f, -0.5f, -0.5f, 0.5f); - } else if (mOrient == eScreenOrientation_PortraitPrimary) { - mScreenTransform = Quaternion((float) -M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2); - } else if (mOrient == eScreenOrientation_PortraitSecondary) { - // Currently, PortraitSecondary event doesn't be triggered. - mScreenTransform = Quaternion((float) M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2); - } -} - -void -HMDInfoCardboard::Notify(const mozilla::hal::SensorData& data) -{ - if (data.sensor() != mozilla::hal::SENSOR_GAME_ROTATION_VECTOR) - return; - - const nsTArray<float>& sensorValues = data.values(); - - // This is super chatty - //LOG("HMDInfoCardboard::Notify %f %f %f %f\n", sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]); - - mSavedLastSensor.Set(sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]); - mSavedLastSensorTime = data.timestamp(); - mNeedsSensorCompute = true; -} - -void -HMDInfoCardboard::ComputeStateFromLastSensor() -{ - if (!mNeedsSensorCompute) - return; - - // apply the zero orientation - Quaternion q = mSensorZeroInverse * mSavedLastSensor; - - // make a matrix from the quat - Matrix4x4 qm; - qm.SetRotationFromQuaternion(q); - - // remap the coordinate space, based on the orientation - Matrix4x4 qmRemapped = RemapMatrixForOrientation(mOrient, qm); - - // turn it back into a quat - q.SetFromRotationMatrix(qmRemapped); - - // apply adjustment based on what's been done to the screen and the original zero - // position of the base coordinate space - q = mScreenTransform * q; - - VRHMDSensorState& state = mLastSensorState; - - state.flags |= State_Orientation; - state.orientation[0] = q.x; - state.orientation[1] = q.y; - state.orientation[2] = q.z; - state.orientation[3] = q.w; - - state.timestamp = mSavedLastSensorTime / 1000000.0; - - mNeedsSensorCompute = false; -} VRHMDSensorState HMDInfoCardboard::GetSensorState(double timeOffset) { - ComputeStateFromLastSensor(); - return mLastSensorState; -} - -void -HMDInfoCardboard::StopSensorTracking() -{ - LOG("HMDInfoCardboard::StopSensorTracking, count %d\n", mStartCount); - if (--mStartCount == 0) { - mozilla::hal::UnregisterScreenConfigurationObserver(this); - mozilla::hal::UnregisterSensorObserver(mozilla::hal::SENSOR_GAME_ROTATION_VECTOR, this); - } + // Actual sensor state is calculated on the main thread, + // within VRDeviceProxyOrientationFallBack + VRHMDSensorState result; + result.Clear(); + return result; } void HMDInfoCardboard::ZeroSensor() { - mSensorZeroInverse = mSavedLastSensor; - mSensorZeroInverse.Invert(); + MOZ_ASSERT(0, "HMDInfoCardboard::ZeroSensor not implemented. " + "Should use VRDeviceProxyOrientationFallBack on main thread"); +} + + +void +HMDInfoCardboard::NotifyVsync(const TimeStamp& aVsyncTimestamp) +{ + // Nothing to do here for Cardboard VR } bool -HMDInfoCardboard::SetFOV(const VRFieldOfView& aFOVLeft, - const VRFieldOfView& aFOVRight, +HMDInfoCardboard::KeepSensorTracking() +{ + // Nothing to do here for Cardboard VR + return true; +} + +bool +HMDInfoCardboard::SetFOV(const gfx::VRFieldOfView& aFOVLeft, + const gfx::VRFieldOfView& aFOVRight, double zNear, double zFar) { const float standardIPD = 0.064f; - for (uint32_t eye = 0; eye < NumEyes; eye++) { - mEyeFOV[eye] = eye == Eye_Left ? aFOVLeft : aFOVRight; - mEyeTranslation[eye] = Point3D(standardIPD * (eye == Eye_Left ? -1.0 : 1.0), 0.0, 0.0); - mEyeProjectionMatrix[eye] = mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true); + for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) { + mDeviceInfo.mEyeFOV[eye] = eye == VRDeviceInfo::Eye_Left ? aFOVLeft : aFOVRight; + mDeviceInfo.mEyeTranslation[eye] = Point3D(standardIPD * (eye == VRDeviceInfo::Eye_Left ? -1.0 : 1.0), 0.0, 0.0); + mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true); mDistortionMesh[eye].mVertices.SetLength(4); mDistortionMesh[eye].mIndices.SetLength(6); HMDInfoCardboard::DistortionVertex *destv = reinterpret_cast<HMDInfoCardboard::DistortionVertex*>(mDistortionMesh[eye].mVertices.Elements()); - float xoffs = eye == Eye_Left ? 0.0f : 1.0f; - float txoffs = eye == Eye_Left ? 0.0f : 0.5f; + float xoffs = eye == VRDeviceInfo::Eye_Left ? 0.0f : 1.0f; + float txoffs = eye == VRDeviceInfo::Eye_Left ? 0.0f : 0.5f; destv[0].pos[0] = -1.0 + xoffs; destv[0].pos[1] = -1.0; destv[0].texR[0] = destv[0].texG[0] = destv[0].texB[0] = 0.0 + txoffs; destv[0].texR[1] = destv[0].texG[1] = destv[0].texB[1] = 1.0; destv[0].padding[0] = 1.0; // vignette factor destv[1].pos[0] = 0.0 + xoffs; destv[1].pos[1] = -1.0; @@ -270,26 +124,26 @@ HMDInfoCardboard::SetFOV(const VRFieldOf destv[3].padding[0] = 1.0; // vignette factor uint16_t *iv = mDistortionMesh[eye].mIndices.Elements(); iv[0] = 0; iv[1] = 1; iv[2] = 2; iv[3] = 2; iv[4] = 3; iv[5] = 0; } // XXX find out the default screen size and use that - mEyeResolution.width = 1920 / 2; - mEyeResolution.height = 1080; + mDeviceInfo.mEyeResolution.width = 1920 / 2; + mDeviceInfo.mEyeResolution.height = 1080; if (PR_GetEnv("FAKE_CARDBOARD_SCREEN")) { // for testing, make the eye resolution 2x of the screen - mEyeResolution.width *= 2; - mEyeResolution.height *= 2; + mDeviceInfo.mEyeResolution.width *= 2; + mDeviceInfo.mEyeResolution.height *= 2; } - mConfiguration.hmdType = mType; + mConfiguration.hmdType = mDeviceInfo.mType; mConfiguration.value = 0; mConfiguration.fov[0] = aFOVLeft; mConfiguration.fov[1] = aFOVRight; return true; } void @@ -327,29 +181,36 @@ HMDInfoCardboard::FillDistortionConstant values.destinationScaleAndOffset[3] = destRect.height / destViewport.height; } void HMDInfoCardboard::Destroy() { } - +/*static*/ already_AddRefed<VRHMDManagerCardboard> +VRHMDManagerCardboard::Create() +{ + MOZ_ASSERT(NS_IsMainThread()); -bool -VRHMDManagerCardboard::PlatformInit() -{ - return gfxPrefs::VREnabled() && gfxPrefs::VRCardboardEnabled(); + if (!gfxPrefs::VREnabled() || !gfxPrefs::VRCardboardEnabled()) + { + return nullptr; + } + + RefPtr<VRHMDManagerCardboard> manager = new VRHMDManagerCardboard(); + return manager.forget(); } bool VRHMDManagerCardboard::Init() { - if (mCardboardInitialized) + if (mCardboardInitialized) { return true; + } RefPtr<HMDInfoCardboard> hmd = new HMDInfoCardboard(); mCardboardHMDs.AppendElement(hmd); mCardboardInitialized = true; return true; } @@ -365,13 +226,16 @@ VRHMDManagerCardboard::Destroy() mCardboardHMDs.Clear(); mCardboardInitialized = false; } void VRHMDManagerCardboard::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) { - Init(); + if (!mCardboardInitialized) { + return; + } + for (size_t i = 0; i < mCardboardHMDs.Length(); ++i) { aHMDResult.AppendElement(mCardboardHMDs[i]); } }
--- a/gfx/vr/gfxVRCardboard.h +++ b/gfx/vr/gfxVRCardboard.h @@ -4,94 +4,74 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GFX_VR_CARDBOARD_H #define GFX_VR_CARDBOARD_H #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Quaternion.h" #include "mozilla/EnumeratedArray.h" -#include "mozilla/HalSensor.h" -#include "mozilla/HalScreenConfiguration.h" #include "gfxVR.h" namespace mozilla { namespace gfx { namespace impl { class HMDInfoCardboard : - public VRHMDInfo, - public hal::ISensorObserver, - public hal::ScreenConfigurationObserver + public VRHMDInfo { public: explicit HMDInfoCardboard(); bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, double zNear, double zFar) override; - bool StartSensorTracking() override; VRHMDSensorState GetSensorState(double timeOffset) override; - void StopSensorTracking() override; void ZeroSensor() override; + bool KeepSensorTracking() override; + void NotifyVsync(const TimeStamp& aVsyncTimestamp) override; void FillDistortionConstants(uint32_t whichEye, const IntSize& textureSize, const IntRect& eyeViewport, const Size& destViewport, const Rect& destRect, VRDistortionConstants& values) override; void Destroy(); - // ISensorObserver interface - void Notify(const hal::SensorData& SensorData) override; - // ScreenConfigurationObserver interface - void Notify(const hal::ScreenConfiguration& ScreenConfiguration) override; +protected: + virtual ~HMDInfoCardboard() { + MOZ_COUNT_DTOR_INHERITED(HMDInfoCardboard, VRHMDInfo); + Destroy(); + } -protected: // must match the size of VRDistortionVertex struct DistortionVertex { float pos[2]; float texR[2]; float texG[2]; float texB[2]; float padding[4]; }; - virtual ~HMDInfoCardboard() { - Destroy(); - } - - void ComputeStateFromLastSensor(); - - uint32_t mStartCount; - VRHMDSensorState mLastSensorState; - uint32_t mOrient; - Quaternion mScreenTransform; - Quaternion mSensorZeroInverse; - - Quaternion mSavedLastSensor; - double mSavedLastSensorTime; - bool mNeedsSensorCompute; // if we need to compute the state from mSavedLastSensor }; } // namespace impl class VRHMDManagerCardboard : public VRHMDManager { public: - VRHMDManagerCardboard() - : mCardboardInitialized(false) - { } - - virtual bool PlatformInit() override; + static already_AddRefed<VRHMDManagerCardboard> Create(); virtual bool Init() override; virtual void Destroy() override; virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo> >& aHMDResult) override; protected: + VRHMDManagerCardboard() + : mCardboardInitialized(false) + { } nsTArray<RefPtr<impl::HMDInfoCardboard>> mCardboardHMDs; bool mCardboardInitialized; }; } // namespace gfx } // namespace mozilla #endif /* GFX_VR_CARDBOARD_H */
--- a/gfx/vr/gfxVROculus.cpp +++ b/gfx/vr/gfxVROculus.cpp @@ -6,28 +6,26 @@ #include <math.h> #include "prlink.h" #include "prmem.h" #include "prenv.h" #include "gfxPrefs.h" #include "nsString.h" #include "mozilla/Preferences.h" +#include "mozilla/TimeStamp.h" #include "mozilla/gfx/Quaternion.h" #ifdef XP_WIN #include "../layers/d3d11/CompositorD3D11.h" #endif #include "gfxVROculus.h" -#include "nsServiceManagerUtils.h" -#include "nsIScreenManager.h" - #ifndef M_PI # define M_PI 3.14159265358979323846 #endif using namespace mozilla::gfx; using namespace mozilla::gfx::impl; namespace { @@ -106,17 +104,17 @@ InitializeOculusCAPI() //libName.AppendPrintf("LibOVRRT_%d", OVR_PRODUCT_VERSION); libName.Append("LibOVRRT_"); libName.AppendInt(OVR_PRODUCT_VERSION); #else libSearchPaths.AppendElement(nsCString("/usr/local/lib")); libSearchPaths.AppendElement(nsCString("/usr/lib")); libName.AppendPrintf("libOVRRT%d_%d.so.%d", BUILD_BITS, OVR_PRODUCT_VERSION, OVR_MAJOR_VERSION); #endif - + // If the pref is present, we override libName nsAdoptingCString prefLibPath = mozilla::Preferences::GetCString("dom.vr.ovr_lib_path"); if (prefLibPath && prefLibPath.get()) { libSearchPaths.InsertElementsAt(0, 1, prefLibPath); } nsAdoptingCString prefLibName = mozilla::Preferences::GetCString("dom.vr.ovr_lib_name"); if (prefLibName && prefLibName.get()) { @@ -222,120 +220,133 @@ do_CalcEyePoses(ovrPosef headPose, outEyePoses[i].Orientation = headPose.Orientation; outEyePoses[i].Position.x = p.x + headPose.Position.x; outEyePoses[i].Position.y = p.y + headPose.Position.y; outEyePoses[i].Position.z = p.z + headPose.Position.z; } } ovrFovPort -ToFovPort(const VRFieldOfView& aFOV) +ToFovPort(const gfx::VRFieldOfView& aFOV) { ovrFovPort fovPort; fovPort.LeftTan = tan(aFOV.leftDegrees * M_PI / 180.0); fovPort.RightTan = tan(aFOV.rightDegrees * M_PI / 180.0); fovPort.UpTan = tan(aFOV.upDegrees * M_PI / 180.0); fovPort.DownTan = tan(aFOV.downDegrees * M_PI / 180.0); return fovPort; } -VRFieldOfView +gfx::VRFieldOfView FromFovPort(const ovrFovPort& aFOV) { - VRFieldOfView fovInfo; + gfx::VRFieldOfView fovInfo; fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI; fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI; fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI; fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI; return fovInfo; } } // namespace -HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD) - : VRHMDInfo(VRHMDType::Oculus) +HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD, bool aDebug, int aDeviceID) + : VRHMDInfo(VRHMDType::Oculus, false) , mHMD(aHMD) - , mStartCount(0) + , mTracking(false) + , mDebug(aDebug) + , mDeviceID(aDeviceID) + , mSensorTrackingFramesRemaining(0) { MOZ_ASSERT(sizeof(HMDInfoOculus::DistortionVertex) == sizeof(VRDistortionVertex), "HMDInfoOculus::DistortionVertex must match the size of VRDistortionVertex"); MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus, VRHMDInfo); - mDeviceName.AssignLiteral("Oculus VR HMD"); - - mSupportedSensorBits = 0; - if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) - mSupportedSensorBits |= State_Orientation; - if (mHMD->TrackingCaps & ovrTrackingCap_Position) - mSupportedSensorBits |= State_Position; - - mRecommendedEyeFOV[Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]); - mRecommendedEyeFOV[Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]); + if (aDebug) { + mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD Debug)"); + } else { + mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD"); + } - mMaximumEyeFOV[Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]); - mMaximumEyeFOV[Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]); - - SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0); + mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_None; + if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) { + mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Orientation; + } + if (mHMD->TrackingCaps & ovrTrackingCap_Position) { + mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Position; + } -#if 1 - int32_t xcoord = 0; - if (getenv("FAKE_OCULUS_SCREEN")) { - const char *env = getenv("FAKE_OCULUS_SCREEN"); - nsresult err; - xcoord = nsCString(env).ToInteger(&err); - if (err != NS_OK) xcoord = 0; - } + mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]); + mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]); + + mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]); + mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]); + uint32_t w = mHMD->Resolution.w; uint32_t h = mHMD->Resolution.h; - mScreen = VRHMDManager::MakeFakeScreen(xcoord, 0, std::max(w, h), std::min(w, h)); + mDeviceInfo.mScreenRect.x = 0; + mDeviceInfo.mScreenRect.y = 0; + mDeviceInfo.mScreenRect.width = std::max(w, h); + mDeviceInfo.mScreenRect.height = std::min(w, h); + mDeviceInfo.mIsFakeScreen = true; + + SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0); +} -#ifdef DEBUG - printf_stderr("OCULUS SCREEN: %d %d %d %d\n", xcoord, 0, std::max(w, h), std::min(w, h)); -#endif -#endif +bool +HMDInfoOculus::GetIsDebug() const +{ + return mDebug; +} + +int +HMDInfoOculus::GetDeviceID() const +{ + return mDeviceID; } void HMDInfoOculus::Destroy() { + StopSensorTracking(); if (mHMD) { ovrHmd_Destroy(mHMD); mHMD = nullptr; } } bool -HMDInfoOculus::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, +HMDInfoOculus::SetFOV(const gfx::VRFieldOfView& aFOVLeft, const gfx::VRFieldOfView& aFOVRight, double zNear, double zFar) { float pixelsPerDisplayPixel = 1.0; ovrSizei texSize[2]; // get eye parameters and create the mesh - for (uint32_t eye = 0; eye < NumEyes; eye++) { - mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight; - mFOVPort[eye] = ToFovPort(mEyeFOV[eye]); + for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) { + mDeviceInfo.mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight; + mFOVPort[eye] = ToFovPort(mDeviceInfo.mEyeFOV[eye]); ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]); // As of Oculus 0.6.0, the HmdToEyeViewOffset values are correct and don't need to be negated. - mEyeTranslation[eye] = Point3D(renderDesc.HmdToEyeViewOffset.x, renderDesc.HmdToEyeViewOffset.y, renderDesc.HmdToEyeViewOffset.z); + mDeviceInfo.mEyeTranslation[eye] = Point3D(renderDesc.HmdToEyeViewOffset.x, renderDesc.HmdToEyeViewOffset.y, renderDesc.HmdToEyeViewOffset.z); // note that we are using a right-handed coordinate system here, to match CSS - mEyeProjectionMatrix[eye] = mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true); + mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true); texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel); } // take the max of both for eye resolution - mEyeResolution.width = std::max(texSize[Eye_Left].w, texSize[Eye_Right].w); - mEyeResolution.height = std::max(texSize[Eye_Left].h, texSize[Eye_Right].h); + mDeviceInfo.mEyeResolution.width = std::max(texSize[VRDeviceInfo::Eye_Left].w, texSize[VRDeviceInfo::Eye_Right].w); + mDeviceInfo.mEyeResolution.height = std::max(texSize[VRDeviceInfo::Eye_Left].h, texSize[VRDeviceInfo::Eye_Right].h); - mConfiguration.hmdType = mType; + mConfiguration.hmdType = mDeviceInfo.mType; mConfiguration.value = 0; mConfiguration.fov[0] = aFOVLeft; mConfiguration.fov[1] = aFOVRight; return true; } void @@ -344,33 +355,61 @@ HMDInfoOculus::FillDistortionConstants(u const IntRect& eyeViewport, const Size& destViewport, const Rect& destRect, VRDistortionConstants& values) { } bool +HMDInfoOculus::KeepSensorTracking() +{ + // Keep sensor tracking alive for short time after the last request for + // tracking state by content. Value conservatively high to accomodate + // potentially high frame rates. + const uint32_t kKeepAliveFrames = 200; + + bool success = true; + if (mSensorTrackingFramesRemaining == 0) { + success = StartSensorTracking(); + } + if (success) { + mSensorTrackingFramesRemaining = kKeepAliveFrames; + } + + return success; +} + +void +HMDInfoOculus::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp) +{ + if (mSensorTrackingFramesRemaining == 1) { + StopSensorTracking(); + } + if (mSensorTrackingFramesRemaining) { + --mSensorTrackingFramesRemaining; + } +} + +bool HMDInfoOculus::StartSensorTracking() { - if (mStartCount == 0) { - bool ok = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0); - if (!ok) - return false; + if (!mTracking) { + mTracking = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0); } - mStartCount++; - return true; + return mTracking; } void HMDInfoOculus::StopSensorTracking() { - if (--mStartCount == 0) { + if (mTracking) { ovrHmd_ConfigureTracking(mHMD, 0, 0); + mTracking = false; } } void HMDInfoOculus::ZeroSensor() { ovrHmd_RecenterPose(mHMD); } @@ -384,34 +423,34 @@ HMDInfoOculus::GetSensorState(double tim // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize // the Oculus time base and the browser one. ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset); ovrPoseStatef& pose(state.HeadPose); result.timestamp = pose.TimeInSeconds; if (state.StatusFlags & ovrStatus_OrientationTracked) { - result.flags |= State_Orientation; + result.flags |= VRStateValidFlags::State_Orientation; result.orientation[0] = pose.ThePose.Orientation.x; result.orientation[1] = pose.ThePose.Orientation.y; result.orientation[2] = pose.ThePose.Orientation.z; result.orientation[3] = pose.ThePose.Orientation.w; result.angularVelocity[0] = pose.AngularVelocity.x; result.angularVelocity[1] = pose.AngularVelocity.y; result.angularVelocity[2] = pose.AngularVelocity.z; result.angularAcceleration[0] = pose.AngularAcceleration.x; result.angularAcceleration[1] = pose.AngularAcceleration.y; result.angularAcceleration[2] = pose.AngularAcceleration.z; } if (state.StatusFlags & ovrStatus_PositionTracked) { - result.flags |= State_Position; + result.flags |= VRStateValidFlags::State_Position; result.position[0] = pose.ThePose.Position.x; result.position[1] = pose.ThePose.Position.y; result.position[2] = pose.ThePose.Position.z; result.linearVelocity[0] = pose.LinearVelocity.x; result.linearVelocity[1] = pose.LinearVelocity.y; result.linearVelocity[2] = pose.LinearVelocity.z; @@ -556,114 +595,146 @@ HMDInfoOculus::SubmitFrame(RenderTargetS layer.Viewport[0].Pos.y = 0; layer.Viewport[0].Size.w = rts->size.width / 2; layer.Viewport[0].Size.h = rts->size.height; layer.Viewport[1].Pos.x = rts->size.width / 2; layer.Viewport[1].Pos.y = 0; layer.Viewport[1].Size.w = rts->size.width / 2; layer.Viewport[1].Size.h = rts->size.height; - const Point3D& l = rts->hmd->mEyeTranslation[0]; - const Point3D& r = rts->hmd->mEyeTranslation[1]; + const Point3D& l = rts->hmd->mDeviceInfo.mEyeTranslation[0]; + const Point3D& r = rts->hmd->mDeviceInfo.mEyeTranslation[1]; const ovrVector3f hmdToEyeViewOffset[2] = { { l.x, l.y, l.z }, { r.x, r.y, r.z } }; do_CalcEyePoses(rts->hmd->mLastTrackingState.HeadPose.ThePose, hmdToEyeViewOffset, layer.RenderPose); ovrLayerHeader *layers = &layer.Header; ovrResult orv = ovrHmd_SubmitFrame(mHMD, 0, nullptr, &layers, 1); //printf_stderr("Submitted frame %d, result: %d\n", rts->textureSet->CurrentIndex, orv); if (orv != ovrSuccess) { // not visible? failed? } } -bool -VRHMDManagerOculus::PlatformInit() +/*static*/ already_AddRefed<VRHMDManagerOculus> +VRHMDManagerOculus::Create() { - if (mOculusPlatformInitialized) - return true; + MOZ_ASSERT(NS_IsMainThread()); - if (!gfxPrefs::VREnabled() || - !gfxPrefs::VROculusEnabled()) + if (!gfxPrefs::VREnabled() || !gfxPrefs::VROculusEnabled()) { - return false; + return nullptr; } - if (!InitializeOculusCAPI()) - return false; + if (!InitializeOculusCAPI()) { + return nullptr; + } - ovrInitParams params; - params.Flags = ovrInit_RequestVersion; - params.RequestedMinorVersion = OVR_MINOR_VERSION; - params.LogCallback = nullptr; - params.ConnectionTimeoutMS = 0; - - ovrResult orv = ovr_Initialize(¶ms); - - if (orv != ovrSuccess) - return false; - - mOculusPlatformInitialized = true; - return true; + RefPtr<VRHMDManagerOculus> manager = new VRHMDManagerOculus(); + return manager.forget(); } bool VRHMDManagerOculus::Init() { - if (mOculusInitialized) - return true; + if (!mOculusInitialized) { + nsIThread* thread = nullptr; + NS_GetCurrentThread(&thread); + mOculusThread = already_AddRefed<nsIThread>(thread); + + ovrInitParams params; + params.Flags = ovrInit_RequestVersion; + params.RequestedMinorVersion = OVR_MINOR_VERSION; + params.LogCallback = nullptr; + params.ConnectionTimeoutMS = 0; + + ovrResult orv = ovr_Initialize(¶ms); + + if (orv == ovrSuccess) { + mOculusInitialized = true; + } + } + + return mOculusInitialized; +} - if (!PlatformInit()) - return false; +void +VRHMDManagerOculus::Destroy() +{ + if(mOculusInitialized) { + MOZ_ASSERT(NS_GetCurrentThread() == mOculusThread); + mOculusThread = nullptr; + + for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { + mOculusHMDs[i]->Destroy(); + } + + mOculusHMDs.Clear(); + + ovr_Shutdown(); + mOculusInitialized = false; + } +} + +void +VRHMDManagerOculus::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) +{ + if (!mOculusInitialized) { + return; + } + + nsTArray<RefPtr<impl::HMDInfoOculus> > newHMDs; ovrResult orv; + int count = ovrHmd_Detect(); - - for (int i = 0; i < count; ++i) { - ovrHmd hmd; - orv = ovrHmd_Create(i, &hmd); - if (orv == ovrSuccess) { - RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd); - mOculusHMDs.AppendElement(oc); + + for (int j = 0; j < count; ++j) { + bool is_new = true; + for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { + if (mOculusHMDs[i]->GetDeviceID() == j) { + newHMDs.AppendElement(mOculusHMDs[i]); + is_new = false; + break; + } + } + + if (is_new) { + ovrHmd hmd; + orv = ovrHmd_Create(j, &hmd); + if (orv == ovrSuccess) { + RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd, false, j); + newHMDs.AppendElement(oc); + } } } // VRAddTestDevices == 1: add test device only if no real devices present // VRAddTestDevices == 2: add test device always if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) || - (gfxPrefs::VRAddTestDevices() == 2)) + (gfxPrefs::VRAddTestDevices() == 2)) { - ovrHmd hmd; - orv = ovrHmd_CreateDebug(ovrHmd_DK2, &hmd); - if (orv == ovrSuccess) { - RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd); - mOculusHMDs.AppendElement(oc); + // Keep existing debug HMD if possible + bool foundDebug = false; + for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { + if (mOculusHMDs[i]->GetIsDebug()) { + newHMDs.AppendElement(mOculusHMDs[i]); + foundDebug = true; + } + } + + // If there isn't already a debug HMD, create one + if (!foundDebug) { + ovrHmd hmd; + orv = ovrHmd_CreateDebug(ovrHmd_DK2, &hmd); + if (orv == ovrSuccess) { + RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd, true, -1); + newHMDs.AppendElement(oc); + } } } - mOculusInitialized = true; - return true; -} - -void -VRHMDManagerOculus::Destroy() -{ - if (!mOculusInitialized) - return; + mOculusHMDs = newHMDs; - for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { - mOculusHMDs[i]->Destroy(); - } - - mOculusHMDs.Clear(); - - ovr_Shutdown(); - mOculusInitialized = false; -} - -void -VRHMDManagerOculus::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) -{ - Init(); - for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { - aHMDResult.AppendElement(mOculusHMDs[i]); + for (size_t j = 0; j < mOculusHMDs.Length(); ++j) { + aHMDResult.AppendElement(mOculusHMDs[j]); } }
--- a/gfx/vr/gfxVROculus.h +++ b/gfx/vr/gfxVROculus.h @@ -2,96 +2,104 @@ * 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/. */ #ifndef GFX_VR_OCULUS_H #define GFX_VR_OCULUS_H #include "nsTArray.h" -#include "nsIScreen.h" -#include "nsCOMPtr.h" #include "mozilla/RefPtr.h" #include "mozilla/gfx/2D.h" #include "mozilla/EnumeratedArray.h" #include "gfxVR.h" //#include <OVR_CAPI.h> //#include <OVR_CAPI_D3D.h> #include "ovr_capi_dynamic.h" namespace mozilla { namespace gfx { namespace impl { class HMDInfoOculus : public VRHMDInfo, public VRHMDRenderingSupport { public: - explicit HMDInfoOculus(ovrHmd aHMD); + explicit HMDInfoOculus(ovrHmd aHMD, bool aDebug, int aDeviceID); bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, double zNear, double zFar) override; - bool StartSensorTracking() override; VRHMDSensorState GetSensorState(double timeOffset) override; - void StopSensorTracking() override; void ZeroSensor() override; + bool KeepSensorTracking() override; + void NotifyVsync(const TimeStamp& aVsyncTimestamp) override; void FillDistortionConstants(uint32_t whichEye, const IntSize& textureSize, const IntRect& eyeViewport, const Size& destViewport, const Rect& destRect, VRDistortionConstants& values) override; VRHMDRenderingSupport* GetRenderingSupport() override { return this; } void Destroy(); /* VRHMDRenderingSupport */ already_AddRefed<RenderTargetSet> CreateRenderTargetSet(layers::Compositor *aCompositor, const IntSize& aSize) override; void DestroyRenderTargetSet(RenderTargetSet *aRTSet) override; void SubmitFrame(RenderTargetSet *aRTSet) override; ovrHmd GetOculusHMD() const { return mHMD; } + bool GetIsDebug() const; + int GetDeviceID() const; protected: + virtual ~HMDInfoOculus() { + Destroy(); + MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo); + } + + bool StartSensorTracking(); + void StopSensorTracking(); + // must match the size of VRDistortionVertex struct DistortionVertex { float pos[2]; float texR[2]; float texG[2]; float texB[2]; float genericAttribs[4]; }; - virtual ~HMDInfoOculus() { - Destroy(); - MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo); - } - ovrHmd mHMD; ovrFovPort mFOVPort[2]; - uint32_t mStartCount; + bool mTracking; ovrTrackingState mLastTrackingState; + + bool mDebug; // True if this is a debug HMD + int mDeviceID; // Index of device passed to ovrHmd_Create + + uint32_t mSensorTrackingFramesRemaining; }; } // namespace impl class VRHMDManagerOculus : public VRHMDManager { public: - VRHMDManagerOculus() - : mOculusInitialized(false), mOculusPlatformInitialized(false) - { } - - virtual bool PlatformInit() override; + static already_AddRefed<VRHMDManagerOculus> Create(); virtual bool Init() override; virtual void Destroy() override; virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo> >& aHMDResult) override; protected: + VRHMDManagerOculus() + : mOculusInitialized(false) + { } + nsTArray<RefPtr<impl::HMDInfoOculus>> mOculusHMDs; bool mOculusInitialized; - bool mOculusPlatformInitialized; + RefPtr<nsIThread> mOculusThread; }; } // namespace gfx } // namespace mozilla #endif /* GFX_VR_OCULUS_H */
--- a/gfx/vr/gfxVROculus050.cpp +++ b/gfx/vr/gfxVROculus050.cpp @@ -9,22 +9,20 @@ #endif #include "prlink.h" #include "prmem.h" #include "prenv.h" #include "gfxPrefs.h" #include "nsString.h" #include "mozilla/Preferences.h" +#include "mozilla/TimeStamp.h" #include "gfxVROculus050.h" -#include "nsServiceManagerUtils.h" -#include "nsIScreenManager.h" - #ifndef M_PI # define M_PI 3.14159265358979323846 #endif using namespace ovr050; using namespace mozilla::gfx; using namespace mozilla::gfx::impl; @@ -235,92 +233,103 @@ FromFovPort(const ovrFovPort& aFOV) fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI; fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI; fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI; return fovInfo; } } // anonymous namespace -HMDInfoOculus050::HMDInfoOculus050(ovrHmd aHMD) - : VRHMDInfo(VRHMDType::Oculus050) +HMDInfoOculus050::HMDInfoOculus050(ovrHmd aHMD, bool aDebug, int aDeviceID) + : VRHMDInfo(VRHMDType::Oculus050, false) , mHMD(aHMD) - , mStartCount(0) + , mTracking(false) + , mDebug(aDebug) + , mDeviceID(aDeviceID) + , mSensorTrackingFramesRemaining(0) { MOZ_ASSERT(sizeof(HMDInfoOculus050::DistortionVertex) == sizeof(VRDistortionVertex), "HMDInfoOculus050::DistortionVertex must match the size of VRDistortionVertex"); MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus050, VRHMDInfo); - mDeviceName.AssignLiteral("Oculus VR HMD (0.5.0)"); - - mSupportedSensorBits = 0; - if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) - mSupportedSensorBits |= State_Orientation; - if (mHMD->TrackingCaps & ovrTrackingCap_Position) - mSupportedSensorBits |= State_Position; + if (aDebug) { + mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD (0.5.0 Debug)"); + } else { + mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD (0.5.0)"); + } - mRecommendedEyeFOV[Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]); - mRecommendedEyeFOV[Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]); + mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_None; + if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) { + mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Orientation; + } + if (mHMD->TrackingCaps & ovrTrackingCap_Position) { + mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Position; + } - mMaximumEyeFOV[Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]); - mMaximumEyeFOV[Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]); + mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]); + mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]); - SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0); + mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]); + mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]); - nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); - if (screenmgr) { -#if 1 - if (getenv("FAKE_OCULUS_SCREEN")) { - const char *env = getenv("FAKE_OCULUS_SCREEN"); - nsresult err; - int32_t xcoord = nsCString(env).ToInteger(&err); - if (err != NS_OK) xcoord = 0; - mScreen = VRHMDManager::MakeFakeScreen(xcoord, 0, 1920, 1080); - } else -#endif - { - screenmgr->ScreenForRect(mHMD->WindowsPos.x, mHMD->WindowsPos.y, - mHMD->Resolution.w, mHMD->Resolution.h, - getter_AddRefs(mScreen)); - } - } + mDeviceInfo.mScreenRect.x = mHMD->WindowsPos.x; + mDeviceInfo.mScreenRect.y = mHMD->WindowsPos.y; + mDeviceInfo.mScreenRect.width = mHMD->Resolution.w; + mDeviceInfo.mScreenRect.height = mHMD->Resolution.h; + mDeviceInfo.mIsFakeScreen = false; + + SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0); +} + +bool +HMDInfoOculus050::GetIsDebug() const +{ + return mDebug; +} + +int +HMDInfoOculus050::GetDeviceID() const +{ + return mDeviceID; } void HMDInfoOculus050::Destroy() { + StopSensorTracking(); + if (mHMD) { ovrHmd_Destroy(mHMD); mHMD = nullptr; } } bool HMDInfoOculus050::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, double zNear, double zFar) { float pixelsPerDisplayPixel = 1.0; ovrSizei texSize[2]; uint32_t caps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette; // XXX TODO add TimeWarp // get eye parameters and create the mesh - for (uint32_t eye = 0; eye < NumEyes; eye++) { - mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight; - mFOVPort[eye] = ToFovPort(mEyeFOV[eye]); + for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) { + mDeviceInfo.mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight; + mFOVPort[eye] = ToFovPort(mDeviceInfo.mEyeFOV[eye]); ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]); // these values are negated so that content can add the adjustment to its camera position, // instead of subtracting - mEyeTranslation[eye] = Point3D(-renderDesc.HmdToEyeViewOffset.x, -renderDesc.HmdToEyeViewOffset.y, -renderDesc.HmdToEyeViewOffset.z); + mDeviceInfo.mEyeTranslation[eye] = Point3D(-renderDesc.HmdToEyeViewOffset.x, -renderDesc.HmdToEyeViewOffset.y, -renderDesc.HmdToEyeViewOffset.z); // note that we are using a right-handed coordinate system here, to match CSS - mEyeProjectionMatrix[eye] = mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true); + mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true); texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel); ovrDistortionMesh mesh; bool ok = ovrHmd_CreateDistortionMesh(mHMD, (ovrEyeType) eye, mFOVPort[eye], caps, &mesh); if (!ok) return false; @@ -345,20 +354,20 @@ HMDInfoOculus050::SetFOV(const VRFieldOf destv[i].genericAttribs[1] = srcv[i].TimeWarpFactor; } memcpy(mDistortionMesh[eye].mIndices.Elements(), mesh.pIndexData, mesh.IndexCount * sizeof(uint16_t)); ovrHmd_DestroyDistortionMesh(&mesh); } // take the max of both for eye resolution - mEyeResolution.width = std::max(texSize[Eye_Left].w, texSize[Eye_Right].w); - mEyeResolution.height = std::max(texSize[Eye_Left].h, texSize[Eye_Right].h); + mDeviceInfo.mEyeResolution.width = std::max(texSize[VRDeviceInfo::Eye_Left].w, texSize[VRDeviceInfo::Eye_Right].w); + mDeviceInfo.mEyeResolution.height = std::max(texSize[VRDeviceInfo::Eye_Left].h, texSize[VRDeviceInfo::Eye_Right].h); - mConfiguration.hmdType = mType; + mConfiguration.hmdType = mDeviceInfo.mType; mConfiguration.value = 0; mConfiguration.fov[0] = aFOVLeft; mConfiguration.fov[1] = aFOVRight; return true; } void @@ -394,33 +403,61 @@ HMDInfoOculus050::FillDistortionConstant values.destinationScaleAndOffset[0] = (x0+x1) / 2.0; values.destinationScaleAndOffset[1] = (y0+y1) / 2.0; // scale values.destinationScaleAndOffset[2] = destRect.width / destViewport.width; values.destinationScaleAndOffset[3] = destRect.height / destViewport.height; } bool +HMDInfoOculus050::KeepSensorTracking() +{ + // Keep sensor tracking alive for short time after the last request for + // tracking state by content. Value conservatively high to accomodate + // potentially high frame rates. + const uint32_t kKeepAliveFrames = 200; + + bool success = true; + if (mSensorTrackingFramesRemaining == 0) { + success = StartSensorTracking(); + } + if (success) { + mSensorTrackingFramesRemaining = kKeepAliveFrames; + } + + return success; +} + +void +HMDInfoOculus050::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp) +{ + if (mSensorTrackingFramesRemaining == 1) { + StopSensorTracking(); + } + if (mSensorTrackingFramesRemaining) { + --mSensorTrackingFramesRemaining; + } +} + +bool HMDInfoOculus050::StartSensorTracking() { - if (mStartCount == 0) { - bool ok = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0); - if (!ok) - return false; + if (!mTracking) { + mTracking = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0); } - mStartCount++; - return true; + return mTracking; } void HMDInfoOculus050::StopSensorTracking() { - if (--mStartCount == 0) { + if (mTracking) { ovrHmd_ConfigureTracking(mHMD, 0, 0); + mTracking = false; } } void HMDInfoOculus050::ZeroSensor() { ovrHmd_RecenterPose(mHMD); } @@ -434,34 +471,34 @@ HMDInfoOculus050::GetSensorState(double // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize // the Oculus time base and the browser one. ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset); ovrPoseStatef& pose(state.HeadPose); result.timestamp = pose.TimeInSeconds; if (state.StatusFlags & ovrStatus_OrientationTracked) { - result.flags |= State_Orientation; + result.flags |= VRStateValidFlags::State_Orientation; result.orientation[0] = pose.ThePose.Orientation.x; result.orientation[1] = pose.ThePose.Orientation.y; result.orientation[2] = pose.ThePose.Orientation.z; result.orientation[3] = pose.ThePose.Orientation.w; result.angularVelocity[0] = pose.AngularVelocity.x; result.angularVelocity[1] = pose.AngularVelocity.y; result.angularVelocity[2] = pose.AngularVelocity.z; result.angularAcceleration[0] = pose.AngularAcceleration.x; result.angularAcceleration[1] = pose.AngularAcceleration.y; result.angularAcceleration[2] = pose.AngularAcceleration.z; } if (state.StatusFlags & ovrStatus_PositionTracked) { - result.flags |= State_Position; + result.flags |= VRStateValidFlags::State_Position; result.position[0] = pose.ThePose.Position.x; result.position[1] = pose.ThePose.Position.y; result.position[2] = pose.ThePose.Position.z; result.linearVelocity[0] = pose.LinearVelocity.x; result.linearVelocity[1] = pose.LinearVelocity.y; result.linearVelocity[2] = pose.LinearVelocity.z; @@ -469,97 +506,128 @@ HMDInfoOculus050::GetSensorState(double result.linearAcceleration[0] = pose.LinearAcceleration.x; result.linearAcceleration[1] = pose.LinearAcceleration.y; result.linearAcceleration[2] = pose.LinearAcceleration.z; } return result; } -bool -VRHMDManagerOculus050::PlatformInit() +/*static*/ already_AddRefed<VRHMDManagerOculus050> +VRHMDManagerOculus050::Create() { - if (mOculusPlatformInitialized) - return true; + MOZ_ASSERT(NS_IsMainThread()); - if (!gfxPrefs::VREnabled() || - !gfxPrefs::VROculus050Enabled()) + if (!gfxPrefs::VREnabled() || !gfxPrefs::VROculus050Enabled()) { - return false; + return nullptr; } - if (!InitializeOculusCAPI()) - return false; + if (!InitializeOculusCAPI()) { + return nullptr; + } - ovrInitParams params; - params.Flags = ovrInit_RequestVersion; - params.RequestedMinorVersion = LIBOVR_MINOR_VERSION; - params.LogCallback = nullptr; - params.ConnectionTimeoutMS = 0; - - bool ok = ovr_Initialize(¶ms); - - if (!ok) - return false; - - mOculusPlatformInitialized = true; - return true; + RefPtr<VRHMDManagerOculus050> manager = new VRHMDManagerOculus050(); + return manager.forget(); } bool VRHMDManagerOculus050::Init() { - if (mOculusInitialized) - return true; + if (!mOculusInitialized) { + nsIThread* thread = nullptr; + NS_GetCurrentThread(&thread); + mOculusThread = already_AddRefed<nsIThread>(thread); + + ovrInitParams params; + params.Flags = ovrInit_RequestVersion; + params.RequestedMinorVersion = LIBOVR_MINOR_VERSION; + params.LogCallback = nullptr; + params.ConnectionTimeoutMS = 0; + + bool ok = ovr_Initialize(¶ms); + + if (ok) { + mOculusInitialized = true; + } + } + return mOculusInitialized; +} - if (!PlatformInit()) - return false; +void +VRHMDManagerOculus050::Destroy() +{ + if (mOculusInitialized) { + MOZ_ASSERT(NS_GetCurrentThread() == mOculusThread); + mOculusThread = nullptr; + + for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { + mOculusHMDs[i]->Destroy(); + } + + mOculusHMDs.Clear(); + + ovr_Shutdown(); + + mOculusInitialized = false; + } +} + +void +VRHMDManagerOculus050::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) +{ + if (!mOculusInitialized) { + return; + } + + nsTArray<RefPtr<impl::HMDInfoOculus050> > newHMDs; int count = ovrHmd_Detect(); - for (int i = 0; i < count; ++i) { - ovrHmd hmd = ovrHmd_Create(i); - if (hmd) { - RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd); - mOculusHMDs.AppendElement(oc); + for (int j = 0; j < count; ++j) { + bool is_new = true; + for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { + if(mOculusHMDs[i]->GetDeviceID() == j) { + newHMDs.AppendElement(mOculusHMDs[i]); + is_new = false; + break; + } + } + + if(is_new) { + ovrHmd hmd = ovrHmd_Create(j); + if (hmd) { + RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd, false, j); + newHMDs.AppendElement(oc); + } } } // VRAddTestDevices == 1: add test device only if no real devices present // VRAddTestDevices == 2: add test device always if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) || (gfxPrefs::VRAddTestDevices() == 2)) { - ovrHmd hmd = ovrHmd_CreateDebug(ovrHmd_DK2); - if (hmd) { - RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd); - mOculusHMDs.AppendElement(oc); + // Keep existing debug HMD if possible + bool foundDebug = false; + for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { + if (mOculusHMDs[i]->GetIsDebug()) { + newHMDs.AppendElement(mOculusHMDs[i]); + foundDebug = true; + } + } + + // If there isn't already a debug HMD, create one + if (!foundDebug) { + ovrHmd hmd = ovrHmd_CreateDebug(ovrHmd_DK2); + if (hmd) { + RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd, true, -1); + newHMDs.AppendElement(oc); + } } } - mOculusInitialized = true; - return true; -} - -void -VRHMDManagerOculus050::Destroy() -{ - if (!mOculusInitialized) - return; + mOculusHMDs = newHMDs; - for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { - mOculusHMDs[i]->Destroy(); - } - - mOculusHMDs.Clear(); - - ovr_Shutdown(); - mOculusInitialized = false; -} - -void -VRHMDManagerOculus050::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) -{ - Init(); - for (size_t i = 0; i < mOculusHMDs.Length(); ++i) { - aHMDResult.AppendElement(mOculusHMDs[i]); + for (size_t j = 0; j < mOculusHMDs.Length(); ++j) { + aHMDResult.AppendElement(mOculusHMDs[j]); } }
--- a/gfx/vr/gfxVROculus050.h +++ b/gfx/vr/gfxVROculus050.h @@ -2,84 +2,91 @@ * 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/. */ #ifndef GFX_VR_OCULUS_050_H #define GFX_VR_OCULUS_050_H #include "nsTArray.h" -#include "nsIScreen.h" -#include "nsCOMPtr.h" +#include "nsThreadUtils.h" +#include "mozilla/EnumeratedArray.h" +#include "mozilla/gfx/2D.h" #include "mozilla/RefPtr.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/EnumeratedArray.h" - #include "gfxVR.h" #include "ovr_capi_dynamic050.h" namespace mozilla { namespace gfx { namespace impl { class HMDInfoOculus050 : public VRHMDInfo { public: - explicit HMDInfoOculus050(ovr050::ovrHmd aHMD); + explicit HMDInfoOculus050(ovr050::ovrHmd aHMD, bool aDebug, int aDeviceID); bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight, double zNear, double zFar) override; - bool StartSensorTracking() override; VRHMDSensorState GetSensorState(double timeOffset) override; - void StopSensorTracking() override; void ZeroSensor() override; + bool KeepSensorTracking() override; + void NotifyVsync(const TimeStamp& aVsyncTimestamp) override; void FillDistortionConstants(uint32_t whichEye, const IntSize& textureSize, const IntRect& eyeViewport, const Size& destViewport, const Rect& destRect, VRDistortionConstants& values) override; void Destroy(); + bool GetIsDebug() const; + int GetDeviceID() const; protected: + virtual ~HMDInfoOculus050() { + Destroy(); + MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus050, VRHMDInfo); + } + + bool StartSensorTracking(); + void StopSensorTracking(); + // must match the size of VRDistortionVertex struct DistortionVertex { float pos[2]; float texR[2]; float texG[2]; float texB[2]; float genericAttribs[4]; }; - virtual ~HMDInfoOculus050() { - Destroy(); - MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus050, VRHMDInfo); - } - ovr050::ovrHmd mHMD; ovr050::ovrFovPort mFOVPort[2]; - uint32_t mStartCount; + uint32_t mTracking; + bool mDebug; // True if this is a debug HMD + int mDeviceID; // ID of device passed to ovrHmd_Create + + uint32_t mSensorTrackingFramesRemaining; }; } // namespace impl class VRHMDManagerOculus050 : public VRHMDManager { public: - VRHMDManagerOculus050() - : mOculusInitialized(false), mOculusPlatformInitialized(false) - { } - - virtual bool PlatformInit() override; + static already_AddRefed<VRHMDManagerOculus050> Create(); virtual bool Init() override; virtual void Destroy() override; virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo> >& aHMDResult) override; protected: - nsTArray<RefPtr<impl::HMDInfoOculus050>> mOculusHMDs; + VRHMDManagerOculus050() + : mOculusInitialized(false) + { } + + nsTArray<RefPtr<impl::HMDInfoOculus050> > mOculusHMDs; bool mOculusInitialized; - bool mOculusPlatformInitialized; + RefPtr<nsIThread> mOculusThread; }; } // namespace gfx } // namespace mozilla #endif /* GFX_VR_OCULUS_050_H */
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/PVRManager.ipdl @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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 "VRMessageUtils.h"; + +using struct mozilla::gfx::VRFieldOfView from "gfxVR.h"; +using struct mozilla::gfx::VRDeviceUpdate from "gfxVR.h"; +using struct mozilla::gfx::VRSensorUpdate from "gfxVR.h"; + +namespace mozilla { +namespace gfx { + +/** + * The PVRManager protocol is used to enable communication of VR device + * enumeration and sensor state between the compositor thread and + * content threads/processes. + */ +sync protocol PVRManager +{ +parent: + // (Re)Enumerate VR Devices. An updated list of VR devices will be returned + // asynchronously to children via UpdateDeviceInfo. + async RefreshDevices(); + + // Reset the sensor of the device identified by aDeviceID so that the current + // sensor state is the "Zero" position. + async ResetSensor(uint32_t aDeviceID); + + // KeepSensorTracking is called continuously by children to indicate their + // interest in receiving sensor data from the device identified by aDeviceID. + // This will activate any physical sensor tracking system requiring + // initialization and guarantee that it will remain active until at least one + // second has passed since the last KeepSensorTracking call has been made. + // Sensor data will be sent asynchronously via UpdateDeviceSensors + async KeepSensorTracking(uint32_t aDeviceID); + + // Set the field of view parameters for an HMD identified by aDeviceID + async SetFOV(uint32_t aDeviceID, VRFieldOfView aFOVLeft, + VRFieldOfView aFOVRight, double zNear, double zFar); + +child: + + // Notify children of updated VR device enumeration and details. This will + // be sent to all children when the parent receives RefreshDevices, even + // if no changes have been detected. This ensures that Promises exposed + // through DOM calls are always resolved. + async UpdateDeviceInfo(VRDeviceUpdate[] aDeviceUpdates); + + // Notify children of updated VR device sensor states. This will be + // sent once per frame for at least one second after the parent receives + // KeepSensorTracking. + async UpdateDeviceSensors(VRSensorUpdate[] aDeviceSensorUpdates); + + async __delete__(); + +}; + +} // gfx +} // mozilla
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRManagerChild.cpp @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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 "VRManagerChild.h" +#include "VRManagerParent.h" +#include "VRDeviceProxy.h" +#include "VRDeviceProxyOrientationFallBack.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/layers/CompositorParent.h" // for CompositorParent +#include "mozilla/dom/Navigator.h" + +namespace mozilla { +namespace gfx { + +static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton; +static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton; + +void ReleaseVRManagerParentSingleton() { + sVRManagerParentSingleton = nullptr; +} + +VRManagerChild::VRManagerChild() +{ + MOZ_COUNT_CTOR(VRManagerChild); + MOZ_ASSERT(NS_IsMainThread()); +} + +VRManagerChild::~VRManagerChild() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_DTOR(VRManagerChild); + + Transport* trans = GetTransport(); + if (trans) { + MOZ_ASSERT(XRE_GetIOMessageLoop()); + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, + new DeleteTask<Transport>(trans)); + } +} + +/*static*/ VRManagerChild* +VRManagerChild::Get() +{ + MOZ_ASSERT(sVRManagerChildSingleton); + return sVRManagerChildSingleton; +} + +/*static*/ VRManagerChild* +VRManagerChild::StartUpInChildProcess(Transport* aTransport, ProcessId aOtherPid) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // There's only one VRManager per child process. + MOZ_ASSERT(!sVRManagerChildSingleton); + + RefPtr<VRManagerChild> child(new VRManagerChild()); + if (!child->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) { + NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); + return nullptr; + } + + sVRManagerChildSingleton = child; + + return sVRManagerChildSingleton; +} + +/*static*/ void +VRManagerChild::StartUpSameProcess() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); + if (sVRManagerChildSingleton == nullptr) { + sVRManagerChildSingleton = new VRManagerChild(); + sVRManagerParentSingleton = VRManagerParent::CreateSameProcess(); + sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(), + mozilla::layers::CompositorParent::CompositorLoop(), + mozilla::ipc::ChildSide); + } +} + +/*static*/ void +VRManagerChild::ShutDown() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (sVRManagerChildSingleton) { + sVRManagerChildSingleton->Destroy(); + sVRManagerChildSingleton = nullptr; + } +} + +/*static*/ void +VRManagerChild::DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild) +{ + aVRManagerChild->Close(); +} + +void +VRManagerChild::Destroy() +{ + // This must not be called from the destructor! + MOZ_ASSERT(mRefCnt != 0); + + // Keep ourselves alive until everything has been shut down + RefPtr<VRManagerChild> selfRef = this; + + // The DeferredDestroyVRManager task takes ownership of + // the VRManagerChild and will release it when it runs. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(DeferredDestroy, selfRef)); +} + +bool +VRManagerChild::RecvUpdateDeviceInfo(nsTArray<VRDeviceUpdate>&& aDeviceUpdates) +{ + // mDevices could be a hashed container for more scalability, but not worth + // it now as we expect < 10 entries. + nsTArray<RefPtr<VRDeviceProxy> > devices; + for (auto& deviceUpdate: aDeviceUpdates) { + bool isNewDevice = true; + for (auto& device: mDevices) { + if (device->GetDeviceInfo().GetDeviceID() == deviceUpdate.mDeviceInfo.GetDeviceID()) { + device->UpdateDeviceInfo(deviceUpdate); + devices.AppendElement(device); + isNewDevice = false; + break; + } + } + if (isNewDevice) { + if (deviceUpdate.mDeviceInfo.GetUseMainThreadOrientation()) { + devices.AppendElement(new VRDeviceProxyOrientationFallBack(deviceUpdate)); + } else { + devices.AppendElement(new VRDeviceProxy(deviceUpdate)); + } + } + } + + mDevices = devices; + + + for (auto& nav: mNavigatorCallbacks) { + nav->NotifyVRDevicesUpdated(); + } + mNavigatorCallbacks.Clear(); + + return true; +} + +bool +VRManagerChild::RecvUpdateDeviceSensors(nsTArray<VRSensorUpdate>&& aDeviceSensorUpdates) +{ + // mDevices could be a hashed container for more scalability, but not worth + // it now as we expect < 10 entries. + for (auto& sensorUpdate: aDeviceSensorUpdates) { + for (auto& device: mDevices) { + if (device->GetDeviceInfo().GetDeviceID() == sensorUpdate.mDeviceID) { + device->UpdateSensorState(sensorUpdate.mSensorState); + break; + } + } + } + + return true; +} + +bool +VRManagerChild::GetVRDevices(nsTArray<RefPtr<VRDeviceProxy> >& aDevices) +{ + aDevices = mDevices; + return true; +} + +bool +VRManagerChild::RefreshVRDevicesWithCallback(dom::Navigator* aNavigator) +{ + bool success = SendRefreshDevices(); + if (success) { + mNavigatorCallbacks.AppendElement(aNavigator); + } + return success; +} + +} // namespace gfx +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRManagerChild.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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/. */ + +#ifndef MOZILLA_GFX_VR_VRMANAGERCHILD_H +#define MOZILLA_GFX_VR_VRMANAGERCHILD_H + +#include "mozilla/gfx/PVRManagerChild.h" +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" + +namespace mozilla { +namespace dom { +class Navigator; +class VRDevice; +} // namespace dom +namespace gfx { +class VRDeviceProxy; + + +class VRManagerChild : public PVRManagerChild +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRManagerChild) + + bool GetVRDevices(nsTArray<RefPtr<VRDeviceProxy> >& aDevices); + bool RefreshVRDevicesWithCallback(dom::Navigator* aNavigator); + static VRManagerChild* StartUpInChildProcess(Transport* aTransport, + ProcessId aOtherProcess); + + static void StartUpSameProcess(); + static void ShutDown(); + + + static VRManagerChild* Get(); + +protected: + explicit VRManagerChild(); + ~VRManagerChild(); + void Destroy(); + static void DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild); + + virtual bool RecvUpdateDeviceInfo(nsTArray<VRDeviceUpdate>&& aDeviceUpdates) override; + virtual bool RecvUpdateDeviceSensors(nsTArray<VRSensorUpdate>&& aDeviceSensorUpdates) override; + + friend class layers::CompositorChild; + +private: + + nsTArray<RefPtr<VRDeviceProxy> > mDevices; + nsTArray<dom::Navigator*> mNavigatorCallbacks; + +}; + +} // namespace mozilla +} // namespace gfx + +#endif // MOZILLA_GFX_VR_VRMANAGERCHILD_H \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRManagerParent.cpp @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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 "VRManagerParent.h" +#include "mozilla/gfx/PVRManagerParent.h" +#include "mozilla/ipc/ProtocolTypes.h" +#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "mozilla/layers/CompositorParent.h" +#include "mozilla/unused.h" +#include "VRManager.h" + +namespace mozilla { +namespace layers { + +// defined in CompositorParent.cpp +CompositorThreadHolder* GetCompositorThreadHolder(); + +} // namespace layers + +namespace gfx { + +VRManagerParent::VRManagerParent(MessageLoop* aLoop, + Transport* aTransport, + ProcessId aChildProcessId) +{ + MOZ_COUNT_CTOR(VRManagerParent); + MOZ_ASSERT(NS_IsMainThread()); + + SetTransport(aTransport); + SetOtherProcessId(aChildProcessId); +} + +VRManagerParent::~VRManagerParent() +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(!mVRManagerHolder); + + Transport* trans = GetTransport(); + if (trans) { + MOZ_ASSERT(XRE_GetIOMessageLoop()); + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, + new DeleteTask<Transport>(trans)); + } + MOZ_COUNT_DTOR(VRManagerParent); +} + +void VRManagerParent::RegisterWithManager() +{ + VRManager* vm = VRManager::Get(); + vm->AddVRManagerParent(this); + mVRManagerHolder = vm; +} + +void VRManagerParent::UnregisterFromManager() +{ + VRManager* vm = VRManager::Get(); + vm->RemoveVRManagerParent(this); + mVRManagerHolder = nullptr; +} + +/*static*/ void +VRManagerParent::ConnectVRManagerInParentProcess(VRManagerParent* aVRManager, + ipc::Transport* aTransport, + base::ProcessId aOtherPid) +{ + aVRManager->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide); + aVRManager->RegisterWithManager(); +} + +/*static*/ VRManagerParent* +VRManagerParent::CreateCrossProcess(Transport* aTransport, ProcessId aChildProcessId) +{ + MessageLoop* loop = mozilla::layers::CompositorParent::CompositorLoop(); + RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, aTransport, aChildProcessId); + vmp->mSelfRef = vmp; + loop->PostTask(FROM_HERE, + NewRunnableFunction(ConnectVRManagerInParentProcess, + vmp.get(), aTransport, aChildProcessId)); + return vmp.get(); +} + +/*static*/ void +VRManagerParent::RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager) +{ + aVRManager->RegisterWithManager(); +} + +/*static*/ VRManagerParent* +VRManagerParent::CreateSameProcess() +{ + MessageLoop* loop = mozilla::layers::CompositorParent::CompositorLoop(); + RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, nullptr, base::GetCurrentProcId()); + vmp->mCompositorThreadHolder = layers::GetCompositorThreadHolder(); + vmp->mSelfRef = vmp; + loop->PostTask(FROM_HERE, + NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get())); + return vmp.get(); +} + +void +VRManagerParent::DeferredDestroy() +{ + MOZ_ASSERT(mCompositorThreadHolder); + mCompositorThreadHolder = nullptr; + mSelfRef = nullptr; +} + +void +VRManagerParent::ActorDestroy(ActorDestroyReason why) +{ + UnregisterFromManager(); + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &VRManagerParent::DeferredDestroy)); +} + +mozilla::ipc::IToplevelProtocol* +VRManagerParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == unsigned(GetProtocolId())) { + Transport* transport = OpenDescriptor(aFds[i].fd(), + Transport::MODE_SERVER); + PVRManagerParent* vm = CreateCrossProcess(transport, base::GetProcId(aPeerProcess)); + vm->CloneManagees(this, aCtx); + vm->IToplevelProtocol::SetTransport(transport); + // The reference to the compositor thread is held in OnChannelConnected(). + // We need to do this for cloned actors, too. + vm->OnChannelConnected(base::GetProcId(aPeerProcess)); + return vm; + } + } + return nullptr; +} + +void +VRManagerParent::OnChannelConnected(int32_t aPid) +{ + mCompositorThreadHolder = layers::GetCompositorThreadHolder(); +} + +bool +VRManagerParent::RecvRefreshDevices() +{ + VRManager* vm = VRManager::Get(); + vm->RefreshVRDevices(); + + return true; +} + +bool +VRManagerParent::RecvResetSensor(const uint32_t& aDeviceID) +{ + VRManager* vm = VRManager::Get(); + RefPtr<gfx::VRHMDInfo> device = vm->GetDevice(aDeviceID); + if (device != nullptr) { + device->ZeroSensor(); + } + + return true; +} + +bool +VRManagerParent::RecvKeepSensorTracking(const uint32_t& aDeviceID) +{ + VRManager* vm = VRManager::Get(); + RefPtr<gfx::VRHMDInfo> device = vm->GetDevice(aDeviceID); + if (device != nullptr) { + Unused << device->KeepSensorTracking(); + } + return true; +} + +bool +VRManagerParent::RecvSetFOV(const uint32_t& aDeviceID, + const VRFieldOfView& aFOVLeft, + const VRFieldOfView& aFOVRight, + const double& zNear, + const double& zFar) +{ + VRManager* vm = VRManager::Get(); + RefPtr<gfx::VRHMDInfo> device = vm->GetDevice(aDeviceID); + if (device != nullptr) { + device->SetFOV(aFOVLeft, aFOVRight, zNear, zFar); + } + return true; +} + +} // namespace gfx +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRManagerParent.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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/. */ + +#ifndef MOZILLA_GFX_VR_VRMANAGERPARENT_H +#define MOZILLA_GFX_VR_VRMANAGERPARENT_H + +#include "mozilla/layers/CompositorParent.h" // for CompositorThreadHolder +#include "mozilla/gfx/PVRManagerParent.h" // for PVRManagerParent +#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "gfxVR.h" // for VRFieldOfView + +namespace mozilla { +namespace gfx { + +class VRManager; + +class VRManagerParent final : public PVRManagerParent +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRManagerParent) +public: + VRManagerParent(MessageLoop* aLoop, Transport* aTransport, ProcessId aChildProcessId); + + static VRManagerParent* CreateCrossProcess(Transport* aTransport, + ProcessId aOtherProcess); + static VRManagerParent* CreateSameProcess(); + + + // Overriden from IToplevelProtocol + ipc::IToplevelProtocol* + CloneToplevel(const InfallibleTArray<ipc::ProtocolFdMapping>& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) override; + +protected: + ~VRManagerParent(); + + virtual void ActorDestroy(ActorDestroyReason why) override; + void OnChannelConnected(int32_t pid) override; + + virtual bool RecvRefreshDevices() override; + virtual bool RecvResetSensor(const uint32_t& aDeviceID) override; + virtual bool RecvKeepSensorTracking(const uint32_t& aDeviceID) override; + virtual bool RecvSetFOV(const uint32_t& aDeviceID, + const VRFieldOfView& aFOVLeft, + const VRFieldOfView& aFOVRight, + const double& zNear, + const double& zFar) override; + +private: + + void RegisterWithManager(); + void UnregisterFromManager(); + + static void RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager); + static void ConnectVRManagerInParentProcess(VRManagerParent* aVRManager, + ipc::Transport* aTransport, + base::ProcessId aOtherPid); + + void DeferredDestroy(); + + // This keeps us alive until ActorDestroy(), at which point we do a + // deferred destruction of ourselves. + RefPtr<VRManagerParent> mSelfRef; + + // Keep the compositor thread alive, until we have destroyed ourselves. + RefPtr<layers::CompositorThreadHolder> mCompositorThreadHolder; + + // Keep the VRManager alive, until we have destroyed ourselves. + RefPtr<VRManager> mVRManagerHolder; +}; + +} // namespace mozilla +} // namespace gfx + +#endif // MOZILLA_GFX_VR_VRMANAGERPARENT_H
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRMessageUtils.h @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* 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/. */ + +#ifndef mozilla_gfx_vr_VRMessageUtils_h +#define mozilla_gfx_vr_VRMessageUtils_h + +#include "ipc/IPCMessageUtils.h" +#include "mozilla/GfxMessageUtils.h" +#include "VRManager.h" + +#include "gfxVR.h" + +namespace IPC { + +template<> +struct ParamTraits<mozilla::gfx::VRHMDType> : + public ContiguousEnumSerializer<mozilla::gfx::VRHMDType, + mozilla::gfx::VRHMDType(0), + mozilla::gfx::VRHMDType(mozilla::gfx::VRHMDType::NumHMDTypes)> {}; + +template<> +struct ParamTraits<mozilla::gfx::VRStateValidFlags> : + public BitFlagsEnumSerializer<mozilla::gfx::VRStateValidFlags, + mozilla::gfx::VRStateValidFlags::State_All> {}; + +template <> +struct ParamTraits<mozilla::gfx::VRDeviceUpdate> +{ + typedef mozilla::gfx::VRDeviceUpdate paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mDeviceInfo); + WriteParam(aMsg, aParam.mSensorState); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &(aResult->mDeviceInfo)) || + !ReadParam(aMsg, aIter, &(aResult->mSensorState))) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::VRSensorUpdate> +{ + typedef mozilla::gfx::VRSensorUpdate paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mDeviceID); + WriteParam(aMsg, aParam.mSensorState); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &(aResult->mDeviceID)) || + !ReadParam(aMsg, aIter, &(aResult->mSensorState))) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::VRDeviceInfo> +{ + typedef mozilla::gfx::VRDeviceInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mType); + WriteParam(aMsg, aParam.mDeviceID); + WriteParam(aMsg, aParam.mDeviceName); + WriteParam(aMsg, aParam.mSupportedSensorBits); + WriteParam(aMsg, aParam.mEyeResolution); + WriteParam(aMsg, aParam.mScreenRect); + WriteParam(aMsg, aParam.mIsFakeScreen); + WriteParam(aMsg, aParam.mUseMainThreadOrientation); + for (int i = 0; i < mozilla::gfx::VRDeviceInfo::NumEyes; i++) { + WriteParam(aMsg, aParam.mMaximumEyeFOV[i]); + WriteParam(aMsg, aParam.mRecommendedEyeFOV[i]); + WriteParam(aMsg, aParam.mEyeFOV[i]); + WriteParam(aMsg, aParam.mEyeTranslation[i]); + WriteParam(aMsg, aParam.mEyeProjectionMatrix[i]); + } + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &(aResult->mType)) || + !ReadParam(aMsg, aIter, &(aResult->mDeviceID)) || + !ReadParam(aMsg, aIter, &(aResult->mDeviceName)) || + !ReadParam(aMsg, aIter, &(aResult->mSupportedSensorBits)) || + !ReadParam(aMsg, aIter, &(aResult->mEyeResolution)) || + !ReadParam(aMsg, aIter, &(aResult->mScreenRect)) || + !ReadParam(aMsg, aIter, &(aResult->mIsFakeScreen)) || + !ReadParam(aMsg, aIter, &(aResult->mUseMainThreadOrientation)) + ) { + return false; + } + for (int i = 0; i < mozilla::gfx::VRDeviceInfo::NumEyes; i++) { + if (!ReadParam(aMsg, aIter, &(aResult->mMaximumEyeFOV[i])) || + !ReadParam(aMsg, aIter, &(aResult->mRecommendedEyeFOV[i])) || + !ReadParam(aMsg, aIter, &(aResult->mEyeFOV[i])) || + !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i])) || + !ReadParam(aMsg, aIter, &(aResult->mEyeProjectionMatrix[i]))) { + return false; + } + } + + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::VRHMDSensorState> +{ + typedef mozilla::gfx::VRHMDSensorState paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.timestamp); + WriteParam(aMsg, aParam.flags); + WriteParam(aMsg, aParam.orientation[0]); + WriteParam(aMsg, aParam.orientation[1]); + WriteParam(aMsg, aParam.orientation[2]); + WriteParam(aMsg, aParam.orientation[3]); + WriteParam(aMsg, aParam.position[0]); + WriteParam(aMsg, aParam.position[1]); + WriteParam(aMsg, aParam.position[2]); + WriteParam(aMsg, aParam.angularVelocity[0]); + WriteParam(aMsg, aParam.angularVelocity[1]); + WriteParam(aMsg, aParam.angularVelocity[2]); + WriteParam(aMsg, aParam.angularAcceleration[0]); + WriteParam(aMsg, aParam.angularAcceleration[1]); + WriteParam(aMsg, aParam.angularAcceleration[2]); + WriteParam(aMsg, aParam.linearVelocity[0]); + WriteParam(aMsg, aParam.linearVelocity[1]); + WriteParam(aMsg, aParam.linearVelocity[2]); + WriteParam(aMsg, aParam.linearAcceleration[0]); + WriteParam(aMsg, aParam.linearAcceleration[1]); + WriteParam(aMsg, aParam.linearAcceleration[2]); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &(aResult->timestamp)) || + !ReadParam(aMsg, aIter, &(aResult->flags)) || + !ReadParam(aMsg, aIter, &(aResult->orientation[0])) || + !ReadParam(aMsg, aIter, &(aResult->orientation[1])) || + !ReadParam(aMsg, aIter, &(aResult->orientation[2])) || + !ReadParam(aMsg, aIter, &(aResult->orientation[3])) || + !ReadParam(aMsg, aIter, &(aResult->position[0])) || + !ReadParam(aMsg, aIter, &(aResult->position[1])) || + !ReadParam(aMsg, aIter, &(aResult->position[2])) || + !ReadParam(aMsg, aIter, &(aResult->angularVelocity[0])) || + !ReadParam(aMsg, aIter, &(aResult->angularVelocity[1])) || + !ReadParam(aMsg, aIter, &(aResult->angularVelocity[2])) || + !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[0])) || + !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[1])) || + !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[2])) || + !ReadParam(aMsg, aIter, &(aResult->linearVelocity[0])) || + !ReadParam(aMsg, aIter, &(aResult->linearVelocity[1])) || + !ReadParam(aMsg, aIter, &(aResult->linearVelocity[2])) || + !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[0])) || + !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[1])) || + !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2]))) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::VRFieldOfView> +{ + typedef mozilla::gfx::VRFieldOfView paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.upDegrees); + WriteParam(aMsg, aParam.rightDegrees); + WriteParam(aMsg, aParam.downDegrees); + WriteParam(aMsg, aParam.leftDegrees); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &(aResult->upDegrees)) || + !ReadParam(aMsg, aIter, &(aResult->rightDegrees)) || + !ReadParam(aMsg, aIter, &(aResult->downDegrees)) || + !ReadParam(aMsg, aIter, &(aResult->leftDegrees))) { + return false; + } + + return true; + } +}; + +} // namespace IPC + +#endif // mozilla_gfx_vr_VRMessageUtils_h
--- a/gfx/vr/moz.build +++ b/gfx/vr/moz.build @@ -1,32 +1,46 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. EXPORTS += [ 'gfxVR.h', + 'ipc/VRManagerChild.h', + 'ipc/VRManagerParent.h', + 'ipc/VRMessageUtils.h', + 'VRDeviceProxy.h', + 'VRManager.h', ] LOCAL_INCLUDES += [ '/gfx/thebes', ] UNIFIED_SOURCES += [ 'gfxVR.cpp', 'gfxVRCardboard.cpp', 'gfxVROculus.cpp', + 'ipc/VRManagerChild.cpp', + 'ipc/VRManagerParent.cpp', + 'VRDeviceProxy.cpp', + 'VRDeviceProxyOrientationFallBack.cpp', + 'VRManager.cpp', ] SOURCES += [ 'gfxVROculus050.cpp', ] +IPDL_SOURCES = [ + 'ipc/PVRManager.ipdl', +] + # For building with the real SDK instead of our local hack #SOURCES += [ # 'OVR_CAPI_Util.cpp', # 'OVR_CAPIShim.c', # 'OVR_StereoProjection.cpp', #] # #CXXFLAGS += ["-Ic:/proj/ovr/OculusSDK-0.6.0-beta/LibOVR/Include"]
--- a/image/Downscaler.h +++ b/image/Downscaler.h @@ -53,16 +53,17 @@ class Downscaler { public: /// Constructs a new Downscaler which to scale to size @aTargetSize. explicit Downscaler(const nsIntSize& aTargetSize); ~Downscaler(); const nsIntSize& OriginalSize() const { return mOriginalSize; } const nsIntSize& TargetSize() const { return mTargetSize; } + const nsIntSize FrameSize() const { return nsIntSize(mFrameRect.width, mFrameRect.height); } const gfxSize& Scale() const { return mScale; } /** * Begins a new frame and reinitializes the Downscaler. * * @param aOriginalSize The original size of this frame, before scaling. * @param aFrameRect The region of the original image which has data. * Every pixel outside @aFrameRect is considered blank and
--- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -1172,17 +1172,17 @@ nsGIFDecoder2::WriteInternal(const char* } } } if (q[8] & 0x40) { mGIFStruct.interlaced = true; mGIFStruct.ipass = 1; if (mDownscaler) { - mDeinterlacer.emplace(mDownscaler->OriginalSize()); + mDeinterlacer.emplace(mDownscaler->FrameSize()); } } else { mGIFStruct.interlaced = false; mGIFStruct.ipass = 0; } // Only apply the Haeberli display hack on the first frame mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
--- a/intl/uconv/ucvja/nsJapaneseToUnicode.cpp +++ b/intl/uconv/ucvja/nsJapaneseToUnicode.cpp @@ -331,16 +331,17 @@ NS_IMETHODIMP nsEUCJPToUnicodeV2::Conver if(0xFF != off) { *dest++ = gJapaneseMap[mData+off]; mState = 0; if(dest >= destEnd) goto error1; break; } // else fall through to error handler + MOZ_FALLTHROUGH; } case 5: // two bytes undefined { if (mErrBehavior == kOnError_Signal) goto error_invalidchar; *dest++ = 0xFFFD; // Undefined JIS 0212 two byte sequence. If the second byte is in // the valid range for a two byte sequence (0xa1 - 0xfe) consume @@ -366,18 +367,16 @@ error1: src++; if ((mState == 0) && (src == srcEnd)) { return NS_OK; } *aSrcLen = src - (const unsigned char*)aSrc; return NS_OK_UDEC_MOREOUTPUT; } - - NS_IMETHODIMP nsISO2022JPToUnicodeV2::Convert( const char * aSrc, int32_t * aSrcLen, char16_t * aDest, int32_t * aDestLen) { static const uint16_t fbIdx[128] = { /* 0x8X */ 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
--- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -14,22 +14,24 @@ #include "js/TypeDecls.h" #include "js/Utility.h" struct JSRuntime; class JSScript; namespace js { class Activation; - class AsmJSProfilingFrameIterator; namespace jit { class JitActivation; class JitProfilingFrameIterator; class JitcodeGlobalEntry; } // namespace jit + namespace wasm { + class ProfilingFrameIterator; + } // namespace wasm } // namespace js namespace JS { struct ForEachTrackedOptimizationAttemptOp; struct ForEachTrackedOptimizationTypeInfoOp; // This iterator can be used to walk the stack of a thread suspended at an @@ -44,25 +46,25 @@ class JS_PUBLIC_API(ProfilingFrameIterat // When moving past a JitActivation, we need to save the prevJitTop // from it to use as the exit-frame pointer when the next caller jit // activation (if any) comes around. void* savedPrevJitTop_; static const unsigned StorageSpace = 8 * sizeof(void*); mozilla::AlignedStorage<StorageSpace> storage_; - js::AsmJSProfilingFrameIterator& asmJSIter() { + js::wasm::ProfilingFrameIterator& asmJSIter() { MOZ_ASSERT(!done()); MOZ_ASSERT(isAsmJS()); - return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr()); + return *reinterpret_cast<js::wasm::ProfilingFrameIterator*>(storage_.addr()); } - const js::AsmJSProfilingFrameIterator& asmJSIter() const { + const js::wasm::ProfilingFrameIterator& asmJSIter() const { MOZ_ASSERT(!done()); MOZ_ASSERT(isAsmJS()); - return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr()); + return *reinterpret_cast<const js::wasm::ProfilingFrameIterator*>(storage_.addr()); } js::jit::JitProfilingFrameIterator& jitIter() { MOZ_ASSERT(!done()); MOZ_ASSERT(isJit()); return *reinterpret_cast<js::jit::JitProfilingFrameIterator*>(storage_.addr()); }
rename from js/src/asmjs/AsmJSValidate.cpp rename to js/src/asmjs/AsmJS.cpp --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -11,54 +11,756 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -#include "asmjs/AsmJSValidate.h" - -#include "mozilla/Move.h" -#include "mozilla/UniquePtr.h" +#include "asmjs/AsmJS.h" + +#include "mozilla/Compression.h" +#include "mozilla/MathAlgorithms.h" #include "jsmath.h" #include "jsprf.h" #include "jsutil.h" -#include "asmjs/AsmJSLink.h" -#include "asmjs/AsmJSModule.h" +#include "jswrapper.h" + #include "asmjs/WasmGenerator.h" +#include "asmjs/WasmSerialize.h" #include "builtin/SIMD.h" #include "frontend/Parser.h" #include "jit/AtomicOperations.h" #include "jit/MIR.h" +#include "js/Class.h" +#include "js/MemoryMetrics.h" +#include "vm/StringBuffer.h" #include "vm/Time.h" +#include "vm/TypedArrayObject.h" #include "jsobjinlines.h" #include "frontend/ParseNode-inl.h" #include "frontend/Parser-inl.h" +#include "vm/ArrayBufferObject-inl.h" using namespace js; using namespace js::frontend; using namespace js::jit; using namespace js::wasm; +using mozilla::Compression::LZ4; using mozilla::HashGeneric; using mozilla::IsNaN; using mozilla::IsNegativeZero; +using mozilla::MallocSizeOf; using mozilla::Move; +using mozilla::PodCopy; +using mozilla::PodEqual; +using mozilla::PodZero; using mozilla::PositiveInfinity; -using mozilla::UniquePtr; using JS::AsmJSOption; using JS::GenericNaN; /*****************************************************************************/ +// asm.js module object + +// The asm.js spec recognizes this set of builtin Math functions. +enum AsmJSMathBuiltinFunction +{ + AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan, + AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan, + AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp, + AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt, + AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul, + AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max, + AsmJSMathBuiltin_clz32 +}; + +// The asm.js spec will recognize this set of builtin Atomics functions. +enum AsmJSAtomicsBuiltinFunction +{ + AsmJSAtomicsBuiltin_compareExchange, + AsmJSAtomicsBuiltin_exchange, + AsmJSAtomicsBuiltin_load, + AsmJSAtomicsBuiltin_store, + AsmJSAtomicsBuiltin_fence, + AsmJSAtomicsBuiltin_add, + AsmJSAtomicsBuiltin_sub, + AsmJSAtomicsBuiltin_and, + AsmJSAtomicsBuiltin_or, + AsmJSAtomicsBuiltin_xor, + AsmJSAtomicsBuiltin_isLockFree +}; + +// Set of known global object SIMD's attributes, i.e. types +enum AsmJSSimdType +{ + AsmJSSimdType_int32x4, + AsmJSSimdType_float32x4, + AsmJSSimdType_bool32x4 +}; + +static inline bool +IsSignedIntSimdType(AsmJSSimdType type) +{ + switch (type) { + case AsmJSSimdType_int32x4: + return true; + case AsmJSSimdType_float32x4: + case AsmJSSimdType_bool32x4: + return false; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type"); +} + +// Set of known operations, for a given SIMD type (int32x4, float32x4,...) +enum AsmJSSimdOperation +{ +#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op, + FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION) +#undef ASMJSSIMDOPERATION +}; + +// An AsmJSModule extends (via containment) a wasm::Module with the extra +// persistent state necessary to represent a compiled asm.js module. +class js::AsmJSModule +{ + public: + class Global + { + public: + enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, + AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength }; + enum VarInitKind { InitConstant, InitImport }; + enum ConstantKind { GlobalConstant, MathConstant }; + + private: + struct CacheablePod { + Which which_; + union { + struct { + uint32_t globalDataOffset_; + VarInitKind initKind_; + union { + wasm::ValType importType_; + wasm::Val val_; + } u; + } var; + uint32_t ffiIndex_; + Scalar::Type viewType_; + AsmJSMathBuiltinFunction mathBuiltinFunc_; + AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_; + AsmJSSimdType simdCtorType_; + struct { + AsmJSSimdType type_; + AsmJSSimdOperation which_; + } simdOp; + struct { + ConstantKind kind_; + double value_; + } constant; + } u; + } pod; + PropertyName* name_; + + friend class AsmJSModule; + + Global(Which which, PropertyName* name) { + mozilla::PodZero(&pod); // zero padding for Valgrind + pod.which_ = which; + name_ = name; + MOZ_ASSERT_IF(name_, name_->isTenured()); + } + + void trace(JSTracer* trc) { + if (name_) + TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name"); + } + + public: + Global() {} + Which which() const { + return pod.which_; + } + uint32_t varGlobalDataOffset() const { + MOZ_ASSERT(pod.which_ == Variable); + return pod.u.var.globalDataOffset_; + } + VarInitKind varInitKind() const { + MOZ_ASSERT(pod.which_ == Variable); + return pod.u.var.initKind_; + } + wasm::Val varInitVal() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitConstant); + return pod.u.var.u.val_; + } + wasm::ValType varInitImportType() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitImport); + return pod.u.var.u.importType_; + } + PropertyName* varImportField() const { + MOZ_ASSERT(pod.which_ == Variable); + MOZ_ASSERT(pod.u.var.initKind_ == InitImport); + return name_; + } + PropertyName* ffiField() const { + MOZ_ASSERT(pod.which_ == FFI); + return name_; + } + uint32_t ffiIndex() const { + MOZ_ASSERT(pod.which_ == FFI); + return pod.u.ffiIndex_; + } + // When a view is created from an imported constructor: + // var I32 = stdlib.Int32Array; + // var i32 = new I32(buffer); + // the second import has nothing to validate and thus has a null field. + PropertyName* maybeViewName() const { + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); + return name_; + } + Scalar::Type viewType() const { + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); + return pod.u.viewType_; + } + PropertyName* mathName() const { + MOZ_ASSERT(pod.which_ == MathBuiltinFunction); + return name_; + } + PropertyName* atomicsName() const { + MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); + return name_; + } + AsmJSMathBuiltinFunction mathBuiltinFunction() const { + MOZ_ASSERT(pod.which_ == MathBuiltinFunction); + return pod.u.mathBuiltinFunc_; + } + AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const { + MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); + return pod.u.atomicsBuiltinFunc_; + } + AsmJSSimdType simdCtorType() const { + MOZ_ASSERT(pod.which_ == SimdCtor); + return pod.u.simdCtorType_; + } + PropertyName* simdCtorName() const { + MOZ_ASSERT(pod.which_ == SimdCtor); + return name_; + } + PropertyName* simdOperationName() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return name_; + } + AsmJSSimdOperation simdOperation() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return pod.u.simdOp.which_; + } + AsmJSSimdType simdOperationType() const { + MOZ_ASSERT(pod.which_ == SimdOperation); + return pod.u.simdOp.type_; + } + PropertyName* constantName() const { + MOZ_ASSERT(pod.which_ == Constant); + return name_; + } + ConstantKind constantKind() const { + MOZ_ASSERT(pod.which_ == Constant); + return pod.u.constant.kind_; + } + double constantValue() const { + MOZ_ASSERT(pod.which_ == Constant); + return pod.u.constant.value_; + } + + WASM_DECLARE_SERIALIZABLE(Global); + }; + + typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector; + + // An import is slightly different than an asm.js FFI function: a single + // asm.js FFI function can be called with many different signatures. When + // compiled to wasm, each unique FFI function paired with signature + // generates a wasm import. + class Import + { + uint32_t ffiIndex_; + public: + Import() = default; + explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {} + uint32_t ffiIndex() const { return ffiIndex_; } + }; + + typedef Vector<Import, 0, SystemAllocPolicy> ImportVector; + + class Export + { + PropertyName* name_; + PropertyName* maybeFieldName_; + struct CacheablePod { + uint32_t wasmIndex_; + uint32_t startOffsetInModule_; // Store module-start-relative offsets + uint32_t endOffsetInModule_; // so preserved by serialization. + } pod; + + public: + Export() {} + Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex, + uint32_t startOffsetInModule, uint32_t endOffsetInModule) + : name_(name), + maybeFieldName_(maybeFieldName) + { + MOZ_ASSERT(name_->isTenured()); + MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured()); + pod.wasmIndex_ = wasmIndex; + pod.startOffsetInModule_ = startOffsetInModule; + pod.endOffsetInModule_ = endOffsetInModule; + } + + void trace(JSTracer* trc) { + TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name"); + if (maybeFieldName_) + TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field"); + } + + PropertyName* name() const { + return name_; + } + PropertyName* maybeFieldName() const { + return maybeFieldName_; + } + uint32_t startOffsetInModule() const { + return pod.startOffsetInModule_; + } + uint32_t endOffsetInModule() const { + return pod.endOffsetInModule_; + } + static const uint32_t ChangeHeap = UINT32_MAX; + bool isChangeHeap() const { + return pod.wasmIndex_ == ChangeHeap; + } + uint32_t wasmIndex() const { + MOZ_ASSERT(!isChangeHeap()); + return pod.wasmIndex_; + } + + WASM_DECLARE_SERIALIZABLE(Export) + }; + + typedef Vector<Export, 0, SystemAllocPolicy> ExportVector; + + typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule; + + private: + UniqueWasmModule wasmModule_; + wasm::UniqueStaticLinkData linkData_; + struct CacheablePod { + uint32_t minHeapLength_; + uint32_t maxHeapLength_; + uint32_t heapLengthMask_; + uint32_t numFFIs_; + uint32_t srcLength_; + uint32_t srcLengthWithRightBrace_; + bool strict_; + bool hasArrayView_; + bool isSharedView_; + bool hasFixedMinHeapLength_; + } pod; + const ScriptSourceHolder scriptSource_; + const uint32_t srcStart_; + const uint32_t srcBodyStart_; + GlobalVector globals_; + ImportVector imports_; + ExportVector exports_; + PropertyName* globalArgumentName_; + PropertyName* importArgumentName_; + PropertyName* bufferArgumentName_; + + public: + explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart, + bool strict) + : scriptSource_(scriptSource), + srcStart_(srcStart), + srcBodyStart_(srcBodyStart), + globalArgumentName_(nullptr), + importArgumentName_(nullptr), + bufferArgumentName_(nullptr) + { + mozilla::PodZero(&pod); + pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0); + pod.maxHeapLength_ = 0x80000000; + pod.strict_ = strict; + + MOZ_ASSERT(srcStart_ <= srcBodyStart_); + + // AsmJSCheckedImmediateRange should be defined to be at most the minimum + // heap length so that offsets can be folded into bounds checks. + MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_); + } + + void trace(JSTracer* trc) { + if (wasmModule_) + wasmModule_->trace(trc); + for (Global& global : globals_) + global.trace(trc); + for (Export& exp : exports_) + exp.trace(trc); + if (globalArgumentName_) + TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name"); + if (importArgumentName_) + TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name"); + if (bufferArgumentName_) + TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name"); + } + + /*************************************************************************/ + // These functions may be used as soon as the module is constructed: + + ScriptSource* scriptSource() const { + return scriptSource_.get(); + } + bool strict() const { + return pod.strict_; + } + + // srcStart() refers to the offset in the ScriptSource to the beginning of + // the asm.js module function. If the function has been created with the + // Function constructor, this will be the first character in the function + // source. Otherwise, it will be the opening parenthesis of the arguments + // list. + uint32_t srcStart() const { + return srcStart_; + } + + // srcBodyStart() refers to the offset in the ScriptSource to the end + // of the 'use asm' string-literal token. + uint32_t srcBodyStart() const { + return srcBodyStart_; + } + + // While these functions may be accessed at any time, their values will + // change as the module is compiled. + uint32_t minHeapLength() const { + return pod.minHeapLength_; + } + uint32_t maxHeapLength() const { + return pod.maxHeapLength_; + } + uint32_t heapLengthMask() const { + MOZ_ASSERT(pod.hasFixedMinHeapLength_); + return pod.heapLengthMask_; + } + + void initGlobalArgumentName(PropertyName* n) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT_IF(n, n->isTenured()); + globalArgumentName_ = n; + } + void initImportArgumentName(PropertyName* n) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT_IF(n, n->isTenured()); + importArgumentName_ = n; + } + void initBufferArgumentName(PropertyName* n) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT_IF(n, n->isTenured()); + bufferArgumentName_ = n; + } + PropertyName* globalArgumentName() const { + return globalArgumentName_; + } + PropertyName* importArgumentName() const { + return importArgumentName_; + } + PropertyName* bufferArgumentName() const { + return bufferArgumentName_; + } + + bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Variable, nullptr); + g.pod.u.var.initKind_ = Global::InitConstant; + g.pod.u.var.u.val_ = v; + g.pod.u.var.globalDataOffset_ = globalDataOffset; + return globals_.append(g); + } + bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Variable, name); + g.pod.u.var.initKind_ = Global::InitImport; + g.pod.u.var.u.importType_ = importType; + g.pod.u.var.globalDataOffset_ = globalDataOffset; + return globals_.append(g); + } + // See Import comment above for FFI vs. Import. + bool addFFI(PropertyName* field, uint32_t* ffiIndex) { + MOZ_ASSERT(!isFinished()); + if (pod.numFFIs_ == UINT32_MAX) + return false; + Global g(Global::FFI, field); + g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++; + return globals_.append(g); + } + bool addArrayView(Scalar::Type vt, PropertyName* maybeField) { + MOZ_ASSERT(!isFinished()); + pod.hasArrayView_ = true; + pod.isSharedView_ = false; + Global g(Global::ArrayView, maybeField); + g.pod.u.viewType_ = vt; + return globals_.append(g); + } + bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT(field); + pod.isSharedView_ = false; + Global g(Global::ArrayViewCtor, field); + g.pod.u.viewType_ = vt; + return globals_.append(g); + } + bool addByteLength() { + MOZ_ASSERT(!isFinished()); + Global g(Global::ByteLength, nullptr); + return globals_.append(g); + } + bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::MathBuiltinFunction, field); + g.pod.u.mathBuiltinFunc_ = func; + return globals_.append(g); + } + bool addMathBuiltinConstant(double value, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Constant, field); + g.pod.u.constant.value_ = value; + g.pod.u.constant.kind_ = Global::MathConstant; + return globals_.append(g); + } + bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::AtomicsBuiltinFunction, field); + g.pod.u.atomicsBuiltinFunc_ = func; + return globals_.append(g); + } + bool addSimdCtor(AsmJSSimdType type, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::SimdCtor, field); + g.pod.u.simdCtorType_ = type; + return globals_.append(g); + } + bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) { + MOZ_ASSERT(!isFinished()); + Global g(Global::SimdOperation, field); + g.pod.u.simdOp.type_ = type; + g.pod.u.simdOp.which_ = op; + return globals_.append(g); + } + bool addGlobalConstant(double value, PropertyName* name) { + MOZ_ASSERT(!isFinished()); + Global g(Global::Constant, name); + g.pod.u.constant.value_ = value; + g.pod.u.constant.kind_ = Global::GlobalConstant; + return globals_.append(g); + } + // See Import comment above for FFI vs. Import. + bool addImport(uint32_t ffiIndex, uint32_t importIndex) { + MOZ_ASSERT(imports_.length() == importIndex); + return imports_.emplaceBack(ffiIndex); + } + bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex, + uint32_t funcSrcBegin, uint32_t funcSrcEnd) + { + // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource + // (the entire file) and ExportedFunctions store offsets relative to + // the beginning of the module (so that they are caching-invariant). + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT(srcStart_ < funcSrcBegin); + MOZ_ASSERT(funcSrcBegin < funcSrcEnd); + return exports_.emplaceBack(name, maybeFieldName, wasmIndex, + funcSrcBegin - srcStart_, funcSrcEnd - srcStart_); + } + bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) { + MOZ_ASSERT(!isFinished()); + MOZ_ASSERT(!pod.hasFixedMinHeapLength_); + MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1)); + MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0)); + MOZ_ASSERT(max <= pod.maxHeapLength_); + MOZ_ASSERT(min <= max); + pod.heapLengthMask_ = mask; + pod.minHeapLength_ = min; + pod.maxHeapLength_ = max; + pod.hasFixedMinHeapLength_ = true; + return true; + } + + const GlobalVector& globals() const { + return globals_; + } + const ImportVector& imports() const { + return imports_; + } + const ExportVector& exports() const { + return exports_; + } + + void setViewsAreShared() { + if (pod.hasArrayView_) + pod.isSharedView_ = true; + } + bool hasArrayView() const { + return pod.hasArrayView_; + } + bool isSharedView() const { + return pod.isSharedView_; + } + bool tryRequireHeapLengthToBeAtLeast(uint32_t len) { + MOZ_ASSERT(!isFinished()); + if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_) + return false; + if (len > pod.maxHeapLength_) + return false; + len = RoundUpToNextValidAsmJSHeapLength(len); + if (len > pod.minHeapLength_) + pod.minHeapLength_ = len; + return true; + } + + /*************************************************************************/ + // A module isFinished() when compilation completes. After being finished, + // a module must be statically and dynamically linked before execution. + + bool isFinished() const { + return !!wasmModule_; + } + void finish(wasm::Module* wasmModule, wasm::UniqueStaticLinkData linkData, + uint32_t endBeforeCurly, uint32_t endAfterCurly) + { + MOZ_ASSERT(!isFinished()); + + wasmModule_.reset(wasmModule); + linkData_ = Move(linkData); + + MOZ_ASSERT(endBeforeCurly >= srcBodyStart_); + MOZ_ASSERT(endAfterCurly >= srcBodyStart_); + pod.srcLength_ = endBeforeCurly - srcStart_; + pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_; + + MOZ_ASSERT(isFinished()); + } + + /*************************************************************************/ + // These accessor functions can only be used after finish(): + + wasm::Module& wasmModule() const { + MOZ_ASSERT(isFinished()); + return *wasmModule_; + } + uint32_t numFFIs() const { + MOZ_ASSERT(isFinished()); + return pod.numFFIs_; + } + uint32_t srcEndBeforeCurly() const { + MOZ_ASSERT(isFinished()); + return srcStart_ + pod.srcLength_; + } + uint32_t srcEndAfterCurly() const { + MOZ_ASSERT(isFinished()); + return srcStart_ + pod.srcLengthWithRightBrace_; + } + bool staticallyLink(ExclusiveContext* cx) { + return wasmModule_->staticallyLink(cx, *linkData_); + } + + // See WASM_DECLARE_SERIALIZABLE. + size_t serializedSize() const; + uint8_t* serialize(uint8_t* cursor) const; + const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor); + bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const; + void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data); +}; + +static void +AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj) +{ + AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>(); + if (moduleObj.hasModule()) + fop->delete_(&moduleObj.module()); +} + +static void +AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj) +{ + AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>(); + if (moduleObj.hasModule()) + moduleObj.module().trace(trc); +} + +const Class AsmJSModuleObject::class_ = { + "AsmJSModuleObject", + JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK | + JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS), + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + AsmJSModuleObject_finalize, + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + AsmJSModuleObject_trace +}; + +static AsmJSModuleObject* +NewAsmJSModuleObject(ExclusiveContext* cx) +{ + AutoSetNewObjectMetadata metadata(cx); + JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr); + if (!obj) + return nullptr; + + return &obj->as<AsmJSModuleObject>(); +} + +bool +AsmJSModuleObject::hasModule() const +{ + MOZ_ASSERT(is<AsmJSModuleObject>()); + return !getReservedSlot(MODULE_SLOT).isUndefined(); +} + +void +AsmJSModuleObject::setModule(AsmJSModule* newModule) +{ + MOZ_ASSERT(is<AsmJSModuleObject>()); + if (hasModule()) + js_delete(&module()); + setReservedSlot(MODULE_SLOT, PrivateValue(newModule)); +} + +AsmJSModule& +AsmJSModuleObject::module() const +{ + MOZ_ASSERT(is<AsmJSModuleObject>()); + return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate(); +} + +void +AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) +{ + module().addSizeOfMisc(mallocSizeOf, code, data); +} + +/*****************************************************************************/ // ParseNode utilities static inline ParseNode* NextNode(ParseNode* pn) { return pn->pn_next; } @@ -535,28 +1237,28 @@ class NumLit Bool32x4, OutOfRangeInt = -1 }; private: Which which_; union { Value scalar_; - jit::SimdConstant simd_; + SimdConstant simd_; } u; public: NumLit() = default; NumLit(Which w, Value v) : which_(w) { u.scalar_ = v; MOZ_ASSERT(!isSimd()); } - NumLit(Which w, jit::SimdConstant c) : which_(w) { + NumLit(Which w, SimdConstant c) : which_(w) { u.simd_ = c; MOZ_ASSERT(isSimd()); } Which which() const { return which_; } @@ -838,40 +1540,40 @@ class Type return ValType::F32; else if (isDouble()) return ValType::F64; else if (isInt32x4()) return ValType::I32x4; return ValType::F32x4; } - jit::MIRType toMIRType() const { + MIRType toMIRType() const { switch (which_) { case Double: case DoubleLit: case MaybeDouble: - return jit::MIRType_Double; + return MIRType_Double; case Float: case Floatish: case MaybeFloat: - return jit::MIRType_Float32; + return MIRType_Float32; case Fixnum: case Int: case Signed: case Unsigned: case Intish: - return jit::MIRType_Int32; + return MIRType_Int32; case Int32x4: - return jit::MIRType_Int32x4; + return MIRType_Int32x4; case Float32x4: - return jit::MIRType_Float32x4; + return MIRType_Float32x4; case Bool32x4: - return jit::MIRType_Bool32x4; + return MIRType_Bool32x4; case Void: - return jit::MIRType_None; + return MIRType_None; } MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type"); } AsmJSSimdType simdType() const { MOZ_ASSERT(isSimd()); switch (which_) { case Int32x4: @@ -1159,23 +1861,23 @@ class MOZ_STACK_CLASS ModuleValidator ArrayView(PropertyName* name, Scalar::Type type) : name(name), type(type) {} PropertyName* name; Scalar::Type type; }; - class ExitDescriptor + class ImportDescriptor { PropertyName* name_; const LifoSig* sig_; public: - ExitDescriptor(PropertyName* name, const LifoSig& sig) + ImportDescriptor(PropertyName* name, const LifoSig& sig) : name_(name), sig_(&sig) {} PropertyName* name() const { return name_; } const LifoSig& sig() const { return *sig_; @@ -1184,71 +1886,72 @@ class MOZ_STACK_CLASS ModuleValidator struct Lookup { // implements HashPolicy PropertyName* name_; const MallocSig& sig_; Lookup(PropertyName* name, const MallocSig& sig) : name_(name), sig_(sig) {} }; static HashNumber hash(const Lookup& l) { return HashGeneric(l.name_, l.sig_.hash()); } - static bool match(const ExitDescriptor& lhs, const Lookup& rhs) { + static bool match(const ImportDescriptor& lhs, const Lookup& rhs) { return lhs.name_ == rhs.name_ && *lhs.sig_ == rhs.sig_; } }; private: typedef HashMap<PropertyName*, Global*> GlobalMap; typedef HashMap<PropertyName*, MathBuiltin> MathNameMap; typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap; typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap; typedef Vector<ArrayView> ArrayViewVector; public: - typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap; + typedef HashMap<ImportDescriptor, unsigned, ImportDescriptor> ImportMap; private: - ExclusiveContext* cx_; - AsmJSParser& parser_; - - ModuleGenerator mg_; - - LifoAlloc validationLifo_; - FuncVector functions_; - FuncPtrTableVector funcPtrTables_; - GlobalMap globals_; - ArrayViewVector arrayViews_; - ExitMap exits_; - - MathNameMap standardLibraryMathNames_; - AtomicsNameMap standardLibraryAtomicsNames_; - SimdOperationNameMap standardLibrarySimdOpNames_; - - ParseNode* moduleFunctionNode_; - PropertyName* moduleFunctionName_; - - UniquePtr<char[], JS::FreePolicy> errorString_; - uint32_t errorOffset_; - bool errorOverRecursed_; - - bool canValidateChangeHeap_; - bool hasChangeHeap_; - bool supportsSimd_; - bool atomicsPresent_; + ExclusiveContext* cx_; + AsmJSParser& parser_; + + ModuleGenerator mg_; + AsmJSModule* module_; + + LifoAlloc validationLifo_; + FuncVector functions_; + FuncPtrTableVector funcPtrTables_; + GlobalMap globals_; + ArrayViewVector arrayViews_; + ImportMap imports_; + + MathNameMap standardLibraryMathNames_; + AtomicsNameMap standardLibraryAtomicsNames_; + SimdOperationNameMap standardLibrarySimdOpNames_; + + ParseNode* moduleFunctionNode_; + PropertyName* moduleFunctionName_; + + UniqueChars errorString_; + uint32_t errorOffset_; + bool errorOverRecursed_; + + bool canValidateChangeHeap_; + bool hasChangeHeap_; + bool supportsSimd_; + bool atomicsPresent_; public: ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser) : cx_(cx), parser_(parser), mg_(cx), validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE), functions_(cx), funcPtrTables_(cx), globals_(cx), arrayViews_(cx), - exits_(cx), + imports_(cx), standardLibraryMathNames_(cx), standardLibraryAtomicsNames_(cx), standardLibrarySimdOpNames_(cx), moduleFunctionNode_(parser.pc->maybeFunction), moduleFunctionName_(nullptr), errorString_(nullptr), errorOffset_(UINT32_MAX), errorOverRecursed_(false), @@ -1298,18 +2001,18 @@ class MOZ_STACK_CLASS ModuleValidator JSAtom* atom = Atomize(cx_, name, strlen(name)); if (!atom) return false; return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op); } public: - bool init() { - if (!globals_.init() || !exits_.init()) + bool init(HandleAsmJSModule moduleObj) { + if (!globals_.init() || !imports_.init()) return false; if (!standardLibraryMathNames_.init() || !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) || !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) || !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) || !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) || !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) || @@ -1366,183 +2069,212 @@ class MOZ_STACK_CLASS ModuleValidator uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin; uint32_t srcBodyStart = tokenStream().currentToken().pos.end; // "use strict" should be added to the source if we are in an implicit // strict context, see also comment above addUseStrict in // js::FunctionToString. bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict(); - return mg_.init(parser_.ss, srcStart, srcBodyStart, strict); - } - - bool finish(ScopedJSDeletePtr<AsmJSModule>* module, SlowFunctionVector* slowFuncs) { - return mg_.finish(parser_.tokenStream, module, slowFuncs); + module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict); + if (!module_) + return false; + + moduleObj->setModule(module_); + + return mg_.init(); + } + + bool finish(SlowFunctionVector* slowFuncs) { + uint32_t endBeforeCurly = tokenStream().currentToken().pos.end; + TokenPos pos; + JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand)); + uint32_t endAfterCurly = pos.end; + + auto usesHeap = Module::HeapBool(module_->hasArrayView()); + auto sharedHeap = Module::SharedBool(module_->isSharedView()); + auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors()); + + CacheableChars filename = make_string_copy(parser_.ss->filename()); + if (!filename) + return false; + + CacheableTwoByteChars displayURL; + if (parser_.ss->hasDisplayURL()) { + uint32_t length = js_strlen(parser_.ss->displayURL()); + displayURL.reset(js_pod_calloc<char16_t>(length + 1)); + if (!displayURL) + return false; + PodCopy(displayURL.get(), parser_.ss->displayURL(), length); + } + + UniqueStaticLinkData linkData; + Module* wasm = mg_.finish(usesHeap, sharedHeap, mutedErrors, + Move(filename), Move(displayURL), + &linkData, slowFuncs); + if (!wasm) + return false; + + module_->finish(wasm, Move(linkData), endBeforeCurly, endAfterCurly); + return true; } // Mutable interface. void initModuleFunctionName(PropertyName* name) { moduleFunctionName_ = name; } void initGlobalArgumentName(PropertyName* n) { module().initGlobalArgumentName(n); } void initImportArgumentName(PropertyName* n) { module().initImportArgumentName(n); } void initBufferArgumentName(PropertyName* n) { module().initBufferArgumentName(n); } - bool addGlobalVarInit(PropertyName* varName, const NumLit& lit, bool isConst) { - // The type of a const is the exact type of the literal (since its value - // cannot change) which is more precise than the corresponding vartype. - Type type = isConst ? Type::lit(lit) : Type::var(lit.type()); + bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) { uint32_t globalDataOffset; - if (!module().addGlobalVarInit(lit.value(), &globalDataOffset)) + if (!mg_.allocateGlobalVar(lit.type(), &globalDataOffset)) return false; Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable; Global* global = validationLifo_.new_<Global>(which); if (!global) return false; global->u.varOrConst.globalDataOffset_ = globalDataOffset; - global->u.varOrConst.type_ = type.which(); + global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : Type::var(lit.type())).which(); if (isConst) global->u.varOrConst.literalValue_ = lit; - return globals_.putNew(varName, global); - } - bool addGlobalVarImport(PropertyName* varName, PropertyName* fieldName, ValType importType, - bool isConst) - { + return globals_.putNew(var, global) && + module().addGlobalVarInit(lit.value(), globalDataOffset); + } + bool addGlobalVarImport(PropertyName* var, PropertyName* field, ValType type, bool isConst) { uint32_t globalDataOffset; - if (!module().addGlobalVarImport(fieldName, importType, &globalDataOffset)) + if (!mg_.allocateGlobalVar(type, &globalDataOffset)) return false; Global::Which which = isConst ? Global::ConstantImport : Global::Variable; Global* global = validationLifo_.new_<Global>(which); if (!global) return false; global->u.varOrConst.globalDataOffset_ = globalDataOffset; - global->u.varOrConst.type_ = Type::var(importType).which(); - return globals_.putNew(varName, global); - } - bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField) - { - if (!arrayViews_.append(ArrayView(varName, vt))) + global->u.varOrConst.type_ = Type::var(type).which(); + return globals_.putNew(var, global) && + module().addGlobalVarImport(field, type, globalDataOffset); + } + bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) { + if (!arrayViews_.append(ArrayView(var, vt))) return false; Global* global = validationLifo_.new_<Global>(Global::ArrayView); if (!global) return false; - if (!module().addArrayView(vt, maybeField)) - return false; global->u.viewInfo.viewType_ = vt; - return globals_.putNew(varName, global); - } - bool addMathBuiltinFunction(PropertyName* varName, AsmJSMathBuiltinFunction func, - PropertyName* fieldName) + return globals_.putNew(var, global) && + module().addArrayView(vt, maybeField); + } + bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func, + PropertyName* field) { - if (!module().addMathBuiltinFunction(func, fieldName)) - return false; Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction); if (!global) return false; global->u.mathBuiltinFunc_ = func; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addMathBuiltinFunction(func, field); } private: - bool addGlobalDoubleConstant(PropertyName* varName, double constant) { + bool addGlobalDoubleConstant(PropertyName* var, double constant) { Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral); if (!global) return false; global->u.varOrConst.type_ = Type::Double; global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant)); - return globals_.putNew(varName, global); + return globals_.putNew(var, global); } public: - bool addMathBuiltinConstant(PropertyName* varName, double constant, PropertyName* fieldName) { - if (!module().addMathBuiltinConstant(constant, fieldName)) - return false; - return addGlobalDoubleConstant(varName, constant); - } - bool addGlobalConstant(PropertyName* varName, double constant, PropertyName* fieldName) { - if (!module().addGlobalConstant(constant, fieldName)) - return false; - return addGlobalDoubleConstant(varName, constant); - } - bool addAtomicsBuiltinFunction(PropertyName* varName, AsmJSAtomicsBuiltinFunction func, - PropertyName* fieldName) + bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) { + return addGlobalDoubleConstant(var, constant) && + module().addMathBuiltinConstant(constant, field); + } + bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) { + return addGlobalDoubleConstant(var, constant) && + module().addGlobalConstant(constant, field); + } + bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func, + PropertyName* field) { - if (!module().addAtomicsBuiltinFunction(func, fieldName)) - return false; Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction); if (!global) return false; atomicsPresent_ = true; global->u.atomicsBuiltinFunc_ = func; - return globals_.putNew(varName, global); - } - bool addSimdCtor(PropertyName* varName, AsmJSSimdType type, PropertyName* fieldName) { - if (!module().addSimdCtor(type, fieldName)) - return false; + return globals_.putNew(var, global) && + module().addAtomicsBuiltinFunction(func, field); + } + bool addSimdCtor(PropertyName* var, AsmJSSimdType type, PropertyName* field) { Global* global = validationLifo_.new_<Global>(Global::SimdCtor); if (!global) return false; global->u.simdCtorType_ = type; - return globals_.putNew(varName, global); - } - bool addSimdOperation(PropertyName* varName, AsmJSSimdType type, AsmJSSimdOperation op, - PropertyName* typeVarName, PropertyName* opName) + return globals_.putNew(var, global) && + module().addSimdCtor(type, field); + } + bool addSimdOperation(PropertyName* var, AsmJSSimdType type, AsmJSSimdOperation op, + PropertyName* opName) { - if (!module().addSimdOperation(type, op, opName)) - return false; Global* global = validationLifo_.new_<Global>(Global::SimdOperation); if (!global) return false; global->u.simdOp.type_ = type; global->u.simdOp.which_ = op; - return globals_.putNew(varName, global); + return globals_.putNew(var, global) && + module().addSimdOperation(type, op, opName); } bool addByteLength(PropertyName* name) { canValidateChangeHeap_ = true; - if (!module().addByteLength()) - return false; Global* global = validationLifo_.new_<Global>(Global::ByteLength); - return global && globals_.putNew(name, global); + return global && + globals_.putNew(name, global) && + module().addByteLength(); } bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) { hasChangeHeap_ = true; - module().addChangeHeap(mask, min, max); Global* global = validationLifo_.new_<Global>(Global::ChangeHeap); if (!global) return false; global->u.changeHeap.srcBegin_ = fn->pn_pos.begin; global->u.changeHeap.srcEnd_ = fn->pn_pos.end; - return globals_.putNew(name, global); - } - bool addArrayViewCtor(PropertyName* varName, Scalar::Type vt, PropertyName* fieldName) { + return globals_.putNew(name, global) && + module().addChangeHeap(mask, min, max); + } + bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) { Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor); if (!global) return false; - if (!module().addArrayViewCtor(vt, fieldName)) - return false; global->u.viewInfo.viewType_ = vt; - return globals_.putNew(varName, global); - } - bool addFFI(PropertyName* varName, PropertyName* field) { + return globals_.putNew(var, global) && + module().addArrayViewCtor(vt, field); + } + bool addFFI(PropertyName* var, PropertyName* field) { Global* global = validationLifo_.new_<Global>(Global::FFI); if (!global) return false; uint32_t index; if (!module().addFFI(field, &index)) return false; global->u.ffiIndex_ = index; - return globals_.putNew(varName, global); - } - bool addExportedFunction(const Func& func, PropertyName* maybeFieldName) { + return globals_.putNew(var, global); + } + bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) { MallocSig::ArgVector args; if (!args.appendAll(func.sig().args())) return false; MallocSig sig(Move(args), func.sig().ret()); - return module().addExportedFunction(func.name(), func.index(), func.srcBegin(), - func.srcEnd(), maybeFieldName, Move(sig)); - } - bool addExportedChangeHeap(PropertyName* name, const Global& g, PropertyName* maybeFieldName) { - return module().addExportedChangeHeap(name, g.changeHeapSrcBegin(), g.changeHeapSrcEnd(), - maybeFieldName); + uint32_t wasmIndex; + if (!mg_.declareExport(Move(sig), func.index(), &wasmIndex)) + return false; + if (wasmIndex == AsmJSModule::Export::ChangeHeap) + return fail(pn, "too many exports"); + return module().addExport(func.name(), maybeFieldName, wasmIndex, + func.srcBegin(), func.srcEnd()); + } + bool addChangeHeapExport(PropertyName* name, const Global& g, PropertyName* maybeFieldName) { + return module().addExport(name, maybeFieldName, AsmJSModule::Export::ChangeHeap, + g.changeHeapSrcBegin(), g.changeHeapSrcEnd()); } private: const LifoSig* getLifoSig(const LifoSig& sig) { return &sig; } const LifoSig* getLifoSig(const MallocSig& sig) { return mg_.newLifoSig(sig); } @@ -1554,19 +2286,17 @@ class MOZ_STACK_CLASS ModuleValidator return false; global->u.funcIndex_ = funcIndex; if (!globals_.putNew(name, global)) return false; const LifoSig* lifoSig = getLifoSig(sig); if (!lifoSig) return false; *func = validationLifo_.new_<Func>(name, firstUse, *lifoSig, funcIndex); - if (!*func) - return false; - return functions_.append(*func); + return *func && functions_.append(*func); } template <class SigT> bool declareFuncPtrTable(PropertyName* name, uint32_t firstUse, SigT& sig, uint32_t mask, uint32_t* index) { if (!mg_.declareFuncPtrTable(/* numElems = */ mask + 1, index)) return false; MOZ_ASSERT(*index == numFuncPtrTables()); @@ -1577,39 +2307,40 @@ class MOZ_STACK_CLASS ModuleValidator if (!globals_.putNew(name, global)) return false; const LifoSig* lifoSig = getLifoSig(sig); if (!lifoSig) return false; FuncPtrTable* t = validationLifo_.new_<FuncPtrTable>(cx_, name, firstUse, *lifoSig, mask); return t && funcPtrTables_.append(t); } - bool defineFuncPtrTable(uint32_t funcPtrTableIndex, ModuleGenerator::FuncIndexVector&& elems) { + bool defineFuncPtrTable(uint32_t funcPtrTableIndex, const Vector<uint32_t>& elems) { FuncPtrTable& table = *funcPtrTables_[funcPtrTableIndex]; if (table.defined()) return false; table.define(); - return mg_.defineFuncPtrTable(funcPtrTableIndex, Move(elems)); - } - bool addExit(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* exitIndex, + mg_.defineFuncPtrTable(funcPtrTableIndex, elems); + return true; + } + bool addImport(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* importIndex, const LifoSig** lifoSig) { - ExitDescriptor::Lookup lookup(name, sig); - ExitMap::AddPtr p = exits_.lookupForAdd(lookup); + ImportDescriptor::Lookup lookup(name, sig); + ImportMap::AddPtr p = imports_.lookupForAdd(lookup); if (p) { *lifoSig = &p->key().sig(); - *exitIndex = p->value(); + *importIndex = p->value(); return true; } *lifoSig = getLifoSig(sig); if (!*lifoSig) return false; - if (!module().addExit(Move(sig), ffiIndex, exitIndex)) - return false; - return exits_.add(p, ExitDescriptor(name, **lifoSig), *exitIndex); + return mg_.declareImport(Move(sig), importIndex) && + imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) && + module().addImport(ffiIndex, *importIndex); } bool tryOnceToValidateChangeHeap() { bool ret = canValidateChangeHeap_; canValidateChangeHeap_ = false; return ret; } bool hasChangeHeap() const { @@ -1631,17 +2362,17 @@ class MOZ_STACK_CLASS ModuleValidator return !!errorString_; } bool failOffset(uint32_t offset, const char* str) { MOZ_ASSERT(!hasAlreadyFailed()); MOZ_ASSERT(errorOffset_ == UINT32_MAX); MOZ_ASSERT(str); errorOffset_ = offset; - errorString_ = DuplicateString(cx_, str); + errorString_ = make_string_copy(str); return false; } bool fail(ParseNode* pn, const char* str) { return failOffset(pn->pn_pos.begin, str); } bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) { @@ -1687,17 +2418,17 @@ class MOZ_STACK_CLASS ModuleValidator return false; } // Read-only interface ExclusiveContext* cx() const { return cx_; } ParseNode* moduleFunctionNode() const { return moduleFunctionNode_; } PropertyName* moduleFunctionName() const { return moduleFunctionName_; } ModuleGenerator& mg() { return mg_; } - AsmJSModule& module() const { return mg_.module(); } + AsmJSModule& module() const { return *module_; } AsmJSParser& parser() const { return parser_; } TokenStream& tokenStream() const { return parser_.tokenStream; } bool supportsSimd() const { return supportsSimd_; } unsigned numArrayViews() const { return arrayViews_.length(); } const ArrayView& arrayView(unsigned i) const { @@ -1748,19 +2479,29 @@ class MOZ_STACK_CLASS ModuleValidator bool lookupStandardSimdOpName(PropertyName* name, AsmJSSimdOperation* op) const { if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) { *op = p->value(); return true; } return false; } - void startFunctionBodies() { - if (atomicsPresent_) + bool startFunctionBodies() { + if (atomicsPresent_) { +#if defined(ENABLE_SHARED_ARRAY_BUFFER) module().setViewsAreShared(); +#else + return failOffset(parser_.tokenStream.currentToken().pos.begin, + "shared memory and atomics not supported by this build"); +#endif + } + return true; + } + bool finishFunctionBodies() { + return mg_.finishFuncs(); } }; } // namespace /*****************************************************************************/ // Numeric literal utilities @@ -2675,26 +3416,25 @@ CheckGlobalSimdImport(ModuleValidator& m AsmJSSimdType simdType; if (!IsSimdTypeName(m, field, &simdType)) return m.failName(initNode, "'%s' is not a standard SIMD type", field); return m.addSimdCtor(varName, simdType, field); } static bool CheckGlobalSimdOperationImport(ModuleValidator& m, const ModuleValidator::Global* global, - ParseNode* initNode, PropertyName* varName, PropertyName* ctorVarName, - PropertyName* opName) + ParseNode* initNode, PropertyName* varName, PropertyName* opName) { AsmJSSimdType simdType = global->simdCtorType(); AsmJSSimdOperation simdOp; if (!m.lookupStandardSimdOpName(opName, &simdOp)) return m.failName(initNode, "'%s' is not a standard SIMD operation", opName); if (!IsSimdValidOperationType(simdType, simdOp)) return m.failName(initNode, "'%s' is not an operation supported by the SIMD type", opName); - return m.addSimdOperation(varName, simdType, simdOp, ctorVarName, opName); + return m.addSimdOperation(varName, simdType, simdOp, opName); } static bool CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode) { ParseNode* base = DotBase(initNode); PropertyName* field = DotMember(initNode); @@ -2746,17 +3486,17 @@ CheckGlobalDotImport(ModuleValidator& m, const ModuleValidator::Global* global = m.lookupGlobal(base->name()); if (!global) return m.failName(initNode, "%s not found in module global scope", base->name()); if (!global->isSimdCtor()) return m.failName(base, "expecting SIMD constructor name, got %s", field); - return CheckGlobalSimdOperationImport(m, global, initNode, varName, base->name(), field); + return CheckGlobalSimdOperationImport(m, global, initNode, varName, field); } static bool CheckModuleGlobal(ModuleValidator& m, ParseNode* var, bool isConst) { if (!IsDefinition(var)) return m.fail(var, "import variable names must be unique"); @@ -3667,17 +4407,17 @@ CheckAtomicsStore(FunctionValidator& f, f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); f.patchU8(viewTypeAt, uint8_t(viewType)); *type = rhsType; return true; } static bool -CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, js::jit::AtomicOp op) +CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op) { if (CallArgListLength(call) != 3) return f.fail(call, "Atomics binary operator must be passed 3 arguments"); ParseNode* arrayArg = CallArgList(call); ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); @@ -4049,18 +4789,17 @@ CheckFuncPtrCall(FunctionValidator& f, P return false; MallocSig sig(Move(args), ret); uint32_t funcPtrTableIndex; if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, sig, mask, &funcPtrTableIndex)) return false; - uint32_t globalDataOffset = f.m().module().funcPtrTable(funcPtrTableIndex).globalDataOffset(); - f.patch32(globalDataOffsetAt, globalDataOffset); + f.patch32(globalDataOffsetAt, f.m().mg().funcPtrTableGlobalDataOffset(funcPtrTableIndex)); f.patchSig(sigAt, &f.m().funcPtrTable(funcPtrTableIndex).sig()); *type = Type::ret(ret); return true; } static bool CheckIsExternType(FunctionValidator& f, ParseNode* argNode, Type type) @@ -4094,34 +4833,33 @@ CheckFFICall(FunctionValidator& f, Parse case ExprType::F64: f.writeOp(F64::CallImport); break; case ExprType::I32x4: f.writeOp(I32X4::CallImport); break; case ExprType::F32x4: f.writeOp(F32X4::CallImport); break; case ExprType::B32x4: f.writeOp(B32X4::CallImport); break; } // Global data offset size_t offsetAt = f.temp32(); - // Pointer to the exit's signature in the module's lifo + // Pointer to the import's signature in the module's lifo size_t sigAt = f.tempPtr(); // Call node position (asm.js specific) WriteCallLineCol(f, callNode); MallocSig::ArgVector args; if (!CheckCallArgs<CheckIsExternType>(f, callNode, &args)) return false; MallocSig sig(Move(args), ret); - unsigned exitIndex = 0; + unsigned importIndex = 0; const LifoSig* lifoSig = nullptr; - if (!f.m().addExit(calleeName, Move(sig), ffiIndex, &exitIndex, &lifoSig)) - return false; - - JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0); - f.patch32(offsetAt, f.module().exit(exitIndex).globalDataOffset()); + if (!f.m().addImport(calleeName, Move(sig), ffiIndex, &importIndex, &lifoSig)) + return false; + + f.patch32(offsetAt, f.m().mg().importExitGlobalDataOffset(importIndex)); f.patchSig(sigAt, lifoSig); *type = Type::ret(ret); return true; } static bool CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType, size_t opcodeAt) @@ -5851,17 +6589,17 @@ CheckExprStatement(FunctionValidator& f, enum class InterruptCheckPosition { Head, Loop }; static void MaybeAddInterruptCheck(FunctionValidator& f, InterruptCheckPosition pos, ParseNode* pn) { - if (f.m().module().usesSignalHandlersForInterrupt()) + if (f.m().mg().args().useSignalHandlersForInterrupt) return; switch (pos) { case InterruptCheckPosition::Head: f.writeOp(Stmt::InterruptCheckHead); break; case InterruptCheckPosition::Loop: f.writeOp(Stmt::InterruptCheckLoop); break; } unsigned lineno = 0, column = 0; @@ -6680,17 +7418,17 @@ CheckFuncPtrTable(ModuleValidator& m, Pa unsigned length = ListLength(arrayLiteral); if (!IsPowerOfTwo(length)) return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length); unsigned mask = length - 1; - ModuleGenerator::FuncIndexVector elems; + Vector<uint32_t> elemFuncIndices(m.cx()); const LifoSig* sig = nullptr; for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) { if (!elem->isKind(PNK_NAME)) return m.fail(elem, "function-pointer table's elements must be names of functions"); PropertyName* funcName = elem->name(); const ModuleValidator::Func* func = m.lookupFunction(funcName); if (!func) @@ -6698,25 +7436,25 @@ CheckFuncPtrTable(ModuleValidator& m, Pa if (sig) { if (*sig != func->sig()) return m.fail(elem, "all functions in table must have same signature"); } else { sig = &func->sig(); } - if (!elems.append(func->index())) + if (!elemFuncIndices.append(func->index())) return false; } uint32_t funcPtrTableIndex; if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), *sig, mask, &funcPtrTableIndex)) return false; - if (!m.defineFuncPtrTable(funcPtrTableIndex, Move(elems))) + if (!m.defineFuncPtrTable(funcPtrTableIndex, elemFuncIndices)) return m.fail(var, "duplicate function-pointer definition"); return true; } static bool CheckFuncPtrTables(ModuleValidator& m) { @@ -6751,20 +7489,20 @@ CheckModuleExportFunction(ModuleValidato return m.fail(pn, "expected name of exported function"); PropertyName* funcName = pn->name(); const ModuleValidator::Global* global = m.lookupGlobal(funcName); if (!global) return m.failName(pn, "exported function name '%s' not found", funcName); if (global->which() == ModuleValidator::Global::Function) - return m.addExportedFunction(m.function(global->funcIndex()), maybeFieldName); + return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName); if (global->which() == ModuleValidator::Global::ChangeHeap) - return m.addExportedChangeHeap(funcName, *global, maybeFieldName); + return m.addChangeHeapExport(funcName, *global, maybeFieldName); return m.failName(pn, "'%s' is not a function", funcName); } static bool CheckModuleExportObject(ModuleValidator& m, ParseNode* object) { MOZ_ASSERT(object->isKind(PNK_OBJECT)); @@ -6837,24 +7575,23 @@ CheckModuleEnd(ModuleValidator &m) "top-level export (return) must be the last statement"); } m.parser().tokenStream.ungetToken(); return true; } static bool -CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, - ScopedJSDeletePtr<AsmJSModule>* module, unsigned* time, - SlowFunctionVector* slowFuncs) +CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, HandleAsmJSModule obj, + unsigned* time, SlowFunctionVector* slowFuncs) { int64_t before = PRMJ_Now(); ModuleValidator m(cx, parser); - if (!m.init()) + if (!m.init(obj)) return false; if (PropertyName* moduleFunctionName = FunctionName(m.moduleFunctionNode())) { if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName)) return false; m.initModuleFunctionName(moduleFunctionName); } @@ -6868,109 +7605,1306 @@ CheckModule(ExclusiveContext* cx, AsmJSP return false; if (!CheckModuleProcessingDirectives(m)) return false; if (!CheckModuleGlobals(m)) return false; - m.startFunctionBodies(); - -#if !defined(ENABLE_SHARED_ARRAY_BUFFER) - if (m.usesSharedMemory()) - return m.failOffset(m.parser().tokenStream.currentToken().pos.begin, - "shared memory and atomics not supported by this build"); -#endif + if (!m.startFunctionBodies()) + return false; if (!CheckFunctions(m)) return false; + if (!m.finishFunctionBodies()) + return false; + if (!CheckFuncPtrTables(m)) return false; if (!CheckModuleReturn(m)) return false; if (!CheckModuleEnd(m)) return false; - if (!m.finish(module, slowFuncs)) + if (!m.finish(slowFuncs)) return false; *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC; return true; } -static bool -BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module, - unsigned time, const SlowFunctionVector& slowFuncs, - JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) -{ -#ifndef JS_MORE_DETERMINISTIC - ScopedJSFreePtr<char> slowText; - if (!slowFuncs.empty()) { - slowText.reset(JS_smprintf("; %d functions compiled slowly: ", slowFuncs.length())); - if (!slowText) - return true; - - for (unsigned i = 0; i < slowFuncs.length(); i++) { - const SlowFunction& func = slowFuncs[i]; - JSAutoByteString name; - if (!AtomToPrintableString(cx, func.name, &name)) +/*****************************************************************************/ +// Runtime calls to asm.js module exports + +static AsmJSModuleObject& +FunctionToModuleObject(JSFunction* fun) +{ + MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT); + return v.toObject().as<AsmJSModuleObject>(); +} + +static unsigned +FunctionToExportIndex(JSFunction* fun) +{ + MOZ_ASSERT(IsAsmJSFunction(fun)); + const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT); + return v.toInt32(); +} + +static bool +ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args) +{ + HandleValue bufferArg = args.get(0); + if (!IsArrayBuffer(bufferArg)) { + ReportIncompatible(cx, args); + return false; + } + + Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>()); + uint32_t heapLength = newBuffer->byteLength(); + if (heapLength & module.heapLengthMask() || + heapLength < module.minHeapLength() || + heapLength > module.maxHeapLength()) + { + args.rval().set(BooleanValue(false)); + return true; + } + + if (!module.hasArrayView()) { + args.rval().set(BooleanValue(true)); + return true; + } + + MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength)); + + bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB; + if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers)) + return false; + + args.rval().set(BooleanValue(module.wasmModule().changeHeap(newBuffer, cx))); + return true; +} + +static bool +CallAsmJS(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedFunction callee(cx, &args.callee().as<JSFunction>()); + + AsmJSModule& module = FunctionToModuleObject(callee).module(); + const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)]; + + // The heap-changing function is a special-case and is implemented by C++. + if (exp.isChangeHeap()) + return ChangeHeap(cx, module, args); + + return module.wasmModule().callExport(cx, exp.wasmIndex(), args); +} + +/*****************************************************************************/ +// Link-time validation + +static bool +LinkFail(JSContext* cx, const char* str) +{ + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, + nullptr, JSMSG_USE_ASM_LINK_FAIL, str); + return false; +} + +static bool +GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v) +{ + if (!objVal.isObject()) + return LinkFail(cx, "accessing property of non-object"); + + RootedObject obj(cx, &objVal.toObject()); + if (IsScriptedProxy(obj)) + return LinkFail(cx, "accessing property of a Proxy"); + + Rooted<PropertyDescriptor> desc(cx); + RootedId id(cx, NameToId(field)); + if (!GetPropertyDescriptor(cx, obj, id, &desc)) + return false; + + if (!desc.object()) + return LinkFail(cx, "property not present on object"); + + if (!desc.isDataDescriptor()) + return LinkFail(cx, "property is not a data property"); + + v.set(desc.value()); + return true; +} + +static bool +HasPureCoercion(JSContext* cx, HandleValue v) +{ + if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v) || IsVectorObject<Bool32x4>(v)) + return true; + + // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a + // bug that generates code that passes functions for some imports. To avoid + // breaking all the code that contains this bug, we make an exception for + // functions that don't have user-defined valueOf or toString, for their + // coercions are not observable and coercion via ToNumber/ToInt32 + // definitely produces NaN/0. We should remove this special case later once + // most apps have been built with newer Emscripten. + jsid toString = NameToId(cx->names().toString); + if (v.toObject().is<JSFunction>() && + HasObjectValueOf(&v.toObject(), cx) && + ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString)) + { + return true; + } + + return false; +} + +static bool +ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData, + HandleValue importVal) +{ + void* datum = globalData + global.varGlobalDataOffset(); + + switch (global.varInitKind()) { + case AsmJSModule::Global::InitConstant: { + Val v = global.varInitVal(); + switch (v.type()) { + case ValType::I32: + *(int32_t*)datum = v.i32(); + break; + case ValType::I64: + MOZ_CRASH("int64"); + case ValType::F32: + *(float*)datum = v.f32(); + break; + case ValType::F64: + *(double*)datum = v.f64(); + break; + case ValType::I32x4: + case ValType::B32x4: + // Bool32x4 uses the same data layout as Int32x4. + memcpy(datum, v.i32x4(), Simd128DataSize); + break; + case ValType::F32x4: + memcpy(datum, v.f32x4(), Simd128DataSize); + break; + } + break; + } + + case AsmJSModule::Global::InitImport: { + RootedPropertyName field(cx, global.varImportField()); + RootedValue v(cx); + if (!GetDataProperty(cx, importVal, field, &v)) + return false; + + if (!v.isPrimitive() && !HasPureCoercion(cx, v)) + return LinkFail(cx, "Imported values must be primitives"); + + switch (global.varInitImportType()) { + case ValType::I32: + if (!ToInt32(cx, v, (int32_t*)datum)) + return false; + break; + case ValType::I64: + MOZ_CRASH("int64"); + case ValType::F32: + if (!RoundFloat32(cx, v, (float*)datum)) + return false; + break; + case ValType::F64: + if (!ToNumber(cx, v, (double*)datum)) + return false; + break; + case ValType::I32x4: { + SimdConstant simdConstant; + if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant)) + return false; + memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize); + break; + } + case ValType::F32x4: { + SimdConstant simdConstant; + if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant)) + return false; + memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize); + break; + } + case ValType::B32x4: { + SimdConstant simdConstant; + if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant)) return false; - - slowText.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowText.get(), - name.ptr(), func.line, func.column, func.ms, - i+1 < slowFuncs.length() ? ", " : "")); - if (!slowText) - return true; - } - } - - const char* cacheString = ""; - switch (cacheResult) { - case JS::AsmJSCache_Success: - cacheString = "stored in cache"; + // Bool32x4 uses the same data layout as Int32x4. + memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize); + break; + } + } + break; + } + } + + return true; +} + +static bool +ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal, + AutoVectorRooter<JSFunction*>* ffis) +{ + RootedPropertyName field(cx, global.ffiField()); + RootedValue v(cx); + if (!GetDataProperty(cx, importVal, field, &v)) + return false; + + if (!v.isObject() || !v.toObject().is<JSFunction>()) + return LinkFail(cx, "FFI imports must be functions"); + + (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>()); + return true; +} + +static bool +ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedPropertyName field(cx, global.maybeViewName()); + if (!field) + return true; + + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, field, &v)) + return false; + + bool tac = IsTypedArrayConstructor(v, global.viewType()); + if (!tac) + return LinkFail(cx, "bad typed array constructor"); + + return true; +} + +static bool +ValidateByteLength(JSContext* cx, HandleValue globalVal) +{ + RootedPropertyName field(cx, cx->names().byteLength); + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, field, &v)) + return false; + + if (!v.isObject() || !v.toObject().isBoundFunction()) + return LinkFail(cx, "byteLength must be a bound function object"); + + RootedFunction fun(cx, &v.toObject().as<JSFunction>()); + + RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget())); + if (!IsNativeFunction(boundTarget, fun_call)) + return LinkFail(cx, "bound target of byteLength must be Function.prototype.call"); + + RootedValue boundThis(cx, fun->getBoundFunctionThis()); + if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter)) + return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor"); + + return true; +} + +static bool +ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) + return false; + + RootedPropertyName field(cx, global.mathName()); + if (!GetDataProperty(cx, v, field, &v)) + return false; + + Native native = nullptr; + switch (global.mathBuiltinFunction()) { + case AsmJSMathBuiltin_sin: native = math_sin; break; + case AsmJSMathBuiltin_cos: native = math_cos; break; + case AsmJSMathBuiltin_tan: native = math_tan; break; + case AsmJSMathBuiltin_asin: native = math_asin; break; + case AsmJSMathBuiltin_acos: native = math_acos; break; + case AsmJSMathBuiltin_atan: native = math_atan; break; + case AsmJSMathBuiltin_ceil: native = math_ceil; break; + case AsmJSMathBuiltin_floor: native = math_floor; break; + case AsmJSMathBuiltin_exp: native = math_exp; break; + case AsmJSMathBuiltin_log: native = math_log; break; + case AsmJSMathBuiltin_pow: native = math_pow; break; + case AsmJSMathBuiltin_sqrt: native = math_sqrt; break; + case AsmJSMathBuiltin_min: native = math_min; break; + case AsmJSMathBuiltin_max: native = math_max; break; + case AsmJSMathBuiltin_abs: native = math_abs; break; + case AsmJSMathBuiltin_atan2: native = math_atan2; break; + case AsmJSMathBuiltin_imul: native = math_imul; break; + case AsmJSMathBuiltin_clz32: native = math_clz32; break; + case AsmJSMathBuiltin_fround: native = math_fround; break; + } + + if (!IsNativeFunction(v, native)) + return LinkFail(cx, "bad Math.* builtin function"); + + return true; +} + +static PropertyName* +SimdTypeToName(JSContext* cx, AsmJSSimdType type) +{ + switch (type) { + case AsmJSSimdType_int32x4: return cx->names().int32x4; + case AsmJSSimdType_float32x4: return cx->names().float32x4; + case AsmJSSimdType_bool32x4: return cx->names().bool32x4; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type"); +} + +static SimdTypeDescr::Type +AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type) +{ + switch (type) { + case AsmJSSimdType_int32x4: return Int32x4::type; + case AsmJSSimdType_float32x4: return Float32x4::type; + case AsmJSSimdType_bool32x4: return Bool32x4::type; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType"); +} + +static bool +ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal, + MutableHandleValue out) +{ + RootedValue v(cx); + if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v)) + return false; + + AsmJSSimdType type; + if (global.which() == AsmJSModule::Global::SimdCtor) + type = global.simdCtorType(); + else + type = global.simdOperationType(); + + RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type)); + if (!GetDataProperty(cx, v, simdTypeName, &v)) + return false; + + if (!v.isObject()) + return LinkFail(cx, "bad SIMD type"); + + RootedObject simdDesc(cx, &v.toObject()); + if (!simdDesc->is<SimdTypeDescr>()) + return LinkFail(cx, "bad SIMD type"); + + if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type()) + return LinkFail(cx, "bad SIMD type"); + + out.set(v); + return true; +} + +static bool +ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + RootedValue _(cx); + return ValidateSimdType(cx, global, globalVal, &_); +} + +static bool +ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal) +{ + // SIMD operations are loaded from the SIMD type, so the type must have been + // validated before the operation. + RootedValue v(cx); + JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v)); + + RootedPropertyName opName(cx, global.simdOperationName()); + if (!GetDataProperty(cx, v, opName, &v)) + return false; + + Native native = nullptr; + switch (global.simdOperationType()) { +#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break; +#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break; +#define SET_NATIVE_BOOL32X4(op) case AsmJSSimdOperation_##op: native = simd_bool32x4_##op; break; +#define FALLTHROUGH(op) case AsmJSSimdOperation_##op: + case AsmJSSimdType_int32x4: + switch (global.simdOperation()) { + FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4) + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first " + "place"); + } + break; + case AsmJSSimdType_float32x4: + switch (global.simdOperation()) { + FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4)