OpenVR, a possible performance Tip, please test

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

OpenVR, a possible performance Tip, please test

Post by xrgo »

Edit: just tried with a vive and camera tracking goes nuts, its really bad, don't do this lol

Hello! as always I am struggling with performance with VR... so I just found something that increased my fps significantly... at least in one of my apps in particular.
So! I wanted to share it so you can test if it also works for you.

disclaimer: I haven't tested it too much myself (its too late now here) so maybe has some consequences. And maybe that's the way its suppose to be done and I just found out xD.

to the point!

before I was doing this:

Code: Select all

bool yOpenVR::frameStarted(const Ogre::FrameEvent& evt){
    
    vr::VRCompositor()->WaitGetPoses( mTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0);

    for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i){

        if (mTrackedDevicePose[i].bPoseIsValid){

            Ogre::Matrix4 mat4DevicePose = convertSteamVRMatrixToOgreMatrix4(mTrackedDevicePose[i].mDeviceToAbsoluteTracking);

            if( mHMD->GetTrackedDeviceClass(i) == vr::TrackedDeviceClass_HMD ){

                mCurrentOrientation = mat4DevicePose.extractQuaternion();
                mCurrentPosition = mat4DevicePose.getTrans();

                mCameraNode->setPosition( mCurrentPosition );
                mCameraNode->setOrientation( mCurrentOrientation );

            }
            if( mHMD->GetTrackedDeviceClass(i) == vr::TrackedDeviceClass_Controller ){
                //controllers stuffs
            }

        }
    }

    return true;
}

bool yOpenVR::frameRenderingQueued(const Ogre::FrameEvent& evt){

    return true;
}

bool yOpenVR::frameEnded(const Ogre::FrameEvent& evt){

    vr::VRCompositor()->Submit(vr::Eye_Left, &mEyeTexture, &mTextureBounds[0]);
    vr::VRCompositor()->Submit(vr::Eye_Right, &mEyeTexture, &mTextureBounds[1]);

    return true;
}
now I do this:

Code: Select all

at init:

        float fDisplayFrequency = mHMD->GetFloatTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float );
        mFrameDuration = 1.f / fDisplayFrequency;
        
        --------------------------

bool yOpenVR::frameStarted(const Ogre::FrameEvent& evt){
    float fSecondsSinceLastVsync;
    mHMD->GetTimeSinceLastVsync( &fSecondsSinceLastVsync, NULL );

    float fVsyncToPhotons = mHMD->GetFloatTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float );

    float fPredictedSecondsFromNow = mFrameDuration - fSecondsSinceLastVsync + fVsyncToPhotons;
    mHMD->GetDeviceToAbsoluteTrackingPose( vr::TrackingUniverseSeated, fPredictedSecondsFromNow, mTrackedDevicePose, vr::k_unMaxTrackedDeviceCount );


    for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i){

        if (mTrackedDevicePose[i].bPoseIsValid){

            Ogre::Matrix4 mat4DevicePose = convertSteamVRMatrixToOgreMatrix4(mTrackedDevicePose[i].mDeviceToAbsoluteTracking);

            if( mHMD->GetTrackedDeviceClass(i) == vr::TrackedDeviceClass_HMD ){

                mCurrentOrientation = mat4DevicePose.extractQuaternion();
                mCurrentPosition = mat4DevicePose.getTrans();

                mCameraNode->setPosition( mCurrentPosition );
                mCameraNode->setOrientation( mCurrentOrientation );

            }
            if( mHMD->GetTrackedDeviceClass(i) == vr::TrackedDeviceClass_Controller ){
                //controllers stuffs
            }

        }
    }

    return true;
}

bool yOpenVR::frameRenderingQueued(const Ogre::FrameEvent& evt){

    vr::VRCompositor()->WaitGetPoses( 0, vr::k_unMaxTrackedDeviceCount, NULL, 0);

    return true;
}

bool yOpenVR::frameEnded(const Ogre::FrameEvent& evt){

    vr::VRCompositor()->Submit(vr::Eye_Left, &mEyeTexture, &mTextureBounds[0]);
    vr::VRCompositor()->Submit(vr::Eye_Right, &mEyeTexture, &mTextureBounds[1]);

    return true;
}
The thing is that WaitGetPoses was using ~3ms, by doing it in the frame started it was leaving me with just 8ms to render, which is very hard to achieve, but just moving it to the framerenderingqueue makes a lot of judder... by using GetDeviceToAbsoluteTrackingPose and calculating fPredictedSecondsFromNow in the frame started things looks pretty great (at least in one quick case scenario I tested)

Please some of you dealing with VR test this and report, or let me know if you do something different/better
Saludos!!
Last edited by xrgo on Tue Mar 27, 2018 6:59 pm, edited 1 time in total.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: OpenVR, a possible performance Tip, please test

Post by dark_sylinc »

I have no experience with VR but I know a few things.

From what you're saying, and after reading this post:
WaitGetPoses is your synchronization point to keep in step with the headset presenting frames. It returns 3ms before vsync - every 11.1ms (i.e 90hz). If you feed too much gpu work for a given frame and blow out your 11.1ms budget, then WaitGetPoses will wait until the next sync point 11.1ms later.
it sounds like WaitGetPoses is implementing the "running start" as described in Advanced VR Rendering by Alex Vlachos (Valve) starting at around 19:10

