Merge inbound to m-c a=merge

This commit is contained in:
Wes Kocher 2015-04-01 17:54:45 -07:00
commit 5d721ab157
175 changed files with 6274 additions and 2312 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ TAGS
tags
ID
.DS_Store*
*.pdb
# Vim swap files.
.*.sw[a-z]

View File

@ -145,34 +145,33 @@
#endif
@RESPATH@/components/accessibility.xpt
#endif
@RESPATH@/components/appshell.xpt
@RESPATH@/components/appstartup.xpt
@RESPATH@/components/autocomplete.xpt
@RESPATH@/components/autoconfig.xpt
@RESPATH@/components/browsercompsbase.xpt
@RESPATH@/components/browser-element.xpt
@RESPATH@/components/browser-feeds.xpt
@RESPATH@/components/caps.xpt
@RESPATH@/components/chardet.xpt
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt
@RESPATH@/components/commandlines.xpt
@RESPATH@/components/compartments.xpt
@RESPATH@/components/composer.xpt
@RESPATH@/components/content_events.xpt
@RESPATH@/components/content_html.xpt
@RESPATH@/components/content_xslt.xpt
@RESPATH@/components/cookie.xpt
@RESPATH@/components/devtools_security.xpt
@RESPATH@/components/directory.xpt
@RESPATH@/components/diskspacewatcher.xpt
@RESPATH@/components/docshell.xpt
@RESPATH@/components/dom.xpt
@RESPATH@/components/dom_activities.xpt
@RESPATH@/components/dom_apps.xpt
@RESPATH@/components/dom_audiochannel.xpt
@RESPATH@/components/dom_base.xpt
@RESPATH@/components/dom_system.xpt
@BINPATH@/components/appshell.xpt
@BINPATH@/components/appstartup.xpt
@BINPATH@/components/autocomplete.xpt
@BINPATH@/components/autoconfig.xpt
@BINPATH@/components/browsercompsbase.xpt
@BINPATH@/components/browser-element.xpt
@BINPATH@/components/browser-feeds.xpt
@BINPATH@/components/caps.xpt
@BINPATH@/components/chardet.xpt
@BINPATH@/components/chrome.xpt
@BINPATH@/components/commandhandler.xpt
@BINPATH@/components/commandlines.xpt
@BINPATH@/components/composer.xpt
@BINPATH@/components/content_events.xpt
@BINPATH@/components/content_html.xpt
@BINPATH@/components/content_xslt.xpt
@BINPATH@/components/cookie.xpt
@BINPATH@/components/devtools_security.xpt
@BINPATH@/components/directory.xpt
@BINPATH@/components/diskspacewatcher.xpt
@BINPATH@/components/docshell.xpt
@BINPATH@/components/dom.xpt
@BINPATH@/components/dom_activities.xpt
@BINPATH@/components/dom_apps.xpt
@BINPATH@/components/dom_audiochannel.xpt
@BINPATH@/components/dom_base.xpt
@BINPATH@/components/dom_system.xpt
#ifdef MOZ_WIDGET_GONK
@RESPATH@/components/dom_wifi.xpt
@RESPATH@/components/dom_system_gonk.xpt
@ -326,6 +325,7 @@
@RESPATH@/components/toolkit_finalizationwitness.xpt
@RESPATH@/components/toolkit_formautofill.xpt
@RESPATH@/components/toolkit_osfile.xpt
@RESPATH@/components/toolkit_perfmonitoring.xpt
@RESPATH@/components/toolkit_xulstore.xpt
@RESPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE

View File

@ -3779,8 +3779,7 @@
};
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event;
let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);
popup.openPopupAtScreen(pos.x, pos.y, true);
popup.openPopupAtScreen(event.screenX, event.screenY, true);
break;
}
case "DOMWebNotificationClicked": {

View File

@ -327,7 +327,7 @@ function waitForEvents(event)
if (painted && loaded) {
subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
subwindow.onload = null;
startTest();
SimpleTest.waitForFocus(startTest, subwindow);
}
}

View File

@ -2,6 +2,7 @@
let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
let AddonWatcher = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}).AddonWatcher;
const ADDON_TYPE_SERVICE = "service";
const ID_SUFFIX = "@services.mozilla.org";

View File

@ -180,7 +180,6 @@
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt
@RESPATH@/components/commandlines.xpt
@RESPATH@/components/compartments.xpt
@RESPATH@/components/composer.xpt
@RESPATH@/components/content_events.xpt
@RESPATH@/components/content_html.xpt
@ -328,6 +327,7 @@
@RESPATH@/components/toolkit_finalizationwitness.xpt
@RESPATH@/components/toolkit_formautofill.xpt
@RESPATH@/components/toolkit_osfile.xpt
@RESPATH@/components/toolkit_perfmonitoring.xpt
@RESPATH@/components/toolkit_xulstore.xpt
@RESPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE

View File

@ -75,7 +75,7 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT
},
{
"compartments", "chrome://global/content/aboutCompartments.xhtml",
"performance", "chrome://global/content/aboutPerformance.xhtml",
nsIAboutModule::ALLOW_SCRIPT
},
{

View File

@ -166,9 +166,8 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "buildconfig", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },

View File

@ -6,6 +6,7 @@
/* static functions */
const DEBUG = false;
const REQUEST_CPU_LOCK_TIMEOUT = 10 * 1000; // 10 seconds.
function debug(aStr) {
if (DEBUG)
@ -18,8 +19,15 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
"@mozilla.org/power/powermanagerservice;1",
"nsIPowerManagerService");
function AlarmsManager() {
debug("Constructor");
// A <requestId, {cpuLock, timer}> map.
this._cpuLockDict = new Map();
}
AlarmsManager.prototype = {
@ -71,8 +79,10 @@ AlarmsManager.prototype = {
data = JSON.parse(Cu.evalInSandbox("JSON.stringify(data)", sandbox));
}
let request = this.createRequest();
let requestId = this.getRequestId(request);
this._lockCpuForRequest(requestId);
this._cpmm.sendAsyncMessage("AlarmsManager:Add",
{ requestId: this.getRequestId(request),
{ requestId: requestId,
date: aDate,
ignoreTimezone: isIgnoreTimezone,
data: data,
@ -111,6 +121,7 @@ AlarmsManager.prototype = {
switch (aMessage.name) {
case "AlarmsManager:Add:Return:OK":
this._unlockCpuForRequest(json.requestId);
Services.DOMRequest.fireSuccess(request, json.id);
break;
@ -131,6 +142,7 @@ AlarmsManager.prototype = {
break;
case "AlarmsManager:Add:Return:KO":
this._unlockCpuForRequest(json.requestId);
Services.DOMRequest.fireError(request, json.errorMsg);
break;
@ -172,6 +184,44 @@ AlarmsManager.prototype = {
uninit: function uninit() {
debug("uninit()");
},
_lockCpuForRequest: function (aRequestId) {
if (this._cpuLockDict.has(aRequestId)) {
debug('Cpu wakelock for request ' + aRequestId + ' has been acquired. ' +
'You may call this function repeatedly or requestId is collision.');
return;
}
// Acquire a lock for given request and save for lookup lately.
debug('Acquire cpu lock for request ' + aRequestId);
let cpuLockInfo = {
cpuLock: gPowerManagerService.newWakeLock("cpu"),
timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
};
this._cpuLockDict.set(aRequestId, cpuLockInfo);
// Start a timer to prevent from non-responding request.
cpuLockInfo.timer.initWithCallback(() => {
debug('Request timeout! Release the cpu lock');
this._unlockCpuForRequest(aRequestId);
}, REQUEST_CPU_LOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
},
_unlockCpuForRequest: function(aRequestId) {
let cpuLockInfo = this._cpuLockDict.get(aRequestId);
if (!cpuLockInfo) {
debug('The cpu lock for requestId ' + aRequestId + ' is either invalid ' +
'or has been released.');
return;
}
// Release the cpu lock and cancel the timer.
debug('Release the cpu lock for ' + aRequestId);
cpuLockInfo.cpuLock.unlock();
cpuLockInfo.timer.cancel();
this._cpuLockDict.delete(aRequestId);
},
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlarmsManager])

View File

@ -884,8 +884,7 @@ nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
// Don't show remote iframe if we are waiting for the completion of reflow.
if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
nsIntPoint chromeDisp = aFrame->GetChromeDisplacement();
mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
}
@ -1383,6 +1382,12 @@ nsFrameLoader::StartDestroy()
}
}
// If the TabParent has installed any event listeners on the window, this is
// its last chance to remove them while we're still in the document.
if (mRemoteBrowser) {
mRemoteBrowser->RemoveWindowListeners();
}
nsCOMPtr<nsIDocument> doc;
bool dynamicSubframeRemoval = false;
if (mOwnerContent) {
@ -2058,8 +2063,7 @@ nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
ScreenIntSize size = aIFrame->GetSubdocumentSize();
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
nsIntPoint chromeDisp = aIFrame->GetChromeDisplacement();
mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
return NS_OK;
}

View File

@ -229,6 +229,9 @@ public:
void ActivateUpdateHitRegion();
void DeactivateUpdateHitRegion();
// Properly retrieves documentSize of any subdocument type.
nsresult GetWindowDimensions(nsIntRect& aRect);
private:
void SetOwnerContent(mozilla::dom::Element* aContent);
@ -284,9 +287,6 @@ private:
nsresult MaybeCreateDocShell();
nsresult EnsureMessageManager();
// Properly retrieves documentSize of any subdocument type.
nsresult GetWindowDimensions(nsIntRect& aRect);
// Updates the subdocument position and size. This gets called only
// when we have our own in-process DocShell.
void UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame);

View File

@ -1265,15 +1265,6 @@ DOMInterfaces = {
'wrapperCache': False,
}],
'VRFieldOfView': {
'wrapperCache': False,
},
'VRFieldOfViewReadOnly': {
'concrete': False,
'wrapperCache': False,
},
'VRDevice': {
'concrete': False
},

View File

@ -694,6 +694,10 @@ void HTMLMediaElement::AbortExistingLoads()
mSuspendedForPreloadNone = false;
mDownloadSuspendedByCache = false;
mMediaInfo = MediaInfo();
mIsEncrypted = false;
#ifdef MOZ_EME
mPendingEncryptedInitData.mInitDatas.Clear();
#endif // MOZ_EME
mSourcePointer = nullptr;
mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
@ -3088,7 +3092,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
nsAutoPtr<const MetadataTags> aTags)
{
mMediaInfo = *aInfo;
mIsEncrypted = aInfo->IsEncrypted();
mIsEncrypted = aInfo->IsEncrypted()
#ifdef MOZ_EME
|| mPendingEncryptedInitData.IsEncrypted()
#endif // MOZ_EME
;
mTags = aTags.forget();
mLoadedDataFired = false;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
@ -3115,8 +3123,12 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
}
#ifdef MOZ_EME
DispatchEncrypted(aInfo->mCrypto.mInitData, aInfo->mCrypto.mType);
#endif
// Dispatch a distinct 'encrypted' event for each initData we have.
for (const auto& initData : mPendingEncryptedInitData.mInitDatas) {
DispatchEncrypted(initData.mInitData, initData.mType);
}
mPendingEncryptedInitData.mInitDatas.Clear();
#endif // MOZ_EME
}
// Expose the tracks to JS directly.
@ -4479,6 +4491,13 @@ void
HTMLMediaElement::DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType)
{
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
// Ready state not HAVE_METADATA (yet), don't dispatch encrypted now.
// Queueing for later dispatch in MetadataLoaded.
mPendingEncryptedInitData.AddInitData(aInitDataType, aInitData);
return;
}
nsRefPtr<MediaEncryptedEvent> event;
if (IsCORSSameOrigin()) {
event = MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData);

View File

@ -561,7 +561,6 @@ public:
void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) override;
bool IsEventAttributeName(nsIAtom* aName) override;
// Returns the principal of the "top level" document; the origin displayed
@ -1322,6 +1321,11 @@ protected:
// True if the media has encryption information.
bool mIsEncrypted;
#ifdef MOZ_EME
// Init Data that needs to be sent in 'encrypted' events in MetadataLoaded().
EncryptionInfo mPendingEncryptedInitData;
#endif // MOZ_EME
// True if the media's channel's download has been suspended.
bool mDownloadSuspendedByCache;

View File

@ -2026,8 +2026,8 @@ TabChild::RecvUpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
mOrientation = orientation;
ScreenIntSize oldScreenSize = mInnerSize;
mInnerSize = size;
mWidget->Resize(0, 0, size.width, size.height,
true);
mWidget->Resize(rect.x + chromeDisp.x, rect.y + chromeDisp.y, size.width, size.height,
true);
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, size.width, size.height,

View File

@ -80,6 +80,7 @@
#include "nsICancelable.h"
#include "gfxPrefs.h"
#include "nsILoginManagerPrompter.h"
#include "nsPIWindowRoot.h"
#include <algorithm>
using namespace mozilla::dom;
@ -267,6 +268,7 @@ TabParent::TabParent(nsIContentParent* aManager,
, mDefaultScale(0)
, mShown(false)
, mUpdatedDimensions(false)
, mChromeOffset(0, 0)
, mManager(aManager)
, mMarkedDestroying(false)
, mIsDestroyed(false)
@ -325,10 +327,36 @@ TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader)
void
TabParent::SetOwnerElement(Element* aElement)
{
// If we held previous content then unregister for its events.
RemoveWindowListeners();
// Update to the new content, and register to listen for events from it.
mFrameElement = aElement;
if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
if (eventTarget) {
eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
this, false, false);
}
}
TryCacheDPIAndScale();
}
void
TabParent::RemoveWindowListeners()
{
if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
if (eventTarget) {
eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
this, false);
}
}
}
void
TabParent::GetAppType(nsAString& aOut)
{
@ -880,8 +908,7 @@ TabParent::RecvSetDimensions(const uint32_t& aFlags,
}
void
TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
const nsIntPoint& aChromeDisp)
TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size)
{
if (mIsDestroyed) {
return;
@ -889,15 +916,25 @@ TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
hal::ScreenConfiguration config;
hal::GetCurrentScreenConfiguration(&config);
ScreenOrientation orientation = config.orientation();
nsIntPoint chromeOffset = -LayoutDevicePixel::ToUntyped(GetChildProcessOffset());
if (!mUpdatedDimensions || mOrientation != orientation ||
mDimensions != size || !mRect.IsEqualEdges(rect)) {
mDimensions != size || !mRect.IsEqualEdges(rect) ||
chromeOffset != mChromeOffset) {
nsCOMPtr<nsIWidget> widget = GetWidget();
nsIntRect contentRect = rect;
if (widget) {
contentRect.x += widget->GetClientOffset().x;
contentRect.y += widget->GetClientOffset().y;
}
mUpdatedDimensions = true;
mRect = rect;
mRect = contentRect;
mDimensions = size;
mOrientation = orientation;
mChromeOffset = chromeOffset;
unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, aChromeDisp);
unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, mChromeOffset);
}
}
@ -2740,6 +2777,27 @@ TabParent::DeallocPPluginWidgetParent(mozilla::plugins::PPluginWidgetParent* aAc
return true;
}
nsresult
TabParent::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("MozUpdateWindowPos") && !mIsDestroyed) {
// This event is sent when the widget moved. Therefore we only update
// the position.
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (!frameLoader) {
return NS_OK;
}
nsIntRect windowDims;
NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims), NS_ERROR_FAILURE);
UpdateDimensions(windowDims, mDimensions);
return NS_OK;
}
return NS_OK;
}
class FakeChannel final : public nsIChannel,
public nsIAuthPromptCallback,
public nsIInterfaceRequestor,

View File

@ -60,6 +60,7 @@ class Element;
struct StructuredCloneData;
class TabParent final : public PBrowserParent
, public nsIDOMEventListener
, public nsITabParent
, public nsIAuthPromptProvider
, public nsISecureBrowserUI
@ -73,6 +74,8 @@ class TabParent final : public PBrowserParent
public:
// nsITabParent
NS_DECL_NSITABPARENT
// nsIDOMEventListener interfaces
NS_DECL_NSIDOMEVENTLISTENER
TabParent(nsIContentParent* aManager,
const TabId& aTabId,
@ -107,6 +110,8 @@ public:
void Destroy();
void RemoveWindowListeners();
virtual bool RecvMoveFocus(const bool& aForward) override;
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override;
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override;
@ -225,8 +230,7 @@ public:
// message-sending functions under a layer of indirection and
// eating the return values
void Show(const ScreenIntSize& size, bool aParentIsActive);
void UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
const nsIntPoint& chromeDisp);
void UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size);
void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
void UIResolutionChanged();
void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
@ -427,6 +431,7 @@ protected:
CSSToLayoutDeviceScale mDefaultScale;
bool mShown;
bool mUpdatedDimensions;
nsIntPoint mChromeOffset;
private:
already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;

View File

@ -136,11 +136,11 @@ public:
#ifdef MOZ_EME
// Dispatches a "encrypted" event to the HTMLMediaElement, with the
// provided init data.
// provided init data. Actual dispatch may be delayed until HAVE_METADATA.
// Main thread only.
virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) = 0;
#endif
#endif // MOZ_EME
};
}

View File

@ -105,16 +105,41 @@ public:
class EncryptionInfo {
public:
EncryptionInfo() : mIsEncrypted(false) {}
struct InitData {
template<typename AInitDatas>
InitData(const nsAString& aType, AInitDatas&& aInitData)
: mType(aType)
, mInitData(Forward<AInitDatas>(aInitData))
{
}
// Encryption type to be passed to JS. Usually `cenc'.
nsString mType;
// Encryption type to be passed to JS. Usually `cenc'.
nsString mType;
// Encryption data.
nsTArray<uint8_t> mInitData;
// Encryption data.
nsTArray<uint8_t> mInitData;
};
typedef nsTArray<InitData> InitDatas;
// True if the stream has encryption metadata
bool mIsEncrypted;
bool IsEncrypted() const
{
return !mInitDatas.IsEmpty();
}
template<typename AInitDatas>
void AddInitData(const nsAString& aType, AInitDatas&& aInitData)
{
mInitDatas.AppendElement(InitData(aType, Forward<AInitDatas>(aInitData)));
}
void AddInitData(const EncryptionInfo& aInfo)
{
mInitDatas.AppendElements(aInfo.mInitDatas);
}
// One 'InitData' per encrypted buffer.
InitDatas mInitDatas;
};
class MediaInfo {
@ -131,7 +156,7 @@ public:
bool IsEncrypted() const
{
return mCrypto.mIsEncrypted;
return mCrypto.IsEncrypted();
}
bool HasValidMedia() const

View File

@ -264,6 +264,34 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor)
return NS_OK;
}
#ifdef MOZ_EME
class DispatchKeyNeededEvent : public nsRunnable {
public:
DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
nsTArray<uint8_t>& aInitData,
const nsString& aInitDataType)
: mDecoder(aDecoder)
, mInitData(aInitData)
, mInitDataType(aInitDataType)
{
}
NS_IMETHOD Run() {
// Note: Null check the owner, as the decoder could have been shutdown
// since this event was dispatched.
MediaDecoderOwner* owner = mDecoder->GetOwner();
if (owner) {
owner->DispatchEncrypted(mInitData, mInitDataType);
}
mDecoder = nullptr;
return NS_OK;
}
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
nsTArray<uint8_t> mInitData;
nsString mInitDataType;
};
#endif // MOZ_EME
void MP4Reader::RequestCodecResource() {
if (mVideo.mDecoder) {
mVideo.mDecoder->AllocateMediaResources();
@ -368,7 +396,7 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
{
MonitorAutoUnlock unlock(mDemuxerMonitor);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mInfo.mCrypto.mIsEncrypted = mIsEncrypted = mCrypto.valid;
mIsEncrypted = mCrypto.valid;
}
// Remember that we've initialized the demuxer, so that if we're decoding
@ -400,15 +428,21 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
}
}
if (mIsEncrypted) {
if (mCrypto.valid) {
nsTArray<uint8_t> initData;
ExtractCryptoInitData(initData);
if (initData.Length() == 0) {
return NS_ERROR_FAILURE;
}
mInfo.mCrypto.mInitData = initData;
mInfo.mCrypto.mType = NS_LITERAL_STRING("cenc");
#ifdef MOZ_EME
// Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
NS_DispatchToMainThread(
new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
#endif // MOZ_EME
// Add init data to info, will get sent from HTMLMediaElement::MetadataLoaded
// (i.e., when transitioning from HAVE_NOTHING to HAVE_METADATA).
mInfo.mCrypto.AddInitData(NS_LITERAL_STRING("cenc"), Move(initData));
}
// Get the duration, and report it to the decoder if we have it.

View File

@ -275,8 +275,6 @@ private:
layers::LayersBackend mLayersBackendType;
nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
// True if we've read the streams' metadata.
bool mDemuxerInitialized;

View File

@ -1068,22 +1068,6 @@ MediaSourceReader::MaybeNotifyHaveData()
IsSeeking(), haveAudio, haveVideo, ended);
}
static void
CombineEncryptionData(EncryptionInfo& aTo, const EncryptionInfo& aFrom)
{
if (!aFrom.mIsEncrypted) {
return;
}
aTo.mIsEncrypted = true;
if (!aTo.mType.IsEmpty() && !aTo.mType.Equals(aFrom.mType)) {
NS_WARNING("mismatched encryption types");
}
aTo.mType = aFrom.mType;
aTo.mInitData.AppendElements(aFrom.mInitData);
}
nsresult
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
{
@ -1107,7 +1091,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
const MediaInfo& info = GetAudioReader()->GetMediaInfo();
MOZ_ASSERT(info.HasAudio());
mInfo.mAudio = info.mAudio;
CombineEncryptionData(mInfo.mCrypto, info.mCrypto);
mInfo.mCrypto.AddInitData(info.mCrypto);
MSE_DEBUG("audio reader=%p duration=%lld",
mAudioSourceDecoder.get(),
mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration());
@ -1120,7 +1104,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
const MediaInfo& info = GetVideoReader()->GetMediaInfo();
MOZ_ASSERT(info.HasVideo());
mInfo.mVideo = info.mVideo;
CombineEncryptionData(mInfo.mCrypto, info.mCrypto);
mInfo.mCrypto.AddInitData(info.mCrypto);
MSE_DEBUG("video reader=%p duration=%lld",
GetVideoReader(),
GetVideoReader()->GetDecoder()->GetMediaDuration());

View File

@ -664,6 +664,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:1,
duration:1.60,
},
{
@ -684,6 +685,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:1,
crossOrigin:true,
duration:1.60,
},
@ -714,6 +716,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:2,
duration:1.60,
},
{
@ -743,6 +746,7 @@ var gEMETests = [
"7e571d047e571d047e571d047e571d04" : "7e5744447e5744447e5744447e574444",
},
sessionType:"temporary",
sessionCount:2,
crossOrigin:true,
duration:1.60,
},

View File

