Hi there, I know time has passed but during a personal project I experienced the same problem. I was doing a portal in VR ( same issue) and I kind of solve it ( not perfect). I woul like to show mye insights to try help finding a final solution.
In some months I will actually write a blogspot about this, but here there is a little resume:
class XRBASE_API FDefaultXRCamera : public IXRCamera, public FHMDSceneViewExtension
{
. . .
virtual void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
}
This last function is the latest one, as far as I am concerned, that calls for the late update ( Apparently late update is implemented in more than 1 place). I think Unreal calls in multi-threading all the FSceneExtension in groups. This means that if I write my logic in a FSceneExtension function that runs AFTER this one I will be able to access the correct pose of the camera.
I did not have time to explore more, but found 2 promissing functions:
/**
* Called on render thread at the start of rendering, for each view, after PreRenderViewFamily_RenderThread call.
*/
virtual void PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) {}
Technically runs after the late update, but the extension for the hmd also implements this function, so this could be not exactly perfect depending on order execution ( really small time difference). The good thing is that it provides the view.
/**
* Called on render thread right before Base Pass rendering. bDepthBufferIsPopulated is true if anything has been rendered to the depth buffer. This does not need to be a full depth prepass.
*/
virtual void PreRenderBasePass_RenderThread(FRDGBuilder& GraphBuilder, bool bDepthBufferIsPopulated) {}
This one looks like it really happens after the late update, but did not have the views and I did not explore.
In the end I used the `PreRenderView_RenderThread() as I had the view. My solution inside this function was to ask directly the current relative position of the hmd, offset of each eye and make the calculation myself:
void FPortalViewExtension::PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView)
{
. . .
GEngine->XRSystem->GetCurrentPose(HMDDeviceId, CamRotationLocal, CamLocationLocal);
. . .
GEngine->XRSystem->GetRelativeEyePose(HMDDeviceId,EyeIndex, offsetRotator, offsetLocation);
FVector EyeLocation = CamRotation.RotateVector(offsetLocation) + CamLocation;
FVector eyeSyncronizedLocation;
PortalTools::XR::TeleportWorldLocationMirroredXR(Portal->OtherPortal, Portal, EyeLocation, eyeSyncronizedLocation);
FQuat eyeSyncronizedRotation;
PortalTools::XR::TeleportWorldRotationMirroredXR(Portal->OtherPortal, Portal, CamRotation, eyeSyncronizedRotation);
. . .
This means that my approach is almost right. The differences between the position that the late update and my calculation uses is negligible, specially if the framrate stays good.
I hope it helps, let me know if you see a better approach or gives you new ideas, I leave also videos of how it was before and after. The video shows the game running and I tap into the headset to show better the results.
Stutter / shaking problem:
After solution (Arrow shows tap effect on debug text, but portal is fine)