Przeglądaj źródła

OculusVR: updated to SDK 1.3

Krzysztof Kondrak 9 lat temu
rodzic
commit
bea6e81d12

+ 1 - 1
README.md

@@ -27,7 +27,7 @@ Supported rendering backends:
 
 Supported HMD:
 
- * OculusVR (0.4.2+)
+ * OculusVR (1.3.0)
 
 Supported platforms:
 

+ 1 - 5
examples/01-cubes/cubes.cpp

@@ -143,11 +143,7 @@ class ExampleCubes : public entry::AppI
 			{
 				float view[16];
 				bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
-
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);
-
-				bgfx::setViewTransform(0, view, proj);
+				bgfx::setViewTransform(0, view, hmd->eye[0].projection);
 
 				// Set view 0 default viewport.
 				//

+ 1 - 5
examples/02-metaballs/metaballs.cpp

@@ -577,11 +577,7 @@ class ExampleMetaballs : public entry::AppI
 			{
 				float view[16];
 				bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
-
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);
-
-				bgfx::setViewTransform(0, view, proj);
+				bgfx::setViewTransform(0, view, hmd->eye[0].projection);
 
 				// Set view 0 default viewport.
 				//

+ 1 - 5
examples/04-mesh/mesh.cpp

@@ -91,11 +91,7 @@ class ExampleMesh : public entry::AppI
 			{
 				float view[16];
 				bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
-
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);
-
-				bgfx::setViewTransform(0, view, proj);
+				bgfx::setViewTransform(0, view, hmd->eye[0].projection);
 
 				// Set view 0 default viewport.
 				//

+ 1 - 5
examples/05-instancing/instancing.cpp

@@ -160,11 +160,7 @@ class ExampleInstancing : public entry::AppI
 				{
 					float view[16];
 					bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
-
-					float proj[16];
-					bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);
-
-					bgfx::setViewTransform(0, view, proj);
+					bgfx::setViewTransform(0, view, hmd->eye[0].projection);
 
 					// Set view 0 default viewport.
 					//

+ 1 - 5
examples/06-bump/bump.cpp

@@ -225,11 +225,7 @@ class ExampleBump : public entry::AppI
 			{
 				float view[16];
 				bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
-
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);
-
-				bgfx::setViewTransform(0, view, proj);
+				bgfx::setViewTransform(0, view, hmd->eye[0].projection);
 
 				// Set view 0 default viewport.
 				//

+ 1 - 5
examples/12-lod/lod.cpp

@@ -174,11 +174,7 @@ class ExampleLod : public entry::AppI
 			{
 				float view[16];
 				bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye);
-
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 100.0f);
-
-				bgfx::setViewTransform(0, view, proj);
+				bgfx::setViewTransform(0, view, hmd->eye[0].projection);
 
 				// Set view 0 default viewport.
 				//

+ 1 - 5
examples/24-nbody/nbody.cpp

@@ -305,11 +305,7 @@ int _main_(int _argc, char** _argv)
 
 				float tmp[16];
 				bx::mtxMul(tmp, view, viewHead);
-
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 10000.0f);
-
-				bgfx::setViewTransform(0, tmp, proj);
+				bgfx::setViewTransform(0, tmp, hmd->eye[0].projection);
 
 				// Set view 0 default viewport.
 				//

+ 2 - 5
examples/26-occlusion/occlusion.cpp

@@ -181,13 +181,10 @@ class ExampleOcclusion : public entry::AppI
 				float tmp[16];
 				bx::mtxMul(tmp, view, viewHead);
 
-				float proj[16];
-				bx::mtxProj(proj, hmd->eye[0].fov, 0.1f, 10000.0f);
-
-				bgfx::setViewTransform(0, tmp, proj);
+				bgfx::setViewTransform(0, tmp, hmd->eye[0].projection);
 				bgfx::setViewRect(0, 0, 0, hmd->width, hmd->height);
 
-				bgfx::setViewTransform(1, tmp, proj);
+				bgfx::setViewTransform(1, tmp, hmd->eye[1].projection);
 				bgfx::setViewRect(1, 0, 0, hmd->width, hmd->height);
 			}
 			else

+ 2 - 1
include/bgfx/bgfx.h

@@ -615,6 +615,7 @@ namespace bgfx
 			float translation[3];       //!< Eye translation.
 			float fov[4];               //!< Field of view (up, down, left, right).
 			float viewOffset[3];        //!< Eye view matrix translation adjustment.
+			float projection[16];       //!< Eye projection matrix
 			float pixelsPerTanAngle[2]; //!<
 		};
 
@@ -1914,7 +1915,7 @@ namespace bgfx
 	///
 	void setViewRect(uint8_t _id, uint16_t _x, uint16_t _y, uint16_t _width, uint16_t _height);
 
-    /// @attention C99 equivalent is `bgfx_set_view_rect_auto`.
+	/// @attention C99 equivalent is `bgfx_set_view_rect_auto`.
 	///
 	void setViewRect(uint8_t _id, uint16_t _x, uint16_t _y, BackbufferRatio::Enum _ratio);
 

+ 10 - 32
scripts/genie.lua

@@ -178,42 +178,20 @@ function exampleProject(_name)
 			"ws2_32",
 		}
 
-		-- Check for LibOVR 5.0+
-		if os.isdir(path.join(os.getenv("OVR_DIR"), "LibOVR/Lib/Windows/Win32/Debug/VS2012")) then
+		configuration { "x32", "Debug" }
+			libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/Win32/Debug", _ACTION) }
 
-			configuration { "x32", "Debug" }
-				libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/Win32/Debug", _ACTION) }
+		configuration { "x32", "Release" }
+			libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/Win32/Release", _ACTION) }
 
-			configuration { "x32", "Release" }
-				libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/Win32/Release", _ACTION) }
+		configuration { "x64", "Debug" }
+			libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/x64/Debug", _ACTION) }
 
-			configuration { "x64", "Debug" }
-				libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/x64/Debug", _ACTION) }
+		configuration { "x64", "Release" }
+			libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/x64/Release", _ACTION) }
 
-			configuration { "x64", "Release" }
-				libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Windows/x64/Release", _ACTION) }
-
-			configuration { "x32 or x64" }
-				links { "libovr" }
-		else
-			configuration { "x32" }
-				libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/Win32", _ACTION) }
-
-			configuration { "x64" }
-				libdirs { path.join("$(OVR_DIR)/LibOVR/Lib/x64", _ACTION) }
-
-			configuration { "x32", "Debug" }
-				links { "libovrd" }
-
-			configuration { "x32", "Release" }
-				links { "libovr" }
-
-			configuration { "x64", "Debug" }
-				links { "libovr64d" }
-
-			configuration { "x64", "Release" }
-				links { "libovr64" }
-		end
+		configuration { "x32 or x64" }
+			links { "libovr" }
 
 		configuration {}
 	end

+ 112 - 194
src/ovr.cpp