@ -48,11 +48,11 @@ function startTest(test, token)
}
);
var gotEncrypted = false;
var gotEncrypted = 0;
var gotPlaying = false;
v.addEventListener("encrypted", function(ev) {
gotEncrypted = true;
gotEncrypted += 1;
});
v.addEventListener("playing", function () { gotPlaying = true; });
@ -66,7 +66,9 @@ function startTest(test, token)
v.addEventListener("ended", function(ev) {
ok(true, TimeStamp(token) + " got ended event");
ok(gotEncrypted, TimeStamp(token) + " encrypted event should have fired");
is(gotEncrypted, test.sessionCount,
TimeStamp(token) + " encrypted events expected: " + test.sessionCount
+ ", actual: " + gotEncrypted);
ok(gotPlaying, TimeStamp(token) + " playing event should have fired");
ok(Math.abs(test.duration - v.duration) < 0.1,

View File

@ -11,6 +11,9 @@
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
// Turn off the authentication dialog blocking for this test.
SpecialPowers.setIntPref("network.auth.allow-subresource-auth", 2)
var tests = [
// Not the same origin no CORS asked for, should have silence
{ url: "http://example.org:80/tests/dom/media/webaudio/test/small-shot.ogg",

View File

@ -790,7 +790,6 @@ NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
tabContentBounds.ScaleInverseRoundOut(scaleFactor);
int32_t windowH = tabContentBounds.height + int(chromeSize.y);
// This is actually relative to window-chrome.
nsPoint pluginPosition = AsNsPoint(pluginFrame->GetScreenRect().TopLeft());
// Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
@ -800,8 +799,8 @@ NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
nsPoint screenPoint;
switch (sourceSpace) {
case NPCoordinateSpacePlugin:
screenPoint = sourcePoint + pluginFrame->GetContentRectRelativeToSelf().TopLeft() +
chromeSize + pluginPosition + windowPosition;
screenPoint = sourcePoint + pluginPosition +
pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
break;
case NPCoordinateSpaceWindow:
screenPoint = nsPoint(sourcePoint.x, windowH-sourcePoint.y) +
@ -824,8 +823,8 @@ NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
nsPoint destPoint;
switch (destSpace) {
case NPCoordinateSpacePlugin:
destPoint = screenPoint - pluginFrame->GetContentRectRelativeToSelf().TopLeft() -
chromeSize - pluginPosition - windowPosition;
destPoint = screenPoint - pluginPosition -
pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
break;
case NPCoordinateSpaceWindow:
destPoint = screenPoint - windowPosition;

View File

@ -17,30 +17,109 @@ using namespace mozilla::gfx;
namespace mozilla {
namespace dom {
VRFieldOfView*
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);
}
already_AddRefed<VRFieldOfView>
VRFieldOfView::Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
ErrorResult& aRV)
{
return new VRFieldOfView(aParams.mUpDegrees, aParams.mRightDegrees,
aParams.mDownDegrees, aParams.mLeftDegrees);
nsRefPtr<VRFieldOfView> fov =
new VRFieldOfView(aGlobal.GetAsSupports(),
aParams.mUpDegrees, aParams.mRightDegrees,
aParams.mDownDegrees, aParams.mLeftDegrees);
return fov.forget();
}
VRFieldOfView*
already_AddRefed<VRFieldOfView>
VRFieldOfView::Constructor(const GlobalObject& aGlobal,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees,
ErrorResult& aRV)
{
return new VRFieldOfView(aUpDegrees, aRightDegrees, aDownDegrees,
aLeftDegrees);
nsRefPtr<VRFieldOfView> fov =
new VRFieldOfView(aGlobal.GetAsSupports(),
aUpDegrees, aRightDegrees, aDownDegrees,
aLeftDegrees);
return fov.forget();
}
bool
JSObject*
VRFieldOfView::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector)
JS::Handle<JSObject*> aGivenProto)
{
return VRFieldOfViewBinding::Wrap(aCx, this, aGivenProto, aReflector);
return VRFieldOfViewBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VREyeParameters, mParent, mMinFOV, mMaxFOV, mRecFOV, mCurFOV, mEyeTranslation, mRenderRect)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VREyeParameters, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VREyeParameters, Release)
VREyeParameters::VREyeParameters(nsISupports* aParent,
const gfx::VRFieldOfView& aMinFOV,
const gfx::VRFieldOfView& aMaxFOV,
const gfx::VRFieldOfView& aRecFOV,
const gfx::Point3D& aEyeTranslation,
const gfx::VRFieldOfView& aCurFOV,
const gfx::IntRect& aRenderRect)
: mParent(aParent)
{
mMinFOV = new VRFieldOfView(aParent, aMinFOV);
mMaxFOV = new VRFieldOfView(aParent, aMaxFOV);
mRecFOV = new VRFieldOfView(aParent, aRecFOV);
mCurFOV = new VRFieldOfView(aParent, aCurFOV);
mEyeTranslation = new DOMPoint(aParent, aEyeTranslation.x, aEyeTranslation.y, aEyeTranslation.z, 0.0);
mRenderRect = new DOMRect(aParent, aRenderRect.x, aRenderRect.y, aRenderRect.width, aRenderRect.height);
}
VRFieldOfView*
VREyeParameters::MinimumFieldOfView()
{
return mMinFOV;
}
VRFieldOfView*
VREyeParameters::MaximumFieldOfView()
{
return mMaxFOV;
}
VRFieldOfView*
VREyeParameters::RecommendedFieldOfView()
{
return mRecFOV;
}
VRFieldOfView*
VREyeParameters::CurrentFieldOfView()
{
return mCurFOV;
}
DOMPoint*
VREyeParameters::EyeTranslation()
{
return mEyeTranslation;
}
DOMRect*
VREyeParameters::RenderRect()
{
return mRenderRect;
}
JSObject*
VREyeParameters::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return VREyeParametersBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRPositionState, mParent)
@ -126,37 +205,8 @@ PositionSensorVRDevice::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenP
return PositionSensorVRDeviceBinding::Wrap(aCx, this, aGivenProto);
}
static void
ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
{
if (aPropertyValue) {
static_cast<VRHMDInfo*>(aPropertyValue)->Release();
}
}
void
HMDVRDevice::XxxToggleElementVR(Element& aElement)
{
VRHMDInfo* hmdPtr = static_cast<VRHMDInfo*>(aElement.GetProperty(nsGkAtoms::vr_state));
if (hmdPtr) {
aElement.DeleteProperty(nsGkAtoms::vr_state);
return;
}
nsRefPtr<VRHMDInfo> hmdRef = mHMD;
aElement.SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
ReleaseHMDInfoRef,
true);
}
namespace {
gfx::VRHMDInfo::Eye
EyeToEye(const VREye& aEye)
{
return aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right;
}
class HMDInfoVRDevice : public HMDVRDevice
{
public:
@ -164,9 +214,21 @@ public:
: HMDVRDevice(aParent, aHMD)
{
// XXX TODO use real names/IDs
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
mDeviceId.AssignLiteral("somedevid");
mDeviceName.AssignLiteral("HMD Device");
uint64_t hmdid = reinterpret_cast<uint64_t>(aHMD);
mHWID.Truncate();
mHWID.AppendPrintf("HMDInfo-0x%llx", hmdid);
mDeviceId.Truncate();
mDeviceId.AppendPrintf("HMDInfo-dev-0x%llx", hmdid);
if (aHMD->GetType() == VRHMDType::Oculus) {
mDeviceName.AssignLiteral("VR HMD Device (oculus)");
} else if (aHMD->GetType() == VRHMDType::Cardboard) {
mDeviceName.AssignLiteral("VR HMD Device (cardboard)");
} else {
mDeviceName.AssignLiteral("VR HMD Device (unknown)");
}
mValid = true;
}
@ -193,46 +255,22 @@ public:
mHMD->SetFOV(left, right, zNear, zFar);
}
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) override
virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) override
{
gfx::Point3D p = mHMD->GetEyeTranslation(EyeToEye(aEye));
nsRefPtr<DOMPoint> obj = new DOMPoint(mParent, p.x, p.y, p.z, 0.0);
return obj.forget();
}
virtual VRFieldOfView* GetCurrentEyeFieldOfView(VREye aEye) override
{
return CopyFieldOfView(mHMD->GetEyeFOV(EyeToEye(aEye)));
}
virtual VRFieldOfView* GetRecommendedEyeFieldOfView(VREye aEye) override
{
return CopyFieldOfView(mHMD->GetRecommendedEyeFOV(EyeToEye(aEye)));
}
virtual VRFieldOfView* GetMaximumEyeFieldOfView(VREye aEye) override
{
return CopyFieldOfView(mHMD->GetMaximumEyeFOV(EyeToEye(aEye)));
}
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) override
{
const IntSize& a(mHMD->SuggestedEyeResolution());
nsRefPtr<DOMRect> obj =
new DOMRect(mParent,
(aEye == VREye::Left) ? 0 : a.width, 0,
a.width, a.height);
return obj.forget();
gfx::IntSize sz(mHMD->SuggestedEyeResolution());
gfx::VRHMDInfo::Eye eye = aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right;
nsRefPtr<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();
}
protected:
VRFieldOfView*
CopyFieldOfView(const gfx::VRFieldOfView& aSrc)
{
return new VRFieldOfView(aSrc.upDegrees, aSrc.rightDegrees,
aSrc.downDegrees, aSrc.leftDegrees);
}
};
class HMDPositionVRDevice : public PositionSensorVRDevice
@ -244,9 +282,21 @@ public:
, mTracking(false)
{
// XXX TODO use real names/IDs
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
mDeviceId.AssignLiteral("somedevid");
mDeviceName.AssignLiteral("HMD Position Device");
uint64_t hmdid = reinterpret_cast<uint64_t>(aHMD);
mHWID.Truncate();
mHWID.AppendPrintf("HMDInfo-0x%llx", hmdid);
mDeviceId.Truncate();
mDeviceId.AppendPrintf("HMDInfo-dev-0x%llx", hmdid);
if (aHMD->GetType() == VRHMDType::Oculus) {
mDeviceName.AssignLiteral("VR Position Device (oculus)");
} else if (aHMD->GetType() == VRHMDType::Cardboard) {
mDeviceName.AssignLiteral("VR Position Device (cardboard)");
} else {
mDeviceName.AssignLiteral("VR Position Device (unknown)");
}
mValid = true;
}
@ -258,20 +308,33 @@ public:
}
}
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) override
virtual already_AddRefed<VRPositionState> GetState() override
{
if (!mTracking) {
mHMD->StartSensorTracking();
mTracking = true;
}
gfx::VRHMDSensorState state = mHMD->GetSensorState(timeOffset);
gfx::VRHMDSensorState state = mHMD->GetSensorState();
nsRefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
return obj.forget();
}
virtual void ZeroSensor() override
virtual already_AddRefed<VRPositionState> GetImmediateState() override
{
if (!mTracking) {
mHMD->StartSensorTracking();
mTracking = true;
}
gfx::VRHMDSensorState state = mHMD->GetSensorState();
nsRefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
return obj.forget();
}
virtual void ResetSensor() override
{
mHMD->ZeroSensor();
}
@ -286,13 +349,8 @@ protected:
bool
VRDevice::CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<nsRefPtr<VRDevice>>& aDevices)
{
if (!gfx::VRHMDManagerOculus::Init()) {
NS_WARNING("Failed to initialize Oculus HMD Manager");
return false;
}
nsTArray<nsRefPtr<gfx::VRHMDInfo>> hmds;
gfx::VRHMDManagerOculus::GetOculusHMDs(hmds);
gfx::VRHMDManager::GetAllHMDs(hmds);
for (size_t i = 0; i < hmds.Length(); ++i) {
uint32_t sensorBits = hmds[i]->GetSupportedSensorStateBits();

View File

@ -26,24 +26,36 @@ namespace dom {
class Element;
class VRFieldOfViewReadOnly : public NonRefcountedDOMObject
class VRFieldOfViewReadOnly : public nsWrapperCache
{
public:
VRFieldOfViewReadOnly(double aUpDegrees, double aRightDegrees,
VRFieldOfViewReadOnly(nsISupports* aParent,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees)
: mUpDegrees(aUpDegrees)
: mParent(aParent)
, mUpDegrees(aUpDegrees)
, mRightDegrees(aRightDegrees)
, mDownDegrees(aDownDegrees)
, mLeftDegrees(aLeftDegrees)
{
}
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFieldOfViewReadOnly)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFieldOfViewReadOnly)
double UpDegrees() const { return mUpDegrees; }
double RightDegrees() const { return mRightDegrees; }
double DownDegrees() const { return mDownDegrees; }
double LeftDegrees() const { return mLeftDegrees; }
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
protected:
virtual ~VRFieldOfViewReadOnly() {}
nsCOMPtr<nsISupports> mParent;
double mUpDegrees;
double mRightDegrees;
double mDownDegrees;
@ -53,22 +65,30 @@ protected:
class VRFieldOfView final : public VRFieldOfViewReadOnly
{
public:
explicit VRFieldOfView(double aUpDegrees = 0.0, double aRightDegrees = 0.0,
double aDownDegrees = 0.0, double aLeftDegrees = 0.0)
: VRFieldOfViewReadOnly(aUpDegrees, aRightDegrees, aDownDegrees, aLeftDegrees)
VRFieldOfView(nsISupports* aParent, const gfx::VRFieldOfView& aSrc)
: VRFieldOfViewReadOnly(aParent,
aSrc.upDegrees, aSrc.rightDegrees,
aSrc.downDegrees, aSrc.leftDegrees)
{}
static VRFieldOfView*
explicit VRFieldOfView(nsISupports* aParent,
double aUpDegrees = 0.0, double aRightDegrees = 0.0,
double aDownDegrees = 0.0, double aLeftDegrees = 0.0)
: VRFieldOfViewReadOnly(aParent,
aUpDegrees, aRightDegrees, aDownDegrees, aLeftDegrees)
{}
static already_AddRefed<VRFieldOfView>
Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
ErrorResult& aRv);
static VRFieldOfView*
static already_AddRefed<VRFieldOfView>
Constructor(const GlobalObject& aGlobal,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees,
ErrorResult& aRv);
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void SetUpDegrees(double aVal) { mUpDegrees = aVal; }
void SetRightDegrees(double aVal) { mRightDegrees = aVal; }
@ -80,7 +100,7 @@ class VRPositionState final : public nsWrapperCache
{
~VRPositionState() {}
public:
explicit VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState);
VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRPositionState)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRPositionState)
@ -117,6 +137,43 @@ protected:
nsRefPtr<DOMPoint> mAngularAcceleration;
};
class VREyeParameters final : public nsWrapperCache
{
public:
VREyeParameters(nsISupports* aParent,
const gfx::VRFieldOfView& aMinFOV,
const gfx::VRFieldOfView& aMaxFOV,
const gfx::VRFieldOfView& aRecFOV,
const gfx::Point3D& aEyeTranslation,
const gfx::VRFieldOfView& aCurFOV,
const gfx::IntRect& aRenderRect);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VREyeParameters)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VREyeParameters)
VRFieldOfView* MinimumFieldOfView();
VRFieldOfView* MaximumFieldOfView();
VRFieldOfView* RecommendedFieldOfView();
DOMPoint* EyeTranslation();
VRFieldOfView* CurrentFieldOfView();
DOMRect* RenderRect();
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
protected:
~VREyeParameters() {}
nsCOMPtr<nsISupports> mParent;
nsRefPtr<VRFieldOfView> mMinFOV;
nsRefPtr<VRFieldOfView> mMaxFOV;
nsRefPtr<VRFieldOfView> mRecFOV;
nsRefPtr<DOMPoint> mEyeTranslation;
nsRefPtr<VRFieldOfView> mCurFOV;
nsRefPtr<DOMRect> mRenderRect;
};
class VRDevice : public nsISupports,
public nsWrapperCache
{
@ -176,20 +233,14 @@ protected:
class HMDVRDevice : public VRDevice
{
public:
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) = 0;
virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) = 0;
virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
const VRFieldOfViewInit& aRightFOV,
double zNear, double zFar) = 0;
virtual VRFieldOfView* GetCurrentEyeFieldOfView(VREye aEye) = 0;
virtual VRFieldOfView* GetRecommendedEyeFieldOfView(VREye aEye) = 0;
virtual VRFieldOfView* GetMaximumEyeFieldOfView(VREye aEye) = 0;
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) = 0;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void XxxToggleElementVR(Element& aElement);
gfx::VRHMDInfo *GetHMD() { return mHMD.get(); }
protected:
@ -206,9 +257,11 @@ protected:
class PositionSensorVRDevice : public VRDevice
{
public:
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) = 0;
virtual already_AddRefed<VRPositionState> GetState() = 0;
virtual void ZeroSensor() = 0;
virtual already_AddRefed<VRPositionState> GetImmediateState() = 0;
virtual void ResetSensor() = 0;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

View File

@ -52,6 +52,20 @@ interface VRPositionState {
readonly attribute DOMPoint? angularAcceleration;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface VREyeParameters {
/* These values are expected to be static per-device/per-user */
[Constant, Cached] readonly attribute VRFieldOfView minimumFieldOfView;
[Constant, Cached] readonly attribute VRFieldOfView maximumFieldOfView;
[Constant, Cached] readonly attribute VRFieldOfView recommendedFieldOfView;
[Constant, Cached] readonly attribute DOMPoint eyeTranslation;
/* These values will vary after a FOV has been set */
[Constant, Cached] readonly attribute VRFieldOfView currentFieldOfView;
[Constant, Cached] readonly attribute DOMRect renderRect;
};
[Pref="dom.vr.enabled"]
interface VRDevice {
/**
@ -59,38 +73,26 @@ interface VRDevice {
* VR Device is a part of. All VRDevice/Sensors that come
* from the same hardware will have the same hardwareId
*/
[Pure] readonly attribute DOMString hardwareUnitId;
[Constant] readonly attribute DOMString hardwareUnitId;
/**
* An identifier for this distinct sensor/device on a physical
* hardware device. This shouldn't change across browser
* restrats, allowing configuration data to be saved based on it.
*/
[Pure] readonly attribute DOMString deviceId;
[Constant] readonly attribute DOMString deviceId;
/**
* a device name, a user-readable name identifying it
*/
[Pure] readonly attribute DOMString deviceName;
[Constant] readonly attribute DOMString deviceName;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface HMDVRDevice : VRDevice {
/* The translation that should be applied to the view matrix for rendering each eye */
DOMPoint getEyeTranslation(VREye whichEye);
// the FOV that the HMD was configured with
[NewObject]
VRFieldOfView getCurrentEyeFieldOfView(VREye whichEye);
// the recommended FOV, per eye.
[NewObject]
VRFieldOfView getRecommendedEyeFieldOfView(VREye whichEye);
// the maximum FOV, per eye. Above this, rendering will look broken.
[NewObject]
VRFieldOfView getMaximumEyeFieldOfView(VREye whichEye);
// Return the current VREyeParameters for the given eye
VREyeParameters getEyeParameters(VREye whichEye);
// Set a field of view. If either of the fields of view is null,
// or if their values are all zeros, then the recommended field of view
@ -99,34 +101,30 @@ interface HMDVRDevice : VRDevice {
optional VRFieldOfViewInit rightFOV,
optional double zNear = 0.01,
optional double zFar = 10000.0);
// return a recommended rect for this eye. Only useful for Canvas rendering,
// the x/y coordinates will be the location in the canvas where this eye should
// begin, and the width/height are the dimensions. Any canvas in the appropriate
// ratio will work.
DOMRect getRecommendedEyeRenderRect(VREye whichEye);
// hack for testing
void xxxToggleElementVR(Element element);
};
[Pref="dom.vr.enabled" ,
HeaderFile="mozilla/dom/VRDevice.h"]
interface PositionSensorVRDevice : VRDevice {
/*
* Return a VRPositionState dictionary containing the state of this position sensor,
* at an optional past time or predicted for a future time if timeOffset is != 0.
* Return a VRPositionState dictionary containing the state of this position sensor
* for the current frame if within a requestAnimationFrame callback, or for the
* previous frame if not.
*
* The VRPositionState will contain the position, orientation, and velocity
* and acceleration of each of these properties. Use "hasPosition" and "hasOrientation"
* to check if the associated members are valid; if these are false, those members
* will be null.
*/
[NewObject]
VRPositionState getState(optional double timeOffset = 0.0);
[NewObject] VRPositionState getState();
/* Zero this sensor, treating its current position and orientation
/*
* Return the current instantaneous sensor state.
*/
[NewObject] VRPositionState getImmediateState();
/* Reset this sensor, treating its current position and orientation
* as the "origin/zero" values.
*/
void zeroSensor();
void resetSensor();
};

View File

@ -4223,7 +4223,8 @@ WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
mCondVar(mMutex, "WorkerDebugger::mCondVar"),
mWorkerPrivate(aWorkerPrivate),
mIsEnabled(false),
mIsInitialized(false)
mIsInitialized(false),
mIsFrozen(false)
{
mWorkerPrivate->AssertIsOnParentThread();
}
@ -4277,6 +4278,21 @@ WorkerDebugger::GetIsChrome(bool* aResult)
return NS_OK;
}
NS_IMETHODIMP
WorkerDebugger::GetIsFrozen(bool* aResult)
{
AssertIsOnMainThread();
MutexAutoLock lock(mMutex);
if (!mWorkerPrivate) {
return NS_ERROR_UNEXPECTED;
}
*aResult = mIsFrozen;
return NS_OK;
}
NS_IMETHODIMP
WorkerDebugger::GetParent(nsIWorkerDebugger** aResult)
{
@ -4474,6 +4490,52 @@ WorkerDebugger::Disable()
NotifyIsEnabled(false);
}
void
WorkerDebugger::Freeze()
{
mWorkerPrivate->AssertIsOnWorkerThread();
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &WorkerDebugger::FreezeOnMainThread);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
}
void
WorkerDebugger::FreezeOnMainThread()
{
AssertIsOnMainThread();
mIsFrozen = true;
for (size_t index = 0; index < mListeners.Length(); ++index) {
mListeners[index]->OnFreeze();
}
}
void
WorkerDebugger::Thaw()
{
mWorkerPrivate->AssertIsOnWorkerThread();
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &WorkerDebugger::ThawOnMainThread);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
}
void
WorkerDebugger::ThawOnMainThread()
{
AssertIsOnMainThread();
mIsFrozen = false;
for (size_t index = 0; index < mListeners.Length(); ++index) {
mListeners[index]->OnThaw();
}
}
void
WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
{
@ -5693,6 +5755,7 @@ WorkerPrivate::FreezeInternal(JSContext* aCx)
NS_ASSERTION(!mFrozen, "Already frozen!");
mFrozen = true;
mDebugger->Freeze();
return true;
}
@ -5704,6 +5767,7 @@ WorkerPrivate::ThawInternal(JSContext* aCx)
NS_ASSERTION(mFrozen, "Not yet frozen!");
mFrozen = false;
mDebugger->Thaw();
return true;
}

View File

