| 944 | }; |
| 945 | |
| 946 | OVR_PUBLIC_FUNCTION(ovrResult) ovr_EndFrame(ovrSession session, long long frameIndex, const ovrViewScaleDesc* viewScaleDesc, |
| 947 | ovrLayerHeader const * const * layerPtrList, unsigned int layerCount) |
| 948 | { |
| 949 | REV_TRACE(ovr_EndFrame); |
| 950 | MICROPROFILE_META_CPU("End Frame", (int)frameIndex); |
| 951 | |
| 952 | if (!session) |
| 953 | return ovrError_InvalidSession; |
| 954 | |
| 955 | SessionStatusBits status = session->SessionStatus; |
| 956 | if (!status.IsVisible) |
| 957 | return ovrSuccess_NotVisible; |
| 958 | |
| 959 | // We are going to use some space handles here, don't destroy them |
| 960 | std::shared_lock<std::shared_mutex> lk(session->TrackingMutex); |
| 961 | |
| 962 | // The oculus runtime is very tolerant of invalid viewports, so this lambda ensures we submit valid ones. |
| 963 | // This fixes UE4 games which can at times submit uninitialized viewports due to a bug in OVR_Math.h. |
| 964 | const auto ClampRect = [](ovrRecti rect, ovrTextureSwapChain chain) |
| 965 | { |
| 966 | OVR::Sizei chainSize(chain->Desc.Width, chain->Desc.Height); |
| 967 | |
| 968 | // Clamp the rectangle size within the chain size |
| 969 | rect.Size = OVR::Sizei::Min(OVR::Sizei::Max(rect.Size, OVR::Sizei(1, 1)), chainSize); |
| 970 | |
| 971 | // Set any invalid coordinates to zero |
| 972 | if (rect.Pos.x < 0 || rect.Pos.x + rect.Size.w > chainSize.w) |
| 973 | rect.Pos.x = 0; |
| 974 | if (rect.Pos.y < 0 || rect.Pos.y + rect.Size.h > chainSize.h) |
| 975 | rect.Pos.y = 0; |
| 976 | |
| 977 | return XR::Recti(rect); |
| 978 | }; |
| 979 | |
| 980 | std::vector<XrCompositionLayerBaseHeader*> layers; |
| 981 | std::list<XrCompositionLayerUnion> layerData; |
| 982 | std::list<XrCompositionLayerProjectionViewStereo> viewData; |
| 983 | std::list<XrCompositionLayerDepthInfoKHR> depthData; |
| 984 | for (unsigned int i = 0; i < layerCount; i++) |
| 985 | { |
| 986 | ovrLayer_Union* layer = (ovrLayer_Union*)layerPtrList[i]; |
| 987 | |
| 988 | if (!layer) |
| 989 | continue; |
| 990 | |
| 991 | ovrLayerType type = layer->Header.Type; |
| 992 | const bool upsideDown = layer->Header.Flags & ovrLayerFlag_TextureOriginAtBottomLeft; |
| 993 | const bool headLocked = layer->Header.Flags & ovrLayerFlag_HeadLocked; |
| 994 | |
| 995 | // Version 1.25 introduced a 128-byte reserved parameter, so on older versions the actual data |
| 996 | // falls within this reserved parameter and we need to move the pointer back into the actual data area. |
| 997 | // NOTE: Do not read the header after this operation as it will fall outside of the layer memory. |
| 998 | if (Runtime::Get().MinorVersion < 25) |
| 999 | layer = (ovrLayer_Union*)((char*)layer - sizeof(ovrLayerHeader::Reserved)); |
| 1000 | |
| 1001 | if (type == ovrLayerType_Disabled) |
| 1002 | continue; |
| 1003 | |