@@ -9,17 +9,15 @@
 
 namespace bgfx
 {
-#if OVR_VERSION <= OVR_VERSION_050
-#	define OVR_EYE_BUFFER 100
-#else
-#	define OVR_EYE_BUFFER 8
-#endif // OVR_VERSION...
-
 	OVR::OVR()
 		: m_hmd(NULL)
 		, m_isenabled(false)
-		, m_debug(false)
+		, m_mirror(NULL)
+		, m_hmdFrameReady(-1)
+		, m_frameIndex(0)
+		, m_sensorSampleTime(0)
 	{
+		memset(m_eyeBuffers, 0, sizeof(m_eyeBuffers));
 	}
 
 	OVR::~OVR()
@@ -29,225 +27,162 @@ namespace bgfx
 
 	void OVR::init()
 	{
-		bool initialized = !!ovr_Initialize();
-		BX_WARN(initialized, "Unable to create OVR device.");
-		if (!initialized)
+		ovrResult initialized = ovr_Initialize(NULL);
+		ovrGraphicsLuid luid;
+
+		BX_WARN(initialized == ovrSuccess, "Unable to create OVR device.");
+		
+		if (initialized != ovrSuccess)
 		{
 			return;
 		}
 
-		m_hmd = ovrHmd_Create(0);
-		if (NULL == m_hmd)
+		initialized = ovr_Create(&m_hmd, &luid);
+		if (initialized != ovrSuccess)
 		{
-			m_hmd = ovrHmd_CreateDebug(ovrHmd_DK2);
-			BX_WARN(NULL != m_hmd, "Unable to create OVR device.");
-			if (NULL == m_hmd)
-			{
-				return;
-			}
+			BX_WARN(initialized == ovrSuccess, "Unable to create OVR device.");
+			return;
 		}
 
+		m_hmdDesc = ovr_GetHmdDesc(m_hmd);
+
 		BX_TRACE("HMD: %s, %s, firmware: %d.%d"
-			, m_hmd->ProductName
-			, m_hmd->Manufacturer
-			, m_hmd->FirmwareMajor
-			, m_hmd->FirmwareMinor
+			, m_hmdDesc.ProductName
+			, m_hmdDesc.Manufacturer
+			, m_hmdDesc.FirmwareMajor
+			, m_hmdDesc.FirmwareMinor
 			);
 
-		ovrSizei sizeL = ovrHmd_GetFovTextureSize(m_hmd, ovrEye_Left,  m_hmd->DefaultEyeFov[0], 1.0f);
-		ovrSizei sizeR = ovrHmd_GetFovTextureSize(m_hmd, ovrEye_Right, m_hmd->DefaultEyeFov[1], 1.0f);
-		m_rtSize.w = sizeL.w + sizeR.w + OVR_EYE_BUFFER;
-		m_rtSize.h = bx::uint32_max(sizeL.h, sizeR.h);
-		m_warning = true;
+		ovrSizei sizeL = ovr_GetFovTextureSize(m_hmd, ovrEye_Left,  m_hmdDesc.DefaultEyeFov[0], 1.0f);
+		ovrSizei sizeR = ovr_GetFovTextureSize(m_hmd, ovrEye_Right, m_hmdDesc.DefaultEyeFov[1], 1.0f);
+		m_hmdSize.w = sizeL.w + sizeR.w;
+		m_hmdSize.h = bx::uint32_max(sizeL.h, sizeR.h);
 	}
 
 	void OVR::shutdown()
 	{
 		BX_CHECK(!m_isenabled, "HMD not disabled.");
-		ovrHmd_Destroy(m_hmd);
+
+		for (int i = 0; i < 2; i++)
+		{
+			if (m_eyeBuffers[i])
+			{
+				m_eyeBuffers[i]->destroy(m_hmd);
+				BX_DELETE(g_allocator, m_eyeBuffers[i]);
+			}
+		}
+
+		if (m_mirror)
+		{
+			m_mirror->destroy(m_hmd);
+			BX_DELETE(g_allocator, m_mirror);
+		}
+
+		ovr_Destroy(m_hmd);
 		m_hmd = NULL;
 		ovr_Shutdown();
 	}
 
 	void OVR::getViewport(uint8_t _eye, Rect* _viewport)
 	{
-		_viewport->m_x      = _eye * (m_rtSize.w + OVR_EYE_BUFFER + 1)/2;
+		_viewport->m_x      = 0;
 		_viewport->m_y      = 0;
-		_viewport->m_width  = (m_rtSize.w - OVR_EYE_BUFFER)/2;
-		_viewport->m_height = m_rtSize.h;
+		_viewport->m_width  = m_eyeBuffers[_eye]->m_eyeTextureSize.w;
+		_viewport->m_height = m_eyeBuffers[_eye]->m_eyeTextureSize.h;
 	}
 
-	bool OVR::postReset(void* _nwh, ovrRenderAPIConfig* _config, bool _debug)
+	void OVR::renderEyeStart(uint8_t _eye)
 	{
-		if (_debug)
-		{
-			switch (_config->Header.API)
-			{
-#if BGFX_CONFIG_RENDERER_DIRECT3D11
-			case ovrRenderAPI_D3D11:
-				{
-					ovrD3D11ConfigData* data = (ovrD3D11ConfigData*)_config;
-#	if OVR_VERSION > OVR_VERSION_043
-					m_rtSize = data->Header.BackBufferSize;
-#	else
-					m_rtSize = data->Header.RTSize;
-#	endif // OVR_VERSION > OVR_VERSION_043
-				}
-				break;
-#endif // BGFX_CONFIG_RENDERER_DIRECT3D11
-
-#if BGFX_CONFIG_RENDERER_OPENGL
-			case ovrRenderAPI_OpenGL:
-				{
-					ovrGLConfigData* data = (ovrGLConfigData*)_config;
-#	if OVR_VERSION > OVR_VERSION_043
-					m_rtSize = data->Header.BackBufferSize;
-#	else
-					m_rtSize = data->Header.RTSize;
-#	endif // OVR_VERSION > OVR_VERSION_043
-				}
-				break;
-#endif // BGFX_CONFIG_RENDERER_OPENGL
-
-			case ovrRenderAPI_None:
-			default:
-				BX_CHECK(false, "You should not be here!");
-				break;
-			}
-
-			m_debug = true;
-			return false;
-		}
+		m_eyeBuffers[_eye]->onRender(m_hmd);
+	}
 
+	bool OVR::postReset()
+	{
 		if (NULL == m_hmd)
 		{
 			return false;
 		}
 
-		m_isenabled = true;
-
-		ovrBool result;
-		result = ovrHmd_AttachToWindow(m_hmd, _nwh, NULL, NULL);
-		if (!result) { goto ovrError; }
-
-		ovrFovPort eyeFov[2] = { m_hmd->DefaultEyeFov[0], m_hmd->DefaultEyeFov[1] };
-		result = ovrHmd_ConfigureRendering(m_hmd
-			, _config
-			, 0
-#if OVR_VERSION < OVR_VERSION_050
-			| ovrDistortionCap_Chromatic // permanently enabled >= v5.0
-#endif
-			| ovrDistortionCap_Vignette
-			| ovrDistortionCap_TimeWarp
-			| ovrDistortionCap_Overdrive
-			| ovrDistortionCap_NoRestore
-			| ovrDistortionCap_HqDistortion
-			, eyeFov
-			, m_erd
-			);
-		if (!result) { goto ovrError; }
-
-		ovrHmd_SetEnabledCaps(m_hmd
-			, 0
-			| ovrHmdCap_LowPersistence
-			| ovrHmdCap_DynamicPrediction
-			);
-
-		result = ovrHmd_ConfigureTracking(m_hmd
-			, 0
-			| ovrTrackingCap_Orientation
-			| ovrTrackingCap_MagYawCorrection
-			| ovrTrackingCap_Position
-			, 0
-			);
-
-		if (!result)
+		for (int eyeIdx = 0; eyeIdx < ovrEye_Count; eyeIdx++)
 		{
-ovrError:
-			BX_TRACE("Failed to initialize OVR.");
-			m_isenabled = false;
-			return false;
+			m_erd[eyeIdx] = ovr_GetRenderDesc(m_hmd, (ovrEyeType)eyeIdx, m_hmdDesc.DefaultEyeFov[eyeIdx]);
 		}
 
-		m_warning = true;
+		m_isenabled = true;
+
 		return true;
 	}
 
-	void OVR::postReset(const ovrTexture& _texture)
+	void OVR::preReset()
 	{
-		if (NULL != m_hmd)
+		if (m_isenabled)
 		{
-			m_texture[0] = _texture;
-			m_texture[1] = _texture;
-
-			ovrRecti rect;
-			rect.Pos.x  = 0;
-			rect.Pos.y  = 0;
-			rect.Size.w = (m_rtSize.w - OVR_EYE_BUFFER)/2;
-			rect.Size.h = m_rtSize.h;
-
-			m_texture[0].Header.RenderViewport = rect;
-
-			rect.Pos.x += rect.Size.w + OVR_EYE_BUFFER;
-			m_texture[1].Header.RenderViewport = rect;
-
-			m_timing = ovrHmd_BeginFrame(m_hmd, 0);
-#if OVR_VERSION > OVR_VERSION_042
-			m_pose[0] = ovrHmd_GetHmdPosePerEye(m_hmd, ovrEye_Left);
-			m_pose[1] = ovrHmd_GetHmdPosePerEye(m_hmd, ovrEye_Right);
-#else
-			m_pose[0] = ovrHmd_GetEyePose(m_hmd, ovrEye_Left);
-			m_pose[1] = ovrHmd_GetEyePose(m_hmd, ovrEye_Right);
-#endif // OVR_VERSION > OVR_VERSION_042
+			// on window resize this will recreate the mirror texture in ovrPostReset
+			m_mirror->destroy(m_hmd);
+			BX_DELETE(g_allocator, m_mirror);
+			m_mirror = NULL;
+			m_isenabled = false;
 		}
 	}
 
-	void OVR::preReset()
+	void OVR::commitEye(uint8_t _eye)
 	{
 		if (m_isenabled)
 		{
-			ovrHmd_EndFrame(m_hmd, m_pose, m_texture);
-			ovrHmd_AttachToWindow(m_hmd, NULL, NULL, NULL);
-			ovrHmd_ConfigureRendering(m_hmd, NULL, 0, NULL, NULL);
-			m_isenabled = false;
+			m_hmdFrameReady = ovr_CommitTextureSwapChain(m_hmd, m_eyeBuffers[_eye]->m_swapTextureChain);
 		}
-
-		m_debug = false;
 	}
 
-	bool OVR::swap(HMD& _hmd)
+	bool OVR::swap(HMD& _hmd, bool originBottomLeft)
 	{
 		_hmd.flags = BGFX_HMD_NONE;
 
 		if (NULL != m_hmd)
 		{
 			_hmd.flags |= BGFX_HMD_DEVICE_RESOLUTION;
-			_hmd.deviceWidth  = m_hmd->Resolution.w;
-			_hmd.deviceHeight = m_hmd->Resolution.h;
+			_hmd.deviceWidth  = m_hmdDesc.Resolution.w;
+			_hmd.deviceHeight = m_hmdDesc.Resolution.h;
 		}
 
-		if (!m_isenabled)
+		if (!m_isenabled || !OVR_SUCCESS(m_hmdFrameReady))
 		{
 			return false;
 		}
 
 		_hmd.flags |= BGFX_HMD_RENDERING;
-		ovrHmd_EndFrame(m_hmd, m_pose, m_texture);
 
-		if (m_warning)
+		// finish frame for current eye
+		ovrViewScaleDesc viewScaleDesc;
+		viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
+		viewScaleDesc.HmdToEyeOffset[0] = m_hmdToEyeOffset[0];
+		viewScaleDesc.HmdToEyeOffset[1] = m_hmdToEyeOffset[1];
+
+		// create the main eye layer
+		ovrLayerEyeFov eyeLayer;
+		eyeLayer.Header.Type = ovrLayerType_EyeFov;
+		eyeLayer.Header.Flags = originBottomLeft ? ovrLayerFlag_TextureOriginAtBottomLeft : 0;
+
+		for (int eye = 0; eye < ovrEye_Count; eye++)
 		{
-			m_warning = !ovrHmd_DismissHSWDisplay(m_hmd);
+			eyeLayer.ColorTexture[eye] = m_eyeBuffers[eye]->m_swapTextureChain;
+			eyeLayer.Viewport[eye]     = ::OVR::Recti(m_eyeBuffers[eye]->m_eyeTextureSize);
+			eyeLayer.Fov[eye]          = m_hmdDesc.DefaultEyeFov[eye];
+			eyeLayer.RenderPose[eye]   = m_pose[eye];
+			eyeLayer.SensorSampleTime  = m_sensorSampleTime;
 		}
 
-		m_timing = ovrHmd_BeginFrame(m_hmd, 0);
+		// append all the layers to global list
+		ovrLayerHeader* layerList = &eyeLayer.Header;
+
+		ovr_SubmitFrame(m_hmd, m_frameIndex, NULL, &layerList, 1);
 
-#if OVR_VERSION > OVR_VERSION_042
-		m_pose[0] = ovrHmd_GetHmdPosePerEye(m_hmd, ovrEye_Left);
-		m_pose[1] = ovrHmd_GetHmdPosePerEye(m_hmd, ovrEye_Right);
-#else
-		m_pose[0] = ovrHmd_GetEyePose(m_hmd, ovrEye_Left);
-		m_pose[1] = ovrHmd_GetEyePose(m_hmd, ovrEye_Right);
-#endif // OVR_VERSION > OVR_VERSION_042
+		// perform mirror texture blit right after the entire frame is submitted to HMD
+		m_mirror->blit(m_hmd);
+
+		m_hmdToEyeOffset[0] = m_erd[0].HmdToEyeOffset;
+		m_hmdToEyeOffset[1] = m_erd[1].HmdToEyeOffset;
+
+		ovr_GetEyePoses(m_hmd, m_frameIndex, ovrTrue, m_hmdToEyeOffset, m_pose, &m_sensorSampleTime);
 
 		getEyePose(_hmd);
 
@@ -258,7 +193,7 @@ ovrError:
 	{
 		if (NULL != m_hmd)
 		{
-			ovrHmd_RecenterPose(m_hmd);
+			ovr_RecenterTrackingOrigin(m_hmd);
 		}
 	}
 
@@ -283,44 +218,27 @@ ovrError:
 				eye.fov[1] = erd.Fov.DownTan;
 				eye.fov[2] = erd.Fov.LeftTan;
 				eye.fov[3] = erd.Fov.RightTan;
-#if OVR_VERSION > OVR_VERSION_042
-				eye.viewOffset[0] = erd.HmdToEyeViewOffset.x;
-				eye.viewOffset[1] = erd.HmdToEyeViewOffset.y;
-				eye.viewOffset[2] = erd.HmdToEyeViewOffset.z;
-#else
-				eye.viewOffset[0] = erd.ViewAdjust.x;
-				eye.viewOffset[1] = erd.ViewAdjust.y;
-				eye.viewOffset[2] = erd.ViewAdjust.z;
-#endif // OVR_VERSION > OVR_VERSION_042
+
+				ovrMatrix4f eyeProj = ovrMatrix4f_Projection(m_erd[ii].Fov, 0.01f, 1000.0f, ovrProjection_LeftHanded);
+				for (int jj = 0; jj < 4; ++jj)
+				{
+					for (int kk = 0; kk < 4; ++kk)
+					{
+						eye.projection[4 * jj + kk] = eyeProj.M[kk][jj];
+					}
+				}
+
+				eye.viewOffset[0] = erd.HmdToEyeOffset.x;
+				eye.viewOffset[1] = erd.HmdToEyeOffset.y;
+				eye.viewOffset[2] = erd.HmdToEyeOffset.z;
+
 				eye.pixelsPerTanAngle[0] = erd.PixelsPerTanAngleAtCenter.x;
 				eye.pixelsPerTanAngle[1] = erd.PixelsPerTanAngleAtCenter.y;
 			}
 		}