@ -727,6 +727,7 @@ class WorkerDebugger : public nsIWorkerDebugger {
// Only touched on the main thread.
bool mIsInitialized;
bool mIsFrozen;
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners;
public:
@ -747,6 +748,12 @@ public:
void
Disable();
void
Freeze();
void
Thaw();
void
PostMessageToDebugger(const nsAString& aMessage);
@ -761,6 +768,12 @@ private:
void
NotifyIsEnabled(bool aIsEnabled);
void
FreezeOnMainThread();
void
ThawOnMainThread();
void
PostMessageToDebuggerOnMainThread(const nsAString& aMessage);

View File

@ -2,7 +2,7 @@
interface nsIDOMWindow;
[scriptable, uuid(55d54034-1573-4889-b1d9-93ba12fc33c7)]
[scriptable, uuid(530db841-1b2c-485a-beeb-f2b1acb9714e)]
interface nsIWorkerDebuggerListener : nsISupports
{
void onClose();
@ -10,10 +10,14 @@ interface nsIWorkerDebuggerListener : nsISupports
void onError(in DOMString filename, in unsigned long lineno,
in DOMString message);
void onFreeze();
void onMessage(in DOMString message);
void onThaw();
};
[scriptable, builtinclass, uuid(28e0a60c-ff10-446c-8c2a-5fbdc01394ea)]
[scriptable, builtinclass, uuid(d7c73e54-3c41-4393-9d13-fa2ed4Ba6764)]
interface nsIWorkerDebugger : nsISupports
{
const unsigned long TYPE_DEDICATED = 0;
@ -24,6 +28,8 @@ interface nsIWorkerDebugger : nsISupports
readonly attribute bool isChrome;
readonly attribute bool isFrozen;
readonly attribute nsIWorkerDebugger parent;
readonly attribute unsigned long type;

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var worker = new Worker("WorkerDebugger.isFrozen_worker1.js");
worker.onmessage = function () {
parent.postMessage("ready", "*");
};
</script>
</head>
<body>
This is page 1.
</body>
<html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var worker = new Worker("WorkerDebugger.isFrozen_worker2.js");
worker.onmessage = function () {
parent.postMessage("ready", "*");
};
</script>
</head>
<body>
This is page 2.
</body>
<html>

View File

@ -0,0 +1,5 @@
"use strict";
onmessage = function () {};
postMessage("ready");

View File

@ -0,0 +1,5 @@
"use strict";
onmessage = function () {};
postMessage("ready");

View File

@ -4,6 +4,10 @@ support-files =
WorkerDebugger.initialize_childWorker.js
WorkerDebugger.initialize_debugger.js
WorkerDebugger.initialize_worker.js
WorkerDebugger.isFrozen_iframe1.html
WorkerDebugger.isFrozen_iframe2.html
WorkerDebugger.isFrozen_worker1.js
WorkerDebugger.isFrozen_worker2.js
WorkerDebugger.postMessage_childWorker.js
WorkerDebugger.postMessage_debugger.js
WorkerDebugger.postMessage_worker.js
@ -47,6 +51,7 @@ support-files =
[test_WorkerDebugger.xul]
[test_WorkerDebugger.initialize.xul]
[test_WorkerDebugger.isFrozen.xul]
[test_WorkerDebugger.postMessage.xul]
[test_WorkerDebuggerGlobalScope.createSandbox.xul]
[test_WorkerDebuggerGlobalScope.enterEventLoop.xul]

View File

@ -130,6 +130,41 @@ function waitForDebuggerMessage(dbg, message) {
});
}
function waitForDebuggerFreeze(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onFreeze: function () {
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForDebuggerThaw(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onThaw: function () {
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForWindowMessage(window, message) {
return new Promise(function (resolve) {
let onmessage = function (event) {
if (event.data !== event.data) {
return;
}
window.removeEventListener("message", onmessage, false);
resolve();
};
window.addEventListener("message", onmessage, false);
});
}
function waitForWorkerMessage(worker, message) {
return new Promise(function (resolve) {
worker.addEventListener("message", function onmessage(event) {

View File

@ -0,0 +1,98 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebugger.isFrozen"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const CACHE_SUBFRAMES = "browser.sessionhistory.cache_subframes";
const MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers";
const IFRAME1_URL = "WorkerDebugger.isFrozen_iframe1.html";
const IFRAME2_URL = "WorkerDebugger.isFrozen_iframe2.html";
const WORKER1_URL = "WorkerDebugger.isFrozen_worker1.js";
const WORKER2_URL = "WorkerDebugger.isFrozen_worker2.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
var oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
SpecialPowers.setBoolPref(CACHE_SUBFRAMES, true);
SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
let iframe = $("iframe");
let promise = waitForMultiple([
waitForRegister(WORKER1_URL),
waitForWindowMessage(window, "ready"),
]);
iframe.src = IFRAME1_URL;
let [dbg1] = yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed");
is(dbg1.isFrozen, false,
"debugger for worker on page 1 should not be frozen");
promise = waitForMultiple([
waitForDebuggerFreeze(dbg1),
waitForRegister(WORKER2_URL),
waitForWindowMessage(window, "ready"),
]);
iframe.src = IFRAME2_URL;
let [_, dbg2] = yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed");
is(dbg1.isFrozen, true,
"debugger for worker on page 1 should be frozen");
is(dbg2.isClosed, false,
"debugger for worker on page 2 should not be closed");
is(dbg2.isFrozen, false,
"debugger for worker on page 2 should not be frozen");
promise = waitForMultiple([
waitForDebuggerFreeze(dbg2),
waitForDebuggerThaw(dbg1),
]);
iframe.contentWindow.history.back();
yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed")
is(dbg1.isFrozen, false,
"debugger for worker on page 1 should not be frozen");
is(dbg2.isClosed, false,
"debugger for worker on page 2 should not be closed");
is(dbg2.isFrozen, true,
"debugger for worker on page 2 should be frozen");
SpecialPowers.clearUserPref(CACHE_SUBFRAMES);
SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, oldMaxTotalViewers);
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
<iframe id="iframe"></iframe>
</body>
<label id="test-result"/>
</window>

View File

@ -1452,9 +1452,15 @@ ChromeTooltipListener::sTooltipCallback(nsITimer *aTimer,
if (textFound) {
nsString tipText(tooltipText);
LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset();
self->ShowTooltip(self->mMouseScreenX - screenDot.x,
self->mMouseScreenY - screenDot.y,
tipText);
double scaleFactor = 1.0;
if (shell->GetPresContext()) {
scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
shell->GetPresContext()->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
}
// ShowTooltip expects widget-relative position.
self->ShowTooltip(self->mMouseScreenX - screenDot.x / scaleFactor,
self->mMouseScreenY - screenDot.y / scaleFactor,
tipText);
}
}

View File

@ -10,6 +10,7 @@
#include "Point.h"
#include "Rect.h"
#include "Matrix.h"
#include "Quaternion.h"
#include "UserData.h"
// GenericRefCountedBase allows us to hold on to refcounted objects of any type

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Matrix.h"
#include "Quaternion.h"
#include "Tools.h"
#include <algorithm>
#include <ostream>
@ -28,6 +29,17 @@ operator<<(std::ostream& aStream, const Matrix& aMatrix)
<< "; ]";
}
std::ostream&
operator<<(std::ostream& aStream, const Matrix4x4& aMatrix)
{
const Float *f = &aMatrix._11;
aStream << "[ " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;" << std::endl; f += 4;
aStream << " " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ]" << std::endl;
return aStream;
}
Matrix
Matrix::Rotation(Float aAngle)
{
@ -283,5 +295,32 @@ Matrix4x4::SetNAN()
_44 = UnspecifiedNaN<Float>();
}
void
Matrix4x4::SetRotationFromQuaternion(const Quaternion& q)
{
const Float x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z;
const Float xx = q.x * x2, xy = q.x * y2, xz = q.x * z2;
const Float yy = q.y * y2, yz = q.y * z2, zz = q.z * z2;
const Float wx = q.w * x2, wy = q.w * y2, wz = q.w * z2;
_11 = 1.0f - (yy + zz);
_21 = xy + wz;
_31 = xz - wy;
_41 = 0.0f;
_12 = xy - wz;
_22 = 1.0f - (xx + zz);
_32 = yz + wx;
_42 = 0.0f;
_13 = xz + wy;
_23 = yz - wx;
_33 = 1.0f - (xx + yy);
_43 = 0.0f;
_14 = _42 = _43 = 0.0f;
_44 = 1.0f;
}
}
}

View File

@ -17,6 +17,8 @@
namespace mozilla {
namespace gfx {
class Quaternion;
static bool FuzzyEqual(Float aV1, Float aV2) {
// XXX - Check if fabs does the smart thing and just negates the sign bit.
return fabs(aV2 - aV1) < 1e-6;
@ -390,6 +392,8 @@ public:
Float _31, _32, _33, _34;
Float _41, _42, _43, _44;
friend std::ostream& operator<<(std::ostream& aStream, const Matrix4x4& aMatrix);
Point4D& operator[](int aIndex)
{
MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
@ -872,6 +876,11 @@ public:
*((&_41)+aIndex) = aVector.w;
}
// Sets this matrix to a rotation matrix given by aQuat.
// This quaternion *MUST* be normalized!
// Implemented in Quaternion.cpp
void SetRotationFromQuaternion(const Quaternion& aQuat);
// Set all the members of the matrix to NaN
void SetNAN();
};

57
gfx/2d/Quaternion.cpp Normal file
View File

@ -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/. */
#include "Quaternion.h"
#include "Matrix.h"
#include "Tools.h"
#include <algorithm>
#include <ostream>
#include <math.h>
using namespace std;
namespace mozilla {
namespace gfx {
std::ostream&
operator<<(std::ostream& aStream, const Quaternion& aQuat)
{
return aStream << "< " << aQuat.x << " " << aQuat.y << " " << aQuat.z << " " << aQuat.w << ">";
}
void
Quaternion::SetFromRotationMatrix(const Matrix4x4& m)
{
// see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
const Float trace = m._11 + m._22 + m._33;
if (trace > 0.0) {
const Float s = 0.5f / sqrt(trace + 1.0f);
w = 0.25f / s;
x = (m._32 - m._23) * s;
y = (m._13 - m._31) * s;
z = (m._21 - m._12) * s;
} else if (m._11 > m._22 && m._11 > m._33) {
const Float s = 2.0f * sqrt(1.0f + m._11 - m._22 - m._33);
w = (m._32 - m._23) / s;
x = 0.25f * s;
y = (m._12 + m._21) / s;
z = (m._13 + m._31) / s;
} else if (m._22 > m._33) {
const Float s = 2.0 * sqrt(1.0f + m._22 - m._11 - m._33);
w = (m._13 - m._31) / s;
x = (m._12 + m._21) / s;
y = 0.25f * s;
z = (m._23 + m._32) / s;
} else {
const Float s = 2.0 * sqrt(1.0f + m._33 - m._11 - m._22);
w = (m._21 - m._12) / s;
x = (m._13 + m._31) / s;
y = (m._23 + m._32) / s;
z = 0.25f * s;
}
}
}
}

100
gfx/2d/Quaternion.h Normal file
View File

@ -0,0 +1,100 @@
/* -*- 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 MOZILLA_GFX_QUATERNION_H_
#define MOZILLA_GFX_QUATERNION_H_
#include "Types.h"
#include <math.h>
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
namespace mozilla {
namespace gfx {
class Matrix4x4;
class Quaternion
{
public:
Quaternion()
: x(0.0f), y(0.0f), z(0.0f), w(1.0f)
{}
Quaternion(Float aX, Float aY, Float aZ, Float aW)
: x(aX), y(aY), z(aZ), w(aW)
{}
Quaternion(const Quaternion& aOther)
{
memcpy(this, &aOther, sizeof(*this));
}
Float x, y, z, w;
friend std::ostream& operator<<(std::ostream& aStream, const Quaternion& aQuat);
void Set(Float aX, Float aY, Float aZ, Float aW)
{
x = aX; y = aY; z = aZ; w = aW;
}
// Assumes upper 3x3 of aMatrix is a pure rotation matrix (no scaling)
void SetFromRotationMatrix(const Matrix4x4& aMatrix);
// result = this * aQuat
Quaternion operator*(const Quaternion &aQuat) const
{
Quaternion o;
const Float bx = aQuat.x, by = aQuat.y, bz = aQuat.z, bw = aQuat.w;
o.x = x*bw + w*bx + y*bz - z*by;
o.y = y*bw + w*by + z*bx - x*bz;
o.z = z*bw + w*bz + x*by - y*bx;
o.w = w*bw - x*bx - y*by - z*bz;
return o;
}
Quaternion& operator*=(const Quaternion &aQuat)
{
*this = *this * aQuat;
return *this;
}
Float Length() const
{
return sqrt(x*x + y*y + z*z + w*w);
}
Quaternion& Conjugate()
{
x *= -1.f; y *= -1.f; z *= -1.f;
return *this;
}
Quaternion& Normalize()
{
Float l = Length();
if (l) {
l = 1.0f / l;
x *= l; y *= l; z *= l; w *= l;
} else {
x = y = z = 0.f;
w = 1.f;
}
return *this;
}
Quaternion& Invert()
{
return Conjugate().Normalize();
}
};
}
}
#endif

View File

@ -29,6 +29,7 @@ EXPORTS.mozilla.gfx += [
'PathHelpers.h',
'PatternHelpers.h',
'Point.h',
'Quaternion.h',
'Rect.h',
'Scale.h',
'ScaleFactor.h',
@ -120,6 +121,7 @@ UNIFIED_SOURCES += [
'Path.cpp',
'PathCairo.cpp',
'PathRecording.cpp',
'Quaternion.cpp',
'RecordedEvent.cpp',
'Scale.cpp',
'ScaledFontBase.cpp',

View File

@ -73,17 +73,19 @@ struct DeviceAttachmentsD3D11
//
// VR pieces
//
RefPtr<ID3D11InputLayout> mVRDistortionInputLayout;
RefPtr<ID3D11Buffer> mVRDistortionConstants;
typedef EnumeratedArray<VRHMDType, VRHMDType::NumHMDTypes, RefPtr<ID3D11InputLayout>>
VRDistortionInputLayoutArray;
typedef EnumeratedArray<VRHMDType, VRHMDType::NumHMDTypes, RefPtr<ID3D11VertexShader>>
VRVertexShaderArray;
typedef EnumeratedArray<VRHMDType, VRHMDType::NumHMDTypes, RefPtr<ID3D11PixelShader>>
VRPixelShaderArray;
VRDistortionInputLayoutArray mVRDistortionInputLayout;
VRVertexShaderArray mVRDistortionVS;
VRPixelShaderArray mVRDistortionPS;
RefPtr<ID3D11Buffer> mVRDistortionConstants;
// These will be created/filled in as needed during rendering whenever the configuration
// changes.
VRHMDConfiguration mVRConfiguration;
@ -334,7 +336,12 @@ CompositorD3D11::Initialize()
sizeof(vrlayout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
OculusVRDistortionVS,
sizeof(OculusVRDistortionVS),
byRef(mAttachments->mVRDistortionInputLayout));
byRef(mAttachments->mVRDistortionInputLayout[VRHMDType::Oculus]));
// XXX shared for now, rename
mAttachments->mVRDistortionInputLayout[VRHMDType::Cardboard] =
mAttachments->mVRDistortionInputLayout[VRHMDType::Oculus];
cBufferDesc.ByteWidth = sizeof(gfx::VRDistortionConstants);
hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, byRef(mAttachments->mVRDistortionConstants));
if (FAILED(hr)) {
@ -661,6 +668,7 @@ CompositorD3D11::DrawVRDistortion(const gfx::Rect& aRect,
gfx::IntSize size = vrEffect->mRenderTarget->GetSize(); // XXX source->GetSize()
VRHMDInfo* hmdInfo = vrEffect->mHMD;
VRHMDType hmdType = hmdInfo->GetType();
VRDistortionConstants shaderConstants;
// do we need to recreate the VR buffers, since the config has changed?
@ -709,20 +717,17 @@ CompositorD3D11::DrawVRDistortion(const gfx::Rect& aRect,
// Triangle lists and same layout for both eyes
mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
mContext->IASetInputLayout(mAttachments->mVRDistortionInputLayout);
// Shaders for this HMD
mContext->VSSetShader(mAttachments->mVRDistortionVS[mAttachments->mVRConfiguration.hmdType], nullptr, 0);
mContext->PSSetShader(mAttachments->mVRDistortionPS[mAttachments->mVRConfiguration.hmdType], nullptr, 0);
mContext->IASetInputLayout(mAttachments->mVRDistortionInputLayout[hmdType]);
mContext->VSSetShader(mAttachments->mVRDistortionVS[hmdType], nullptr, 0);
mContext->PSSetShader(mAttachments->mVRDistortionPS[hmdType], nullptr, 0);
// This is the source texture SRV for the pixel shader
// XXX, um should we cache this SRV?
// XXX, um should we cache this SRV on the source?
RefPtr<ID3D11ShaderResourceView> view;
mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
ID3D11ShaderResourceView* srView = view;
mContext->PSSetShaderResources(0, 1, &srView);
gfx::IntSize vpSizeInt = mCurrentRT->GetSize();
gfx::Size vpSize(vpSizeInt.width, vpSizeInt.height);
ID3D11Buffer* vbuffer;
@ -1343,6 +1348,11 @@ CompositorD3D11::CreateShaders()
return false;
}
// These are shared
// XXX rename Oculus shaders to something more generic
mAttachments->mVRDistortionVS[VRHMDType::Cardboard] = mAttachments->mVRDistortionVS[VRHMDType::Oculus];
mAttachments->mVRDistortionPS[VRHMDType::Cardboard] = mAttachments->mVRDistortionPS[VRHMDType::Oculus];
return true;
}

View File

@ -1,9 +1,8 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -104,10 +103,10 @@ ret
const BYTE LayerQuadVS[] =
{
68, 88, 66, 67, 67, 61,
27, 151, 57, 33, 48, 19,
55, 6, 95, 77, 254, 163,
118, 237, 1, 0, 0, 0,
68, 88, 66, 67, 200, 251,
64, 251, 166, 240, 101, 137,
191, 140, 75, 217, 9, 168,
61, 163, 1, 0, 0, 0,
180, 6, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
152, 1, 0, 0, 160, 3,
@ -262,7 +261,7 @@ const BYTE LayerQuadVS[] =
65, 84, 116, 0, 0, 0,
13, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 6, 0,
3, 0, 0, 0, 12, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
@ -272,7 +271,7 @@ const BYTE LayerQuadVS[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -363,10 +362,10 @@ const BYTE LayerQuadVS[] =
41, 32, 72, 76, 83, 76,
32, 83, 104, 97, 100, 101,
114, 32, 67, 111, 109, 112,
105, 108, 101, 114, 32, 57,
46, 51, 48, 46, 57, 50,
48, 48, 46, 50, 48, 53,
52, 54, 0, 171, 73, 83,
105, 108, 101, 114, 32, 54,
46, 51, 46, 57, 54, 48,
48, 46, 49, 54, 51, 56,
52, 0, 171, 171, 73, 83,
71, 78, 44, 0, 0, 0,
1, 0, 0, 0, 8, 0,
0, 0, 32, 0, 0, 0,
@ -393,10 +392,9 @@ const BYTE LayerQuadVS[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -460,10 +458,10 @@ ret
const BYTE SolidColorShader[] =
{
68, 88, 66, 67, 182, 98,
102, 100, 187, 218, 19, 40,
99, 74, 29, 228, 47, 107,
160, 122, 1, 0, 0, 0,
68, 88, 66, 67, 30, 148,
104, 202, 165, 39, 58, 182,
100, 205, 95, 195, 52, 137,
197, 241, 1, 0, 0, 0,
224, 3, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
132, 0, 0, 0, 204, 0,
@ -508,7 +506,7 @@ const BYTE SolidColorShader[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -599,9 +597,9 @@ const BYTE SolidColorShader[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 80, 0,
0, 0, 2, 0, 0, 0,
8, 0, 0, 0, 56, 0,
@ -629,10 +627,9 @@ const BYTE SolidColorShader[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -716,10 +713,10 @@ ret
const BYTE RGBShader[] =
{
68, 88, 66, 67, 195, 54,
227, 44, 79, 159, 121, 69,
60, 252, 145, 90, 151, 241,
175, 162, 1, 0, 0, 0,
68, 88, 66, 67, 239, 198,
87, 206, 69, 92, 245, 30,
125, 195, 239, 77, 37, 241,
175, 187, 1, 0, 0, 0,
232, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
204, 0, 0, 0, 136, 1,
@ -795,7 +792,7 @@ const BYTE RGBShader[] =
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -899,9 +896,9 @@ const BYTE RGBShader[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 80, 0,
0, 0, 2, 0, 0, 0,
8, 0, 0, 0, 56, 0,
@ -929,10 +926,9 @@ const BYTE RGBShader[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -1014,10 +1010,10 @@ ret
const BYTE RGBAShader[] =
{
68, 88, 66, 67, 124, 18,
8, 218, 34, 168, 20, 218,
144, 232, 183, 104, 152, 211,
5, 26, 1, 0, 0, 0,
68, 88, 66, 67, 230, 59,
90, 23, 60, 77, 18, 113,
14, 129, 183, 152, 233, 55,
111, 42, 1, 0, 0, 0,
196, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
192, 0, 0, 0, 100, 1,
@ -1087,7 +1083,7 @@ const BYTE RGBAShader[] =
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -1191,9 +1187,9 @@ const BYTE RGBAShader[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 80, 0,
0, 0, 2, 0, 0, 0,
8, 0, 0, 0, 56, 0,
@ -1221,10 +1217,9 @@ const BYTE RGBAShader[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -1324,10 +1319,10 @@ ret
const BYTE ComponentAlphaShader[] =
{
68, 88, 66, 67, 152, 37,
117, 77, 87, 153, 20, 62,
92, 142, 77, 134, 246, 203,
174, 59, 1, 0, 0, 0,
68, 88, 66, 67, 186, 162,
72, 42, 69, 36, 160, 68,
108, 121, 216, 238, 108, 37,
6, 145, 1, 0, 0, 0,
68, 6, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
64, 1, 0, 0, 160, 2,
@ -1450,7 +1445,7 @@ const BYTE ComponentAlphaShader[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -1561,9 +1556,9 @@ const BYTE ComponentAlphaShader[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 80, 0,
0, 0, 2, 0, 0, 0,
8, 0, 0, 0, 56, 0,
@ -1595,10 +1590,9 @@ const BYTE ComponentAlphaShader[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -1714,10 +1708,10 @@ ret
const BYTE YCbCrShader[] =
{
68, 88, 66, 67, 26, 187,
43, 127, 28, 135, 212, 40,
57, 230, 160, 198, 151, 242,
106, 110, 1, 0, 0, 0,
68, 88, 66, 67, 181, 118,
100, 53, 248, 120, 136, 92,
59, 190, 18, 201, 139, 224,
32, 141, 1, 0, 0, 0,
212, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
220, 1, 0, 0, 44, 4,
@ -1896,7 +1890,7 @@ const BYTE YCbCrShader[] =
0, 0, 15, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
6, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -1906,7 +1900,7 @@ const BYTE YCbCrShader[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -2022,9 +2016,9 @@ const BYTE YCbCrShader[] =
76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
57, 46, 51, 48, 46, 57,
50, 48, 48, 46, 50, 48,
53, 52, 54, 0, 73, 83,
54, 46, 51, 46, 57, 54,
48, 48, 46, 49, 54, 51,
56, 52, 0, 171, 73, 83,
71, 78, 80, 0, 0, 0,
2, 0, 0, 0, 8, 0,
0, 0, 56, 0, 0, 0,
@ -2051,10 +2045,9 @@ const BYTE YCbCrShader[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -2165,10 +2158,10 @@ ret
const BYTE LayerQuadMaskVS[] =
{
68, 88, 66, 67, 229, 18,
238, 182, 176, 120, 118, 84,
74, 135, 103, 188, 146, 51,
229, 207, 1, 0, 0, 0,
68, 88, 66, 67, 223, 251,
10, 17, 13, 90, 47, 25,
119, 198, 20, 157, 124, 193,
251, 234, 1, 0, 0, 0,
120, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
224, 1, 0, 0, 76, 4,
@ -2352,7 +2345,7 @@ const BYTE LayerQuadMaskVS[] =
116, 0, 0, 0, 16, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 4, 0,
0, 0, 8, 0, 0, 0,
0, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -2362,7 +2355,7 @@ const BYTE LayerQuadMaskVS[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -2453,10 +2446,10 @@ const BYTE LayerQuadMaskVS[] =
72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
101, 114, 32, 57, 46, 51,
48, 46, 57, 50, 48, 48,
46, 50, 48, 53, 52, 54,
0, 171, 73, 83, 71, 78,
101, 114, 32, 54, 46, 51,
46, 57, 54, 48, 48, 46,
49, 54, 51, 56, 52, 0,
171, 171, 73, 83, 71, 78,
44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
32, 0, 0, 0, 0, 0,
@ -2487,10 +2480,9 @@ const BYTE LayerQuadMaskVS[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -2605,10 +2597,10 @@ ret
const BYTE LayerQuadMask3DVS[] =
{
68, 88, 66, 67, 81, 198,
45, 88, 207, 133, 27, 66,
4, 235, 107, 238, 69, 93,
43, 232, 1, 0, 0, 0,
68, 88, 66, 67, 151, 141,
11, 11, 111, 244, 17, 242,
119, 116, 248, 53, 235, 192,
38, 193, 1, 0, 0, 0,
204, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
24, 2, 0, 0, 160, 4,
@ -2806,7 +2798,7 @@ const BYTE LayerQuadMask3DVS[] =
116, 0, 0, 0, 17, 0,
0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 4, 0,
0, 0, 9, 0, 0, 0,
0, 0, 15, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -2816,7 +2808,7 @@ const BYTE LayerQuadMask3DVS[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -2907,10 +2899,10 @@ const BYTE LayerQuadMask3DVS[] =
72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108,
101, 114, 32, 57, 46, 51,
48, 46, 57, 50, 48, 48,
46, 50, 48, 53, 52, 54,
0, 171, 73, 83, 71, 78,
101, 114, 32, 54, 46, 51,
46, 57, 54, 48, 48, 46,
49, 54, 51, 56, 52, 0,
171, 171, 73, 83, 71, 78,
44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
32, 0, 0, 0, 0, 0,
@ -2941,10 +2933,9 @@ const BYTE LayerQuadMask3DVS[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -3028,10 +3019,10 @@ ret
const BYTE SolidColorShaderMask[] =
{
68, 88, 66, 67, 110, 173,
179, 170, 121, 56, 16, 38,
131, 202, 191, 200, 149, 158,
191, 136, 1, 0, 0, 0,
68, 88, 66, 67, 236, 109,
19, 151, 23, 187, 157, 205,
112, 188, 91, 187, 108, 106,
138, 14, 1, 0, 0, 0,
232, 4, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
204, 0, 0, 0, 112, 1,
@ -3103,7 +3094,7 @@ const BYTE SolidColorShaderMask[] =
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -3207,9 +3198,9 @@ const BYTE SolidColorShaderMask[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
8, 0, 0, 0, 80, 0,
@ -3241,10 +3232,9 @@ const BYTE SolidColorShaderMask[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -3339,10 +3329,10 @@ ret
const BYTE RGBShaderMask[] =
{
68, 88, 66, 67, 90, 156,
108, 215, 2, 184, 95, 225,
139, 102, 23, 57, 234, 197,
48, 52, 1, 0, 0, 0,
68, 88, 66, 67, 30, 30,
87, 58, 114, 156, 251, 151,
29, 94, 34, 100, 228, 250,
37, 251, 1, 0, 0, 0,
192, 5, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
8, 1, 0, 0, 32, 2,
@ -3443,7 +3433,7 @@ const BYTE RGBShaderMask[] =
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -3554,9 +3544,9 @@ const BYTE RGBShaderMask[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
8, 0, 0, 0, 80, 0,
@ -3588,10 +3578,9 @@ const BYTE RGBShaderMask[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -3684,10 +3673,10 @@ ret
const BYTE RGBAShaderMask[] =
{
68, 88, 66, 67, 106, 15,
51, 47, 230, 18, 55, 40,
97, 21, 143, 67, 32, 99,
176, 32, 1, 0, 0, 0,
68, 88, 66, 67, 188, 13,
191, 168, 231, 201, 42, 209,
88, 243, 29, 35, 226, 31,
145, 20, 1, 0, 0, 0,
156, 5, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
252, 0, 0, 0, 252, 1,
@ -3782,7 +3771,7 @@ const BYTE RGBAShaderMask[] =
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -3893,9 +3882,9 @@ const BYTE RGBAShaderMask[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
8, 0, 0, 0, 80, 0,
@ -3927,10 +3916,9 @@ const BYTE RGBAShaderMask[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -4028,10 +4016,10 @@ ret
const BYTE RGBAShaderMask3D[] =
{
68, 88, 66, 67, 176, 186,
72, 64, 199, 228, 205, 219,
97, 152, 199, 132, 157, 124,
226, 212, 1, 0, 0, 0,
68, 88, 66, 67, 113, 141,
78, 23, 128, 223, 235, 10,
0, 97, 49, 111, 47, 53,
229, 55, 1, 0, 0, 0,
24, 6, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
24, 1, 0, 0, 64, 2,
@ -4138,7 +4126,7 @@ const BYTE RGBAShaderMask3D[] =
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -4258,9 +4246,9 @@ const BYTE RGBAShaderMask3D[] =
76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
57, 46, 51, 48, 46, 57,
50, 48, 48, 46, 50, 48,
53, 52, 54, 0, 73, 83,
54, 46, 51, 46, 57, 54,
48, 48, 46, 49, 54, 51,
56, 52, 0, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,
@ -4291,10 +4279,9 @@ const BYTE RGBAShaderMask3D[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -4421,10 +4408,10 @@ ret
const BYTE YCbCrShaderMask[] =
{
68, 88, 66, 67, 115, 10,
33, 43, 108, 217, 72, 92,
140, 65, 97, 68, 194, 221,
95, 25, 1, 0, 0, 0,
68, 88, 66, 67, 103, 162,
223, 236, 236, 142, 143, 151,
73, 154, 187, 112, 81, 114,
229, 251, 1, 0, 0, 0,
168, 8, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
24, 2, 0, 0, 196, 4,
@ -4628,7 +4615,7 @@ const BYTE YCbCrShaderMask[] =
116, 0, 0, 0, 17, 0,
0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 7, 0, 0, 0,
0, 0, 11, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -4638,7 +4625,7 @@ const BYTE YCbCrShaderMask[] =
4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -4760,9 +4747,9 @@ const BYTE YCbCrShaderMask[] =
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 57, 46, 51, 48,
46, 57, 50, 48, 48, 46,
50, 48, 53, 52, 54, 0,
114, 32, 54, 46, 51, 46,
57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 171,
73, 83, 71, 78, 104, 0,
0, 0, 3, 0, 0, 0,
8, 0, 0, 0, 80, 0,
@ -4794,10 +4781,9 @@ const BYTE YCbCrShaderMask[] =
};
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.30.9200.20546
// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.16384
//
//
///
// Buffer Definitions:
//
// cbuffer $Globals
@ -4908,10 +4894,10 @@ ret
const BYTE ComponentAlphaShaderMask[] =
{
68, 88, 66, 67, 66, 175,
106, 103, 136, 76, 200, 80,
95, 179, 74, 140, 138, 144,
12, 21, 1, 0, 0, 0,
68, 88, 66, 67, 245, 71,
211, 223, 156, 101, 223, 204,
145, 138, 53, 12, 16, 220,
106, 83, 1, 0, 0, 0,
20, 7, 0, 0, 6, 0,
0, 0, 56, 0, 0, 0,
124, 1, 0, 0, 52, 3,
@ -5058,7 +5044,7 @@ const BYTE ComponentAlphaShaderMask[] =
0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 3, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
@ -5176,9 +5162,9 @@ const BYTE ComponentAlphaShaderMask[] =
76, 32, 83, 104, 97, 100,
101, 114, 32, 67, 111, 109,
112, 105, 108, 101, 114, 32,
57, 46, 51, 48, 46, 57,
50, 48, 48, 46, 50, 48,
53, 52, 54, 0, 73, 83,
54, 46, 51, 46, 57, 54,
48, 48, 46, 49, 54, 51,
56, 52, 0, 171, 73, 83,
71, 78, 104, 0, 0, 0,
3, 0, 0, 0, 8, 0,
0, 0, 80, 0, 0, 0,

File diff suppressed because it is too large Load Diff

View File

@ -37,13 +37,16 @@ SamplerState Linear
AddressV = Clamp;
};
/*
* Oculus basic distortion, with chroma aberration correction
*/
VS_VR_OUTPUT OculusVRDistortionVS(const VS_VR_INPUT aVertex)
{
VS_VR_OUTPUT res;
float2 tc0 = VREyeToSource.xy * aVertex.vTexCoord0 + VREyeToSource.zw;
float2 tc1 = VREyeToSource.xy * aVertex.vTexCoord1 + VREyeToSource.zw;
float2 tc2 = VREyeToSource.xy * aVertex.vTexCoord2 + VREyeToSource.zw;
float2 tc0 = aVertex.vTexCoord0 * VREyeToSource.zw + VREyeToSource.xy;
float2 tc1 = aVertex.vTexCoord1 * VREyeToSource.zw + VREyeToSource.xy;
float2 tc2 = aVertex.vTexCoord2 * VREyeToSource.zw + VREyeToSource.xy;
//res.vPosition.xy = aVertex.vPosition.xy;
res.vPosition.xy = aVertex.vPosition.xy * VRDestinationScaleAndOffset.zw + VRDestinationScaleAndOffset.xy;

View File

@ -178,6 +178,7 @@ struct CompositorOGLVRObjects {
GLint mUTexture[2];
GLint mUVREyeToSource[2];
GLint mUVRDestionatinScaleAndOffset[2];
GLint mUHeight[2];
};
// If you want to make this class not final, first remove calls to virtual

View File

@ -55,6 +55,7 @@ CompositorOGL::InitializeVR()
vs << "uniform vec4 uVREyeToSource;\n";
vs << "uniform vec4 uVRDestinationScaleAndOffset;\n";
vs << "uniform float uHeight;\n";
vs << "attribute vec2 aPosition;\n";
vs << "attribute vec2 aTexCoord0;\n";
vs << "attribute vec2 aTexCoord1;\n";
@ -66,12 +67,12 @@ CompositorOGL::InitializeVR()
vs << "varying vec4 vGenericAttribs;\n";
vs << "void main() {\n";
vs << " gl_Position = vec4(aPosition.xy * uVRDestinationScaleAndOffset.zw + uVRDestinationScaleAndOffset.xy, 0.5, 1.0);\n";
vs << " vTexCoord0 = uVREyeToSource.xy * aTexCoord0 + uVREyeToSource.zw;\n";
vs << " vTexCoord1 = uVREyeToSource.xy * aTexCoord1 + uVREyeToSource.zw;\n";
vs << " vTexCoord2 = uVREyeToSource.xy * aTexCoord2 + uVREyeToSource.zw;\n";
vs << " vTexCoord0.y = 1.0 - vTexCoord0.y;\n";
vs << " vTexCoord1.y = 1.0 - vTexCoord1.y;\n";
vs << " vTexCoord2.y = 1.0 - vTexCoord2.y;\n";
vs << " vTexCoord0 = uVREyeToSource.zw * aTexCoord0 + uVREyeToSource.xy;\n";
vs << " vTexCoord1 = uVREyeToSource.zw * aTexCoord1 + uVREyeToSource.xy;\n";
vs << " vTexCoord2 = uVREyeToSource.zw * aTexCoord2 + uVREyeToSource.xy;\n";
vs << " vTexCoord0.y = uHeight - vTexCoord0.y;\n";
vs << " vTexCoord1.y = uHeight - vTexCoord1.y;\n";
vs << " vTexCoord2.y = uHeight - vTexCoord2.y;\n";
vs << " vGenericAttribs = aGenericAttribs;\n";
vs << "}\n";
@ -97,7 +98,6 @@ CompositorOGL::InitializeVR()
}
fs << "uniform " << sampler2D << " uTexture;\n";
fs << "varying vec2 vTexCoord0;\n";
fs << "varying vec2 vTexCoord1;\n";
fs << "varying vec2 vTexCoord2;\n";
@ -179,6 +179,7 @@ CompositorOGL::InitializeVR()
mVR.mUTexture[programIndex] = gl()->fGetUniformLocation(prog, "uTexture");
mVR.mUVREyeToSource[programIndex] = gl()->fGetUniformLocation(prog, "uVREyeToSource");
mVR.mUVRDestionatinScaleAndOffset[programIndex] = gl()->fGetUniformLocation(prog, "uVRDestinationScaleAndOffset");
mVR.mUHeight[programIndex] = gl()->fGetUniformLocation(prog, "uHeight");
mVR.mDistortionProgram[programIndex] = prog;
@ -267,6 +268,10 @@ CompositorOGL::DrawVRDistortion(const gfx::Rect& aRect,
gl()->fClearColor(0.0, 0.0, 0.0, 1.0);
gl()->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
// Make sure that the cached current program is reset for the
// rest of the compositor, since we're using a custom program here
ResetProgram();
gl()->fUseProgram(mVR.mDistortionProgram[programIndex]);
gl()->fEnableVertexAttribArray(mVR.mAPosition);
@ -293,21 +298,32 @@ CompositorOGL::DrawVRDistortion(const gfx::Rect& aRect,
vpSize, aRect,
shaderConstants);
float height = 1.0f;
float texScaleAndOffset[4] = { shaderConstants.eyeToSourceScaleAndOffset[0],
shaderConstants.eyeToSourceScaleAndOffset[1],
shaderConstants.eyeToSourceScaleAndOffset[2],
shaderConstants.eyeToSourceScaleAndOffset[3] };
if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
texScaleAndOffset[0] *= size.width;
texScaleAndOffset[1] *= size.height;
texScaleAndOffset[2] *= size.width;
texScaleAndOffset[3] *= size.height;
height = size.height;
}
gl()->fUniform4fv(mVR.mUVRDestionatinScaleAndOffset[programIndex], 1, shaderConstants.destinationScaleAndOffset);
gl()->fUniform4fv(mVR.mUVREyeToSource[programIndex], 1, shaderConstants.eyeToSourceScaleAndOffset);
gl()->fUniform4fv(mVR.mUVREyeToSource[programIndex], 1, texScaleAndOffset);
gl()->fUniform1f(mVR.mUHeight[programIndex], height);
gl()->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mVR.mDistortionVertices[eye]);
gl()->fVertexAttribPointer(mVR.mAPosition, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex),
(void*) offsetof(gfx::VRDistortionVertex, pos));
gl()->fVertexAttribPointer(mVR.mATexCoord0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex),
(void*) offsetof(gfx::VRDistortionVertex, texR));
gl()->fVertexAttribPointer(mVR.mATexCoord1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex),
(void*) offsetof(gfx::VRDistortionVertex, texG));
gl()->fVertexAttribPointer(mVR.mATexCoord2, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex),
(void*) offsetof(gfx::VRDistortionVertex, texB));
gl()->fVertexAttribPointer(mVR.mAGenericAttribs, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex),
(void*) offsetof(gfx::VRDistortionVertex, genericAttribs));
/* This is for Oculus DistortionVertex */
gl()->fVertexAttribPointer(mVR.mAPosition, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex), (void*) (sizeof(float) * 0));
gl()->fVertexAttribPointer(mVR.mATexCoord0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex), (void*) (sizeof(float) * 2));
gl()->fVertexAttribPointer(mVR.mATexCoord1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex), (void*) (sizeof(float) * 4));
gl()->fVertexAttribPointer(mVR.mATexCoord2, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex), (void*) (sizeof(float) * 6));
gl()->fVertexAttribPointer(mVR.mAGenericAttribs, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, sizeof(gfx::VRDistortionVertex), (void*) (sizeof(float) * 8));
gl()->fBindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, mVR.mDistortionIndices[eye]);
gl()->fDrawElements(LOCAL_GL_TRIANGLES, mVR.mDistortionIndexCount[eye], LOCAL_GL_UNSIGNED_SHORT, 0);

