og-revive/Revive/CompositorBase.cpp

412 lines
14 KiB
C++
Raw Permalink Normal View History

#include "CompositorBase.h"
#include "OVR_CAPI.h"
#include "REV_Math.h"
#include "Settings.h"
#include "Session.h"
#include "SessionDetails.h"
2017-04-15 17:51:35 +02:00
#include "microprofile.h"
2017-04-15 17:51:35 +02:00
#include <openvr.h>
#include <vector>
#include <algorithm>
#define REV_LAYER_BIAS 0.0001f
MICROPROFILE_DEFINE(WaitToBeginFrame, "Compositor", "WaitFrame", 0x00ff00);
MICROPROFILE_DEFINE(BeginFrame, "Compositor", "BeginFrame", 0x00ff00);
MICROPROFILE_DEFINE(EndFrame, "Compositor", "EndFrame", 0x00ff00);
2017-04-15 17:51:35 +02:00
MICROPROFILE_DEFINE(SubmitFovLayer, "Compositor", "SubmitFovLayer", 0x00ff00);
MICROPROFILE_DEFINE(SubmitSceneLayer, "Compositor", "SubmitSceneLayer", 0x00ff00);
ovrResult rev_CompositorErrorToOvrError(vr::EVRCompositorError error)
{
switch (error)
{
case vr::VRCompositorError_None: return ovrSuccess;
case vr::VRCompositorError_IncompatibleVersion: return ovrError_ServiceError;
case vr::VRCompositorError_DoNotHaveFocus: return ovrSuccess_NotVisible;
case vr::VRCompositorError_InvalidTexture: return ovrError_TextureSwapChainInvalid;
case vr::VRCompositorError_IsNotSceneApplication: return ovrError_InvalidSession;
case vr::VRCompositorError_TextureIsOnWrongDevice: return ovrError_TextureSwapChainInvalid;
case vr::VRCompositorError_TextureUsesUnsupportedFormat: return ovrError_TextureSwapChainInvalid;
case vr::VRCompositorError_SharedTexturesNotSupported: return ovrError_TextureSwapChainInvalid;
case vr::VRCompositorError_IndexOutOfRange: return ovrError_InvalidParameter;
default: return ovrError_RuntimeException;
}
}
CompositorBase::CompositorBase()
: m_MirrorTexture(nullptr)
2017-04-15 17:51:35 +02:00
, m_ChainCount(0)
{
}
CompositorBase::~CompositorBase()
{
if (m_MirrorTexture)
delete m_MirrorTexture;
}
ovrResult CompositorBase::CreateTextureSwapChain(const ovrTextureSwapChainDesc* desc, ovrTextureSwapChain* out_TextureSwapChain)
{
ovrTextureSwapChain swapChain = new ovrTextureSwapChainData(*desc);
swapChain->Identifier = m_ChainCount++;
// FIXME: A bug in OpenVR causes Asynchronous Reprojection to fail with swapchains
if (GetAPI() == vr::TextureType_OpenGL)
swapChain->Length = 1;
for (int i = 0; i < swapChain->Length; i++)
{
TextureBase* texture = CreateTexture();
bool success = texture->Init(desc->Type, desc->Width, desc->Height, desc->MipLevels,
desc->ArraySize, desc->Format, desc->MiscFlags, desc->BindFlags);
if (!success)
return ovrError_RuntimeException;
swapChain->Textures[i].reset(texture);
}
*out_TextureSwapChain = swapChain;
return ovrSuccess;
}
ovrResult CompositorBase::CreateMirrorTexture(const ovrMirrorTextureDesc* desc, ovrMirrorTexture* out_MirrorTexture)
{
// There can only be one mirror texture at a time
if (m_MirrorTexture)
return ovrError_RuntimeException;
2017-12-02 14:11:40 -08:00
// TODO: Support ovrMirrorOptions
ovrMirrorTexture mirrorTexture = new ovrMirrorTextureData(*desc);
TextureBase* texture = CreateTexture();
bool success = texture->Init(ovrTexture_2D, desc->Width, desc->Height, 1, 1, desc->Format,
desc->MiscFlags | ovrTextureMisc_AllowGenerateMips, ovrTextureBind_DX_RenderTarget);
if (!success)
return ovrError_RuntimeException;
mirrorTexture->Texture.reset(texture);
m_MirrorTexture = mirrorTexture;
*out_MirrorTexture = mirrorTexture;
return ovrSuccess;
}
ovrResult CompositorBase::WaitToBeginFrame(ovrSession session, long long frameIndex)
{
MICROPROFILE_SCOPE(WaitToBeginFrame);
vr::EVRCompositorError err = vr::VRCompositorError_None;
for (long long index = session->FrameIndex; index < frameIndex; index++)
{
// Call WaitGetPoses to block until the running start, also known as queue-ahead in the Oculus SDK.
err = vr::VRCompositor()->WaitGetPoses(nullptr, 0, nullptr, 0);
}
return rev_CompositorErrorToOvrError(err);
}
ovrResult CompositorBase::BeginFrame(ovrSession session, long long frameIndex)
{
MICROPROFILE_SCOPE(BeginFrame);
session->FrameIndex = frameIndex;
return ovrSuccess;
}
ovrResult CompositorBase::EndFrame(ovrSession session, ovrLayerHeader const * const * layerPtrList, unsigned int layerCount)
{
MICROPROFILE_SCOPE(EndFrame);
if (layerCount == 0 || !layerPtrList)
return ovrError_InvalidParameter;
2017-04-15 17:51:35 +02:00
// Flush all pending draw calls.
Flush();
ovrLayerEyeFov baseLayer;
bool baseLayerFound = false;
std::vector<vr::VROverlayHandle_t> activeOverlays;
for (uint32_t i = 0; i < layerCount; i++)
{
if (layerPtrList[i] == nullptr)
continue;
2017-12-02 14:11:40 -08:00
// TODO: Support ovrLayerType_Cylinder and ovrLayerType_Cube
if (layerPtrList[i]->Type == ovrLayerType_Quad)
{
ovrLayerQuad* layer = (ovrLayerQuad*)layerPtrList[i];
ovrTextureSwapChain chain = layer->ColorTexture;
// Every overlay is associated with a swapchain.
// This is necessary because the position of the layer may change in the array,
// which would otherwise cause flickering between overlays.
// TODO: Support multiple overlays using the same texture.
vr::VROverlayHandle_t overlay = layer->ColorTexture->Overlay;
if (overlay == vr::k_ulOverlayHandleInvalid)
{
overlay = CreateOverlay();
layer->ColorTexture->Overlay = overlay;
vr::VRTextureWithPose_t texture = chain->Textures[chain->SubmitIndex]->ToVRTexture();
vr::VROverlay()->SetOverlayTexture(chain->Overlay, &texture);
}
activeOverlays.push_back(overlay);
// Set the layer rendering order.
vr::VROverlay()->SetOverlaySortOrder(overlay, i);
// Transform the overlay.
vr::HmdMatrix34_t transform = REV::Matrix4f(layer->QuadPoseCenter);
vr::VROverlay()->SetOverlayWidthInMeters(overlay, layer->QuadSize.x);
if (layer->Header.Flags & ovrLayerFlag_HeadLocked)
vr::VROverlay()->SetOverlayTransformTrackedDeviceRelative(overlay, vr::k_unTrackedDeviceIndex_Hmd, &transform);
else
vr::VROverlay()->SetOverlayTransformAbsolute(overlay, vr::VRCompositor()->GetTrackingSpace(), &transform);
// Set the texture and show the overlay.
vr::VRTextureBounds_t bounds = ViewportToTextureBounds(layer->Viewport, layer->ColorTexture, layer->Header.Flags);
vr::Texture_t texture = chain->Textures[chain->SubmitIndex]->ToVRTexture();
vr::VROverlay()->SetOverlayTextureBounds(overlay, &bounds);
// Show the overlay, unfortunately we have no control over the order in which
// overlays are drawn.
// TODO: Support ovrLayerFlag_HighQuality for overlays with anisotropic sampling.
// TODO: Handle overlay errors.
vr::VROverlay()->ShowOverlay(overlay);
chain->Submit();
}
2017-12-02 14:11:40 -08:00
else if (layerPtrList[i]->Type == ovrLayerType_EyeFov ||
layerPtrList[i]->Type == ovrLayerType_EyeFovDepth ||
layerPtrList[i]->Type == ovrLayerType_EyeFovMultires)
{
ovrLayerEyeFov* layer = (ovrLayerEyeFov*)layerPtrList[i];
// We can only submit one eye layer, so once we have a base layer we blit the others.
if (!baseLayerFound)
baseLayer = *layer;
else
BlitFovLayers(&baseLayer, layer);
baseLayerFound = true;
}
else if (layerPtrList[i]->Type == ovrLayerType_EyeMatrix)
{
ovrLayerEyeFov layer = ToFovLayer((ovrLayerEyeMatrix*)layerPtrList[i]);
// We can only submit one eye layer, so once we have a base layer we blit the others.
if (!baseLayerFound)
baseLayer = layer;
else
BlitFovLayers(&baseLayer, &layer);
baseLayerFound = true;
}
}
// Hide previous overlays that are not part of the current layers.
for (vr::VROverlayHandle_t overlay : m_ActiveOverlays)
{
// Find the overlay in the current active overlays, if it was not found then hide it.
// TODO: Handle overlay errors.
if (std::find(activeOverlays.begin(), activeOverlays.end(), overlay) == activeOverlays.end())
vr::VROverlay()->HideOverlay(overlay);
}
m_ActiveOverlays = activeOverlays;
vr::EVRCompositorError error = vr::VRCompositorError_None;
if (baseLayerFound)
error = SubmitFovLayer(session, &baseLayer);
if (m_MirrorTexture && error == vr::VRCompositorError_None)
RenderMirrorTexture(m_MirrorTexture);
// Flip the profiler.
MicroProfileFlip();
return rev_CompositorErrorToOvrError(error);
}
vr::VROverlayHandle_t CompositorBase::CreateOverlay()
{
// Each overlay needs a unique key, so just count how many overlays we've created until now.
char keyName[vr::k_unVROverlayMaxKeyLength];
snprintf(keyName, vr::k_unVROverlayMaxKeyLength, "revive.runtime.layer%d", m_OverlayCount++);
vr::VROverlayHandle_t handle = vr::k_ulOverlayHandleInvalid;
vr::VROverlay()->CreateOverlay((const char*)keyName, "Revive Layer", &handle);
return handle;
}
vr::VRTextureBounds_t CompositorBase::ViewportToTextureBounds(ovrRecti viewport, ovrTextureSwapChain swapChain, unsigned int flags)
{
vr::VRTextureBounds_t bounds;
float w = (float)swapChain->Desc.Width;
float h = (float)swapChain->Desc.Height;
bounds.uMin = viewport.Pos.x / w;
bounds.vMin = viewport.Pos.y / h;
// Sanity check for the viewport size.
// Workaround for Defense Grid 2, which leaves these variables uninitialized.
if (viewport.Size.w > 0 && viewport.Size.h > 0)
{
bounds.uMax = (viewport.Pos.x + viewport.Size.w) / w;
bounds.vMax = (viewport.Pos.y + viewport.Size.h) / h;
}
else
{
bounds.uMax = 1.0f;
bounds.vMax = 1.0f;
}
if (flags & ovrLayerFlag_TextureOriginAtBottomLeft)
{
bounds.vMin = 1.0f - bounds.vMin;
bounds.vMax = 1.0f - bounds.vMax;
}
2017-04-11 19:07:19 +02:00
if (GetAPI() == vr::TextureType_OpenGL)
{
bounds.vMin = 1.0f - bounds.vMin;
bounds.vMax = 1.0f - bounds.vMax;
}
return bounds;
}
ovrLayerEyeFov CompositorBase::ToFovLayer(ovrLayerEyeMatrix* matrix)
{
ovrLayerEyeFov layer = { ovrLayerType_EyeFov };
layer.Header.Flags = matrix->Header.Flags;
layer.SensorSampleTime = matrix->SensorSampleTime;
for (int i = 0; i < ovrEye_Count; i++)
{
layer.Fov[i].LeftTan = layer.Fov[i].RightTan = .5f / matrix->Matrix[i].M[0][0];
layer.Fov[i].UpTan = layer.Fov[i].DownTan = -.5f / matrix->Matrix[i].M[1][1];
layer.ColorTexture[i] = matrix->ColorTexture[i];
layer.Viewport[i] = matrix->Viewport[i];
layer.RenderPose[i] = matrix->RenderPose[i];
}
return layer;
}
void CompositorBase::BlitFovLayers(ovrLayerEyeFov* dstLayer, ovrLayerEyeFov* srcLayer)
{
2017-04-15 17:51:35 +02:00
MICROPROFILE_SCOPE(SubmitFovLayer);
ovrTextureSwapChain swapChain[ovrEye_Count] = {
srcLayer->ColorTexture[ovrEye_Left],
srcLayer->ColorTexture[ovrEye_Right]
};
// If the right eye isn't set use the left eye for both
if (!swapChain[ovrEye_Right])
swapChain[ovrEye_Right] = swapChain[ovrEye_Left];
2017-04-15 17:51:35 +02:00
MICROPROFILE_META_CPU("SwapChain Right", swapChain[ovrEye_Right]->Identifier);
MICROPROFILE_META_CPU("SwapChain Left", swapChain[ovrEye_Left]->Identifier);
// Render the scene layer
for (int i = 0; i < ovrEye_Count; i++)
{
// Get the scene fov
ovrFovPort sceneFov = dstLayer->Fov[i];
// Calculate the fov quad
vr::HmdVector4_t quad;
quad.v[0] = srcLayer->Fov[i].LeftTan / -sceneFov.LeftTan;
quad.v[1] = srcLayer->Fov[i].RightTan / sceneFov.RightTan;
quad.v[2] = srcLayer->Fov[i].UpTan / sceneFov.UpTan;
quad.v[3] = srcLayer->Fov[i].DownTan / -sceneFov.DownTan;
// Calculate the texture bounds
vr::VRTextureBounds_t bounds = ViewportToTextureBounds(srcLayer->Viewport[i], swapChain[i], srcLayer->Header.Flags);
// Composit the layer
RenderTextureSwapChain((vr::EVREye)i, swapChain[i], dstLayer->ColorTexture[i], dstLayer->Viewport[i], bounds, quad);
}
swapChain[ovrEye_Left]->Submit();
if (swapChain[ovrEye_Left] != swapChain[ovrEye_Right])
swapChain[ovrEye_Right]->Submit();
}
vr::VRCompositorError CompositorBase::SubmitFovLayer(ovrSession session, ovrLayerEyeFov* fovLayer)
{
2017-04-15 17:51:35 +02:00
MICROPROFILE_SCOPE(SubmitSceneLayer);
ovrTextureSwapChain swapChain[ovrEye_Count] = {
fovLayer->ColorTexture[ovrEye_Left],
fovLayer->ColorTexture[ovrEye_Right]
};
// If the right eye isn't set use the left eye for both
if (!swapChain[ovrEye_Right])
swapChain[ovrEye_Right] = swapChain[ovrEye_Left];
2017-04-15 17:51:35 +02:00
MICROPROFILE_META_CPU("SwapChain Right", swapChain[ovrEye_Right]->Identifier);
MICROPROFILE_META_CPU("Right Submit", swapChain[ovrEye_Right]->SubmitIndex);
2017-04-15 17:51:35 +02:00
MICROPROFILE_META_CPU("SwapChain Left", swapChain[ovrEye_Left]->Identifier);
MICROPROFILE_META_CPU("Left Submit", swapChain[ovrEye_Left]->SubmitIndex);
2017-04-15 17:51:35 +02:00
// Submit the scene layer.
vr::VRCompositorError err;
for (int i = 0; i < ovrEye_Count; i++)
{
ovrTextureSwapChain chain = swapChain[i];
vr::VRTextureBounds_t bounds = ViewportToTextureBounds(fovLayer->Viewport[i], swapChain[i], fovLayer->Header.Flags);
// Get the descriptor for this eye
ovrEyeRenderDesc* desc = session->Details->RenderDesc[i];
// Shrink the bounds to account for the overlapping fov
vr::VRTextureBounds_t fovBounds = FovPortToTextureBounds(desc->Fov, fovLayer->Fov[i]);
// Combine the fov bounds with the viewport bounds
bounds.uMin += fovBounds.uMin * bounds.uMax;
bounds.uMax *= fovBounds.uMax;
bounds.vMin += fovBounds.vMin * bounds.vMax;
bounds.vMax *= fovBounds.vMax;
vr::VRTextureWithPose_t texture = chain->Textures[chain->SubmitIndex]->ToVRTexture();
// Add the pose data to the eye texture
REV::Matrix4f pose(fovLayer->RenderPose[i]);
if (session->TrackingOrigin == vr::TrackingUniverseSeated)
{
REV::Matrix4f offset(vr::VRSystem()->GetSeatedZeroPoseToStandingAbsoluteTrackingPose());
texture.mDeviceToAbsoluteTracking = REV::Matrix4f(offset * pose);
}
else
{
texture.mDeviceToAbsoluteTracking = pose;
}
err = vr::VRCompositor()->Submit((vr::EVREye)i, (vr::Texture_t*)&texture, &bounds, vr::Submit_TextureWithPose);
if (err != vr::VRCompositorError_None)
break;
}
2016-09-23 17:39:43 +02:00
swapChain[ovrEye_Left]->Submit();
if (swapChain[ovrEye_Left] != swapChain[ovrEye_Right])
swapChain[ovrEye_Right]->Submit();
return err;
}
void CompositorBase::SetMirrorTexture(ovrMirrorTexture mirrorTexture)
{
m_MirrorTexture = mirrorTexture;
}
vr::VRTextureBounds_t CompositorBase::FovPortToTextureBounds(ovrFovPort eyeFov, ovrFovPort fov)
{
vr::VRTextureBounds_t result;
// Adjust the bounds based on the field-of-view in the game
result.uMin = 0.5f - 0.5f * eyeFov.LeftTan / fov.LeftTan;
result.uMax = 0.5f + 0.5f * eyeFov.RightTan / fov.RightTan;
result.vMin = 0.5f - 0.5f * eyeFov.UpTan / fov.UpTan;
result.vMax = 0.5f + 0.5f * eyeFov.DownTan / fov.DownTan;
return result;
}