-		else
-		{
-			for (int ii = 0; ii < 2; ++ii)
-			{
-				_hmd.eye[ii].rotation[0] = 0.0f;
-				_hmd.eye[ii].rotation[1] = 0.0f;
-				_hmd.eye[ii].rotation[2] = 0.0f;
-				_hmd.eye[ii].rotation[3] = 1.0f;
-				_hmd.eye[ii].translation[0] = 0.0f;
-				_hmd.eye[ii].translation[1] = 0.0f;
-				_hmd.eye[ii].translation[2] = 0.0f;
-				_hmd.eye[ii].fov[0] = 1.32928634f;
-				_hmd.eye[ii].fov[1] = 1.32928634f;
-				_hmd.eye[ii].fov[2] = 0 == ii ? 1.05865765f : 1.09236801f;
-				_hmd.eye[ii].fov[3] = 0 == ii ? 1.09236801f : 1.05865765f;
-				_hmd.eye[ii].viewOffset[0] = 0 == ii ? 0.0355070010f  : -0.0375000015f;
-				_hmd.eye[ii].viewOffset[1] = 0.0f;
-				_hmd.eye[ii].viewOffset[2] = 0 == ii ? 0.00150949787f : -0.00150949787f;
-				_hmd.eye[ii].pixelsPerTanAngle[0] = 1;
-				_hmd.eye[ii].pixelsPerTanAngle[1] = 1;
-			}
-		}
 