View File

@ -23,6 +23,7 @@ DIRS += [
'ots/src',
'thebes',
'ipc',
'vr',
]
if CONFIG['MOZ_ENABLE_SKIA']:

View File

@ -403,9 +403,8 @@ gfxPlatform::gfxPlatform()
contentMask, BackendType::CAIRO);
mTotalSystemMemory = mozilla::hal::GetTotalSystemMemory();
// give ovr_Initialize a chance to be called very early on; we don't
// care if it succeeds or not
VRHMDManagerOculus::PlatformInit();
// give HMDs a chance to be initialized very early on
VRHMDManager::ManagerInit();
}
gfxPlatform*
@ -700,7 +699,7 @@ gfxPlatform::~gfxPlatform()
mScreenReferenceDrawTarget = nullptr;
// Clean up any VR stuff
VRHMDManagerOculus::Destroy();
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

View File

@ -929,8 +929,15 @@ gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
}
if (aMetrics) {
*aMetrics = MeasureText(aStart, charsFit - trimmableChars,
*aMetrics = MeasureText(aStart, charsFit,
aBoundingBoxType, aRefContext, aProvider);
if (trimmableChars) {
Metrics trimMetrics =
MeasureText(aStart + charsFit - trimmableChars,
trimmableChars, aBoundingBoxType,
aRefContext, aProvider);
aMetrics->mAdvanceWidth -= trimMetrics.mAdvanceWidth;
}
}
if (aTrimWhitespace) {
*aTrimWhitespace = trimmableAdvance;

View File

@ -1875,7 +1875,6 @@ gfxWindowsPlatform::InitD3D11Devices()
MOZ_ASSERT(!mD3D11Device);
bool useWARP = false;
ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
if (gfxInfo) {
@ -1962,6 +1961,7 @@ gfxWindowsPlatform::InitD3D11Devices()
MOZ_ASSERT(!mD3D11Device);
MOZ_ASSERT(!adapter);
ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
hr = d3d11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr,
// Use
// D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS

View File

@ -49,7 +49,6 @@ EXPORTS += [
'gfxTypes.h',
'gfxUserFontSet.h',
'gfxUtils.h',
'gfxVR.h',
'GraphicsFilter.h',
'RoundedRect.h',
'SoftwareVsyncSource.h',
@ -244,7 +243,6 @@ UNIFIED_SOURCES += [
'gfxTextRun.cpp',
'gfxUserFontSet.cpp',
'gfxUtils.cpp',
'gfxVR.cpp',
'nsUnicodeRange.cpp',
'SoftwareVsyncSource.cpp',
'VsyncSource.cpp',

View File

@ -1,228 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
/* This file contains just the needed struct definitions for
* interacting with the Oculus VR C API, without needing to #include
* OVR_CAPI.h directly. Note that it uses the same type names as the
* CAPI, and cannot be #included at the same time as OVR_CAPI.h. It
* does not include the entire C API, just want's needed.
*/
#ifdef OVR_CAPI_h
#warning OVR_CAPI.h included before ovr_capi_dynamic.h, skpping this
#define mozilla_ovr_capi_dynamic_h_
#endif
#ifndef mozilla_ovr_capi_dynamic_h_
#define mozilla_ovr_capi_dynamic_h_
#define OVR_CAPI_LIMITED_MOZILLA 1
#ifdef __cplusplus
extern "C" {
#endif
typedef char ovrBool;
typedef struct { int x, y; } ovrVector2i;
typedef struct { int w, h; } ovrSizei;
typedef struct { ovrVector2i Pos; ovrSizei Size; } ovrRecti;
typedef struct { float x, y, z, w; } ovrQuatf;
typedef struct { float x, y; } ovrVector2f;
typedef struct { float x, y, z; } ovrVector3f;
typedef struct { float M[4][4]; } ovrMatrix4f;
typedef struct {
ovrQuatf Orientation;
ovrVector3f Position;
} ovrPosef;
typedef struct {
ovrPosef ThePose;
ovrVector3f AngularVelocity;
ovrVector3f LinearVelocity;
ovrVector3f AngularAcceleration;
ovrVector3f LinearAcceleration;
double TimeInSeconds;
} ovrPoseStatef;
typedef struct {
float UpTan;
float DownTan;
float LeftTan;
float RightTan;
} ovrFovPort;
typedef enum {
ovrHmd_None = 0,
ovrHmd_DK1 = 3,
ovrHmd_DKHD = 4,
ovrHmd_DK2 = 6,
ovrHmd_Other
} ovrHmdType;
typedef enum {
ovrHmdCap_Present = 0x0001,
ovrHmdCap_Available = 0x0002,
ovrHmdCap_Captured = 0x0004,
ovrHmdCap_ExtendDesktop = 0x0008,
ovrHmdCap_DisplayOff = 0x0040,
ovrHmdCap_LowPersistence = 0x0080,
ovrHmdCap_DynamicPrediction = 0x0200,
ovrHmdCap_NoVSync = 0x1000,
ovrHmdCap_NoMirrorToWindow = 0x2000
} ovrHmdCapBits;
typedef enum
{
ovrTrackingCap_Orientation = 0x0010,
ovrTrackingCap_MagYawCorrection = 0x0020,
ovrTrackingCap_Position = 0x0040,
ovrTrackingCap_Idle = 0x0100
} ovrTrackingCaps;
typedef enum {
ovrDistortionCap_Chromatic = 0x01,
ovrDistortionCap_TimeWarp = 0x02,
ovrDistortionCap_Vignette = 0x08,
ovrDistortionCap_NoRestore = 0x10,
ovrDistortionCap_FlipInput = 0x20,
ovrDistortionCap_SRGB = 0x40,
ovrDistortionCap_Overdrive = 0x80,
ovrDistortionCap_ProfileNoTimewarpSpinWaits = 0x10000
} ovrDistortionCaps;
typedef enum {
ovrEye_Left = 0,
ovrEye_Right = 1,
ovrEye_Count = 2
} ovrEyeType;
typedef struct ovrHmdDesc_ {
void* Handle;
ovrHmdType Type;
const char* ProductName;
const char* Manufacturer;
short VendorId;
short ProductId;
char SerialNumber[24];
short FirmwareMajor;
short FirmwareMinor;
float CameraFrustumHFovInRadians;
float CameraFrustumVFovInRadians;
float CameraFrustumNearZInMeters;
float CameraFrustumFarZInMeters;
unsigned int HmdCaps;
unsigned int TrackingCaps;
unsigned int DistortionCaps;
ovrFovPort DefaultEyeFov[ovrEye_Count];
ovrFovPort MaxEyeFov[ovrEye_Count];
ovrEyeType EyeRenderOrder[ovrEye_Count];
ovrSizei Resolution;
ovrVector2i WindowsPos;
const char* DisplayDeviceName;
int DisplayId;
} ovrHmdDesc;
typedef const ovrHmdDesc* ovrHmd;
typedef enum {
ovrStatus_OrientationTracked = 0x0001,
ovrStatus_PositionTracked = 0x0002,
ovrStatus_CameraPoseTracked = 0x0004,
ovrStatus_PositionConnected = 0x0020,
ovrStatus_HmdConnected = 0x0080
} ovrStatusBits;
typedef struct ovrSensorData_ {
ovrVector3f Accelerometer;
ovrVector3f Gyro;
ovrVector3f Magnetometer;
float Temperature;
float TimeInSeconds;
} ovrSensorData;
typedef struct ovrTrackingState_ {
ovrPoseStatef HeadPose;
ovrPosef CameraPose;
ovrPosef LeveledCameraPose;
ovrSensorData RawSensorData;
unsigned int StatusFlags;
double LastVisionProcessingTime;
double LastVisionFrameLatency;
uint32_t LastCameraFrameCounter;
} ovrTrackingState;
typedef struct ovrFrameTiming_ {
float DeltaSeconds;
double ThisFrameSeconds;
double TimewarpPointSeconds;
double NextFrameSeconds;
double ScanoutMidpointSeconds;
double EyeScanoutSeconds[2];
} ovrFrameTiming;
typedef struct ovrEyeRenderDesc_ {
ovrEyeType Eye;
ovrFovPort Fov;
ovrRecti DistortedViewport;
ovrVector2f PixelsPerTanAngleAtCenter;
ovrVector3f ViewAdjust;
} ovrEyeRenderDesc;
typedef struct ovrDistortionVertex_ {
ovrVector2f ScreenPosNDC;
float TimeWarpFactor;
float VignetteFactor;
ovrVector2f TanEyeAnglesR;
ovrVector2f TanEyeAnglesG;
ovrVector2f TanEyeAnglesB;
} ovrDistortionVertex;
typedef struct ovrDistortionMesh_ {
ovrDistortionVertex* pVertexData;
unsigned short* pIndexData;
unsigned int VertexCount;
unsigned int IndexCount;
} ovrDistortionMesh;
typedef ovrBool (*pfn_ovr_Initialize)();
typedef void (*pfn_ovr_Shutdown)();
typedef int (*pfn_ovrHmd_Detect)();
typedef ovrHmd (*pfn_ovrHmd_Create)(int index);
typedef void (*pfn_ovrHmd_Destroy)(ovrHmd hmd);
typedef ovrHmd (*pfn_ovrHmd_CreateDebug)(ovrHmdType type);
typedef const char* (*pfn_ovrHmd_GetLastError)(ovrHmd hmd);
typedef ovrBool (*pfn_ovrHmd_AttachToWindow)(ovrHmd hmd, void* window, const ovrRecti* destMirrorRect, const ovrRecti* sourceRenderTargetRect);
typedef unsigned int (*pfn_ovrHmd_GetEnabledCaps)(ovrHmd hmd);
typedef void (*pfn_ovrHmd_SetEnabledCaps)(ovrHmd hmd, unsigned int hmdCaps);
typedef ovrBool (*pfn_ovrHmd_ConfigureTracking)(ovrHmd hmd, unsigned int supportedTrackingCaps, unsigned int requiredTrackingCaps);
typedef void (*pfn_ovrHmd_RecenterPose)(ovrHmd hmd);
typedef ovrTrackingState (*pfn_ovrHmd_GetTrackingState)(ovrHmd hmd, double absTime);
typedef ovrSizei (*pfn_ovrHmd_GetFovTextureSize)(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel);
typedef ovrEyeRenderDesc (*pfn_ovrHmd_GetRenderDesc)(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov);
typedef ovrBool (*pfn_ovrHmd_CreateDistortionMesh)(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov, unsigned int distortionCaps, ovrDistortionMesh *meshData);
typedef void (*pfn_ovrHmd_DestroyDistortionMesh)(ovrDistortionMesh* meshData);
typedef void (*pfn_ovrHmd_GetRenderScaleAndOffset)(ovrFovPort fov, ovrSizei textureSize, ovrRecti renderViewport, ovrVector2f uvScaleOffsetOut[2]);
typedef ovrFrameTiming (*pfn_ovrHmd_GetFrameTiming)(ovrHmd hmd, unsigned int frameIndex);
typedef ovrFrameTiming (*pfn_ovrHmd_BeginFrameTiming)(ovrHmd hmd, unsigned int frameIndex);
typedef void (*pfn_ovrHmd_EndFrameTiming)(ovrHmd hmd);
typedef void (*pfn_ovrHmd_ResetFrameTiming)(ovrHmd hmd, unsigned int frameIndex, bool vsync);
typedef void (*pfn_ovrHmd_GetEyePoses)(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2], ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState);
typedef ovrPosef (*pfn_ovrHmd_GetHmdPosePerEye)(ovrHmd hmd, ovrEyeType eye);
typedef void (*pfn_ovrHmd_GetEyeTimewarpMatrices)(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]);
typedef ovrMatrix4f (*pfn_ovrMatrix4f_Projection) (ovrFovPort fov, float znear, float zfar, ovrBool rightHanded );
typedef ovrMatrix4f (*pfn_ovrMatrix4f_OrthoSubProjection) (ovrFovPort fov, ovrVector2f orthoScale, float orthoDistance, float eyeViewAdjustX);
typedef double (*pfn_ovr_GetTimeInSeconds)();
#ifdef __cplusplus
}
#endif
#endif /* mozilla_ovr_capi_dynamic_h_ */

118
gfx/vr/gfxVR.cpp Normal file
View File

@ -0,0 +1,118 @@
/* -*- 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 "nsString.h"
#include "gfxPrefs.h"
#include "gfxVR.h"
#include "gfxVROculus.h"
#include "gfxVRCardboard.h"
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
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);
}
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)
VRHMDManager::VRHMDManagerArray *VRHMDManager::sManagers = nullptr;
/* static */ void
VRHMDManager::ManagerInit()
{
if (sManagers)
return;
sManagers = new VRHMDManagerArray();
nsRefPtr<VRHMDManager> mgr;
mgr = new VRHMDManagerOculus();
if (mgr->PlatformInit())
sManagers->AppendElement(mgr);
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<nsRefPtr<VRHMDInfo>>& aHMDResult)
{
if (!sManagers)
return;
for (uint32_t i = 0; i < sManagers->Length(); ++i) {
(*sManagers)[i]->GetHMDs(aHMDResult);
}
}

View File

@ -19,6 +19,7 @@ namespace gfx {
enum class VRHMDType : uint16_t {
Oculus,
Cardboard,
NumHMDTypes
};
@ -69,11 +70,7 @@ struct VRDistortionConstants {
};
struct VRDistortionVertex {
float pos[2];
float texR[2];
float texG[2];
float texB[2];
float genericAttribs[4];
float values[12];
};
struct VRDistortionMesh {
@ -201,14 +198,27 @@ protected:
nsCOMPtr<nsIScreen> mScreen;
};
class VRHMDManagerOculusImpl;
class VRHMDManagerOculus {
static VRHMDManagerOculusImpl *mImpl;
class VRHMDManager {
public:
static bool PlatformInit();
static bool Init();
static void Destroy();
static void GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult);
static void ManagerInit();
static void ManagerDestroy();
static void GetAllHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult);
protected:
typedef nsTArray<nsRefPtr<VRHMDManager>> VRHMDManagerArray;
static VRHMDManagerArray *sManagers;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDManager)
virtual bool PlatformInit() = 0;
virtual bool Init() = 0;
virtual void Destroy() = 0;
virtual void GetHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult) = 0;
protected:
VRHMDManager() { }
virtual ~VRHMDManager() { }
};
} // namespace gfx

382
gfx/vr/gfxVRCardboard.cpp Normal file
View File

