diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/vr/vrhost | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/vr/vrhost')
-rw-r--r-- | gfx/vr/vrhost/moz.build | 40 | ||||
-rw-r--r-- | gfx/vr/vrhost/testhost/moz.build | 12 | ||||
-rw-r--r-- | gfx/vr/vrhost/testhost/testhost.cpp | 43 | ||||
-rw-r--r-- | gfx/vr/vrhost/vrhost.def | 15 | ||||
-rw-r--r-- | gfx/vr/vrhost/vrhostapi.cpp | 374 | ||||
-rw-r--r-- | gfx/vr/vrhost/vrhostex.h | 33 | ||||
-rw-r--r-- | gfx/vr/vrhost/vrhostnightly.def | 23 | ||||
-rw-r--r-- | gfx/vr/vrhost/vrhosttest.cpp | 370 |
8 files changed, 910 insertions, 0 deletions
diff --git a/gfx/vr/vrhost/moz.build b/gfx/vr/vrhost/moz.build new file mode 100644 index 0000000000..1fa6e77f03 --- /dev/null +++ b/gfx/vr/vrhost/moz.build @@ -0,0 +1,40 @@ +# -*- Mode: python; 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/. + +SOURCES += ["/gfx/vr/service/VRSession.cpp", "/gfx/vr/VRShMem.cpp", "vrhostapi.cpp"] + +# Since .def files do not support preprocessing, switch which file is used +# to declare exports. See comments in the files for more info. +if CONFIG["NIGHTLY_BUILD"]: + DEFFILE = "vrhostnightly.def" + SOURCES += ["vrhosttest.cpp"] +else: + DEFFILE = "vrhost.def" + +LOCAL_INCLUDES += [ + "/gfx/vr", + "/gfx/vr/external_api", + "/gfx/vr/service", + "/ipc/chromium/src", +] + +EXPORTS.vrhost = ["vrhostex.h"] + +DIRS += ["testhost"] + +# this is Windows-only for now +DEFINES["XP_WIN"] = True +# fixes "lld-link: error: undefined symbol: __imp_moz_xmalloc" +DEFINES["MOZ_NO_MOZALLOC"] = True +# fixes "STL code can only be used with infallible ::operator new()" +DisableStlWrapping() + +# Define UNICODE for default support in this dll +DEFINES["UNICODE"] = True +DEFINES["_UNICODE"] = True + +# Use SharedLibrary to generate the dll +SharedLibrary("vrhost") diff --git a/gfx/vr/vrhost/testhost/moz.build b/gfx/vr/vrhost/testhost/moz.build new file mode 100644 index 0000000000..5b0d3b16e1 --- /dev/null +++ b/gfx/vr/vrhost/testhost/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; 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/. + +SOURCES += ["testhost.cpp"] + +USE_LIBS += ["vrhost"] + +# Use Progam to generate the executable +Program("vrtesthost") diff --git a/gfx/vr/vrhost/testhost/testhost.cpp b/gfx/vr/vrhost/testhost/testhost.cpp new file mode 100644 index 0000000000..ae667aaee4 --- /dev/null +++ b/gfx/vr/vrhost/testhost/testhost.cpp @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <windows.h> +#include "vrhost/vrhostex.h" + +int main(int argc, char* argv[], char* envp[]) { + HINSTANCE hVR = ::LoadLibraryW(L"vrhost.dll"); + if (hVR != nullptr) { + // Sometimes LoadLibrary can set an error, but, if it returns a handle, + // then it succeeded. Clear the last error so that subsequent calls to + // GetLastError don't improperly attribute failure to another API. + ::SetLastError(0); + + PFN_SAMPLE lpfnSample = (PFN_SAMPLE)GetProcAddress(hVR, "SampleExport"); + if (lpfnSample != nullptr) { + lpfnSample(); + } + + if (strcmp(argv[1], "-testsvc") == 0) { + PFN_SAMPLE lpfnService = + (PFN_SAMPLE)GetProcAddress(hVR, "TestTheService"); + + lpfnService(); + } else if (strcmp(argv[1], "-testmgr") == 0) { + PFN_SAMPLE lpfnManager = + (PFN_SAMPLE)GetProcAddress(hVR, "TestTheManager"); + lpfnManager(); + } else if (strcmp(argv[1], "-testvrwin") == 0) { + PFN_SAMPLE lpfnManager = + (PFN_SAMPLE)GetProcAddress(hVR, "TestCreateVRWindow"); + lpfnManager(); + } + + ::FreeLibrary(hVR); + hVR = nullptr; + } + + return 0; +} diff --git a/gfx/vr/vrhost/vrhost.def b/gfx/vr/vrhost/vrhost.def new file mode 100644 index 0000000000..7a80c3b247 --- /dev/null +++ b/gfx/vr/vrhost/vrhost.def @@ -0,0 +1,15 @@ +;+# 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/.
+
+LIBRARY vrhost.dll
+
+;+= Note: this file defines exports that are only available in Beta and
+;+= Release builds so that test exports are not exposed.
+;+= Please ensure that these exports are also declared in vrhostnightly.def.
+EXPORTS
+ CreateVRWindow PRIVATE
+ CloseVRWindow PRIVATE
+ SendUIMessageToVRWindow PRIVATE
+ WaitForVREvent PRIVATE
+ SendVRTelemetry PRIVATE
\ No newline at end of file diff --git a/gfx/vr/vrhost/vrhostapi.cpp b/gfx/vr/vrhost/vrhostapi.cpp new file mode 100644 index 0000000000..508e379a5f --- /dev/null +++ b/gfx/vr/vrhost/vrhostapi.cpp @@ -0,0 +1,374 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// vrhostapi.cpp +// Definition of functions that are exported from this dll + +#include "vrhostex.h" +#include "VRShMem.h" + +#include <stdio.h> +#include <string.h> +#include <random> +#include <queue> + +#include "windows.h" + +class VRShmemInstance { + public: + VRShmemInstance() = delete; + VRShmemInstance(const VRShmemInstance& aRHS) = delete; + + static mozilla::gfx::VRShMem& GetInstance() { + static mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/); + return shmem; + } +}; + +// VRWindowManager adds a level of indirection so that system HWND isn't exposed +// outside of these APIs +class VRWindowManager { + public: + HWND GetHWND(uint32_t nId) { + if (nId == nWindow) { + return hWindow; + } else { + return nullptr; + } + } + + uint32_t GetId(HWND hwnd) { + if (hwnd == hWindow) { + return nWindow; + } else { + return 0; + } + } + + HANDLE GetProc(uint32_t nId) { + if (nId == nWindow) { + return hProc; + } else { + return nullptr; + } + } + + HANDLE GetEvent() { return hEvent; } + + uint32_t SetHWND(HWND hwnd, HANDLE hproc, HANDLE hevent) { + if (hWindow == nullptr) { + MOZ_ASSERT(hwnd != nullptr && hproc != nullptr); + hWindow = hwnd; + hProc = hproc; + hEvent = hevent; + nWindow = GetRandomUInt(); +#if defined(DEBUG) && defined(NIGHTLY_BUILD) + printf("VRWindowManager: Storing HWND: 0x%p as ID: 0x%X\n", hWindow, + nWindow); +#endif + return nWindow; + } else { + return -1; + } + } + + uint32_t GetRandomUInt() { return randomGenerator(); } + + static VRWindowManager* GetManager() { + if (Instance == nullptr) { + Instance = new VRWindowManager(); + } + return Instance; + } + + private: + static VRWindowManager* Instance; + + // For now, simply store the ID and HWND, and expand + // to a map when multiple windows/instances are supported. + uint32_t nWindow = 0; + HWND hWindow = nullptr; + HANDLE hProc = nullptr; + HANDLE hEvent = nullptr; + std::random_device randomGenerator; +}; +VRWindowManager* VRWindowManager::Instance = nullptr; + +class VRTelemetryManager { + public: + void SendTelemetry(uint32_t aTelemetryId, uint32_t aValue) { + if (!aTelemetryId) { + return; + } + + mozilla::gfx::VRTelemetryState telemetryState = {0}; + VRShmemInstance::GetInstance().PullTelemetryState(telemetryState); + + if (telemetryState.uid == 0) { + telemetryState.uid = sUid; + } + + switch (mozilla::gfx::VRTelemetryId(aTelemetryId)) { + case mozilla::gfx::VRTelemetryId::INSTALLED_FROM: + MOZ_ASSERT(aValue <= 0x07, + "VRTelemetryId::INSTALLED_FROM only allows 3 bits."); + telemetryState.installedFrom = true; + telemetryState.installedFromValue = aValue; + break; + case mozilla::gfx::VRTelemetryId::ENTRY_METHOD: + MOZ_ASSERT(aValue <= 0x07, + "VRTelemetryId::ENTRY_METHOD only allows 3 bits."); + telemetryState.entryMethod = true; + telemetryState.entryMethodValue = aValue; + break; + case mozilla::gfx::VRTelemetryId::FIRST_RUN: + MOZ_ASSERT(aValue <= 0x01, + "VRTelemetryId::FIRST_RUN only allows 1 bit."); + telemetryState.firstRun = true; + telemetryState.firstRunValue = aValue; + break; + default: + MOZ_CRASH("Undefined VR telemetry type."); + break; + } + VRShmemInstance::GetInstance().PushTelemetryState(telemetryState); + ++sUid; + } + + static VRTelemetryManager* GetManager() { + if (Instance == nullptr) { + Instance = new VRTelemetryManager(); + } + return Instance; + } + + private: + static VRTelemetryManager* Instance; + static uint32_t sUid; // It starts from 1, Zero means the data is read yet + // from VRManager. +}; +uint32_t VRTelemetryManager::sUid = 1; +VRTelemetryManager* VRTelemetryManager::Instance = nullptr; + +// Struct to send params to StartFirefoxThreadProc +struct StartFirefoxParams { + char* firefoxFolder; + char* firefoxProfileFolder; + HANDLE hProcessFx; +}; + +// Helper threadproc function for CreateVRWindow +DWORD StartFirefoxThreadProc(_In_ LPVOID lpParameter) { + wchar_t cmd[] = L"%Sfirefox.exe -wait-for-browser -profile %S --fxr"; + + StartFirefoxParams* params = static_cast<StartFirefoxParams*>(lpParameter); + wchar_t cmdWithPath[MAX_PATH + MAX_PATH] = {0}; + int err = swprintf_s(cmdWithPath, ARRAYSIZE(cmdWithPath), cmd, + params->firefoxFolder, params->firefoxProfileFolder); + + if (err != -1) { + PROCESS_INFORMATION procFx = {0}; + STARTUPINFO startupInfoFx = {0}; + +#if defined(DEBUG) && defined(NIGHTLY_BUILD) + printf("Starting Firefox via: %S\n", cmdWithPath); +#endif + + // Start Firefox + bool fCreateContentProc = ::CreateProcess(nullptr, // lpApplicationName, + cmdWithPath, + nullptr, // lpProcessAttributes, + nullptr, // lpThreadAttributes, + TRUE, // bInheritHandles, + 0, // dwCreationFlags, + nullptr, // lpEnvironment, + nullptr, // lpCurrentDirectory, + &startupInfoFx, &procFx); + + if (!fCreateContentProc) { + printf("Failed to create Firefox process"); + } + + params->hProcessFx = procFx.hProcess; + } + + return 0; +} + +// This export is responsible for starting up a new VR window in Firefox and +// returning data related to its creation back to the caller. +// See nsFxrCommandLineHandler::Handle for more details about the bootstrapping +// process with Firefox. +void CreateVRWindow(char* firefoxFolderPath, char* firefoxProfilePath, + uint32_t dxgiAdapterID, uint32_t widthHost, + uint32_t heightHost, uint32_t* windowId, void** hTex, + uint32_t* width, uint32_t* height) { + mozilla::gfx::VRWindowState windowState = {0}; + + int err = sprintf_s(windowState.signalName, ARRAYSIZE(windowState.signalName), + "fxr::CreateVRWindow::%X", + VRWindowManager::GetManager()->GetRandomUInt()); + + if (err > 0) { + HANDLE hEvent = ::CreateEventA(nullptr, // attributes + FALSE, // bManualReset + FALSE, // bInitialState + windowState.signalName); + + if (hEvent != nullptr) { + // Create Shmem and push state + VRShmemInstance::GetInstance().CreateShMem( + true /*aCreateOnSharedMemory*/); + VRShmemInstance::GetInstance().PushWindowState(windowState); + + // Start Firefox in another thread so that this thread can wait for the + // window state to be updated during Firefox startup + StartFirefoxParams fxParams = {0}; + fxParams.firefoxFolder = firefoxFolderPath; + fxParams.firefoxProfileFolder = firefoxProfilePath; + DWORD dwTid = 0; + HANDLE hThreadFx = CreateThread(nullptr, 0, StartFirefoxThreadProc, + &fxParams, 0, &dwTid); + if (hThreadFx != nullptr) { + // Wait for Firefox to populate rest of window state + ::WaitForSingleObject(hEvent, INFINITE); + + // Update local WindowState with data from Firefox + VRShmemInstance::GetInstance().PullWindowState(windowState); + + (*hTex) = windowState.textureFx; + (*windowId) = VRWindowManager::GetManager()->SetHWND( + (HWND)windowState.hwndFx, fxParams.hProcessFx, hEvent); + (*width) = windowState.widthFx; + (*height) = windowState.heightFx; + } else { + // How do I failfast? + } + } + } +} + +// Keep track of when WaitForVREvent is running to manage shutdown of +// this vrhost. See CloseVRWindow for more details. +volatile bool s_WaitingForVREvent = false; + +// Blocks until a new event is set on the shmem. +// Note: this function can be called from any thread. +void WaitForVREvent(uint32_t& nVRWindowID, uint32_t& eventType, + uint32_t& eventData1, uint32_t& eventData2) { + MOZ_ASSERT(!s_WaitingForVREvent); + s_WaitingForVREvent = true; + + // Initialize all of the out params + nVRWindowID = 0; + eventType = 0; + eventData1 = 0; + eventData2 = 0; + + if (VRShmemInstance::GetInstance().HasExternalShmem()) { + HANDLE evt = VRWindowManager::GetManager()->GetEvent(); + const DWORD waitResult = ::WaitForSingleObject(evt, INFINITE); + if (waitResult != WAIT_OBJECT_0) { + MOZ_ASSERT(false && "Error WaitForVREvent().\n"); + return; + } + mozilla::gfx::VRWindowState windowState = {0}; + VRShmemInstance::GetInstance().PullWindowState(windowState); + + nVRWindowID = + VRWindowManager::GetManager()->GetId((HWND)windowState.hwndFx); + if (nVRWindowID != 0) { + eventType = (uint32_t)windowState.eventType; + mozilla::gfx::VRFxEventType fxEvent = + mozilla::gfx::VRFxEventType(eventType); + + switch (fxEvent) { + case mozilla::gfx::VRFxEventType::IME: + eventData1 = (uint32_t)windowState.eventState; + break; + case mozilla::gfx::VRFxEventType::FULLSCREEN: + eventData1 = (uint32_t)windowState.eventState; + break; + case mozilla::gfx::VRFxEventType::SHUTDOWN: + VRShmemInstance::GetInstance().CloseShMem(); + break; + default: + MOZ_ASSERT(false && "Undefined VR Fx event."); + break; + } + } + } + s_WaitingForVREvent = false; +} + +// Sends a message to the VR window to close. +void CloseVRWindow(uint32_t nVRWindowID, bool waitForTerminate) { + HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID); + if (hwnd != nullptr) { + ::SendMessage(hwnd, WM_CLOSE, 0, 0); + + if (waitForTerminate) { + // Wait for Firefox main process to exit + ::WaitForSingleObject(VRWindowManager::GetManager()->GetProc(nVRWindowID), + INFINITE); + } + } + + // If a thread is currently blocked on WaitForVREvent, then defer closing the + // shmem to that thread by sending an async event. + // If WaitForVREvent is not running, it is safe to close the shmem now. + // Subsequent calls to WaitForVREvent will return early because it does not + // have an external shmem. + if (s_WaitingForVREvent) { + VRShmemInstance::GetInstance().SendShutdowmState(nVRWindowID); + } else { + VRShmemInstance::GetInstance().CloseShMem(); + } +} + +// Forwards Win32 UI window messages to the Firefox widget/window associated +// with nVRWindowID. Note that not all message type is supported (only those +// allowed in the switch block below). +void SendUIMessageToVRWindow(uint32_t nVRWindowID, uint32_t msg, + uint64_t wparam, uint64_t lparam) { + HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID); + if (hwnd != nullptr) { + switch (msg) { + case WM_MOUSEWHEEL: + // For MOUSEWHEEL, the coordinates are supposed to be at Screen origin + // rather than window client origin. + // Make the conversion to screen coordinates before posting the message + // to the Fx window. + POINT pt; + POINTSTOPOINT(pt, MAKEPOINTS(lparam)); + if (!::ClientToScreen(hwnd, &pt)) { + break; + } + // otherwise, fallthrough + lparam = POINTTOPOINTS(pt); + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP: + ::PostMessage(hwnd, msg, wparam, lparam); + break; + + default: + break; + } + } +} + +void SendVRTelemetry(uint32_t nVRWindowID, uint32_t telemetryId, + uint32_t value) { + HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID); + if (hwnd == nullptr) { + return; + } + VRTelemetryManager::GetManager()->SendTelemetry(telemetryId, value); +}
\ No newline at end of file diff --git a/gfx/vr/vrhost/vrhostex.h b/gfx/vr/vrhost/vrhostex.h new file mode 100644 index 0000000000..70f4f98fba --- /dev/null +++ b/gfx/vr/vrhost/vrhostex.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// vrhostex.h +// This file declares the functions and their typedefs for functions exported +// by vrhost.dll + +#pragma once +#include <stdint.h> + +// void SampleExport(); +typedef void (*PFN_SAMPLE)(); + +typedef void (*PFN_CREATEVRWINDOW)(char* firefoxFolderPath, + char* firefoxProfilePath, + uint32_t dxgiAdapterID, uint32_t widthHost, + uint32_t heightHost, uint32_t* windowId, + void** hTex, uint32_t* width, + uint32_t* height); + +typedef void (*PFN_CLOSEVRWINDOW)(uint32_t nVRWindowID, bool waitForTerminate); + +typedef void (*PFN_SENDUIMSG)(uint32_t nVRWindowID, uint32_t msg, + uint64_t wparam, uint64_t lparam); + +typedef void (*PFN_WAITFORVREVENT)(uint32_t& nVRWindowID, uint32_t& eventType, + uint32_t& eventData1, uint32_t& eventData2); + +typedef void (*PFN_SENDVRTELEMETRY)(uint32_t nVRWindowID, uint32_t telemetryId, + uint32_t value); diff --git a/gfx/vr/vrhost/vrhostnightly.def b/gfx/vr/vrhost/vrhostnightly.def new file mode 100644 index 0000000000..0a849e37ce --- /dev/null +++ b/gfx/vr/vrhost/vrhostnightly.def @@ -0,0 +1,23 @@ +;+# 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/.
+
+LIBRARY vrhost.dll
+
+;+= Note: this file defines exports that are only available in Nightly builds
+;+= so that test exports are not exposed in Beta and Release builds.
+;+= Please ensure that the Public Export APIs in the first section below are
+;+= also declared in vrhost.def.
+EXPORTS
+;+= Public Export APIs for vrhost
+ CreateVRWindow PRIVATE
+ CloseVRWindow PRIVATE
+ SendUIMessageToVRWindow PRIVATE
+ WaitForVREvent PRIVATE
+ SendVRTelemetry PRIVATE
+
+;+= Exports only available in Nightlies for testing
+ SampleExport PRIVATE
+ TestTheManager PRIVATE
+ TestTheService PRIVATE
+ TestCreateVRWindow PRIVATE
\ No newline at end of file diff --git a/gfx/vr/vrhost/vrhosttest.cpp b/gfx/vr/vrhost/vrhosttest.cpp new file mode 100644 index 0000000000..84c11c10d6 --- /dev/null +++ b/gfx/vr/vrhost/vrhosttest.cpp @@ -0,0 +1,370 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// vrhosttest.cpp +// Definition of testing and validation functions that are exported from this +// dll + +#include "vrhostex.h" +#include "VRShMem.h" + +#include <stdio.h> +#include "windows.h" + +static const char s_pszSharedEvent[] = "vrhost_test_event_signal"; +static const DWORD s_dwWFSO_WAIT = 20000; + +void SampleExport() { printf("vrhost.cpp hello world\n"); } + +// For testing ShMem as Manager and Service: +// The two processes should output the steps, synchronously, to validate +// 2-way communication via VRShMem as follows +// 01 mgr: create mgr +// 02 mgr: wait for signal +// 03 svc: create svc +// 04 svc: send signal +// 05 svc: wait for signal +// 06 mgr: push browser +// 07 mgr: send signal +// 08 mgr: wait for signal +// 09 svc: pull browser +// 10 svc: verify data +// 11 svc: push system +// 12 svc: send signal +// 13 svc: wait for signal +// 14 mgr: pull system +// 15 mgr: verify data +// 16 mgr: push window +// 17 mgr: send signal +// 18 mgr: wait for signal +// 19 svc: pull window +// 20 svc: verify data +// 21 svc: push window +// 22 svc: send signal +// 23 mgr: pull window +// 24 mgr: verify data +// 25 return +// These tests can be run with two instances of vrtesthost.exe, one first +// running with -testmgr and the second running with -testsvc. +// TODO: Bug 1563235 - Convert vrtesthost.exe tests into unit tests + +// For testing VRShMem as the Manager (i.e., the one who creates the +// shmem). The sequence of how it tests with the service is listed above. +void TestTheManager() { + printf("TestTheManager Start\n"); + + MOZ_ASSERT(GetLastError() == 0, + "TestTheManager should start with no OS errors"); + HANDLE hEvent = ::CreateEventA(nullptr, // lpEventAttributes + FALSE, // bManualReset + FALSE, // bInitialState + s_pszSharedEvent // lpName + ); + + printf("\n01 mgr: create mgr\n"); + mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/); + shmem.CreateShMem(true /*aCreateOnSharedMemory*/); + + printf("02 mgr: wait for signal\n"); + ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT); + + // Set some state to verify on the other side + mozilla::gfx::VRBrowserState browserState = {0}; + browserState.presentationActive = true; + browserState.layerState[0].type = + mozilla::gfx::VRLayerType::LayerType_2D_Content; + browserState.hapticState[0].controllerIndex = 987; + + printf("06 mgr: push browser\n"); + shmem.PushBrowserState(browserState, true); + + printf("07 mgr: send signal\n"); + ::SetEvent(hEvent); + + printf("08 mgr: wait for signal\n"); + ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT); + + printf("14 mgr: pull system\n"); + mozilla::gfx::VRSystemState state; + shmem.PullSystemState(state.displayState, state.sensorState, + state.controllerState, state.enumerationCompleted, + nullptr); + + printf( + "15 mgr: verify data\n" + "\tstate.enumerationCompleted = %d\n" + "\tstate.displayState.displayName = \"%s\"\n" + "\tstate.controllerState[1].hand = %hhu\n" + "\tstate.sensorState.inputFrameID = %llu\n", + state.enumerationCompleted, state.displayState.displayName, + state.controllerState[1].hand, state.sensorState.inputFrameID); + + // Test the WindowState functions as the host + mozilla::gfx::VRWindowState windowState = {0}; + strcpy(windowState.signalName, "randomsignalstring"); + windowState.dxgiAdapterHost = 99; + windowState.heightHost = 42; + windowState.widthHost = 24; + + printf("16 mgr: push window\n"); + shmem.PushWindowState(windowState); + + printf("17 mgr: send signal\n"); + ::SetEvent(hEvent); + + printf("18 mgr: wait for signal\n"); + ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT); + + printf("23 mgr: pull window\n"); + shmem.PullWindowState(windowState); + + printf( + "24 svc: verify data\n" + "\tstate.hwndFx = 0x%llX\n" + "\tstate.heightFx = %d\n" + "\tstate.widthFx = %d\n" + "\tstate.textureHandle = %p\n", + windowState.hwndFx, windowState.heightFx, windowState.widthFx, + windowState.textureFx); + + shmem.CloseShMem(); + + printf("TestTheManager complete"); + fflush(nullptr); +} + +// For testing VRShMem as the Service (i.e., the one who consumes the +// shmem). The sequence of how it tests with the service is listed above. +void TestTheService() { + printf("TestTheService Start\n"); + + MOZ_ASSERT(GetLastError() == 0, + "TestTheService should start with no OS errors"); + // Handle created by TestTheManager above. + HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess + FALSE, // bInheritHandle + s_pszSharedEvent // lpName + ); + + printf("\n03 svc: create svc\n"); + mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/); + shmem.JoinShMem(); + + printf("04 svc: send signal\n"); + ::SetEvent(hEvent); + + printf("05 svc: wait for signal\n"); + ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT); + + printf("09 svc: pull browser\n"); + mozilla::gfx::VRBrowserState state; + shmem.PullBrowserState(state); + + printf( + "10 svc: verify data\n" + "\tstate.presentationActive = %d\n" + "\tstate.layerState[0].type = %hu\n" + "\tstate.hapticState[0].controllerIndex = %d\n", + state.presentationActive, state.layerState[0].type, + state.hapticState[0].controllerIndex); + + // Set some state to verify on the other side + mozilla::gfx::VRSystemState systemState; + systemState.enumerationCompleted = true; + strncpy(systemState.displayState.displayName, "test from vrservice shmem", + mozilla::gfx::kVRDisplayNameMaxLen); + systemState.controllerState[1].hand = mozilla::gfx::ControllerHand::Left; + systemState.sensorState.inputFrameID = 1234567; + + printf("11 svc: push system\n"); + shmem.PushSystemState(systemState); + + printf("12 svc: send signal\n"); + ::SetEvent(hEvent); + + printf("13 svc: wait for signal\n"); + ::WaitForSingleObject(hEvent, s_dwWFSO_WAIT); + + // Test the WindowState functions as Firefox + printf("19 svc: pull window\n"); + mozilla::gfx::VRWindowState windowState; + shmem.PullWindowState(windowState); + + printf( + "20 svc: verify data\n" + "\tstate.signalName = \"%s\"\n" + "\tstate.dxgiAdapterHost = %d\n" + "\tstate.heightHost = %d\n" + "\tstate.widthHost = %d\n", + windowState.signalName, windowState.dxgiAdapterHost, + windowState.heightHost, windowState.widthHost); + + windowState.hwndFx = 0x1234; + windowState.heightFx = 1234; + windowState.widthFx = 4321; + windowState.textureFx = (HANDLE)0x77777; + + printf("21 svc: push window\n"); + shmem.PushWindowState(windowState); + + printf("22 svc: send signal\n"); + ::SetEvent(hEvent); + + shmem.LeaveShMem(); + + printf("TestTheService complete"); + fflush(nullptr); +} + +DWORD TestWaitForVREventThreadProc(_In_ LPVOID lpParameter) { + // WaitForVREvent + printf("\nStarting TestWaitForVREventThreadProc\n"); + + PFN_WAITFORVREVENT fnWaitForVRMsg = (PFN_WAITFORVREVENT)lpParameter; + + uint32_t nVRWindowID = 0; + uint32_t eventType = 0; + uint32_t eventData1 = 0; + uint32_t eventData2 = 0; + + while (eventType != 2) { // FxEvent_SHUTDOWN + fnWaitForVRMsg(nVRWindowID, eventType, eventData1, eventData2); + printf( + "\nWaitForVRMessage:\n\tvrWindowID: %d\n\teventType: %d\n\teventData1: " + "%d\n\teventData2: %d\n", + nVRWindowID, eventType, eventData1, eventData2); + } + + printf("\nReturning from TestWaitForVREventThreadProc\n"); + + return 0; +} + +// This function tests the export CreateVRWindow by outputting the return values +// from the call to the console, as well as testing CloseVRWindow after the data +// is retrieved. +void TestCreateVRWindow() { + printf("TestCreateVRWindow Start\n"); + + // Cache function calls to test real-world export and usage + HMODULE hVRHost = ::GetModuleHandleA("vrhost.dll"); + PFN_CREATEVRWINDOW fnCreate = + (PFN_CREATEVRWINDOW)::GetProcAddress(hVRHost, "CreateVRWindow"); + PFN_CLOSEVRWINDOW fnClose = + (PFN_CLOSEVRWINDOW)::GetProcAddress(hVRHost, "CloseVRWindow"); + PFN_SENDUIMSG fnSendMsg = + (PFN_SENDUIMSG)::GetProcAddress(hVRHost, "SendUIMessageToVRWindow"); + PFN_WAITFORVREVENT fnWaitForVRMsg = + (PFN_WAITFORVREVENT)::GetProcAddress(hVRHost, "WaitForVREvent"); + + // Create the VR Window and store data from creation + char currentDir[MAX_PATH] = {0}; + char currentDirProfile[MAX_PATH] = {0}; + DWORD currentDirLength = + ::GetCurrentDirectoryA(ARRAYSIZE(currentDir), currentDir); + currentDir[currentDirLength] = '\\'; + + int err = sprintf_s(currentDirProfile, ARRAYSIZE(currentDirProfile), + "%svrhosttest-profile", currentDir); + if (err > 0) { + printf("Starting Firefox from %s\n", currentDir); + + UINT windowId; + HANDLE hTex; + UINT width; + UINT height; + fnCreate(currentDir, currentDirProfile, 0, 100, 200, &windowId, &hTex, + &width, &height); + + // Now that the Fx window is created, start a new thread to wait for VR + // events to be sent from it + DWORD dwTid; + HANDLE hThreadWait = CreateThread(nullptr, 0, TestWaitForVREventThreadProc, + (void*)fnWaitForVRMsg, 0, &dwTid); + + // Wait for Fx to finish launch +#ifdef DEBUG + ::Sleep(5000); +#else + ::Sleep(2000); +#endif + + printf( + "1. Simulating a click on the Home button, which should look " + "pressed\n"); + POINT pt; + pt.x = 180; + pt.y = 700; + fnSendMsg(windowId, WM_LBUTTONDOWN, 0, POINTTOPOINTS(pt)); + ::Sleep(3000); + fnSendMsg(windowId, WM_LBUTTONUP, 0, POINTTOPOINTS(pt)); + + printf( + "2. Simulating hovering across the URL bar, which should turn " + "blue\n"); + pt.x = 600; + for (int i = 0; i < 100; ++i) { + pt.x++; + fnSendMsg(windowId, WM_MOUSEMOVE, 0, POINTTOPOINTS(pt)); + ::Sleep(5); + } + + printf( + "3. Simulating clicking inside the URL bar, which should " + "highlight the text\n"); + pt.x = 700; + pt.y = 700; + fnSendMsg(windowId, WM_LBUTTONDOWN, 0, POINTTOPOINTS(pt)); + fnSendMsg(windowId, WM_LBUTTONUP, 0, POINTTOPOINTS(pt)); + + ::Sleep(3000); + + printf("4. Type some UTF16 characters in the URL bar\n"); + fnSendMsg(windowId, WM_CHAR, 0x4E64, 0); + fnSendMsg(windowId, WM_CHAR, 0x312D, 0); + fnSendMsg(windowId, WM_CHAR, 0x0BB9, 0); + fnSendMsg(windowId, WM_CHAR, 0x2745, 0); + + printf( + "5. Simulating clicking outside the URL bar, which should " + "send a keyboard blur event\n"); + pt.x = 20; + pt.y = 20; + fnSendMsg(windowId, WM_LBUTTONDOWN, 0, POINTTOPOINTS(pt)); + fnSendMsg(windowId, WM_LBUTTONUP, 0, POINTTOPOINTS(pt)); + + ::Sleep(1000); + + printf("6. Simulating scrolling the web content down and then up\n"); + + pt.x = 100; + pt.y = 100; + for (int i = -20; i < 10; ++i) { + fnSendMsg(windowId, WM_MOUSEWHEEL, + MAKELONG(0, (i < 0) ? -WHEEL_DELTA : WHEEL_DELTA), + POINTTOPOINTS(pt)); + ::Sleep(100); + } + + ::Sleep(5000); + + // Close the Firefox VR Window + fnClose(windowId, true); + + // Wait for the VR event thread to return + ::WaitForSingleObject(hThreadWait, INFINITE); + + // Print output from CreateVRWindow + printf( + "\n\nTestCreateVRWindow End:\n" + "\twindowId = 0x%X\n" + "\thTex = 0x%p\n" + "\twidth = %d\n" + "\theight = %d\n", + windowId, hTex, width, height); + printf("\n***Note: profile folder created at %s***\n", currentDirProfile); + } +} |