-		_hmd.width  = uint16_t(m_rtSize.w);
-		_hmd.height = uint16_t(m_rtSize.h);
+		_hmd.width  = uint16_t(m_hmdSize.w);
+		_hmd.height = uint16_t(m_hmdSize.h);
 	}
 
 } // namespace bgfx

+ 52 - 48
src/ovr.h

@@ -14,36 +14,44 @@
 
 #	define OVR_VERSION_(_a, _b, _c) (_a * 10000 + _b * 100 + _c)
 #	define OVR_VERSION     OVR_VERSION_(OVR_PRODUCT_VERSION, OVR_MAJOR_VERSION, OVR_MINOR_VERSION)
-#	define OVR_VERSION_042 OVR_VERSION_(0, 4, 2)
-#	define OVR_VERSION_043 OVR_VERSION_(0, 4, 3)
-#	define OVR_VERSION_044 OVR_VERSION_(0, 4, 4)
-#	define OVR_VERSION_050 OVR_VERSION_(0, 5, 0)
 
-#	if OVR_VERSION < OVR_VERSION_050
-#		include <OVR.h>
-#	else
-#		include <OVR_CAPI.h>
-#	endif // OVR_VERSION < OVR_VERSION_050
+#	include <OVR_CAPI.h>
 
 #	if BGFX_CONFIG_RENDERER_DIRECT3D11
-#		if OVR_VERSION < OVR_VERSION_050
-#			define OVR_D3D_VERSION 11
-#			include <OVR_D3D.h>
-#		else
-#			include <OVR_CAPI_D3D.h>
-#		endif
+#		include <OVR_CAPI_D3D.h>
 #	endif // BGFX_CONFIG_RENDERER_DIRECT3D11
 
 #	if BGFX_CONFIG_RENDERER_OPENGL
-#		if OVR_VERSION < OVR_VERSION_050
-#			include <OVR_GL.h>
-#		else
-#			include <OVR_CAPI_GL.h>
-#		endif
+#		include <OVR_CAPI_GL.h>
 #	endif // BGFX_CONFIG_RENDERER_OPENGL
 
+#include "Extras/OVR_Math.h"
+
 namespace bgfx
 {
+	// single eye buffer
+	struct OVRBufferI
+	{
+		virtual ~OVRBufferI() {};
+		virtual void onRender(const ovrSession &session) = 0;
+		virtual void destroy(const ovrSession &session) = 0;
+
+		ovrSizei   m_eyeTextureSize;
+		ovrTextureSwapChain m_swapTextureChain;
+	};
+
+	// mirrored window output
+	struct OVRMirrorI
+	{
+		virtual ~OVRMirrorI() {};
+		virtual void init(const ovrSession &session, int windowWidth, int windowHeight) = 0;
+		virtual void destroy(const ovrSession &session) = 0;
+		virtual void blit(const ovrSession &session) = 0;
+
+		ovrMirrorTexture     m_mirrorTexture;
+		ovrMirrorTextureDesc m_mirrorDesc;
+	};
+
 	struct OVR
 	{
 		OVR();
@@ -59,37 +67,31 @@ namespace bgfx
 			return m_isenabled;
 		}
 
-		bool isDebug() const
-		{
-			return m_debug;
-		}
-
 		void init();
 		void shutdown();
 
 		void getViewport(uint8_t _eye, Rect* _viewport);
-		bool postReset(void* _nwh, ovrRenderAPIConfig* _config, bool _debug = false);
-		void postReset(const ovrTexture& _texture);
+		void renderEyeStart(uint8_t _eye);
+		bool postReset();
 		void preReset();
-		bool swap(HMD& _hmd);
+		void commitEye(uint8_t _eye);
+		bool swap(HMD& _hmd, bool originBottomLeft);
 		void recenter();
 		void getEyePose(HMD& _hmd);
-		void getSize(uint32_t& _width, uint32_t& _height) const
-		{
-			_width  = m_rtSize.w;
-			_height = m_rtSize.h;
-		}
 
-		ovrHmd m_hmd;
-		ovrFrameTiming m_timing;
+		ovrSession m_hmd;
+		ovrHmdDesc m_hmdDesc;
 		ovrEyeRenderDesc m_erd[2];
-		ovrRecti m_rect[2];
-		ovrPosef m_pose[2];
-		ovrTexture m_texture[2];
-		ovrSizei m_rtSize;
-		bool m_warning;
+		ovrRecti    m_rect[2];
+		ovrPosef    m_pose[2];
+		ovrVector3f m_hmdToEyeOffset[2];
+		ovrSizei    m_hmdSize;
+		ovrResult   m_hmdFrameReady;
+		OVRBufferI *m_eyeBuffers[2];
+		OVRMirrorI *m_mirror;
+		long long   m_frameIndex;
+		double      m_sensorSampleTime;
 		bool m_isenabled;
-		bool m_debug;
 	};
 
 } // namespace bgfx
@@ -139,7 +141,15 @@ namespace bgfx
 			_viewport->m_height = 0;
 		}
 
-		bool swap(HMD& _hmd)
+		void commitEye(uint8_t /*_eye*/)
+		{
+		}
+
+		void renderEyeStart(uint8_t /*_eye*/)
+		{
+		}
+
+		bool swap(HMD& _hmd, bool /*originBottomLeft*/)
 		{
 			_hmd.flags = BGFX_HMD_NONE;
 			getEyePose(_hmd);
@@ -155,12 +165,6 @@ namespace bgfx
 			_hmd.width  = 0;
 			_hmd.height = 0;
 		}