@ -0,0 +1,382 @@
/* -*- 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/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
#ifndef M_PI
# define M_PI 3.14159265358979323846
#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(ScreenOrientation 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 ||
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;
}
}
HMDInfoCardboard::HMDInfoCardboard()
: VRHMDInfo(VRHMDType::Cardboard)
, mStartCount(0)
, mOrient(eScreenOrientation_PortraitPrimary)
{
MOZ_ASSERT(sizeof(HMDInfoCardboard::DistortionVertex) == sizeof(VRDistortionVertex),
"HMDInfoCardboard::DistortionVertex must match the size of VRDistortionVertex");
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);
SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0);
}
bool
HMDInfoCardboard::StartSensorTracking()
{
LOG("HMDInfoCardboard::StartSensorTracking %d\n", mStartCount);
if (mStartCount == 0) {
// it's never been started before; initialize observers and
// initial state.
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;
}
void
HMDInfoCardboard::Notify(const mozilla::hal::ScreenConfiguration& config)
{
mOrient = config.orientation();
if (mOrient == eScreenOrientation_LandscapePrimary) {
mScreenTransform = Quaternion(0.f, 0.f, M_SQRT1_2, M_SQRT1_2);
} else if (mOrient == eScreenOrientation_LandscapeSecondary) {
mScreenTransform = Quaternion(0.f, 0.f, -M_SQRT1_2, M_SQRT1_2);
} else if (mOrient == eScreenOrientation_PortraitPrimary) {
mScreenTransform = Quaternion();
} else if (mOrient == eScreenOrientation_PortraitSecondary) {
mScreenTransform = Quaternion(0.f, 0.f, 1.f, 0.f);
}
}
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);
}
}
void
HMDInfoCardboard::ZeroSensor()
{
mSensorZeroInverse = mSavedLastSensor;
mSensorZeroInverse.Invert();
}
static Matrix4x4
ConstructProjectionMatrix(const VRFieldOfView& fov, bool rightHanded, double zNear, double zFar)
{
float upTan = tan(fov.upDegrees * M_PI / 180.0);
float downTan = tan(fov.downDegrees * M_PI / 180.0);
float leftTan = tan(fov.leftDegrees * M_PI / 180.0);
float rightTan = tan(fov.rightDegrees * M_PI / 180.0);
float handednessScale = rightHanded ? -1.0 : 1.0;
float pxscale = 2.0f / (leftTan + rightTan);
float pxoffset = (leftTan - rightTan) * pxscale * 0.5;
float pyscale = 2.0f / (upTan + downTan);
float pyoffset = (upTan - downTan) * pyscale * 0.5;
Matrix4x4 mobj;
float *m = &mobj._11;
m[0*4+0] = pxscale;
m[0*4+2] = pxoffset * handednessScale;
m[1*4+1] = pyscale;
m[1*4+2] = -pyoffset * handednessScale;
m[2*4+2] = zFar / (zNear - zFar) * -handednessScale;
m[2*4+3] = (zFar * zNear) / (zNear - zFar);
m[3*4+2] = handednessScale;
return mobj;
}
bool
HMDInfoCardboard::SetFOV(const VRFieldOfView& aFOVLeft,
const 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] = ConstructProjectionMatrix(mEyeFOV[eye], true, zNear, zFar);
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;
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;
destv[1].texR[0] = destv[1].texG[0] = destv[1].texB[0] = 0.5 + txoffs;
destv[1].texR[1] = destv[1].texG[1] = destv[1].texB[1] = 1.0;
destv[1].padding[0] = 1.0; // vignette factor
destv[2].pos[0] = 0.0 + xoffs;
destv[2].pos[1] = 1.0;
destv[2].texR[0] = destv[2].texG[0] = destv[2].texB[0] = 0.5 + txoffs;
destv[2].texR[1] = destv[2].texG[1] = destv[2].texB[1] = 0.0;
destv[2].padding[0] = 1.0; // vignette factor
destv[3].pos[0] = -1.0 + xoffs;
destv[3].pos[1] = 1.0;
destv[3].texR[0] = destv[3].texG[0] = destv[3].texB[0] = 0.0 + txoffs;
destv[3].texR[1] = destv[3].texG[1] = destv[3].texB[1] = 0.0;
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;
mConfiguration.hmdType = mType;
mConfiguration.value = 0;
mConfiguration.fov[0] = aFOVLeft;
mConfiguration.fov[1] = aFOVRight;
return true;
}
void
HMDInfoCardboard::FillDistortionConstants(uint32_t whichEye,
const IntSize& textureSize, const IntRect& eyeViewport,
const Size& destViewport, const Rect& destRect,
VRDistortionConstants& values)
{
// these modify the texture coordinates; texcoord * zw + xy
values.eyeToSourceScaleAndOffset[0] = 0.0;
values.eyeToSourceScaleAndOffset[1] = 0.0;
values.eyeToSourceScaleAndOffset[2] = 1.0;
values.eyeToSourceScaleAndOffset[3] = 1.0;
// Our mesh positions are in the [-1..1] clip space; we give appropriate offset
// and scaling for the right viewport. (In the 0..2 space for sanity)
// this is the destRect in clip space
float x0 = destRect.x / destViewport.width * 2.0 - 1.0;
float x1 = (destRect.x + destRect.width) / destViewport.width * 2.0 - 1.0;
float y0 = destRect.y / destViewport.height * 2.0 - 1.0;
float y1 = (destRect.y + destRect.height) / destViewport.height * 2.0 - 1.0;
// offset
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;
}
void
HMDInfoCardboard::Destroy()
{
}
bool
VRHMDManagerCardboard::PlatformInit()
{
return true;
}
bool
VRHMDManagerCardboard::Init()
{
if (mCardboardInitialized)
return true;
nsRefPtr<HMDInfoCardboard> hmd = new HMDInfoCardboard();
mCardboardHMDs.AppendElement(hmd);
mCardboardInitialized = true;
return true;
}
void
VRHMDManagerCardboard::Destroy()
{
if (!mCardboardInitialized)
return;
for (size_t i = 0; i < mCardboardHMDs.Length(); ++i) {
mCardboardHMDs[i]->Destroy();
}
mCardboardHMDs.Clear();
mCardboardInitialized = false;
}
void
VRHMDManagerCardboard::GetHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
{
Init();
for (size_t i = 0; i < mCardboardHMDs.Length(); ++i) {
aHMDResult.AppendElement(mCardboardHMDs[i]);
}
}

97
gfx/vr/gfxVRCardboard.h Normal file
View File

@ -0,0 +1,97 @@
/* -*- 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_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:
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;
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:
// 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;
virtual bool Init() override;
virtual void Destroy() override;
virtual void GetHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult) override;
protected:
nsTArray<nsRefPtr<impl::HMDInfoCardboard>> mCardboardHMDs;
bool mCardboardInitialized;
};
} // namespace gfx
} // namespace mozilla
#endif /* GFX_VR_CARDBOARD_H */

View File

@ -9,23 +9,21 @@
#include "prmem.h"
#include "prenv.h"
#include "gfxPrefs.h"
#include "gfxVR.h"
#include "nsString.h"
#include "mozilla/Preferences.h"
#include "ovr_capi_dynamic.h"
#include "gfxVROculus.h"
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
#ifdef XP_WIN
#include "gfxWindowsPlatform.h" // for gfxWindowsPlatform::GetDPIScale
#endif
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
using namespace mozilla::gfx;
using namespace mozilla::gfx::impl;
namespace {
#ifdef OVR_CAPI_LIMITED_MOZILLA
@ -58,69 +56,98 @@ static pfn_ovrMatrix4f_Projection ovrMatrix4f_Projection = nullptr;
static pfn_ovrMatrix4f_OrthoSubProjection ovrMatrix4f_OrthoSubProjection = nullptr;
static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
#if defined(XP_WIN)
# ifdef HAVE_64BIT_BUILD
# define OVR_LIB_NAME "libovr64.dll"
# else
# define OVR_LIB_NAME "libovr.dll"
# endif
#elif defined(XP_MACOSX)
# define OVR_LIB_NAME "libovr.dylib"
#ifdef HAVE_64BIT_BUILD
#define BUILD_BITS 64
#else
# define OVR_LIB_NAME 0
#define BUILD_BITS 32
#endif
#define LIBOVR_PRODUCT_VERSION 0
#define LIBOVR_MAJOR_VERSION 5
#define LIBOVR_MINOR_VERSION 0
static bool
InitializeOculusCAPI()
{
static PRLibrary *ovrlib = nullptr;
if (!ovrlib) {
const char *libName = OVR_LIB_NAME;
nsTArray<nsCString> libSearchPaths;
nsCString libName;
nsCString searchPath;
#if defined(_WIN32)
static const char dirSep = '\\';
#else
static const char dirSep = '/';
#endif
#if defined(_WIN32)
static const int pathLen = 260;
searchPath.SetCapacity(pathLen);
int realLen = ::GetSystemDirectoryA(searchPath.BeginWriting(), pathLen);
if (realLen != 0 && realLen < pathLen) {
searchPath.SetLength(realLen);
libSearchPaths.AppendElement(searchPath);
}
libName.AppendPrintf("LibOVRRT%d_%d_%d.dll", BUILD_BITS, LIBOVR_PRODUCT_VERSION, LIBOVR_MAJOR_VERSION);
#elif defined(__APPLE__)
searchPath.Truncate();
searchPath.AppendPrintf("/Library/Frameworks/LibOVRRT_%d.framework/Versions/%d", LIBOVR_PRODUCT_VERSION, LIBOVR_MAJOR_VERSION);
libSearchPaths.AppendElement(searchPath);
if (PR_GetEnv("HOME")) {
searchPath.Truncate();
searchPath.AppendPrintf("%s/Library/Frameworks/LibOVRRT_%d.framework/Versions/%d", PR_GetEnv("HOME"), LIBOVR_PRODUCT_VERSION, LIBOVR_MAJOR_VERSION);
libSearchPaths.AppendElement(searchPath);
}
libName.AppendPrintf("LibOVRRT_%d", LIBOVR_PRODUCT_VERSION);
#else
libSearchPaths.AppendElement(nsCString("/usr/local/lib"));
libSearchPaths.AppendElement(nsCString("/usr/lib"));
libName.AppendPrintf("LibOVRRT%d_%d.so.%d", BUILD_BITS, LIBOVR_PRODUCT_VERSION, LIBOVR_MAJOR_VERSION);
#endif
// If the pref is present, we override libName
nsAdoptingCString prefLibName = mozilla::Preferences::GetCString("dom.vr.ovr_lib_path");
if (prefLibName && prefLibName.get()) {
libName = prefLibName.get();
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()) {
libName.Assign(prefLibName);
}
// search the path/module dir
libSearchPaths.InsertElementsAt(0, 1, nsCString());
// If the env var is present, we override libName
if (PR_GetEnv("OVR_LIB_PATH")) {
searchPath = PR_GetEnv("OVR_LIB_PATH");
libSearchPaths.InsertElementsAt(0, 1, searchPath);
}
if (PR_GetEnv("OVR_LIB_NAME")) {
libName = PR_GetEnv("OVR_LIB_NAME");
}
if (!libName) {
printf_stderr("Don't know how to find Oculus VR library; missing dom.vr.ovr_lib_path or OVR_LIB_NAME\n");
return false;
}
ovrlib = PR_LoadLibrary(libName);
if (!ovrlib) {
// Not found? Try harder. Needed mainly on OSX/etc. where
// the binary location is not in the search path.
const char *xulName = "libxul.so";
#if defined(XP_MACOSX)
xulName = "XUL";
#endif
char *xulpath = PR_GetLibraryFilePathname(xulName, (PRFuncPtr) &InitializeOculusCAPI);
if (xulpath) {
char *xuldir = strrchr(xulpath, '/');
if (xuldir) {
*xuldir = 0;
xuldir = xulpath;
char *ovrpath = PR_GetLibraryName(xuldir, libName);
ovrlib = PR_LoadLibrary(ovrpath);
PR_Free(ovrpath);
}
PR_Free(xulpath);
for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
nsCString& libPath = libSearchPaths[i];
nsCString fullName;
if (libPath.Length() == 0) {
fullName.Assign(libName);
} else {
fullName.AppendPrintf("%s%c%s", libPath.BeginReading(), dirSep, libName.BeginReading());
}
ovrlib = PR_LoadLibrary(fullName.BeginReading());
if (ovrlib)
break;
}
if (!ovrlib) {
printf_stderr("Failed to load Oculus VR library, tried '%s'\n", libName);
printf_stderr("Failed to load Oculus VR library!\n");
return false;
}
}
@ -181,93 +208,7 @@ static bool InitializeOculusCAPI()
}
#endif
} // anonymous namespace
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);
}
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)
class HMDInfoOculus : public VRHMDInfo {
friend class VRHMDManagerOculusImpl;
public:
explicit HMDInfoOculus(ovrHmd aHMD);
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;
void FillDistortionConstants(uint32_t whichEye,
const IntSize& textureSize, const IntRect& eyeViewport,
const Size& destViewport, const Rect& destRect,
VRDistortionConstants& values) override;
void Destroy();
protected:
virtual ~HMDInfoOculus() {
Destroy();
MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
}
ovrHmd mHMD;
ovrFovPort mFOVPort[2];
uint32_t mStartCount;
};
static ovrFovPort
ovrFovPort
ToFovPort(const VRFieldOfView& aFOV)
{
ovrFovPort fovPort;
@ -278,7 +219,7 @@ ToFovPort(const VRFieldOfView& aFOV)
return fovPort;
}
static VRFieldOfView
VRFieldOfView
FromFovPort(const ovrFovPort& aFOV)
{
VRFieldOfView fovInfo;
@ -289,11 +230,16 @@ FromFovPort(const ovrFovPort& aFOV)
return fovInfo;
}
} // anonymous namespace
HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD)
: VRHMDInfo(VRHMDType::Oculus)
, mHMD(aHMD)
, mStartCount(0)
{
MOZ_ASSERT(sizeof(HMDInfoOculus::DistortionVertex) == sizeof(VRDistortionVertex),
"HMDInfoOculus::DistortionVertex must match the size of VRDistortionVertex");
MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
mSupportedSensorBits = 0;
@ -345,7 +291,7 @@ HMDInfoOculus::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRi
// these values are negated so that content can add the adjustment to its camera position,
// instead of subtracting
mEyeTranslation[eye] = Point3D(-renderDesc.ViewAdjust.x, -renderDesc.ViewAdjust.y, -renderDesc.ViewAdjust.z);
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
ovrMatrix4f projMatrix = ovrMatrix4f_Projection(mFOVPort[eye], zNear, zFar, true);
@ -365,7 +311,7 @@ HMDInfoOculus::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRi
mDistortionMesh[eye].mIndices.SetLength(mesh.IndexCount);
ovrDistortionVertex *srcv = mesh.pVertexData;
VRDistortionVertex *destv = mDistortionMesh[eye].mVertices.Elements();
HMDInfoOculus::DistortionVertex *destv = reinterpret_cast<HMDInfoOculus::DistortionVertex*>(mDistortionMesh[eye].mVertices.Elements());
memset(destv, 0, mesh.VertexCount * sizeof(VRDistortionVertex));
for (uint32_t i = 0; i < mesh.VertexCount; ++i) {
destv[i].pos[0] = srcv[i].ScreenPosNDC.x;
@ -414,10 +360,10 @@ HMDInfoOculus::FillDistortionConstants(uint32_t whichEye,
ovrHmd_GetRenderScaleAndOffset(mFOVPort[whichEye], texSize, eyePort, scaleOut);
values.eyeToSourceScaleAndOffset[0] = scaleOut[0].x;
values.eyeToSourceScaleAndOffset[1] = scaleOut[0].y;
values.eyeToSourceScaleAndOffset[2] = scaleOut[1].x;
values.eyeToSourceScaleAndOffset[3] = scaleOut[1].y;
values.eyeToSourceScaleAndOffset[0] = scaleOut[1].x;
values.eyeToSourceScaleAndOffset[1] = scaleOut[1].y;
values.eyeToSourceScaleAndOffset[2] = scaleOut[0].x;
values.eyeToSourceScaleAndOffset[3] = scaleOut[0].y;
// These values are in clip space [-1..1] range, but we're providing
// scaling in the 0..2 space for sanity.
@ -513,71 +459,8 @@ HMDInfoOculus::GetSensorState(double timeOffset)
return result;
}
namespace mozilla {
namespace gfx {
class VRHMDManagerOculusImpl {
public:
VRHMDManagerOculusImpl() : mOculusInitialized(false), mOculusPlatformInitialized(false)
{ }
bool PlatformInit();
bool Init();
void Destroy();
void GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult);
protected:
nsTArray<nsRefPtr<HMDInfoOculus>> mOculusHMDs;
bool mOculusInitialized;
bool mOculusPlatformInitialized;
};
} // namespace gfx
} // namespace mozilla
VRHMDManagerOculusImpl *VRHMDManagerOculus::mImpl = nullptr;
// These just forward to the Impl class, to have a non-static container for various
// objects.
bool
VRHMDManagerOculus::PlatformInit()
{
if (!mImpl) {
mImpl = new VRHMDManagerOculusImpl;
}
return mImpl->PlatformInit();
}
bool
VRHMDManagerOculus::Init()
{
if (!mImpl) {
mImpl = new VRHMDManagerOculusImpl;
}
return mImpl->Init();
}
void
VRHMDManagerOculus::GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
{
if (!mImpl) {
mImpl = new VRHMDManagerOculusImpl;
}
mImpl->GetOculusHMDs(aHMDResult);
}
void
VRHMDManagerOculus::Destroy()
{
if (!mImpl)
return;
mImpl->Destroy();
delete mImpl;
mImpl = nullptr;
}
bool
VRHMDManagerOculusImpl::PlatformInit()
{
if (mOculusPlatformInitialized)
return true;
@ -588,7 +471,13 @@ VRHMDManagerOculusImpl::PlatformInit()
if (!InitializeOculusCAPI())
return false;
bool ok = ovr_Initialize();
ovrInitParams params;
params.Flags = ovrInit_RequestVersion;
params.RequestedMinorVersion = LIBOVR_MINOR_VERSION;
params.LogCallback = nullptr;
params.ConnectionTimeoutMS = 0;
bool ok = ovr_Initialize(&params);
if (!ok)
return false;
@ -598,7 +487,7 @@ VRHMDManagerOculusImpl::PlatformInit()
}
bool
VRHMDManagerOculusImpl::Init()
VRHMDManagerOculus::Init()
{
if (mOculusInitialized)
return true;
@ -610,10 +499,10 @@ VRHMDManagerOculusImpl::Init()
for (int i = 0; i < count; ++i) {
ovrHmd hmd = ovrHmd_Create(i);
if (!hmd)
continue;
nsRefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
mOculusHMDs.AppendElement(oc);
if (hmd) {
nsRefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
mOculusHMDs.AppendElement(oc);
}
}
// VRAddTestDevices == 1: add test device only if no real devices present
@ -633,7 +522,7 @@ VRHMDManagerOculusImpl::Init()
}
void
VRHMDManagerOculusImpl::Destroy()
VRHMDManagerOculus::Destroy()
{
if (!mOculusInitialized)
return;
@ -649,7 +538,7 @@ VRHMDManagerOculusImpl::Destroy()
}
void
VRHMDManagerOculusImpl::GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
VRHMDManagerOculus::GetHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
{
Init();
for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {

85
gfx/vr/gfxVROculus.h Normal file
View File

@ -0,0 +1,85 @@
/* -*- 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_OCULUS_H
#define GFX_VR_OCULUS_H
#include "nsTArray.h"
#include "nsIScreen.h"
#include "nsCOMPtr.h"
#include "nsRefPtr.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/EnumeratedArray.h"
#include "gfxVR.h"
#include "ovr_capi_dynamic.h"
namespace mozilla {
namespace gfx {
namespace impl {
class HMDInfoOculus : public VRHMDInfo {
public:
explicit HMDInfoOculus(ovrHmd aHMD);
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;
void FillDistortionConstants(uint32_t whichEye,
const IntSize& textureSize, const IntRect& eyeViewport,
const Size& destViewport, const Rect& destRect,
VRDistortionConstants& values) override;
void Destroy();
protected:
// 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;
};
} // namespace impl
class VRHMDManagerOculus : public VRHMDManager
{
public:
VRHMDManagerOculus()
: mOculusInitialized(false), mOculusPlatformInitialized(false)
{ }
virtual bool PlatformInit() override;
virtual bool Init() override;
virtual void Destroy() override;
virtual void GetHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult) override;
protected:
nsTArray<nsRefPtr<impl::HMDInfoOculus>> mOculusHMDs;
bool mOculusInitialized;
bool mOculusPlatformInitialized;
};
} // namespace gfx
} // namespace mozilla
#endif /* GFX_VR_OCULUS_H */

33
gfx/vr/moz.build Normal file
View File

@ -0,0 +1,33 @@
# -*- 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',
]
LOCAL_INCLUDES += [
'/gfx/thebes',
]
UNIFIED_SOURCES += [
'gfxVR.cpp',
'gfxVRCardboard.cpp',
'gfxVROculus.cpp',
]
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
CXXFLAGS += CONFIG['TK_CFLAGS']
CFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
CFLAGS += CONFIG['TK_CFLAGS']
FAIL_ON_WARNINGS = not CONFIG['_MSC_VER']
MSVC_ENABLE_PGO = True
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

292
gfx/vr/ovr_capi_dynamic.h Normal file
View File

@ -0,0 +1,292 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
/* This file contains just the needed struct definitions for
* interacting with the Oculus VR C API, without needing to #include
* OVR_CAPI.h directly. Note that it uses the same type names as the
* CAPI, and cannot be #included at the same time as OVR_CAPI.h. It
* does not include the entire C API, just want's needed.
*/
#ifdef OVR_CAPI_h
#warning OVR_CAPI.h included before ovr_capi_dynamic.h, skpping this
#define mozilla_ovr_capi_dynamic_h_
#else
#ifndef mozilla_ovr_capi_dynamic_h_
#define mozilla_ovr_capi_dynamic_h_
#define OVR_CAPI_LIMITED_MOZILLA 1
#if defined(_WIN32)
#define OVR_PFN __cdecl
#else
#define OVR_PFN
#endif
#if !defined(OVR_ALIGNAS)
#if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 408) && (defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L))
#define OVR_ALIGNAS(n) alignas(n)
#elif defined(__clang__) && !defined(__APPLE__) && (((__clang_major__ * 100) + __clang_minor__) >= 300) && (__cplusplus >= 201103L)
#define OVR_ALIGNAS(n) alignas(n)
#elif defined(__clang__) && defined(__APPLE__) && (((__clang_major__ * 100) + __clang_minor__) >= 401) && (__cplusplus >= 201103L)
#define OVR_ALIGNAS(n) alignas(n)
#elif defined(_MSC_VER) && (_MSC_VER >= 1900)
#define OVR_ALIGNAS(n) alignas(n)
#elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 408)
#define OVR_ALIGNAS(n) alignas(n)
#elif defined(__GNUC__) || defined(__clang__)
#define OVR_ALIGNAS(n) __attribute__((aligned(n)))
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define OVR_ALIGNAS(n) __declspec(align(n))
#else
#error Need to define OVR_ALIGNAS
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef char ovrBool;
typedef struct { int x, y; } ovrVector2i;
typedef struct { int w, h; } ovrSizei;
typedef struct { ovrVector2i Pos; ovrSizei Size; } ovrRecti;
typedef struct { float x, y, z, w; } ovrQuatf;
typedef struct { float x, y; } ovrVector2f;
typedef struct { float x, y, z; } ovrVector3f;
typedef struct { float M[4][4]; } ovrMatrix4f;
typedef struct {
ovrQuatf Orientation;
ovrVector3f Position;
} ovrPosef;
typedef struct OVR_ALIGNAS(8) {
ovrPosef ThePose;
ovrVector3f AngularVelocity;
ovrVector3f LinearVelocity;
ovrVector3f AngularAcceleration;
ovrVector3f LinearAcceleration;
float Pad;
double TimeInSeconds;
} ovrPoseStatef;
typedef struct {
float UpTan;
float DownTan;
float LeftTan;
float RightTan;
} ovrFovPort;
typedef enum {
ovrHmd_None = 0,
ovrHmd_DK1 = 3,
ovrHmd_DKHD = 4,
ovrHmd_DK2 = 6,
ovrHmd_BlackStar = 7,
ovrHmd_CB = 8,
ovrHmd_Other = 9,
ovrHmd_EnumSize = 0x7fffffff
} ovrHmdType;
typedef enum {
ovrHmdCap_Present = 0x0001,
ovrHmdCap_Available = 0x0002,
ovrHmdCap_Captured = 0x0004,
ovrHmdCap_ExtendDesktop = 0x0008,
ovrHmdCap_DebugDevice = 0x0010,
ovrHmdCap_DisplayOff = 0x0040,
ovrHmdCap_LowPersistence = 0x0080,
ovrHmdCap_DynamicPrediction = 0x0200,
ovrHmdCap_NoVSync = 0x1000,
ovrHmdCap_NoMirrorToWindow = 0x2000
} ovrHmdCapBits;
typedef enum
{
ovrTrackingCap_Orientation = 0x0010,
ovrTrackingCap_MagYawCorrection = 0x0020,
ovrTrackingCap_Position = 0x0040,
ovrTrackingCap_Idle = 0x0100,
ovrTrackingCap_EnumSize = 0x7fffffff
} ovrTrackingCaps;
typedef enum {
ovrDistortionCap_Chromatic = 0x01,
ovrDistortionCap_TimeWarp = 0x02,
ovrDistortionCap_Vignette = 0x08,
ovrDistortionCap_NoRestore = 0x10,
ovrDistortionCap_FlipInput = 0x20,
ovrDistortionCap_SRGB = 0x40,
ovrDistortionCap_Overdrive = 0x80,
ovrDistortionCap_HqDistortion = 0x100,
ovrDistortionCap_LinuxDevFullscreen = 0x200,
ovrDistortionCap_ComputeShader = 0x400,
ovrDistortionCap_TimewarpJitDelay = 0x1000,
ovrDistortionCap_ProfileNoSpinWaits = 0x10000,
ovrDistortionCap_EnumSize = 0x7fffffff
} ovrDistortionCaps;
typedef enum {
ovrEye_Left = 0,
ovrEye_Right = 1,
ovrEye_Count = 2,
ovrEye_EnumSize = 0x7fffffff
} ovrEyeType;
typedef struct ovrHmdDesc_ {
void* Handle;
ovrHmdType Type;
const char* ProductName;
const char* Manufacturer;
short VendorId;
short ProductId;
char SerialNumber[24];
short FirmwareMajor;
short FirmwareMinor;
float CameraFrustumHFovInRadians;
float CameraFrustumVFovInRadians;
float CameraFrustumNearZInMeters;
float CameraFrustumFarZInMeters;
unsigned int HmdCaps;
unsigned int TrackingCaps;
unsigned int DistortionCaps;
ovrFovPort DefaultEyeFov[ovrEye_Count];
ovrFovPort MaxEyeFov[ovrEye_Count];
ovrEyeType EyeRenderOrder[ovrEye_Count];
ovrSizei Resolution;
ovrVector2i WindowsPos;
const char* DisplayDeviceName;
int DisplayId;
} ovrHmdDesc;
typedef const ovrHmdDesc* ovrHmd;
typedef enum {
ovrStatus_OrientationTracked = 0x0001,
ovrStatus_PositionTracked = 0x0002,
ovrStatus_CameraPoseTracked = 0x0004,
ovrStatus_PositionConnected = 0x0020,
ovrStatus_HmdConnected = 0x0080,
ovrStatus_EnumSize = 0x7fffffff
} ovrStatusBits;
typedef struct ovrSensorData_ {
ovrVector3f Accelerometer;
ovrVector3f Gyro;
ovrVector3f Magnetometer;
float Temperature;
float TimeInSeconds;
} ovrSensorData;
typedef struct ovrTrackingState_ {
ovrPoseStatef HeadPose;
ovrPosef CameraPose;
ovrPosef LeveledCameraPose;
ovrSensorData RawSensorData;
unsigned int StatusFlags;
double LastVisionProcessingTime;
uint32_t LastCameraFrameCounter;
uint32_t Pad;
} ovrTrackingState;
typedef struct OVR_ALIGNAS(8) ovrFrameTiming_ {
float DeltaSeconds;
float Pad;
double ThisFrameSeconds;
double TimewarpPointSeconds;
double NextFrameSeconds;
double ScanoutMidpointSeconds;
double EyeScanoutSeconds[2];
} ovrFrameTiming;
typedef struct ovrEyeRenderDesc_ {
ovrEyeType Eye;
ovrFovPort Fov;
ovrRecti DistortedViewport;
ovrVector2f PixelsPerTanAngleAtCenter;
ovrVector3f HmdToEyeViewOffset;
} ovrEyeRenderDesc;
typedef struct ovrDistortionVertex_ {
ovrVector2f ScreenPosNDC;
float TimeWarpFactor;
float VignetteFactor;
ovrVector2f TanEyeAnglesR;
ovrVector2f TanEyeAnglesG;
ovrVector2f TanEyeAnglesB;
} ovrDistortionVertex;
typedef struct ovrDistortionMesh_ {
ovrDistortionVertex* pVertexData;
unsigned short* pIndexData;
unsigned int VertexCount;
unsigned int IndexCount;
} ovrDistortionMesh;
typedef enum {
ovrInit_Debug = 0x00000001,
ovrInit_ServerOptional = 0x00000002,
ovrInit_RequestVersion = 0x00000004,
ovrInit_ForceNoDebug = 0x00000008
} ovrInitFlags;
typedef enum {
ovrLogLevel_Debug = 0,
ovrLogLevel_Info = 1,
ovrLogLevel_Error = 2
} ovrLogLevel;
typedef void (OVR_PFN *ovrLogCallback)(int level, const char* message);
typedef struct {
uint32_t Flags;
uint32_t RequestedMinorVersion;
ovrLogCallback LogCallback;
uint32_t ConnectionTimeoutMS;
} ovrInitParams;
typedef ovrBool (OVR_PFN *pfn_ovr_Initialize)(ovrInitParams const* params);
typedef void (OVR_PFN *pfn_ovr_Shutdown)();
typedef int (OVR_PFN *pfn_ovrHmd_Detect)();
typedef ovrHmd (OVR_PFN *pfn_ovrHmd_Create)(int index);
typedef void (OVR_PFN *pfn_ovrHmd_Destroy)(ovrHmd hmd);
typedef ovrHmd (OVR_PFN *pfn_ovrHmd_CreateDebug)(ovrHmdType type);
typedef const char* (OVR_PFN *pfn_ovrHmd_GetLastError)(ovrHmd hmd);
typedef ovrBool (OVR_PFN *pfn_ovrHmd_AttachToWindow)(ovrHmd hmd, void* window, const ovrRecti* destMirrorRect, const ovrRecti* sourceRenderTargetRect);
typedef unsigned int (OVR_PFN *pfn_ovrHmd_GetEnabledCaps)(ovrHmd hmd);
typedef void (OVR_PFN *pfn_ovrHmd_SetEnabledCaps)(ovrHmd hmd, unsigned int hmdCaps);
typedef ovrBool (OVR_PFN *pfn_ovrHmd_ConfigureTracking)(ovrHmd hmd, unsigned int supportedTrackingCaps, unsigned int requiredTrackingCaps);
typedef void (OVR_PFN *pfn_ovrHmd_RecenterPose)(ovrHmd hmd);
typedef ovrTrackingState (OVR_PFN *pfn_ovrHmd_GetTrackingState)(ovrHmd hmd, double absTime);
typedef ovrSizei (OVR_PFN *pfn_ovrHmd_GetFovTextureSize)(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel);
typedef ovrEyeRenderDesc (OVR_PFN *pfn_ovrHmd_GetRenderDesc)(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov);
typedef ovrBool (OVR_PFN *pfn_ovrHmd_CreateDistortionMesh)(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov, unsigned int distortionCaps, ovrDistortionMesh *meshData);
typedef void (OVR_PFN *pfn_ovrHmd_DestroyDistortionMesh)(ovrDistortionMesh* meshData);
typedef void (OVR_PFN *pfn_ovrHmd_GetRenderScaleAndOffset)(ovrFovPort fov, ovrSizei textureSize, ovrRecti renderViewport, ovrVector2f uvScaleOffsetOut[2]);
typedef ovrFrameTiming (OVR_PFN *pfn_ovrHmd_GetFrameTiming)(ovrHmd hmd, unsigned int frameIndex);
typedef ovrFrameTiming (OVR_PFN *pfn_ovrHmd_BeginFrameTiming)(ovrHmd hmd, unsigned int frameIndex);
typedef void (OVR_PFN *pfn_ovrHmd_EndFrameTiming)(ovrHmd hmd);
typedef void (OVR_PFN *pfn_ovrHmd_ResetFrameTiming)(ovrHmd hmd, unsigned int frameIndex, bool vsync);
typedef void (OVR_PFN *pfn_ovrHmd_GetEyePoses)(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2], ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState);
typedef ovrPosef (OVR_PFN *pfn_ovrHmd_GetHmdPosePerEye)(ovrHmd hmd, ovrEyeType eye);
typedef void (OVR_PFN *pfn_ovrHmd_GetEyeTimewarpMatrices)(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]);
typedef ovrMatrix4f (OVR_PFN *pfn_ovrMatrix4f_Projection) (ovrFovPort fov, float znear, float zfar, ovrBool rightHanded );
typedef ovrMatrix4f (OVR_PFN *pfn_ovrMatrix4f_OrthoSubProjection) (ovrFovPort fov, ovrVector2f orthoScale, float orthoDistance, float eyeViewAdjustX);
typedef double (OVR_PFN *pfn_ovr_GetTimeInSeconds)();
#ifdef __cplusplus
}
#endif
#endif /* mozilla_ovr_capi_dynamic_h_ */
#endif /* OVR_CAPI_h */

