mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-11 10:08:41 +00:00
Merge inbound to m-c a=merge
This commit is contained in:
commit
1b666e0294
@ -1555,6 +1555,9 @@ nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
|
||||
if (aContext->IsList() &&
|
||||
aContext->GetContent() == aContent->GetParent()) {
|
||||
newAcc = new HTMLLIAccessible(aContent, document);
|
||||
} else {
|
||||
// Otherwise create a generic text accessible to avoid text jamming.
|
||||
newAcc = new HyperTextAccessibleWrap(aContent, document);
|
||||
}
|
||||
break;
|
||||
case eHTMLSelectListType:
|
||||
|
@ -34,10 +34,15 @@
|
||||
accTree = { // div@role="list"
|
||||
role: ROLE_LIST,
|
||||
children: [
|
||||
{ // li text leaf
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "item1",
|
||||
children: [ ]
|
||||
{ // li
|
||||
role: ROLE_PARAGRAPH,
|
||||
children: [
|
||||
{ // li text leaf
|
||||
role: ROLE_TEXT_LEAF,
|
||||
name: "item1",
|
||||
children: [ ]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ // li@role="listitem"
|
||||
role: ROLE_LISTITEM,
|
||||
|
@ -56,6 +56,15 @@
|
||||
] };
|
||||
testAccessibleTree("tblfocusable_cnt", tree);
|
||||
|
||||
// Presentation list, expose generic accesisble for list items.
|
||||
tree =
|
||||
{ SECTION: [ // container
|
||||
{ PARAGRAPH: [ // li generic accessible
|
||||
{ TEXT_LEAF: [ ] } // li text
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("list_cnt", tree);
|
||||
|
||||
// Has ARIA globals or referred by ARIA relationship.
|
||||
tree =
|
||||
{ SECTION: [ // container
|
||||
@ -113,6 +122,12 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="list_cnt">
|
||||
<ul role="presentation">
|
||||
<li>item</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="airaglobalprop_cnt">
|
||||
<label role="presentation" aria-owns="ariaowned">has aria-owns</label>
|
||||
<label role="presentation" id="ariaowned">referred by aria-owns</label>
|
||||
|
@ -80,15 +80,15 @@
|
||||
|
||||
ok(!isAccessible("presentation_ul"),
|
||||
"presentational ul shouldn't be accessible");
|
||||
ok(!isAccessible("item_in_presentation_ul"),
|
||||
"li in presentational ul shouldn't be accessible");
|
||||
ok(!isAccessible("styleditem_in_presentation_ul"),
|
||||
"list styled span in presentational ul shouldn't be accessible");
|
||||
ok(isAccessible("item_in_presentation_ul"),
|
||||
"li in presentational ul should have generic accessible");
|
||||
ok(isAccessible("styleditem_in_presentation_ul"),
|
||||
"list styled span in presentational ul should have generic accessible");
|
||||
|
||||
ok(!isAccessible("presentation_ol"),
|
||||
"presentational ol shouldn't be accessible");
|
||||
ok(!isAccessible("item_in_presentation_ol"),
|
||||
"li in presentational ol shouldn't be accessible");
|
||||
ok(isAccessible("item_in_presentation_ol"),
|
||||
"li in presentational ol should have generic accessible");
|
||||
|
||||
ok(!isAccessible("presentation_dl"),
|
||||
"presentational dl shouldn't be accessible");
|
||||
|
@ -181,7 +181,6 @@ class Automation(object):
|
||||
"log",
|
||||
"runApp",
|
||||
"Process",
|
||||
"addCommonOptions",
|
||||
"initializeProfile",
|
||||
"DIST_BIN",
|
||||
"DEFAULT_APP",
|
||||
@ -385,15 +384,6 @@ class Automation(object):
|
||||
proxy=proxy)
|
||||
return profile
|
||||
|
||||
def addCommonOptions(self, parser):
|
||||
"Adds command-line options which are common to mochitest and reftest."
|
||||
|
||||
parser.add_option("--setpref",
|
||||
action = "append", type = "string",
|
||||
default = [],
|
||||
dest = "extraPrefs", metavar = "PREF=VALUE",
|
||||
help = "defines an extra user preference")
|
||||
|
||||
def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath):
|
||||
pwfilePath = os.path.join(profileDir, ".crtdbpw")
|
||||
pwfile = open(pwfilePath, "w")
|
||||
|
@ -18,7 +18,6 @@ import mozinfo
|
||||
|
||||
__all__ = [
|
||||
"ZipFileReader",
|
||||
"addCommonOptions",
|
||||
"dumpLeakLog",
|
||||
"processLeakLog",
|
||||
'systemMemory',
|
||||
@ -140,30 +139,6 @@ def printstatus(status, name = ""):
|
||||
# This is probably a can't-happen condition on Unix, but let's be defensive
|
||||
print "TEST-INFO | %s: undecodable exit status %04x\n" % (name, status)
|
||||
|
||||
def addCommonOptions(parser, defaults={}):
|
||||
parser.add_option("--xre-path",
|
||||
action = "store", type = "string", dest = "xrePath",
|
||||
# individual scripts will set a sane default
|
||||
default = None,
|
||||
help = "absolute path to directory containing XRE (probably xulrunner)")
|
||||
if 'SYMBOLS_PATH' not in defaults:
|
||||
defaults['SYMBOLS_PATH'] = None
|
||||
parser.add_option("--symbols-path",
|
||||
action = "store", type = "string", dest = "symbolsPath",
|
||||
default = defaults['SYMBOLS_PATH'],
|
||||
help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
|
||||
parser.add_option("--debugger",
|
||||
action = "store", dest = "debugger",
|
||||
help = "use the given debugger to launch the application")
|
||||
parser.add_option("--debugger-args",
|
||||
action = "store", dest = "debuggerArgs",
|
||||
help = "pass the given args to the debugger _before_ "
|
||||
"the application on the command line")
|
||||
parser.add_option("--debugger-interactive",
|
||||
action = "store_true", dest = "debuggerInteractive",
|
||||
help = "prevents the test harness from redirecting "
|
||||
"stdout and stderr for interactive debuggers")
|
||||
|
||||
def dumpLeakLog(leakLogFile, filter = False):
|
||||
"""Process the leak log, without parsing it.
|
||||
|
||||
|
@ -138,6 +138,7 @@
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/VRDevice.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -3073,7 +3074,7 @@ GetFullScreenError(nsIDocument* aDoc)
|
||||
}
|
||||
|
||||
void
|
||||
Element::MozRequestFullScreen()
|
||||
Element::MozRequestFullScreen(const RequestFullscreenOptions& aOptions)
|
||||
{
|
||||
// Only grant full-screen requests if this is called from inside a trusted
|
||||
// event handler (i.e. inside an event handler for a user initiated event).
|
||||
@ -3097,7 +3098,12 @@ Element::MozRequestFullScreen()
|
||||
return;
|
||||
}
|
||||
|
||||
OwnerDoc()->AsyncRequestFullScreen(this);
|
||||
FullScreenOptions opts;
|
||||
if (aOptions.mVrDisplay) {
|
||||
opts.mVRHMDDevice = aOptions.mVrDisplay->GetHMD();
|
||||
}
|
||||
|
||||
OwnerDoc()->AsyncRequestFullScreen(this, opts);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "Units.h"
|
||||
|
||||
class nsIDOMEventListener;
|
||||
@ -724,7 +725,7 @@ public:
|
||||
nsIPresShell::SetCapturingContent(nullptr, 0);
|
||||
}
|
||||
}
|
||||
void MozRequestFullScreen();
|
||||
void MozRequestFullScreen(const RequestFullscreenOptions& aOptions);
|
||||
void MozRequestPointerLock();
|
||||
Attr* GetAttributeNode(const nsAString& aName);
|
||||
already_AddRefed<Attr> SetAttributeNode(Attr& aNewAttr,
|
||||
@ -1768,7 +1769,7 @@ NS_IMETHOD ReleaseCapture(void) MOZ_FINAL \
|
||||
} \
|
||||
NS_IMETHOD MozRequestFullScreen(void) MOZ_FINAL \
|
||||
{ \
|
||||
Element::MozRequestFullScreen(); \
|
||||
Element::MozRequestFullScreen(mozilla::dom::RequestFullscreenOptions()); \
|
||||
return NS_OK; \
|
||||
} \
|
||||
NS_IMETHOD MozRequestPointerLock(void) MOZ_FINAL \
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "mozilla/dom/Telephony.h"
|
||||
#include "mozilla/dom/Voicemail.h"
|
||||
#include "mozilla/dom/TVManager.h"
|
||||
#include "mozilla/dom/VRDevice.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "nsISiteSpecificUserAgent.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
@ -1722,6 +1723,32 @@ Navigator::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads,
|
||||
}
|
||||
#endif
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Navigator::GetVRDevices(ErrorResult& aRv)
|
||||
{
|
||||
if (!mWindow || !mWindow->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
|
||||
nsRefPtr<Promise> p = Promise::Create(go, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(mWindow.get());
|
||||
|
||||
nsTArray<nsRefPtr<VRDevice>> vrDevs;
|
||||
if (!win->GetVRDevices(vrDevs)) {
|
||||
p->MaybeReject(NS_ERROR_FAILURE);
|
||||
} else {
|
||||
p->MaybeResolve(vrDevs);
|
||||
}
|
||||
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// Navigator::nsIMozNavigatorNetwork
|
||||
//*****************************************************************************
|
||||
|
@ -246,6 +246,7 @@ public:
|
||||
#ifdef MOZ_GAMEPAD
|
||||
void GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
|
||||
#endif // MOZ_GAMEPAD
|
||||
already_AddRefed<Promise> GetVRDevices(ErrorResult& aRv);
|
||||
#ifdef MOZ_B2G_FM
|
||||
FMRadio* GetMozFMRadio(ErrorResult& aRv);
|
||||
#endif
|
||||
|
@ -10730,13 +10730,13 @@ nsIDocument::MozCancelFullScreen()
|
||||
// Element::UnbindFromTree().
|
||||
class nsSetWindowFullScreen : public nsRunnable {
|
||||
public:
|
||||
nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
|
||||
: mDoc(aDoc), mValue(aValue) {}
|
||||
nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo* aHMD = nullptr)
|
||||
: mDoc(aDoc), mValue(aValue), mHMD(aHMD) {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mDoc->GetWindow()) {
|
||||
mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
|
||||
mDoc->GetWindow()->SetFullScreenInternal(mValue, false, mHMD);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -10744,6 +10744,7 @@ public:
|
||||
private:
|
||||
nsCOMPtr<nsIDocument> mDoc;
|
||||
bool mValue;
|
||||
nsRefPtr<gfx::VRHMDInfo> mHMD;
|
||||
};
|
||||
|
||||
static nsIDocument*
|
||||
@ -10763,7 +10764,7 @@ GetFullscreenRootDocument(nsIDocument* aDoc)
|
||||
}
|
||||
|
||||
static void
|
||||
SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
|
||||
SetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo *aVRHMD = nullptr)
|
||||
{
|
||||
// Maintain list of fullscreen root documents.
|
||||
nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc);
|
||||
@ -10773,7 +10774,7 @@ SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
|
||||
FullscreenRoots::Remove(root);
|
||||
}
|
||||
if (!nsContentUtils::IsFullscreenApiContentOnly()) {
|
||||
nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
|
||||
nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue, aVRHMD));
|
||||
}
|
||||
}
|
||||
|
||||
@ -11093,12 +11094,13 @@ nsDocument::IsFullScreenDoc()
|
||||
class nsCallRequestFullScreen : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit nsCallRequestFullScreen(Element* aElement)
|
||||
explicit nsCallRequestFullScreen(Element* aElement, FullScreenOptions& aOptions)
|
||||
: mElement(aElement),
|
||||
mDoc(aElement->OwnerDoc()),
|
||||
mWasCallerChrome(nsContentUtils::IsCallerChrome()),
|
||||
mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
|
||||
mAsyncFullscreenPending)
|
||||
mAsyncFullscreenPending),
|
||||
mOptions(aOptions)
|
||||
{
|
||||
static_cast<nsDocument*>(mDoc.get())->
|
||||
mAsyncFullscreenPending = true;
|
||||
@ -11110,6 +11112,7 @@ public:
|
||||
mAsyncFullscreenPending = mHadRequestPending;
|
||||
nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
|
||||
doc->RequestFullScreen(mElement,
|
||||
mOptions,
|
||||
mWasCallerChrome,
|
||||
/* aNotifyOnOriginChange */ true);
|
||||
return NS_OK;
|
||||
@ -11119,10 +11122,12 @@ public:
|
||||
nsCOMPtr<nsIDocument> mDoc;
|
||||
bool mWasCallerChrome;
|
||||
bool mHadRequestPending;
|
||||
FullScreenOptions mOptions;
|
||||
};
|
||||
|
||||
void
|
||||
nsDocument::AsyncRequestFullScreen(Element* aElement)
|
||||
nsDocument::AsyncRequestFullScreen(Element* aElement,
|
||||
FullScreenOptions& aOptions)
|
||||
{
|
||||
NS_ASSERTION(aElement,
|
||||
"Must pass non-null element to nsDocument::AsyncRequestFullScreen");
|
||||
@ -11130,7 +11135,7 @@ nsDocument::AsyncRequestFullScreen(Element* aElement)
|
||||
return;
|
||||
}
|
||||
// Request full-screen asynchronously.
|
||||
nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement));
|
||||
nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement, aOptions));
|
||||
NS_DispatchToCurrentThread(event);
|
||||
}
|
||||
|
||||
@ -11198,6 +11203,9 @@ nsDocument::CleanupFullscreenState()
|
||||
Element* top = FullScreenStackTop();
|
||||
NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
|
||||
if (top) {
|
||||
// Remove any VR state properties
|
||||
top->DeleteProperty(nsGkAtoms::vr_state);
|
||||
|
||||
EventStateManager::SetFullScreenState(top, false);
|
||||
}
|
||||
mFullScreenStack.Clear();
|
||||
@ -11234,8 +11242,12 @@ nsDocument::FullScreenStackPop()
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove styles from existing top element.
|
||||
Element* top = FullScreenStackTop();
|
||||
|
||||
// Remove any VR state properties
|
||||
top->DeleteProperty(nsGkAtoms::vr_state);
|
||||
|
||||
// Remove styles from existing top element.
|
||||
EventStateManager::SetFullScreenState(top, false);
|
||||
|
||||
// Remove top element. Note the remaining top element in the stack
|
||||
@ -11323,7 +11335,9 @@ nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
|
||||
// If the frame element is already the fullscreen element in this document,
|
||||
// this has no effect.
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
|
||||
FullScreenOptions opts;
|
||||
RequestFullScreen(content->AsElement(),
|
||||
opts,
|
||||
/* aWasCallerChrome */ false,
|
||||
/* aNotifyOnOriginChange */ false);
|
||||
|
||||
@ -11349,8 +11363,17 @@ nsresult nsDocument::RemoteFrameFullscreenReverted()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
|
||||
{
|
||||
if (aPropertyValue) {
|
||||
static_cast<gfx::VRHMDInfo*>(aPropertyValue)->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RequestFullScreen(Element* aElement,
|
||||
FullScreenOptions& aOptions,
|
||||
bool aWasCallerChrome,
|
||||
bool aNotifyOnOriginChange)
|
||||
{
|
||||
@ -11439,6 +11462,14 @@ nsDocument::RequestFullScreen(Element* aElement,
|
||||
UnlockPointer();
|
||||
}
|
||||
|
||||
// Process options -- in this case, just HMD
|
||||
if (aOptions.mVRHMDDevice) {
|
||||
nsRefPtr<gfx::VRHMDInfo> hmdRef = aOptions.mVRHMDDevice;
|
||||
aElement->SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
|
||||
ReleaseHMDInfoRef,
|
||||
true);
|
||||
}
|
||||
|
||||
// Set the full-screen element. This sets the full-screen style on the
|
||||
// element, and the full-screen-ancestor styles on ancestors of the element
|
||||
// in this document.
|
||||
@ -11543,7 +11574,7 @@ nsDocument::RequestFullScreen(Element* aElement,
|
||||
// modes. Also note that nsGlobalWindow::SetFullScreen() (which
|
||||
// SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
|
||||
// and does not operate on the a per-nsIDOMWindow basis.
|
||||
SetWindowFullScreen(this, true);
|
||||
SetWindowFullScreen(this, true, aOptions.mVRHMDDevice);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1146,7 +1146,8 @@ public:
|
||||
virtual Element* FindImageMap(const nsAString& aNormalizedMapName) MOZ_OVERRIDE;
|
||||
|
||||
virtual Element* GetFullScreenElement() MOZ_OVERRIDE;
|
||||
virtual void AsyncRequestFullScreen(Element* aElement) MOZ_OVERRIDE;
|
||||
virtual void AsyncRequestFullScreen(Element* aElement,
|
||||
mozilla::dom::FullScreenOptions& aOptions) MOZ_OVERRIDE;
|
||||
virtual void RestorePreviousFullScreenState() MOZ_OVERRIDE;
|
||||
virtual bool IsFullscreenLeaf() MOZ_OVERRIDE;
|
||||
virtual bool IsFullScreenDoc() MOZ_OVERRIDE;
|
||||
@ -1197,6 +1198,7 @@ public:
|
||||
// need to send the notification with the origin of the document which
|
||||
// originally requested fullscreen, not *this* document's origin.
|
||||
void RequestFullScreen(Element* aElement,
|
||||
mozilla::dom::FullScreenOptions& aOptions,
|
||||
bool aWasCallerChrome,
|
||||
bool aNotifyOnOriginChange);
|
||||
|
||||
|
@ -2325,3 +2325,5 @@ GK_ATOM(onresume, "onresume")
|
||||
GK_ATOM(onmark, "onmark")
|
||||
GK_ATOM(onboundary, "onboundary")
|
||||
#endif
|
||||
|
||||
GK_ATOM(vr_state, "vr-state")
|
||||
|
@ -190,6 +190,8 @@
|
||||
#include "mozilla/dom/GamepadService.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/VRDevice.h"
|
||||
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
#include "mozilla/dom/SelectionChangeEvent.h"
|
||||
@ -1134,7 +1136,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
||||
mCleanedUp(false),
|
||||
mDialogAbuseCount(0),
|
||||
mAreDialogsEnabled(true),
|
||||
mCanSkipCCGeneration(0)
|
||||
mCanSkipCCGeneration(0),
|
||||
mVRDevicesInitialized(false)
|
||||
{
|
||||
nsLayoutStatics::AddRef();
|
||||
|
||||
@ -1771,6 +1774,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
|
||||
#endif
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDevices)
|
||||
|
||||
// Traverse stuff from nsPIDOMWindow
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
|
||||
@ -1832,6 +1837,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
|
||||
#endif
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDevices)
|
||||
|
||||
// Unlink stuff from nsPIDOMWindow
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
|
||||
@ -5923,7 +5930,7 @@ nsGlobalWindow::SetFullScreen(bool aFullScreen)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
|
||||
nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust, gfx::VRHMDInfo* aHMD)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -5945,7 +5952,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
|
||||
if (!window)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (rootItem != mDocShell)
|
||||
return window->SetFullScreenInternal(aFullScreen, aRequireTrust);
|
||||
return window->SetFullScreenInternal(aFullScreen, aRequireTrust, aHMD);
|
||||
|
||||
// make sure we don't try to set full screen on a non-chrome window,
|
||||
// which might happen in embedding world
|
||||
@ -5980,8 +5987,13 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
|
||||
// want the content to fill the entire client area of the emulator window.
|
||||
if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) {
|
||||
nsCOMPtr<nsIWidget> widget = GetMainWidget();
|
||||
if (widget)
|
||||
widget->MakeFullScreen(aFullScreen);
|
||||
if (widget) {
|
||||
nsCOMPtr<nsIScreen> screen;
|
||||
if (aHMD) {
|
||||
screen = aHMD->GetScreen();
|
||||
}
|
||||
widget->MakeFullScreen(aFullScreen, screen);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mFullScreen) {
|
||||
@ -13416,7 +13428,26 @@ nsGlobalWindow::SyncGamepadState()
|
||||
mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // MOZ_GAMEPAD
|
||||
|
||||
bool
|
||||
nsGlobalWindow::GetVRDevices(nsTArray<nsRefPtr<mozilla::dom::VRDevice>>& aDevices)
|
||||
{
|
||||
FORWARD_TO_INNER(GetVRDevices, (aDevices), false);
|
||||
|
||||
if (!mVRDevicesInitialized) {
|
||||
bool ok = mozilla::dom::VRDevice::CreateAllKnownVRDevices(ToSupports(this), mVRDevices);
|
||||
if (!ok) {
|
||||
mVRDevices.Clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mVRDevicesInitialized = true;
|
||||
aDevices = mVRDevices;
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsGlobalChromeWindow implementation
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
|
||||
|
@ -104,6 +104,7 @@ class Console;
|
||||
class External;
|
||||
class Function;
|
||||
class Gamepad;
|
||||
class VRDevice;
|
||||
class MediaQueryList;
|
||||
class MozSelfSupport;
|
||||
class Navigator;
|
||||
@ -118,6 +119,9 @@ namespace indexedDB {
|
||||
class IDBFactory;
|
||||
} // namespace indexedDB
|
||||
} // namespace dom
|
||||
namespace gfx {
|
||||
class VRHMDInfo;
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
extern nsresult
|
||||
@ -464,7 +468,8 @@ public:
|
||||
virtual void RefreshCompartmentPrincipal();
|
||||
|
||||
// Outer windows only.
|
||||
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust);
|
||||
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust,
|
||||
mozilla::gfx::VRHMDInfo *aHMD = nullptr);
|
||||
bool FullScreen() const;
|
||||
|
||||
// Inner windows only.
|
||||
@ -734,6 +739,8 @@ public:
|
||||
void EnableGamepadUpdates();
|
||||
void DisableGamepadUpdates();
|
||||
|
||||
// Get the VR devices for this window, initializing if necessary
|
||||
bool GetVRDevices(nsTArray<nsRefPtr<mozilla::dom::VRDevice>>& aDevices);
|
||||
|
||||
#define EVENT(name_, id_, type_, struct_) \
|
||||
mozilla::dom::EventHandlerNonNull* GetOn##name_() \
|
||||
@ -1630,6 +1637,13 @@ protected:
|
||||
// This is the CC generation the last time we called CanSkip.
|
||||
uint32_t mCanSkipCCGeneration;
|
||||
|
||||
// Did VR get initialized for this window?
|
||||
bool mVRDevicesInitialized;
|
||||
// The VRDevies for this window
|
||||
nsTArray<nsRefPtr<mozilla::dom::VRDevice>> mVRDevices;
|
||||
// Any attached HMD when fullscreen
|
||||
nsRefPtr<mozilla::gfx::VRHMDInfo> mVRHMDInfo;
|
||||
|
||||
friend class nsDOMScriptableHelper;
|
||||
friend class nsDOMWindowUtils;
|
||||
friend class PostMessageEvent;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "nsExpirationTracker.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "prclist.h"
|
||||
#include "gfxVR.h"
|
||||
|
||||
class imgIRequest;
|
||||
class nsAString;
|
||||
@ -135,6 +136,12 @@ template<typename> class Sequence;
|
||||
|
||||
template<typename, typename> class CallbackObjectHolder;
|
||||
typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
|
||||
|
||||
struct FullScreenOptions {
|
||||
FullScreenOptions() { }
|
||||
nsRefPtr<gfx::VRHMDInfo> mVRHMDDevice;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@ -164,6 +171,7 @@ already_AddRefed<nsContentList>
|
||||
NS_GetContentList(nsINode* aRootNode,
|
||||
int32_t aMatchNameSpaceId,
|
||||
const nsAString& aTagname);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Document interface. This is implemented by all document objects in
|
||||
@ -1064,7 +1072,8 @@ public:
|
||||
* the <iframe> or <browser> that contains this document is also mode
|
||||
* fullscreen. This happens recursively in all ancestor documents.
|
||||
*/
|
||||
virtual void AsyncRequestFullScreen(Element* aElement) = 0;
|
||||
virtual void AsyncRequestFullScreen(Element* aElement,
|
||||
mozilla::dom::FullScreenOptions& aOptions) = 0;
|
||||
|
||||
/**
|
||||
* Called when a frame in a child process has entered fullscreen or when a
|
||||
|
@ -38,6 +38,9 @@ namespace dom {
|
||||
class AudioContext;
|
||||
class Element;
|
||||
}
|
||||
namespace gfx {
|
||||
class VRHMDInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Popup control state enum. The values in this enum must go from most
|
||||
@ -60,8 +63,8 @@ enum UIStateChangeType
|
||||
};
|
||||
|
||||
#define NS_PIDOMWINDOW_IID \
|
||||
{ 0x71412748, 0x6368, 0x4332, \
|
||||
{ 0x82, 0x66, 0xff, 0xaa, 0x19, 0xda, 0x09, 0x7c } }
|
||||
{ 0x19fb3019, 0x7b5d, 0x4235, \
|
||||
{ 0xa9, 0x59, 0xa2, 0x31, 0xa2, 0xe7, 0x94, 0x79 } }
|
||||
|
||||
class nsPIDOMWindow : public nsIDOMWindowInternal
|
||||
{
|
||||
@ -460,9 +463,13 @@ public:
|
||||
* otherwise exits fullscreen. If aRequireTrust is true, this method only
|
||||
* changes window state in a context trusted for write.
|
||||
*
|
||||
* If aHMD is not null, the window is made full screen on the given VR HMD
|
||||
* device instead of its currrent display.
|
||||
*
|
||||
* Outer windows only.
|
||||
*/
|
||||
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust) = 0;
|
||||
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust,
|
||||
mozilla::gfx::VRHMDInfo *aHMD = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Call this to check whether some node (this window, its document,
|
||||
|
@ -1272,6 +1272,14 @@ DOMInterfaces = {
|
||||
'wrapperCache': False,
|
||||
}],
|
||||
|
||||
'VRFieldOfViewReadOnly': {
|
||||
'concrete': False
|
||||
},
|
||||
|
||||
'VRDevice': {
|
||||
'concrete': False
|
||||
},
|
||||
|
||||
'VTTCue': {
|
||||
'nativeType': 'mozilla::dom::TextTrackCue'
|
||||
},
|
||||
|
16
dom/events/test/bug1096146_embedded.html
Normal file
16
dom/events/test/bug1096146_embedded.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Embedded iframe</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body onload="">
|
||||
<p id="display"></p>
|
||||
<h1>Top</h1>
|
||||
<input id="input" style="display: block;">
|
||||
<pre id="test">
|
||||
<div id="content" style="height: 2000px;"></div>
|
||||
<h1>Bottom</h1>
|
||||
</body>
|
||||
</html>
|
@ -176,3 +176,6 @@ support-files =
|
||||
bug989198_embedded.html
|
||||
bug989198_helper.js
|
||||
skip-if = buildapp == 'b2g' || e10s
|
||||
[test_bug1096146.html]
|
||||
support-files =
|
||||
bug1096146_embedded.html
|
||||
|
170
dom/events/test/test_bug1096146.html
Normal file
170
dom/events/test/test_bug1096146.html
Normal file
@ -0,0 +1,170 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1096146
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1096146</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body onload="runTests();">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1096146">Mozilla Bug 1096146</a>
|
||||
<div id="content" style="display: none;">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const kKeydownEvent = 0x1;
|
||||
const kScrollEvent = 0x2;
|
||||
|
||||
var gCurrentTest = 0;
|
||||
var kTests = [
|
||||
{
|
||||
description: "no preventDefault at 'mozbrowserbeforekeydown'",
|
||||
expectedEvents: kKeydownEvent | kScrollEvent,
|
||||
resultEvents: 0x0,
|
||||
doPreventDefault: false
|
||||
},
|
||||
{
|
||||
description: "do preventDefault at 'mozbrowserbeforekeydown'",
|
||||
expectedEvents: 0x0,
|
||||
resultEvents: 0x0,
|
||||
doPreventDefault: true
|
||||
}
|
||||
]
|
||||
|
||||
function frameScript()
|
||||
{
|
||||
function handler(e) {
|
||||
sendSyncMessage("forwardevent", { type: e.type });
|
||||
}
|
||||
addEventListener('keydown', handler);
|
||||
addEventListener('scroll', handler);
|
||||
}
|
||||
|
||||
function testDefaultAction()
|
||||
{
|
||||
synthesizeKey('VK_END', {}, document.getElementById("embedded").contentWindow);
|
||||
setTimeout(function () {
|
||||
is(kTests[gCurrentTest].expectedEvents,
|
||||
kTests[gCurrentTest].resultEvents,
|
||||
"verify result");
|
||||
runTests();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function prepareTest()
|
||||
{
|
||||
var handler;
|
||||
if (kTests[gCurrentTest].doPreventDefault) {
|
||||
handler = preventDefaultHandler;
|
||||
} else {
|
||||
handler = noPreventDefaultHandler;
|
||||
}
|
||||
window.addEventListener("mozbrowserbeforekeydown", handler);
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.id = "embedded";
|
||||
iframe.src = "bug1096146_embedded.html";
|
||||
iframe.setAttribute("remote", "false");
|
||||
SpecialPowers.wrap(iframe).mozbrowser = true;
|
||||
|
||||
iframe.addEventListener("mozbrowserloadend", function onloadend() {
|
||||
iframe.removeEventListener("mozbrowserloadend", onloadend);
|
||||
iframe.focus();
|
||||
var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
mm.addMessageListener("forwardevent", function(msg) {
|
||||
var value = 0;
|
||||
switch(msg.json.type) {
|
||||
case "scroll":
|
||||
value = kScrollEvent;
|
||||
break;
|
||||
case "keydown":
|
||||
value = kKeydownEvent;
|
||||
break;
|
||||
default:
|
||||
ok(false, "unexpected event");
|
||||
}
|
||||
|
||||
kTests[gCurrentTest].resultEvents =
|
||||
kTests[gCurrentTest].resultEvents | value;
|
||||
});
|
||||
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
|
||||
runTests();
|
||||
return;
|
||||
});
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
function preventDefaultHandler(evt)
|
||||
{
|
||||
ok(true, "receive " + evt.type + " and do preventDefault.");
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
function noPreventDefaultHandler(evt)
|
||||
{
|
||||
ok(true, "receive " + evt.type + ".");
|
||||
}
|
||||
|
||||
function teardownHandler()
|
||||
{
|
||||
var handler;
|
||||
if (kTests[gCurrentTest].doPreventDefault) {
|
||||
handler = preventDefaultHandler;
|
||||
} else {
|
||||
handler = noPreventDefaultHandler;
|
||||
}
|
||||
window.removeEventListener("mozbrowserbeforekeydown", handler);
|
||||
|
||||
runTests();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function addPermissions() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ type: "before-after-keyboard-event", allow: true, context: document },
|
||||
{ type: "browser", allow: true, context: document }],
|
||||
runTests);
|
||||
},
|
||||
function addPreferences() {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["dom.ipc.tabs.disabled", false]] },
|
||||
runTests);
|
||||
},
|
||||
|
||||
prepareTest,
|
||||
testDefaultAction,
|
||||
teardownHandler,
|
||||
|
||||
function() {
|
||||
gCurrentTest++;
|
||||
runTests();
|
||||
},
|
||||
prepareTest,
|
||||
testDefaultAction,
|
||||
teardownHandler
|
||||
];
|
||||
|
||||
function runTests()
|
||||
{
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -5347,6 +5347,13 @@ public:
|
||||
void
|
||||
CloseOnOwningThread();
|
||||
|
||||
void
|
||||
AssertInvalidatedOnMainThread() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mInvalidatedOnMainThread);
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
private:
|
||||
@ -9514,7 +9521,9 @@ QuotaClient::AbortTransactionsForStorage(nsIOfflineStorage* aStorage)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aStorage);
|
||||
MOZ_ASSERT(aStorage->GetClient() == this);
|
||||
MOZ_ASSERT(aStorage->IsClosed());
|
||||
|
||||
static_cast<DatabaseOfflineStorage*>(aStorage)->
|
||||
AssertInvalidatedOnMainThread();
|
||||
|
||||
// Nothing to do here, calling DatabaseOfflineStorage::Close() should have
|
||||
// aborted any transactions already.
|
||||
@ -9909,8 +9918,6 @@ DatabaseOfflineStorage::InvalidateOnMainThread()
|
||||
nsCOMPtr<nsIEventTarget> owningThread = mOwningThread;
|
||||
MOZ_ASSERT(owningThread);
|
||||
|
||||
CloseOnMainThread();
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(owningThread->Dispatch(runnable,
|
||||
NS_DISPATCH_NORMAL)));
|
||||
}
|
||||
|
@ -750,6 +750,39 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class TabChild::DelayedDeleteRunnable MOZ_FINAL
|
||||
: public nsRunnable
|
||||
{
|
||||
nsRefPtr<TabChild> mTabChild;
|
||||
|
||||
public:
|
||||
DelayedDeleteRunnable(TabChild* aTabChild)
|
||||
: mTabChild(aTabChild)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aTabChild);
|
||||
}
|
||||
|
||||
private:
|
||||
~DelayedDeleteRunnable()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mTabChild);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mTabChild);
|
||||
|
||||
unused << PBrowserChild::Send__delete__(mTabChild);
|
||||
|
||||
mTabChild = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
StaticRefPtr<TabChild> sPreallocatedTab;
|
||||
|
||||
/*static*/
|
||||
@ -2748,7 +2781,12 @@ TabChild::RecvDestroy()
|
||||
// XXX what other code in ~TabChild() should we be running here?
|
||||
DestroyWindow();
|
||||
|
||||
return Send__delete__(this);
|
||||
// Bounce through the event loop once to allow any delayed teardown runnables
|
||||
// that were just generated to have a chance to run.
|
||||
nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -583,6 +583,7 @@ private:
|
||||
|
||||
class CachedFileDescriptorInfo;
|
||||
class CachedFileDescriptorCallbackRunnable;
|
||||
class DelayedDeleteRunnable;
|
||||
|
||||
TextureFactoryIdentifier mTextureFactoryIdentifier;
|
||||
nsCOMPtr<nsIWebNavigation> mWebNav;
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
nsRefPtr<TrackChange> runnable =
|
||||
new TrackChange(this, aID, aTrackOffset, aTrackEvents,
|
||||
aQueuedMedia.GetType());
|
||||
NS_DispatchToMainThread(runnable);
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1136,7 +1136,7 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate);
|
||||
NS_DispatchToMainThread(event);
|
||||
DispatchToMainThreadAfterStreamStateUpdate(event.forget());
|
||||
}
|
||||
if (!aStream->mNotifiedFinished) {
|
||||
aStream->mLastPlayedVideoFrame = *frame;
|
||||
|
@ -81,8 +81,8 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) :
|
||||
mappedTracksFinished.AppendElement(true);
|
||||
mappedTracksWithMatchingInputTracks.AppendElement(false);
|
||||
}
|
||||
bool allFinished = true;
|
||||
bool allHaveCurrentData = true;
|
||||
bool allFinished = !mInputs.IsEmpty();
|
||||
bool allHaveCurrentData = !mInputs.IsEmpty();
|
||||
for (uint32_t i = 0; i < mInputs.Length(); ++i) {
|
||||
MediaStream* stream = mInputs[i]->GetSource();
|
||||
if (!stream->IsFinishedOnGraphThread()) {
|
||||
|
@ -105,6 +105,7 @@ DIRS += [
|
||||
'xul',
|
||||
'resourcestats',
|
||||
'manifest',
|
||||
'vr',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
|
@ -441,6 +441,8 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "Headers", pref: "dom.fetch.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"History",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "HMDVRDevice", pref: "dom.vr.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"HTMLAllCollection",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
@ -879,6 +881,8 @@ var interfaceNamesInGlobalScope =
|
||||
"PopupBlockedEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "PopupBoxObject", xbl: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "PositionSensorVRDevice", pref: "dom.vr.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ProcessingInstruction",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
@ -1291,6 +1295,14 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "VideoPlaybackQuality", pref: "media.mediasource.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"VideoStreamTrack",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "VRDevice", pref: "dom.vr.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "VRPositionState", pref: "dom.vr.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "VRFieldOfView", pref: "dom.vr.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "VRFieldOfViewReadOnly", pref: "dom.vr.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"VTTCue",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
322
dom/vr/VRDevice.cpp
Normal file
322
dom/vr/VRDevice.cpp
Normal file
@ -0,0 +1,322 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/VRDeviceBinding.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/VRDevice.h"
|
||||
#include "gfxVR.h"
|
||||
#include "nsIFrame.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfView, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfView, Release)
|
||||
|
||||
already_AddRefed<VRFieldOfView>
|
||||
VRFieldOfView::Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
|
||||
ErrorResult& aRV)
|
||||
{
|
||||
nsRefPtr<VRFieldOfView> obj =
|
||||
new VRFieldOfView(aGlobal.GetAsSupports(),
|
||||
aParams.mUpDegrees, aParams.mRightDegrees,
|
||||
aParams.mDownDegrees, aParams.mLeftDegrees);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<VRFieldOfView>
|
||||
VRFieldOfView::Constructor(const GlobalObject& aGlobal,
|
||||
double aUpDegrees, double aRightDegrees,
|
||||
double aDownDegrees, double aLeftDegrees,
|
||||
ErrorResult& aRV)
|
||||
{
|
||||
nsRefPtr<VRFieldOfView> obj =
|
||||
new VRFieldOfView(aGlobal.GetAsSupports(),
|
||||
aUpDegrees, aRightDegrees,
|
||||
aDownDegrees, aLeftDegrees);
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
VRFieldOfView::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return VRFieldOfViewBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRPositionState, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRPositionState, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRPositionState, Release)
|
||||
|
||||
VRPositionState::VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState)
|
||||
: mParent(aParent)
|
||||
, mVRState(aState)
|
||||
{
|
||||
mTimeStamp = aState.timestamp;
|
||||
|
||||
if (aState.flags & gfx::VRHMDInfo::State_Position) {
|
||||
mPosition = new DOMPoint(mParent, aState.position[0], aState.position[1], aState.position[2], 0.0);
|
||||
}
|
||||
|
||||
if (aState.flags & gfx::VRHMDInfo::State_Orientation) {
|
||||
mOrientation = new DOMPoint(mParent, aState.orientation[0], aState.orientation[1], aState.orientation[2], aState.orientation[3]);
|
||||
}
|
||||
}
|
||||
|
||||
DOMPoint*
|
||||
VRPositionState::GetLinearVelocity()
|
||||
{
|
||||
if (!mLinearVelocity) {
|
||||
mLinearVelocity = new DOMPoint(mParent, mVRState.linearVelocity[0], mVRState.linearVelocity[1], mVRState.linearVelocity[2], 0.0);
|
||||
}
|
||||
return mLinearVelocity;
|
||||
}
|
||||
|
||||
DOMPoint*
|
||||
VRPositionState::GetLinearAcceleration()
|
||||
{
|
||||
if (!mLinearAcceleration) {
|
||||
mLinearAcceleration = new DOMPoint(mParent, mVRState.linearAcceleration[0], mVRState.linearAcceleration[1], mVRState.linearAcceleration[2], 0.0);
|
||||
}
|
||||
return mLinearAcceleration;
|
||||
}
|
||||
|
||||
DOMPoint*
|
||||
VRPositionState::GetAngularVelocity()
|
||||
{
|
||||
if (!mAngularVelocity) {
|
||||
mAngularVelocity = new DOMPoint(mParent, mVRState.angularVelocity[0], mVRState.angularVelocity[1], mVRState.angularVelocity[2], 0.0);
|
||||
}
|
||||
return mAngularVelocity;
|
||||
}
|
||||
|
||||
DOMPoint*
|
||||
VRPositionState::GetAngularAcceleration()
|
||||
{
|
||||
if (!mAngularAcceleration) {
|
||||
mAngularAcceleration = new DOMPoint(mParent, mVRState.angularAcceleration[0], mVRState.angularAcceleration[1], mVRState.angularAcceleration[2], 0.0);
|
||||
}
|
||||
return mAngularAcceleration;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
VRPositionState::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return VRPositionStateBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(VRDevice)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(VRDevice)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VRDevice)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDevice, mParent)
|
||||
|
||||
/* virtual */ JSObject*
|
||||
HMDVRDevice::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return HMDVRDeviceBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
PositionSensorVRDevice::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return PositionSensorVRDeviceBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
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:
|
||||
HMDInfoVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
|
||||
: HMDVRDevice(aParent, aHMD)
|
||||
{
|
||||
// XXX TODO use real names/IDs
|
||||
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
|
||||
mDeviceId.AssignLiteral("somedevid");
|
||||
mDeviceName.AssignLiteral("HMD Device");
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
virtual ~HMDInfoVRDevice() { }
|
||||
|
||||
/* If a field of view that is set to all 0's is passed in,
|
||||
* the recommended field of view for that eye is used.
|
||||
*/
|
||||
virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
|
||||
const VRFieldOfViewInit& aRightFOV,
|
||||
double zNear, double zFar) MOZ_OVERRIDE
|
||||
{
|
||||
gfx::VRFieldOfView left = gfx::VRFieldOfView(aLeftFOV.mUpDegrees, aLeftFOV.mRightDegrees,
|
||||
aLeftFOV.mDownDegrees, aLeftFOV.mLeftDegrees);
|
||||
gfx::VRFieldOfView right = gfx::VRFieldOfView(aRightFOV.mUpDegrees, aRightFOV.mRightDegrees,
|
||||
aRightFOV.mDownDegrees, aRightFOV.mLeftDegrees);
|
||||
|
||||
if (left.IsZero())
|
||||
left = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Left);
|
||||
if (right.IsZero())
|
||||
right = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Right);
|
||||
|
||||
mHMD->SetFOV(left, right, zNear, zFar);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) MOZ_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 already_AddRefed<VRFieldOfView> GetCurrentEyeFieldOfView(VREye aEye) MOZ_OVERRIDE
|
||||
{
|
||||
return CopyFieldOfView(mHMD->GetEyeFOV(EyeToEye(aEye)));
|
||||
}
|
||||
|
||||
virtual already_AddRefed<VRFieldOfView> GetRecommendedEyeFieldOfView(VREye aEye) MOZ_OVERRIDE
|
||||
{
|
||||
return CopyFieldOfView(mHMD->GetRecommendedEyeFOV(EyeToEye(aEye)));
|
||||
}
|
||||
|
||||
virtual already_AddRefed<VRFieldOfView> GetMaximumEyeFieldOfView(VREye aEye) MOZ_OVERRIDE
|
||||
{
|
||||
return CopyFieldOfView(mHMD->GetMaximumEyeFOV(EyeToEye(aEye)));
|
||||
}
|
||||
|
||||
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) MOZ_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();
|
||||
}
|
||||
|
||||
protected:
|
||||
already_AddRefed<VRFieldOfView>
|
||||
CopyFieldOfView(const gfx::VRFieldOfView& aSrc)
|
||||
{
|
||||
nsRefPtr<VRFieldOfView> obj =
|
||||
new VRFieldOfView(mParent, aSrc.upDegrees, aSrc.rightDegrees,
|
||||
aSrc.downDegrees, aSrc.leftDegrees);
|
||||
return obj.forget();
|
||||
}
|
||||
};
|
||||
|
||||
class HMDPositionVRDevice : public PositionSensorVRDevice
|
||||
{
|
||||
public:
|
||||
HMDPositionVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
|
||||
: PositionSensorVRDevice(aParent)
|
||||
, mHMD(aHMD)
|
||||
, mTracking(false)
|
||||
{
|
||||
// XXX TODO use real names/IDs
|
||||
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
|
||||
mDeviceId.AssignLiteral("somedevid");
|
||||
mDeviceName.AssignLiteral("HMD Position Device");
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
~HMDPositionVRDevice()
|
||||
{
|
||||
if (mTracking) {
|
||||
mHMD->StopSensorTracking();
|
||||
}
|
||||
}
|
||||
|
||||
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) MOZ_OVERRIDE
|
||||
{
|
||||
if (!mTracking) {
|
||||
mHMD->StartSensorTracking();
|
||||
mTracking = true;
|
||||
}
|
||||
|
||||
gfx::VRHMDSensorState state = mHMD->GetSensorState(timeOffset);
|
||||
nsRefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
|
||||
|
||||
return obj.forget();
|
||||
}
|
||||
|
||||
virtual void ZeroSensor() MOZ_OVERRIDE
|
||||
{
|
||||
mHMD->ZeroSensor();
|
||||
}
|
||||
|
||||
protected:
|
||||
nsRefPtr<gfx::VRHMDInfo> mHMD;
|
||||
bool mTracking;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
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);
|
||||
|
||||
for (size_t i = 0; i < hmds.Length(); ++i) {
|
||||
uint32_t sensorBits = hmds[i]->GetSupportedSensorStateBits();
|
||||
aDevices.AppendElement(new HMDInfoVRDevice(aParent, hmds[i]));
|
||||
|
||||
if (sensorBits &
|
||||
(gfx::VRHMDInfo::State_Position | gfx::VRHMDInfo::State_Orientation))
|
||||
{
|
||||
aDevices.AppendElement(new HMDPositionVRDevice(aParent, hmds[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
235
dom/vr/VRDevice.h
Normal file
235
dom/vr/VRDevice.h
Normal file
@ -0,0 +1,235 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_VRDevice_h_
|
||||
#define mozilla_dom_VRDevice_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/dom/VRDeviceBinding.h"
|
||||
#include "mozilla/dom/DOMPoint.h"
|
||||
#include "mozilla/dom/DOMRect.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "gfxVR.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Element;
|
||||
|
||||
class VRFieldOfViewReadOnly : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
VRFieldOfViewReadOnly(nsISupports* aParent,
|
||||
double aUpDegrees, double aRightDegrees,
|
||||
double aDownDegrees, double aLeftDegrees)
|
||||
: mParent(aParent)
|
||||
, mUpDegrees(aUpDegrees)
|
||||
, mRightDegrees(aRightDegrees)
|
||||
, mDownDegrees(aDownDegrees)
|
||||
, mLeftDegrees(aLeftDegrees)
|
||||
{
|
||||
}
|
||||
|
||||
double UpDegrees() const { return mUpDegrees; }
|
||||
double RightDegrees() const { return mRightDegrees; }
|
||||
double DownDegrees() const { return mDownDegrees; }
|
||||
double LeftDegrees() const { return mLeftDegrees; }
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
double mUpDegrees;
|
||||
double mRightDegrees;
|
||||
double mDownDegrees;
|
||||
double mLeftDegrees;
|
||||
};
|
||||
|
||||
class VRFieldOfView MOZ_FINAL : public VRFieldOfViewReadOnly
|
||||
{
|
||||
~VRFieldOfView() {}
|
||||
public:
|
||||
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)
|
||||
{}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFieldOfView)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFieldOfView)
|
||||
|
||||
static already_AddRefed<VRFieldOfView>
|
||||
Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<VRFieldOfView>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
double aUpDegrees, double aRightDegrees,
|
||||
double aDownDegrees, double aLeftDegrees,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
void SetUpDegrees(double aVal) { mUpDegrees = aVal; }
|
||||
void SetRightDegrees(double aVal) { mRightDegrees = aVal; }
|
||||
void SetDownDegrees(double aVal) { mDownDegrees = aVal; }
|
||||
void SetLeftDegrees(double aVal) { mLeftDegrees = aVal; }
|
||||
};
|
||||
|
||||
class VRPositionState MOZ_FINAL : public nsWrapperCache
|
||||
{
|
||||
~VRPositionState() {}
|
||||
public:
|
||||
explicit VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState);
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRPositionState)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRPositionState)
|
||||
|
||||
double TimeStamp() const { return mTimeStamp; }
|
||||
|
||||
bool HasPosition() const { return mPosition != nullptr; }
|
||||
DOMPoint* GetPosition() const { return mPosition; }
|
||||
|
||||
bool HasOrientation() const { return mOrientation != nullptr; }
|
||||
DOMPoint* GetOrientation() const { return mOrientation; }
|
||||
|
||||
// these are created lazily
|
||||
DOMPoint* GetLinearVelocity();
|
||||
DOMPoint* GetLinearAcceleration();
|
||||
DOMPoint* GetAngularVelocity();
|
||||
DOMPoint* GetAngularAcceleration();
|
||||
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
double mTimeStamp;
|
||||
gfx::VRHMDSensorState mVRState;
|
||||
|
||||
nsRefPtr<DOMPoint> mPosition;
|
||||
nsRefPtr<DOMPoint> mLinearVelocity;
|
||||
nsRefPtr<DOMPoint> mLinearAcceleration;
|
||||
|
||||
nsRefPtr<DOMPoint> mOrientation;
|
||||
nsRefPtr<DOMPoint> mAngularVelocity;
|
||||
nsRefPtr<DOMPoint> mAngularAcceleration;
|
||||
};
|
||||
|
||||
class VRDevice : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
// create new VRDevice objects for all known underlying gfx::vr devices
|
||||
static bool CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<nsRefPtr<VRDevice>>& aDevices);
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(VRDevice)
|
||||
|
||||
void GetHardwareUnitId(nsAString& aHWID) const { aHWID = mHWID; }
|
||||
void GetDeviceId(nsAString& aDeviceId) const { aDeviceId = mDeviceId; }
|
||||
void GetDeviceName(nsAString& aDeviceName) const { aDeviceName = mDeviceName; }
|
||||
|
||||
bool IsValid() { return mValid; }
|
||||
|
||||
virtual void Shutdown() { }
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
enum VRDeviceType {
|
||||
HMD,
|
||||
PositionSensor
|
||||
};
|
||||
|
||||
VRDeviceType GetType() const { return mType; }
|
||||
|
||||
protected:
|
||||
VRDevice(nsISupports* aParent, VRDeviceType aType)
|
||||
: mParent(aParent)
|
||||
, mType(aType)
|
||||
, mValid(false)
|
||||
{
|
||||
mHWID.AssignLiteral("uknown");
|
||||
mDeviceId.AssignLiteral("unknown");
|
||||
mDeviceName.AssignLiteral("unknown");
|
||||
}
|
||||
|
||||
virtual ~VRDevice() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsString mHWID;
|
||||
nsString mDeviceId;
|
||||
nsString mDeviceName;
|
||||
|
||||
VRDeviceType mType;
|
||||
|
||||
bool mValid;
|
||||
};
|
||||
|
||||
class HMDVRDevice : public VRDevice
|
||||
{
|
||||
public:
|
||||
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) = 0;
|
||||
|
||||
virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
|
||||
const VRFieldOfViewInit& aRightFOV,
|
||||
double zNear, double zFar) = 0;
|
||||
virtual already_AddRefed<VRFieldOfView> GetCurrentEyeFieldOfView(VREye aEye) = 0;
|
||||
virtual already_AddRefed<VRFieldOfView> GetRecommendedEyeFieldOfView(VREye aEye) = 0;
|
||||
virtual already_AddRefed<VRFieldOfView> GetMaximumEyeFieldOfView(VREye aEye) = 0;
|
||||
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) = 0;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
void XxxToggleElementVR(Element& aElement);
|
||||
|
||||
gfx::VRHMDInfo *GetHMD() { return mHMD.get(); }
|
||||
|
||||
protected:
|
||||
HMDVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
|
||||
: VRDevice(aParent, VRDevice::HMD)
|
||||
, mHMD(aHMD)
|
||||
{ }
|
||||
|
||||
virtual ~HMDVRDevice() { }
|
||||
|
||||
nsRefPtr<gfx::VRHMDInfo> mHMD;
|
||||
};
|
||||
|
||||
class PositionSensorVRDevice : public VRDevice
|
||||
{
|
||||
public:
|
||||
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) = 0;
|
||||
|
||||
virtual void ZeroSensor() = 0;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
PositionSensorVRDevice(nsISupports* aParent)
|
||||
: VRDevice(aParent, VRDevice::PositionSensor)
|
||||
{ }
|
||||
|
||||
virtual ~PositionSensorVRDevice() { }
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
22
dom/vr/moz.build
Normal file
22
dom/vr/moz.build
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- 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.mozilla.dom += [
|
||||
'VRDevice.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES = [
|
||||
'VRDevice.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base'
|
||||
]
|
@ -124,9 +124,11 @@ interface Element : Node {
|
||||
* Requests that this element be made the full-screen element, as per the DOM
|
||||
* full-screen api.
|
||||
*
|
||||
* The fsOptions parameter is non-standard.
|
||||
*
|
||||
* @see <https://wiki.mozilla.org/index.php?title=Gecko:FullScreenAPI>
|
||||
*/
|
||||
void mozRequestFullScreen();
|
||||
void mozRequestFullScreen(optional RequestFullscreenOptions fsOptions);
|
||||
|
||||
/**
|
||||
* Requests that this element be made the pointer-locked element, as per the DOM
|
||||
@ -235,3 +237,10 @@ Element implements NonDocumentTypeChildNode;
|
||||
Element implements ParentNode;
|
||||
Element implements Animatable;
|
||||
Element implements GeometryUtils;
|
||||
|
||||
// non-standard: allows passing options to Element.requestFullScreen
|
||||
dictionary RequestFullscreenOptions {
|
||||
// Which HMDVRDevice to go full screen on; also enables VR rendering.
|
||||
// If null, normal fullscreen is entered.
|
||||
HMDVRDevice? vrDisplay = null;
|
||||
};
|
||||
|
@ -322,6 +322,11 @@ partial interface Navigator {
|
||||
};
|
||||
#endif // MOZ_GAMEPAD
|
||||
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.vr.enabled"]
|
||||
Promise<sequence<VRDevice>> getVRDevices();
|
||||
};
|
||||
|
||||
#ifdef MOZ_B2G_BT
|
||||
partial interface Navigator {
|
||||
[Throws, CheckPermissions="bluetooth"]
|
||||
|
128
dom/webidl/VRDevice.webidl
Normal file
128
dom/webidl/VRDevice.webidl
Normal file
@ -0,0 +1,128 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
enum VREye {
|
||||
"left",
|
||||
"right"
|
||||
};
|
||||
|
||||
[Pref="dom.vr.enabled",
|
||||
HeaderFile="mozilla/dom/VRDevice.h"]
|
||||
interface VRFieldOfViewReadOnly {
|
||||
readonly attribute double upDegrees;
|
||||
readonly attribute double rightDegrees;
|
||||
readonly attribute double downDegrees;
|
||||
readonly attribute double leftDegrees;
|
||||
};
|
||||
|
||||
[Pref="dom.vr.enabled",
|
||||
HeaderFile="mozilla/dom/VRDevice.h",
|
||||
Constructor(optional VRFieldOfViewInit fov),
|
||||
Constructor(double upDegrees, double rightDegrees, double downDegrees, double leftDegrees)]
|
||||
interface VRFieldOfView : VRFieldOfViewReadOnly {
|
||||
inherit attribute double upDegrees;
|
||||
inherit attribute double rightDegrees;
|
||||
inherit attribute double downDegrees;
|
||||
inherit attribute double leftDegrees;
|
||||
};
|
||||
|
||||
dictionary VRFieldOfViewInit {
|
||||
double upDegrees = 0.0;
|
||||
double rightDegrees = 0.0;
|
||||
double downDegrees = 0.0;
|
||||
double leftDegrees = 0.0;
|
||||
};
|
||||
|
||||
[Pref="dom.vr.enabled",
|
||||
HeaderFile="mozilla/dom/VRDevice.h"]
|
||||
interface VRPositionState {
|
||||
readonly attribute double timeStamp;
|
||||
|
||||
readonly attribute boolean hasPosition;
|
||||
readonly attribute DOMPoint? position;
|
||||
readonly attribute DOMPoint? linearVelocity;
|
||||
readonly attribute DOMPoint? linearAcceleration;
|
||||
|
||||
readonly attribute boolean hasOrientation;
|
||||
// XXX should be DOMQuaternion as soon as we add that
|
||||
readonly attribute DOMPoint? orientation;
|
||||
readonly attribute DOMPoint? angularVelocity;
|
||||
readonly attribute DOMPoint? angularAcceleration;
|
||||
};
|
||||
|
||||
[Pref="dom.vr.enabled"]
|
||||
interface VRDevice {
|
||||
/**
|
||||
* An identifier for the distinct hardware unit that this
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* a device name, a user-readable name identifying it
|
||||
*/
|
||||
[Pure] 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
|
||||
VRFieldOfView getCurrentEyeFieldOfView(VREye whichEye);
|
||||
|
||||
// the recommended FOV, per eye.
|
||||
VRFieldOfView getRecommendedEyeFieldOfView(VREye whichEye);
|
||||
|
||||
// the maximum FOV, per eye. Above this, rendering will look broken.
|
||||
VRFieldOfView getMaximumEyeFieldOfView(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
|
||||
// for that eye will be used.
|
||||
void setFieldOfView(optional VRFieldOfViewInit leftFOV,
|
||||
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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
VRPositionState getState(optional double timeOffset = 0.0);
|
||||
|
||||
/* Zero this sensor, treating its current position and orientation
|
||||
* as the "origin/zero" values.
|
||||
*/
|
||||
void zeroSensor();
|
||||
};
|
@ -528,6 +528,7 @@ WEBIDL_FILES = [
|
||||
'VideoStreamTrack.webidl',
|
||||
'VideoTrack.webidl',
|
||||
'VideoTrackList.webidl',
|
||||
'VRDevice.webidl',
|
||||
'VTTCue.webidl',
|
||||
'VTTRegion.webidl',
|
||||
'WaveShaperNode.webidl',
|
||||
|
@ -377,6 +377,11 @@ public:
|
||||
, _41(a41), _42(a42), _43(a43), _44(a44)
|
||||
{}
|
||||
|
||||
Matrix4x4(const Matrix4x4& aOther)
|
||||
{
|
||||
memcpy(this, &aOther, sizeof(*this));
|
||||
}
|
||||
|
||||
Float _11, _12, _13, _14;
|
||||
Float _21, _22, _23, _24;
|
||||
Float _31, _32, _33, _34;
|
||||
@ -541,6 +546,11 @@ public:
|
||||
aX, aY, aZ, 1.0f);
|
||||
}
|
||||
|
||||
static Matrix4x4 Translation(const Point3D& aP)
|
||||
{
|
||||
return Translation(aP.x, aP.y, aP.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a translation to this matrix.
|
||||
*
|
||||
@ -570,6 +580,10 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix4x4 &PreTranslate(const Point3D& aPoint) {
|
||||
return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to PreTranslate, but the translation is applied -after- this
|
||||
* matrix's existing transformation instead of before it.
|
||||
@ -600,6 +614,10 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix4x4 &PostTranslate(const Point3D& aPoint) {
|
||||
return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
|
||||
}
|
||||
|
||||
static Matrix4x4 Scaling(Float aScaleX, Float aScaleY, float aScaleZ)
|
||||
{
|
||||
return Matrix4x4(aScaleX, 0.0f, 0.0f, 0.0f,
|
||||
@ -673,6 +691,19 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix4x4& Transpose() {
|
||||
std::swap(_12, _21);
|
||||
std::swap(_13, _31);
|
||||
std::swap(_14, _41);
|
||||
|
||||
std::swap(_23, _32);
|
||||
std::swap(_24, _42);
|
||||
|
||||
std::swap(_34, _43);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Matrix4x4& o) const
|
||||
{
|
||||
// XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
|
||||
|
@ -150,6 +150,7 @@ MOZ_BEGIN_ENUM_CLASS(EffectTypes, uint8_t)
|
||||
COMPONENT_ALPHA,
|
||||
SOLID_COLOR,
|
||||
RENDER_TARGET,
|
||||
VR_DISTORTION,
|
||||
MAX //sentinel for the count of all effect types
|
||||
MOZ_END_ENUM_CLASS(EffectTypes)
|
||||
|
||||
|
@ -69,3 +69,10 @@ EffectColorMatrix::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
AppendToString(aStream, mColorMatrix, " [matrix=", "]");
|
||||
}
|
||||
|
||||
void
|
||||
EffectVRDistortion::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
{
|
||||
aStream << aPrefix;
|
||||
aStream << nsPrintfCString("EffectVRDistortion (0x%p) [hmd=%p] [render-target=%p] [texture=%p]",
|
||||
this, mHMD.get(), mRenderTarget.get(), mTexture).get();
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "nscore.h" // for nsACString
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "gfxVR.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
@ -96,6 +97,36 @@ struct EffectMask : public Effect
|
||||
gfx::Matrix4x4 mMaskTransform;
|
||||
};
|
||||
|
||||
struct EffectVRDistortion : public Effect
|
||||
{
|
||||
EffectVRDistortion(gfx::VRHMDInfo* aHMD,
|
||||
CompositingRenderTarget* aRenderTarget)
|
||||
: Effect(EffectTypes::VR_DISTORTION)
|
||||
, mHMD(aHMD)
|
||||
, mRenderTarget(aRenderTarget)
|
||||
, mTexture(aRenderTarget)
|
||||
{}
|
||||
|
||||
EffectVRDistortion(gfx::VRHMDInfo* aHMD,
|
||||
TextureSource* aTexture)
|
||||
: Effect(EffectTypes::VR_DISTORTION)
|
||||
, mHMD(aHMD)
|
||||
, mRenderTarget(nullptr)
|
||||
, mTexture(aTexture)
|
||||
{}
|
||||
|
||||
virtual const char* Name() { return "EffectVRDistortion"; }
|
||||
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
|
||||
|
||||
nsRefPtr<gfx::VRHMDInfo> mHMD;
|
||||
RefPtr<CompositingRenderTarget> mRenderTarget;
|
||||
TextureSource* mTexture;
|
||||
|
||||
// The viewport for each eye in the source and
|
||||
// destination textures.
|
||||
gfx::IntRect mViewports[2];
|
||||
};
|
||||
|
||||
struct EffectBlendMode : public Effect
|
||||
{
|
||||
explicit EffectBlendMode(gfx::CompositionOp aBlendMode)
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Endian.h"
|
||||
#include "TexturePoolOGL.h"
|
||||
#include "mozilla/layers/CompositorOGL.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
#include "mozilla/layers/LayerManagerComposite.h"
|
||||
#include "mozilla/layers/TextureHostOGL.h"
|
||||
|
||||
@ -333,7 +334,7 @@ public:
|
||||
static void CreateServerSocket()
|
||||
{
|
||||
// Create Web Server Socket (which has to be on the main thread)
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
if (!sWebSocketManager) {
|
||||
sWebSocketManager = new LayerScopeWebSocketManager();
|
||||
}
|
||||
@ -651,6 +652,16 @@ protected:
|
||||
|
||||
NS_IMPL_ISUPPORTS(DebugDataSender, nsIRunnable);
|
||||
|
||||
|
||||
class CreateServerSocketRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run() {
|
||||
WebSocketHelper::CreateServerSocket();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* LayerScope SendXXX Structure
|
||||
* 1. SendLayer
|
||||
@ -915,15 +926,19 @@ LayerScope::Init()
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: The server socket has to be created on the main thread
|
||||
WebSocketHelper::CreateServerSocket();
|
||||
}
|
||||
|
||||
void
|
||||
LayerScope::DeInit()
|
||||
{
|
||||
// Destroy Web Server Socket
|
||||
WebSocketHelper::DestroyServerSocket();
|
||||
if (NS_IsMainThread()) {
|
||||
WebSocketHelper::CreateServerSocket();
|
||||
} else {
|
||||
// Dispatch creation to main thread, and make sure we
|
||||
// dispatch this only once after booting
|
||||
static bool dispatched = false;
|
||||
if (dispatched) {
|
||||
return;
|
||||
}
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(new CreateServerSocketRunnable());
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch WebSocket Creation to main thread");
|
||||
dispatched = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -965,7 +980,14 @@ LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
|
||||
bool
|
||||
LayerScope::CheckSendable()
|
||||
{
|
||||
// Only compositor threads check LayerScope status
|
||||
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
||||
|
||||
if (!gfxPrefs::LayerScopeEnabled()) {
|
||||
return false;
|
||||
}
|
||||
if (!WebSocketHelper::GetSocketManager()) {
|
||||
Init();
|
||||
return false;
|
||||
}
|
||||
if (!WebSocketHelper::GetSocketManager()->IsConnected()) {
|
||||
|
@ -24,8 +24,6 @@ class LayerComposite;
|
||||
|
||||
class LayerScope {
|
||||
public:
|
||||
static void Init();
|
||||
static void DeInit();
|
||||
static void SendEffectChain(gl::GLContext* aGLContext,
|
||||
const EffectChain& aEffectChain,
|
||||
int aWidth,
|
||||
@ -36,6 +34,9 @@ public:
|
||||
static void SendLayerDump(UniquePtr<layerscope::Packet> aPacket);
|
||||
static bool CheckSendable();
|
||||
static void CleanLayer();
|
||||
|
||||
private:
|
||||
static void Init();
|
||||
};
|
||||
|
||||
// Perform BeginFrame and EndFrame automatically
|
||||
|
@ -994,7 +994,8 @@ void
|
||||
ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
|
||||
{
|
||||
aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
|
||||
mInheritedXScale, mInheritedYScale);
|
||||
mInheritedXScale, mInheritedYScale,
|
||||
reinterpret_cast<uint64_t>(mHMDInfo.get()));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1658,6 +1659,9 @@ ContainerLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
if (1.0 != mPreXScale || 1.0 != mPreYScale) {
|
||||
aStream << nsPrintfCString(" [preScale=%g, %g]", mPreXScale, mPreYScale).get();
|
||||
}
|
||||
if (mHMDInfo) {
|
||||
aStream << nsPrintfCString(" [hmd=%p]", mHMDInfo.get()).get();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "nscore.h" // for nsACString, nsAString
|
||||
#include "prlog.h" // for PRLogModuleInfo
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxVR.h"
|
||||
|
||||
class gfxContext;
|
||||
|
||||
@ -1893,6 +1894,12 @@ public:
|
||||
mChildrenChanged = aVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* VR
|
||||
*/
|
||||
void SetVRHMDInfo(gfx::VRHMDInfo* aHMD) { mHMDInfo = aHMD; }
|
||||
gfx::VRHMDInfo* GetVRHMDInfo() { return mHMDInfo; }
|
||||
|
||||
protected:
|
||||
friend class ReadbackProcessor;
|
||||
|
||||
@ -1938,6 +1945,7 @@ protected:
|
||||
// This is updated by ComputeDifferences. This will be true if we need to invalidate
|
||||
// the intermediate surface.
|
||||
bool mChildrenChanged;
|
||||
nsRefPtr<gfx::VRHMDInfo> mHMDInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
||||
#include "nsPoint.h" // for nsIntPoint
|
||||
#include "nsString.h" // for nsAutoCString
|
||||
#include "gfxVR.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -133,6 +133,105 @@ struct PreparedLayer
|
||||
RenderTargetIntRect mClipRect;
|
||||
};
|
||||
|
||||
|
||||
template<class ContainerT> void
|
||||
ContainerRenderVR(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const nsIntRect& aClipRect,
|
||||
gfx::VRHMDInfo* aHMD)
|
||||
{
|
||||
RefPtr<CompositingRenderTarget> surface;
|
||||
|
||||
Compositor* compositor = aManager->GetCompositor();
|
||||
|
||||
RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
|
||||
|
||||
nsIntRect visibleRect = aContainer->GetEffectiveVisibleRegion().GetBounds();
|
||||
|
||||
float opacity = aContainer->GetEffectiveOpacity();
|
||||
|
||||
gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
|
||||
visibleRect.width, visibleRect.height);
|
||||
// we're about to create a framebuffer backed by textures to use as an intermediate
|
||||
// surface. What to do if its size (as given by framebufferRect) would exceed the
|
||||
// maximum texture size supported by the GL? The present code chooses the compromise
|
||||
// of just clamping the framebuffer's size to the max supported size.
|
||||
// This gives us a lower resolution rendering of the intermediate surface (children layers).
|
||||
// See bug 827170 for a discussion.
|
||||
int32_t maxTextureSize = compositor->GetMaxTextureSize();
|
||||
surfaceRect.width = std::min(maxTextureSize, surfaceRect.width);
|
||||
surfaceRect.height = std::min(maxTextureSize, surfaceRect.height);
|
||||
|
||||
// use NONE here, because we draw black to clear below
|
||||
surface = compositor->CreateRenderTarget(surfaceRect, INIT_MODE_NONE);
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
compositor->SetRenderTarget(surface);
|
||||
|
||||
nsAutoTArray<Layer*, 12> children;
|
||||
aContainer->SortChildrenBy3DZOrder(children);
|
||||
|
||||
/**
|
||||
* Render this container's contents.
|
||||
*/
|
||||
nsIntRect surfaceClipRect(0, 0, surfaceRect.width, surfaceRect.height);
|
||||
RenderTargetIntRect rtClipRect(0, 0, surfaceRect.width, surfaceRect.height);
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
|
||||
Layer* layer = layerToRender->GetLayer();
|
||||
|
||||
if (layer->GetEffectiveVisibleRegion().IsEmpty() &&
|
||||
!layer->AsContainerLayer()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderTargetIntRect clipRect = layer->CalculateScissorRect(rtClipRect);
|
||||
if (clipRect.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
layerToRender->Prepare(rtClipRect);
|
||||
layerToRender->RenderLayer(surfaceClipRect);
|
||||
}
|
||||
|
||||
// Unbind the current surface and rebind the previous one.
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (gfxUtils::sDumpPainting) {
|
||||
RefPtr<gfx::DataSourceSurface> surf = surface->Dump(aManager->GetCompositor());
|
||||
if (surf) {
|
||||
WriteSnapshotToDumpFile(aContainer, surf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
compositor->SetRenderTarget(previousTarget);
|
||||
|
||||
gfx::Rect rect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height);
|
||||
gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
|
||||
|
||||
// The VR geometry may not cover the entire area; we need to fill with a solid color
|
||||
// first.
|
||||
// XXX should DrawQuad handle this on its own? Is there a time where we wouldn't want
|
||||
// to do this? (e.g. something like Cardboard would not require distortion so will fill
|
||||
// the entire rect)
|
||||
EffectChain solidEffect(aContainer);
|
||||
solidEffect.mPrimaryEffect = new EffectSolidColor(Color(0.0, 0.0, 0.0, 1.0));
|
||||
aManager->GetCompositor()->DrawQuad(rect, clipRect, solidEffect, opacity,
|
||||
aContainer->GetEffectiveTransform());
|
||||
|
||||
// draw the temporary surface with VR distortion to the original destination
|
||||
EffectChain vrEffect(aContainer);
|
||||
vrEffect.mPrimaryEffect = new EffectVRDistortion(aHMD, surface);
|
||||
|
||||
// XXX we shouldn't use visibleRect here -- the VR distortion needs to know the
|
||||
// full rect, not just the visible one. Luckily, right now, VR distortion is only
|
||||
// rendered when the element is fullscreen, so the visibleRect will be right anyway.
|
||||
aManager->GetCompositor()->DrawQuad(rect, clipRect, vrEffect, opacity,
|
||||
aContainer->GetEffectiveTransform());
|
||||
}
|
||||
|
||||
/* all of the prepared data that we need in RenderLayer() */
|
||||
struct PreparedData
|
||||
{
|
||||
@ -150,6 +249,15 @@ ContainerPrepare(ContainerT* aContainer,
|
||||
aContainer->mPrepared = MakeUnique<PreparedData>();
|
||||
aContainer->mPrepared->mNeedsSurfaceCopy = false;
|
||||
|
||||
gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
|
||||
if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
|
||||
// we're not going to do anything here; instead, we'll do it all in ContainerRender.
|
||||
// XXX fix this; we can win with the same optimizations. Specifically, we
|
||||
// want to render thebes layers only once and then composite the intermeidate surfaces
|
||||
// with different transforms twice.
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which layers to draw.
|
||||
*/
|
||||
@ -366,6 +474,14 @@ ContainerRender(ContainerT* aContainer,
|
||||
const nsIntRect& aClipRect)
|
||||
{
|
||||
MOZ_ASSERT(aContainer->mPrepared);
|
||||
|
||||
gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
|
||||
if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
|
||||
ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo);
|
||||
aContainer->mPrepared = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aContainer->UseIntermediateSurface()) {
|
||||
RefPtr<CompositingRenderTarget> surface;
|
||||
|
||||
|
@ -48,6 +48,10 @@ typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
|
||||
using mozilla::layout::RenderFrameParent;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class VRHMDInfo;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
|
||||
class PGrallocBufferParent;
|
||||
@ -378,6 +382,15 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
||||
specific.get_ContainerLayerAttributes();
|
||||
containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
|
||||
containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
|
||||
|
||||
if (attrs.hmdInfo()) {
|
||||
if (!IsSameProcess()) {
|
||||
NS_WARNING("VR layers currently not supported with cross-process compositing");
|
||||
return false;
|
||||
}
|
||||
containerLayer->SetVRHMDInfo(reinterpret_cast<mozilla::gfx::VRHMDInfo*>(attrs.hmdInfo()));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Specific::TColorLayerAttributes: {
|
||||
|
@ -227,6 +227,10 @@ struct ContainerLayerAttributes {
|
||||
float preYScale;
|
||||
float inheritedXScale;
|
||||
float inheritedYScale;
|
||||
// This is a bare pointer; LayerTransactionParent::RecvUpdate prevents this
|
||||
// from being used when !IsSameProcess(), but we should make this truly
|
||||
// cross process at some point by passing the HMDConfig
|
||||
uint64_t hmdInfo;
|
||||
};
|
||||
struct ColorLayerAttributes { LayerColor color; nsIntRect bounds; };
|
||||
struct CanvasLayerAttributes { GraphicsFilterType filter; nsIntRect bounds; };
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "gfxTextRun.h"
|
||||
#include "gfxVR.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <process.h>
|
||||
@ -386,6 +387,10 @@ gfxPlatform::gfxPlatform()
|
||||
InitBackendPrefs(canvasMask, BackendType::CAIRO,
|
||||
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();
|
||||
}
|
||||
|
||||
gfxPlatform*
|
||||
@ -677,6 +682,9 @@ gfxPlatform::~gfxPlatform()
|
||||
mScreenReferenceSurface = nullptr;
|
||||
mScreenReferenceDrawTarget = nullptr;
|
||||
|
||||
// Clean up any VR stuff
|
||||
VRHMDManagerOculus::Destroy();
|
||||
|
||||
// The cairo folks think we should only clean up in debug builds,
|
||||
// but we're generally in the habit of trying to shut down as
|
||||
// cleanly as possible even in production code, so call this
|
||||
|
@ -326,6 +326,9 @@ private:
|
||||
DECL_GFX_PREF(Once, "webgl.angle.force-d3d11", WebGLANGLEForceD3D11, bool, false);
|
||||
|
||||
DECL_GFX_PREF(Once, "layers.stereo-video.enabled", StereoVideoEnabled, bool, false);
|
||||
|
||||
DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "dom.vr.add-test-devices", VRAddTestDevices, int32_t, 1);
|
||||
public:
|
||||
// Manage the singleton:
|
||||
static gfxPrefs& GetSingleton()
|
||||
|
645
gfx/thebes/gfxVR.cpp
Normal file
645
gfx/thebes/gfxVR.cpp
Normal file
@ -0,0 +1,645 @@
|
||||
/* -*- 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 "gfxVR.h"
|
||||
|
||||
#include "ovr_capi_dynamic.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
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef OVR_CAPI_LIMITED_MOZILLA
|
||||
static pfn_ovr_Initialize ovr_Initialize = nullptr;
|
||||
static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
|
||||
static pfn_ovrHmd_Detect ovrHmd_Detect = nullptr;
|
||||
static pfn_ovrHmd_Create ovrHmd_Create = nullptr;
|
||||
static pfn_ovrHmd_Destroy ovrHmd_Destroy = nullptr;
|
||||
static pfn_ovrHmd_CreateDebug ovrHmd_CreateDebug = nullptr;
|
||||
static pfn_ovrHmd_GetLastError ovrHmd_GetLastError = nullptr;
|
||||
static pfn_ovrHmd_AttachToWindow ovrHmd_AttachToWindow = nullptr;
|
||||
static pfn_ovrHmd_GetEnabledCaps ovrHmd_GetEnabledCaps = nullptr;
|
||||
static pfn_ovrHmd_SetEnabledCaps ovrHmd_SetEnabledCaps = nullptr;
|
||||
static pfn_ovrHmd_ConfigureTracking ovrHmd_ConfigureTracking = nullptr;
|
||||
static pfn_ovrHmd_RecenterPose ovrHmd_RecenterPose = nullptr;
|
||||
static pfn_ovrHmd_GetTrackingState ovrHmd_GetTrackingState = nullptr;
|
||||
static pfn_ovrHmd_GetFovTextureSize ovrHmd_GetFovTextureSize = nullptr;
|
||||
static pfn_ovrHmd_GetRenderDesc ovrHmd_GetRenderDesc = nullptr;
|
||||
static pfn_ovrHmd_CreateDistortionMesh ovrHmd_CreateDistortionMesh = nullptr;
|
||||
static pfn_ovrHmd_DestroyDistortionMesh ovrHmd_DestroyDistortionMesh = nullptr;
|
||||
static pfn_ovrHmd_GetRenderScaleAndOffset ovrHmd_GetRenderScaleAndOffset = nullptr;
|
||||
static pfn_ovrHmd_GetFrameTiming ovrHmd_GetFrameTiming = nullptr;
|
||||
static pfn_ovrHmd_BeginFrameTiming ovrHmd_BeginFrameTiming = nullptr;
|
||||
static pfn_ovrHmd_EndFrameTiming ovrHmd_EndFrameTiming = nullptr;
|
||||
static pfn_ovrHmd_ResetFrameTiming ovrHmd_ResetFrameTiming = nullptr;
|
||||
static pfn_ovrHmd_GetEyePoses ovrHmd_GetEyePoses = nullptr;
|
||||
static pfn_ovrHmd_GetHmdPosePerEye ovrHmd_GetHmdPosePerEye = nullptr;
|
||||
static pfn_ovrHmd_GetEyeTimewarpMatrices ovrHmd_GetEyeTimewarpMatrices = nullptr;
|
||||
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"
|
||||
#else
|
||||
# define OVR_LIB_NAME 0
|
||||
#endif
|
||||
|
||||
static bool
|
||||
InitializeOculusCAPI()
|
||||
{
|
||||
static PRLibrary *ovrlib = nullptr;
|
||||
|
||||
if (!ovrlib) {
|
||||
const char *libName = OVR_LIB_NAME;
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ovrlib) {
|
||||
printf_stderr("Failed to load Oculus VR library, tried '%s'\n", libName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// was it already initialized?
|
||||
if (ovr_Initialize)
|
||||
return true;
|
||||
|
||||
#define REQUIRE_FUNCTION(_x) do { \
|
||||
*(void **)&_x = (void *) PR_FindSymbol(ovrlib, #_x); \
|
||||
if (!_x) { printf_stderr(#_x " symbol missing\n"); goto fail; } \
|
||||
} while (0)
|
||||
|
||||
REQUIRE_FUNCTION(ovr_Initialize);
|
||||
REQUIRE_FUNCTION(ovr_Shutdown);
|
||||
REQUIRE_FUNCTION(ovrHmd_Detect);
|
||||
REQUIRE_FUNCTION(ovrHmd_Create);
|
||||
REQUIRE_FUNCTION(ovrHmd_Destroy);
|
||||
REQUIRE_FUNCTION(ovrHmd_CreateDebug);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetLastError);
|
||||
REQUIRE_FUNCTION(ovrHmd_AttachToWindow);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetEnabledCaps);
|
||||
REQUIRE_FUNCTION(ovrHmd_SetEnabledCaps);
|
||||
REQUIRE_FUNCTION(ovrHmd_ConfigureTracking);
|
||||
REQUIRE_FUNCTION(ovrHmd_RecenterPose);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetTrackingState);
|
||||
|
||||
REQUIRE_FUNCTION(ovrHmd_GetFovTextureSize);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetRenderDesc);
|
||||
REQUIRE_FUNCTION(ovrHmd_CreateDistortionMesh);
|
||||
REQUIRE_FUNCTION(ovrHmd_DestroyDistortionMesh);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetRenderScaleAndOffset);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetFrameTiming);
|
||||
REQUIRE_FUNCTION(ovrHmd_BeginFrameTiming);
|
||||
REQUIRE_FUNCTION(ovrHmd_EndFrameTiming);
|
||||
REQUIRE_FUNCTION(ovrHmd_ResetFrameTiming);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetEyePoses);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetHmdPosePerEye);
|
||||
REQUIRE_FUNCTION(ovrHmd_GetEyeTimewarpMatrices);
|
||||
REQUIRE_FUNCTION(ovrMatrix4f_Projection);
|
||||
REQUIRE_FUNCTION(ovrMatrix4f_OrthoSubProjection);
|
||||
REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
|
||||
|
||||
#undef REQUIRE_FUNCTION
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
ovr_Initialize = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
// we're statically linked; it's available
|
||||
static bool InitializeOculusCAPI()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
// Dummy nsIScreen implementation, for when we just need to specify a size
|
||||
class FakeScreen : public nsIScreen
|
||||
{
|
||||
public:
|
||||
FakeScreen(const IntRect& aScreenRect)
|
||||
: mScreenRect(aScreenRect)
|
||||
{ }
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
|
||||
*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) {
|
||||
return GetRect(l, t, w, h);
|
||||
}
|
||||
NS_IMETHOD GetRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
|
||||
return GetRect(l, t, w, h);
|
||||
}
|
||||
NS_IMETHOD GetAvailRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
|
||||
return GetAvailRect(l, t, w, h);
|
||||
}
|
||||
|
||||
NS_IMETHOD GetId(uint32_t* aId) { *aId = (uint32_t)-1; return NS_OK; }
|
||||
NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) { *aPixelDepth = 24; return NS_OK; }
|
||||
NS_IMETHOD GetColorDepth(int32_t* aColorDepth) { *aColorDepth = 24; return NS_OK; }
|
||||
|
||||
NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness) { return NS_ERROR_NOT_AVAILABLE; }
|
||||
NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness) { return NS_ERROR_NOT_AVAILABLE; }
|
||||
NS_IMETHOD GetRotation(uint32_t* aRotation) {
|
||||
*aRotation = nsIScreen::ROTATION_0_DEG;
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD SetRotation(uint32_t aRotation) { return NS_ERROR_NOT_AVAILABLE; }
|
||||
NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) {
|
||||
*aContentsScaleFactor = 1.0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~FakeScreen() {}
|
||||
|
||||
IntRect mScreenRect;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen)
|
||||
|
||||
class HMDInfoOculus : public VRHMDInfo {
|
||||
friend class VRHMDManagerOculusImpl;
|
||||
public:
|
||||
HMDInfoOculus(ovrHmd aHMD);
|
||||
|
||||
bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
|
||||
double zNear, double zFar) MOZ_OVERRIDE;
|
||||
|
||||
bool StartSensorTracking() MOZ_OVERRIDE;
|
||||
VRHMDSensorState GetSensorState(double timeOffset) MOZ_OVERRIDE;
|
||||
void StopSensorTracking() MOZ_OVERRIDE;
|
||||
void ZeroSensor() MOZ_OVERRIDE;
|
||||
|
||||
void FillDistortionConstants(uint32_t whichEye,
|
||||
const IntSize& textureSize, const IntRect& eyeViewport,
|
||||
const Size& destViewport, const Rect& destRect,
|
||||
VRDistortionConstants& values) MOZ_OVERRIDE;
|
||||
|
||||
void Destroy();
|
||||
|
||||
protected:
|
||||
virtual ~HMDInfoOculus() {
|
||||
Destroy();
|
||||
MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
|
||||
}
|
||||
|
||||
ovrHmd mHMD;
|
||||
ovrFovPort mFOVPort[2];
|
||||
uint32_t mStartCount;
|
||||
};
|
||||
|
||||
static ovrFovPort
|
||||
ToFovPort(const VRFieldOfView& aFOV)
|
||||
{
|
||||
ovrFovPort fovPort;
|
||||
fovPort.LeftTan = tan(aFOV.leftDegrees * M_PI / 180.0);
|
||||
fovPort.RightTan = tan(aFOV.rightDegrees * M_PI / 180.0);
|
||||
fovPort.UpTan = tan(aFOV.upDegrees * M_PI / 180.0);
|
||||
fovPort.DownTan = tan(aFOV.downDegrees * M_PI / 180.0);
|
||||
return fovPort;
|
||||
}
|
||||
|
||||
static VRFieldOfView
|
||||
FromFovPort(const ovrFovPort& aFOV)
|
||||
{
|
||||
VRFieldOfView fovInfo;
|
||||
fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
|
||||
fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
|
||||
fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
|
||||
fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
|
||||
return fovInfo;
|
||||
}
|
||||
|
||||
HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD)
|
||||
: VRHMDInfo(VRHMDType::Oculus)
|
||||
, mHMD(aHMD)
|
||||
, mStartCount(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
|
||||
|
||||
mSupportedSensorBits = 0;
|
||||
if (mHMD->TrackingCaps & ovrTrackingCap_Orientation)
|
||||
mSupportedSensorBits |= State_Orientation;
|
||||
if (mHMD->TrackingCaps & ovrTrackingCap_Position)
|
||||
mSupportedSensorBits |= State_Position;
|
||||
|
||||
mRecommendedEyeFOV[Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
|
||||
mRecommendedEyeFOV[Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
|
||||
|
||||
mMaximumEyeFOV[Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
|
||||
mMaximumEyeFOV[Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
|
||||
|
||||
SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0);
|
||||
|
||||
nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
|
||||
if (screenmgr) {
|
||||
screenmgr->ScreenForRect(mHMD->WindowsPos.x, mHMD->WindowsPos.y,
|
||||
mHMD->Resolution.w, mHMD->Resolution.h,
|
||||
getter_AddRefs(mScreen));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HMDInfoOculus::Destroy()
|
||||
{
|
||||
if (mHMD) {
|
||||
ovrHmd_Destroy(mHMD);
|
||||
mHMD = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
HMDInfoOculus::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
|
||||
double zNear, double zFar)
|
||||
{
|
||||
float pixelsPerDisplayPixel = 1.0;
|
||||
ovrSizei texSize[2];
|
||||
|
||||
uint32_t caps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette; // XXX TODO add TimeWarp
|
||||
|
||||
// get eye parameters and create the mesh
|
||||
for (uint32_t eye = 0; eye < NumEyes; eye++) {
|
||||
mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
|
||||
mFOVPort[eye] = ToFovPort(mEyeFOV[eye]);
|
||||
|
||||
ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]);
|
||||
|
||||
// these values are negated so that content can add the adjustment to its camera position,
|
||||
// instead of subtracting
|
||||
mEyeTranslation[eye] = Point3D(-renderDesc.ViewAdjust.x, -renderDesc.ViewAdjust.y, -renderDesc.ViewAdjust.z);
|
||||
|
||||
// note that we are using a right-handed coordinate system here, to match CSS
|
||||
ovrMatrix4f projMatrix = ovrMatrix4f_Projection(mFOVPort[eye], zNear, zFar, true);
|
||||
|
||||
// XXX this is gross, we really need better methods on Matrix4x4
|
||||
memcpy(&mEyeProjectionMatrix[eye], projMatrix.M, sizeof(ovrMatrix4f));
|
||||
mEyeProjectionMatrix[eye].Transpose();
|
||||
|
||||
texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel);
|
||||
|
||||
ovrDistortionMesh mesh;
|
||||
bool ok = ovrHmd_CreateDistortionMesh(mHMD, (ovrEyeType) eye, mFOVPort[eye], caps, &mesh);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
mDistortionMesh[eye].mVertices.SetLength(mesh.VertexCount);
|
||||
mDistortionMesh[eye].mIndices.SetLength(mesh.IndexCount);
|
||||
|
||||
ovrDistortionVertex *srcv = mesh.pVertexData;
|
||||
VRDistortionVertex *destv = 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;
|
||||
destv[i].pos[1] = srcv[i].ScreenPosNDC.y;
|
||||
|
||||
destv[i].texR[0] = srcv[i].TanEyeAnglesR.x;
|
||||
destv[i].texR[1] = srcv[i].TanEyeAnglesR.y;
|
||||
destv[i].texG[0] = srcv[i].TanEyeAnglesG.x;
|
||||
destv[i].texG[1] = srcv[i].TanEyeAnglesG.y;
|
||||
destv[i].texB[0] = srcv[i].TanEyeAnglesB.x;
|
||||
destv[i].texB[1] = srcv[i].TanEyeAnglesB.y;
|
||||
|
||||
destv[i].genericAttribs[0] = srcv[i].VignetteFactor;
|
||||
destv[i].genericAttribs[1] = srcv[i].TimeWarpFactor;
|
||||
}
|
||||
|
||||
memcpy(mDistortionMesh[eye].mIndices.Elements(), mesh.pIndexData, mesh.IndexCount * sizeof(uint16_t));
|
||||
ovrHmd_DestroyDistortionMesh(&mesh);
|
||||
}
|
||||
|
||||
// take the max of both for eye resolution
|
||||
mEyeResolution.width = std::max(texSize[Eye_Left].w, texSize[Eye_Right].w);
|
||||
mEyeResolution.height = std::max(texSize[Eye_Left].h, texSize[Eye_Right].h);
|
||||
|
||||
mConfiguration.hmdType = mType;
|
||||
mConfiguration.value = 0;
|
||||
mConfiguration.fov[0] = aFOVLeft;
|
||||
mConfiguration.fov[1] = aFOVRight;
|
||||
|
||||
return true;
|
||||
//* need to call this during rendering each frame I think? */
|
||||
//ovrHmd_GetRenderScaleAndOffset(fovPort, texSize, renderViewport, uvScaleOffsetOut);
|
||||
}
|
||||
|
||||
void
|
||||
HMDInfoOculus::FillDistortionConstants(uint32_t whichEye,
|
||||
const IntSize& textureSize,
|
||||
const IntRect& eyeViewport,
|
||||
const Size& destViewport,
|
||||
const Rect& destRect,
|
||||
VRDistortionConstants& values)
|
||||
{
|
||||
ovrSizei texSize = { textureSize.width, textureSize.height };
|
||||
ovrRecti eyePort = { { eyeViewport.x, eyeViewport.y }, { eyeViewport.width, eyeViewport.height } };
|
||||
ovrVector2f scaleOut[2];
|
||||
|
||||
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;
|
||||
|
||||
// These values are in clip space [-1..1] range, but we're providing
|
||||
// scaling 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;
|
||||
}
|
||||
|
||||
bool
|
||||
HMDInfoOculus::StartSensorTracking()
|
||||
{
|
||||
if (mStartCount == 0) {
|
||||
bool ok = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
|
||||
if (!ok)
|
||||
return false;
|
||||
}
|
||||
|
||||
mStartCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HMDInfoOculus::StopSensorTracking()
|
||||
{
|
||||
if (--mStartCount == 0) {
|
||||
ovrHmd_ConfigureTracking(mHMD, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HMDInfoOculus::ZeroSensor()
|
||||
{
|
||||
ovrHmd_RecenterPose(mHMD);
|
||||
}
|
||||
|
||||
VRHMDSensorState
|
||||
HMDInfoOculus::GetSensorState(double timeOffset)
|
||||
{
|
||||
VRHMDSensorState result;
|
||||
result.Clear();
|
||||
|
||||
// XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
|
||||
// the Oculus time base and the browser one.
|
||||
ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset);
|
||||
ovrPoseStatef& pose(state.HeadPose);
|
||||
|
||||
result.timestamp = pose.TimeInSeconds;
|
||||
|
||||
if (state.StatusFlags & ovrStatus_OrientationTracked) {
|
||||
result.flags |= State_Orientation;
|
||||
|
||||
result.orientation[0] = pose.ThePose.Orientation.x;
|
||||
result.orientation[1] = pose.ThePose.Orientation.y;
|
||||
result.orientation[2] = pose.ThePose.Orientation.z;
|
||||
result.orientation[3] = pose.ThePose.Orientation.w;
|
||||
|
||||
result.angularVelocity[0] = pose.AngularVelocity.x;
|
||||
result.angularVelocity[1] = pose.AngularVelocity.y;
|
||||
result.angularVelocity[2] = pose.AngularVelocity.z;
|
||||
|
||||
result.angularAcceleration[0] = pose.AngularAcceleration.x;
|
||||
result.angularAcceleration[1] = pose.AngularAcceleration.y;
|
||||
result.angularAcceleration[2] = pose.AngularAcceleration.z;
|
||||
}
|
||||
|
||||
if (state.StatusFlags & ovrStatus_PositionTracked) {
|
||||
result.flags |= State_Position;
|
||||
|
||||
result.position[0] = pose.ThePose.Position.x;
|
||||
result.position[1] = pose.ThePose.Position.y;
|
||||
result.position[2] = pose.ThePose.Position.z;
|
||||
|
||||
result.linearVelocity[0] = pose.LinearVelocity.x;
|
||||
result.linearVelocity[1] = pose.LinearVelocity.y;
|
||||
result.linearVelocity[2] = pose.LinearVelocity.z;
|
||||
|
||||
result.linearAcceleration[0] = pose.LinearAcceleration.x;
|
||||
result.linearAcceleration[1] = pose.LinearAcceleration.y;
|
||||
result.linearAcceleration[2] = pose.LinearAcceleration.z;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!gfxPrefs::VREnabled())
|
||||
return false;
|
||||
|
||||
if (!InitializeOculusCAPI())
|
||||
return false;
|
||||
|
||||
bool ok = ovr_Initialize();
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
mOculusPlatformInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
VRHMDManagerOculusImpl::Init()
|
||||
{
|
||||
if (mOculusInitialized)
|
||||
return true;
|
||||
|
||||
if (!PlatformInit())
|
||||
return false;
|
||||
|
||||
int count = ovrHmd_Detect();
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ovrHmd hmd = ovrHmd_Create(i);
|
||||
nsRefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
|
||||
mOculusHMDs.AppendElement(oc);
|
||||
}
|
||||
|
||||
// VRAddTestDevices == 1: add test device only if no real devices present
|
||||
// VRAddTestDevices == 2: add test device always
|
||||
if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) ||
|
||||
(gfxPrefs::VRAddTestDevices() == 2))
|
||||
{
|
||||
ovrHmd hmd = ovrHmd_CreateDebug(ovrHmd_DK2);
|
||||
nsRefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
|
||||
mOculusHMDs.AppendElement(oc);
|
||||
}
|
||||
|
||||
mOculusInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
VRHMDManagerOculusImpl::Destroy()
|
||||
{
|
||||
if (!mOculusInitialized)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
|
||||
mOculusHMDs[i]->Destroy();
|
||||
}
|
||||
|
||||
mOculusHMDs.Clear();
|
||||
|
||||
ovr_Shutdown();
|
||||
mOculusInitialized = false;
|
||||
}
|
||||
|
||||
void
|
||||
VRHMDManagerOculusImpl::GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
|
||||
{
|
||||
Init();
|
||||
for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
|
||||
aHMDResult.AppendElement(mOculusHMDs[i]);
|
||||
}
|
||||
}
|
217
gfx/thebes/gfxVR.h
Normal file
217
gfx/thebes/gfxVR.h
Normal file
@ -0,0 +1,217 @@
|
||||
/* -*- 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_H
|
||||
#define GFX_VR_H
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsIScreen.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsRefPtr.h"
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(VRHMDType, uint16_t)
|
||||
Oculus,
|
||||
NumHMDTypes
|
||||
MOZ_END_ENUM_CLASS(VRHMDType)
|
||||
|
||||
struct VRFieldOfView {
|
||||
static VRFieldOfView FromCSSPerspectiveInfo(double aPerspectiveDistance,
|
||||
const Point& aPerspectiveOrigin,
|
||||
const Point& aTransformOrigin,
|
||||
const Rect& aContentRectangle)
|
||||
{
|
||||
/**/
|
||||
return VRFieldOfView();
|
||||
}
|
||||
|
||||
VRFieldOfView() {}
|
||||
VRFieldOfView(double up, double right, double down, double left)
|
||||
: upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
|
||||
{}
|
||||
|
||||
bool operator==(const VRFieldOfView& other) const {
|
||||
return other.upDegrees == upDegrees &&
|
||||
other.downDegrees == downDegrees &&
|
||||
other.rightDegrees == rightDegrees &&
|
||||
other.leftDegrees == leftDegrees;
|
||||
}
|
||||
|
||||
bool operator!=(const VRFieldOfView& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool IsZero() const {
|
||||
return upDegrees == 0.0 ||
|
||||
rightDegrees == 0.0 ||
|
||||
downDegrees == 0.0 ||
|
||||
leftDegrees == 0.0;
|
||||
}
|
||||
|
||||
double upDegrees;
|
||||
double rightDegrees;
|
||||
double downDegrees;
|
||||
double leftDegrees;
|
||||
};
|
||||
|
||||
// 12 floats per vertex. Position, tex coordinates
|
||||
// for each channel, and 4 generic attributes
|
||||
struct VRDistortionConstants {
|
||||
float eyeToSourceScaleAndOffset[4];
|
||||
float destinationScaleAndOffset[4];
|
||||
};
|
||||
|
||||
struct VRDistortionVertex {
|
||||
float pos[2];
|
||||
float texR[2];
|
||||
float texG[2];
|
||||
float texB[2];
|
||||
float genericAttribs[4];
|
||||
};
|
||||
|
||||
struct VRDistortionMesh {
|
||||
nsTArray<VRDistortionVertex> mVertices;
|
||||
nsTArray<uint16_t> mIndices;
|
||||
};
|
||||
|
||||
struct VRHMDSensorState {
|
||||
double timestamp;
|
||||
uint32_t flags;
|
||||
float orientation[4];
|
||||
float position[3];
|
||||
float angularVelocity[3];
|
||||
float angularAcceleration[3];
|
||||
float linearVelocity[3];
|
||||
float linearAcceleration[3];
|
||||
|
||||
void Clear() {
|
||||
memset(this, 0, sizeof(VRHMDSensorState));
|
||||
}
|
||||
};
|
||||
|
||||
/* A pure data struct that can be used to see if
|
||||
* the configuration of one HMDInfo matches another; for rendering purposes,
|
||||
* really asking "can the rendering details of this one be used for the other"
|
||||
*/
|
||||
struct VRHMDConfiguration {
|
||||
VRHMDConfiguration() : hmdType(VRHMDType::NumHMDTypes) {}
|
||||
|
||||
bool operator==(const VRHMDConfiguration& other) const {
|
||||
return hmdType == other.hmdType &&
|
||||
value == other.value &&
|
||||
fov[0] == other.fov[0] &&
|
||||
fov[1] == other.fov[1];
|
||||
}
|
||||
|
||||
bool operator!=(const VRHMDConfiguration& other) const {
|
||||
return hmdType != other.hmdType ||
|
||||
value != other.value ||
|
||||
fov[0] != other.fov[0] ||
|
||||
fov[1] != other.fov[1];
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return hmdType != VRHMDType::NumHMDTypes;
|
||||
}
|
||||
|
||||
VRHMDType hmdType;
|
||||
uint32_t value;
|
||||
VRFieldOfView fov[2];
|
||||
};
|
||||
|
||||
class VRHMDInfo {
|
||||
public:
|
||||
enum Eye {
|
||||
Eye_Left,
|
||||
Eye_Right,
|
||||
NumEyes
|
||||
};
|
||||
|
||||
enum StateValidFlags {
|
||||
State_Position = 1 << 1,
|
||||
State_Orientation = 1 << 2
|
||||
};
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDInfo)
|
||||
|
||||
VRHMDType GetType() const { return mType; }
|
||||
|
||||
virtual const VRFieldOfView& GetRecommendedEyeFOV(uint32_t whichEye) { return mRecommendedEyeFOV[whichEye]; }
|
||||
virtual const VRFieldOfView& GetMaximumEyeFOV(uint32_t whichEye) { return mMaximumEyeFOV[whichEye]; }
|
||||
|
||||
const VRHMDConfiguration& GetConfiguration() const { return mConfiguration; }
|
||||
|
||||
/* set the FOV for this HMD unit; this triggers a computation of all the remaining bits. Returns false if it fails */
|
||||
virtual bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
|
||||
double zNear, double zFar) = 0;
|
||||
const VRFieldOfView& GetEyeFOV(uint32_t whichEye) { return mEyeFOV[whichEye]; }
|
||||
|
||||
/* Suggested resolution for rendering a single eye.
|
||||
* Assumption is that left/right rendering will be 2x of this size.
|
||||
* XXX fix this for vertical displays
|
||||
*/
|
||||
const IntSize& SuggestedEyeResolution() const { return mEyeResolution; }
|
||||
const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; }
|
||||
const Matrix4x4& GetEyeProjectionMatrix(uint32_t whichEye) const { return mEyeProjectionMatrix[whichEye]; }
|
||||
|
||||
virtual uint32_t GetSupportedSensorStateBits() { return mSupportedSensorBits; }
|
||||
virtual bool StartSensorTracking() = 0;
|
||||
virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) = 0;
|
||||
virtual void StopSensorTracking() = 0;
|
||||
|
||||
virtual void ZeroSensor() = 0;
|
||||
|
||||
virtual void FillDistortionConstants(uint32_t whichEye,
|
||||
const IntSize& textureSize, // the full size of the texture
|
||||
const IntRect& eyeViewport, // the viewport within the texture for the current eye
|
||||
const Size& destViewport, // the size of the destination viewport
|
||||
const Rect& destRect, // the rectangle within the dest viewport that this should be rendered
|
||||
VRDistortionConstants& values) = 0;
|
||||
|
||||
virtual const VRDistortionMesh& GetDistortionMesh(uint32_t whichEye) const { return mDistortionMesh[whichEye]; }
|
||||
|
||||
// The nsIScreen that represents this device
|
||||
virtual nsIScreen* GetScreen() { return mScreen; }
|
||||
|
||||
protected:
|
||||
VRHMDInfo(VRHMDType aType) : mType(aType) { MOZ_COUNT_CTOR(VRHMDInfo); }
|
||||
virtual ~VRHMDInfo() { MOZ_COUNT_DTOR(VRHMDInfo); }
|
||||
|
||||
VRHMDType mType;
|
||||
VRHMDConfiguration mConfiguration;
|
||||
|
||||
VRFieldOfView mEyeFOV[NumEyes];
|
||||
IntSize mEyeResolution;
|
||||
Point3D mEyeTranslation[NumEyes];
|
||||
Matrix4x4 mEyeProjectionMatrix[NumEyes];
|
||||
VRDistortionMesh mDistortionMesh[NumEyes];
|
||||
uint32_t mSupportedSensorBits;
|
||||
|
||||
VRFieldOfView mRecommendedEyeFOV[NumEyes];
|
||||
VRFieldOfView mMaximumEyeFOV[NumEyes];
|
||||
|
||||
nsCOMPtr<nsIScreen> mScreen;
|
||||
};
|
||||
|
||||
class VRHMDManagerOculusImpl;
|
||||
class VRHMDManagerOculus {
|
||||
static VRHMDManagerOculusImpl *mImpl;
|
||||
public:
|
||||
static bool PlatformInit();
|
||||
static bool Init();
|
||||
static void Destroy();
|
||||
static void GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult);
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* GFX_VR_H */
|
@ -87,16 +87,18 @@ DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
|
||||
if (aDrawTarget.GetBackendType() == BackendType::CAIRO) {
|
||||
cairo_surface_t *surf = (cairo_surface_t*)
|
||||
aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
|
||||
cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
|
||||
if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
|
||||
surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
|
||||
mDC = cairo_win32_surface_get_dc(surf);
|
||||
mNeedsRelease = false;
|
||||
SaveDC(mDC);
|
||||
cairo_t* ctx = (cairo_t*)
|
||||
aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
|
||||
cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
|
||||
cairo_win32_scaled_font_select_font(scaled, mDC);
|
||||
if (surf) {
|
||||
cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
|
||||
if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
|
||||
surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
|
||||
mDC = cairo_win32_surface_get_dc(surf);
|
||||
mNeedsRelease = false;
|
||||
SaveDC(mDC);
|
||||
cairo_t* ctx = (cairo_t*)
|
||||
aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
|
||||
cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
|
||||
cairo_win32_scaled_font_select_font(scaled, mDC);
|
||||
}
|
||||
}
|
||||
if (!mDC) {
|
||||
mDC = GetDC(nullptr);
|
||||
|
@ -49,6 +49,7 @@ EXPORTS += [
|
||||
'gfxTypes.h',
|
||||
'gfxUserFontSet.h',
|
||||
'gfxUtils.h',
|
||||
'gfxVR.h',
|
||||
'GraphicsFilter.h',
|
||||
'RoundedRect.h',
|
||||
]
|
||||
@ -243,6 +244,7 @@ UNIFIED_SOURCES += [
|
||||
'gfxTextRun.cpp',
|
||||
'gfxUserFontSet.cpp',
|
||||
'gfxUtils.cpp',
|
||||
'gfxVR.cpp',
|
||||
'nsUnicodeRange.cpp',
|
||||
]
|
||||
|
||||
|
228
gfx/thebes/ovr_capi_dynamic.h
Normal file
228
gfx/thebes/ovr_capi_dynamic.h
Normal file
@ -0,0 +1,228 @@
|
||||
/* -*- 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_ */
|
@ -754,6 +754,29 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx)
|
||||
MOZ_ASSERT(isStaticallyLinked());
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
static inline size_t
|
||||
ViewTypeByteSize(AsmJSHeapAccess::ViewType vt)
|
||||
{
|
||||
switch (vt) {
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8:
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32:
|
||||
case AsmJSHeapAccess::Float32:
|
||||
case AsmJSHeapAccess::Float64:
|
||||
return 1 << TypedArrayShift(Scalar::Type(vt));
|
||||
case AsmJSHeapAccess::Float32x4:
|
||||
case AsmJSHeapAccess::Int32x4:
|
||||
return 16;
|
||||
}
|
||||
MOZ_CRASH("unexpected view type");
|
||||
}
|
||||
#endif // JS_CODEGEN_X86 || JS_CODEGEN_X64
|
||||
|
||||
void
|
||||
AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx)
|
||||
{
|
||||
@ -767,11 +790,17 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
uint8_t *heapOffset = heap->dataPointer();
|
||||
void *heapLength = (void*)heap->byteLength();
|
||||
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
|
||||
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
|
||||
if (access.hasLengthCheck())
|
||||
X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
|
||||
if (access.hasLengthCheck()) {
|
||||
// An access is out-of-bounds iff
|
||||
// ptr + data-type-byte-size > heapLength
|
||||
// i.e. ptr >= heapLength + 1 - data-type-byte-size
|
||||
// (Note that we need >= as this is what codegen uses.)
|
||||
AsmJSHeapAccess::ViewType vt = access.viewType();
|
||||
X86Assembler::setPointer(access.patchLengthAt(code_),
|
||||
(void*)(heap->byteLength() + 1 - ViewTypeByteSize(vt)));
|
||||
}
|
||||
void *addr = access.patchOffsetAt(code_);
|
||||
uint32_t disp = reinterpret_cast<uint32_t>(X86Assembler::getPointer(addr));
|
||||
MOZ_ASSERT(disp <= INT32_MAX);
|
||||
@ -788,8 +817,11 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx
|
||||
int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
|
||||
for (size_t i = 0; i < heapAccesses_.length(); i++) {
|
||||
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
|
||||
if (access.hasLengthCheck())
|
||||
X86Assembler::setInt32(access.patchLengthAt(code_), heapLength);
|
||||
if (access.hasLengthCheck()) {
|
||||
// See comment above for x86 codegen.
|
||||
X86Assembler::setInt32(access.patchLengthAt(code_),
|
||||
heapLength + 1 - ViewTypeByteSize(access.viewType()));
|
||||
}
|
||||
}
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
|
||||
uint32_t heapLength = heap->byteLength();
|
||||
|
@ -331,45 +331,72 @@ ContextToPC(CONTEXT *context)
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
template <class T>
|
||||
static void
|
||||
SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
|
||||
SetXMMRegToNaN(AsmJSHeapAccess::ViewType viewType, T *xmm_reg)
|
||||
{
|
||||
if (isFloat32) {
|
||||
switch (viewType) {
|
||||
case AsmJSHeapAccess::Float32: {
|
||||
JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
|
||||
float *floats = reinterpret_cast<float*>(xmm_reg);
|
||||
floats[0] = GenericNaN();
|
||||
floats[1] = 0;
|
||||
floats[2] = 0;
|
||||
floats[3] = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case AsmJSHeapAccess::Float64: {
|
||||
JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
|
||||
double *dbls = reinterpret_cast<double*>(xmm_reg);
|
||||
dbls[0] = GenericNaN();
|
||||
dbls[1] = 0;
|
||||
break;
|
||||
}
|
||||
case AsmJSHeapAccess::Float32x4: {
|
||||
JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
|
||||
float *floats = reinterpret_cast<float*>(xmm_reg);
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
floats[i] = GenericNaN();
|
||||
break;
|
||||
}
|
||||
case AsmJSHeapAccess::Int32x4: {
|
||||
JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(int32_t));
|
||||
int32_t *ints = reinterpret_cast<int32_t*>(xmm_reg);
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
ints[i] = 0;
|
||||
break;
|
||||
}
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8:
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32:
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
MOZ_CRASH("unexpected type in SetXMMRegToNaN");
|
||||
}
|
||||
}
|
||||
|
||||
# if !defined(XP_MACOSX)
|
||||
static void
|
||||
SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg)
|
||||
SetRegisterToCoercedUndefined(CONTEXT *context, AsmJSHeapAccess::ViewType viewType, AnyRegister reg)
|
||||
{
|
||||
if (reg.isFloat()) {
|
||||
switch (reg.fpu().code()) {
|
||||
case X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 0)); break;
|
||||
case X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 1)); break;
|
||||
case X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 2)); break;
|
||||
case X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 3)); break;
|
||||
case X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 4)); break;
|
||||
case X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 5)); break;
|
||||
case X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 6)); break;
|
||||
case X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 7)); break;
|
||||
case X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 8)); break;
|
||||
case X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 9)); break;
|
||||
case X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 10)); break;
|
||||
case X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 11)); break;
|
||||
case X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 12)); break;
|
||||
case X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 13)); break;
|
||||
case X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 14)); break;
|
||||
case X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 15)); break;
|
||||
case X86Registers::xmm0: SetXMMRegToNaN(viewType, &XMM_sig(context, 0)); break;
|
||||
case X86Registers::xmm1: SetXMMRegToNaN(viewType, &XMM_sig(context, 1)); break;
|
||||
case X86Registers::xmm2: SetXMMRegToNaN(viewType, &XMM_sig(context, 2)); break;
|
||||
case X86Registers::xmm3: SetXMMRegToNaN(viewType, &XMM_sig(context, 3)); break;
|
||||
case X86Registers::xmm4: SetXMMRegToNaN(viewType, &XMM_sig(context, 4)); break;
|
||||
case X86Registers::xmm5: SetXMMRegToNaN(viewType, &XMM_sig(context, 5)); break;
|
||||
case X86Registers::xmm6: SetXMMRegToNaN(viewType, &XMM_sig(context, 6)); break;
|
||||
case X86Registers::xmm7: SetXMMRegToNaN(viewType, &XMM_sig(context, 7)); break;
|
||||
case X86Registers::xmm8: SetXMMRegToNaN(viewType, &XMM_sig(context, 8)); break;
|
||||
case X86Registers::xmm9: SetXMMRegToNaN(viewType, &XMM_sig(context, 9)); break;
|
||||
case X86Registers::xmm10: SetXMMRegToNaN(viewType, &XMM_sig(context, 10)); break;
|
||||
case X86Registers::xmm11: SetXMMRegToNaN(viewType, &XMM_sig(context, 11)); break;
|
||||
case X86Registers::xmm12: SetXMMRegToNaN(viewType, &XMM_sig(context, 12)); break;
|
||||
case X86Registers::xmm13: SetXMMRegToNaN(viewType, &XMM_sig(context, 13)); break;
|
||||
case X86Registers::xmm14: SetXMMRegToNaN(viewType, &XMM_sig(context, 14)); break;
|
||||
case X86Registers::xmm15: SetXMMRegToNaN(viewType, &XMM_sig(context, 15)); break;
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
} else {
|
||||
@ -455,7 +482,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
// register) and set the PC to the next op. Upon return from the handler,
|
||||
// execution will resume at this next PC.
|
||||
if (heapAccess->isLoad())
|
||||
SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg());
|
||||
SetRegisterToCoercedUndefined(context, heapAccess->viewType(), heapAccess->loadedReg());
|
||||
*ppc += heapAccess->opLength();
|
||||
|
||||
return true;
|
||||
@ -505,24 +532,24 @@ SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
|
||||
if (kret != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
bool f32 = heapAccess.isFloat32Load();
|
||||
AsmJSHeapAccess::ViewType viewType = heapAccess.viewType();
|
||||
switch (heapAccess.loadedReg().fpu().code()) {
|
||||
case X86Registers::xmm0: SetXMMRegToNaN(f32, &fstate.__fpu_xmm0); break;
|
||||
case X86Registers::xmm1: SetXMMRegToNaN(f32, &fstate.__fpu_xmm1); break;
|
||||
case X86Registers::xmm2: SetXMMRegToNaN(f32, &fstate.__fpu_xmm2); break;
|
||||
case X86Registers::xmm3: SetXMMRegToNaN(f32, &fstate.__fpu_xmm3); break;
|
||||
case X86Registers::xmm4: SetXMMRegToNaN(f32, &fstate.__fpu_xmm4); break;
|
||||
case X86Registers::xmm5: SetXMMRegToNaN(f32, &fstate.__fpu_xmm5); break;
|
||||
case X86Registers::xmm6: SetXMMRegToNaN(f32, &fstate.__fpu_xmm6); break;
|
||||
case X86Registers::xmm7: SetXMMRegToNaN(f32, &fstate.__fpu_xmm7); break;
|
||||
case X86Registers::xmm8: SetXMMRegToNaN(f32, &fstate.__fpu_xmm8); break;
|
||||
case X86Registers::xmm9: SetXMMRegToNaN(f32, &fstate.__fpu_xmm9); break;
|
||||
case X86Registers::xmm10: SetXMMRegToNaN(f32, &fstate.__fpu_xmm10); break;
|
||||
case X86Registers::xmm11: SetXMMRegToNaN(f32, &fstate.__fpu_xmm11); break;
|
||||
case X86Registers::xmm12: SetXMMRegToNaN(f32, &fstate.__fpu_xmm12); break;
|
||||
case X86Registers::xmm13: SetXMMRegToNaN(f32, &fstate.__fpu_xmm13); break;
|
||||
case X86Registers::xmm14: SetXMMRegToNaN(f32, &fstate.__fpu_xmm14); break;
|
||||
case X86Registers::xmm15: SetXMMRegToNaN(f32, &fstate.__fpu_xmm15); break;
|
||||
case X86Registers::xmm0: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm0); break;
|
||||
case X86Registers::xmm1: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm1); break;
|
||||
case X86Registers::xmm2: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm2); break;
|
||||
case X86Registers::xmm3: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm3); break;
|
||||
case X86Registers::xmm4: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm4); break;
|
||||
case X86Registers::xmm5: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm5); break;
|
||||
case X86Registers::xmm6: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm6); break;
|
||||
case X86Registers::xmm7: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm7); break;
|
||||
case X86Registers::xmm8: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm8); break;
|
||||
case X86Registers::xmm9: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm9); break;
|
||||
case X86Registers::xmm10: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm10); break;
|
||||
case X86Registers::xmm11: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm11); break;
|
||||
case X86Registers::xmm12: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm12); break;
|
||||
case X86Registers::xmm13: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm13); break;
|
||||
case X86Registers::xmm14: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm14); break;
|
||||
case X86Registers::xmm15: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm15); break;
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
|
||||
@ -847,7 +874,7 @@ HandleFault(int signum, siginfo_t *info, void *ctx)
|
||||
// register) and set the PC to the next op. Upon return from the handler,
|
||||
// execution will resume at this next PC.
|
||||
if (heapAccess->isLoad())
|
||||
SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg());
|
||||
SetRegisterToCoercedUndefined(context, heapAccess->viewType(), heapAccess->loadedReg());
|
||||
*ppc += heapAccess->opLength();
|
||||
|
||||
return true;
|
||||
|
@ -566,13 +566,13 @@ class Type
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
|
||||
}
|
||||
|
||||
Type simdToScalarType() const {
|
||||
AsmJSSimdType simdType() const {
|
||||
MOZ_ASSERT(isSimd());
|
||||
switch (which_) {
|
||||
case Int32x4:
|
||||
return Signed;
|
||||
return AsmJSSimdType_int32x4;
|
||||
case Float32x4:
|
||||
return Float;
|
||||
return AsmJSSimdType_float32x4;
|
||||
// Scalar types
|
||||
case Double:
|
||||
case DoubleLit:
|
||||
@ -588,32 +588,7 @@ class Type
|
||||
case Void:
|
||||
break;
|
||||
}
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type");
|
||||
}
|
||||
|
||||
Type simdToCoercedScalarType() const {
|
||||
MOZ_ASSERT(isSimd());
|
||||
switch (which_) {
|
||||
case Int32x4:
|
||||
return Intish;
|
||||
case Float32x4:
|
||||
return Floatish;
|
||||
// Scalar types
|
||||
case Double:
|
||||
case DoubleLit:
|
||||
case MaybeDouble:
|
||||
case Float:
|
||||
case MaybeFloat:
|
||||
case Floatish:
|
||||
case Fixnum:
|
||||
case Int:
|
||||
case Signed:
|
||||
case Unsigned:
|
||||
case Intish:
|
||||
case Void:
|
||||
break;
|
||||
}
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type");
|
||||
MOZ_CRASH("not a SIMD Type");
|
||||
}
|
||||
|
||||
const char *toChars() const {
|
||||
@ -2781,7 +2756,7 @@ class FunctionCompiler
|
||||
curBlock_->setSlot(info().localSlot(local.slot), def);
|
||||
}
|
||||
|
||||
MDefinition *loadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk)
|
||||
MDefinition *loadHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
@ -2792,7 +2767,8 @@ class FunctionCompiler
|
||||
return load;
|
||||
}
|
||||
|
||||
void storeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
|
||||
void storeHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, MDefinition *v,
|
||||
NeedsBoundsCheck chk)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return;
|
||||
@ -2810,7 +2786,7 @@ class FunctionCompiler
|
||||
curBlock_->add(ins);
|
||||
}
|
||||
|
||||
MDefinition *atomicLoadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk)
|
||||
MDefinition *atomicLoadHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
@ -2822,7 +2798,8 @@ class FunctionCompiler
|
||||
return load;
|
||||
}
|
||||
|
||||
void atomicStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
|
||||
void atomicStoreHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, MDefinition *v,
|
||||
NeedsBoundsCheck chk)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return;
|
||||
@ -2833,7 +2810,8 @@ class FunctionCompiler
|
||||
curBlock_->add(store);
|
||||
}
|
||||
|
||||
MDefinition *atomicCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv, NeedsBoundsCheck chk)
|
||||
MDefinition *atomicCompareExchangeHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr,
|
||||
MDefinition *oldv, MDefinition *newv, NeedsBoundsCheck chk)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
@ -2846,7 +2824,8 @@ class FunctionCompiler
|
||||
return cas;
|
||||
}
|
||||
|
||||
MDefinition *atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
|
||||
MDefinition *atomicBinopHeap(js::jit::AtomicOp op, AsmJSHeapAccess::ViewType vt,
|
||||
MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
@ -4474,7 +4453,7 @@ CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *ty
|
||||
if (!CheckArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &pointerDef, &needsBoundsCheck))
|
||||
return false;
|
||||
|
||||
*def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
|
||||
*def = f.loadHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, needsBoundsCheck);
|
||||
*type = TypedArrayLoadType(viewType);
|
||||
return true;
|
||||
}
|
||||
@ -4515,7 +4494,11 @@ CheckDotAccess(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *ty
|
||||
else
|
||||
return f.fail(base, "dot access field must be a lane name (x, y, z, w) or signMask");
|
||||
|
||||
*type = baseType.simdToScalarType();
|
||||
switch (baseType.simdType()) {
|
||||
case AsmJSSimdType_int32x4: *type = Type::Signed; break;
|
||||
case AsmJSSimdType_float32x4: *type = Type::Float; break;
|
||||
}
|
||||
|
||||
*def = f.extractSimdElement(lane, baseDef, type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
@ -4564,7 +4547,7 @@ CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition
|
||||
MOZ_CRASH("Unexpected view type");
|
||||
}
|
||||
|
||||
f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
|
||||
f.storeHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, rhsDef, needsBoundsCheck);
|
||||
|
||||
*def = rhsDef;
|
||||
*type = rhsType;
|
||||
@ -4832,7 +4815,7 @@ CheckAtomicsLoad(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &pointerDef, &needsBoundsCheck))
|
||||
return false;
|
||||
|
||||
*def = f.atomicLoadHeap(viewType, pointerDef, needsBoundsCheck);
|
||||
*def = f.atomicLoadHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, needsBoundsCheck);
|
||||
*type = Type::Signed;
|
||||
return true;
|
||||
}
|
||||
@ -4861,7 +4844,7 @@ CheckAtomicsStore(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type
|
||||
if (!rhsType.isIntish())
|
||||
return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
|
||||
|
||||
f.atomicStoreHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
|
||||
f.atomicStoreHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, rhsDef, needsBoundsCheck);
|
||||
|
||||
*def = rhsDef;
|
||||
*type = Type::Signed;
|
||||
@ -4892,7 +4875,8 @@ CheckAtomicsBinop(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type
|
||||
if (!valueArgType.isIntish())
|
||||
return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
|
||||
|
||||
*def = f.atomicBinopHeap(op, viewType, pointerDef, valueArgDef, needsBoundsCheck);
|
||||
*def = f.atomicBinopHeap(op, AsmJSHeapAccess::ViewType(viewType), pointerDef, valueArgDef,
|
||||
needsBoundsCheck);
|
||||
*type = Type::Signed;
|
||||
return true;
|
||||
}
|
||||
@ -4930,7 +4914,8 @@ CheckAtomicsCompareExchange(FunctionCompiler &f, ParseNode *call, MDefinition **
|
||||
if (!newValueArgType.isIntish())
|
||||
return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
|
||||
|
||||
*def = f.atomicCompareExchangeHeap(viewType, pointerDef, oldValueArgDef, newValueArgDef, needsBoundsCheck);
|
||||
*def = f.atomicCompareExchangeHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef,
|
||||
oldValueArgDef, newValueArgDef, needsBoundsCheck);
|
||||
*type = Type::Signed;
|
||||
return true;
|
||||
}
|
||||
@ -5365,7 +5350,7 @@ class CheckArgIsSubtypeOf
|
||||
Type formalType_;
|
||||
|
||||
public:
|
||||
explicit CheckArgIsSubtypeOf(Type t) : formalType_(t) {}
|
||||
explicit CheckArgIsSubtypeOf(AsmJSSimdType t) : formalType_(t) {}
|
||||
|
||||
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
|
||||
MDefinition **def) const
|
||||
@ -5378,14 +5363,26 @@ class CheckArgIsSubtypeOf
|
||||
}
|
||||
};
|
||||
|
||||
static inline Type
|
||||
SimdToCoercedScalarType(AsmJSSimdType t)
|
||||
{
|
||||
switch (t) {
|
||||
case AsmJSSimdType_int32x4:
|
||||
return Type::Intish;
|
||||
case AsmJSSimdType_float32x4:
|
||||
return Type::Floatish;
|
||||
}
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
|
||||
}
|
||||
|
||||
class CheckSimdScalarArgs
|
||||
{
|
||||
Type simdType_;
|
||||
AsmJSSimdType simdType_;
|
||||
Type formalType_;
|
||||
|
||||
public:
|
||||
CheckSimdScalarArgs(Type simdType)
|
||||
: simdType_(simdType), formalType_(simdType.simdToCoercedScalarType())
|
||||
CheckSimdScalarArgs(AsmJSSimdType simdType)
|
||||
: simdType_(simdType), formalType_(SimdToCoercedScalarType(simdType))
|
||||
{}
|
||||
|
||||
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
|
||||
@ -5394,10 +5391,10 @@ class CheckSimdScalarArgs
|
||||
if (!(actualType <= formalType_)) {
|
||||
// As a special case, accept doublelit arguments to float32x4 ops by
|
||||
// re-emitting them as float32 constants.
|
||||
if (!simdType_.isFloat32x4() || !actualType.isDoubleLit()) {
|
||||
if (simdType_ != AsmJSSimdType_float32x4 || !actualType.isDoubleLit()) {
|
||||
return f.failf(arg, "%s is not a subtype of %s%s",
|
||||
actualType.toChars(), formalType_.toChars(),
|
||||
simdType_.isFloat32x4() ? " or doublelit" : "");
|
||||
simdType_ == AsmJSSimdType_float32x4 ? " or doublelit" : "");
|
||||
}
|
||||
|
||||
AsmJSNumLit doubleLit = ExtractNumericLiteral(f.m(), arg);
|
||||
@ -5413,7 +5410,7 @@ class CheckSimdSelectArgs
|
||||
Type formalType_;
|
||||
|
||||
public:
|
||||
explicit CheckSimdSelectArgs(Type t) : formalType_(t) {}
|
||||
explicit CheckSimdSelectArgs(AsmJSSimdType t) : formalType_(t) {}
|
||||
|
||||
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
|
||||
MDefinition **def) const
|
||||
@ -5435,10 +5432,10 @@ class CheckSimdSelectArgs
|
||||
|
||||
class CheckSimdVectorScalarArgs
|
||||
{
|
||||
Type formalType_;
|
||||
AsmJSSimdType formalSimdType_;
|
||||
|
||||
public:
|
||||
explicit CheckSimdVectorScalarArgs(Type t) : formalType_(t) {}
|
||||
explicit CheckSimdVectorScalarArgs(AsmJSSimdType t) : formalSimdType_(t) {}
|
||||
|
||||
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
|
||||
MDefinition **def) const
|
||||
@ -5446,15 +5443,15 @@ class CheckSimdVectorScalarArgs
|
||||
MOZ_ASSERT(argIndex < 2);
|
||||
if (argIndex == 0) {
|
||||
// First argument is the vector
|
||||
if (!(actualType <= formalType_)) {
|
||||
if (!(actualType <= Type(formalSimdType_))) {
|
||||
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
|
||||
formalType_.toChars());
|
||||
Type(formalSimdType_).toChars());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second argument is the scalar
|
||||
Type coercedFormalType = formalType_.simdToCoercedScalarType();
|
||||
Type coercedFormalType = SimdToCoercedScalarType(formalSimdType_);
|
||||
if (!(actualType <= coercedFormalType)) {
|
||||
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
|
||||
coercedFormalType.toChars());
|
||||
@ -5466,82 +5463,87 @@ class CheckSimdVectorScalarArgs
|
||||
} // anonymous namespace
|
||||
|
||||
static inline bool
|
||||
CheckSimdUnary(FunctionCompiler &f, ParseNode *call, Type retType, MSimdUnaryArith::Operation op,
|
||||
MDefinition **def, Type *type)
|
||||
CheckSimdUnary(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType,
|
||||
MSimdUnaryArith::Operation op, MDefinition **def, Type *type)
|
||||
{
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(retType), &defs))
|
||||
if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType), &defs))
|
||||
return false;
|
||||
*def = f.unarySimd(defs[0], op, retType.toMIRType());
|
||||
*type = retType;
|
||||
*type = opType;
|
||||
*def = f.unarySimd(defs[0], op, type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class OpEnum>
|
||||
static inline bool
|
||||
CheckSimdBinary(FunctionCompiler &f, ParseNode *call, Type retType, OpEnum op, MDefinition **def,
|
||||
Type *type)
|
||||
CheckSimdBinary(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, OpEnum op,
|
||||
MDefinition **def, Type *type)
|
||||
{
|
||||
DefinitionVector argDefs;
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(retType), &argDefs))
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType), &argDefs))
|
||||
return false;
|
||||
*def = f.binarySimd(argDefs[0], argDefs[1], op, retType.toMIRType());
|
||||
*type = retType;
|
||||
*type = opType;
|
||||
*def = f.binarySimd(argDefs[0], argDefs[1], op, type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
CheckSimdBinary<MSimdBinaryComp::Operation>(FunctionCompiler &f, ParseNode *call, Type retType,
|
||||
MSimdBinaryComp::Operation op, MDefinition **def,
|
||||
Type *type)
|
||||
CheckSimdBinary<MSimdBinaryComp::Operation>(FunctionCompiler &f, ParseNode *call,
|
||||
AsmJSSimdType opType, MSimdBinaryComp::Operation op,
|
||||
MDefinition **def, Type *type)
|
||||
{
|
||||
DefinitionVector argDefs;
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(retType), &argDefs))
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType), &argDefs))
|
||||
return false;
|
||||
*def = f.binarySimd<MSimdBinaryComp>(argDefs[0], argDefs[1], op);
|
||||
*type = Type::Int32x4;
|
||||
*def = f.binarySimd<MSimdBinaryComp>(argDefs[0], argDefs[1], op);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
CheckSimdBinary<MSimdShift::Operation>(FunctionCompiler &f, ParseNode *call, Type retType,
|
||||
CheckSimdBinary<MSimdShift::Operation>(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType,
|
||||
MSimdShift::Operation op, MDefinition **def, Type *type)
|
||||
{
|
||||
DefinitionVector argDefs;
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &argDefs))
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType), &argDefs))
|
||||
return false;
|
||||
*def = f.binarySimd<MSimdShift>(argDefs[0], argDefs[1], op);
|
||||
*type = Type::Int32x4;
|
||||
*def = f.binarySimd<MSimdShift>(argDefs[0], argDefs[1], op);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdWith(FunctionCompiler &f, ParseNode *call, Type retType, SimdLane lane, MDefinition **def,
|
||||
Type *type)
|
||||
CheckSimdWith(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, SimdLane lane,
|
||||
MDefinition **def, Type *type)
|
||||
{
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &defs))
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType), &defs))
|
||||
return false;
|
||||
*def = f.insertElementSimd(defs[0], defs[1], lane, retType.toMIRType());
|
||||
*type = retType;
|
||||
*type = opType;
|
||||
*def = f.insertElementSimd(defs[0], defs[1], lane, type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Include CheckSimdCast in unnamed namespace to avoid MSVC name lookup bug (due to the use of Type).
|
||||
|
||||
template<class T>
|
||||
static bool
|
||||
CheckSimdCast(FunctionCompiler &f, ParseNode *call, Type fromType, Type toType, MDefinition **def,
|
||||
Type *type)
|
||||
CheckSimdCast(FunctionCompiler &f, ParseNode *call, AsmJSSimdType fromType, AsmJSSimdType toType,
|
||||
MDefinition **def, Type *type)
|
||||
{
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(fromType), &defs))
|
||||
return false;
|
||||
*def = f.convertSimd<T>(defs[0], fromType.toMIRType(), toType.toMIRType());
|
||||
*type = toType;
|
||||
*def = f.convertSimd<T>(defs[0], Type(fromType).toMIRType(), type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdShuffleSelectors(FunctionCompiler &f, ParseNode *lane, int32_t lanes[4], uint32_t maxLane)
|
||||
{
|
||||
@ -5557,12 +5559,14 @@ CheckSimdShuffleSelectors(FunctionCompiler &f, ParseNode *lane, int32_t lanes[4]
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdSwizzle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition **def, Type *type)
|
||||
CheckSimdSwizzle(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def,
|
||||
Type *type)
|
||||
{
|
||||
unsigned numArgs = CallArgListLength(call);
|
||||
if (numArgs != 5)
|
||||
return f.failf(call, "expected 5 arguments to SIMD swizzle, got %u", numArgs);
|
||||
|
||||
Type retType = opType;
|
||||
ParseNode *vec = CallArgList(call);
|
||||
MDefinition *vecDef;
|
||||
Type vecType;
|
||||
@ -5581,12 +5585,14 @@ CheckSimdSwizzle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition **def, Type *type)
|
||||
CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def,
|
||||
Type *type)
|
||||
{
|
||||
unsigned numArgs = CallArgListLength(call);
|
||||
if (numArgs != 6)
|
||||
return f.failf(call, "expected 6 arguments to SIMD shuffle, got %u", numArgs);
|
||||
|
||||
Type retType = opType;
|
||||
ParseNode *arg = CallArgList(call);
|
||||
MDefinition *vecs[2];
|
||||
for (unsigned i = 0; i < 2; i++, arg = NextNode(arg)) {
|
||||
@ -5607,107 +5613,216 @@ CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdLoadStoreArgs(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType,
|
||||
AsmJSHeapAccess::ViewType *viewType, MDefinition **index,
|
||||
NeedsBoundsCheck *needsBoundsCheck)
|
||||
{
|
||||
ParseNode *view = CallArgList(call);
|
||||
if (!view->isKind(PNK_NAME))
|
||||
return f.fail(view, "expected Uint8Array view as SIMD.*.store first argument");
|
||||
|
||||
const ModuleCompiler::Global *global = f.lookupGlobal(view->name());
|
||||
if (!global ||
|
||||
global->which() != ModuleCompiler::Global::ArrayView ||
|
||||
global->viewType() != Scalar::Uint8)
|
||||
{
|
||||
return f.fail(view, "expected Uint8Array view as SIMD.*.store first argument");
|
||||
}
|
||||
|
||||
*needsBoundsCheck = NEEDS_BOUNDS_CHECK;
|
||||
|
||||
switch (opType) {
|
||||
case AsmJSSimdType_int32x4: *viewType = AsmJSHeapAccess::Int32x4; break;
|
||||
case AsmJSSimdType_float32x4: *viewType = AsmJSHeapAccess::Float32x4; break;
|
||||
}
|
||||
|
||||
ParseNode *indexExpr = NextNode(view);
|
||||
uint32_t indexLit;
|
||||
if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
|
||||
if (indexLit > INT32_MAX)
|
||||
return f.fail(indexExpr, "constant index out of range");
|
||||
|
||||
if (!f.m().tryRequireHeapLengthToBeAtLeast(indexLit + Simd128DataSize)) {
|
||||
return f.failf(indexExpr, "constant index outside heap size range declared by the "
|
||||
"change-heap function (0x%x - 0x%x)",
|
||||
f.m().minHeapLength(), f.m().module().maxHeapLength());
|
||||
}
|
||||
|
||||
*needsBoundsCheck = NO_BOUNDS_CHECK;
|
||||
*index = f.constant(Int32Value(indexLit), Type::Int);
|
||||
return true;
|
||||
}
|
||||
|
||||
f.enterHeapExpression();
|
||||
|
||||
Type indexType;
|
||||
if (!CheckExpr(f, indexExpr, index, &indexType))
|
||||
return false;
|
||||
if (!indexType.isIntish())
|
||||
return f.failf(indexExpr, "%s is not a subtype of intish", indexType.toChars());
|
||||
|
||||
f.leaveHeapExpression();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdLoad(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def, Type *type)
|
||||
{
|
||||
unsigned numArgs = CallArgListLength(call);
|
||||
if (numArgs != 2)
|
||||
return f.failf(call, "expected 2 arguments to SIMD load, got %u", numArgs);
|
||||
|
||||
AsmJSHeapAccess::ViewType viewType;
|
||||
MDefinition *index;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &index, &needsBoundsCheck))
|
||||
return false;
|
||||
|
||||
*def = f.loadHeap(viewType, index, needsBoundsCheck);
|
||||
*type = opType;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdStore(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def, Type *type)
|
||||
{
|
||||
unsigned numArgs = CallArgListLength(call);
|
||||
if (numArgs != 3)
|
||||
return f.failf(call, "expected 3 arguments to SIMD load, got %u", numArgs);
|
||||
|
||||
AsmJSHeapAccess::ViewType viewType;
|
||||
MDefinition *index;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &index, &needsBoundsCheck))
|
||||
return false;
|
||||
|
||||
Type retType = opType;
|
||||
ParseNode *vecExpr = NextNode(NextNode(CallArgList(call)));
|
||||
MDefinition *vec;
|
||||
Type vecType;
|
||||
if (!CheckExpr(f, vecExpr, &vec, &vecType))
|
||||
return false;
|
||||
if (!(vecType <= retType))
|
||||
return f.failf(vecExpr, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
|
||||
|
||||
f.storeHeap(viewType, index, vec, needsBoundsCheck);
|
||||
*def = vec;
|
||||
*type = vecType;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
|
||||
MDefinition **def, Type *type)
|
||||
{
|
||||
MOZ_ASSERT(global->isSimdOperation());
|
||||
|
||||
Type retType = global->simdOperationType();
|
||||
AsmJSSimdType opType = global->simdOperationType();
|
||||
|
||||
switch (global->simdOperation()) {
|
||||
case AsmJSSimdOperation_add:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Add, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Add, def, type);
|
||||
case AsmJSSimdOperation_sub:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Sub, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Sub, def, type);
|
||||
case AsmJSSimdOperation_mul:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Mul, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Mul, def, type);
|
||||
case AsmJSSimdOperation_div:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Div, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Div, def, type);
|
||||
case AsmJSSimdOperation_max:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Max, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Max, def, type);
|
||||
case AsmJSSimdOperation_min:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Min, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Min, def, type);
|
||||
case AsmJSSimdOperation_maxNum:
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MaxNum, def, type);
|
||||
case AsmJSSimdOperation_minNum:
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MinNum, def, type);
|
||||
|
||||
case AsmJSSimdOperation_lessThan:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::lessThan, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThan, def, type);
|
||||
case AsmJSSimdOperation_lessThanOrEqual:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::lessThanOrEqual, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThanOrEqual, def, type);
|
||||
case AsmJSSimdOperation_equal:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::equal, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::equal, def, type);
|
||||
case AsmJSSimdOperation_notEqual:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::notEqual, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::notEqual, def, type);
|
||||
case AsmJSSimdOperation_greaterThan:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::greaterThan, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::greaterThan, def, type);
|
||||
case AsmJSSimdOperation_greaterThanOrEqual:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::greaterThanOrEqual, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::greaterThanOrEqual, def, type);
|
||||
|
||||
case AsmJSSimdOperation_and:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::and_, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::and_, def, type);
|
||||
case AsmJSSimdOperation_or:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::or_, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::or_, def, type);
|
||||
case AsmJSSimdOperation_xor:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::xor_, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::xor_, def, type);
|
||||
|
||||
case AsmJSSimdOperation_withX:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneX, def, type);
|
||||
return CheckSimdWith(f, call, opType, SimdLane::LaneX, def, type);
|
||||
case AsmJSSimdOperation_withY:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneY, def, type);
|
||||
return CheckSimdWith(f, call, opType, SimdLane::LaneY, def, type);
|
||||
case AsmJSSimdOperation_withZ:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneZ, def, type);
|
||||
return CheckSimdWith(f, call, opType, SimdLane::LaneZ, def, type);
|
||||
case AsmJSSimdOperation_withW:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneW, def, type);
|
||||
return CheckSimdWith(f, call, opType, SimdLane::LaneW, def, type);
|
||||
|
||||
case AsmJSSimdOperation_fromInt32x4:
|
||||
return CheckSimdCast<MSimdConvert>(f, call, Type::Int32x4, retType, def, type);
|
||||
return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_int32x4, opType, def, type);
|
||||
case AsmJSSimdOperation_fromInt32x4Bits:
|
||||
return CheckSimdCast<MSimdReinterpretCast>(f, call, Type::Int32x4, retType, def, type);
|
||||
return CheckSimdCast<MSimdReinterpretCast>(f, call, AsmJSSimdType_int32x4, opType, def, type);
|
||||
case AsmJSSimdOperation_fromFloat32x4:
|
||||
return CheckSimdCast<MSimdConvert>(f, call, Type::Float32x4, retType, def, type);
|
||||
return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_float32x4, opType, def, type);
|
||||
case AsmJSSimdOperation_fromFloat32x4Bits:
|
||||
return CheckSimdCast<MSimdReinterpretCast>(f, call, Type::Float32x4, retType, def, type);
|
||||
return CheckSimdCast<MSimdReinterpretCast>(f, call, AsmJSSimdType_float32x4, opType, def, type);
|
||||
|
||||
case AsmJSSimdOperation_shiftLeft:
|
||||
return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::lsh, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdShift::lsh, def, type);
|
||||
case AsmJSSimdOperation_shiftRight:
|
||||
return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::rsh, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdShift::rsh, def, type);
|
||||
case AsmJSSimdOperation_shiftRightLogical:
|
||||
return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::ursh, def, type);
|
||||
return CheckSimdBinary(f, call, opType, MSimdShift::ursh, def, type);
|
||||
|
||||
case AsmJSSimdOperation_abs:
|
||||
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::abs, def, type);
|
||||
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::abs, def, type);
|
||||
case AsmJSSimdOperation_neg:
|
||||
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::neg, def, type);
|
||||
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::neg, def, type);
|
||||
case AsmJSSimdOperation_not:
|
||||
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::not_, def, type);
|
||||
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::not_, def, type);
|
||||
case AsmJSSimdOperation_sqrt:
|
||||
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::sqrt, def, type);
|
||||
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::sqrt, def, type);
|
||||
case AsmJSSimdOperation_reciprocal:
|
||||
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::reciprocal, def, type);
|
||||
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::reciprocal, def, type);
|
||||
case AsmJSSimdOperation_reciprocalSqrt:
|
||||
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::reciprocalSqrt, def, type);
|
||||
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::reciprocalSqrt, def, type);
|
||||
|
||||
case AsmJSSimdOperation_swizzle:
|
||||
return CheckSimdSwizzle(f, call, retType, def, type);
|
||||
return CheckSimdSwizzle(f, call, opType, def, type);
|
||||
case AsmJSSimdOperation_shuffle:
|
||||
return CheckSimdShuffle(f, call, retType, def, type);
|
||||
return CheckSimdShuffle(f, call, opType, def, type);
|
||||
|
||||
case AsmJSSimdOperation_load:
|
||||
return CheckSimdLoad(f, call, opType, def, type);
|
||||
case AsmJSSimdOperation_store:
|
||||
return CheckSimdStore(f, call, opType, def, type);
|
||||
|
||||
case AsmJSSimdOperation_splat: {
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, 1, CheckSimdScalarArgs(retType), &defs))
|
||||
if (!CheckSimdCallArgs(f, call, 1, CheckSimdScalarArgs(opType), &defs))
|
||||
return false;
|
||||
*def = f.splatSimd(defs[0], retType.toMIRType());
|
||||
*type = retType;
|
||||
*type = opType;
|
||||
*def = f.splatSimd(defs[0], type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
|
||||
case AsmJSSimdOperation_select: {
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(retType), &defs))
|
||||
if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(opType), &defs))
|
||||
return false;
|
||||
*type = opType;
|
||||
*def = f.ternarySimd(defs[0], defs[1], defs[2], MSimdTernaryBitwise::select,
|
||||
retType.toMIRType());
|
||||
*type = retType;
|
||||
type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -5726,16 +5841,15 @@ CheckSimdCtorCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Gl
|
||||
return CheckCoercionArg(f, argNode, coercion, def, type);
|
||||
|
||||
AsmJSSimdType simdType = global->simdCtorType();
|
||||
Type retType = simdType;
|
||||
unsigned length = SimdTypeToLength(simdType);
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(retType), &defs))
|
||||
if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(simdType), &defs))
|
||||
return false;
|
||||
|
||||
// This code will need to be generalized when we handle float64x2
|
||||
MOZ_ASSERT(length == 4);
|
||||
*def = f.constructSimd<MSimdValueX4>(defs[0], defs[1], defs[2], defs[3], retType.toMIRType());
|
||||
*type = retType;
|
||||
*type = simdType;
|
||||
*def = f.constructSimd<MSimdValueX4>(defs[0], defs[1], defs[2], defs[3], type->toMIRType());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,9 @@ const size_t AsmJSPageSize = 4096;
|
||||
// On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the
|
||||
// byteLength portion of which is accessible) so that out-of-bounds accesses
|
||||
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
|
||||
static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL;
|
||||
// Unaligned accesses and mask optimizations might also try to access a few
|
||||
// bytes after this limit, so just inflate it by AsmJSPageSize.
|
||||
static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL + AsmJSPageSize;
|
||||
#endif
|
||||
|
||||
// From the asm.js spec Linking section:
|
||||
|
@ -508,11 +508,19 @@ struct Mul {
|
||||
};
|
||||
template<typename T>
|
||||
struct Minimum {
|
||||
static inline T apply(T l, T r) { return l < r ? l : r; }
|
||||
static inline T apply(T l, T r) { return math_min_impl(l, r); }
|
||||
};
|
||||
template<typename T>
|
||||
struct MinNum {
|
||||
static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); }
|
||||
};
|
||||
template<typename T>
|
||||
struct Maximum {
|
||||
static inline T apply(T l, T r) { return l > r ? l : r; }
|
||||
static inline T apply(T l, T r) { return math_max_impl(l, r); }
|
||||
};
|
||||
template<typename T>
|
||||
struct MaxNum {
|
||||
static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); }
|
||||
};
|
||||
template<typename T>
|
||||
struct LessThan {
|
||||
@ -971,6 +979,84 @@ Float32x4Select(JSContext *cx, unsigned argc, Value *vp)
|
||||
return StoreResult<Float32x4>(cx, args, result);
|
||||
}
|
||||
|
||||
template<class VElem, unsigned NumElem>
|
||||
static bool
|
||||
TypedArrayDataPtrFromArgs(JSContext *cx, const CallArgs &args, VElem **data)
|
||||
{
|
||||
if (!args[0].isObject())
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
JSObject &argobj = args[0].toObject();
|
||||
if (!argobj.is<TypedArrayObject>())
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
Rooted<TypedArrayObject*> typedArray(cx, &argobj.as<TypedArrayObject>());
|
||||
|
||||
int32_t index;
|
||||
if (!ToInt32(cx, args[1], &index))
|
||||
return false;
|
||||
|
||||
int32_t byteStart = index * typedArray->bytesPerElement();
|
||||
if (byteStart < 0 || (uint32_t(byteStart) + NumElem * sizeof(VElem)) > typedArray->byteLength())
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
*data = reinterpret_cast<VElem*>(static_cast<char*>(typedArray->viewData()) + byteStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class V, unsigned NumElem>
|
||||
static bool
|
||||
Load(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
typedef typename V::Elem Elem;
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 2)
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
Elem *typedArrayData = nullptr;
|
||||
if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
|
||||
return false;
|
||||
|
||||
Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
|
||||
MOZ_ASSERT(typeDescr);
|
||||
Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0));
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
Elem *dest = reinterpret_cast<Elem*>(result->typedMem());
|
||||
for (unsigned i = 0; i < NumElem; i++)
|
||||
dest[i] = typedArrayData[i];
|
||||
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class V, unsigned NumElem>
|
||||
static bool
|
||||
Store(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
typedef typename V::Elem Elem;
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 3)
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
Elem *typedArrayData = nullptr;
|
||||
if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
|
||||
return false;
|
||||
|
||||
if (!IsVectorObject<V>(args[2]))
|
||||
return ErrorBadArgs(cx);
|
||||
|
||||
Elem *src = TypedObjectMemory<Elem*>(args[2]);
|
||||
for (unsigned i = 0; i < NumElem; i++)
|
||||
typedArrayData[i] = src[i];
|
||||
|
||||
args.rval().setObject(args[2].toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
|
||||
bool \
|
||||
js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \
|
||||
|
@ -41,12 +41,22 @@
|
||||
V(greaterThanOrEqual, (CompareFunc<Float32x4, GreaterThanOrEqual>), 2, 0) \
|
||||
V(lessThan, (CompareFunc<Float32x4, LessThan>), 2, 0) \
|
||||
V(lessThanOrEqual, (CompareFunc<Float32x4, LessThanOrEqual>), 2, 0) \
|
||||
V(load, (Load<Float32x4, 4>), 2, 0) \
|
||||
V(loadXYZ, (Load<Float32x4, 3>), 2, 0) \
|
||||
V(loadXY, (Load<Float32x4, 2>), 2, 0) \
|
||||
V(loadX, (Load<Float32x4, 1>), 2, 0) \
|
||||
V(max, (BinaryFunc<Float32x4, Maximum, Float32x4>), 2, 0) \
|
||||
V(maxNum, (BinaryFunc<Float32x4, MaxNum, Float32x4>), 2, 0) \
|
||||
V(min, (BinaryFunc<Float32x4, Minimum, Float32x4>), 2, 0) \
|
||||
V(minNum, (BinaryFunc<Float32x4, MinNum, Float32x4>), 2, 0) \
|
||||
V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2, 0) \
|
||||
V(notEqual, (CompareFunc<Float32x4, NotEqual>), 2, 0) \
|
||||
V(or, (CoercedBinaryFunc<Float32x4, Int32x4, Or, Float32x4>), 2, 0) \
|
||||
V(scale, (FuncWith<Float32x4, Scale>), 2, 0) \
|
||||
V(store, (Store<Float32x4, 4>), 3, 0) \
|
||||
V(storeXYZ, (Store<Float32x4, 3>), 3, 0) \
|
||||
V(storeXY, (Store<Float32x4, 2>), 3, 0) \
|
||||
V(storeX, (Store<Float32x4, 1>), 3, 0) \
|
||||
V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2, 0) \
|
||||
V(withX, (FuncWith<Float32x4, WithX>), 2, 0) \
|
||||
V(withY, (FuncWith<Float32x4, WithY>), 2, 0) \
|
||||
@ -85,12 +95,20 @@
|
||||
V(equal, (CompareFunc<Int32x4, Equal>), 2, 0) \
|
||||
V(greaterThan, (CompareFunc<Int32x4, GreaterThan>), 2, 0) \
|
||||
V(lessThan, (CompareFunc<Int32x4, LessThan>), 2, 0) \
|
||||
V(load, (Load<Int32x4, 4>), 2, 0) \
|
||||
V(loadXYZ, (Load<Int32x4, 3>), 2, 0) \
|
||||
V(loadXY, (Load<Int32x4, 2>), 2, 0) \
|
||||
V(loadX, (Load<Int32x4, 1>), 2, 0) \
|
||||
V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2, 0) \
|
||||
V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2, 0) \
|
||||
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2, 0) \
|
||||
V(shiftLeft, (Int32x4BinaryScalar<ShiftLeft>), 2, 0) \
|
||||
V(shiftRight, (Int32x4BinaryScalar<ShiftRight>), 2, 0) \
|
||||
V(shiftRightLogical, (Int32x4BinaryScalar<ShiftRightLogical>), 2, 0) \
|
||||
V(store, (Store<Int32x4, 4>), 3, 0) \
|
||||
V(storeXYZ, (Store<Int32x4, 3>), 3, 0) \
|
||||
V(storeXY, (Store<Int32x4, 2>), 3, 0) \
|
||||
V(storeX, (Store<Int32x4, 1>), 3, 0) \
|
||||
V(withFlagX, (FuncWith<Int32x4, WithFlagX>), 2, 0) \
|
||||
V(withFlagY, (FuncWith<Int32x4, WithFlagY>), 2, 0) \
|
||||
V(withFlagZ, (FuncWith<Int32x4, WithFlagZ>), 2, 0) \
|
||||
@ -136,6 +154,8 @@
|
||||
_(div) \
|
||||
_(max) \
|
||||
_(min) \
|
||||
_(maxNum) \
|
||||
_(minNum) \
|
||||
_(lessThanOrEqual) \
|
||||
_(notEqual) \
|
||||
_(greaterThanOrEqual)
|
||||
@ -157,7 +177,9 @@
|
||||
_(withZ) \
|
||||
_(withW) \
|
||||
_(not) \
|
||||
_(neg)
|
||||
_(neg) \
|
||||
_(load) \
|
||||
_(store)
|
||||
#define FORALL_SIMD_OP(_) \
|
||||
FOREACH_INT32X4_SIMD_OP(_) \
|
||||
FOREACH_FLOAT32X4_SIMD_OP(_) \
|
||||
|
@ -421,7 +421,7 @@ function unsafeVariableAddressTaken(suppressed, variable)
|
||||
|
||||
function computePrintedLines(functionName)
|
||||
{
|
||||
assert(!system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
|
||||
assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
|
||||
var lines = snarf(tmpfile).split('\n');
|
||||
|
||||
for (var body of functionBodies)
|
||||
|
@ -444,6 +444,18 @@ CheckF4(F32M, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [4,6,15,8]);
|
||||
CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [Math.fround(13.37) * 4,6,15,8]);
|
||||
CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4m(x,y))', [Math.fround(13.37) * 4,6,15,8]);
|
||||
|
||||
var f32x4 = SIMD.float32x4(0, NaN, -0, NaN);
|
||||
var another = SIMD.float32x4(NaN, -1, -0, NaN);
|
||||
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + "function f(x, y) {x=f4(x); y=f4(y); x=f4m(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]);
|
||||
|
||||
CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]);
|
||||
CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
|
||||
CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fround(13.37) / 4,+Infinity,-Infinity,2]);
|
||||
|
||||
var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
|
||||
var another = SIMD.float32x4(0, -0, 0, 0);
|
||||
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
|
||||
|
||||
// Unary arithmetic operators
|
||||
function CheckUnaryF4(op, checkFunc, assertFunc) {
|
||||
var _ = asmLink(asmCompile('glob', USE_ASM + F32 + 'var op=f4.' + op + '; function f(x){x=f4(x); return f4(op(x)); } return f'), this);
|
||||
@ -513,31 +525,36 @@ assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.max; function f() {} retur
|
||||
const F32MIN = 'var min = f4.min;'
|
||||
const F32MAX = 'var max = f4.max;'
|
||||
|
||||
// TODO amend tests once float32x4.min/max is fully specified, for NaN and -0
|
||||
// vs 0. See also comment in js::jit::CodeGeneratorX86Shared::visitSimdBinaryArithFx4.
|
||||
// In these tests, we assume x86/x64, so min(+0, -0) === +0, as specified in
|
||||
// the Intel developer manual. See also bug 1068028.
|
||||
CheckF4(F32MIN, 'var x=f4(1,2,3,4); x=min(x,x)', [1,2,3,4]);
|
||||
CheckF4(F32MIN, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=min(x,y)', [4,2,3,2]);
|
||||
CheckF4(F32MIN + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=min(x,y)', [2310,-Infinity,3,0]);
|
||||
CheckF4(F32MIN + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=min(x,y)', [2310,-Infinity,3,-0]);
|
||||
|
||||
CheckF4(F32MIN, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=min(x,y)', [0,-0,-0,-0]);
|
||||
CheckF4(F32MIN + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=min(x,y)', [NaN, NaN, NaN, 0]);
|
||||
|
||||
CheckF4(F32MAX, 'var x=f4(1,2,3,4); x=max(x,x)', [1,2,3,4]);
|
||||
CheckF4(F32MAX, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=max(x,y)', [13.37, 3, 5, 4]);
|
||||
CheckF4(F32MAX + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=max(x,y)', [+Infinity,3,5,0]);
|
||||
|
||||
// Test NaN
|
||||
var f32x4 = SIMD.float32x4(0, NaN, -0, NaN);
|
||||
var another = SIMD.float32x4(NaN, -1, -0, NaN);
|
||||
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + "function f(x, y) {x=f4(x); y=f4(y); x=f4m(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]);
|
||||
CheckF4(F32MAX, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=max(x,y)', [0,0,0,-0]);
|
||||
CheckF4(F32MAX + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=max(x,y)', [NaN, NaN, NaN, 0]);
|
||||
|
||||
CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]);
|
||||
CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
|
||||
CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fround(13.37) / 4,+Infinity,-Infinity,2]);
|
||||
const F32MINNUM = 'var min = f4.minNum;'
|
||||
const F32MAXNUM = 'var max = f4.maxNum;'
|
||||
|
||||
// Test NaN
|
||||
var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
|
||||
var another = SIMD.float32x4(0, -0, 0, 0);
|
||||
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
|
||||
CheckF4(F32MINNUM, 'var x=f4(1,2,3,4); x=min(x,x)', [1,2,3,4]);
|
||||
CheckF4(F32MINNUM, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=min(x,y)', [4,2,3,2]);
|
||||
CheckF4(F32MINNUM + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=min(x,y)', [2310,-Infinity,3,-0]);
|
||||
|
||||
CheckF4(F32MINNUM, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=min(x,y)', [0,-0,-0,-0]);
|
||||
CheckF4(F32MINNUM + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=min(x,y)', [NaN, 0, 0, 0]);
|
||||
|
||||
CheckF4(F32MAXNUM, 'var x=f4(1,2,3,4); x=max(x,x)', [1,2,3,4]);
|
||||
CheckF4(F32MAXNUM, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=max(x,y)', [13.37, 3, 5, 4]);
|
||||
CheckF4(F32MAXNUM + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=max(x,y)', [+Infinity,3,5,0]);
|
||||
|
||||
CheckF4(F32MAXNUM, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=max(x,y)', [0,0,0,-0]);
|
||||
CheckF4(F32MAXNUM + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=max(x,y)', [NaN, 0, 0, 0]);
|
||||
|
||||
// With
|
||||
const WXF = 'var w = f4.withX;';
|
||||
@ -945,6 +962,224 @@ assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f(
|
||||
assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); x=i4(func());} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); x=f4(func());} return f");
|
||||
|
||||
// Load / Store
|
||||
(function testLoadStore() {
|
||||
|
||||
var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.int32x4; var load=i4.load; var store=i4.store;';
|
||||
|
||||
// Bad number of args
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3);} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 4, 5);} return f");
|
||||
|
||||
// Bad type of args
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 5);} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, 5.0);} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0.;load(H, i);} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=new glob.Int32Array(heap); function f(){var i=0;load(H2, i)} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=42; function f(){var i=0;load(H2, i)} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;load(H2, i)} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var f4=glob.SIMD.float32x4; function f(){var i=0;var vec=f4(1,2,3,4); store(H, i, vec)} return f");
|
||||
|
||||
// Bad coercions of returned values
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return load(H, i)|0;} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return +load(H, i);} return f");
|
||||
|
||||
// Literal index constants
|
||||
var buf = new ArrayBuffer(BUF_MIN);
|
||||
var asI32 = new Int32Array(buf);
|
||||
asI32[(BUF_MIN >> 2) - 4] = 4;
|
||||
asI32[(BUF_MIN >> 2) - 3] = 3;
|
||||
asI32[(BUF_MIN >> 2) - 2] = 2;
|
||||
asI32[(BUF_MIN >> 2) - 1] = 1;
|
||||
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1) + ");} return f");
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f");
|
||||
asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f");
|
||||
|
||||
assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf);
|
||||
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
|
||||
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
|
||||
|
||||
var CONSTANT_INDEX = 42;
|
||||
var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2;
|
||||
|
||||
var loadStoreCode = `
|
||||
"use asm";
|
||||
|
||||
var H = new glob.Uint8Array(heap);
|
||||
|
||||
var i4 = glob.SIMD.int32x4;
|
||||
var i4load = i4.load;
|
||||
var i4store = i4.store;
|
||||
|
||||
var f4 = glob.SIMD.float32x4;
|
||||
var f4load = f4.load;
|
||||
var f4store = f4.store;
|
||||
|
||||
function f32l(i) { i=i|0; return f4(f4load(H, i|0)); }
|
||||
function f32lcst() { return f4(f4load(H, ${CONSTANT_BYTE_INDEX})); }
|
||||
function f32s(i, vec) { i=i|0; vec=f4(vec); f4store(H, i|0, vec); }
|
||||
function f32scst(vec) { vec=f4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
|
||||
|
||||
function i32l(i) { i=i|0; return i4(i4load(H, i|0)); }
|
||||
function i32lcst() { return i4(i4load(H, ${CONSTANT_BYTE_INDEX})); }
|
||||
function i32s(i, vec) { i=i|0; vec=i4(vec); i4store(H, i|0, vec); }
|
||||
function i32scst(vec) { vec=i4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
|
||||
|
||||
function f32lbndcheck(i) {
|
||||
i=i|0;
|
||||
if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
|
||||
if ((i|0) < 0) i = 0;
|
||||
return f4(f4load(H, i|0));
|
||||
}
|
||||
function f32sbndcheck(i, vec) {
|
||||
i=i|0;
|
||||
vec=f4(vec);
|
||||
if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
|
||||
if ((i|0) < 0) i = 0;
|
||||
return f4(f4store(H, i|0, vec));
|
||||
}
|
||||
|
||||
return {
|
||||
f32l: f32l,
|
||||
f32lcst: f32lcst,
|
||||
f32s: f32s,
|
||||
f32scst: f32scst,
|
||||
f32lbndcheck: f32lbndcheck,
|
||||
f32sbndcheck: f32sbndcheck,
|
||||
i32l: i32l,
|
||||
i32lcst: i32lcst,
|
||||
i32s: i32s,
|
||||
i32scst: i32scst
|
||||
}
|
||||
`;
|
||||
|
||||
const SIZE = 0x8000;
|
||||
|
||||
var F32 = new Float32Array(SIZE);
|
||||
var reset = function() {
|
||||
for (var i = 0; i < SIZE; i++)
|
||||
F32[i] = i + 1;
|
||||
};
|
||||
reset();
|
||||
|
||||
var buf = F32.buffer;
|
||||
var m = asmLink(asmCompile('glob', 'ffi', 'heap', loadStoreCode), this, null, buf);
|
||||
|
||||
function slice(TA, i, n) { return Array.prototype.slice.call(TA, i, i + n); }
|
||||
|
||||
// Float32x4.load
|
||||
function f32l(n) { return m.f32l((n|0) << 2 | 0); };
|
||||
|
||||
// Correct accesses
|
||||
assertEqX4(f32l(0), slice(F32, 0, 4));
|
||||
assertEqX4(f32l(1), slice(F32, 1, 4));
|
||||
assertEqX4(f32l(SIZE - 4), slice(F32, SIZE - 4, 4));
|
||||
|
||||
assertEqX4(m.f32lcst(), slice(F32, CONSTANT_INDEX, 4));
|
||||
assertEqX4(m.f32lbndcheck(CONSTANT_BYTE_INDEX), slice(F32, CONSTANT_INDEX, 4));
|
||||
|
||||
// OOB
|
||||
var BatNaN = [NaN, NaN, NaN, NaN] // NaNNaNNaNNaN etc.
|
||||
assertEqX4(f32l(-1), BatNaN);
|
||||
assertEqX4(f32l(SIZE), BatNaN);
|
||||
assertEqX4(f32l(SIZE - 1), BatNaN);
|
||||
assertEqX4(f32l(SIZE - 2), BatNaN);
|
||||
assertEqX4(f32l(SIZE - 3), BatNaN);
|
||||
|
||||
// Float32x4.store
|
||||
function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); };
|
||||
|
||||
var vec = SIMD.float32x4(5,6,7,8);
|
||||
var vec2 = SIMD.float32x4(0,1,2,3);
|
||||
|
||||
reset();
|
||||
f32s(0, vec);
|
||||
assertEqX4(vec, slice(F32, 0, 4));
|
||||
|
||||
reset();
|
||||
f32s(0, vec2);
|
||||
assertEqX4(vec2, slice(F32, 0, 4));
|
||||
|
||||
reset();
|
||||
f32s(4, vec);
|
||||
assertEqX4(vec, slice(F32, 4, 4));
|
||||
|
||||
reset();
|
||||
m.f32scst(vec2);
|
||||
assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4));
|
||||
|
||||
reset();
|
||||
m.f32sbndcheck(CONSTANT_BYTE_INDEX, vec);
|
||||
assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4));
|
||||
|
||||
// OOB
|
||||
reset();
|
||||
f32s(SIZE - 3, vec);
|
||||
f32s(SIZE - 2, vec);
|
||||
f32s(SIZE - 1, vec);
|
||||
f32s(SIZE, vec);
|
||||
for (var i = 0; i < SIZE; i++)
|
||||
assertEq(F32[i], i + 1);
|
||||
|
||||
// Int32x4.load
|
||||
var I32 = new Int32Array(buf);
|
||||
reset = function () {
|
||||
for (var i = 0; i < SIZE; i++)
|
||||
I32[i] = i + 1;
|
||||
};
|
||||
reset();
|
||||
|
||||
function i32(n) { return m.i32l((n|0) << 2 | 0); };
|
||||
|
||||
// Correct accesses
|
||||
assertEqX4(i32(0), slice(I32, 0, 4));
|
||||
assertEqX4(i32(1), slice(I32, 1, 4));
|
||||
assertEqX4(i32(SIZE - 4), slice(I32, SIZE - 4, 4));
|
||||
|
||||
assertEqX4(m.i32lcst(), slice(I32, CONSTANT_INDEX, 4));
|
||||
|
||||
// OOB
|
||||
assertEqX4(i32(-1), [0,0,0,0]);
|
||||
assertEqX4(i32(SIZE), [0,0,0,0]);
|
||||
assertEqX4(i32(SIZE - 1), [0,0,0,0]);
|
||||
assertEqX4(i32(SIZE - 2), [0,0,0,0]);
|
||||
assertEqX4(i32(SIZE - 3), [0,0,0,0]);
|
||||
|
||||
// Int32x4.store
|
||||
function i32s(n, v) { return m.i32s((n|0) << 2 | 0, v); };
|
||||
|
||||
var vec = SIMD.int32x4(5,6,7,8);
|
||||
var vec2 = SIMD.int32x4(0,1,2,3);
|
||||
|
||||
reset();
|
||||
i32s(0, vec);
|
||||
assertEqX4(vec, slice(I32, 0, 4));
|
||||
|
||||
reset();
|
||||
i32s(0, vec2);
|
||||
assertEqX4(vec2, slice(I32, 0, 4));
|
||||
|
||||
reset();
|
||||
i32s(4, vec);
|
||||
assertEqX4(vec, slice(I32, 4, 4));
|
||||
|
||||
reset();
|
||||
m.i32scst(vec2);
|
||||
assertEqX4(vec2, slice(I32, CONSTANT_INDEX, 4));
|
||||
|
||||
// OOB
|
||||
reset();
|
||||
i32s(SIZE - 3, vec);
|
||||
i32s(SIZE - 2, vec);
|
||||
i32s(SIZE - 1, vec);
|
||||
i32s(SIZE - 0, vec);
|
||||
for (var i = 0; i < SIZE; i++)
|
||||
assertEq(I32[i], i + 1);
|
||||
|
||||
})();
|
||||
|
||||
// 3.3 Internal calls
|
||||
// asm.js -> asm.js
|
||||
// Retrieving values from asm.js
|
||||
|
@ -927,6 +927,48 @@ function rregexp_m_literal_replace(i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_string_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace'))
|
||||
function rstring_replace(i) {
|
||||
var re = /str\d+9/;
|
||||
|
||||
assertEq(re.lastIndex == 0, true);
|
||||
var res = "str00123456789".replace(re, "abc");
|
||||
if (uceFault_string_replace(i) || uceFault_string_replace(i)) {
|
||||
assertEq(res, "abc");
|
||||
}
|
||||
assertEq(re.lastIndex == 0, true);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_string_replace_y = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_y'))
|
||||
function rstring_replace_y(i) {
|
||||
var re = /str\d+9/y;
|
||||
|
||||
assertEq(re.lastIndex == 0, true);
|
||||
var res = "str00123456789".replace(re, "abc");
|
||||
if (uceFault_string_replace_y(i) || uceFault_string_replace_y(i)) {
|
||||
assertEq(res, "abc");
|
||||
}
|
||||
assertEq(re.lastIndex == 0, true);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_string_replace_g = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_g'))
|
||||
function rstring_replace_g(i) {
|
||||
var re = /str\d+9/g;
|
||||
|
||||
assertEq(re.lastIndex == 0, true);
|
||||
var res = "str00123456789str00123456789".replace(re, "abc");
|
||||
if (uceFault_string_replace_g(i) || uceFault_string_replace_g(i)) {
|
||||
assertEq(res, "abcabc");
|
||||
}
|
||||
assertEq(re.lastIndex == 0, true);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_typeof = eval(uneval(uceFault).replace('uceFault', 'uceFault_typeof'))
|
||||
function rtypeof(i) {
|
||||
var inputs = [ {}, [], 1, true, undefined, function(){}, null ];
|
||||
@ -1098,6 +1140,9 @@ for (i = 0; i < 100; i++) {
|
||||
rregexp_i_literal_replace(i);
|
||||
rregexp_m_replace(i);
|
||||
rregexp_m_literal_replace(i);
|
||||
rstring_replace(i);
|
||||
rstring_replace_y(i);
|
||||
rstring_replace_g(i);
|
||||
rtypeof(i);
|
||||
rtodouble_value(i);
|
||||
rtodouble_number(i);
|
||||
|
@ -366,19 +366,21 @@ class LSimdBinaryCompFx4 : public LSimdBinaryComp
|
||||
};
|
||||
|
||||
// Binary SIMD arithmetic operation between two SIMD operands
|
||||
class LSimdBinaryArith : public LInstructionHelper<1, 2, 0>
|
||||
template<size_t Temps>
|
||||
class LSimdBinaryArith : public LInstructionHelper<1, 2, Temps>
|
||||
{
|
||||
public:
|
||||
LSimdBinaryArith() {}
|
||||
|
||||
const LAllocation *lhs() {
|
||||
return getOperand(0);
|
||||
return this->getOperand(0);
|
||||
}
|
||||
const LAllocation *rhs() {
|
||||
return getOperand(1);
|
||||
return this->getOperand(1);
|
||||
}
|
||||
|
||||
MSimdBinaryArith::Operation operation() const {
|
||||
return mir_->toSimdBinaryArith()->operation();
|
||||
return this->mir_->toSimdBinaryArith()->operation();
|
||||
}
|
||||
const char *extraName() const {
|
||||
return MSimdBinaryArith::OperationName(operation());
|
||||
@ -386,19 +388,23 @@ class LSimdBinaryArith : public LInstructionHelper<1, 2, 0>
|
||||
};
|
||||
|
||||
// Binary SIMD arithmetic operation between two Int32x4 operands
|
||||
class LSimdBinaryArithIx4 : public LSimdBinaryArith
|
||||
class LSimdBinaryArithIx4 : public LSimdBinaryArith<0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SimdBinaryArithIx4);
|
||||
LSimdBinaryArithIx4() : LSimdBinaryArith() {}
|
||||
LSimdBinaryArithIx4() : LSimdBinaryArith<0>() {}
|
||||
};
|
||||
|
||||
// Binary SIMD arithmetic operation between two Float32x4 operands
|
||||
class LSimdBinaryArithFx4 : public LSimdBinaryArith
|
||||
class LSimdBinaryArithFx4 : public LSimdBinaryArith<1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SimdBinaryArithFx4);
|
||||
LSimdBinaryArithFx4() : LSimdBinaryArith() {}
|
||||
LSimdBinaryArithFx4() : LSimdBinaryArith<1>() {}
|
||||
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Unary SIMD arithmetic operation on a SIMD operand
|
||||
|
@ -4072,20 +4072,23 @@ LIRGenerator::visitSimdBinaryArith(MSimdBinaryArith *ins)
|
||||
|
||||
MDefinition *lhs = ins->lhs();
|
||||
MDefinition *rhs = ins->rhs();
|
||||
|
||||
if (ins->isCommutative())
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
|
||||
if (ins->type() == MIRType_Int32x4) {
|
||||
LSimdBinaryArithIx4 *add = new(alloc()) LSimdBinaryArithIx4();
|
||||
return lowerForFPU(add, ins, lhs, rhs);
|
||||
}
|
||||
if (ins->type() == MIRType_Int32x4)
|
||||
return lowerForFPU(new(alloc()) LSimdBinaryArithIx4(), ins, lhs, rhs);
|
||||
|
||||
if (ins->type() == MIRType_Float32x4) {
|
||||
LSimdBinaryArithFx4 *add = new(alloc()) LSimdBinaryArithFx4();
|
||||
return lowerForFPU(add, ins, lhs, rhs);
|
||||
}
|
||||
MOZ_ASSERT(ins->type() == MIRType_Float32x4, "unknown simd type on binary arith operation");
|
||||
|
||||
MOZ_CRASH("Unknown SIMD kind when adding values");
|
||||
LSimdBinaryArithFx4 *lir = new(alloc()) LSimdBinaryArithFx4();
|
||||
|
||||
bool needsTemp = ins->operation() == MSimdBinaryArith::Max ||
|
||||
ins->operation() == MSimdBinaryArith::MinNum ||
|
||||
ins->operation() == MSimdBinaryArith::MaxNum;
|
||||
lir->setTemp(0, needsTemp ? temp(LDefinition::FLOAT32X4) : LDefinition::BogusTemp());
|
||||
|
||||
return lowerForFPU(lir, ins, lhs, rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1857,17 +1857,21 @@ class MSimdBinaryArith : public MBinaryInstruction
|
||||
Mul,
|
||||
Div,
|
||||
Min,
|
||||
Max
|
||||
Max,
|
||||
MinNum,
|
||||
MaxNum
|
||||
};
|
||||
|
||||
static const char* OperationName(Operation op) {
|
||||
switch (op) {
|
||||
case Add: return "Add";
|
||||
case Sub: return "Sub";
|
||||
case Mul: return "Mul";
|
||||
case Div: return "Div";
|
||||
case Min: return "Min";
|
||||
case Max: return "Max";
|
||||
case Add: return "Add";
|
||||
case Sub: return "Sub";
|
||||
case Mul: return "Mul";
|
||||
case Div: return "Div";
|
||||
case Min: return "Min";
|
||||
case Max: return "Max";
|
||||
case MinNum: return "MinNum";
|
||||
case MaxNum: return "MaxNum";
|
||||
}
|
||||
MOZ_CRASH("unexpected operation");
|
||||
}
|
||||
@ -6950,6 +6954,13 @@ class MStringReplace
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
bool writeRecoverData(CompactBufferWriter &writer) const;
|
||||
bool canRecoverOnBailout() const {
|
||||
if (pattern()->isRegExp())
|
||||
return !pattern()->toRegExp()->source()->global();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class MSubstr
|
||||
@ -12092,15 +12103,19 @@ class MAsmJSNeg : public MUnaryInstruction
|
||||
|
||||
class MAsmJSHeapAccess
|
||||
{
|
||||
Scalar::Type viewType_;
|
||||
protected:
|
||||
typedef AsmJSHeapAccess::ViewType ViewType;
|
||||
|
||||
private:
|
||||
ViewType viewType_;
|
||||
bool needsBoundsCheck_;
|
||||
|
||||
public:
|
||||
MAsmJSHeapAccess(Scalar::Type vt, bool needsBoundsCheck)
|
||||
MAsmJSHeapAccess(ViewType vt, bool needsBoundsCheck)
|
||||
: viewType_(vt), needsBoundsCheck_(needsBoundsCheck)
|
||||
{}
|
||||
|
||||
Scalar::Type viewType() const { return viewType_; }
|
||||
ViewType viewType() const { return viewType_; }
|
||||
bool needsBoundsCheck() const { return needsBoundsCheck_; }
|
||||
void removeBoundsCheck() { needsBoundsCheck_ = false; }
|
||||
};
|
||||
@ -12110,7 +12125,7 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
|
||||
MemoryBarrierBits barrierBefore_;
|
||||
MemoryBarrierBits barrierAfter_;
|
||||
|
||||
MAsmJSLoadHeap(Scalar::Type vt, MDefinition *ptr, bool needsBoundsCheck,
|
||||
MAsmJSLoadHeap(ViewType vt, MDefinition *ptr, bool needsBoundsCheck,
|
||||
MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
: MUnaryInstruction(ptr),
|
||||
MAsmJSHeapAccess(vt, needsBoundsCheck),
|
||||
@ -12121,18 +12136,37 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
|
||||
setGuard(); // Not removable
|
||||
else
|
||||
setMovable();
|
||||
if (vt == Scalar::Float32)
|
||||
setResultType(MIRType_Float32);
|
||||
else if (vt == Scalar::Float64)
|
||||
setResultType(MIRType_Double);
|
||||
else
|
||||
|
||||
switch (vt) {
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8:
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32:
|
||||
setResultType(MIRType_Int32);
|
||||
break;
|
||||
case AsmJSHeapAccess::Float32:
|
||||
setResultType(MIRType_Float32);
|
||||
break;
|
||||
case AsmJSHeapAccess::Float64:
|
||||
setResultType(MIRType_Double);
|
||||
break;
|
||||
case AsmJSHeapAccess::Float32x4:
|
||||
setResultType(MIRType_Float32x4);
|
||||
break;
|
||||
case AsmJSHeapAccess::Int32x4:
|
||||
setResultType(MIRType_Int32x4);
|
||||
break;
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
MOZ_CRASH("unexpected uint8clamped load heap in asm.js");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(AsmJSLoadHeap);
|
||||
|
||||
static MAsmJSLoadHeap *New(TempAllocator &alloc, Scalar::Type vt,
|
||||
static MAsmJSLoadHeap *New(TempAllocator &alloc, ViewType vt,
|
||||
MDefinition *ptr, bool needsBoundsCheck,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
@ -12156,7 +12190,7 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
|
||||
MemoryBarrierBits barrierBefore_;
|
||||
MemoryBarrierBits barrierAfter_;
|
||||
|
||||
MAsmJSStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck,
|
||||
MAsmJSStoreHeap(ViewType vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck,
|
||||
MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
: MBinaryInstruction(ptr, v),
|
||||
MAsmJSHeapAccess(vt, needsBoundsCheck),
|
||||
@ -12170,7 +12204,7 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
|
||||
public:
|
||||
INSTRUCTION_HEADER(AsmJSStoreHeap);
|
||||
|
||||
static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type vt,
|
||||
static MAsmJSStoreHeap *New(TempAllocator &alloc, ViewType vt,
|
||||
MDefinition *ptr, MDefinition *v, bool needsBoundsCheck,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
@ -12191,7 +12225,7 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
|
||||
|
||||
class MAsmJSCompareExchangeHeap : public MTernaryInstruction, public MAsmJSHeapAccess
|
||||
{
|
||||
MAsmJSCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv,
|
||||
MAsmJSCompareExchangeHeap(ViewType vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv,
|
||||
bool needsBoundsCheck)
|
||||
: MTernaryInstruction(ptr, oldv, newv),
|
||||
MAsmJSHeapAccess(vt, needsBoundsCheck)
|
||||
@ -12203,7 +12237,7 @@ class MAsmJSCompareExchangeHeap : public MTernaryInstruction, public MAsmJSHeapA
|
||||
public:
|
||||
INSTRUCTION_HEADER(AsmJSCompareExchangeHeap);
|
||||
|
||||
static MAsmJSCompareExchangeHeap *New(TempAllocator &alloc, Scalar::Type vt,
|
||||
static MAsmJSCompareExchangeHeap *New(TempAllocator &alloc, ViewType vt,
|
||||
MDefinition *ptr, MDefinition *oldv,
|
||||
MDefinition *newv, bool needsBoundsCheck)
|
||||
{
|
||||
@ -12223,7 +12257,7 @@ class MAsmJSAtomicBinopHeap : public MBinaryInstruction, public MAsmJSHeapAccess
|
||||
{
|
||||
AtomicOp op_;
|
||||
|
||||
MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type vt, MDefinition *ptr, MDefinition *v,
|
||||
MAsmJSAtomicBinopHeap(AtomicOp op, ViewType vt, MDefinition *ptr, MDefinition *v,
|
||||
bool needsBoundsCheck)
|
||||
: MBinaryInstruction(ptr, v),
|
||||
MAsmJSHeapAccess(vt, needsBoundsCheck),
|
||||
@ -12236,7 +12270,7 @@ class MAsmJSAtomicBinopHeap : public MBinaryInstruction, public MAsmJSHeapAccess
|
||||
public:
|
||||
INSTRUCTION_HEADER(AsmJSAtomicBinopHeap);
|
||||
|
||||
static MAsmJSAtomicBinopHeap *New(TempAllocator &alloc, AtomicOp op, Scalar::Type vt,
|
||||
static MAsmJSAtomicBinopHeap *New(TempAllocator &alloc, AtomicOp op, ViewType vt,
|
||||
MDefinition *ptr, MDefinition *v, bool needsBoundsCheck)
|
||||
{
|
||||
return new(alloc) MAsmJSAtomicBinopHeap(op, vt, ptr, v, needsBoundsCheck);
|
||||
|
@ -1258,3 +1258,28 @@ RArrayState::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||
iter.storeInstructionResult(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MStringReplace::writeRecoverData(CompactBufferWriter &writer) const
|
||||
{
|
||||
MOZ_ASSERT(canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace));
|
||||
return true;
|
||||
}
|
||||
|
||||
RStringReplace::RStringReplace(CompactBufferReader &reader)
|
||||
{ }
|
||||
|
||||
bool RStringReplace::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||
{
|
||||
RootedString string(cx, iter.read().toString());
|
||||
RootedString pattern(cx, iter.read().toString());
|
||||
RootedString replace(cx, iter.read().toString());
|
||||
RootedValue result(cx);
|
||||
|
||||
if (!js::str_replace_string_raw(cx, string, pattern, replace, &result))
|
||||
return false;
|
||||
|
||||
iter.storeInstructionResult(result);
|
||||
return true;
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ namespace jit {
|
||||
_(RegExpExec) \
|
||||
_(RegExpTest) \
|
||||
_(RegExpReplace) \
|
||||
_(StringReplace) \
|
||||
_(TypeOf) \
|
||||
_(ToDouble) \
|
||||
_(ToFloat32) \
|
||||
@ -518,6 +519,18 @@ class RRegExpReplace MOZ_FINAL : public RInstruction
|
||||
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||
};
|
||||
|
||||
class RStringReplace MOZ_FINAL : public RInstruction
|
||||
{
|
||||
public:
|
||||
RINSTRUCTION_HEADER_(StringReplace)
|
||||
|
||||
virtual uint32_t numOperands() const {
|
||||
return 3;
|
||||
}
|
||||
|
||||
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||
};
|
||||
|
||||
class RTypeOf MOZ_FINAL : public RInstruction
|
||||
{
|
||||
public:
|
||||
|
@ -2028,7 +2028,10 @@ bool
|
||||
CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
|
||||
{
|
||||
MAsmJSCompareExchangeHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
|
||||
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
|
||||
Scalar::Type vt = Scalar::Type(mir->viewType());
|
||||
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
Register ptrReg = ToRegister(ptr);
|
||||
BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
|
||||
@ -2063,7 +2066,10 @@ bool
|
||||
CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
|
||||
{
|
||||
MAsmJSAtomicBinopHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
|
||||
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
|
||||
Scalar::Type vt = Scalar::Type(mir->viewType());
|
||||
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
Register ptrReg = ToRegister(ptr);
|
||||
Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
|
||||
|
@ -199,8 +199,9 @@ LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
|
||||
|
||||
}
|
||||
|
||||
template<size_t Temps>
|
||||
bool
|
||||
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
|
||||
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
|
||||
{
|
||||
ins->setOperand(0, useRegisterAtStart(lhs));
|
||||
ins->setOperand(1, useRegisterAtStart(rhs));
|
||||
@ -208,6 +209,11 @@ LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
||||
}
|
||||
|
||||
template bool LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
template bool LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 1> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
|
||||
bool
|
||||
LIRGeneratorARM::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
|
||||
MDefinition *lhs, MDefinition *rhs)
|
||||
@ -640,9 +646,7 @@ LIRGeneratorARM::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArra
|
||||
bool
|
||||
LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->viewType() != Scalar::Uint8Clamped);
|
||||
MOZ_ASSERT(ins->viewType() != Scalar::Float32);
|
||||
MOZ_ASSERT(ins->viewType() != Scalar::Float64);
|
||||
MOZ_ASSERT(ins->viewType() < AsmJSHeapAccess::Float32);
|
||||
|
||||
MDefinition *ptr = ins->ptr();
|
||||
MOZ_ASSERT(ptr->type() == MIRType_Int32);
|
||||
@ -658,9 +662,7 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins)
|
||||
bool
|
||||
LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->viewType() != Scalar::Uint8Clamped);
|
||||
MOZ_ASSERT(ins->viewType() != Scalar::Float32);
|
||||
MOZ_ASSERT(ins->viewType() != Scalar::Float64);
|
||||
MOZ_ASSERT(ins->viewType() < AsmJSHeapAccess::Float32);
|
||||
|
||||
MDefinition *ptr = ins->ptr();
|
||||
MOZ_ASSERT(ptr->type() == MIRType_Int32);
|
||||
|
@ -56,7 +56,8 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||
|
||||
bool lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
|
||||
MDefinition *src);
|
||||
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
template<size_t Temps>
|
||||
bool lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
|
||||
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
|
||||
|
@ -201,8 +201,9 @@ LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir
|
||||
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
||||
}
|
||||
|
||||
template<size_t Temps>
|
||||
bool
|
||||
LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs)
|
||||
{
|
||||
ins->setOperand(0, useRegister(lhs));
|
||||
@ -211,6 +212,11 @@ LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir
|
||||
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
||||
}
|
||||
|
||||
template bool LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
template bool LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 1> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
|
||||
bool
|
||||
LIRGeneratorMIPS::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
|
||||
MDefinition *lhs, MDefinition *rhs)
|
||||
|
@ -56,7 +56,8 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
|
||||
|
||||
bool lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
|
||||
MDefinition *src);
|
||||
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
template<size_t Temps>
|
||||
bool lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
|
||||
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
|
||||
|
@ -731,11 +731,27 @@ static const unsigned AsmJSNaN32GlobalDataOffset = 2 * sizeof(void*) + sizeof(do
|
||||
// #ifdefery.
|
||||
class AsmJSHeapAccess
|
||||
{
|
||||
public:
|
||||
enum ViewType {
|
||||
Int8 = Scalar::Int8,
|
||||
Uint8 = Scalar::Uint8,
|
||||
Int16 = Scalar::Int16,
|
||||
Uint16 = Scalar::Uint16,
|
||||
Int32 = Scalar::Int32,
|
||||
Uint32 = Scalar::Uint32,
|
||||
Float32 = Scalar::Float32,
|
||||
Float64 = Scalar::Float64,
|
||||
Uint8Clamped = Scalar::Uint8Clamped,
|
||||
Float32x4,
|
||||
Int32x4
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t offset_;
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
uint8_t cmpDelta_; // the number of bytes from the cmp to the load/store instruction
|
||||
uint8_t opLength_; // the length of the load/store instruction
|
||||
uint8_t isFloat32Load_;
|
||||
ViewType viewType_;
|
||||
AnyRegister::Code loadedReg_ : 8;
|
||||
#endif
|
||||
|
||||
@ -748,19 +764,20 @@ class AsmJSHeapAccess
|
||||
|
||||
// If 'cmp' equals 'offset' or if it is not supplied then the
|
||||
// cmpDelta_ is zero indicating that there is no length to patch.
|
||||
AsmJSHeapAccess(uint32_t offset, uint32_t after, Scalar::Type vt,
|
||||
AsmJSHeapAccess(uint32_t offset, uint32_t after, ViewType viewType,
|
||||
AnyRegister loadedReg, uint32_t cmp = NoLengthCheck)
|
||||
: offset_(offset),
|
||||
cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
|
||||
opLength_(after - offset),
|
||||
isFloat32Load_(vt == Scalar::Float32),
|
||||
viewType_(viewType),
|
||||
loadedReg_(loadedReg.code())
|
||||
{}
|
||||
AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = NoLengthCheck)
|
||||
AsmJSHeapAccess(uint32_t offset, uint8_t after, ViewType viewType,
|
||||
uint32_t cmp = NoLengthCheck)
|
||||
: offset_(offset),
|
||||
cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
|
||||
opLength_(after - offset),
|
||||
isFloat32Load_(false),
|
||||
viewType_(viewType),
|
||||
loadedReg_(UINT8_MAX)
|
||||
{}
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
|
||||
@ -779,7 +796,7 @@ class AsmJSHeapAccess
|
||||
void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
|
||||
unsigned opLength() const { return opLength_; }
|
||||
bool isLoad() const { return loadedReg_ != UINT8_MAX; }
|
||||
bool isFloat32Load() const { return isFloat32Load_; }
|
||||
ViewType viewType() const { return viewType_; }
|
||||
AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); }
|
||||
#endif
|
||||
};
|
||||
|
@ -475,6 +475,9 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
case Operand::MEM_SCALE:
|
||||
masm.movaps_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
|
||||
break;
|
||||
case Operand::FPREG:
|
||||
masm.movaps_rr(src.fpu(), dest.code());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
@ -1688,6 +1691,21 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void cmpeqps(const Operand &src, FloatRegister dest) {
|
||||
cmpps(src, dest, X86Assembler::ConditionCmp_EQ);
|
||||
}
|
||||
void cmpltps(const Operand &src, FloatRegister dest) {
|
||||
cmpps(src, dest, X86Assembler::ConditionCmp_LT);
|
||||
}
|
||||
void cmpleps(const Operand &src, FloatRegister dest) {
|
||||
cmpps(src, dest, X86Assembler::ConditionCmp_LE);
|
||||
}
|
||||
void cmpunordps(const Operand &src, FloatRegister dest) {
|
||||
cmpps(src, dest, X86Assembler::ConditionCmp_UNORD);
|
||||
}
|
||||
void cmpneqps(const Operand &src, FloatRegister dest) {
|
||||
cmpps(src, dest, X86Assembler::ConditionCmp_NEQ);
|
||||
}
|
||||
void rcpps(const Operand &src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
@ -1889,6 +1907,7 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
}
|
||||
}
|
||||
void andnps(const Operand &src, FloatRegister dest) {
|
||||
// Negates bits of dest and then applies AND
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::FPREG:
|
||||
|
@ -197,6 +197,18 @@ public:
|
||||
ConditionNC = ConditionAE
|
||||
} Condition;
|
||||
|
||||
// Conditions for CMP instructions (CMPSS, CMPSD, CMPPS, CMPPD, etc).
|
||||
typedef enum {
|
||||
ConditionCmp_EQ = 0x0,
|
||||
ConditionCmp_LT = 0x1,
|
||||
ConditionCmp_LE = 0x2,
|
||||
ConditionCmp_UNORD = 0x3,
|
||||
ConditionCmp_NEQ = 0x4,
|
||||
ConditionCmp_NLT = 0x5,
|
||||
ConditionCmp_NLE = 0x6,
|
||||
ConditionCmp_ORD = 0x7,
|
||||
} ConditionCmp;
|
||||
|
||||
static const char* nameCC(Condition cc)
|
||||
{
|
||||
static const char* const names[16]
|
||||
@ -3227,6 +3239,19 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movups_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
spew("movups %p, %s", address, nameFPReg(dst));
|
||||
m_formatter.twoByteOp(OP2_MOVPS_VpsWps, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movdqu_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
spew("movdqu %p, %s", address, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVDQ_VdqWdq, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movsd_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movsd %s, %p", nameFPReg(src), address);
|
||||
@ -3251,6 +3276,19 @@ public:
|
||||
void movaps_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movaps %s, %p", nameFPReg(src), address);
|
||||
m_formatter.twoByteOp(OP2_MOVAPS_WsdVsd, (RegisterID)src, address);
|
||||
}
|
||||
|
||||
void movdqu_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movdqu %s, %p", nameFPReg(src), address);
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVDQ_WdqVdq, (RegisterID)src, address);
|
||||
}
|
||||
|
||||
void movups_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movups %s, %p", nameFPReg(src), address);
|
||||
m_formatter.twoByteOp(OP2_MOVPS_WpsVps, (RegisterID)src, address);
|
||||
}
|
||||
#ifdef JS_CODEGEN_X64
|
||||
@ -3334,6 +3372,12 @@ public:
|
||||
nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
|
||||
m_formatter.twoByteOp(OP2_MOVPS_WpsVps, (RegisterID)src, base, offset);
|
||||
}
|
||||
void movups_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
|
||||
{
|
||||
spew("movups %s, %s0x%x(%s)",
|
||||
nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
|
||||
m_formatter.twoByteOp_disp32(OP2_MOVPS_WpsVps, (RegisterID)src, base, offset);
|
||||
}
|
||||
void movups_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("movups %s, %d(%s,%s,%d)",
|
||||
@ -3346,6 +3390,12 @@ public:
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.twoByteOp(OP2_MOVPS_VpsWps, (RegisterID)dst, base, offset);
|
||||
}
|
||||
void movups_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("movups %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.twoByteOp_disp32(OP2_MOVPS_VpsWps, (RegisterID)dst, base, offset);
|
||||
}
|
||||
void movups_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
|
||||
{
|
||||
spew("movups %d(%s,%s,%d), %s",
|
||||
@ -3398,6 +3448,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MOVDQ_WdqVdq, (RegisterID)src, base, offset);
|
||||
}
|
||||
|
||||
void movdqu_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
|
||||
{
|
||||
spew("movdqu %s, %s0x%x(%s)",
|
||||
nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp_disp32(OP2_MOVDQ_WdqVdq, (RegisterID)src, base, offset);
|
||||
}
|
||||
|
||||
void movdqu_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("movdqu %s, %d(%s,%s,%d)",
|
||||
@ -3414,6 +3472,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MOVDQ_VdqWdq, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void movdqu_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("movdqu %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp_disp32(OP2_MOVDQ_VdqWdq, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void movdqu_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
|
||||
{
|
||||
spew("movdqu %d(%s,%s,%d), %s",
|
||||
|
@ -346,14 +346,29 @@ CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
|
||||
{
|
||||
if (ool->dest().isFloat()) {
|
||||
if (ool->isFloat32Load())
|
||||
masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
|
||||
else
|
||||
masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
|
||||
} else {
|
||||
switch (ool->viewType()) {
|
||||
case AsmJSHeapAccess::Float32:
|
||||
masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
|
||||
break;
|
||||
case AsmJSHeapAccess::Float64:
|
||||
masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
|
||||
break;
|
||||
case AsmJSHeapAccess::Float32x4:
|
||||
masm.loadConstantFloat32x4(SimdConstant::SplatX4(float(GenericNaN())), ool->dest().fpu());
|
||||
break;
|
||||
case AsmJSHeapAccess::Int32x4:
|
||||
masm.loadConstantInt32x4(SimdConstant::SplatX4(0), ool->dest().fpu());
|
||||
break;
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8:
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32:
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
Register destReg = ool->dest().gpr();
|
||||
masm.mov(ImmWord(0), destReg);
|
||||
break;
|
||||
}
|
||||
masm.jmp(ool->rejoin());
|
||||
return true;
|
||||
@ -2705,16 +2720,16 @@ CodeGeneratorX86Shared::visitSimdBinaryCompFx4(LSimdBinaryCompFx4 *ins)
|
||||
MSimdBinaryComp::Operation op = ins->operation();
|
||||
switch (op) {
|
||||
case MSimdBinaryComp::equal:
|
||||
masm.cmpps(rhs, lhs, 0x0);
|
||||
masm.cmpeqps(rhs, lhs);
|
||||
return true;
|
||||
case MSimdBinaryComp::lessThan:
|
||||
masm.cmpps(rhs, lhs, 0x1);
|
||||
masm.cmpltps(rhs, lhs);
|
||||
return true;
|
||||
case MSimdBinaryComp::lessThanOrEqual:
|
||||
masm.cmpps(rhs, lhs, 0x2);
|
||||
masm.cmpleps(rhs, lhs);
|
||||
return true;
|
||||
case MSimdBinaryComp::notEqual:
|
||||
masm.cmpps(rhs, lhs, 0x4);
|
||||
masm.cmpneqps(rhs, lhs);
|
||||
return true;
|
||||
case MSimdBinaryComp::greaterThanOrEqual:
|
||||
case MSimdBinaryComp::greaterThan:
|
||||
@ -2754,6 +2769,9 @@ CodeGeneratorX86Shared::visitSimdBinaryArithIx4(LSimdBinaryArithIx4 *ins)
|
||||
// we can do max with a single instruction only if we have SSE4.1
|
||||
// using the PMINSD instruction.
|
||||
break;
|
||||
case MSimdBinaryArith::MinNum:
|
||||
case MSimdBinaryArith::MaxNum:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("unexpected SIMD op");
|
||||
}
|
||||
@ -2779,19 +2797,75 @@ CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4 *ins)
|
||||
case MSimdBinaryArith::Div:
|
||||
masm.packedDivFloat32(rhs, lhs);
|
||||
return true;
|
||||
case MSimdBinaryArith::Max:
|
||||
// TODO: standardization of float32x4.min/max needs to define the
|
||||
// semantics for particular values: if both input operands are -0 or 0,
|
||||
// or one operand is NaN, the return value will be the "second operand"
|
||||
// in the instruction.
|
||||
// See also bug 1068028, which is about fixing semantics once the
|
||||
// specification is stable.
|
||||
case MSimdBinaryArith::Max: {
|
||||
masm.movaps(lhs, ScratchSimdReg);
|
||||
masm.cmpunordps(rhs, ScratchSimdReg);
|
||||
|
||||
FloatRegister tmp = ToFloatRegister(ins->temp());
|
||||
masm.movaps(rhs, tmp);
|
||||
masm.maxps(Operand(lhs), tmp);
|
||||
masm.maxps(rhs, lhs);
|
||||
|
||||
masm.andps(tmp, lhs);
|
||||
masm.orps(ScratchSimdReg, lhs); // or in the all-ones NaNs
|
||||
return true;
|
||||
case MSimdBinaryArith::Min:
|
||||
// See comment above.
|
||||
}
|
||||
case MSimdBinaryArith::Min: {
|
||||
FloatRegister rhsCopy = ScratchSimdReg;
|
||||
masm.movaps(rhs, rhsCopy);
|
||||
masm.minps(Operand(lhs), rhsCopy);
|
||||
masm.minps(rhs, lhs);
|
||||
masm.orps(rhsCopy, lhs); // NaN or'd with arbitrary bits is NaN
|
||||
return true;
|
||||
}
|
||||
case MSimdBinaryArith::MinNum: {
|
||||
FloatRegister tmp = ToFloatRegister(ins->temp());
|
||||
masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), ScratchSimdReg);
|
||||
masm.movdqa(ScratchSimdReg, tmp);
|
||||
|
||||
FloatRegister mask = ScratchSimdReg;
|
||||
masm.pcmpeqd(Operand(lhs), mask);
|
||||
masm.andps(tmp, mask);
|
||||
|
||||
masm.movaps(lhs, tmp);
|
||||
masm.minps(rhs, tmp);
|
||||
masm.orps(mask, tmp);
|
||||
|
||||
masm.movaps(rhs, mask);
|
||||
masm.cmpneqps(Operand(mask), mask);
|
||||
|
||||
// Emulates blendv
|
||||
masm.andps(Operand(mask), lhs);
|
||||
masm.andnps(Operand(tmp), mask);
|
||||
masm.orps(Operand(mask), lhs);
|
||||
return true;
|
||||
}
|
||||
case MSimdBinaryArith::MaxNum: {
|
||||
FloatRegister mask = ScratchSimdReg;
|
||||
masm.loadConstantInt32x4(SimdConstant::SplatX4(0), mask);
|
||||
masm.pcmpeqd(Operand(lhs), mask);
|
||||
|
||||
FloatRegister tmp = ToFloatRegister(ins->temp());
|
||||
masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp);
|
||||
masm.andps(tmp, mask);
|
||||
|
||||
masm.movaps(lhs, tmp);
|
||||
masm.maxps(rhs, tmp);
|
||||
masm.andnps(Operand(tmp), mask);
|
||||
|
||||
// Ensure tmp always contains the temporary result
|
||||
mask = tmp;
|
||||
tmp = ScratchSimdReg;
|
||||
|
||||
masm.movaps(rhs, mask);
|
||||
masm.cmpneqps(Operand(mask), mask);
|
||||
|
||||
// Emulates blendv
|
||||
masm.andps(Operand(mask), lhs);
|
||||
masm.andnps(Operand(tmp), mask);
|
||||
masm.orps(Operand(mask), lhs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("unexpected SIMD op");
|
||||
}
|
||||
@ -2942,8 +3016,6 @@ CodeGeneratorX86Shared::visitSimdSelect(LSimdSelect *ins)
|
||||
FloatRegister onFalse = ToFloatRegister(ins->rhs());
|
||||
|
||||
MOZ_ASSERT(onTrue == ToFloatRegister(ins->output()));
|
||||
// The onFalse argument is not destroyed but due to limitations of the
|
||||
// register allocator its life ends at the start of the operation.
|
||||
masm.bitwiseAndX4(Operand(mask), onTrue);
|
||||
masm.bitwiseAndNotX4(Operand(onFalse), mask);
|
||||
masm.bitwiseOrX4(Operand(mask), onTrue);
|
||||
|
@ -38,14 +38,14 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
class OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86Shared>
|
||||
{
|
||||
AnyRegister dest_;
|
||||
bool isFloat32Load_;
|
||||
AsmJSHeapAccess::ViewType viewType_;
|
||||
public:
|
||||
OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
|
||||
: dest_(dest), isFloat32Load_(isFloat32Load)
|
||||
OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, AsmJSHeapAccess::ViewType viewType)
|
||||
: dest_(dest), viewType_(viewType)
|
||||
{}
|
||||
|
||||
AnyRegister dest() const { return dest_; }
|
||||
bool isFloat32Load() const { return isFloat32Load_; }
|
||||
AsmJSHeapAccess::ViewType viewType() const { return viewType_; }
|
||||
bool accept(CodeGeneratorX86Shared *codegen) {
|
||||
return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this);
|
||||
}
|
||||
|
@ -100,14 +100,20 @@ LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition
|
||||
return defineReuseInput(ins, mir, 0);
|
||||
}
|
||||
|
||||
template<size_t Temps>
|
||||
bool
|
||||
LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
|
||||
LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
|
||||
{
|
||||
ins->setOperand(0, useRegisterAtStart(lhs));
|
||||
ins->setOperand(1, lhs != rhs ? use(rhs) : useAtStart(rhs));
|
||||
return defineReuseInput(ins, mir, 0);
|
||||
}
|
||||
|
||||
template bool LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
template bool LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 1> *ins, MDefinition *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
|
||||
bool
|
||||
LIRGeneratorX86Shared::lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir, MDefinition *lhs, MDefinition *rhs)
|
||||
{
|
||||
|
@ -31,7 +31,8 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared
|
||||
bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
|
||||
bool lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
|
||||
MDefinition *rhs);
|
||||
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
|
||||
template<size_t Temps>
|
||||
bool lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir, MDefinition *lhs,
|
||||
MDefinition *rhs);
|
||||
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
|
||||
MDefinition *lhs, MDefinition *rhs);
|
||||
|
@ -844,9 +844,15 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
void loadUnalignedInt32x4(const Address &src, FloatRegister dest) {
|
||||
movdqu(Operand(src), dest);
|
||||
}
|
||||
void loadUnalignedInt32x4(const Operand &src, FloatRegister dest) {
|
||||
movdqu(src, dest);
|
||||
}
|
||||
void storeUnalignedInt32x4(FloatRegister src, const Address &dest) {
|
||||
movdqu(src, Operand(dest));
|
||||
}
|
||||
void storeUnalignedInt32x4(FloatRegister src, const Operand &dest) {
|
||||
movdqu(src, dest);
|
||||
}
|
||||
void packedEqualInt32x4(const Operand &src, FloatRegister dest) {
|
||||
pcmpeqd(src, dest);
|
||||
}
|
||||
@ -907,9 +913,15 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
void loadUnalignedFloat32x4(const Address &src, FloatRegister dest) {
|
||||
movups(Operand(src), dest);
|
||||
}
|
||||
void loadUnalignedFloat32x4(const Operand &src, FloatRegister dest) {
|
||||
movups(src, dest);
|
||||
}
|
||||
void storeUnalignedFloat32x4(FloatRegister src, const Address &dest) {
|
||||
movups(src, Operand(dest));
|
||||
}
|
||||
void storeUnalignedFloat32x4(FloatRegister src, const Operand &dest) {
|
||||
movups(src, dest);
|
||||
}
|
||||
void packedAddFloat32(const Operand &src, FloatRegister dest) {
|
||||
addps(src, dest);
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ bool
|
||||
CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
|
||||
{
|
||||
MAsmJSLoadHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
AsmJSHeapAccess::ViewType vt = mir->viewType();
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
const LDefinition *out = ins->output();
|
||||
Operand srcAddr(HeapReg);
|
||||
@ -286,8 +286,7 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
|
||||
OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
|
||||
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
|
||||
if (mir->needsBoundsCheck()) {
|
||||
bool isFloat32Load = vt == Scalar::Float32;
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt);
|
||||
if (!addOutOfLineCode(ool, ins->mir()))
|
||||
return false;
|
||||
|
||||
@ -298,15 +297,17 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
|
||||
|
||||
uint32_t before = masm.size();
|
||||
switch (vt) {
|
||||
case Scalar::Int8: masm.movsbl(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Uint8: masm.movzbl(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Int16: masm.movswl(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Uint16: masm.movzwl(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32: masm.movl(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break;
|
||||
case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Int8: masm.movsbl(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Uint8: masm.movzbl(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Int16: masm.movswl(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Uint16: masm.movzwl(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32: masm.movl(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Float32x4: masm.loadUnalignedFloat32x4(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Int32x4: masm.loadUnalignedInt32x4(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Uint8Clamped: MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
uint32_t after = masm.size();
|
||||
if (ool)
|
||||
@ -320,7 +321,7 @@ bool
|
||||
CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
|
||||
{
|
||||
MAsmJSStoreHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
AsmJSHeapAccess::ViewType vt = mir->viewType();
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
Operand dstAddr(HeapReg);
|
||||
|
||||
@ -344,32 +345,38 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
|
||||
uint32_t before = masm.size();
|
||||
if (ins->value()->isConstant()) {
|
||||
switch (vt) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Uint8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
|
||||
case Scalar::Int16:
|
||||
case Scalar::Uint16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float32:
|
||||
case AsmJSHeapAccess::Float64:
|
||||
case AsmJSHeapAccess::Float32x4:
|
||||
case AsmJSHeapAccess::Int32x4:
|
||||
case AsmJSHeapAccess::Uint8Clamped: MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
} else {
|
||||
switch (vt) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Uint8: masm.movb(ToRegister(ins->value()), dstAddr); break;
|
||||
case Scalar::Int16:
|
||||
case Scalar::Uint16: masm.movw(ToRegister(ins->value()), dstAddr); break;
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32: masm.movl(ToRegister(ins->value()), dstAddr); break;
|
||||
case Scalar::Float32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break;
|
||||
case Scalar::Float64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8: masm.movb(ToRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16: masm.movw(ToRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32: masm.movl(ToRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float32x4: masm.storeUnalignedFloat32x4(ToFloatRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int32x4: masm.storeUnalignedInt32x4(ToFloatRegister(ins->value()), dstAddr); break;
|
||||
case AsmJSHeapAccess::Uint8Clamped: MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
}
|
||||
uint32_t after = masm.size();
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
memoryBarrier(ins->mir()->barrierAfter());
|
||||
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
|
||||
masm.append(AsmJSHeapAccess(before, after, vt, maybeCmpOffset));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -377,7 +384,10 @@ bool
|
||||
CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
|
||||
{
|
||||
MAsmJSCompareExchangeHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
|
||||
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
|
||||
Scalar::Type vt = Scalar::Type(mir->viewType());
|
||||
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
|
||||
MOZ_ASSERT(ptr->isRegister());
|
||||
@ -395,7 +405,7 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
|
||||
masm.j(Assembler::LessThan, &goahead);
|
||||
memoryBarrier(MembarFull);
|
||||
Register out = ToRegister(ins->output());
|
||||
masm.xorl(out,out);
|
||||
masm.xorl(out, out);
|
||||
masm.jmp(&rejoin);
|
||||
masm.bind(&goahead);
|
||||
}
|
||||
@ -408,7 +418,7 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
|
||||
uint32_t after = masm.size();
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
masm.append(AsmJSHeapAccess(after, after, maybeCmpOffset));
|
||||
masm.append(AsmJSHeapAccess(after, after, mir->viewType(), maybeCmpOffset));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -416,7 +426,10 @@ bool
|
||||
CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
|
||||
{
|
||||
MAsmJSAtomicBinopHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
|
||||
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
|
||||
Scalar::Type vt = Scalar::Type(mir->viewType());
|
||||
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
|
||||
const LAllocation* value = ins->value();
|
||||
@ -456,7 +469,7 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
|
||||
uint32_t after = masm.size();
|
||||
if (rejoin.used())
|
||||
masm.bind(&rejoin);
|
||||
masm.append(AsmJSHeapAccess(after, after, maybeCmpOffset));
|
||||
masm.append(AsmJSHeapAccess(after, after, mir->viewType(), maybeCmpOffset));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -163,19 +163,21 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
|
||||
|
||||
LAsmJSStoreHeap *lir;
|
||||
switch (ins->viewType()) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Uint8:
|
||||
case Scalar::Int16:
|
||||
case Scalar::Uint16:
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32:
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8:
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32:
|
||||
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterOrConstantAtStart(ins->value()));
|
||||
break;
|
||||
case Scalar::Float32:
|
||||
case Scalar::Float64:
|
||||
case AsmJSHeapAccess::Float32:
|
||||
case AsmJSHeapAccess::Float64:
|
||||
case AsmJSHeapAccess::Float32x4:
|
||||
case AsmJSHeapAccess::Int32x4:
|
||||
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
|
||||
break;
|
||||
default:
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
|
||||
|
@ -465,6 +465,16 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movsd_mr_disp32(src.offset, src.base.code(), dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movupsWithPatch(Address src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movups_mr_disp32(src.offset, src.base.code(), dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movdquWithPatch(Address src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movdqu_mr_disp32(src.offset, src.base.code(), dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
|
||||
// Store to *(base + disp32) where disp32 can be patched.
|
||||
CodeOffsetLabel movbWithPatch(Register src, Address dest) {
|
||||
@ -489,6 +499,16 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movsd_rm_disp32(src.code(), dest.offset, dest.base.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movupsWithPatch(FloatRegister src, Address dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movups_rm_disp32(src.code(), dest.offset, dest.base.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movdquWithPatch(FloatRegister src, Address dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movdqu_rm_disp32(src.code(), dest.offset, dest.base.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
|
||||
// Load from *(addr + index*scale) where addr can be patched.
|
||||
CodeOffsetLabel movlWithPatch(PatchedAbsoluteAddress addr, Register index, Scale scale,
|
||||
@ -534,11 +554,21 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movdqa_mr(src.addr, dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movdquWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movdqu_mr(src.addr, dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movapsWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movaps_mr(src.addr, dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movupsWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movups_mr(src.addr, dest.code());
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
|
||||
// Store to *dest where dest can be patched.
|
||||
CodeOffsetLabel movbWithPatch(Register src, PatchedAbsoluteAddress dest) {
|
||||
@ -573,6 +603,16 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movaps_rm(src.code(), dest.addr);
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movdquWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movdqu_rm(src.code(), dest.addr);
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
CodeOffsetLabel movupsWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.movups_rm(src.code(), dest.addr);
|
||||
return CodeOffsetLabel(masm.currentOffset());
|
||||
}
|
||||
|
||||
void loadAsmJSActivation(Register dest) {
|
||||
CodeOffsetLabel label = movlWithPatch(PatchedAbsoluteAddress(), dest);
|
||||
|
@ -267,26 +267,27 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
CodeGeneratorX86::loadViewTypeElement(Scalar::Type vt, const T &srcAddr,
|
||||
CodeGeneratorX86::loadViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
|
||||
const LDefinition *out)
|
||||
{
|
||||
switch (vt) {
|
||||
case Scalar::Int8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Uint8Clamped:
|
||||
case Scalar::Uint8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Int16: masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Uint16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case Scalar::Float32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break;
|
||||
case Scalar::Float64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Int8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
case AsmJSHeapAccess::Uint8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Int16: masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Uint16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
|
||||
case AsmJSHeapAccess::Float32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Float64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Float32x4: masm.movupsWithPatch(srcAddr, ToFloatRegister(out)); break;
|
||||
case AsmJSHeapAccess::Int32x4: masm.movdquWithPatch(srcAddr, ToFloatRegister(out)); break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
CodeGeneratorX86::loadAndNoteViewTypeElement(Scalar::Type vt, const T &srcAddr,
|
||||
CodeGeneratorX86::loadAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
|
||||
const LDefinition *out)
|
||||
{
|
||||
uint32_t before = masm.size();
|
||||
@ -300,16 +301,15 @@ bool
|
||||
CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
|
||||
{
|
||||
const MLoadTypedArrayElementStatic *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
MOZ_ASSERT_IF(vt == Scalar::Float32, mir->type() == MIRType_Float32);
|
||||
AsmJSHeapAccess::ViewType vt = AsmJSHeapAccess::ViewType(mir->viewType());
|
||||
MOZ_ASSERT_IF(vt == AsmJSHeapAccess::Float32, mir->type() == MIRType_Float32);
|
||||
|
||||
Register ptr = ToRegister(ins->ptr());
|
||||
const LDefinition *out = ins->output();
|
||||
|
||||
OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
|
||||
bool isFloat32Load = (vt == Scalar::Float32);
|
||||
if (!mir->fallible()) {
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
|
||||
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt);
|
||||
if (!addOutOfLineCode(ool, ins->mir()))
|
||||
return false;
|
||||
}
|
||||
@ -322,9 +322,9 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
|
||||
|
||||
Address srcAddr(ptr, (int32_t) mir->base());
|
||||
loadViewTypeElement(vt, srcAddr, out);
|
||||
if (vt == Scalar::Float64)
|
||||
if (vt == AsmJSHeapAccess::Float64)
|
||||
masm.canonicalizeDouble(ToFloatRegister(out));
|
||||
if (vt == Scalar::Float32)
|
||||
if (vt == AsmJSHeapAccess::Float32)
|
||||
masm.canonicalizeFloat(ToFloatRegister(out));
|
||||
if (ool)
|
||||
masm.bind(ool->rejoin());
|
||||
@ -369,7 +369,7 @@ bool
|
||||
CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
|
||||
{
|
||||
const MAsmJSLoadHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
AsmJSHeapAccess::ViewType vt = mir->viewType();
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
const LDefinition *out = ins->output();
|
||||
|
||||
@ -395,8 +395,7 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isFloat32Load = vt == Scalar::Float32;
|
||||
OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
|
||||
OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt);
|
||||
if (!addOutOfLineCode(ool, mir))
|
||||
return false;
|
||||
|
||||
@ -414,39 +413,40 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
CodeGeneratorX86::storeViewTypeElement(Scalar::Type vt, const LAllocation *value,
|
||||
CodeGeneratorX86::storeViewTypeElement(AsmJSHeapAccess::ViewType vt, const LAllocation *value,
|
||||
const T &dstAddr)
|
||||
{
|
||||
switch (vt) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Uint8Clamped:
|
||||
case Scalar::Uint8: masm.movbWithPatch(ToRegister(value), dstAddr); break;
|
||||
case Scalar::Int16:
|
||||
case Scalar::Uint16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
|
||||
case Scalar::Float32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break;
|
||||
case Scalar::Float64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Int8:
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
case AsmJSHeapAccess::Uint8: masm.movbWithPatch(ToRegister(value), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int16:
|
||||
case AsmJSHeapAccess::Uint16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int32:
|
||||
case AsmJSHeapAccess::Uint32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
|
||||
case AsmJSHeapAccess::Float32x4: masm.movupsWithPatch(ToFloatRegister(value), dstAddr); break;
|
||||
case AsmJSHeapAccess::Int32x4: masm.movdquWithPatch(ToFloatRegister(value), dstAddr); break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
CodeGeneratorX86::storeAndNoteViewTypeElement(Scalar::Type vt, const LAllocation *value,
|
||||
const T &dstAddr)
|
||||
CodeGeneratorX86::storeAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt,
|
||||
const LAllocation *value, const T &dstAddr)
|
||||
{
|
||||
uint32_t before = masm.size();
|
||||
storeViewTypeElement(vt, value, dstAddr);
|
||||
uint32_t after = masm.size();
|
||||
masm.append(AsmJSHeapAccess(before, after));
|
||||
masm.append(AsmJSHeapAccess(before, after, vt));
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
|
||||
{
|
||||
MStoreTypedArrayElementStatic *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
AsmJSHeapAccess::ViewType vt = AsmJSHeapAccess::ViewType(mir->viewType());
|
||||
|
||||
Register ptr = ToRegister(ins->ptr());
|
||||
const LAllocation *value = ins->value();
|
||||
@ -465,7 +465,7 @@ bool
|
||||
CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
|
||||
{
|
||||
MAsmJSStoreHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
AsmJSHeapAccess::ViewType vt = mir->viewType();
|
||||
const LAllocation *value = ins->value();
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
|
||||
@ -500,7 +500,7 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
|
||||
uint32_t after = masm.size();
|
||||
masm.bind(&rejoin);
|
||||
memoryBarrier(ins->mir()->barrierAfter());
|
||||
masm.append(AsmJSHeapAccess(before, after, cmp.offset()));
|
||||
masm.append(AsmJSHeapAccess(before, after, vt, cmp.offset()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -508,7 +508,10 @@ bool
|
||||
CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
|
||||
{
|
||||
MAsmJSCompareExchangeHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
|
||||
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
|
||||
Scalar::Type vt = Scalar::Type(mir->viewType());
|
||||
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
Register oldval = ToRegister(ins->oldValue());
|
||||
Register newval = ToRegister(ins->newValue());
|
||||
@ -536,7 +539,7 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
|
||||
uint32_t before = masm.size();
|
||||
masm.addl_wide(Imm32(0), ptrReg);
|
||||
uint32_t after = masm.size();
|
||||
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
|
||||
masm.append(AsmJSHeapAccess(before, after, mir->viewType(), maybeCmpOffset));
|
||||
|
||||
Address memAddr(ToRegister(ptr), 0);
|
||||
masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
@ -555,7 +558,10 @@ bool
|
||||
CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
|
||||
{
|
||||
MAsmJSAtomicBinopHeap *mir = ins->mir();
|
||||
Scalar::Type vt = mir->viewType();
|
||||
|
||||
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
|
||||
Scalar::Type vt = Scalar::Type(mir->viewType());
|
||||
|
||||
const LAllocation *ptr = ins->ptr();
|
||||
Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
|
||||
const LAllocation* value = ins->value();
|
||||
@ -584,7 +590,7 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
|
||||
uint32_t before = masm.size();
|
||||
masm.addl_wide(Imm32(0), ptrReg);
|
||||
uint32_t after = masm.size();
|
||||
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
|
||||
masm.append(AsmJSHeapAccess(before, after, mir->viewType(), maybeCmpOffset));
|
||||
|
||||
Address memAddr(ptrReg, 0);
|
||||
if (value->isConstant()) {
|
||||
|
@ -29,17 +29,18 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
|
||||
ValueOperand ToTempValue(LInstruction *ins, size_t pos);
|
||||
|
||||
template<typename T>
|
||||
bool loadAndNoteViewTypeElement(Scalar::Type vt, const T &srcAddr,
|
||||
bool loadAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
|
||||
const LDefinition *out);
|
||||
template<typename T>
|
||||
void loadViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
|
||||
const LDefinition *out);
|
||||
template<typename T>
|
||||
void loadViewTypeElement(Scalar::Type vt, const T &srcAddr,
|
||||
const LDefinition *out);
|
||||
template<typename T>
|
||||
void storeAndNoteViewTypeElement(Scalar::Type vt, const LAllocation *value,
|
||||
void storeAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt, const LAllocation *value,
|
||||
const T &dstAddr);
|
||||
template<typename T>
|
||||
void storeViewTypeElement(Scalar::Type vt, const LAllocation *value,
|
||||
void storeViewTypeElement(AsmJSHeapAccess::ViewType vt, const LAllocation *value,
|
||||
const T &dstAddr);
|
||||
|
||||
void memoryBarrier(MemoryBarrierBits barrier);
|
||||
|
||||
public:
|
||||
|
@ -243,34 +243,38 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
|
||||
MOZ_ASSERT(ptrValue >= 0);
|
||||
LAllocation ptrAlloc = LAllocation(ptr->toConstant()->vp());
|
||||
switch (ins->viewType()) {
|
||||
case Scalar::Int8: case Scalar::Uint8:
|
||||
case AsmJSHeapAccess::Int8: case AsmJSHeapAccess::Uint8:
|
||||
// See comment below.
|
||||
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax));
|
||||
break;
|
||||
case Scalar::Int16: case Scalar::Uint16:
|
||||
case Scalar::Int32: case Scalar::Uint32:
|
||||
case Scalar::Float32: case Scalar::Float64:
|
||||
case AsmJSHeapAccess::Int16: case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32: case AsmJSHeapAccess::Uint32:
|
||||
case AsmJSHeapAccess::Float32: case AsmJSHeapAccess::Float64:
|
||||
case AsmJSHeapAccess::Float32x4: case AsmJSHeapAccess::Int32x4:
|
||||
// See comment below.
|
||||
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
|
||||
break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
return add(lir, ins);
|
||||
}
|
||||
|
||||
switch (ins->viewType()) {
|
||||
case Scalar::Int8: case Scalar::Uint8:
|
||||
case AsmJSHeapAccess::Int8: case AsmJSHeapAccess::Uint8:
|
||||
// See comment for LIRGeneratorX86::useByteOpRegister.
|
||||
lir = new(alloc()) LAsmJSStoreHeap(useRegister(ins->ptr()), useFixed(ins->value(), eax));
|
||||
break;
|
||||
case Scalar::Int16: case Scalar::Uint16:
|
||||
case Scalar::Int32: case Scalar::Uint32:
|
||||
case Scalar::Float32: case Scalar::Float64:
|
||||
case AsmJSHeapAccess::Int16: case AsmJSHeapAccess::Uint16:
|
||||
case AsmJSHeapAccess::Int32: case AsmJSHeapAccess::Uint32:
|
||||
case AsmJSHeapAccess::Float32: case AsmJSHeapAccess::Float64:
|
||||
case AsmJSHeapAccess::Float32x4: case AsmJSHeapAccess::Int32x4:
|
||||
// For now, don't allow constant values. The immediate operand
|
||||
// affects instruction layout which affects patching.
|
||||
lir = new(alloc()) LAsmJSStoreHeap(useRegisterAtStart(ptr), useRegisterAtStart(ins->value()));
|
||||
break;
|
||||
default: MOZ_CRASH("unexpected array type");
|
||||
case AsmJSHeapAccess::Uint8Clamped:
|
||||
MOZ_CRASH("unexpected array type");
|
||||
}
|
||||
|
||||
return add(lir, ins);
|
||||
|
@ -3229,7 +3229,9 @@ static const JSFunctionSpec array_methods[] = {
|
||||
JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0),
|
||||
|
||||
/* ES7 additions */
|
||||
#ifdef NIGHTLY_BUILD
|
||||
JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0),
|
||||
#endif
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
|
@ -565,8 +565,8 @@ js::math_log(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static double
|
||||
max_double(double x, double y)
|
||||
double
|
||||
js::math_max_impl(double x, double y)
|
||||
{
|
||||
// Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
|
||||
if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
|
||||
@ -584,14 +584,14 @@ js::math_max(JSContext *cx, unsigned argc, Value *vp)
|
||||
double x;
|
||||
if (!ToNumber(cx, args[i], &x))
|
||||
return false;
|
||||
maxval = max_double(x, maxval);
|
||||
maxval = math_max_impl(x, maxval);
|
||||
}
|
||||
args.rval().setNumber(maxval);
|
||||
return true;
|
||||
}
|
||||
|
||||
static double
|
||||
min_double(double x, double y)
|
||||
double
|
||||
js::math_min_impl(double x, double y)
|
||||
{
|
||||
// Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
|
||||
if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x)))
|
||||
@ -609,7 +609,7 @@ js::math_min(JSContext *cx, unsigned argc, Value *vp)
|
||||
double x;
|
||||
if (!ToNumber(cx, args[i], &x))
|
||||
return false;
|
||||
minval = min_double(x, minval);
|
||||
minval = math_min_impl(x, minval);
|
||||
}
|
||||
args.rval().setNumber(minval);
|
||||
return true;
|
||||
@ -626,9 +626,9 @@ js::minmax_impl(JSContext *cx, bool max, HandleValue a, HandleValue b, MutableHa
|
||||
return false;
|
||||
|
||||
if (max)
|
||||
res.setNumber(max_double(x, y));
|
||||
res.setNumber(math_max_impl(x, y));
|
||||
else
|
||||
res.setNumber(min_double(x, y));
|
||||
res.setNumber(math_min_impl(x, y));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -120,9 +120,15 @@ math_abs_handle(JSContext *cx, js::HandleValue v, js::MutableHandleValue r);
|
||||
extern bool
|
||||
math_abs(JSContext *cx, unsigned argc, js::Value *vp);
|
||||
|
||||
extern double
|
||||
math_max_impl(double x, double y);
|
||||
|
||||
extern bool
|
||||
math_max(JSContext *cx, unsigned argc, js::Value *vp);
|
||||
|
||||
extern double
|
||||
math_min_impl(double x, double y);
|
||||
|
||||
extern bool
|
||||
math_min(JSContext *cx, unsigned argc, js::Value *vp);
|
||||
|
||||
|
@ -8,7 +8,18 @@
|
||||
|
||||
#include "shell/OSObject.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef XP_WIN
|
||||
#include <process.h>
|
||||
#include <string.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// For JSFunctionSpecWithHelp
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
@ -38,9 +49,239 @@ os_getenv(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpec os_functions[] = {
|
||||
JS_FS("getenv", os_getenv, 1, 0),
|
||||
JS_FS_END
|
||||
static bool
|
||||
os_getpid(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 0) {
|
||||
JS_ReportError(cx, "os.getpid takes no arguments");
|
||||
return false;
|
||||
}
|
||||
args.rval().setInt32(getpid());
|
||||
return true;
|
||||
}
|
||||
|
||||
// There are two possible definitions of strerror_r floating around. The GNU
|
||||
// one returns a char* which may or may not be the buffer you passed in. The
|
||||
// other one returns an integer status code, and always writes the result into
|
||||
// the provided buffer.
|
||||
|
||||
static inline char *
|
||||
strerror_message(int result, char *buffer)
|
||||
{
|
||||
return result == 0 ? buffer : nullptr;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
strerror_message(char *result, char *buffer)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
ReportSysError(JSContext *cx, const char *prefix)
|
||||
{
|
||||
static char buffer[200];
|
||||
#if defined(XP_WIN)
|
||||
strerror_s(buffer, sizeof(buffer), errno);
|
||||
const char *errstr = buffer;
|
||||
#else
|
||||
const char *errstr = strerror_message(strerror_r(errno, buffer, sizeof(buffer)), buffer);
|
||||
#endif
|
||||
|
||||
if (!errstr)
|
||||
errstr = "unknown error";
|
||||
|
||||
size_t nbytes = strlen(prefix) + strlen(errstr) + 3;
|
||||
char *final = (char*) js_malloc(nbytes);
|
||||
if (!final) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
_snprintf(final, nbytes, "%s: %s", prefix, errstr);
|
||||
#else
|
||||
snprintf(final, nbytes, "%s: %s", prefix, errstr);
|
||||
#endif
|
||||
JS_ReportError(cx, final);
|
||||
js_free(final);
|
||||
}
|
||||
|
||||
static bool
|
||||
os_system(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
JS_ReportError(cx, "os.system requires 1 argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSAutoByteString command(cx, str);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
int result = system(command.ptr());
|
||||
if (result == -1) {
|
||||
ReportSysError(cx, "system call failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setInt32(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef XP_WIN
|
||||
static bool
|
||||
os_spawn(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
JS_ReportError(cx, "os.spawn requires 1 argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSAutoByteString command(cx, str);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
int32_t childPid = fork();
|
||||
if (childPid) {
|
||||
args.rval().setInt32(childPid);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (childPid == -1) {
|
||||
ReportSysError(cx, "fork failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are in the child
|
||||
|
||||
const char *cmd[] = {"sh", "-c", nullptr, nullptr};
|
||||
cmd[2] = command.ptr();
|
||||
|
||||
execvp("sh", (char * const*)cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool
|
||||
os_kill(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
int32_t pid;
|
||||
if (args.length() < 1) {
|
||||
JS_ReportError(cx, "os.kill requires 1 argument");
|
||||
return false;
|
||||
}
|
||||
if (!JS::ToInt32(cx, args[0], &pid))
|
||||
return false;
|
||||
|
||||
// It is too easy to kill yourself accidentally with os.kill("goose").
|
||||
if (pid == 0 && !args[0].isInt32()) {
|
||||
JS_ReportError(cx, "os.kill requires numeric pid");
|
||||
return false;
|
||||
}
|
||||
|
||||
int signal = SIGINT;
|
||||
if (args.length() > 1) {
|
||||
if (!JS::ToInt32(cx, args[1], &signal))
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = kill(pid, signal);
|
||||
if (status == -1)
|
||||
ReportSysError(cx, "kill failed");
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
os_waitpid(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
int32_t pid;
|
||||
if (args.length() == 0) {
|
||||
pid = -1;
|
||||
} else {
|
||||
if (!JS::ToInt32(cx, args[0], &pid))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nohang = false;
|
||||
if (args.length() >= 2)
|
||||
nohang = JS::ToBoolean(args[1]);
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, nohang ? WNOHANG : 0);
|
||||
if (result == -1) {
|
||||
ReportSysError(cx, "os.waitpid failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
RootedValue v(cx);
|
||||
if (result != 0) {
|
||||
v.setInt32(result);
|
||||
if (!JS_DefineProperty(cx, info, "pid", v, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
v.setInt32(WEXITSTATUS(status));
|
||||
if (!JS_DefineProperty(cx, info, "exitStatus", v, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*info);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpecWithHelp os_functions[] = {
|
||||
JS_FN_HELP("getenv", os_getenv, 1, 0,
|
||||
"getenv(variable)",
|
||||
" Get the value of an environment variable."),
|
||||
|
||||
JS_FN_HELP("getpid", os_getpid, 0, 0,
|
||||
"getpid()",
|
||||
" Return the current process id."),
|
||||
|
||||
JS_FN_HELP("system", os_system, 1, 0,
|
||||
"system(command)",
|
||||
" Execute command on the current host, returning result code or throwing an\n"
|
||||
" exception on failure."),
|
||||
|
||||
#ifndef XP_WIN
|
||||
JS_FN_HELP("spawn", os_spawn, 1, 0,
|
||||
"spawn(command)",
|
||||
" Start up a separate process running the given command. Returns the pid."),
|
||||
|
||||
JS_FN_HELP("kill", os_kill, 1, 0,
|
||||
"kill(pid[, signal])",
|
||||
" Send a signal to the given pid. The default signal is SIGINT. The signal\n"
|
||||
" passed in must be numeric, if given."),
|
||||
|
||||
JS_FN_HELP("waitpid", os_waitpid, 1, 0,
|
||||
"waitpid(pid[, nohang])",
|
||||
" Calls waitpid(). 'nohang' is a boolean indicating whether to pass WNOHANG.\n"
|
||||
" The return value is an object containing a 'pid' field, if a process was waitable\n"
|
||||
" and an 'exitStatus' field if a pid exited."),
|
||||
#endif
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
bool
|
||||
@ -48,6 +289,6 @@ js::DefineOS(JSContext *cx, HandleObject global)
|
||||
{
|
||||
RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
|
||||
return obj &&
|
||||
JS_DefineFunctions(cx, obj, os_functions) &&
|
||||
JS_DefineFunctionsWithHelp(cx, obj, os_functions) &&
|
||||
JS_DefineProperty(cx, global, "os", obj, 0);
|
||||
}
|
||||
|
@ -3575,30 +3575,6 @@ RedirectOutput(JSContext *cx, unsigned argc, jsval *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
System(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
||||
"system");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSAutoByteString command(cx, str);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
int result = system(command.ptr());
|
||||
args.rval().setInt32(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sArgc;
|
||||
static char **sArgv;
|
||||
|
||||
@ -4565,10 +4541,6 @@ static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
|
||||
" Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
|
||||
" redirecting. Filenames are relative to the current working directory."),
|
||||
|
||||
JS_FN_HELP("system", System, 1, 0,
|
||||
"system(command)",
|
||||
" Execute command on the current host, returning result code."),
|
||||
|
||||
JS_FN_HELP("nestedShell", NestedShell, 0, 0,
|
||||
"nestedShell(shellArgs...)",
|
||||
" Execute the given code in a new JS shell process, passing this nested shell\n"
|
||||
|
54
js/src/tests/ecma_6/TypedObject/simd/float32x4-minmax.js
Normal file
54
js/src/tests/ecma_6/TypedObject/simd/float32x4-minmax.js
Normal file
@ -0,0 +1,54 @@
|
||||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 946042;
|
||||
|
||||
var float32x4 = SIMD.float32x4;
|
||||
|
||||
function testMaxFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)));
|
||||
}
|
||||
function testMinFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)));
|
||||
}
|
||||
|
||||
function maxNum(x, y) {
|
||||
if (x != x)
|
||||
return y;
|
||||
if (y != y)
|
||||
return x;
|
||||
return Math.max(x, y);
|
||||
}
|
||||
|
||||
function minNum(x, y) {
|
||||
if (x != x)
|
||||
return y;
|
||||
if (y != y)
|
||||
return x;
|
||||
return Math.min(x, y);
|
||||
}
|
||||
|
||||
function testMaxNumFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.maxNum, maxNum);
|
||||
}
|
||||
function testMinNumFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.minNum, minNum);
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
|
||||
[float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
|
||||
[float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
|
||||
{
|
||||
testMinFloat32(v, w);
|
||||
testMaxFloat32(v, w);
|
||||
testMinNumFloat32(v, w);
|
||||
testMaxNumFloat32(v, w);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
@ -1,35 +0,0 @@
|
||||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 946042;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float32x4 max';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 30, 4);
|
||||
var b = float32x4(10, 2, 3, 40);
|
||||
var c = SIMD.float32x4.max(a, b);
|
||||
assertEq(c.x, 10);
|
||||
assertEq(c.y, 20);
|
||||
assertEq(c.z, 30);
|
||||
assertEq(c.w, 40);
|
||||
|
||||
var d = float32x4(9.999, 2.1234, 30.4443, 4);
|
||||
var e = float32x4(10, 2.1233, 30.4444, 4.0001);
|
||||
var f = float32x4.max(d, e);
|
||||
assertEq(f.x, 10);
|
||||
assertEq(f.y, Math.fround(2.1234));
|
||||
assertEq(f.z, Math.fround(30.4444));
|
||||
assertEq(f.w, Math.fround(4.0001));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
@ -1,35 +0,0 @@
|
||||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 946042;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float32x4 min';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(1, 20, 3, 40);
|
||||
var b = float32x4(10, 2, 30, 4);
|
||||
var c = SIMD.float32x4.min(a, b);
|
||||
assertEq(c.x, 1);
|
||||
assertEq(c.y, 2);
|
||||
assertEq(c.z, 3);
|
||||
assertEq(c.w, 4);
|
||||
|
||||
var d = float32x4(1.4321, 20.5567, 30.8999, 4.0002);
|
||||
var e = float32x4(1.432, 20.5568, 30.8998, 4.0001);
|
||||
var f = float32x4.min(d, e);
|
||||
assertEq(f.x, Math.fround(1.432));
|
||||
assertEq(f.y, Math.fround(20.5567));
|
||||
assertEq(f.z, Math.fround(30.8998));
|
||||
assertEq(f.w, Math.fround(4.0001));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
@ -4,9 +4,6 @@ var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'int32x4 select';
|
||||
|
||||
const INT32_MAX = Math.pow(2, 31) - 1;
|
||||
const INT32_MIN = INT32_MAX + 1 | 0;
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
|
154
js/src/tests/ecma_6/TypedObject/simd/load.js
Normal file
154
js/src/tests/ecma_6/TypedObject/simd/load.js
Normal file
@ -0,0 +1,154 @@
|
||||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Our float32array will have 16 elements
|
||||
const SIZE_ARRAY = 16;
|
||||
|
||||
// 1 float32 == 4 bytes
|
||||
const SIZE_BYTES = SIZE_ARRAY * 4;
|
||||
|
||||
function MakeComparator(kind, arr) {
|
||||
var bpe = arr.BYTES_PER_ELEMENT;
|
||||
var uint8 = (bpe != 1) ? new Uint8Array(arr.buffer) : arr;
|
||||
|
||||
// Size in bytes of a single element in the SIMD vector.
|
||||
var sizeOfLaneElem;
|
||||
// Typed array constructor corresponding to the SIMD kind.
|
||||
var typedArrayCtor;
|
||||
switch (kind) {
|
||||
case 'int32x4':
|
||||
sizeOfLaneElem = 4;
|
||||
typedArrayCtor = Int32Array;
|
||||
break;
|
||||
case 'float32x4':
|
||||
sizeOfLaneElem = 4;
|
||||
typedArrayCtor = Float32Array;
|
||||
break;
|
||||
default:
|
||||
assertEq(true, false, "unknown SIMD kind");
|
||||
}
|
||||
|
||||
// Reads (numElemToRead * sizeOfLaneElem) bytes in arr, and reinterprets
|
||||
// these bytes as a typed array equivalent to the typed SIMD vector.
|
||||
var slice = function(start, numElemToRead) {
|
||||
// Read enough bytes
|
||||
var startBytes = start * bpe;
|
||||
var endBytes = startBytes + numElemToRead * sizeOfLaneElem;
|
||||
var asArray = Array.prototype.slice.call(uint8, startBytes, endBytes);
|
||||
|
||||
// If length is less than SIZE_BYTES bytes, fill with 0.
|
||||
// This is needed for loadX, loadXY, loadXYZ which do only partial
|
||||
// reads.
|
||||
for (var i = asArray.length; i < SIZE_BYTES; i++) asArray[i] = 0;
|
||||
assertEq(asArray.length, SIZE_BYTES);
|
||||
|
||||
return new typedArrayCtor(new Uint8Array(asArray).buffer);
|
||||
}
|
||||
|
||||
return {
|
||||
loadX: function(index) {
|
||||
var v = SIMD[kind].loadX(arr, index);
|
||||
assertEqX4(v, slice(index, 1));
|
||||
},
|
||||
|
||||
loadXY: function(index) {
|
||||
var v = SIMD[kind].loadXY(arr, index);
|
||||
assertEqX4(v, slice(index, 2));
|
||||
},
|
||||
|
||||
loadXYZ: function(index) {
|
||||
var v = SIMD[kind].loadXYZ(arr, index);
|
||||
assertEqX4(v, slice(index, 3));
|
||||
},
|
||||
|
||||
load: function(index) {
|
||||
var v = SIMD[kind].load(arr, index);
|
||||
assertEqX4(v, slice(index, 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testLoad(kind, TA) {
|
||||
for (var i = SIZE_ARRAY; i--;)
|
||||
TA[i] = i;
|
||||
|
||||
for (var ta of [
|
||||
new Uint8Array(TA.buffer),
|
||||
new Int8Array(TA.buffer),
|
||||
new Uint16Array(TA.buffer),
|
||||
new Int16Array(TA.buffer),
|
||||
new Uint32Array(TA.buffer),
|
||||
new Int32Array(TA.buffer),
|
||||
new Float32Array(TA.buffer),
|
||||
new Float64Array(TA.buffer)
|
||||
])
|
||||
{
|
||||
// Invalid args
|
||||
assertThrowsInstanceOf(() => SIMD[kind].load(), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].load(ta), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].load("hello", 0), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].load(ta, -1), TypeError);
|
||||
|
||||
// Valid and invalid reads
|
||||
var C = MakeComparator(kind, ta);
|
||||
var bpe = ta.BYTES_PER_ELEMENT;
|
||||
|
||||
var lastValidArgLoadX = (SIZE_BYTES - 4) / bpe | 0;
|
||||
var lastValidArgLoadXY = (SIZE_BYTES - 8) / bpe | 0;
|
||||
var lastValidArgLoadXYZ = (SIZE_BYTES - 12) / bpe | 0;
|
||||
var lastValidArgLoad = (SIZE_BYTES - 16) / bpe | 0;
|
||||
|
||||
C.load(0);
|
||||
C.load(1);
|
||||
C.load(2);
|
||||
C.load(3);
|
||||
C.load(lastValidArgLoad);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].load(ta, lastValidArgLoad + 1), TypeError);
|
||||
|
||||
C.loadX(0);
|
||||
C.loadX(1);
|
||||
C.loadX(2);
|
||||
C.loadX(3);
|
||||
C.loadX(lastValidArgLoadX);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].loadX(ta, lastValidArgLoadX + 1), TypeError);
|
||||
|
||||
C.loadXY(0);
|
||||
C.loadXY(1);
|
||||
C.loadXY(2);
|
||||
C.loadXY(3);
|
||||
C.loadXY(lastValidArgLoadXY);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), TypeError);
|
||||
|
||||
C.loadXYZ(0);
|
||||
C.loadXYZ(1);
|
||||
C.loadXYZ(2);
|
||||
C.loadXYZ(3);
|
||||
C.loadXYZ(lastValidArgLoadXYZ);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), TypeError);
|
||||
}
|
||||
|
||||
// Test ToInt32 behavior
|
||||
var v = SIMD[kind].load(TA, 12.5);
|
||||
assertEqX4(v, [12, 13, 14, 15]);
|
||||
|
||||
var obj = {
|
||||
valueOf: function() { return 12 }
|
||||
}
|
||||
var v = SIMD[kind].load(TA, obj);
|
||||
assertEqX4(v, [12, 13, 14, 15]);
|
||||
|
||||
var obj = {
|
||||
valueOf: function() { throw new TypeError("i ain't a number"); }
|
||||
}
|
||||
assertThrowsInstanceOf(() => SIMD[kind].load(TA, obj), TypeError);
|
||||
}
|
||||
|
||||
testLoad('float32x4', new Float32Array(SIZE_ARRAY));
|
||||
testLoad('int32x4', new Int32Array(SIZE_ARRAY));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
@ -0,0 +1,30 @@
|
||||
function assertEqX4(v, arr) {
|
||||
try {
|
||||
assertEq(v.x, arr[0]);
|
||||
assertEq(v.y, arr[1]);
|
||||
assertEq(v.z, arr[2]);
|
||||
assertEq(v.w, arr[3]);
|
||||
} catch (e) {
|
||||
print("stack trace:", e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function simdToArray(v) {
|
||||
return [v.x, v.y, v.z, v.w];
|
||||
}
|
||||
|
||||
const INT32_MAX = Math.pow(2, 31) - 1;
|
||||
const INT32_MIN = -Math.pow(2, 31);
|
||||
assertEq(INT32_MAX + 1 | 0, INT32_MIN);
|
||||
|
||||
function testBinaryFunc(v, w, simdFunc, func) {
|
||||
var varr = simdToArray(v);
|
||||
var warr = simdToArray(w);
|
||||
|
||||
var observed = simdToArray(simdFunc(v, w));
|
||||
var expected = varr.map(function(v, i) { return func(varr[i], warr[i]); });
|
||||
|
||||
for (var i = 0; i < observed.length; i++)
|
||||
assertEq(observed[i], expected[i]);
|
||||
}
|
85
js/src/tests/ecma_6/TypedObject/simd/store.js
Normal file
85
js/src/tests/ecma_6/TypedObject/simd/store.js
Normal file
@ -0,0 +1,85 @@
|
||||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// As SIMD.*.store is entirely symmetric to SIMD.*.load, this file just
|
||||
// contains basic tests to store on one single TypedArray kind, while load is
|
||||
// exhaustively tested. See load.js for more details.
|
||||
|
||||
const POISON = 42;
|
||||
|
||||
function reset(ta) {
|
||||
for (var i = 0; i < ta.length; i++)
|
||||
ta[i] = POISON + i;
|
||||
}
|
||||
|
||||
function assertChanged(ta, from, expected) {
|
||||
var i = 0;
|
||||
for (; i < from; i++)
|
||||
assertEq(ta[i], POISON + i);
|
||||
for (; i < from + expected.length; i++)
|
||||
assertEq(ta[i], expected[i - from]);
|
||||
for (; i < ta.length; i++)
|
||||
assertEq(ta[i], POISON + i);
|
||||
}
|
||||
|
||||
function testStore(ta, kind, i, v) {
|
||||
reset(ta);
|
||||
SIMD[kind].storeX(ta, i, v);
|
||||
assertChanged(ta, i, [v.x]);
|
||||
|
||||
reset(ta);
|
||||
SIMD[kind].storeXY(ta, i, v);
|
||||
assertChanged(ta, i, [v.x, v.y]);
|
||||
|
||||
reset(ta);
|
||||
SIMD[kind].storeXYZ(ta, i, v);
|
||||
assertChanged(ta, i, [v.x, v.y, v.z]);
|
||||
|
||||
reset(ta);
|
||||
SIMD[kind].store(ta, i, v);
|
||||
assertChanged(ta, i, [v.x, v.y, v.z, v.w]);
|
||||
}
|
||||
|
||||
function testStoreInt32x4() {
|
||||
var I32 = new Int32Array(16);
|
||||
|
||||
var v = SIMD.int32x4(0, 1, Math.pow(2,31) - 1, -Math.pow(2, 31));
|
||||
testStore(I32, 'int32x4', 0, v);
|
||||
testStore(I32, 'int32x4', 1, v);
|
||||
testStore(I32, 'int32x4', 2, v);
|
||||
testStore(I32, 'int32x4', 12, v);
|
||||
|
||||
assertThrowsInstanceOf(() => SIMD.int32x4.store(I32), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD.int32x4.store(I32, 0), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD.float32x4.store(I32, 0, v), TypeError);
|
||||
}
|
||||
|
||||
function testStoreFloat32x4() {
|
||||
var F32 = new Float32Array(16);
|
||||
|
||||
var v = SIMD.float32x4(1,2,3,4);
|
||||
testStore(F32, 'float32x4', 0, v);
|
||||
testStore(F32, 'float32x4', 1, v);
|
||||
testStore(F32, 'float32x4', 2, v);
|
||||
testStore(F32, 'float32x4', 12, v);
|
||||
|
||||
var v = SIMD.float32x4(NaN, -0, -Infinity, 5e-324);
|
||||
testStore(F32, 'float32x4', 0, v);
|
||||
testStore(F32, 'float32x4', 1, v);
|
||||
testStore(F32, 'float32x4', 2, v);
|
||||
testStore(F32, 'float32x4', 12, v);
|
||||
|
||||
assertThrowsInstanceOf(() => SIMD.float32x4.store(F32), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD.float32x4.store(F32, 0), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD.int32x4.store(F32, 0, v), TypeError);
|
||||
}
|
||||
|
||||
testStoreInt32x4();
|
||||
testStoreFloat32x4();
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user