-
-		void getSize(uint32_t& _width, uint32_t& _height) const
-		{
-			_width  = 0;
-			_height = 0;
-		}
 	};
 
 } // namespace bgfx

+ 183 - 90
src/renderer_d3d11.cpp

@@ -601,6 +601,170 @@ namespace bgfx { namespace d3d11
 	static PFN_GET_DEBUG_INTERFACE1 DXGIGetDebugInterface1;
 #endif // USE_D3D11_DYNAMIC_LIB
 
+
+#if BGFX_CONFIG_USE_OVR
+
+#include <tinystl/vector.h>
+
+	// Oculus Rift eye buffer
+	struct OVRBufferDX11 : public OVRBufferI
+	{
+		OVRBufferDX11(const ovrSession& session, int eyeIdx, ID3D11Device* d3dDevice, ID3D11DeviceContext* d3dCtx)
+		{
+			m_d3dDevice  = d3dDevice;
+			m_d3dContext = d3dCtx;
+			ovrHmdDesc hmdDesc = ovr_GetHmdDesc(session);
+			m_eyeTextureSize = ovr_GetFovTextureSize(session, (ovrEyeType)eyeIdx, hmdDesc.DefaultEyeFov[eyeIdx], 1.0f);
+
+			ovrTextureSwapChainDesc desc = {};
+			desc.Type = ovrTexture_2D;
+			desc.ArraySize = 1;
+			desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
+			desc.Width  = m_eyeTextureSize.w;
+			desc.Height = m_eyeTextureSize.h;
+			desc.MipLevels   = 1;
+			desc.SampleCount = 1;
+			desc.MiscFlags = ovrTextureMisc_DX_Typeless;
+			desc.BindFlags = ovrTextureBind_DX_RenderTarget;
+			desc.StaticImage = ovrFalse;
+
+			ovrResult result = ovr_CreateTextureSwapChainDX(session, d3dDevice, &desc, &m_swapTextureChain);
+
+			if (!OVR_SUCCESS(result))
+			{
+				BX_CHECK(false, "Could not create D3D11 OVR swap texture");
+			}
+
+			int textureCount = 0;
+			ovr_GetTextureSwapChainLength(session, m_swapTextureChain, &textureCount);
+
+			for (int i = 0; i < textureCount; ++i)
+			{
+				ID3D11Texture2D* tex = NULL;
+				ovr_GetTextureSwapChainBufferDX(session, m_swapTextureChain, i, IID_PPV_ARGS(&tex));
+				D3D11_RENDER_TARGET_VIEW_DESC rtvd = {};
+				rtvd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+				rtvd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+
+				ID3D11RenderTargetView* rtv;
+				DX_CHECK(d3dDevice->CreateRenderTargetView(tex, &rtvd, &rtv));
+				m_eyeRtv.push_back(rtv);
+				tex->Release();
+			}
+
+			// setup depth buffer
+			D3D11_TEXTURE2D_DESC dbDesc;
+			dbDesc.Width = m_eyeTextureSize.w;
+			dbDesc.Height = m_eyeTextureSize.h;
+			dbDesc.MipLevels = 1;
+			dbDesc.ArraySize = 1;
+			dbDesc.Format = DXGI_FORMAT_D32_FLOAT;
+			dbDesc.SampleDesc.Count = 1;
+			dbDesc.SampleDesc.Quality = 0;
+			dbDesc.Usage = D3D11_USAGE_DEFAULT;
+			dbDesc.CPUAccessFlags = 0;
+			dbDesc.MiscFlags = 0;
+			dbDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+			ID3D11Texture2D* tex;
+			DX_CHECK(d3dDevice->CreateTexture2D(&dbDesc, NULL, &tex));
+			DX_CHECK(d3dDevice->CreateDepthStencilView(tex, NULL, &m_depthBuffer));
+			tex->Release();
+		}
+
+		void onRender(const ovrSession& session)
+		{
+			// Clear and set up rendertarget
+			int texIndex = 0;
+			ovr_GetTextureSwapChainCurrentIndex(session, m_swapTextureChain, &texIndex);
+
+			float black[] = { 0.f, 0.f, 0.f, 0.f }; // Important that alpha=0, if want pixels to be transparent, for manual layers
+			m_d3dContext->OMSetRenderTargets(1, &m_eyeRtv[texIndex], m_depthBuffer);
+			m_d3dContext->ClearRenderTargetView(m_eyeRtv[texIndex], black);
+			m_d3dContext->ClearDepthStencilView(m_depthBuffer, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0);
+
+			D3D11_VIEWPORT D3Dvp;
+			D3Dvp.TopLeftX = 0;
+			D3Dvp.TopLeftY = 0;
+			D3Dvp.Width  = (FLOAT)m_eyeTextureSize.w;
+			D3Dvp.Height = (FLOAT)m_eyeTextureSize.h;
+			D3Dvp.MinDepth = 0;
+			D3Dvp.MaxDepth = 1;
+			m_d3dContext->RSSetViewports(1, &D3Dvp);
+		}
+
+		void destroy(const ovrSession& session)
+		{
+			for (size_t i = 0; i < m_eyeRtv.size(); ++i)
+			{
+				m_eyeRtv[i]->Release();
+			}
+
+			ovr_DestroyTextureSwapChain(session, m_swapTextureChain);
+			m_depthBuffer->Release();
+		}
+
+		ID3D11Device*           m_d3dDevice;
+		ID3D11DeviceContext*    m_d3dContext;
+		stl::vector<ID3D11RenderTargetView *> m_eyeRtv;
+		ID3D11DepthStencilView* m_depthBuffer;
+	};
+
+	// Oculus Rift mirror
+	struct OVRMirrorDX11 : public OVRMirrorI
+	{
+		OVRMirrorDX11(ID3D11Device* d3dDevice, 
+					  ID3D11DeviceContext* d3dCtx, 
+					  IDXGISwapChain* d3dSc) : m_d3dDevice(d3dDevice)
+											 , m_d3dContext(d3dCtx)
+											 , m_d3dSwapChain(d3dSc)
+		{
+		}
+
+		void init(const ovrSession& session, int windowWidth, int windowHeight)
+		{
+			m_mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
+			m_mirrorDesc.Width  = windowWidth;
+			m_mirrorDesc.Height = windowHeight;
+			ovrResult result = ovr_CreateMirrorTextureDX(session, m_d3dDevice, &m_mirrorDesc, &m_mirrorTexture);
+
+			if (!OVR_SUCCESS(result))
+			{
+				BX_CHECK(false, "Could not create D3D11 OVR mirror texture");
+			}
+		}
+
+		void destroy(const ovrSession& session)
+		{
+			if (!m_mirrorTexture)
+				return;
+
+			ovr_DestroyMirrorTexture(session, m_mirrorTexture);
+			m_mirrorTexture = NULL;
+		}
+
+		void blit(const ovrSession& session)
+		{
+			if (!m_mirrorTexture)
+				return;
+
+			ID3D11Texture2D* tex = NULL;
+			ovr_GetMirrorTextureBufferDX(session, m_mirrorTexture, IID_PPV_ARGS(&tex));
+			ID3D11Texture2D* backBuffer;
+			DX_CHECK(m_d3dSwapChain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&backBuffer));
+
+			m_d3dContext->CopyResource(backBuffer, tex);
+			DX_CHECK(m_d3dSwapChain->Present(0, 0));
+
+			tex->Release();
+			backBuffer->Release();
+		}
+
+		ID3D11Device*        m_d3dDevice;
+		ID3D11DeviceContext* m_d3dContext;
+		IDXGISwapChain*      m_d3dSwapChain;
+	};
+#endif // BGFX_CONFIG_USE_OVR
+
 	struct RendererContextD3D11 : public RendererContextI
 	{
 		RendererContextD3D11()
@@ -635,8 +799,6 @@ namespace bgfx { namespace d3d11
 			, m_fsChanges(0)
 			, m_rtMsaa(false)
 			, m_timerQuerySupport(false)
-			, m_ovrRtv(NULL)
-			, m_ovrDsv(NULL)
 		{
 			m_fbh.idx = invalidHandle;
 			memset(&m_adapterDesc, 0, sizeof(m_adapterDesc) );
@@ -2001,10 +2163,6 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 
 			uint32_t width  = getBufferWidth();
 			uint32_t height = getBufferHeight();
-			if (m_ovr.isEnabled() )
-			{
-				m_ovr.getSize(width, height);
-			}
 
 			FrameBufferHandle fbh = BGFX_INVALID_HANDLE;
 			setFrameBuffer(fbh, false);
@@ -2192,7 +2350,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 
 				if (SUCCEEDED(hr) )
 				{
-					if (!m_ovr.swap(_hmd) )
+					if (!m_ovr.swap(_hmd, false) )
 					{
 						hr = m_swapChain->Present(syncInterval, 0);
 					}
@@ -3083,77 +3241,22 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 		void ovrPostReset()
 		{
 #if BGFX_CONFIG_USE_OVR
-			if (m_flags & (BGFX_RESET_HMD|BGFX_RESET_HMD_DEBUG) )
+			if (m_resolution.m_flags & (BGFX_RESET_HMD|BGFX_RESET_HMD_DEBUG) )
 			{
-				ovrD3D11Config config;
-				config.D3D11.Header.API = ovrRenderAPI_D3D11;
-#	if OVR_VERSION > OVR_VERSION_043
-				config.D3D11.Header.BackBufferSize.w = m_scd.BufferDesc.Width;
-				config.D3D11.Header.BackBufferSize.h = m_scd.BufferDesc.Height;
-				config.D3D11.pBackBufferUAV = NULL;
-#	else
-				config.D3D11.Header.RTSize.w = m_scd.BufferDesc.Width;
-				config.D3D11.Header.RTSize.h = m_scd.BufferDesc.Height;
-#	endif // OVR_VERSION > OVR_VERSION_042
-				config.D3D11.Header.Multisample = 0;
-				config.D3D11.pDevice        = m_device;
-				config.D3D11.pDeviceContext = m_deviceCtx;
-				config.D3D11.pBackBufferRT  = m_backBufferColor;
-				config.D3D11.pSwapChain     = m_swapChain;
-				if (m_ovr.postReset(g_platformData.nwh, &config.Config, !!(m_flags & BGFX_RESET_HMD_DEBUG) ) )
+				if (m_ovr.postReset())
 				{
-					uint32_t size = sizeof(uint32_t) + sizeof(TextureCreate);
-					const Memory* mem = alloc(size);
-
-					bx::StaticMemoryBlockWriter writer(mem->data, mem->size);
-					uint32_t magic = BGFX_CHUNK_MAGIC_TEX;
-					bx::write(&writer, magic);
-
-					TextureCreate tc;
-					tc.m_flags   = BGFX_TEXTURE_RT|( ((m_flags & BGFX_RESET_MSAA_MASK) >> BGFX_RESET_MSAA_SHIFT) << BGFX_TEXTURE_RT_MSAA_SHIFT);
-					tc.m_width   = m_ovr.m_rtSize.w;
-					tc.m_height  = m_ovr.m_rtSize.h;
-					tc.m_sides   = 0;
-					tc.m_depth   = 0;
-					tc.m_numMips = 1;
-					tc.m_format  = uint8_t(bgfx::TextureFormat::BGRA8);
-					tc.m_cubeMap = false;
-					tc.m_mem     = NULL;
-					bx::write(&writer, tc);
-					m_ovrRT.create(mem, tc.m_flags, 0);
-
-					release(mem);
-
-					DX_CHECK(m_device->CreateRenderTargetView(m_ovrRT.m_ptr, NULL, &m_ovrRtv) );
-
-					D3D11_TEXTURE2D_DESC dsd;
-					dsd.Width      = m_ovr.m_rtSize.w;
-					dsd.Height     = m_ovr.m_rtSize.h;
-					dsd.MipLevels  = 1;
-					dsd.ArraySize  = 1;
-					dsd.Format     = DXGI_FORMAT_D24_UNORM_S8_UINT;
-					dsd.SampleDesc = m_scd.SampleDesc;
-					dsd.Usage      = D3D11_USAGE_DEFAULT;
-					dsd.BindFlags  = D3D11_BIND_DEPTH_STENCIL;
-					dsd.CPUAccessFlags = 0;
-					dsd.MiscFlags      = 0;
-
-					ID3D11Texture2D* depthStencil;
-					DX_CHECK(m_device->CreateTexture2D(&dsd, NULL, &depthStencil) );
-					DX_CHECK(m_device->CreateDepthStencilView(depthStencil, NULL, &m_ovrDsv) );
-					DX_RELEASE(depthStencil, 0);
-
-					ovrD3D11Texture texture;
-					texture.D3D11.Header.API         = ovrRenderAPI_D3D11;
-					texture.D3D11.Header.TextureSize = m_ovr.m_rtSize;
-					texture.D3D11.pTexture           = m_ovrRT.m_texture2d;
-					texture.D3D11.pSRView            = m_ovrRT.m_srv;
-					m_ovr.postReset(texture.Texture);
-
-					bx::xchg(m_ovrRtv, m_backBufferColor);
-
-					BX_CHECK(NULL == m_backBufferDepthStencil, "");
-					bx::xchg(m_ovrDsv, m_backBufferDepthStencil);
+					for (int eyeIdx = 0; eyeIdx < ovrEye_Count; eyeIdx++)
+					{
+						// eye buffers need to be initialized only once during application lifetime
+						if (!m_ovr.m_eyeBuffers[eyeIdx])
+						{
+							m_ovr.m_eyeBuffers[eyeIdx] = BX_NEW(g_allocator, OVRBufferDX11(m_ovr.m_hmd, eyeIdx, m_device, m_deviceCtx));
+						}
+					}
+
+					// recreate mirror texture
+					m_ovr.m_mirror = BX_NEW(g_allocator, OVRMirrorDX11(m_device, m_deviceCtx, m_swapChain));
+					m_ovr.m_mirror->init(m_ovr.m_hmd, m_resolution.m_width, m_resolution.m_height);
 				}
 			}
 #endif // BGFX_CONFIG_USE_OVR
@@ -3163,16 +3266,6 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 		{
 #if BGFX_CONFIG_USE_OVR
 			m_ovr.preReset();
-			if (NULL != m_ovrRtv)
-			{
-				bx::xchg(m_ovrRtv, m_backBufferColor);
-				bx::xchg(m_ovrDsv, m_backBufferDepthStencil);
-				BX_CHECK(NULL == m_backBufferDepthStencil, "");
-
-				DX_RELEASE(m_ovrRtv, 0);
-				DX_RELEASE(m_ovrDsv, 0);
-				m_ovrRT.destroy();
-			}
 #endif // BGFX_CONFIG_USE_OVR
 		}
 
@@ -3564,9 +3657,6 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 		bool m_timerQuerySupport;
 
 		OVR m_ovr;
-		TextureD3D11 m_ovrRT;
-		ID3D11RenderTargetView* m_ovrRtv;
-		ID3D11DepthStencilView* m_ovrDsv;
 	};
 
 	static RendererContextD3D11* s_renderD3D11;
@@ -4864,7 +4954,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 
 		_render->m_hmdInitialized = m_ovr.isInitialized();
 
-		const bool hmdEnabled = m_ovr.isEnabled() || m_ovr.isDebug();
+		const bool hmdEnabled = m_ovr.isEnabled();
 		ViewState viewState(_render, hmdEnabled);
 
 		bool wireframe = !!(_render->m_debug&BGFX_DEBUG_WIREFRAME);
@@ -4987,6 +5077,9 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 						if (m_ovr.isEnabled() )
 						{
 							m_ovr.getViewport(eye, &viewState.m_rect);
+							// commit previous eye to HMD and start rendering new frame
+							m_ovr.commitEye(eye);
+							m_ovr.renderEyeStart(eye);
 						}
 						else
 						{

+ 163 - 78
src/renderer_gl.cpp

@@ -1274,6 +1274,144 @@ namespace bgfx { namespace gl
 		BX_UNUSED(supported);
 	}
 
+#if BGFX_CONFIG_USE_OVR
+
+	// Oculus Rift eye buffer
+	struct OVRBufferGL : public OVRBufferI
+	{
+		OVRBufferGL(const ovrSession& session, int eyeIdx)
+		{
+			ovrHmdDesc hmdDesc = ovr_GetHmdDesc(session);
+			m_eyeTextureSize = ovr_GetFovTextureSize(session, (ovrEyeType)eyeIdx, hmdDesc.DefaultEyeFov[eyeIdx], 1.0f);
+
+			ovrTextureSwapChainDesc desc = {};
+			desc.Type = ovrTexture_2D;
+			desc.ArraySize = 1;
+			desc.Width  = m_eyeTextureSize.w;
+			desc.Height = m_eyeTextureSize.h;
+			desc.MipLevels = 1;
+			desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
+			desc.SampleCount = 1;
+			desc.StaticImage = ovrFalse;
+
+			ovr_CreateTextureSwapChainGL(session, &desc, &m_swapTextureChain);
+
+			int textureCount = 0;
+			ovr_GetTextureSwapChainLength(session, m_swapTextureChain, &textureCount);
+
+			for (int j = 0; j < textureCount; ++j)
+			{
+				GLuint chainTexId;
+				ovr_GetTextureSwapChainBufferGL(session, m_swapTextureChain, j, &chainTexId);
+				GL_CHECK(glBindTexture(GL_TEXTURE_2D, chainTexId));
+
+				GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+				GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+				GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+				GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+			}
+
+			GL_CHECK(glGenFramebuffers(1, &m_eyeFbo));
+
+			// create depth buffer
+			GL_CHECK(glGenTextures(1, &m_depthBuffer));
+			GL_CHECK(glBindTexture(GL_TEXTURE_2D, m_depthBuffer));
+			GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+			GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+			GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+			GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+			GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_eyeTextureSize.w, m_eyeTextureSize.h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL));
+		}
+
+		void onRender(const ovrSession& session)
+		{
+			// set the current eye texture in swap chain
+			int curIndex;
+			ovr_GetTextureSwapChainCurrentIndex(session, m_swapTextureChain, &curIndex);
+			ovr_GetTextureSwapChainBufferGL(session, m_swapTextureChain, curIndex, &m_eyeTexId);
+
+			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_eyeFbo));
+			GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_eyeTexId, 0));
+			GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthBuffer, 0));
+
+			GL_CHECK(glViewport(0, 0, m_eyeTextureSize.w, m_eyeTextureSize.h));
+			GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
+		}
+
+		void destroy(const ovrSession& session)
+		{
+			GL_CHECK(glDeleteFramebuffers(1, &m_eyeFbo));
+			GL_CHECK(glDeleteTextures(1, &m_depthBuffer));
+
+			ovr_DestroyTextureSwapChain(session, m_swapTextureChain);
+		}
+
+		GLuint m_eyeFbo;
+		GLuint m_eyeTexId;
+		GLuint m_depthBuffer;
+	};
+
+	// Oculus Rift mirror
+	struct OVRMirrorGL : public OVRMirrorI
+	{
+		void init(const ovrSession& session, int windowWidth, int windowHeight)
+		{
+			memset(&m_mirrorDesc, 0, sizeof(m_mirrorDesc));
+			m_mirrorDesc.Width  = windowWidth;
+			m_mirrorDesc.Height = windowHeight;
+			m_mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
+
+			ovr_CreateMirrorTextureGL(session, &m_mirrorDesc, &m_mirrorTexture);
+
+			// Fallback to doing nothing if mirror was not created. This is to prevent errors with fast window resizes
+			if (!m_mirrorTexture)
+				return;
+
+			// Configure the mirror read buffer
+			GLuint texId;
+			ovr_GetMirrorTextureBufferGL(session, m_mirrorTexture, &texId);
+			GL_CHECK(glGenFramebuffers(1, &m_mirrorFBO));
+			GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, m_mirrorFBO));
+			GL_CHECK(glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0));
+			GL_CHECK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+			GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
+
+			if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+			{
+				GL_CHECK(glDeleteFramebuffers(1, &m_mirrorFBO));
+				BX_CHECK(false, "Could not initialize VR buffers!");
+			}
+		}
+
+		void destroy(const ovrSession& session)
+		{
+			if (!m_mirrorTexture)
+				return;
+
+			GL_CHECK(glDeleteFramebuffers(1, &m_mirrorFBO));
+			ovr_DestroyMirrorTexture(session, m_mirrorTexture);
+			m_mirrorTexture = NULL;
+		}
+
+		void blit(const ovrSession& /*session*/)
+		{
+			if (!m_mirrorTexture)
+				return;
+
+			// Blit mirror texture to back buffer
+			GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, m_mirrorFBO));
+			GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
+			GLint w = m_mirrorDesc.Width;
+			GLint h = m_mirrorDesc.Height;
+			GL_CHECK(glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST));
+			GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
+		}
+
+		GLuint m_mirrorFBO;
+	};
+#endif // BGFX_CONFIG_USE_OVR
+
 	struct RendererContextGL : public RendererContextI
 	{
 		RendererContextGL()
@@ -1304,7 +1442,6 @@ namespace bgfx { namespace gl
 			, m_hash( (BX_PLATFORM_WINDOWS<<1) | BX_ARCH_64BIT)
 			, m_backBufferFbo(0)
 			, m_msaaBackBufferFbo(0)
-			, m_ovrFbo(0)
 		{
 			memset(m_msaaBackBufferRbos, 0, sizeof(m_msaaBackBufferRbos) );
 		}
@@ -2107,10 +2244,9 @@ namespace bgfx { namespace gl
 					m_glctx.swap(m_frameBuffers[m_windows[ii].idx].m_swapChain);
 				}
 
-				if (!m_ovr.swap(_hmd) )
-				{
-					m_glctx.swap();
-				}
+				m_ovr.swap(_hmd, true);
+				// need to swap GL render context even if OVR is enabled to get the mirror texture in the output
+				m_glctx.swap();
 			}
 		}
 