View File

@ -15,15 +15,18 @@ namespace hal {
/**
* Enumeration of sensor types. They are used to specify type while
* register or unregister an observer for a sensor of given type.
* If you add or change any here, do the same in GeckoHalDefines.java.
*/
enum SensorType {
SENSOR_UNKNOWN = -1,
SENSOR_ORIENTATION,
SENSOR_ACCELERATION,
SENSOR_PROXIMITY,
SENSOR_LINEAR_ACCELERATION,
SENSOR_GYROSCOPE,
SENSOR_LIGHT,
SENSOR_ORIENTATION = 0,
SENSOR_ACCELERATION = 1,
SENSOR_PROXIMITY = 2,
SENSOR_LINEAR_ACCELERATION = 3,
SENSOR_GYROSCOPE = 4,
SENSOR_LIGHT = 5,
SENSOR_ROTATION_VECTOR = 6,
SENSOR_GAME_ROTATION_VECTOR = 7,
NUM_SENSOR_TYPE
};

View File

@ -38,6 +38,10 @@ namespace mozilla {
// different orientation angles
#define ACCELEROMETER_POLL_RATE 66667000 /*66.667ms*/
// This is present in Android from API level 18 onwards, which is 4.3. We might
// be building on something before 4.3, so use a local define for its value
#define MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR 15
double radToDeg(double a) {
return a * (180.0 / M_PI);
}
@ -58,6 +62,10 @@ HardwareSensorToHalSensor(int type)
return SENSOR_GYROSCOPE;
case SENSOR_TYPE_LINEAR_ACCELERATION:
return SENSOR_LINEAR_ACCELERATION;
case SENSOR_TYPE_ROTATION_VECTOR:
return SENSOR_ROTATION_VECTOR;
case MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR:
return SENSOR_GAME_ROTATION_VECTOR;
default:
return SENSOR_UNKNOWN;
}
@ -84,6 +92,10 @@ HalSensorToHardwareSensor(SensorType type)
return SENSOR_TYPE_GYROSCOPE;
case SENSOR_LINEAR_ACCELERATION:
return SENSOR_TYPE_LINEAR_ACCELERATION;
case SENSOR_ROTATION_VECTOR:
return SENSOR_TYPE_ROTATION_VECTOR;
case SENSOR_GAME_ROTATION_VECTOR:
return MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR;
default:
return -1;
}
@ -131,6 +143,28 @@ public:
}
} else if (mSensorData.sensor() == SENSOR_LIGHT) {
mSensorValues.AppendElement(data.data[0]);
} else if (mSensorData.sensor() == SENSOR_ROTATION_VECTOR) {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(data.data[1]);
mSensorValues.AppendElement(data.data[2]);
if (data.data[3] == 0.0) {
// data.data[3] was optional in Android <= API level 18. It can be computed from 012,
// but it's better to take the actual value if one is provided. The computation is
// v = 1 - d[0]*d[0] - d[1]*d[1] - d[2]*d[2]
// d[3] = v > 0 ? sqrt(v) : 0;
// I'm assuming that it will be 0 if it's not passed in. (The values form a unit
// quaternion, so the angle can be computed from the direction vector.)
float sx = data.data[0], sy = data.data[1], sz = data.data[2];
float v = 1.0f - sx*sx - sy*sy - sz*sz;
mSensorValues.AppendElement(v > 0.0f ? sqrt(v) : 0.0f);
} else {
mSensorValues.AppendElement(data.data[3]);
}
} else if (mSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(data.data[1]);
mSensorValues.AppendElement(data.data[2]);
mSensorValues.AppendElement(data.data[3]);
} else {
mSensorValues.AppendElement(data.data[0]);
mSensorValues.AppendElement(data.data[1]);
@ -149,7 +183,7 @@ public:
private:
SensorData mSensorData;
InfallibleTArray<float> mSensorValues;
nsAutoTArray<float, 4> mSensorValues;
};
namespace hal_impl {

View File

@ -6,22 +6,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsfriendapi.h"
#include "xpcprivate.h"
#include "nsContentUtils.h"
#include "CPOWTimer.h"
CPOWTimer::~CPOWTimer()
{
/* This is a best effort to find the compartment responsible for this CPOW call */
nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal();
if (!global)
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
if (!cx)
return;
JSObject* obj = global->GetGlobalJSObject();
if (!obj)
JSRuntime* runtime = JS_GetRuntime(cx);
if (!js::IsStopwatchActive(runtime))
return;
JSCompartment* compartment = js::GetObjectCompartment(obj);
xpc::CompartmentPrivate* compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
if (!compartmentPrivate)
return;
PRIntervalTime time = PR_IntervalNow() - startInterval;
compartmentPrivate->CPOWTime += time;
js::PerformanceData *performance = js::GetPerformanceData(runtime);
uint64_t duration = PR_IntervalToMicroseconds(PR_IntervalNow() - startInterval);
performance->totalCPOWTime += duration;
}

View File

@ -12,12 +12,30 @@
class JSObject;
class MOZ_STACK_CLASS CPOWTimer {
/**
* A stopwatch measuring the duration of a CPOW call.
*
* As the process is consuming neither user time nor system time
* during a CPOW call, we measure such durations using wallclock time.
*
* This stopwatch is active iff JSRuntime::stopwatch.isActive is set.
* Upon destruction, update JSRuntime::stopwatch.data.totalCPOWTime.
*/
class MOZ_STACK_CLASS CPOWTimer final {
public:
CPOWTimer(): startInterval(PR_IntervalNow()) {}
explicit inline CPOWTimer(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
: startInterval(PR_IntervalNow())
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~CPOWTimer();
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
/**
* The instant at which the stopwatch was started.
*/
PRIntervalTime startInterval;
};

View File

@ -35,6 +35,7 @@ LOCAL_INCLUDES += [
'/dom/base',
'/js/ipc',
'/js/public',
'/js/src',
'/js/xpconnect/src',
'/js/xpconnect/wrappers',
]

View File

@ -15,6 +15,10 @@
#include "jspubtd.h"
namespace js {
struct PerformanceGroup;
}
struct JSPrincipals {
/* Don't call "destroy"; use reference counting macros below. */
mozilla::Atomic<int32_t> refcount;

View File

@ -2603,100 +2603,87 @@ BytecodeEmitter::enterBlockScope(StmtInfoBCE* stmtInfo, ObjectBox* objbox, JSOp
MOZ_NEVER_INLINE bool
BytecodeEmitter::emitSwitch(ParseNode* pn)
{
JSOp switchOp;
bool hasDefault;
ptrdiff_t top, off, defaultOffset;
ParseNode* pn2, *pn3, *pn4;
int32_t low, high;
size_t switchSize;
jsbytecode* pc;
/* Try for most optimal, fall back if not dense ints. */
switchOp = JSOP_TABLESWITCH;
hasDefault = false;
defaultOffset = -1;
pn2 = pn->pn_right;
MOZ_ASSERT(pn2->isKind(PNK_LEXICALSCOPE) || pn2->isKind(PNK_STATEMENTLIST));
ParseNode* cases = pn->pn_right;
MOZ_ASSERT(cases->isKind(PNK_LEXICALSCOPE) || cases->isKind(PNK_STATEMENTLIST));
/* Push the discriminant. */
if (!emitTree(pn->pn_left))
return false;
StmtInfoBCE stmtInfo(cx);
if (pn2->isKind(PNK_LEXICALSCOPE)) {
if (!enterBlockScope(&stmtInfo, pn2->pn_objbox, JSOP_UNINITIALIZED, 0))
ptrdiff_t top;
if (cases->isKind(PNK_LEXICALSCOPE)) {
if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0))
return false;
stmtInfo.type = STMT_SWITCH;
stmtInfo.update = top = offset();
/* Advance pn2 to refer to the switch case list. */
pn2 = pn2->expr();
/* Advance |cases| to refer to the switch case list. */
cases = cases->expr();
} else {
MOZ_ASSERT(pn2->isKind(PNK_STATEMENTLIST));
MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST));
top = offset();
pushStatement(&stmtInfo, STMT_SWITCH, top);
}
/* Switch bytecodes run from here till end of final case. */
uint32_t caseCount = pn2->pn_count;
uint32_t tableLength = 0;
UniquePtr<ParseNode*[], JS::FreePolicy> table(nullptr);
uint32_t caseCount = cases->pn_count;
if (caseCount > JS_BIT(16)) {
parser->tokenStream.reportError(JSMSG_TOO_MANY_CASES);
return false;
}
/* Try for most optimal, fall back if not dense ints. */
JSOp switchOp = JSOP_TABLESWITCH;
uint32_t tableLength = 0;
int32_t low, high;
bool hasDefault = false;
if (caseCount == 0 ||
(caseCount == 1 &&
(hasDefault = (pn2->pn_head->isKind(PNK_DEFAULT))))) {
(hasDefault = cases->pn_head->isKind(PNK_DEFAULT)))) {
caseCount = 0;
low = 0;
high = -1;
} else {
bool ok = true;
#define INTMAP_LENGTH 256
jsbitmap intmap_space[INTMAP_LENGTH];
jsbitmap* intmap = nullptr;
int32_t intmap_bitlen = 0;
Vector<jsbitmap, 128, SystemAllocPolicy> intmap;
int32_t intmapBitLength = 0;
low = JSVAL_INT_MAX;
high = JSVAL_INT_MIN;
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
if (pn3->isKind(PNK_DEFAULT)) {
for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
if (caseNode->isKind(PNK_DEFAULT)) {
hasDefault = true;
caseCount--; /* one of the "cases" was the default */
continue;
}
MOZ_ASSERT(pn3->isKind(PNK_CASE));
MOZ_ASSERT(caseNode->isKind(PNK_CASE));
if (switchOp == JSOP_CONDSWITCH)
continue;
MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
pn4 = pn3->pn_left;
ParseNode* caseValue = caseNode->pn_left;
if (pn4->getKind() != PNK_NUMBER) {
if (caseValue->getKind() != PNK_NUMBER) {
switchOp = JSOP_CONDSWITCH;
continue;
}
int32_t i;
if (!NumberIsInt32(pn4->pn_dval, &i)) {
if (!NumberIsInt32(caseValue->pn_dval, &i)) {
switchOp = JSOP_CONDSWITCH;
continue;
}
if ((unsigned)(i + (int)JS_BIT(15)) >= (unsigned)JS_BIT(16)) {
if (unsigned(i + int(JS_BIT(15))) >= unsigned(JS_BIT(16))) {
switchOp = JSOP_CONDSWITCH;
continue;
}
if (i < low)
low = i;
if (high < i)
if (i > high)
high = i;
/*
@ -2706,21 +2693,11 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
*/
if (i < 0)
i += JS_BIT(16);
if (i >= intmap_bitlen) {
if (!intmap &&
size_t(i) < (INTMAP_LENGTH * JS_BITMAP_NBITS)) {
intmap = intmap_space;
intmap_bitlen = INTMAP_LENGTH * JS_BITMAP_NBITS;
} else {
/* Just grab 8K for the worst-case bitmap. */
intmap_bitlen = JS_BIT(16);
intmap = cx->pod_malloc<jsbitmap>(JS_BIT(16) / JS_BITMAP_NBITS);
if (!intmap) {
ReportOutOfMemory(cx);
return false;
}
}
memset(intmap, 0, size_t(intmap_bitlen) / CHAR_BIT);
if (i >= intmapBitLength) {
size_t newLength = (i / JS_BITMAP_NBITS) + 1;
if (!intmap.resize(newLength))
return false;
intmapBitLength = newLength * JS_BITMAP_NBITS;
}
if (JS_TEST_BIT(intmap, i)) {
switchOp = JSOP_CONDSWITCH;
@ -2729,17 +2706,12 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
JS_SET_BIT(intmap, i);
}
if (intmap && intmap != intmap_space)
js_free(intmap);
if (!ok)
return false;
/*
* Compute table length and select condswitch instead if overlarge or
* more than half-sparse.
*/
if (switchOp == JSOP_TABLESWITCH) {
tableLength = (uint32_t)(high - low + 1);
tableLength = uint32_t(high - low + 1);
if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
switchOp = JSOP_CONDSWITCH;
}
@ -2750,6 +2722,7 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
* second (if condswitch) tells offset to first JSOP_CASE.
*/
unsigned noteIndex;
size_t switchSize;
if (switchOp == JSOP_CONDSWITCH) {
/* 0 bytes of immediate for unoptimized switch. */
switchSize = 0;
@ -2768,38 +2741,39 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
if (!emitN(switchOp, switchSize))
return false;
off = -1;
Vector<ParseNode*, 32, SystemAllocPolicy> table;
ptrdiff_t condSwitchDefaultOff = -1;
if (switchOp == JSOP_CONDSWITCH) {
unsigned caseNoteIndex;
bool beforeCases = true;
ptrdiff_t prevCaseOffset;
/* Emit code for evaluating cases and jumping to case statements. */
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
pn4 = pn3->pn_left;
if (pn4 && !emitTree(pn4))
for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
ParseNode* caseValue = caseNode->pn_left;
if (caseValue && !emitTree(caseValue))
return false;
if (!beforeCases) {
/* off is the previous JSOP_CASE's bytecode offset. */
if (!setSrcNoteOffset(caseNoteIndex, 0, offset() - off))
/* prevCaseOffset is the previous JSOP_CASE's bytecode offset. */
if (!setSrcNoteOffset(caseNoteIndex, 0, offset() - prevCaseOffset))
return false;
}
if (!pn4) {
MOZ_ASSERT(pn3->isKind(PNK_DEFAULT));
if (!caseValue) {
MOZ_ASSERT(caseNode->isKind(PNK_DEFAULT));
continue;
}
if (!newSrcNote2(SRC_NEXTCASE, 0, &caseNoteIndex))
return false;
if (!emitJump(JSOP_CASE, 0, &off))
if (!emitJump(JSOP_CASE, 0, &prevCaseOffset))
return false;
pn3->pn_offset = off;
caseNode->pn_offset = prevCaseOffset;
if (beforeCases) {
unsigned noteCount, noteCountDelta;
/* Switch note's second offset is to first JSOP_CASE. */
noteCount = notes().length();
if (!setSrcNoteOffset(noteIndex, 1, off - top))
unsigned noteCount = notes().length();
if (!setSrcNoteOffset(noteIndex, 1, prevCaseOffset - top))
return false;
noteCountDelta = notes().length() - noteCount;
unsigned noteCountDelta = notes().length() - noteCount;
if (noteCountDelta != 0)
caseNoteIndex += noteCountDelta;
beforeCases = false;
@ -2813,18 +2787,18 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
* the benefit of IonBuilder.
*/
if (!hasDefault &&
caseNoteIndex != UINT_MAX &&
!setSrcNoteOffset(caseNoteIndex, 0, offset() - off))
!beforeCases &&
!setSrcNoteOffset(caseNoteIndex, 0, offset() - prevCaseOffset))
{
return false;
}
/* Emit default even if no explicit default statement. */
if (!emitJump(JSOP_DEFAULT, 0, &defaultOffset))
if (!emitJump(JSOP_DEFAULT, 0, &condSwitchDefaultOff))
return false;
} else {
MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
pc = code(top + JUMP_OFFSET_LEN);
jsbytecode* pc = code(top + JUMP_OFFSET_LEN);
/* Fill in switch bounds, which we know fit in 16-bit offsets. */
SET_JUMP_OFFSET(pc, low);
@ -2832,67 +2806,65 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
SET_JUMP_OFFSET(pc, high);
pc += JUMP_OFFSET_LEN;
/*
* Use malloc to avoid arena bloat for programs with many switches.
* UniquePtr takes care of freeing it on exit.
*/
if (tableLength != 0) {
table = cx->make_zeroed_pod_array<ParseNode*>(tableLength);
if (!table)
if (!table.growBy(tableLength))
return false;
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
if (pn3->isKind(PNK_DEFAULT))
for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
if (caseNode->isKind(PNK_DEFAULT))
continue;
MOZ_ASSERT(pn3->isKind(PNK_CASE));
MOZ_ASSERT(caseNode->isKind(PNK_CASE));
pn4 = pn3->pn_left;
MOZ_ASSERT(pn4->getKind() == PNK_NUMBER);
ParseNode* caseValue = caseNode->pn_left;
MOZ_ASSERT(caseValue->isKind(PNK_NUMBER));
int32_t i = int32_t(pn4->pn_dval);
MOZ_ASSERT(double(i) == pn4->pn_dval);
int32_t i = int32_t(caseValue->pn_dval);
MOZ_ASSERT(double(i) == caseValue->pn_dval);
i -= low;
MOZ_ASSERT(uint32_t(i) < tableLength);
table[i] = pn3;
MOZ_ASSERT(!table[i]);
table[i] = caseNode;
}
}
}
/* Emit code for each case's statements, copying pn_offset up to pn3. */
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
if (switchOp == JSOP_CONDSWITCH && !pn3->isKind(PNK_DEFAULT))
setJumpOffsetAt(pn3->pn_offset);
pn4 = pn3->pn_right;
if (!emitTree(pn4))
ptrdiff_t defaultOffset = -1;
/* Emit code for each case's statements, copying pn_offset up to caseNode. */
for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
if (switchOp == JSOP_CONDSWITCH && !caseNode->isKind(PNK_DEFAULT))
setJumpOffsetAt(caseNode->pn_offset);
ParseNode* caseValue = caseNode->pn_right;
if (!emitTree(caseValue))
return false;
pn3->pn_offset = pn4->pn_offset;
if (pn3->isKind(PNK_DEFAULT))
off = pn3->pn_offset - top;
caseNode->pn_offset = caseValue->pn_offset;
if (caseNode->isKind(PNK_DEFAULT))
defaultOffset = caseNode->pn_offset - top;
}
if (!hasDefault) {
/* If no default case, offset for default is to end of switch. */
off = offset() - top;
defaultOffset = offset() - top;
}
/* We better have set "off" by now. */
MOZ_ASSERT(off != -1);
/* We better have set "defaultOffset" by now. */
MOZ_ASSERT(defaultOffset != -1);
/* Set the default offset (to end of switch if no default). */
jsbytecode* pc;
if (switchOp == JSOP_CONDSWITCH) {
pc = nullptr;
MOZ_ASSERT(defaultOffset != -1);
SET_JUMP_OFFSET(code(defaultOffset), off - (defaultOffset - top));
SET_JUMP_OFFSET(code(condSwitchDefaultOff), defaultOffset - (condSwitchDefaultOff - top));
} else {
pc = code(top);
SET_JUMP_OFFSET(pc, off);
SET_JUMP_OFFSET(pc, defaultOffset);
pc += JUMP_OFFSET_LEN;
}
/* Set the SRC_SWITCH note's offset operand to tell end of switch. */
off = offset() - top;
if (!setSrcNoteOffset(unsigned(noteIndex), 0, off))
if (!setSrcNoteOffset(noteIndex, 0, offset() - top))
return false;
if (switchOp == JSOP_TABLESWITCH) {
@ -2901,8 +2873,8 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
/* Fill in the jump table, if there is one. */
for (uint32_t i = 0; i < tableLength; i++) {
pn3 = table[i];
off = pn3 ? pn3->pn_offset - top : 0;
ParseNode* caseNode = table[i];
ptrdiff_t off = caseNode ? caseNode->pn_offset - top : 0;
SET_JUMP_OFFSET(pc, off);
pc += JUMP_OFFSET_LEN;
}

View File

@ -658,7 +658,7 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne
if (offset != NoOffset && !err.report.filename && cx->isJSContext()) {
NonBuiltinFrameIter iter(cx->asJSContext(),
FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED,
cx->compartment()->principals);
cx->compartment()->principals());
if (!iter.done() && iter.scriptFilename()) {
callerFilename = true;
err.report.filename = iter.scriptFilename();

View File

@ -0,0 +1,11 @@
function f() {
var s = "switch (x) {";
for (var i=8000; i<16400; i++) {
s += "case " + i + ": return " + i + "; break;";
}
s += "case 8005: return -1; break;";
s += "}";
var g = Function("x", s);
assertEq(g(8005), 8005);
}
f();

View File

@ -94,6 +94,59 @@ BlockComputesConstant(MBasicBlock* block, MDefinition* value)
return true;
}
// Find phis that are redudant:
//
// 1) phi(a, a)
// can get replaced by a
//
// 2) phi(filtertypeset(a, type1), filtertypeset(a, type1))
// equals filtertypeset(a, type1)
//
// 3) phi(a, filtertypeset(a, type1))
// equals filtertypeset(a, type1 union type(a))
// equals filtertypeset(a, type(a))
// equals a
//
// 4) phi(filtertypeset(a, type1), filtertypeset(a, type2))
// equals filtertypeset(a, type1 union type2)
//
// This is the special case. We can only replace this with 'a' iif
// type(a) == type1 union type2. Since optimizations could have
// happened based on a more specific phi type.
static bool
IsPhiRedudantFilter(MPhi* phi)
{
// Handle (1) and (2)
if (phi->operandIfRedundant())
return true;
// Handle (3)
bool onlyFilters = false;
MDefinition* a = phi->getOperand(0);
if (a->isFilterTypeSet()) {
a = a->toFilterTypeSet()->input();
onlyFilters = true;
}
for (size_t i = 1; i < phi->numOperands(); i++) {
MDefinition* operand = phi->getOperand(i);
if (operand == a) {
onlyFilters = false;
continue;
}
if (operand->isFilterTypeSet() && operand->toFilterTypeSet()->input() == a)
continue;
return false;
}
if (!onlyFilters)
return true;
// Handle (4)
MOZ_ASSERT(onlyFilters);
return EqualTypes(a->type(), a->resultTypeSet(),
phi->type(), phi->resultTypeSet());
}
// Determine whether phiBlock/testBlock simply compute a phi and perform a
// test on it.
static bool
@ -131,8 +184,13 @@ BlockIsSingleTest(MBasicBlock* phiBlock, MBasicBlock* testBlock, MPhi** pphi, MT
}
for (MPhiIterator iter = phiBlock->phisBegin(); iter != phiBlock->phisEnd(); ++iter) {
if (*iter != phi)
return false;
if (*iter == phi)
continue;
if (IsPhiRedudantFilter(*iter))
continue;
return false;
}
if (phiBlock != testBlock && !testBlock->phisEmpty())
@ -257,6 +315,23 @@ MaybeFoldConditionBlock(MIRGraph& graph, MBasicBlock* initialBlock)
// OK, we found the desired pattern, now transform the graph.
// Patch up phis that filter their input.
for (MPhiIterator iter = phiBlock->phisBegin(); iter != phiBlock->phisEnd(); ++iter) {
if (*iter == phi)
continue;
MOZ_ASSERT(IsPhiRedudantFilter(*iter));
MDefinition* redundant = (*iter)->operandIfRedundant();
if (!redundant) {
redundant = (*iter)->getOperand(0);
if (redundant->isFilterTypeSet())
redundant = redundant->toFilterTypeSet()->input();
}
(*iter)->replaceAllUsesWith(redundant);
}
// Remove the phi from phiBlock.
phiBlock->discardPhi(*phiBlock->phisBegin());
@ -305,112 +380,11 @@ MaybeFoldConditionBlock(MIRGraph& graph, MBasicBlock* initialBlock)
graph.removeBlock(testBlock);
}
static void
MaybeFoldAndOrBlock(MIRGraph& graph, MBasicBlock* initialBlock)
{
// Optimize the MIR graph to improve the code generated for && and ||
// operations when they are used in tests. This is very similar to the
// above method for folding condition blocks, though the two are
// separated (with as much common code as possible) for clarity. This
// normally requires three blocks. The final test can always be eliminated,
// though we don't try to constant fold away the branch block as well.
// Look for a triangle pattern:
//
// initialBlock
// / |
// branchBlock |
// \ |
// phiBlock
// |
// testBlock
//
// Where phiBlock contains a single phi combining values pushed onto the
// stack by initialBlock and testBlock, and testBlock contains a test on
// that phi. phiBlock and testBlock may be the same block; generated code
// will use different blocks if the &&/|| is in an inlined function.
MInstruction* ins = initialBlock->lastIns();
if (!ins->isTest())
return;
MTest* initialTest = ins->toTest();
bool branchIsTrue = true;
MBasicBlock* branchBlock = initialTest->ifTrue();
MBasicBlock* phiBlock = initialTest->ifFalse();
if (branchBlock->numSuccessors() != 1 || branchBlock->getSuccessor(0) != phiBlock) {
branchIsTrue = false;
branchBlock = initialTest->ifFalse();
phiBlock = initialTest->ifTrue();
}
if (branchBlock->numSuccessors() != 1 || branchBlock->getSuccessor(0) != phiBlock)
return;
if (branchBlock->numPredecessors() != 1 || phiBlock->numPredecessors() != 2)
return;
if (initialBlock->isLoopBackedge() || branchBlock->isLoopBackedge())
return;
MBasicBlock* testBlock = phiBlock;
if (testBlock->numSuccessors() == 1) {
if (testBlock->isLoopBackedge())
return;
testBlock = testBlock->getSuccessor(0);
if (testBlock->numPredecessors() != 1)
return;
}
// Make sure the test block does not have any outgoing loop backedges.
if (!SplitCriticalEdgesForBlock(graph, testBlock))
CrashAtUnhandlableOOM("MaybeFoldAndOrBlock");
MPhi* phi;
MTest* finalTest;
if (!BlockIsSingleTest(phiBlock, testBlock, &phi, &finalTest))
return;
MDefinition* branchResult = phi->getOperand(phiBlock->indexForPredecessor(branchBlock));
MDefinition* initialResult = phi->getOperand(phiBlock->indexForPredecessor(initialBlock));
if (initialResult != initialTest->input())
return;
// OK, we found the desired pattern, now transform the graph.
// Remove the phi from phiBlock.
phiBlock->discardPhi(*phiBlock->phisBegin());
// Change the end of the initial and branch blocks to a test that jumps
// directly to successors of testBlock, rather than to testBlock itself.
UpdateTestSuccessors(graph.alloc(), initialBlock, initialResult,
branchIsTrue ? branchBlock : finalTest->ifTrue(),
branchIsTrue ? finalTest->ifFalse() : branchBlock,
testBlock);
UpdateTestSuccessors(graph.alloc(), branchBlock, branchResult,
finalTest->ifTrue(), finalTest->ifFalse(), testBlock);
// Remove phiBlock, if different from testBlock.
if (phiBlock != testBlock) {
testBlock->removePredecessor(phiBlock);
graph.removeBlock(phiBlock);
}
// Remove testBlock itself.
finalTest->ifTrue()->removePredecessor(testBlock);
finalTest->ifFalse()->removePredecessor(testBlock);
graph.removeBlock(testBlock);
}
void
jit::FoldTests(MIRGraph& graph)
{
for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++)
MaybeFoldConditionBlock(graph, *block);
MaybeFoldAndOrBlock(graph, *block);
}
}
static void
@ -475,8 +449,11 @@ jit::EliminateDeadResumePointOperands(MIRGenerator* mir, MIRGraph& graph)
// parameter passing might be live. Rewriting uses of these terms
// in resume points may affect the interpreter's behavior. Rather
// than doing a more sophisticated analysis, just ignore these.
if (ins->isUnbox() || ins->isParameter() || ins->isTypeBarrier() || ins->isComputeThis())
if (ins->isUnbox() || ins->isParameter() || ins->isTypeBarrier() ||
ins->isComputeThis() || ins->isFilterTypeSet())
{
continue;
}
// Early intermediate values captured by resume points, such as
// TypedObject, ArrayState and its allocation, may be legitimately

View File

@ -302,12 +302,12 @@ IonBuilder::CFGState::IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, MTest* t
}
IonBuilder::CFGState
IonBuilder::CFGState::AndOr(jsbytecode* join, MBasicBlock* joinStart)
IonBuilder::CFGState::AndOr(jsbytecode* join, MBasicBlock* lhs)
{
CFGState state;
state.state = AND_OR;
state.stopAt = join;
state.branch.ifFalse = joinStart;
state.branch.ifFalse = lhs;
state.branch.test = nullptr;
return state;
}
@ -2706,16 +2706,25 @@ IonBuilder::processNextTableSwitchCase(CFGState& state)
IonBuilder::ControlStatus
IonBuilder::processAndOrEnd(CFGState& state)
{
// We just processed the RHS of an && or || expression.
// Now jump to the join point (the false block).
current->end(MGoto::New(alloc(), state.branch.ifFalse));
MOZ_ASSERT(current);
MBasicBlock* lhs = state.branch.ifFalse;
if (!state.branch.ifFalse->addPredecessor(alloc(), current))
// Create a new block to represent the join.
MBasicBlock* join = newBlock(current, state.stopAt);
if (!join)
return ControlStatus_Error;
if (!setCurrentAndSpecializePhis(state.branch.ifFalse))
// End the rhs.
current->end(MGoto::New(alloc(), join));
// End the lhs.
lhs->end(MGoto::New(alloc(), join));
if (!join->addPredecessor(alloc(), state.branch.ifFalse))
return ControlStatus_Error;
// Set the join path as current path.
if (!setCurrentAndSpecializePhis(join))
return ControlStatus_Error;
graph().moveBlockToEnd(current);
pc = current->pc();
return ControlStatus_Joined;
}
@ -4134,20 +4143,34 @@ IonBuilder::jsop_andor(JSOp op)
// We have to leave the LHS on the stack.
MDefinition* lhs = current->peek(-1);
MBasicBlock* evalLhs = newBlock(current, joinStart);
MBasicBlock* evalRhs = newBlock(current, rhsStart);
MBasicBlock* join = newBlock(current, joinStart);
if (!evalRhs || !join)
if (!evalLhs || !evalRhs)
return false;
MTest* test = (op == JSOP_AND)
? newTest(lhs, evalRhs, join)
: newTest(lhs, join, evalRhs);
? newTest(lhs, evalRhs, evalLhs)
: newTest(lhs, evalLhs, evalRhs);
current->end(test);
if (!cfgStack_.append(CFGState::AndOr(joinStart, join)))
// Create the lhs block and specialize.
if (!setCurrentAndSpecializePhis(evalLhs))
return false;
return setCurrentAndSpecializePhis(evalRhs);
if (!improveTypesAtTest(test->getOperand(0), test->ifTrue() == current, test))
return false;
// Create the rhs block.
if (!cfgStack_.append(CFGState::AndOr(joinStart, evalLhs)))
return false;
if (!setCurrentAndSpecializePhis(evalRhs))
return false;
if (!improveTypesAtTest(test->getOperand(0), test->ifTrue() == current, test))
return false;
return true;
}
bool
@ -7504,36 +7527,6 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
rvalType, barrier, types);
}
// Whether 'types' includes all possible values represented by input/inputTypes.
bool
jit::TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes)
{
if (!types)
return inputTypes && inputTypes->empty();
switch (input) {
case MIRType_Undefined:
case MIRType_Null:
case MIRType_Boolean:
case MIRType_Int32:
case MIRType_Double:
case MIRType_Float32:
case MIRType_String:
case MIRType_Symbol:
case MIRType_MagicOptimizedArguments:
return types->hasType(TypeSet::PrimitiveType(ValueTypeFromMIRType(input)));
case MIRType_Object:
return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
case MIRType_Value:
return types->unknown() || (inputTypes && inputTypes->isSubset(types));
default:
MOZ_CRASH("Bad input type");
}
}
// Whether a write of the given value may need a post-write barrier for GC purposes.
bool
jit::NeedsPostBarrier(CompileInfo& info, MDefinition* value)

View File

@ -206,7 +206,7 @@ class IonBuilder
static CFGState If(jsbytecode* join, MTest* test);
static CFGState IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, MTest* test);
static CFGState AndOr(jsbytecode* join, MBasicBlock* joinStart);
static CFGState AndOr(jsbytecode* join, MBasicBlock* lhs);
static CFGState TableSwitch(jsbytecode* exitpc, MTableSwitch* ins);
static CFGState CondSwitch(IonBuilder* builder, jsbytecode* exitpc, jsbytecode* defaultTarget);
static CFGState Label(jsbytecode* exitpc);
@ -1351,8 +1351,6 @@ class CallInfo
}
};
bool TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
bool NeedsPostBarrier(CompileInfo& info, MDefinition* value);
} // namespace jit

