Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit 960eeca

Browse files
authored
Reduce WebVR idle times (#2970)
* Reduce WebVR idle times * Fix discarded frames during the transition to one frame ahead prediction
1 parent 6a45087 commit 960eeca

12 files changed

+93
-35
lines changed

app/src/main/cpp/BrowserWorld.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ PerformanceObserver::PerformanceRestored(const double& aTargetFrameRate, const d
144144
} // namespace
145145

146146
namespace crow {
147-
148147
struct BrowserWorld::State {
149148
BrowserWorldWeakPtr self;
150149
std::vector<WidgetPtr> widgets;
@@ -934,7 +933,7 @@ BrowserWorld::EndFrame() {
934933
m.frameEndHandler();
935934
m.frameEndHandler = nullptr;
936935
} else {
937-
m.device->EndFrame(false);
936+
m.device->EndFrame();
938937
}
939938
m.drawHandler = nullptr;
940939

@@ -1422,14 +1421,33 @@ BrowserWorld::TickImmersive() {
14221421
m.externalVR->SetCompositorEnabled(false);
14231422
m.device->SetRenderMode(device::RenderMode::Immersive);
14241423

1425-
m.device->StartFrame();
1424+
const bool supportsFrameAhead = m.device->SupportsFramePrediction(DeviceDelegate::FramePrediction::ONE_FRAME_AHEAD);
14261425
VRB_GL_CHECK(glDepthMask(GL_FALSE));
1427-
m.externalVR->PushFramePoses(m.device->GetHeadTransform(), m.controllers->GetControllers(), m.context->GetTimestamp());
1426+
if (!supportsFrameAhead || (m.externalVR->GetVRState() != ExternalVR::VRState::Rendering)) {
1427+
// Do not use one frame ahead prediction if not supported or we are rendering the spinner.
1428+
m.device->StartFrame(DeviceDelegate::FramePrediction::NO_FRAME_AHEAD);
1429+
m.externalVR->PushFramePoses(m.device->GetHeadTransform(), m.controllers->GetControllers(),
1430+
m.context->GetTimestamp());
1431+
}
14281432
int32_t surfaceHandle, textureWidth, textureHeight = 0;
14291433
device::EyeRect leftEye, rightEye;
14301434
bool aDiscardFrame = !m.externalVR->WaitFrameResult();
14311435
m.externalVR->GetFrameResult(surfaceHandle, textureWidth, textureHeight, leftEye, rightEye);
14321436
ExternalVR::VRState state = m.externalVR->GetVRState();
1437+
if (supportsFrameAhead) {
1438+
if (m.externalVR->WasFirstPresentingFrame()) {
1439+
// StartFrame() has been already called to render the spinner, do not call it again.
1440+
// Instead, repeat the XR frame and render the spinner while we transition
1441+
// to one frame ahead prediction.
1442+
state = ExternalVR::VRState::Loading;
1443+
} else {
1444+
// Predict poses for one frame ahead and push the data to shmem so Gecko
1445+
// can start the next XR RAF ASAP.
1446+
m.device->StartFrame(DeviceDelegate::FramePrediction::ONE_FRAME_AHEAD);
1447+
}
1448+
m.externalVR->PushFramePoses(m.device->GetHeadTransform(), m.controllers->GetControllers(),
1449+
m.context->GetTimestamp());
1450+
}
14331451
if (state == ExternalVR::VRState::Rendering) {
14341452
if (!aDiscardFrame) {
14351453
if (textureWidth > 0 && textureHeight > 0) {
@@ -1441,7 +1459,7 @@ BrowserWorld::TickImmersive() {
14411459
};
14421460
}
14431461
m.frameEndHandler = [=]() {
1444-
m.device->EndFrame(aDiscardFrame);
1462+
m.device->EndFrame(aDiscardFrame ? DeviceDelegate::FrameEndMode::DISCARD : DeviceDelegate::FrameEndMode::APPLY);
14451463
m.blitter->EndFrame();
14461464
};
14471465
} else {
@@ -1494,7 +1512,7 @@ BrowserWorld::TickSplashAnimation() {
14941512
if (m.fadeAnimation) {
14951513
m.fadeAnimation->FadeIn();
14961514
}
1497-
m.device->EndFrame(false);
1515+
m.device->EndFrame();
14981516
};
14991517
}
15001518
}

app/src/main/cpp/DeviceDelegate.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ class ImmersiveDisplay {
4545

4646
class DeviceDelegate {
4747
public:
48+
enum class FramePrediction {
49+
NO_FRAME_AHEAD,
50+
ONE_FRAME_AHEAD,
51+
};
52+
enum class FrameEndMode {
53+
APPLY,
54+
DISCARD
55+
};
4856
virtual device::DeviceType GetDeviceType() { return device::UnknownType; }
4957
virtual void SetRenderMode(const device::RenderMode aMode) = 0;
5058
virtual device::RenderMode GetRenderMode() = 0;
@@ -63,9 +71,12 @@ class DeviceDelegate {
6371
virtual const std::string GetControllerModelName(const int32_t aModelIndex) const = 0;
6472
virtual void SetCPULevel(const device::CPULevel aLevel) {};
6573
virtual void ProcessEvents() = 0;
66-
virtual void StartFrame() = 0;
74+
virtual bool SupportsFramePrediction(FramePrediction aPrediction) const {
75+
return aPrediction == FramePrediction::NO_FRAME_AHEAD;
76+
}
77+
virtual void StartFrame(const FramePrediction aPrediction = FramePrediction::NO_FRAME_AHEAD) = 0;
6778
virtual void BindEye(const device::Eye aWhich) = 0;
68-
virtual void EndFrame(bool aDiscard = false) = 0;
79+
virtual void EndFrame(const FrameEndMode aMode = FrameEndMode::APPLY) = 0;
6980
virtual bool IsInGazeMode() const { return false; };
7081
virtual int32_t GazeModeIndex() const { return -1; };
7182
virtual VRLayerQuadPtr CreateLayerQuad(int32_t aWidth, int32_t aHeight,

app/src/main/cpp/ExternalVR.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ struct ExternalVR::State {
127127
vrb::Vector eyeOffsets[device::EyeCount];
128128
uint64_t lastFrameId = 0;
129129
bool firstPresentingFrame = false;
130+
bool wasFirstPresentingFrame = false;
130131
bool compositorEnabled = false;
131132
bool waitingForExit = false;
132133

@@ -455,6 +456,7 @@ ExternalVR::WaitFrameResult() {
455456
m.PullBrowserStateWhileLocked();
456457
while (true) {
457458
if (!IsPresenting() || m.browser.layerState[0].layer_stereo_immersive.frameId != m.lastFrameId) {
459+
m.wasFirstPresentingFrame = m.firstPresentingFrame;
458460
m.firstPresentingFrame = false;
459461
m.system.displayState.lastSubmittedFrameSuccessful = true;
460462
m.system.displayState.lastSubmittedFrameId = m.browser.layerState[0].layer_stereo_immersive.frameId;
@@ -480,6 +482,10 @@ ExternalVR::WaitFrameResult() {
480482
return true;
481483
}
482484

485+
bool ExternalVR::WasFirstPresentingFrame() const {
486+
return m.wasFirstPresentingFrame;
487+
}
488+
483489
void
484490
ExternalVR::CompleteEnumeration()
485491
{

app/src/main/cpp/ExternalVR.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ExternalVR : public ImmersiveDisplay {
5555
VRState GetVRState() const;
5656
void PushFramePoses(const vrb::Matrix& aHeadTransform, const std::vector<Controller>& aControllers, const double aTimestamp);
5757
bool WaitFrameResult();
58+
bool WasFirstPresentingFrame() const;
5859
void GetFrameResult(int32_t& aSurfaceHandle,
5960
int32_t& aTextureWidth,
6061
int32_t& aTextureHeight,

app/src/noapi/cpp/DeviceDelegateNoAPI.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ DeviceDelegateNoAPI::ProcessEvents() {
211211
}
212212

213213
void
214-
DeviceDelegateNoAPI::StartFrame() {
214+
DeviceDelegateNoAPI::StartFrame(const FramePrediction aPrediction) {
215215
VRB_GL_CHECK(glClearColor(m.clearColor.Red(), m.clearColor.Green(), m.clearColor.Blue(), m.clearColor.Alpha()));
216216
VRB_GL_CHECK(glEnable(GL_DEPTH_TEST));
217217
VRB_GL_CHECK(glEnable(GL_CULL_FACE));
@@ -240,7 +240,7 @@ DeviceDelegateNoAPI::BindEye(const device::Eye aEye) {
240240
}
241241

242242
void
243-
DeviceDelegateNoAPI::EndFrame(const bool aDiscard) {
243+
DeviceDelegateNoAPI::EndFrame(const FrameEndMode aMode) {
244244
// noop
245245
}
246246

app/src/noapi/cpp/DeviceDelegateNoAPI.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ class DeviceDelegateNoAPI : public DeviceDelegate {
3737
int32_t GetControllerModelCount() const override;
3838
const std::string GetControllerModelName(const int32_t aModelIndex) const override;
3939
void ProcessEvents() override;
40-
void StartFrame() override;
40+
void StartFrame(const FramePrediction aPrediction) override;
4141
void BindEye(const device::Eye) override;
42-
void EndFrame(const bool aDiscard) override;
42+
void EndFrame(const FrameEndMode aMode) override;
4343
// DeviceDelegateNoAPI interface
4444
void InitializeJava(JNIEnv* aEnv, jobject aActivity);
4545
void ShutdownJava();

app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ struct DeviceDelegateOculusVR::State {
8888
vrb::FBOPtr previousFBO;
8989
vrb::CameraEyePtr cameras[2];
9090
uint32_t frameIndex = 0;
91+
FramePrediction framePrediction = FramePrediction::NO_FRAME_AHEAD;
92+
double prevPredictedDisplayTime = 0;
9193
double predictedDisplayTime = 0;
94+
ovrTracking2 prevPredictedTracking = {};
9295
ovrTracking2 predictedTracking = {};
9396
ovrTracking2 discardPredictedTracking = {};
9497
uint32_t discardedFrameIndex = 0;
@@ -822,15 +825,28 @@ DeviceDelegateOculusVR::ProcessEvents() {
822825
}
823826
}
824827

828+
bool
829+
DeviceDelegateOculusVR::SupportsFramePrediction(FramePrediction aPrediction) const {
830+
return true;
831+
}
832+
825833
void
826-
DeviceDelegateOculusVR::StartFrame() {
834+
DeviceDelegateOculusVR::StartFrame(const FramePrediction aPrediction) {
827835
if (!m.ovr) {
828836
VRB_LOG("StartFrame called while not in VR mode");
829837
return;
830838
}
831839

840+
m.framePrediction = aPrediction;
832841
m.frameIndex++;
833-
m.predictedDisplayTime = vrapi_GetPredictedDisplayTime(m.ovr, m.frameIndex);
842+
if (aPrediction == FramePrediction::ONE_FRAME_AHEAD) {
843+
m.prevPredictedDisplayTime = m.predictedDisplayTime;
844+
m.prevPredictedTracking = m.predictedTracking;
845+
m.predictedDisplayTime = vrapi_GetPredictedDisplayTime(m.ovr, m.frameIndex + 1);
846+
} else {
847+
m.predictedDisplayTime = vrapi_GetPredictedDisplayTime(m.ovr, m.frameIndex);
848+
}
849+
834850
m.predictedTracking = vrapi_GetPredictedTracking2(m.ovr, m.predictedDisplayTime);
835851

836852
float ipd = vrapi_GetInterpupillaryDistance(&m.predictedTracking);
@@ -912,7 +928,7 @@ DeviceDelegateOculusVR::BindEye(const device::Eye aWhich) {
912928
}
913929

914930
void
915-
DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
931+
DeviceDelegateOculusVR::EndFrame(const FrameEndMode aEndMode) {
916932
if (!m.ovr) {
917933
VRB_LOG("EndFrame called while not in VR mode");
918934
return;
@@ -922,11 +938,15 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
922938
m.currentFBO.reset();
923939
}
924940

925-
if (aDiscard) {
941+
const bool frameAhead = m.framePrediction == FramePrediction::ONE_FRAME_AHEAD;
942+
const ovrTracking2& tracking = frameAhead ? m.prevPredictedTracking : m.predictedTracking;
943+
const double displayTime = frameAhead ? m.prevPredictedDisplayTime : m.predictedDisplayTime;
944+
945+
if (aEndMode == FrameEndMode::DISCARD) {
926946
// Reuse the last frame when a frame is discarded.
927947
// The last frame is timewarped by the VR compositor.
928948
if (m.discardCount == 0) {
929-
m.discardPredictedTracking = m.predictedTracking;
949+
m.discardPredictedTracking = tracking;
930950
m.discardedFrameIndex = m.frameIndex;
931951
}
932952
m.discardCount++;
@@ -940,13 +960,13 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
940960
const ovrLayerHeader2* layers[ovrMaxLayerCount] = {};
941961

942962
if (m.cubeLayer && m.cubeLayer->IsLoaded() && m.cubeLayer->IsDrawRequested()) {
943-
m.cubeLayer->Update(m.predictedTracking, m.clearColorSwapChain);
963+
m.cubeLayer->Update(tracking, m.clearColorSwapChain);
944964
layers[layerCount++] = m.cubeLayer->Header();
945965
m.cubeLayer->ClearRequestDraw();
946966
}
947967

948968
if (m.equirectLayer && m.equirectLayer->IsDrawRequested()) {
949-
m.equirectLayer->Update(m.predictedTracking, m.clearColorSwapChain);
969+
m.equirectLayer->Update(tracking, m.clearColorSwapChain);
950970
layers[layerCount++] = m.equirectLayer->Header();
951971
m.equirectLayer->ClearRequestDraw();
952972
}
@@ -959,7 +979,7 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
959979
// Draw back layers
960980
for (const OculusLayerPtr& layer: m.uiLayers) {
961981
if (!layer->GetDrawInFront() && layer->IsDrawRequested() && (layerCount < ovrMaxLayerCount - 1)) {
962-
layer->Update(m.predictedTracking, m.clearColorSwapChain);
982+
layer->Update(tracking, m.clearColorSwapChain);
963983
layers[layerCount++] = layer->Header();
964984
layer->ClearRequestDraw();
965985
}
@@ -971,7 +991,7 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
971991
const ovrMatrix4f projectionMatrix = ovrMatrix4f_CreateProjectionFov(fovX, fovY, 0.0f, 0.0f, VRAPI_ZNEAR, 0.0f);
972992

973993
ovrLayerProjection2 projection = vrapi_DefaultLayerProjection2();
974-
projection.HeadPose = m.predictedTracking.HeadPose;
994+
projection.HeadPose = tracking.HeadPose;
975995
projection.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_ONE;
976996
projection.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA;
977997
for (int i = 0; i < VRAPI_FRAME_LAYER_EYE_MAX; ++i) {
@@ -987,7 +1007,7 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
9871007
// Draw front layers
9881008
for (const OculusLayerPtr& layer: m.uiLayers) {
9891009
if (layer->GetDrawInFront() && layer->IsDrawRequested() && layerCount < ovrMaxLayerCount) {
990-
layer->Update(m.predictedTracking, m.clearColorSwapChain);
1010+
layer->Update(tracking, m.clearColorSwapChain);
9911011
layers[layerCount++] = layer->Header();
9921012
layer->ClearRequestDraw();
9931013
}
@@ -1002,7 +1022,7 @@ DeviceDelegateOculusVR::EndFrame(const bool aDiscard) {
10021022
}
10031023
frameDesc.SwapInterval = 1;
10041024
frameDesc.FrameIndex = m.frameIndex;
1005-
frameDesc.DisplayTime = m.predictedDisplayTime;
1025+
frameDesc.DisplayTime = displayTime;
10061026

10071027
frameDesc.LayerCount = layerCount;
10081028
frameDesc.Layers = layers;

app/src/oculusvr/cpp/DeviceDelegateOculusVR.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ class DeviceDelegateOculusVR : public DeviceDelegate {
4141
const std::string GetControllerModelName(const int32_t aModelIndex) const override;
4242
void SetCPULevel(const device::CPULevel aLevel) override;
4343
void ProcessEvents() override;
44-
void StartFrame() override;
44+
bool SupportsFramePrediction(FramePrediction aPrediction) const override;
45+
void StartFrame(const FramePrediction aPrediction) override;
4546
void BindEye(const device::Eye aWhich) override;
46-
void EndFrame(const bool aDiscard) override;
47+
void EndFrame(const FrameEndMode aMode) override;
4748
VRLayerQuadPtr CreateLayerQuad(int32_t aWidth, int32_t aHeight,
4849
VRLayerSurface::SurfaceType aSurfaceType) override;
4950
VRLayerQuadPtr CreateLayerQuad(const VRLayerSurfacePtr& aMoveLayer) override;

app/src/picovr/cpp/DeviceDelegatePicoVR.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ DeviceDelegatePicoVR::ProcessEvents() {
385385
}
386386

387387
void
388-
DeviceDelegatePicoVR::StartFrame() {
388+
DeviceDelegatePicoVR::StartFrame(const FramePrediction aPrediction) {
389389
vrb::Matrix head = vrb::Matrix::Rotation(m.orientation);
390390
head.TranslateInPlace(m.position);
391391

@@ -431,7 +431,7 @@ DeviceDelegatePicoVR::BindEye(const device::Eye aWhich) {
431431
}
432432

433433
void
434-
DeviceDelegatePicoVR::EndFrame(const bool aDiscard) {
434+
DeviceDelegatePicoVR::EndFrame(const FrameEndMode aMode) {
435435

436436
}
437437

app/src/picovr/cpp/DeviceDelegatePicoVR.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ class DeviceDelegatePicoVR : public DeviceDelegate {
3737
int32_t GetControllerModelCount() const override;
3838
const std::string GetControllerModelName(const int32_t aModelIndex) const override;
3939
void ProcessEvents() override;
40-
void StartFrame() override;
40+
void StartFrame(const FramePrediction aPrediction) override;
4141
void BindEye(const device::Eye aWhich) override;
42-
void EndFrame(const bool aDiscard) override;
42+
void EndFrame(const FrameEndMode aMode) override;
4343
bool IsInGazeMode() const override;
4444
int32_t GazeModeIndex() const override;
4545
// Custom methods

0 commit comments

Comments
 (0)