@@ -2397,10 +2533,6 @@ namespace bgfx { namespace gl
 
 			uint32_t width  = m_resolution.m_width;
 			uint32_t height = m_resolution.m_height;
-			if (m_ovr.isEnabled() )
-			{
-				m_ovr.getSize(width, height);
-			}
 
 			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_backBufferFbo) );
 			GL_CHECK(glViewport(0, 0, width, height) );
@@ -2857,63 +2989,22 @@ namespace bgfx { namespace gl
 		void ovrPostReset()
 		{
 #if BGFX_CONFIG_USE_OVR
-			if (m_resolution.m_flags & (BGFX_RESET_HMD|BGFX_RESET_HMD_DEBUG) )
-			{
-				ovrGLConfig config;
-				config.OGL.Header.API = ovrRenderAPI_OpenGL;
-#	if OVR_VERSION > OVR_VERSION_043
-				config.OGL.Header.BackBufferSize.w = m_resolution.m_width;
-				config.OGL.Header.BackBufferSize.h = m_resolution.m_height;
-#	else
-				config.OGL.Header.RTSize.w = m_resolution.m_width;
-				config.OGL.Header.RTSize.h = m_resolution.m_height;
-#	endif // OVR_VERSION > OVR_VERSION_043
-				config.OGL.Header.Multisample = 0;
-				config.OGL.Window = (HWND)g_platformData.nwh;
-				config.OGL.DC = GetDC(config.OGL.Window);
-				if (m_ovr.postReset(g_platformData.nwh, &config.Config, !!(m_resolution.m_flags & BGFX_RESET_HMD_DEBUG) ) )
+			if (m_resolution.m_flags & (BGFX_RESET_HMD | BGFX_RESET_HMD_DEBUG))
+			{
+				if (m_ovr.postReset())
 				{
-					uint32_t size = sizeof(uint32_t) + sizeof(TextureCreate);
-					const Memory* mem = alloc(size);
-
-					bx::StaticMemoryBlockWriter writer(mem->data, mem->size);
-					uint32_t magic = BGFX_CHUNK_MAGIC_TEX;
-					bx::write(&writer, magic);
-
-					TextureCreate tc;
-					tc.m_flags   = BGFX_TEXTURE_RT|( ((m_resolution.m_flags & BGFX_RESET_MSAA_MASK) >> BGFX_RESET_MSAA_SHIFT) << BGFX_TEXTURE_RT_MSAA_SHIFT);;
-					tc.m_width   = m_ovr.m_rtSize.w;
-					tc.m_height  = m_ovr.m_rtSize.h;
-					tc.m_sides   = 0;
-					tc.m_depth   = 0;
-					tc.m_numMips = 1;
-					tc.m_format  = uint8_t(bgfx::TextureFormat::BGRA8);
-					tc.m_cubeMap = false;
-					tc.m_mem = NULL;
-					bx::write(&writer, tc);
-
-					m_ovrRT.create(mem, tc.m_flags, 0);
-					release(mem);
-
-					m_ovrFbo = m_msaaBackBufferFbo;
-
-					GL_CHECK(glGenFramebuffers(1, &m_msaaBackBufferFbo) );
-					GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_msaaBackBufferFbo) );
-
-					GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER
-						, GL_COLOR_ATTACHMENT0
-						, GL_TEXTURE_2D
-						, m_ovrRT.m_id
-						, 0
-						) );
-
-					GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_ovrFbo) );
+					for (int eyeIdx = 0; eyeIdx < ovrEye_Count; eyeIdx++)
+					{
+						// eye buffers need to be initialized only once during application lifetime
+						if (!m_ovr.m_eyeBuffers[eyeIdx])
+						{
+							m_ovr.m_eyeBuffers[eyeIdx] = BX_NEW(g_allocator, OVRBufferGL(m_ovr.m_hmd, eyeIdx));
+						}
+					}
 