So basically yes, moving that call around may yield you incredible gains. Considering the slides at 26:43, this function should be called either before Present(), or right before this from OgreRenderQueue.cpp:

Code: Select all

for( size_t i=0; i<HLMS_MAX; ++i )
{
    Hlms *hlms = mHlmsManager->getHlms( static_cast<HlmsTypes>( i ) );
    if( hlms )
        hlms->preCommandBufferExecution( mCommandBuffer );
}

mCommandBuffer->execute();

for( size_t i=0; i<HLMS_MAX; ++i )
{
    Hlms *hlms = mHlmsManager->getHlms( static_cast<HlmsTypes>( i ) );
    if( hlms )
        hlms->postCommandBufferExecution( mCommandBuffer );
}
Be careful that this snippet will be called once per render_scene pass so you make sure you don't end up calling WaitGetPoses multiple times per eye!

I'm not saying that's the best place to place it (YMMV), but you may have to experiment (also the "ideal" location may vary based on system performance).
You may want to use our Remotery profiling to see at which point there's 3ms left before VSync. That's the ideal location.
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: OpenVR, a possible performance Tip, please test

Post by xrgo »

thank you so much for the detailed answer, I'll definitely look in to that
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: OpenVR, a possible performance Tip, please test

Post by xrgo »

ok, bad news, I just tried with another application that gives me less fps, and it indeed increases! but if also gives a little vibration that its very annoying, even when I am holding still... the vibration disappears when fps are close to 90, and increases at lower fps...
so in other words, the overall experience is worst =/
damn this is hard

have to try dark_sylinc stuff yet
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: OpenVR, a possible performance Tip, please test

Post by dark_sylinc »

xrgo wrote: Sat Mar 24, 2018 4:42 pm ok, bad news, I just tried with another application that gives me less fps, and it indeed increases! but if also gives a little vibration that its very annoying, even when I am holding still... the vibration disappears when fps are close to 90, and increases at lower fps...
so in other words, the overall experience is worst =/
damn this is hard
I suspect it's some latency problem, like Frame B receives the input that should've been in Frame A; or for some reason Frame B and C receive both the same input. Or Frame B gets rendered twice (i.e. B is displayed twice due to missing a VBlank).

Unfortunately measuring this is a PITA. The best tools (best != good) are GPUView, PresentMon (both for Windows) and GPUViz (for Linux).

GL isn't also the best choice for this as it doesn't have fine granularity control over the swap chain mechanism. There may be some GLX and WGL extensions though that may give you that control.
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: OpenVR, a possible performance Tip, please test

Post by xrgo »

now I noticed some tiny weird visual glitches, I am using an Oculus CV1... so my theory is that this method informs times badly to the oculus runtime and its trying to do its warping techniques (ASW) in a wrong way and causes those vibrations and glitches... have to test with a vive on monday.

I am going to check those tools, but I haven't had much luck with gpu tools before (nsight crash at launch and renderdoc is all black), thanks!
al2950
OGRE Expert User
OGRE Expert User
Posts: 1227
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 157

Re: OpenVR, a possible performance Tip, please test

Post by al2950 »

good news! I am currently trying to bleed some more FPS out of my system, and I am having similar issues. I however do have a more complicated situation where I am not only rendering the VR chain, but x number of normal render windows, not to mention other RTT like environment maps,etc.

So as dark_sylinc said waitGetPoses not only gets the latest pose but blocks for 3 (ish!) ms, ive read somewhere from a Valve dev that is a variable number based on frame stats before the next sync point. (Although I might be wrong about that). The actual advice for waitGetPoses is to call it directly after present, but that is for engines that a have separate render thread.

The other thing you may want to look at is PostPresentHandoff

Anyway I am just testing out a few ideas, and I will let you know how I get on. Basically I plan to do something like this
VRCompositorStart
- WaitGetPoses
- Update Head and hand scene nodes
VRCompositorEnd
- Submit
- PostPresentHandoff (This may have to be delayed until all my rendering has finished...)

Give me a week and I will post results, good or bad.

**EDIT** Can we add a WorkspacePostUpdate method to the CompositorWorkspaceListener!??
al2950
OGRE Expert User
OGRE Expert User
Posts: 1227
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 157

Re: OpenVR, a possible performance Tip, please test

Post by al2950 »

Ok this is complicated and I am not sure one can get it to go faster. However I have found a great presentation that anyone using OpenVR or VR fullstop should watch.
http://www.gdcvault.com/play/1021771/Advanced-VR

For waitGetPose stuff look at the 'Pipelined Architectures' through to the 'Running Start' sections
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: OpenVR, a possible performance Tip, please test

Post by xrgo »

just tried with a vive and camera tracking goes nuts, its really bad xD, it doesn't work at all
al2950 wrote: Tue Mar 27, 2018 6:18 pm Ok this is complicated and I am not sure one can get it to go faster. However I have found a great presentation that anyone using OpenVR or VR fullstop should watch.
http://www.gdcvault.com/play/1021771/Advanced-VR

For waitGetPose stuff look at the 'Pipelined Architectures' through to the 'Running Start' sections
yes, that's the one dark_sylinc mentioned before, its a bit too advance for me...
dark_sylinc wrote: Sat Mar 24, 2018 4:51 am or right before this from OgreRenderQueue.cpp:
I tried that and with the oculus and it felt the same as with my first solution, I have to try with the vive now but I'll have time to experiment on friday
Post Reply