View File

@ -1771,6 +1771,61 @@ jit::MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet,
return true;
}
// Tests whether 'types' includes all possible values represented by
// input/inputTypes.
bool
jit::TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes)
{
if (!types)
return inputTypes && inputTypes->empty();
switch (input) {
case MIRType_Undefined:
case MIRType_Null:
case MIRType_Boolean:
case MIRType_Int32:
case MIRType_Double:
case MIRType_Float32:
case MIRType_String:
case MIRType_Symbol:
case MIRType_MagicOptimizedArguments:
return types->hasType(TypeSet::PrimitiveType(ValueTypeFromMIRType(input)));
case MIRType_Object:
return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
case MIRType_Value:
return types->unknown() || (inputTypes && inputTypes->isSubset(types));
default:
MOZ_CRASH("Bad input type");
}
}
// Tests if two type combos (type/typeset) are equal.
bool
jit::EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
MIRType type2, TemporaryTypeSet* typeset2)
{
// Types should equal.
if (type1 != type2)
return false;
// Both have equal type and no typeset.
if (!typeset1 && !typeset2)
return true;
// If only one instructions has a typeset.
// Test if the typset contains the same information as the MIRType.
if (typeset1 && !typeset2)
return TypeSetIncludes(typeset1, type2, nullptr);
if (!typeset1 && typeset2)
return TypeSetIncludes(typeset2, type1, nullptr);
// Typesets should equal.
return typeset1->equals(typeset2);
}
bool
MPhi::specializeType()
{

View File

@ -2833,6 +2833,13 @@ bool
MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet,
MIRType newType, TemporaryTypeSet* newTypeSet);
bool
TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
bool
EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
MIRType type2, TemporaryTypeSet* typeset2);
// Helper class to assert all GC pointers embedded in MIR instructions are
// tenured. Off-thread Ion compilation and nursery GCs can happen in parallel,
// so it's invalid to store pointers to nursery things. There's no need to root

View File