-					ovrGLTexture texture;
-					texture.OGL.Header.API         = ovrRenderAPI_OpenGL;
-					texture.OGL.Header.TextureSize = m_ovr.m_rtSize;
-					texture.OGL.TexId              = m_ovrRT.m_id;
-					m_ovr.postReset(texture.Texture);
+					// recreate mirror texture
+					m_ovr.m_mirror = BX_NEW(g_allocator, OVRMirrorGL);
+					m_ovr.m_mirror->init(m_ovr.m_hmd, m_resolution.m_width, m_resolution.m_height);
 				}
 			}
 #endif // BGFX_CONFIG_USE_OVR
@@ -2923,14 +3014,6 @@ namespace bgfx { namespace gl
 		{
 #if BGFX_CONFIG_USE_OVR
 			m_ovr.preReset();
-			if (m_ovr.isEnabled() )
-			{
-				GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0) );
-				GL_CHECK(glDeleteFramebuffers(1, &m_msaaBackBufferFbo) );
-				m_msaaBackBufferFbo = m_ovrFbo;
-				m_ovrFbo = 0;
-				m_ovrRT.destroy();
-			}
 #endif // BGFX_CONFIG_USE_OVR
 		}
 
@@ -3354,8 +3437,6 @@ namespace bgfx { namespace gl
 		const char* m_glslVersion;
 
 		OVR m_ovr;
-		TextureGL m_ovrRT;
-		GLint m_ovrFbo;
 	};
 
 	RendererContextGL* s_renderGL;
@@ -3647,7 +3728,7 @@ namespace bgfx { namespace gl
 		}
 
 		m_numPredefined = 0;
- 		m_numSamplers = 0;
+		m_numSamplers = 0;
 
 		BX_TRACE("Uniforms (%d):", activeUniforms);
 		for (int32_t ii = 0; ii < activeUniforms; ++ii)
@@ -5439,7 +5520,7 @@ namespace bgfx { namespace gl
 
 		_render->m_hmdInitialized = m_ovr.isInitialized();
 
-		const bool hmdEnabled = m_ovr.isEnabled() || m_ovr.isDebug();
+		const bool hmdEnabled = m_ovr.isEnabled();
 		ViewState viewState(_render, hmdEnabled);
 
 		uint16_t programIdx = invalidHandle;
@@ -5491,12 +5572,13 @@ namespace bgfx { namespace gl
 			m_occlusionQuery.resolve(_render);
 		}
 
+		uint8_t eye = 0;
+
 		if (0 == (_render->m_debug&BGFX_DEBUG_IFH) )
 		{
 			GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, m_msaaBackBufferFbo) );
 
 			bool viewRestart = false;
-			uint8_t eye = 0;
 			uint8_t restartState = 0;
 			viewState.m_rect = _render->m_rect[0];
 
@@ -5578,6 +5660,9 @@ namespace bgfx { namespace gl
 						if (m_ovr.isEnabled() )
 						{
 							m_ovr.getViewport(eye, &viewState.m_rect);
+							// commit previous eye to HMD and start rendering new frame
+							m_ovr.commitEye(eye);
+							m_ovr.renderEyeStart(eye);
 						}
 						else
 						{
@@ -5679,7 +5764,7 @@ namespace bgfx { namespace gl
 						const RenderCompute& compute = renderItem.compute;
 
 						ProgramGL& program = m_program[key.m_program];
- 						GL_CHECK(glUseProgram(program.m_id) );
+						GL_CHECK(glUseProgram(program.m_id) );
 
 						GLbitfield barrier = 0;
 						for (uint32_t ii = 0; ii < BGFX_MAX_COMPUTE_BINDINGS; ++ii)