@ -270,30 +270,78 @@ JS_GetEmptyString(JSRuntime* rt)
return rt->emptyString;
}
JS_PUBLIC_API(bool)
JS_GetCompartmentStats(JSRuntime* rt, CompartmentStatsVector& stats)
{
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
if (!stats.growBy(1))
return false;
namespace js {
CompartmentTimeStats* stat = &stats.back();
stat->time = c.get()->totalTime;
stat->compartment = c.get();
stat->addonId = c.get()->addonId;
if (rt->compartmentNameCallback) {
(*rt->compartmentNameCallback)(rt, stat->compartment,
stat->compartmentName,
MOZ_ARRAY_LENGTH(stat->compartmentName));
JS_PUBLIC_API(bool)
GetPerformanceStats(JSRuntime* rt,
PerformanceStatsVector& stats,
PerformanceStats& processStats)
{
// As a PerformanceGroup is typically associated to several
// compartments, use a HashSet to make sure that we only report
// each PerformanceGroup once.
typedef HashSet<js::PerformanceGroup*,
js::DefaultHasher<js::PerformanceGroup*>,
js::SystemAllocPolicy> Set;
Set set;
if (!set.init(100)) {
return false;
}
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
JSCompartment* compartment = c.get();
if (!compartment->performanceMonitoring.isLinked()) {
// Don't report compartments that do not even have a PerformanceGroup.
continue;
}
PerformanceGroup* group = compartment->performanceMonitoring.getGroup();
if (group->data.ticks == 0) {
// Don't report compartments that have never been used.
continue;
}
Set::AddPtr ptr = set.lookupForAdd(group);
if (ptr) {
// Don't report the same group twice.
continue;
}
if (!stats.growBy(1)) {
// Memory issue
return false;
}
PerformanceStats* stat = &stats.back();
stat->isSystem = compartment->isSystem();
if (compartment->addonId)
stat->addonId = compartment->addonId;
if (compartment->addonId || !compartment->isSystem()) {
if (rt->compartmentNameCallback) {
(*rt->compartmentNameCallback)(rt, compartment,
stat->name,
mozilla::ArrayLength(stat->name));
} else {
strcpy(stat->name, "<unknown>");
}
} else {
strcpy(stat->compartmentName, "<unknown>");
strcpy(stat->name, "<platform>");
}
stat->performance = group->data;
if (!set.add(ptr, group)) {
// Memory issue
return false;
}
}
strcpy(processStats.name, "<process>");
processStats.addonId = nullptr;
processStats.isSystem = true;
processStats.performance = rt->stopwatch.performance;
return true;
}
namespace js {
void
AssertHeapIsIdle(JSRuntime* rt)
{

View File

@ -972,19 +972,6 @@ JS_GetEmptyStringValue(JSContext* cx);
extern JS_PUBLIC_API(JSString*)
JS_GetEmptyString(JSRuntime* rt);
struct CompartmentTimeStats {
char compartmentName[1024];
JSAddonId* addonId;
JSCompartment* compartment;
uint64_t time; // microseconds
uint64_t cpowTime; // microseconds
};
typedef js::Vector<CompartmentTimeStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
extern JS_PUBLIC_API(bool)
JS_GetCompartmentStats(JSRuntime* rt, CompartmentStatsVector& stats);
extern JS_PUBLIC_API(bool)
JS_ValueToObject(JSContext* cx, JS::HandleValue v, JS::MutableHandleObject objp);
@ -998,7 +985,7 @@ extern JS_PUBLIC_API(JSString*)
JS_ValueToSource(JSContext* cx, JS::Handle<JS::Value> v);
extern JS_PUBLIC_API(bool)
JS_DoubleIsInt32(double d, int32_t* ip);
JS_DoubleIsInt32(double d, int32_t *ip);
extern JS_PUBLIC_API(JSType)
JS_TypeOfValue(JSContext* cx, JS::Handle<JS::Value> v);
@ -5273,4 +5260,258 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp)
} /* namespace JS */
/* Stopwatch-based CPU monitoring. */
namespace js {
struct AutoStopwatch;
// Container for performance data
// All values are monotonic.
struct PerformanceData {
// Number of times we have spent at least 2^n consecutive
// milliseconds executing code in this group.
// durations[0] is increased whenever we spend at least 1 ms
// executing code in this group
// durations[1] whenever we spend 2ms+
//
// durations[i] whenever we spend 2^ims+
uint64_t durations[10];
// Total amount of time spent executing code in this group, in
// microseconds.
uint64_t totalUserTime;
uint64_t totalSystemTime;
uint64_t totalCPOWTime;
// Total number of times code execution entered this group,
// since process launch. This may be greater than the number
// of times we have entered the event loop.
uint64_t ticks;
PerformanceData()
: totalUserTime(0)
, totalSystemTime(0)
, totalCPOWTime(0)
, ticks(0)
{
mozilla::PodArrayZero(durations);
}
PerformanceData(const PerformanceData& from)
: totalUserTime(from.totalUserTime)
, totalSystemTime(from.totalSystemTime)
, totalCPOWTime(from.totalCPOWTime)
, ticks(from.ticks)
{
mozilla::PodArrayCopy(durations, from.durations);
}
PerformanceData& operator=(const PerformanceData& from)
{
mozilla::PodArrayCopy(durations, from.durations);
totalUserTime = from.totalUserTime;
totalSystemTime = from.totalSystemTime;
totalCPOWTime = from.totalCPOWTime;
ticks = from.ticks;
return *this;
}
};
// A group of compartments forming a single unit in terms of
// performance monitoring.
//
// Two compartments belong to the same group if either:
// - they are part of the same add-on;
// - they are part of the same webpage;
// - they are both system built-ins.
//
// This class is refcounted by instances of `JSCompartment`.
// Do not attempt to hold to a pointer to a `PerformanceGroup`.
struct PerformanceGroup {
// Performance data for this group.
PerformanceData data;
// `true` if an instance of `AutoStopwatch` is already monitoring
// the performance of this performance group for this iteration
// of the event loop, `false` otherwise.
bool hasStopwatch(uint64_t iteration) const {
return stopwatch_ != nullptr && iteration_ == iteration;
}
// Mark that an instance of `AutoStopwatch` is monitoring
// the performance of this group for a given iteration.
void acquireStopwatch(uint64_t iteration, const AutoStopwatch *stopwatch) {
iteration_ = iteration;
stopwatch_ = stopwatch;
}
// Mark that no `AutoStopwatch` is monitoring the
// performance of this group for the iteration.
void releaseStopwatch(uint64_t iteration, const AutoStopwatch *stopwatch) {
if (iteration_ != iteration)
return;
MOZ_ASSERT(stopwatch == stopwatch_ || stopwatch_ == nullptr);
stopwatch_ = nullptr;
}
PerformanceGroup()
: stopwatch_(nullptr)
, iteration_(0)
, refCount_(0)
{ }
~PerformanceGroup()
{
MOZ_ASSERT(refCount_ == 0);
}
private:
PerformanceGroup& operator=(const PerformanceGroup&) = delete;
PerformanceGroup(const PerformanceGroup&) = delete;
// The stopwatch currently monitoring the group,
// or `nullptr` if none. Used ony for comparison.
const AutoStopwatch *stopwatch_;
// The current iteration of the event loop. If necessary,
// may safely overflow.
uint64_t iteration_;
// Increment/decrement the refcounter, return the updated value.
uint64_t incRefCount() {
MOZ_ASSERT(refCount_ + 1 > 0);
return ++refCount_;
}
uint64_t decRefCount() {
MOZ_ASSERT(refCount_ > 0);
return --refCount_;
}
friend struct PerformanceGroupHolder;
private:
// A reference counter. Maintained by PerformanceGroupHolder.
uint64_t refCount_;
};
//
// Indirection towards a PerformanceGroup.
// This structure handles reference counting for instances of PerformanceGroup.
//
struct PerformanceGroupHolder {
// Get the group.
// On first call, this causes a single Hashtable lookup.
// Successive calls do not require further lookups.
js::PerformanceGroup *getGroup();
// `true` if the this holder is currently associated to a
// PerformanceGroup, `false` otherwise. Use this method to avoid
// instantiating a PerformanceGroup if you only need to get
// available performance data.
inline bool isLinked() const {
return group_ != nullptr;
}
// Remove the link to the PerformanceGroup. This method is designed
// as an invalidation mechanism if the JSCompartment changes nature
// (new values of `isSystem()`, `principals()` or `addonId`).
void unlink();
PerformanceGroupHolder(JSRuntime *runtime, JSCompartment *compartment)
: runtime_(runtime)
, compartment_(compartment)
, group_(nullptr)
{ }
~PerformanceGroupHolder();
private:
// Return the key representing this PerformanceGroup in
// Runtime::Stopwatch.
// Do not deallocate the key.
void* getHashKey();
JSRuntime *runtime_;
JSCompartment *compartment_;
// The PerformanceGroup held by this object.
// Initially set to `nullptr` until the first cal to `getGroup`.
// May be reset to `nullptr` by a call to `unlink`.
js::PerformanceGroup *group_;
};
/**
* Reset any stopwatch currently measuring.
*
* This function is designed to be called when we process a new event.
*/
extern JS_PUBLIC_API(void)
ResetStopwatches(JSRuntime*);
/**
* Turn on/off stopwatch-based CPU monitoring.
*
* `SetStopwatchActive` may return `false` if monitoring could not be
* activated, which may happen if we are out of memory.
*/
extern JS_PUBLIC_API(bool)
SetStopwatchActive(JSRuntime*, bool);
extern JS_PUBLIC_API(bool)
IsStopwatchActive(JSRuntime*);
/**
* Access the performance information stored in a compartment.
*/
extern JS_PUBLIC_API(PerformanceData*)
GetPerformanceData(JSRuntime*);
/**
* Performance statistics for a performance group (a process, an
* add-on, a webpage, the built-ins or a special compartment).
*/
struct PerformanceStats {
/**
* If this group represents an add-on, the ID of the addon,
* otherwise `nullptr`.
*/
JSAddonId *addonId;
/**
* If this group represents a webpage, the process itself or a special
* compartment, a human-readable name. Unspecified for add-ons.
*/
char name[1024];
/**
* `true` if the group represents in system compartments, `false`
* otherwise. A group may never contain both system and non-system
* compartments.
*/
bool isSystem;
/**
* Performance information.
*/
js::PerformanceData performance;
PerformanceStats()
: addonId(nullptr)
, isSystem(false)
{
name[0] = '\0';
}
};
typedef js::Vector<PerformanceStats, 0, js::SystemAllocPolicy> PerformanceStatsVector;
/**
* Extract the performance statistics.
*
* After a successful call, `stats` holds the `PerformanceStats` for
* all performance groups, and `global` holds a `PerformanceStats`
* representing the entire process.
*/
extern JS_PUBLIC_API(bool)
GetPerformanceStats(JSRuntime *rt, js::PerformanceStatsVector &stats, js::PerformanceStats &global);
} /* namespace js */
#endif /* jsapi_h */

View File

@ -380,7 +380,7 @@ JSContext::setPendingException(js::Value v)
inline bool
JSContext::runningWithTrustedPrincipals() const
{
return !compartment() || compartment()->principals == runtime()->trustedPrincipals();
return !compartment() || compartment()->principals() == runtime()->trustedPrincipals();
}
inline void

View File

@ -41,8 +41,8 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
: options_(options),
zone_(zone),
runtime_(zone->runtimeFromMainThread()),
principals(nullptr),
isSystem(false),
principals_(nullptr),
isSystem_(false),
isSelfHosting(false),
marked(true),
warnedAboutNoSuchMethod(false),
@ -53,7 +53,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
#endif
global_(nullptr),
enterCompartmentDepth(0),
totalTime(0),
performanceMonitoring(runtime_, this),
data(nullptr),
objectMetadataCallback(nullptr),
lastAnimationTime(0),
@ -871,7 +871,7 @@ void
JSCompartment::reportTelemetry()
{
// Only report telemetry for web content, not add-ons or chrome JS.
if (addonId || isSystem)
if (addonId || isSystem_)
return;
// Hazard analysis can't tell that the telemetry callbacks don't GC.
@ -888,7 +888,7 @@ void
JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
{
// Only report telemetry for web content, not add-ons or chrome JS.
if (addonId || isSystem || !filename || strncmp(filename, "http", 4) != 0)
if (addonId || isSystem_ || !filename || strncmp(filename, "http", 4) != 0)
return;
sawDeprecatedLanguageExtension[e] = true;

View File

@ -144,8 +144,50 @@ struct JSCompartment
JSRuntime* runtime_;
public:
JSPrincipals* principals;
bool isSystem;
/*
* The principals associated with this compartment. Note that the
* same several compartments may share the same principals and
* that a compartment may change principals during its lifetime
* (e.g. in case of lazy parsing).
*/
inline JSPrincipals* principals() {
return principals_;
}
inline void setPrincipals(JSPrincipals* principals) {
if (principals_ == principals)
return;
// If we change principals, we need to unlink immediately this
// compartment from its PerformanceGroup. For one thing, the
// performance data we collect should not be improperly associated
// with a group to which we do not belong anymore. For another thing,
// we use `principals()` as part of the key to map compartments
// to a `PerformanceGroup`, so if we do not unlink now, this will
// be too late once we have updated `principals_`.
performanceMonitoring.unlink();
principals_ = principals;
}
inline bool isSystem() const {
return isSystem_;
}
inline void setIsSystem(bool isSystem) {
if (isSystem_ == isSystem)
return;
// If we change `isSystem*(`, we need to unlink immediately this
// compartment from its PerformanceGroup. For one thing, the
// performance data we collect should not be improperly associated
// to a group to which we do not belong anymore. For another thing,
// we use `isSystem()` as part of the key to map compartments
// to a `PerformanceGroup`, so if we do not unlink now, this will
// be too late once we have updated `isSystem_`.
performanceMonitoring.unlink();
isSystem_ = isSystem;
}
private:
JSPrincipals* principals_;
bool isSystem_;
public:
bool isSelfHosting;
bool marked;
bool warnedAboutNoSuchMethod;
@ -153,7 +195,7 @@ struct JSCompartment
// A null add-on ID means that the compartment is not associated with an
// add-on.
JSAddonId* addonId;
JSAddonId* const addonId;
#ifdef DEBUG
bool firedOnNewGlobalObject;
@ -171,18 +213,13 @@ struct JSCompartment
int64_t startInterval;
public:
int64_t totalTime;
js::PerformanceGroupHolder performanceMonitoring;
void enter() {
if (addonId && !enterCompartmentDepth) {
startInterval = PRMJ_Now();
}
enterCompartmentDepth++;
}
void leave() {
enterCompartmentDepth--;
if (addonId && !enterCompartmentDepth) {
totalTime += (PRMJ_Now() - startInterval);
}
}
bool hasBeenEntered() { return !!enterCompartmentDepth; }

View File

@ -156,14 +156,14 @@ JS_NewObjectWithoutMetadata(JSContext* cx, const JSClass* clasp, JS::Handle<JSOb
JS_FRIEND_API(JSPrincipals*)
JS_GetCompartmentPrincipals(JSCompartment* compartment)
{
return compartment->principals;
return compartment->principals();
}
JS_FRIEND_API(void)
JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals)
{
// Short circuit if there's no change.
if (principals == compartment->principals)
if (principals == compartment->principals())
return;
// Any compartment with the trusted principals -- and there can be
@ -172,24 +172,24 @@ JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals
bool isSystem = principals && principals == trusted;
// Clear out the old principals, if any.
if (compartment->principals) {
JS_DropPrincipals(compartment->runtimeFromMainThread(), compartment->principals);
compartment->principals = nullptr;
if (compartment->principals()) {
JS_DropPrincipals(compartment->runtimeFromMainThread(), compartment->principals());
compartment->setPrincipals(nullptr);
// We'd like to assert that our new principals is always same-origin
// with the old one, but JSPrincipals doesn't give us a way to do that.
// But we can at least assert that we're not switching between system
// and non-system.
MOZ_ASSERT(compartment->isSystem == isSystem);
MOZ_ASSERT(compartment->isSystem() == isSystem);
}
// Set up the new principals.
if (principals) {
JS_HoldPrincipals(principals);
compartment->principals = principals;
compartment->setPrincipals(principals);
}
// Update the system flag.
compartment->isSystem = isSystem;
compartment->setIsSystem(isSystem);
}
JS_FRIEND_API(JSPrincipals*)
@ -280,7 +280,7 @@ js::GetCompartmentZone(JSCompartment* comp)
JS_FRIEND_API(bool)
js::IsSystemCompartment(JSCompartment* comp)
{
return comp->isSystem;
return comp->isSystem();
}
JS_FRIEND_API(bool)

View File

@ -3529,8 +3529,8 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
if ((!comp->marked && !dontDelete) || destroyingRuntime) {
if (callback)
callback(fop, comp);
if (comp->principals)
JS_DropPrincipals(rt, comp->principals);
if (comp->principals())
JS_DropPrincipals(rt, comp->principals());
js_delete(comp);
} else {
*write++ = comp;

View File

@ -142,7 +142,7 @@ JSScript::global() const
inline JSPrincipals*
JSScript::principals()
{
return compartment()->principals;
return compartment()->principals();
}
inline void

View File

@ -336,7 +336,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
if (!global)
return false;
JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals);
JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals());
RootedObject obj(cx);

View File

@ -10,6 +10,7 @@
#include "vm/Interpreter-inl.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/PodOperations.h"
@ -52,6 +53,13 @@
#include "vm/ScopeObject-inl.h"
#include "vm/Stack-inl.h"
#if defined(XP_UNIX)
#include <sys/resource.h>
#elif defined(XP_WIN)
#include <Processthreadsapi.h>
#include <Windows.h>
#endif // defined(XP_UNIX) || defined(XP_WIN)
using namespace js;
using namespace js::gc;
@ -382,12 +390,234 @@ ExecuteState::pushInterpreterFrame(JSContext* cx)
return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, scopeChain_,
type_, evalInFrame_);
}
namespace js {
// Implementation of per-performance group performance measurement.
//
//
// All mutable state is stored in `Runtime::stopwatch` (per-process
// performance stats and logistics) and in `PerformanceGroup` (per
// group performance stats).
struct AutoStopwatch final
{
// If the stopwatch is active, constructing an instance of
// AutoStopwatch causes it to become the current owner of the
// stopwatch.
//
// Previous owner is restored upon destruction.
explicit inline AutoStopwatch(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: compartment_(nullptr)
, runtime_(nullptr)
, iteration_(0)
, isActive_(false)
, isTop_(false)
, userTimeStart_(0)
, systemTimeStart_(0)
, CPOWTimeStart_(0)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
runtime_ = cx->runtime();
if (!runtime_->stopwatch.isActive())
return;
compartment_ = cx->compartment();
MOZ_ASSERT(compartment_);
if (compartment_->scheduledForDestruction)
return;
iteration_ = runtime_->stopwatch.iteration;
PerformanceGroup *group = compartment_->performanceMonitoring.getGroup();
MOZ_ASSERT(group);
if (group->hasStopwatch(iteration_)) {
// Someone is already monitoring this group during this
// tick, no need for further monitoring.
return;
}
// Start the stopwatch.
if (!this->getTimes(&userTimeStart_, &systemTimeStart_))
return;
isActive_ = true;
CPOWTimeStart_ = runtime_->stopwatch.performance.totalCPOWTime;
// We are now in charge of monitoring this group for the tick,
// until destruction of `this` or until we enter a nested event
// loop and `iteration_` is incremented.
group->acquireStopwatch(iteration_, this);
if (runtime_->stopwatch.isEmpty) {
// This is the topmost stopwatch on the stack.
// It will be in charge of updating the per-process
// performance data.
runtime_->stopwatch.isEmpty = false;
runtime_->stopwatch.performance.ticks++;
isTop_ = true;
}
}
inline ~AutoStopwatch() {
if (!isActive_) {
// We are not in charge of monitoring anything.
return;
}
MOZ_ASSERT(!compartment_->scheduledForDestruction);
if (!runtime_->stopwatch.isActive()) {
// Monitoring has been stopped while we were
// executing the code. Drop everything.
return;
}
if (iteration_ != runtime_->stopwatch.iteration) {
// We have entered a nested event loop at some point.
// Any information we may have is obsolete.
return;
}
PerformanceGroup *group = compartment_->performanceMonitoring.getGroup();
MOZ_ASSERT(group);
// Compute time spent.
group->releaseStopwatch(iteration_, this);
uint64_t userTimeEnd, systemTimeEnd;
if (!this->getTimes(&userTimeEnd, &systemTimeEnd))
return;
uint64_t userTimeDelta = userTimeEnd - userTimeStart_;
uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_;
uint64_t CPOWTimeDelta = runtime_->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
group->data.totalUserTime += userTimeDelta;
group->data.totalSystemTime += systemTimeDelta;
group->data.totalCPOWTime += CPOWTimeDelta;
uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
updateDurations(totalTimeDelta, group->data.durations);
group->data.ticks++;
if (isTop_) {
// This is the topmost stopwatch on the stack.
// Record the timing information.
runtime_->stopwatch.performance.totalUserTime = userTimeEnd;
runtime_->stopwatch.performance.totalSystemTime = systemTimeEnd;
updateDurations(totalTimeDelta, runtime_->stopwatch.performance.durations);
runtime_->stopwatch.isEmpty = true;
}
}
private:
// Update an array containing the number of times we have missed
// at least 2^0 successive ms, 2^1 successive ms, ...
// 2^i successive ms.
template<int N>
void updateDurations(uint64_t totalTimeDelta, uint64_t (&array)[N]) const {
// Duration of one frame, i.e. 16ms in museconds
size_t i = 0;
uint64_t duration = 1000;
for (i = 0, duration = 1000;
i < N && duration < totalTimeDelta;
++i, duration *= 2) {
array[i]++;
}
}
// Get the OS-reported time spent in userland/systemland,
// in microseconds.
bool getTimes(uint64_t *userTime, uint64_t *systemTime) const {
MOZ_ASSERT(userTime);
MOZ_ASSERT(systemTime);
#if defined(XP_UNIX)
struct rusage rusage;
#if defined(RUSAGE_THREAD)
// Under Linux, we can obtain per-thread statistics
int err = getrusage(RUSAGE_THREAD, &rusage);
#else
// Under other Unices, including MacOS X, we need to
// do with more noisy per-process statistics.
int err = getrusage(RUSAGE_SELF, &rusage);
#endif // defined(RUSAGE_THREAD)
MOZ_ASSERT(!err);
if (err)
return false;
*userTime = rusage.ru_utime.tv_usec
+ rusage.ru_utime.tv_sec * 1000000;
*systemTime = rusage.ru_stime.tv_usec
+ rusage.ru_stime.tv_sec * 1000000;
#elif defined(XP_WIN)
// Under Windows, we can obtain per-thread statistics,
// although experience seems to suggest that they are
// not very good under Windows XP.
FILETIME creationFileTime; // Ignored
FILETIME exitFileTime; // Ignored
FILETIME kernelFileTime;
FILETIME userFileTime;
BOOL success = GetThreadTimes(GetCurrentThread(),
&creationFileTime, &exitFileTime,
&kernelFileTime, &userFileTime);
MOZ_ASSERT(success);
if (!success)
return false;
ULARGE_INTEGER kernelTimeInt;
ULARGE_INTEGER userTimeInt;
kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
*systemTime = kernelTimeInt.QuadPart / 10; // 100 ns to 1 us
userTimeInt.LowPart = userFileTime.dwLowDateTime;
userTimeInt.HighPart = userFileTime.dwHighDateTime;
*userTime = userTimeInt.QuadPart / 10; // 100 ns to 1 us
#endif // defined(XP_UNIX) || defined(XP_WIN)
return true;
}
private:
// The compartment with which this object was initialized.
// Non-null.
JSCompartment *compartment_;
// The runtime with which this object was initialized.
// Non-null.
JSRuntime *runtime_;
// An indication of the number of times we have entered the event
// loop. Used only for comparison.
uint64_t iteration_;
// `true` if this object is currently used to monitor performance,
// `false` otherwise, i.e. if the stopwatch mechanism is off or if
// another stopwatch is already in charge of monitoring for the
// same PerformanceGroup.
bool isActive_;
// `true` if this stopwatch is the topmost stopwatch on the stack
// for this event, `false` otherwise.
bool isTop_;
// Timestamps captured while starting the stopwatch.
uint64_t userTimeStart_;
uint64_t systemTimeStart_;
uint64_t CPOWTimeStart_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
}
bool
js::RunScript(JSContext* cx, RunState& state)
{
JS_CHECK_RECURSION(cx, return false);
#if defined(NIGHTLY_BUILD)
js::AutoStopwatch stopwatch(cx);
#endif // defined(NIGHTLY_BUILD)
SPSEntryMarker marker(cx->runtime(), state.script());
state.script()->ensureNonLazyCanonicalFunction(cx);

View File

@ -795,7 +795,7 @@ JS::SystemCompartmentCount(JSRuntime* rt)
{
size_t n = 0;
for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) {
if (comp->isSystem)
if (comp->isSystem())
++n;
}
return n;
@ -806,7 +806,7 @@ JS::UserCompartmentCount(JSRuntime* rt)
{
size_t n = 0;
for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) {
if (!comp->isSystem)
if (!comp->isSystem())
++n;
}
return n;

View File

@ -290,7 +290,7 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
gc.zones.append(atomsZone.get());
atomsZone->compartments.append(atomsCompartment.get());
atomsCompartment->isSystem = true;
atomsCompartment->setIsSystem(true);
atomsZone.forget();
this->atomsCompartment_ = atomsCompartment.forget();
@ -859,3 +859,93 @@ JS::IsProfilingEnabledForRuntime(JSRuntime* runtime)
MOZ_ASSERT(runtime);
return runtime->spsProfiler.enabled();
}
void
js::ResetStopwatches(JSRuntime *rt)
{
MOZ_ASSERT(rt);
rt->stopwatch.reset();
}
bool
js::SetStopwatchActive(JSRuntime *rt, bool isActive)
{
MOZ_ASSERT(rt);
return rt->stopwatch.setIsActive(isActive);
}
bool
js::IsStopwatchActive(JSRuntime *rt)
{
MOZ_ASSERT(rt);
return rt->stopwatch.isActive();
}
js::PerformanceGroupHolder::~PerformanceGroupHolder()
{
unlink();
}
void*
js::PerformanceGroupHolder::getHashKey()
{
return compartment_->isSystem() ?
(void*)compartment_->addonId :
(void*)JS_GetCompartmentPrincipals(compartment_);
// This key may be `nullptr` if we have `isSystem() == true`
// and `compartment_->addonId`. This is absolutely correct,
// and this represents the `PerformanceGroup` used to track
// the performance of the the platform compartments.
}
void
js::PerformanceGroupHolder::unlink()
{
if (!group_) {
// The group has never been instantiated.
return;
}
js::PerformanceGroup* group = group_;
group_ = nullptr;
if (group->decRefCount() > 0) {
// The group has at least another owner.
return;
}
JSRuntime::Stopwatch::Groups::Ptr ptr =
runtime_->stopwatch.groups_.lookup(getHashKey());
MOZ_ASSERT(ptr);
runtime_->stopwatch.groups_.remove(ptr);
js_delete(group);
}
PerformanceGroup *
js::PerformanceGroupHolder::getGroup()
{
if (group_)
return group_;
void* key = getHashKey();
JSRuntime::Stopwatch::Groups::AddPtr ptr =
runtime_->stopwatch.groups_.lookupForAdd(key);
if (ptr) {
group_ = ptr->value();
MOZ_ASSERT(group_);
} else {
group_ = runtime_->new_<PerformanceGroup>();
runtime_->stopwatch.groups_.add(ptr, key, group_);
}
group_->incRefCount();
return group_;
}
PerformanceData*
js::GetPerformanceData(JSRuntime *rt)
{
return &rt->stopwatch.performance;
}

View File

@ -567,6 +567,7 @@ class PerThreadData : public PerThreadDataFriendFields
class AutoLockForExclusiveAccess;
struct AutoStopwatch;
} // namespace js
struct JSRuntime : public JS::shadow::Runtime,
@ -1453,6 +1454,113 @@ struct JSRuntime : public JS::shadow::Runtime,
/* Last time at which an animation was played for this runtime. */
int64_t lastAnimationTime;
public:
/* ------------------------------------------
Performance measurements
------------------------------------------ */
struct Stopwatch {
/**
* The number of times we have entered the event loop.
* Used to reset counters whenever we enter the loop,
* which may be caused either by having completed the
* previous run of the event loop, or by entering a
* nested loop.
*
* Always incremented by 1, may safely overflow.
*/
uint64_t iteration;
/**
* `true` if no stopwatch has been registered for the
* current run of the event loop, `false` until then.
*/
bool isEmpty;
/**
* Performance data on the entire runtime.
*/
js::PerformanceData performance;
Stopwatch()
: iteration(0)
, isEmpty(true)
, isActive_(false)
{ }
/**
* Reset the stopwatch.
*
* This method is meant to be called whenever we start processing
* an event, to ensure that stop any ongoing measurement that would
* otherwise provide irrelevant results.
*/
void reset() {
++iteration;
isEmpty = true;
}
/**
* Activate/deactivate stopwatch measurement.
*
* Noop if `value` is `true` and the stopwatch is already active,
* or if `value` is `false` and the stopwatch is already inactive.
*
* Otherwise, any pending measurements are dropped, but previous
* measurements remain stored.
*
* May return `false` if the underlying hashtable cannot be allocated.
*/
bool setIsActive(bool value) {
if (isActive_ != value)
reset();
if (value && !groups_.initialized()) {
if (!groups_.init(128))
return false;
}
isActive_ = value;
return true;
}
/**
* `true` if the stopwatch is currently monitoring, `false` otherwise.
*/
bool isActive() const {
return isActive_;
}
private:
/**
* A map used to collapse compartments belonging to the same
* add-on (respectively to the same webpage, to the platform)
* into a single group.
*
* Keys: for system compartments, a `JSAddonId*` (which may be
* `nullptr`), and for webpages, a `JSPrincipals*` (which may
* not). Note that compartments may start as non-system
* compartments and become compartments later during their
* lifetime, which requires an invalidation.
*
* This map is meant to be accessed only by instances of
* PerformanceGroupHolder, which handle both reference-counting
* of the values and invalidation of the key/value pairs.
*/
typedef js::HashMap<void*, js::PerformanceGroup*,
js::DefaultHasher<void*>,
js::SystemAllocPolicy> Groups;
Groups groups_;
friend struct js::PerformanceGroupHolder;
/**
* `true` if stopwatch monitoring is active, `false` otherwise.
*/
bool isActive_;
};
Stopwatch stopwatch;
};
namespace js {

View File

@ -370,7 +370,7 @@ GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, bool& skippedAsync)
if (!subsumes)
return frame;
JSPrincipals* principals = cx->compartment()->principals;
JSPrincipals* principals = cx->compartment()->principals();
RootedSavedFrame rootedFrame(cx, frame);
while (rootedFrame && !subsumes(principals, rootedFrame->getPrincipals())) {
@ -475,8 +475,8 @@ public:
if (obj && cx->compartment() != obj->compartment())
{
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
if (subsumes && subsumes(cx->compartment()->principals,
obj->compartment()->principals))
if (subsumes && subsumes(cx->compartment()->principals(),
obj->compartment()->principals()))
{
ac_.emplace(cx, obj);
}
@ -649,7 +649,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp)
}
DebugOnly<JSSubsumesOp> subsumes = cx->runtime()->securityCallbacks->subsumes;
DebugOnly<JSPrincipals*> principals = cx->compartment()->principals;
DebugOnly<JSPrincipals*> principals = cx->compartment()->principals();
js::RootedSavedFrame parent(cx);
do {
@ -936,7 +936,7 @@ SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFram
iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr,
nullptr,
nullptr,
iter.compartment()->principals
iter.compartment()->principals()
);
++iter;

View File

@ -1133,7 +1133,7 @@ JSRuntime::createSelfHostingGlobal(JSContext* cx)
cx->runtime()->selfHostingGlobal_ = shg;
compartment->isSelfHosting = true;
compartment->isSystem = true;
compartment->setIsSystem(true);
if (!GlobalObject::initSelfHostingBuiltins(cx, shg, intrinsic_functions))
return nullptr;

View File

@ -252,7 +252,7 @@ uint8_t*
InterpreterStack::allocateFrame(JSContext* cx, size_t size)
{
size_t maxFrames;
if (cx->compartment()->principals == cx->runtime()->trustedPrincipals())
if (cx->compartment()->principals() == cx->runtime()->trustedPrincipals())
maxFrames = MAX_FRAMES_TRUSTED;
else
maxFrames = MAX_FRAMES;

View File

@ -530,7 +530,7 @@ FrameIter::settleOnActivation()
if (data_.principals_) {
JSContext* cx = data_.cx_->asJSContext();
if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) {
if (!subsumes(data_.principals_, activation->compartment()->principals)) {
if (!subsumes(data_.principals_, activation->compartment()->principals())) {
++data_.activations_;
continue;
}

View File

@ -348,13 +348,6 @@ xpc::TraceXPCGlobal(JSTracer* trc, JSObject* obj)
namespace xpc {
uint64_t
GetCompartmentCPOWMicroseconds(JSCompartment* compartment)
{
xpc::CompartmentPrivate* compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
return compartmentPrivate ? PR_IntervalToMicroseconds(compartmentPrivate->CPOWTime) : 0;
}
JSObject*
CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
JS::CompartmentOptions& aOptions)

View File

@ -627,6 +627,7 @@ public:
void OnProcessNextEvent() {
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
mSlowScriptSecondHalf = false;
js::ResetStopwatches(Get()->Runtime());
}
void OnAfterProcessNextEvent() {
mSlowScriptCheckpoint = mozilla::TimeStamp();
@ -3647,7 +3648,6 @@ public:
, skipWriteToGlobalPrototype(false)
, universalXPConnectEnabled(false)
, forcePermissiveCOWs(false)
, CPOWTime(0)
, skipCOWCallableChecks(false)
, scriptability(c)
, scope(nullptr)
@ -3701,9 +3701,6 @@ public:
// Using it in production is inherently unsafe.
bool forcePermissiveCOWs;
// A running count of how much time we've spent processing CPOWs.
PRIntervalTime CPOWTime;
// Disables the XPConnect security checks that deny access to callables and
// accessor descriptors on COWs. Do not use this unless you are bholley.
bool skipCOWCallableChecks;

View File

@ -902,6 +902,7 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
if (widget) {
nsIntRect widgetBounds;
widget->GetBounds(widgetBounds);
widgetBounds.MoveTo(0,0);
metrics.mCompositionBounds = ParentLayerRect(ViewAs<ParentLayerPixel>(widgetBounds));
#ifdef MOZ_WIDGET_ANDROID
if (frameBounds.height < metrics.mCompositionBounds.height) {

Some files were not shown because too many files have changed in this diff Show More