summaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/CAPI
diff options
context:
space:
mode:
authorBrad Davis <[email protected]>2014-04-14 21:25:09 -0700
committerBrad Davis <[email protected]>2014-04-14 21:25:09 -0700
commit07d0f4d0bbf3477ac6a9584f726e8ec6ab285707 (patch)
tree1854d0c690eff32e77b137567c88a52d56d8b660 /LibOVR/Src/CAPI
parentf28388ff2af14b56ef2d973b2f4f9da021716d4c (diff)
Adding windows 0.3.1 SDK
Diffstat (limited to 'LibOVR/Src/CAPI')
-rw-r--r--LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp63
-rw-r--r--LibOVR/Src/CAPI/CAPI_DistortionRenderer.h100
-rw-r--r--LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp674
-rw-r--r--LibOVR/Src/CAPI/CAPI_FrameTimeManager.h264
-rw-r--r--LibOVR/Src/CAPI/CAPI_GlobalState.cpp142
-rw-r--r--LibOVR/Src/CAPI/CAPI_GlobalState.h84
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp147
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDRenderState.h93
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDState.cpp774
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDState.h334
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.cpp29
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.h34
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.cpp30
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.h34
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.cpp773
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.h131
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.cpp416
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.h505
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.cpp251
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.h120
-rw-r--r--LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_Util.cpp317
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp1006
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h125
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp516
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_Util.h522
-rw-r--r--LibOVR/Src/CAPI/Shaders/DistortionChroma_ps.psh12
-rw-r--r--LibOVR/Src/CAPI/Shaders/DistortionChroma_vs.vsh24
-rw-r--r--LibOVR/Src/CAPI/Shaders/DistortionTimewarpChroma_vs.vsh40
-rw-r--r--LibOVR/Src/CAPI/Shaders/DistortionTimewarp_vs.vsh36
-rw-r--r--LibOVR/Src/CAPI/Shaders/Distortion_ps.psh9
-rw-r--r--LibOVR/Src/CAPI/Shaders/Distortion_vs.vsh14
-rw-r--r--LibOVR/Src/CAPI/Shaders/SimpleQuad_ps.psh6
-rw-r--r--LibOVR/Src/CAPI/Shaders/SimpleQuad_vs.vsh8
-rw-r--r--LibOVR/Src/CAPI/Shaders/genPixelShaderHeader.bat15
-rw-r--r--LibOVR/Src/CAPI/Shaders/genVertexShaderHeader.bat15
35 files changed, 7663 insertions, 0 deletions
diff --git a/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp
new file mode 100644
index 0000000..8c0f8b8
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp
@@ -0,0 +1,63 @@
+/************************************************************************************
+
+Filename : CAPI_DistortionRenderer.cpp
+Content : Combines all of the rendering state associated with the HMD
+Created : February 2, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_DistortionRenderer.h"
+
+// TBD: Move to separate config file that handles back-ends.
+#define OVR_D3D_VERSION 11
+#include "D3D1X/CAPI_D3D1X_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#define OVR_D3D_VERSION 10
+#include "D3D1X/CAPI_D3D1X_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#define OVR_D3D_VERSION 9
+#include "D3D1X/CAPI_D3D9_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#include "GL/CAPI_GL_DistortionRenderer.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** DistortionRenderer
+
+// TBD: Move to separate config file that handles back-ends.
+
+DistortionRenderer::CreateFunc DistortionRenderer::APICreateRegistry[ovrRenderAPI_Count] =
+{
+ 0, // None
+ &GL::DistortionRenderer::Create,
+ 0, // Android_GLES
+ &D3D9::DistortionRenderer::Create,
+ &D3D10::DistortionRenderer::Create,
+ &D3D11::DistortionRenderer::Create
+};
+
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h
new file mode 100644
index 0000000..d1b8011
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h
@@ -0,0 +1,100 @@
+/************************************************************************************
+
+Filename : CAPI_DistortionRenderer.h
+Content : Abstract interface for platform-specific rendering of distortion
+Created : February 2, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_DistortionRenderer_h
+#define OVR_CAPI_DistortionRenderer_h
+
+#include "CAPI_HMDRenderState.h"
+#include "CAPI_FrameTimeManager.h"
+
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** CAPI::DistortionRenderer
+
+// DistortionRenderer implements rendering of distortion and other overlay elements
+// in platform-independent way.
+// Platform-specific renderer back ends for CAPI are derived from this class.
+
+class DistortionRenderer : public RefCountBase<DistortionRenderer>
+{
+ // Quiet assignment compiler warning.
+ void operator = (const DistortionRenderer&) { }
+public:
+
+ DistortionRenderer(ovrRenderAPIType api, ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+ : RenderAPI(api), HMD(hmd), TimeManager(timeManager), RState(renderState)
+ { }
+ virtual ~DistortionRenderer()
+ { }
+
+
+ // Configures the Renderer based on externally passed API settings. Must be
+ // called before use.
+ // Under D3D, apiConfig includes D3D Device pointer, back buffer and other
+ // needed structures.
+ virtual bool Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned distortionCaps) = 0;
+
+ // Submits one eye texture for rendering. This is in the separate method to
+ // allow "submit as you render" scenarios on horizontal screens where one
+ // eye can be scanned out before the other.
+ virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture) = 0;
+
+ // Finish the frame, optionally swapping buffers.
+ // Many implementations may actually apply the distortion here.
+ virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor,
+ unsigned char* latencyTester2DrawColor) = 0;
+
+
+
+ // *** Creation Factory logic
+
+ ovrRenderAPIType GetRenderAPI() const { return RenderAPI; }
+
+ // Creation function for this interface, registered for API.
+ typedef DistortionRenderer* (*CreateFunc)(ovrHmd hmd,
+ FrameTimeManager &timeManager,
+ const HMDRenderState& renderState);
+
+ static CreateFunc APICreateRegistry[ovrRenderAPI_Count];
+
+protected:
+ const ovrRenderAPIType RenderAPI;
+ const ovrHmd HMD;
+ FrameTimeManager& TimeManager;
+ const HMDRenderState& RState;
+};
+
+}} // namespace OVR::CAPI
+
+
+#endif // OVR_CAPI_DistortionRenderer_h
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
new file mode 100644
index 0000000..de7eeeb
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
@@ -0,0 +1,674 @@
+/************************************************************************************
+
+Filename : CAPI_FrameTimeManager.cpp
+Content : Manage frame timing and pose prediction for rendering
+Created : November 30, 2013
+Authors : Volga Aksoy, Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_FrameTimeManager.h"
+
+
+namespace OVR { namespace CAPI {
+
+
+//-------------------------------------------------------------------------------------
+// ***** FrameLatencyTracker
+
+
+FrameLatencyTracker::FrameLatencyTracker()
+{
+ Reset();
+}
+
+void FrameLatencyTracker::Reset()
+{
+ TrackerEnabled = true;
+ WaitMode = SampleWait_Zeroes;
+ FrameIndex = 0;
+ MatchCount = 0;
+ RenderLatencySeconds = 0.0;
+ TimewarpLatencySeconds = 0.0;
+
+ FrameDeltas.Clear();
+}
+
+
+unsigned char FrameLatencyTracker::GetNextDrawColor()
+{
+ if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes) ||
+ (FrameIndex >= FramesTracked))
+ {
+ return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(0);
+ }
+
+ OVR_ASSERT(FrameIndex < FramesTracked);
+ return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1);
+}
+
+
+void FrameLatencyTracker::SaveDrawColor(unsigned char drawColor, double endFrameTime,
+ double renderIMUTime, double timewarpIMUTime )
+{
+ if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes))
+ return;
+
+ if (FrameIndex < FramesTracked)
+ {
+ OVR_ASSERT(Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1) == drawColor);
+ OVR_UNUSED(drawColor);
+
+ // saves {color, endFrame time}
+ FrameEndTimes[FrameIndex].ReadbackIndex = FrameIndex + 1;
+ FrameEndTimes[FrameIndex].TimeSeconds = endFrameTime;
+ FrameEndTimes[FrameIndex].RenderIMUTimeSeconds = renderIMUTime;
+ FrameEndTimes[FrameIndex].TimewarpIMUTimeSeconds= timewarpIMUTime;
+ FrameEndTimes[FrameIndex].MatchedRecord = false;
+ FrameIndex++;
+ }
+ else
+ {
+ // If the request was outstanding for too long, switch to zero mode to restart.
+ if (endFrameTime > (FrameEndTimes[FrameIndex-1].TimeSeconds + 0.15))
+ {
+ if (MatchCount == 0)
+ {
+ // If nothing was matched, we have no latency reading.
+ RenderLatencySeconds = 0.0;
+ TimewarpLatencySeconds = 0.0;
+ }
+
+ WaitMode = SampleWait_Zeroes;
+ MatchCount = 0;
+ FrameIndex = 0;
+ }
+ }
+}
+
+
+void FrameLatencyTracker::MatchRecord(const Util::FrameTimeRecordSet &r)
+{
+ if (!TrackerEnabled)
+ return;
+
+ if (WaitMode == SampleWait_Zeroes)
+ {
+ // Do we have all zeros?
+ if (r.IsAllZeroes())
+ {
+ OVR_ASSERT(FrameIndex == 0);
+ WaitMode = SampleWait_Match;
+ MatchCount = 0;
+ }
+ return;
+ }
+
+ // We are in Match Mode. Wait until all colors are matched or timeout,
+ // at which point we go back to zeros.
+
+ for (int i = 0; i < FrameIndex; i++)
+ {
+ int recordIndex = 0;
+ int consecutiveMatch = 0;
+
+ OVR_ASSERT(FrameEndTimes[i].ReadbackIndex != 0);
+
+ if (r.FindReadbackIndex(&recordIndex, FrameEndTimes[i].ReadbackIndex))
+ {
+ // Advance forward to see that we have several more matches.
+ int ri = recordIndex + 1;
+ int j = i + 1;
+
+ consecutiveMatch++;
+
+ for (; (j < FrameIndex) && (ri < Util::FrameTimeRecordSet::RecordCount); j++, ri++)
+ {
+ if (r[ri].ReadbackIndex != FrameEndTimes[j].ReadbackIndex)
+ break;
+ consecutiveMatch++;
+ }
+
+ // Match at least 2 items in the row, to avoid accidentally matching color.
+ if (consecutiveMatch > 1)
+ {
+ // Record latency values for all but last samples. Keep last 2 samples
+ // for the future to simplify matching.
+ for (int q = 0; q < consecutiveMatch; q++)
+ {
+ const Util::FrameTimeRecord &scanoutFrame = r[recordIndex+q];
+ FrameTimeRecordEx &renderFrame = FrameEndTimes[i+q];
+
+ if (!renderFrame.MatchedRecord)
+ {
+ double deltaSeconds = scanoutFrame.TimeSeconds - renderFrame.TimeSeconds;
+ if (deltaSeconds > 0.0)
+ {
+ FrameDeltas.AddTimeDelta(deltaSeconds);
+ LatencyRecordTime = scanoutFrame.TimeSeconds;
+ RenderLatencySeconds = scanoutFrame.TimeSeconds - renderFrame.RenderIMUTimeSeconds;
+ TimewarpLatencySeconds = (renderFrame.TimewarpIMUTimeSeconds == 0.0) ? 0.0 :
+ (scanoutFrame.TimeSeconds - renderFrame.TimewarpIMUTimeSeconds);
+ }
+
+ renderFrame.MatchedRecord = true;
+ MatchCount++;
+ }
+ }
+
+ // Exit for.
+ break;
+ }
+ }
+ } // for ( i => FrameIndex )
+
+
+ // If we matched all frames, start over.
+ if (MatchCount == FramesTracked)
+ {
+ WaitMode = SampleWait_Zeroes;
+ MatchCount = 0;
+ FrameIndex = 0;
+ }
+}
+
+
+void FrameLatencyTracker::GetLatencyTimings(float latencies[3])
+{
+ if (ovr_GetTimeInSeconds() > (LatencyRecordTime + 2.0))
+ {
+ latencies[0] = 0.0f;
+ latencies[1] = 0.0f;
+ latencies[2] = 0.0f;
+ }
+ else
+ {
+ latencies[0] = (float)RenderLatencySeconds;
+ latencies[1] = (float)TimewarpLatencySeconds;
+ latencies[2] = (float)FrameDeltas.GetMedianTimeDelta();
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+
+FrameTimeManager::FrameTimeManager(bool vsyncEnabled)
+ : VsyncEnabled(vsyncEnabled), DynamicPrediction(true), SdkRender(false),
+ FrameTiming()
+{
+ RenderIMUTimeSeconds = 0.0;
+ TimewarpIMUTimeSeconds = 0.0;
+
+ // HACK: SyncToScanoutDelay observed close to 1 frame in video cards.
+ // Overwritten by dynamic latency measurement on DK2.
+ VSyncToScanoutDelay = 0.013f;
+ NoVSyncToScanoutDelay = 0.004f;
+}
+
+void FrameTimeManager::Init(HmdRenderInfo& renderInfo)
+{
+ // Set up prediction distances.
+ // With-Vsync timings.
+ RenderInfo = renderInfo;
+
+ ScreenSwitchingDelay = RenderInfo.Shutter.PixelSettleTime * 0.5f +
+ RenderInfo.Shutter.PixelPersistence * 0.5f;
+}
+
+void FrameTimeManager::ResetFrameTiming(unsigned frameIndex,
+ bool vsyncEnabled, bool dynamicPrediction,
+ bool sdkRender)
+{
+ VsyncEnabled = vsyncEnabled;
+ DynamicPrediction = dynamicPrediction;
+ SdkRender = sdkRender;
+
+ FrameTimeDeltas.Clear();
+ DistortionRenderTimes.Clear();
+ ScreenLatencyTracker.Reset();
+
+ FrameTiming.FrameIndex = frameIndex;
+ FrameTiming.NextFrameTime = 0.0;
+ FrameTiming.ThisFrameTime = 0.0;
+ FrameTiming.Inputs.FrameDelta = calcFrameDelta();
+ FrameTiming.Inputs.ScreenDelay = calcScreenDelay();
+ FrameTiming.Inputs.TimewarpWaitDelta = 0.0f;
+
+ LocklessTiming.SetState(FrameTiming);
+}
+
+
+double FrameTimeManager::calcFrameDelta() const
+{
+ // Timing difference between frame is tracked by FrameTimeDeltas, or
+ // is a hard-coded value of 1/FrameRate.
+ double frameDelta;
+
+ if (!VsyncEnabled)
+ {
+ frameDelta = 0.0;
+ }
+ else if (FrameTimeDeltas.GetCount() > 3)
+ {
+ frameDelta = FrameTimeDeltas.GetMedianTimeDelta();
+ if (frameDelta > (RenderInfo.Shutter.VsyncToNextVsync + 0.001))
+ frameDelta = RenderInfo.Shutter.VsyncToNextVsync;
+ }
+ else
+ {
+ frameDelta = RenderInfo.Shutter.VsyncToNextVsync;
+ }
+
+ return frameDelta;
+}
+
+
+double FrameTimeManager::calcScreenDelay() const
+{
+ double screenDelay = ScreenSwitchingDelay;
+ double measuredVSyncToScanout;
+
+ // Use real-time DK2 latency tester HW for prediction if its is working.
+ // Do sanity check under 60 ms
+ if (!VsyncEnabled)
+ {
+ screenDelay += NoVSyncToScanoutDelay;
+ }
+ else if ( DynamicPrediction &&
+ (ScreenLatencyTracker.FrameDeltas.GetCount() > 3) &&
+ (measuredVSyncToScanout = ScreenLatencyTracker.FrameDeltas.GetMedianTimeDelta(),
+ (measuredVSyncToScanout > 0.0001) && (measuredVSyncToScanout < 0.06)) )
+ {
+ screenDelay += measuredVSyncToScanout;
+ }
+ else
+ {
+ screenDelay += VSyncToScanoutDelay;
+ }
+
+ return screenDelay;
+}
+
+
+double FrameTimeManager::calcTimewarpWaitDelta() const
+{
+ // If timewarp timing hasn't been calculated, we should wait.
+ if (!VsyncEnabled)
+ return 0.0;
+
+ if (SdkRender)
+ {
+ if (NeedDistortionTimeMeasurement())
+ return 0.0;
+ return -(DistortionRenderTimes.GetMedianTimeDelta() + 0.002);
+ }
+
+ // Just a hard-coded "high" value for game-drawn code.
+ // TBD: Just return 0 and let users calculate this themselves?
+ return -0.003;
+}
+
+
+
+void FrameTimeManager::Timing::InitTimingFromInputs(const FrameTimeManager::TimingInputs& inputs,
+ HmdShutterTypeEnum shutterType,
+ double thisFrameTime, unsigned int frameIndex)
+{
+ // ThisFrameTime comes from the end of last frame, unless it it changed.
+ double nextFrameBase;
+ double frameDelta = inputs.FrameDelta;
+
+ FrameIndex = frameIndex;
+
+ ThisFrameTime = thisFrameTime;
+ NextFrameTime = ThisFrameTime + frameDelta;
+ nextFrameBase = NextFrameTime + inputs.ScreenDelay;
+ MidpointTime = nextFrameBase + frameDelta * 0.5;
+ TimewarpPointTime = (inputs.TimewarpWaitDelta == 0.0) ?
+ 0.0 : (NextFrameTime + inputs.TimewarpWaitDelta);
+
+ // Calculate absolute points in time when eye rendering or corresponding time-warp
+ // screen edges will become visible.
+ // This only matters with VSync.
+ switch(shutterType)
+ {
+ case HmdShutter_RollingTopToBottom:
+ EyeRenderTimes[0] = MidpointTime;
+ EyeRenderTimes[1] = MidpointTime;
+ TimeWarpStartEndTimes[0][0] = nextFrameBase;
+ TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
+ TimeWarpStartEndTimes[1][0] = nextFrameBase;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+ break;
+ case HmdShutter_RollingLeftToRight:
+ EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.25;
+ EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.75;
+
+ /*
+ // TBD: MA: It is probably better if mesh sets it up per-eye.
+ // Would apply if screen is 0 -> 1 for each eye mesh
+ TimeWarpStartEndTimes[0][0] = nextFrameBase;
+ TimeWarpStartEndTimes[0][1] = MidpointTime;
+ TimeWarpStartEndTimes[1][0] = MidpointTime;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+ */
+
+ // Mesh is set up to vary from Edge of scree 0 -> 1 across both eyes
+ TimeWarpStartEndTimes[0][0] = nextFrameBase;
+ TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
+ TimeWarpStartEndTimes[1][0] = nextFrameBase;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+
+ break;
+ case HmdShutter_RollingRightToLeft:
+
+ EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.75;
+ EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.25;
+
+ // This is *Correct* with Tom's distortion mesh organization.
+ TimeWarpStartEndTimes[0][0] = nextFrameBase ;
+ TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
+ TimeWarpStartEndTimes[1][0] = nextFrameBase ;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+ break;
+ case HmdShutter_Global:
+ // TBD
+ EyeRenderTimes[0] = MidpointTime;
+ EyeRenderTimes[1] = MidpointTime;
+ TimeWarpStartEndTimes[0][0] = MidpointTime;
+ TimeWarpStartEndTimes[0][1] = MidpointTime;
+ TimeWarpStartEndTimes[1][0] = MidpointTime;
+ TimeWarpStartEndTimes[1][1] = MidpointTime;
+ break;
+ }
+}
+
+
+double FrameTimeManager::BeginFrame(unsigned frameIndex)
+{
+ RenderIMUTimeSeconds = 0.0;
+ TimewarpIMUTimeSeconds = 0.0;
+
+ // ThisFrameTime comes from the end of last frame, unless it it changed.
+ double thisFrameTime = (FrameTiming.NextFrameTime != 0.0) ?
+ FrameTiming.NextFrameTime : ovr_GetTimeInSeconds();
+
+ // We are starting to process a new frame...
+ FrameTiming.InitTimingFromInputs(FrameTiming.Inputs, RenderInfo.Shutter.Type,
+ thisFrameTime, frameIndex);
+
+ return FrameTiming.ThisFrameTime;
+}
+
+
+void FrameTimeManager::EndFrame()
+{
+ // Record timing since last frame; must be called after Present & sync.
+ FrameTiming.NextFrameTime = ovr_GetTimeInSeconds();
+ if (FrameTiming.ThisFrameTime > 0.0)
+ {
+ FrameTimeDeltas.AddTimeDelta(FrameTiming.NextFrameTime - FrameTiming.ThisFrameTime);
+ FrameTiming.Inputs.FrameDelta = calcFrameDelta();
+ }
+
+ // Write to Lock-less
+ LocklessTiming.SetState(FrameTiming);
+}
+
+
+
+// Thread-safe function to query timing for a future frame
+
+FrameTimeManager::Timing FrameTimeManager::GetFrameTiming(unsigned frameIndex)
+{
+ Timing frameTiming = LocklessTiming.GetState();
+
+ if (frameTiming.ThisFrameTime != 0.0)
+ {
+ // If timing hasn't been initialized, starting based on "now" is the best guess.
+ frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type,
+ ovr_GetTimeInSeconds(), frameIndex);
+ }
+
+ else if (frameIndex > frameTiming.FrameIndex)
+ {
+ unsigned frameDelta = frameIndex - frameTiming.FrameIndex;
+ double thisFrameTime = frameTiming.NextFrameTime +
+ double(frameDelta-1) * frameTiming.Inputs.FrameDelta;
+ // Don't run away too far into the future beyond rendering.
+ OVR_ASSERT(frameDelta < 6);
+
+ frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type,
+ thisFrameTime, frameIndex);
+ }
+
+ return frameTiming;
+}
+
+
+double FrameTimeManager::GetEyePredictionTime(ovrEyeType eye)
+{
+ if (VsyncEnabled)
+ {
+ return FrameTiming.EyeRenderTimes[eye];
+ }
+
+ // No VSync: Best guess for the near future
+ return ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay;
+}
+
+Posef FrameTimeManager::GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye)
+{
+ double eyeRenderTime = GetEyePredictionTime(eye);
+ ovrSensorState eyeState = ovrHmd_GetSensorState(hmd, eyeRenderTime);
+
+// EyeRenderPoses[eye] = eyeState.Predicted.Pose;
+
+ // Record view pose sampling time for Latency reporting.
+ if (RenderIMUTimeSeconds == 0.0)
+ RenderIMUTimeSeconds = eyeState.Recorded.TimeInSeconds;
+
+ return eyeState.Predicted.Pose;
+}
+
+
+void FrameTimeManager::GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2])
+{
+ if (VsyncEnabled)
+ {
+ timewarpStartEnd[0] = FrameTiming.TimeWarpStartEndTimes[eye][0];
+ timewarpStartEnd[1] = FrameTiming.TimeWarpStartEndTimes[eye][1];
+ return;
+ }
+
+ // Free-running, so this will be displayed immediately.
+ // Unfortunately we have no idea which bit of the screen is actually going to be displayed.
+ // TODO: guess which bit of the screen is being displayed!
+ // (e.g. use DONOTWAIT on present and see when the return isn't WASSTILLWAITING?)
+
+ // We have no idea where scan-out is currently, so we can't usefully warp the screen spatially.
+ timewarpStartEnd[0] = ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay;
+ timewarpStartEnd[1] = timewarpStartEnd[0];
+}
+
+
+void FrameTimeManager::GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eyeId,
+ ovrPosef renderPose, ovrMatrix4f twmOut[2])
+{
+ if (!hmd)
+ {
+ return;
+ }
+
+ double timewarpStartEnd[2] = { 0.0, 0.0 };
+ GetTimewarpPredictions(eyeId, timewarpStartEnd);
+
+ ovrSensorState startState = ovrHmd_GetSensorState(hmd, timewarpStartEnd[0]);
+ ovrSensorState endState = ovrHmd_GetSensorState(hmd, timewarpStartEnd[1]);
+
+ if (TimewarpIMUTimeSeconds == 0.0)
+ TimewarpIMUTimeSeconds = startState.Recorded.TimeInSeconds;
+
+ Quatf quatFromStart = startState.Predicted.Pose.Orientation;
+ Quatf quatFromEnd = endState.Predicted.Pose.Orientation;
+ Quatf quatFromEye = renderPose.Orientation; //EyeRenderPoses[eyeId].Orientation;
+ quatFromEye.Invert();
+
+ Quatf timewarpStartQuat = quatFromEye * quatFromStart;
+ Quatf timewarpEndQuat = quatFromEye * quatFromEnd;
+
+ Matrix4f timewarpStart(timewarpStartQuat);
+ Matrix4f timewarpEnd(timewarpEndQuat);
+
+
+ // The real-world orientations have: X=right, Y=up, Z=backwards.
+ // The vectors inside the mesh are in NDC to keep the shader simple: X=right, Y=down, Z=forwards.
+ // So we need to perform a similarity transform on this delta matrix.
+ // The verbose code would look like this:
+ /*
+ Matrix4f matBasisChange;
+ matBasisChange.SetIdentity();
+ matBasisChange.M[0][0] = 1.0f;
+ matBasisChange.M[1][1] = -1.0f;
+ matBasisChange.M[2][2] = -1.0f;
+ Matrix4f matBasisChangeInv = matBasisChange.Inverted();
+ matRenderFromNow = matBasisChangeInv * matRenderFromNow * matBasisChange;
+ */
+ // ...but of course all the above is a constant transform and much more easily done.
+ // We flip the signs of the Y&Z row, then flip the signs of the Y&Z column,
+ // and of course most of the flips cancel:
+ // +++ +-- +--
+ // +++ -> flip Y&Z columns -> +-- -> flip Y&Z rows -> -++
+ // +++ +-- -++
+ timewarpStart.M[0][1] = -timewarpStart.M[0][1];
+ timewarpStart.M[0][2] = -timewarpStart.M[0][2];
+ timewarpStart.M[1][0] = -timewarpStart.M[1][0];
+ timewarpStart.M[2][0] = -timewarpStart.M[2][0];
+
+ timewarpEnd .M[0][1] = -timewarpEnd .M[0][1];
+ timewarpEnd .M[0][2] = -timewarpEnd .M[0][2];
+ timewarpEnd .M[1][0] = -timewarpEnd .M[1][0];
+ timewarpEnd .M[2][0] = -timewarpEnd .M[2][0];
+
+ twmOut[0] = timewarpStart;
+ twmOut[1] = timewarpEnd;
+}
+
+
+// Used by renderer to determine if it should time distortion rendering.
+bool FrameTimeManager::NeedDistortionTimeMeasurement() const
+{
+ if (!VsyncEnabled)
+ return false;
+ return DistortionRenderTimes.GetCount() < 10;
+}
+
+
+void FrameTimeManager::AddDistortionTimeMeasurement(double distortionTimeSeconds)
+{
+ DistortionRenderTimes.AddTimeDelta(distortionTimeSeconds);
+
+ // If timewarp timing changes based on this sample, update it.
+ double newTimewarpWaitDelta = calcTimewarpWaitDelta();
+ if (newTimewarpWaitDelta != FrameTiming.Inputs.TimewarpWaitDelta)
+ {
+ FrameTiming.Inputs.TimewarpWaitDelta = newTimewarpWaitDelta;
+ LocklessTiming.SetState(FrameTiming);
+ }
+}
+
+
+void FrameTimeManager::UpdateFrameLatencyTrackingAfterEndFrame(
+ unsigned char frameLatencyTestColor,
+ const Util::FrameTimeRecordSet& rs)
+{
+ // FrameTiming.NextFrameTime in this context (after EndFrame) is the end frame time.
+ ScreenLatencyTracker.SaveDrawColor(frameLatencyTestColor,
+ FrameTiming.NextFrameTime,
+ RenderIMUTimeSeconds,
+ TimewarpIMUTimeSeconds);
+
+ ScreenLatencyTracker.MatchRecord(rs);
+
+ // If screen delay changed, update timing.
+ double newScreenDelay = calcScreenDelay();
+ if (newScreenDelay != FrameTiming.Inputs.ScreenDelay)
+ {
+ FrameTiming.Inputs.ScreenDelay = newScreenDelay;
+ LocklessTiming.SetState(FrameTiming);
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** TimeDeltaCollector
+
+void TimeDeltaCollector::AddTimeDelta(double timeSeconds)
+{
+ // avoid adding invalid timing values
+ if(timeSeconds < 0.0f)
+ return;
+
+ if (Count == Capacity)
+ {
+ for(int i=0; i< Count-1; i++)
+ TimeBufferSeconds[i] = TimeBufferSeconds[i+1];
+ Count--;
+ }
+ TimeBufferSeconds[Count++] = timeSeconds;
+}
+
+double TimeDeltaCollector::GetMedianTimeDelta() const
+{
+ double SortedList[Capacity];
+ bool used[Capacity];
+
+ memset(used, 0, sizeof(used));
+ SortedList[0] = 0.0; // In case Count was 0...
+
+ // Probably the slowest way to find median...
+ for (int i=0; i<Count; i++)
+ {
+ double smallestDelta = 1000000.0;
+ int index = 0;
+
+ for (int j = 0; j < Count; j++)
+ {
+ if (!used[j])
+ {
+ if (TimeBufferSeconds[j] < smallestDelta)
+ {
+ smallestDelta = TimeBufferSeconds[j];
+ index = j;
+ }
+ }
+ }
+
+ // Mark as used
+ used[index] = true;
+ SortedList[i] = smallestDelta;
+ }
+
+ return SortedList[Count/2];
+}
+
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h
new file mode 100644
index 0000000..07a2963
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h
@@ -0,0 +1,264 @@
+/************************************************************************************
+
+Filename : CAPI_FrameTimeManager.h
+Content : Manage frame timing and pose prediction for rendering
+Created : November 30, 2013
+Authors : Volga Aksoy, Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_FrameTimeManager_h
+#define OVR_CAPI_FrameTimeManager_h
+
+#include "../OVR_CAPI.h"
+#include "../Kernel/OVR_Timer.h"
+#include "../Kernel/OVR_Math.h"
+#include "../Util/Util_Render_Stereo.h"
+#include "../Util/Util_LatencyTest2.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+
+// Helper class to collect median times between frames, so that we know
+// how long to wait.
+struct TimeDeltaCollector
+{
+ TimeDeltaCollector() : Count(0) { }
+
+ void AddTimeDelta(double timeSeconds);
+ void Clear() { Count = 0; }
+
+ double GetMedianTimeDelta() const;
+
+ double GetCount() const { return Count; }
+
+ enum { Capacity = 12 };
+private:
+ int Count;
+ double TimeBufferSeconds[Capacity];
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** FrameLatencyTracker
+
+// FrameLatencyTracker tracks frame Present to display Scan-out timing, as reported by
+// the DK2 internal latency tester pixel read-back. The computed value is used in
+// FrameTimeManager for prediction. View Render and TimeWarp to scan-out latencies are
+// also reported for debugging.
+//
+// The class operates by generating color values from GetNextDrawColor() that must
+// be rendered on the back end and then looking for matching values in FrameTimeRecordSet
+// structure as reported by HW.
+
+class FrameLatencyTracker
+{
+public:
+
+ enum { FramesTracked = Util::LT2_IncrementCount-1 };
+
+ FrameLatencyTracker();
+
+ // DrawColor == 0 is special in that it doesn't need saving of timestamp
+ unsigned char GetNextDrawColor();
+
+ void SaveDrawColor(unsigned char drawColor, double endFrameTime,
+ double renderIMUTime, double timewarpIMUTime );
+
+ void MatchRecord(const Util::FrameTimeRecordSet &r);
+
+ void GetLatencyTimings(float latencies[3]);
+
+ void Reset();
+
+public:
+
+ struct FrameTimeRecordEx : public Util::FrameTimeRecord
+ {
+ bool MatchedRecord;
+ double RenderIMUTimeSeconds;
+ double TimewarpIMUTimeSeconds;
+ };
+
+ // True if rendering read-back is enabled.
+ bool TrackerEnabled;
+
+ enum SampleWaitType {
+ SampleWait_Zeroes, // We are waiting for a record with all zeros.
+ SampleWait_Match // We are issuing & matching colors.
+ };
+
+ SampleWaitType WaitMode;
+ int MatchCount;
+ // Records of frame timings that we are trying to measure.
+ FrameTimeRecordEx FrameEndTimes[FramesTracked];
+ int FrameIndex;
+ // Median filter for (ScanoutTimeSeconds - PostPresent frame time)
+ TimeDeltaCollector FrameDeltas;
+ // Latency reporting results
+ double RenderLatencySeconds;
+ double TimewarpLatencySeconds;
+ double LatencyRecordTime;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** FrameTimeManager
+
+// FrameTimeManager keeps track of rendered frame timing and handles predictions for
+// orientations and time-warp.
+
+class FrameTimeManager
+{
+public:
+ FrameTimeManager(bool vsyncEnabled = true);
+
+ // Data that affects frame timing computation.
+ struct TimingInputs
+ {
+ // Hard-coded value or dynamic as reported by FrameTimeDeltas.GetMedianTimeDelta().
+ double FrameDelta;
+ // Screen delay from present to scan-out, as potentially reported by ScreenLatencyTracker.
+ double ScreenDelay;
+ // Negative value of how many seconds before EndFrame we start timewarp. 0.0 if not used.
+ double TimewarpWaitDelta;
+
+ TimingInputs()
+ : FrameDelta(0), ScreenDelay(0), TimewarpWaitDelta(0)
+ { }
+ };
+
+ // Timing values for a specific frame.
+ struct Timing
+ {
+ TimingInputs Inputs;
+
+ // Index of a frame that started at ThisFrameTime.
+ unsigned int FrameIndex;
+ // Predicted absolute times for when this frame will show up on screen.
+ // Generally, all values will be >= NextFrameTime, since that's the time we expect next
+ // vsync to succeed.
+ double ThisFrameTime;
+ double TimewarpPointTime;
+ double NextFrameTime;
+ double MidpointTime;
+ double EyeRenderTimes[2];
+ double TimeWarpStartEndTimes[2][2];
+
+ Timing()
+ {
+ memset(this, 0, sizeof(Timing));
+ }
+
+ void InitTimingFromInputs(const TimingInputs& inputs, HmdShutterTypeEnum shutterType,
+ double thisFrameTime, unsigned int frameIndex);
+ };
+
+
+ // Called on startup to provided data on HMD timing.
+ void Init(HmdRenderInfo& renderInfo);
+
+ // Called with each new ConfigureRendering.
+ void ResetFrameTiming(unsigned frameIndex,
+ bool vsyncEnabled, bool dynamicPrediction, bool sdkRender);
+
+ void SetVsync(bool enabled) { VsyncEnabled = enabled; }
+
+ // BeginFrame returns time of the call
+ // TBD: Should this be a predicted time value instead ?
+ double BeginFrame(unsigned frameIndex);
+ void EndFrame();
+
+ // Thread-safe function to query timing for a future frame
+ Timing GetFrameTiming(unsigned frameIndex);
+
+ double GetEyePredictionTime(ovrEyeType eye);
+ Posef GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye);
+
+ void GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2]);
+ void GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]);
+
+ // Used by renderer to determine if it should time distortion rendering.
+ bool NeedDistortionTimeMeasurement() const;
+ void AddDistortionTimeMeasurement(double distortionTimeSeconds);
+
+
+ // DK2 Lateny test interface
+
+ // Get next draw color for DK2 latency tester
+ unsigned char GetFrameLatencyTestDrawColor()
+ { return ScreenLatencyTracker.GetNextDrawColor(); }
+
+ // Must be called after EndFrame() to update latency tester timings.
+ // Must pass color reported by NextFrameColor for this frame.
+ void UpdateFrameLatencyTrackingAfterEndFrame(unsigned char frameLatencyTestColor,
+ const Util::FrameTimeRecordSet& rs);
+
+ void GetLatencyTimings(float latencies[3])
+ { return ScreenLatencyTracker.GetLatencyTimings(latencies); }
+
+
+ const Timing& GetFrameTiming() const { return FrameTiming; }
+
+private:
+
+ double calcFrameDelta() const;
+ double calcScreenDelay() const;
+ double calcTimewarpWaitDelta() const;
+
+
+ HmdRenderInfo RenderInfo;
+ // Timings are collected through a median filter, to avoid outliers.
+ TimeDeltaCollector FrameTimeDeltas;
+ TimeDeltaCollector DistortionRenderTimes;
+ FrameLatencyTracker ScreenLatencyTracker;
+
+ // Timing changes if we have no Vsync (all prediction is reduced to fixed interval).
+ bool VsyncEnabled;
+ // Set if we are rendering via the SDK, so DistortionRenderTimes is valid.
+ bool DynamicPrediction;
+ // Set if SDk is doing teh rendering.
+ bool SdkRender;
+
+ // Total frame delay due to VsyncToFirstScanline, persistence and settle time.
+ // Computed from RenderInfor.Shutter.
+ double VSyncToScanoutDelay;
+ double NoVSyncToScanoutDelay;
+ double ScreenSwitchingDelay;
+
+ // Current (or last) frame timing info. Used as a source for LocklessTiming.
+ Timing FrameTiming;
+ // TBD: Don't we need NextFrame here as well?
+ LocklessUpdater<Timing> LocklessTiming;
+
+
+ // IMU Read timings
+ double RenderIMUTimeSeconds;
+ double TimewarpIMUTimeSeconds;
+};
+
+
+}} // namespace OVR::CAPI
+
+#endif // OVR_CAPI_FrameTimeManager_h
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_GlobalState.cpp b/LibOVR/Src/CAPI/CAPI_GlobalState.cpp
new file mode 100644
index 0000000..2ed1794
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_GlobalState.cpp
@@ -0,0 +1,142 @@
+/************************************************************************************
+
+Filename : CAPI_GlobalState.cpp
+Content : Maintains global state of the CAPI
+Created : January 24, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_GlobalState.h"
+
+namespace OVR { namespace CAPI {
+
+
+//-------------------------------------------------------------------------------------
+// Open Questions / Notes
+
+// 2. Detect HMDs.
+// Challenge: If we do everything through polling, it would imply we want all the devices
+// initialized. However, there may be multiple rifts, extra sensors, etc,
+// which shouldn't be allocated.
+//
+
+// How do you reset orientation Quaternion?
+// Can you change IPD?
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVRGlobalState
+
+// Global instance
+GlobalState* GlobalState::pInstance = 0;
+
+
+GlobalState::GlobalState()
+{
+ pManager = *DeviceManager::Create();
+ // Handle the DeviceManager's messages
+ pManager->AddMessageHandler( this );
+ EnumerateDevices();
+
+ // PhoneSensors::Init();
+}
+
+GlobalState::~GlobalState()
+{
+ RemoveHandlerFromDevices();
+ OVR_ASSERT(HMDs.IsEmpty());
+}
+
+int GlobalState::EnumerateDevices()
+{
+ // Need to use separate lock for device enumeration, as pManager->GetHandlerLock()
+ // would produce deadlocks here.
+ Lock::Locker lock(&EnumerationLock);
+
+ EnumeratedDevices.Clear();
+
+ DeviceEnumerator<HMDDevice> e = pManager->EnumerateDevices<HMDDevice>();
+ while(e.IsAvailable())
+ {
+ EnumeratedDevices.PushBack(DeviceHandle(e));
+ e.Next();
+ }
+
+ return (int)EnumeratedDevices.GetSize();
+}
+
+
+HMDDevice* GlobalState::CreateDevice(int index)
+{
+ Lock::Locker lock(&EnumerationLock);
+
+ if (index >= (int)EnumeratedDevices.GetSize())
+ return 0;
+ return EnumeratedDevices[index].CreateDeviceTyped<HMDDevice>();
+}
+
+
+void GlobalState::AddHMD(HMDState* hmd)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ HMDs.PushBack(hmd);
+}
+void GlobalState::RemoveHMD(HMDState* hmd)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ hmd->RemoveNode();
+}
+
+void GlobalState::NotifyHMDs_AddDevice(DeviceType deviceType)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ for(HMDState* hmd = HMDs.GetFirst(); !HMDs.IsNull(hmd); hmd = hmd->pNext)
+ hmd->NotifyAddDevice(deviceType);
+}
+
+void GlobalState::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded || msg.Type == Message_DeviceRemoved)
+ {
+ if (msg.pDevice == pManager)
+ {
+ const MessageDeviceStatus& statusMsg =
+ static_cast<const MessageDeviceStatus&>(msg);
+
+ if (msg.Type == Message_DeviceAdded)
+ {
+ //LogText("OnMessage DeviceAdded.\n");
+
+ // We may have added a sensor/other device; notify any HMDs that might
+ // need it to check for it later.
+ NotifyHMDs_AddDevice(statusMsg.Handle.GetType());
+ }
+ else
+ {
+ //LogText("OnMessage DeviceRemoved.\n");
+ }
+ }
+ }
+}
+
+
+}} // namespace OVR::CAPI
diff --git a/LibOVR/Src/CAPI/CAPI_GlobalState.h b/LibOVR/Src/CAPI/CAPI_GlobalState.h
new file mode 100644
index 0000000..54ab8cc
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_GlobalState.h
@@ -0,0 +1,84 @@
+/************************************************************************************
+
+Filename : CAPI_GlobalState.h
+Content : Maintains global state of the CAPI
+Created : January 24, 2013
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_GlobalState_h
+#define OVR_CAPI_GlobalState_h
+
+#include "../OVR_CAPI.h"
+#include "../OVR_Device.h"
+#include "../Kernel/OVR_Timer.h"
+#include "../Kernel/OVR_Math.h"
+
+#include "CAPI_HMDState.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** OVRGlobalState
+
+// Global DeviceManager state - singleton instance of this is created
+// by ovr_Initialize().
+class GlobalState : public MessageHandler, public NewOverrideBase
+{
+public:
+ GlobalState();
+ ~GlobalState();
+
+ static GlobalState *pInstance;
+
+ int EnumerateDevices();
+ HMDDevice* CreateDevice(int index);
+
+ // MessageHandler implementation
+ void OnMessage(const Message& msg);
+
+ // Helpers used to keep track of HMDs and notify them of sensor changes.
+ void AddHMD(HMDState* hmd);
+ void RemoveHMD(HMDState* hmd);
+ void NotifyHMDs_AddDevice(DeviceType deviceType);
+
+ const char* GetLastError()
+ {
+ return 0;
+ }
+
+ DeviceManager* GetManager() { return pManager; }
+
+protected:
+
+ Ptr<DeviceManager> pManager;
+ Lock EnumerationLock;
+ Array<DeviceHandle> EnumeratedDevices;
+
+ // Currently created hmds; protected by Manager lock.
+ List<HMDState> HMDs;
+};
+
+}} // namespace OVR::CAPI
+
+#endif
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp b/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp
new file mode 100644
index 0000000..bdfa0c7
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp
@@ -0,0 +1,147 @@
+/************************************************************************************
+
+Filename : OVR_CAPI_HMDRenderState.cpp
+Content : Combines all of the rendering state associated with the HMD
+Created : February 2, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+
+
+
+#include "CAPI_HMDRenderState.h"
+
+
+namespace OVR { namespace CAPI {
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDRenderState
+
+
+HMDRenderState::HMDRenderState(ovrHmd hmd, Profile* userProfile, const OVR::HMDInfo& hmdInfo)
+ : HMD(hmd), HMDInfo(hmdInfo)
+{
+ RenderInfo = GenerateHmdRenderInfoFromHmdInfo( HMDInfo, userProfile );
+
+ Distortion[0] = CalculateDistortionRenderDesc(StereoEye_Left, RenderInfo, 0);
+ Distortion[1] = CalculateDistortionRenderDesc(StereoEye_Right, RenderInfo, 0);
+
+ ClearColor[0] = ClearColor[1] = ClearColor[2] = ClearColor[3] =0.0f;
+}
+
+HMDRenderState::~HMDRenderState()
+{
+
+}
+
+
+ovrHmdDesc HMDRenderState::GetDesc()
+{
+ ovrHmdDesc d;
+ memset(&d, 0, sizeof(d));
+
+ d.Type = ovrHmd_Other;
+
+ d.ProductName = HMDInfo.ProductName;
+ d.Manufacturer = HMDInfo.Manufacturer;
+ d.Resolution.w = HMDInfo.ResolutionInPixels.w;
+ d.Resolution.h = HMDInfo.ResolutionInPixels.h;
+ d.WindowsPos.x = HMDInfo.DesktopX;
+ d.WindowsPos.y = HMDInfo.DesktopY;
+ d.DisplayDeviceName = HMDInfo.DisplayDeviceName;
+ d.DisplayId = HMDInfo.DisplayId;
+
+ d.Caps = ovrHmdCap_YawCorrection | ovrHmdCap_Orientation | ovrHmdCap_Present;
+
+ if (strstr(HMDInfo.ProductName, "DK1"))
+ {
+ d.Type = ovrHmd_DK1;
+ }
+ else if (strstr(HMDInfo.ProductName, "DK2"))
+ {
+ d.Type = ovrHmd_DK2;
+ d.Caps |= ovrHmdCap_Position | ovrHmdCap_LowPersistence;
+ }
+
+ DistortionRenderDesc& leftDistortion = Distortion[0];
+ DistortionRenderDesc& rightDistortion = Distortion[1];
+
+ // The suggested FOV (assuming eye rotation)
+ d.DefaultEyeFov[0] = CalculateFovFromHmdInfo(StereoEye_Left, leftDistortion, RenderInfo, OVR_DEFAULT_EXTRA_EYE_ROTATION);
+ d.DefaultEyeFov[1] = CalculateFovFromHmdInfo(StereoEye_Right, rightDistortion, RenderInfo, OVR_DEFAULT_EXTRA_EYE_ROTATION);
+
+ // FOV extended across the entire screen
+ d.MaxEyeFov[0] = GetPhysicalScreenFov(StereoEye_Left, leftDistortion);
+ d.MaxEyeFov[1] = GetPhysicalScreenFov(StereoEye_Right, rightDistortion);
+
+ if (HMDInfo.Shutter.Type == HmdShutter_RollingRightToLeft)
+ {
+ d.EyeRenderOrder[0] = ovrEye_Right;
+ d.EyeRenderOrder[1] = ovrEye_Left;
+ }
+ else
+ {
+ d.EyeRenderOrder[0] = ovrEye_Left;
+ d.EyeRenderOrder[1] = ovrEye_Right;
+ }
+
+ return d;
+}
+
+
+ovrSizei HMDRenderState::GetFOVTextureSize(int eye, ovrFovPort fov, float pixelsPerDisplayPixel)
+{
+ OVR_ASSERT((unsigned)eye < 2);
+ StereoEye seye = (eye == ovrEye_Left) ? StereoEye_Left : StereoEye_Right;
+ return CalculateIdealPixelSize(seye, Distortion[eye], fov, pixelsPerDisplayPixel);
+}
+
+ovrEyeRenderDesc HMDRenderState::calcRenderDesc(const ovrEyeDesc& eyeDesc)
+{
+ HmdRenderInfo& hmdri = RenderInfo;
+ StereoEye eye = (eyeDesc.Eye == ovrEye_Left) ? StereoEye_Left : StereoEye_Right;
+ ovrEyeRenderDesc e0;
+
+ e0.Desc = eyeDesc;
+ e0.ViewAdjust = CalculateEyeVirtualCameraOffset(hmdri, eye, false);
+ e0.DistortedViewport = GetFramebufferViewport(eye, hmdri);
+ e0.PixelsPerTanAngleAtCenter = Distortion[0].PixelsPerTanAngleAtCenter;
+
+ // If RenderViewport is uninitialized, set it to texture size.
+ if (Sizei(e0.Desc.RenderViewport.Size) == Sizei(0))
+ e0.Desc.RenderViewport.Size = e0.Desc.TextureSize;
+
+ return e0;
+}
+
+
+void HMDRenderState::setupRenderDesc( ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrEyeDesc eyeDescIn[2] )
+{
+ eyeRenderDescOut[0] = EyeRenderDesc[0] = calcRenderDesc(eyeDescIn[0]);
+ eyeRenderDescOut[1] = EyeRenderDesc[1] = calcRenderDesc(eyeDescIn[1]);
+}
+
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDRenderState.h b/LibOVR/Src/CAPI/CAPI_HMDRenderState.h
new file mode 100644
index 0000000..408af51
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDRenderState.h
@@ -0,0 +1,93 @@
+/************************************************************************************
+
+Filename : CAPI_HMDRenderState.h
+Content : Combines all of the rendering state associated with the HMD
+Created : February 2, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_HMDRenderState_h
+#define OVR_CAPI_HMDRenderState_h
+
+#include "../OVR_CAPI.h"
+#include "../Kernel/OVR_Math.h"
+#include "../Util/Util_Render_Stereo.h"
+
+
+namespace OVR { namespace CAPI {
+
+using namespace OVR::Util::Render;
+
+//-------------------------------------------------------------------------------------
+// ***** HMDRenderState
+
+// Combines all of the rendering setup information about one HMD.
+
+class HMDRenderState : public NewOverrideBase
+{
+ // Quiet assignment compiler warning.
+ void operator = (const HMDRenderState&) { }
+public:
+
+ HMDRenderState(ovrHmd hmd, Profile* userProfile, const OVR::HMDInfo& hmdInfo);
+ virtual ~HMDRenderState();
+
+
+ // *** Rendering Setup
+
+ // Delegated access APIs
+ ovrHmdDesc GetDesc();
+ ovrSizei GetFOVTextureSize(int eye, ovrFovPort fov, float pixelsPerDisplayPixel);
+
+ ovrEyeRenderDesc calcRenderDesc(const ovrEyeDesc& eyeDesc);
+
+ void setupRenderDesc(ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrEyeDesc eyeDescIn[2]);
+public:
+
+ // HMDInfo shouldn't change, as its string pointers are passed out.
+ ovrHmd HMD;
+ const OVR::HMDInfo& HMDInfo;
+
+ //const char* pLastError;
+
+ HmdRenderInfo RenderInfo;
+ DistortionRenderDesc Distortion[2];
+ ovrEyeRenderDesc EyeRenderDesc[2];
+
+ // Clear color used for distortion
+ float ClearColor[4];
+
+ // Pose at which last time the eye was rendered, as submitted by EndEyeRender.
+ ovrPosef EyeRenderPoses[2];
+
+ // Capabilities passed to Configure.
+ unsigned HMDCaps;
+ unsigned DistortionCaps;
+};
+
+
+}} // namespace OVR::CAPI
+
+
+#endif // OVR_CAPI_HMDState_h
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDState.cpp b/LibOVR/Src/CAPI/CAPI_HMDState.cpp
new file mode 100644
index 0000000..156b84a
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDState.cpp
@@ -0,0 +1,774 @@
+/************************************************************************************
+
+Filename : CAPI_HMDState.cpp
+Content : State associated with a single HMD
+Created : January 24, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_HMDState.h"
+#include "CAPI_GlobalState.h"
+#include "../OVR_Profile.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** HMDState
+
+
+HMDState::HMDState(HMDDevice* device)
+ : pHMD(device), HMDInfoW(device), HMDInfo(HMDInfoW.h),
+ SensorStarted(0), SensorCreated(0), SensorCaps(0),
+ AddSensorCount(0), AddLatencyTestCount(0), AddLatencyTestDisplayCount(0),
+ RenderState(getThis(), pHMD->GetProfile(), HMDInfoW.h),
+ LastFrameTimeSeconds(0.0f), LastGetFrameTimeSeconds(0.0),
+ LatencyTestActive(false),
+ LatencyTest2Active(false)
+{
+ pLastError = 0;
+ GlobalState::pInstance->AddHMD(this);
+
+ // Should be in renderer?
+ TimeManager.Init(RenderState.RenderInfo);
+
+ EyeRenderActive[0] = false;
+ EyeRenderActive[1] = false;
+
+ LatencyTestDrawColor[0] = 0;
+ LatencyTestDrawColor[1] = 0;
+ LatencyTestDrawColor[2] = 0;
+
+ OVR_CAPI_VISION_CODE( pPoseTracker = 0; )
+
+ RenderingConfigured = false;
+ BeginFrameCalled = false;
+ BeginFrameThreadId = 0;
+ BeginFrameTimingCalled = false;
+}
+
+HMDState::HMDState(ovrHmdType hmdType)
+ : pHMD(0), HMDInfoW(hmdType), HMDInfo(HMDInfoW.h),
+ SensorStarted(0), SensorCreated(0), SensorCaps(0),
+ AddSensorCount(0), AddLatencyTestCount(0), AddLatencyTestDisplayCount(0),
+ RenderState(getThis(), 0, HMDInfoW.h), // No profile.
+ LastFrameTimeSeconds(0.0), LastGetFrameTimeSeconds(0.0)
+{
+ // TBD: We should probably be looking up the default profile for the given
+ // device type + user.
+
+ pLastError = 0;
+ GlobalState::pInstance->AddHMD(this);
+
+ // Should be in renderer?
+ TimeManager.Init(RenderState.RenderInfo);
+
+ EyeRenderActive[0] = false;
+ EyeRenderActive[1] = false;
+
+ OVR_CAPI_VISION_CODE( pPoseTracker = 0; )
+
+ RenderingConfigured = false;
+ BeginFrameCalled = false;
+ BeginFrameThreadId = 0;
+ BeginFrameTimingCalled = false;
+}
+
+
+HMDState::~HMDState()
+{
+ OVR_ASSERT(GlobalState::pInstance);
+
+ StopSensor();
+ ConfigureRendering(0,0,0,0,0);
+
+ OVR_CAPI_VISION_CODE( OVR_ASSERT(pPoseTracker == 0); )
+
+ GlobalState::pInstance->RemoveHMD(this);
+}
+
+
+//-------------------------------------------------------------------------------------
+// *** Sensor
+
+bool HMDState::StartSensor(unsigned supportedCaps, unsigned requiredCaps)
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ // TBD: Implement an optimized path that allows you to change caps such as yaw.
+ if (SensorStarted)
+ {
+
+ if ((SensorCaps ^ ovrHmdCap_LowPersistence) == supportedCaps)
+ {
+ // TBD: Fast persistance switching; redesign to make this better.
+ if (HMDInfo.HmdType == HmdType_CrystalCoveProto || HMDInfo.HmdType == HmdType_DK2)
+ {
+ // switch to full persistence
+ updateLowPersistenceMode((supportedCaps & ovrHmdCap_LowPersistence) != 0);
+ SensorCaps = supportedCaps;
+ return true;
+ }
+ }
+
+ if ((SensorCaps ^ ovrHmdCap_DynamicPrediction) == supportedCaps)
+ {
+ // TBD: Fast persistance switching; redesign to make this better.
+ if (HMDInfo.HmdType == HmdType_DK2)
+ {
+ // switch to full persistence
+ TimeManager.ResetFrameTiming(TimeManager.GetFrameTiming().FrameIndex,
+ (supportedCaps & ovrHmdCap_NoVSync) ? false : true,
+ (supportedCaps & ovrHmdCap_DynamicPrediction) ? true : false,
+ RenderingConfigured);
+ SensorCaps = supportedCaps;
+ return true;
+ }
+ }
+
+ StopSensor();
+ }
+
+ supportedCaps |= requiredCaps;
+
+ // TBD: In case of sensor not being immediately available, it would be good to check
+ // yaw config availability to match it with ovrHmdCap_YawCorrection requirement.
+ //
+
+ if (requiredCaps & ovrHmdCap_Position)
+ {
+ if (HMDInfo.HmdType != HmdType_CrystalCoveProto && HMDInfo.HmdType != HmdType_DK2)
+ {
+ pLastError = "ovrHmdCap_Position not supported on this HMD.";
+ return false;
+ }
+ }
+ if (requiredCaps & ovrHmdCap_LowPersistence)
+ {
+ if (HMDInfo.HmdType != HmdType_CrystalCoveProto && HMDInfo.HmdType != HmdType_DK2)
+ {
+ pLastError = "ovrHmdCap_LowPersistence not supported on this HMD.";
+ return false;
+ }
+ }
+
+
+ SensorCreated = false;
+ pSensor.Clear();
+ if (pHMD)
+ {
+ // Zero AddSensorCount before creation, in case it fails (or succeeds but then
+ // immediately gets disconnected) followed by another Add notification.
+ AddSensorCount = 0;
+ pSensor = *pHMD->GetSensor();
+ }
+
+ if (!pSensor)
+ {
+ if (requiredCaps & ovrHmdCap_Orientation)
+ {
+ pLastError = "Failed to create sensor.";
+ return false;
+ }
+ // Succeed, waiting for sensor become available later.
+ LogText("StartSensor succeeded - waiting for sensor.\n");
+ }
+ else
+ {
+ pSensor->SetReportRate(500);
+ SFusion.AttachToSensor(pSensor);
+ applyProfileToSensorFusion();
+
+ if (requiredCaps & ovrHmdCap_YawCorrection)
+ {
+ if (!SFusion.HasMagCalibration())
+ {
+ pLastError = "ovrHmdCap_YawCorrection not available.";
+ SFusion.AttachToSensor(0);
+ SFusion.Reset();
+ pSensor.Clear();
+ return false;
+ }
+ }
+
+ SFusion.SetYawCorrectionEnabled((supportedCaps & ovrHmdCap_YawCorrection) != 0);
+ LogText("Sensor created.\n");
+
+ if (supportedCaps & ovrHmdCap_LowPersistence)
+ {
+ updateLowPersistenceMode(true);
+ }
+ else
+ {
+ if (HMDInfo.HmdType == HmdType_CrystalCoveProto || HMDInfo.HmdType == HmdType_DK2)
+ {
+ // switch to full persistence
+ updateLowPersistenceMode(false);
+ }
+ }
+
+ if (HMDInfo.HmdType == HmdType_DK2)
+ {
+ updateLatencyTestForHmd((supportedCaps & ovrHmdCap_LatencyTest) != 0);
+ }
+
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (supportedCaps & ovrHmdCap_Position)
+ {
+ pPoseTracker = new Vision::PoseTracker(SFusion);
+ if (pPoseTracker)
+ {
+ pPoseTracker->AssociateHMD(pSensor);
+ }
+ LogText("Sensor Pose tracker created.\n");
+ }
+ // TBD: How do we verify that position tracking is actually available
+ // i.e. camera is plugged in?
+
+#endif // OVR_CAPI_VISIONSUPPORT
+
+ SensorCreated = true;
+ }
+
+ SensorCaps = supportedCaps;
+ SensorStarted = true;
+
+ return true;
+}
+
+
+// Stops sensor sampling, shutting down internal resources.
+void HMDState::StopSensor()
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ if (SensorStarted)
+ {
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (pPoseTracker)
+ {
+ // TBD: Internals not thread safe - must fix!!
+ delete pPoseTracker;
+ pPoseTracker = 0;
+ LogText("Sensor Pose tracker destroyed.\n");
+ }
+#endif // OVR_CAPI_VISION_CODE
+
+ SFusion.AttachToSensor(0);
+ SFusion.Reset();
+ pSensor.Clear();
+ AddSensorCount = 0;
+ SensorCaps = 0;
+ SensorCreated = false;
+ SensorStarted = false;
+
+ LogText("StopSensor succeeded.\n");
+ }
+}
+
+// Resets sensor orientation.
+void HMDState::ResetSensor()
+{
+ SFusion.Reset();
+}
+
+
+// Returns prediction for time.
+ovrSensorState HMDState::PredictedSensorState(double absTime)
+{
+ SensorState ss;
+
+ // We are trying to keep this path lockless unless we are notified of new device
+ // creation while not having a sensor yet. It's ok to check SensorCreated volatile
+ // flag here, since GetSensorStateAtTime() is internally lockless and safe.
+
+ if (SensorCreated || checkCreateSensor())
+ {
+ ss = SFusion.GetSensorStateAtTime(absTime);
+
+ if (!(ss.StatusFlags & ovrStatus_OrientationTracked))
+ {
+ Lock::Locker lockScope(&DevicesLock);
+
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (pPoseTracker)
+ {
+ // TBD: Internals not thread safe - must fix!!
+ delete pPoseTracker;
+ pPoseTracker = 0;
+ LogText("Sensor Pose tracker destroyed.\n");
+ }
+#endif // OVR_CAPI_VISION_CODE
+ // Not needed yet; SFusion.AttachToSensor(0);
+ // This seems to reset orientation anyway...
+ pSensor.Clear();
+ SensorCreated = false;
+ }
+ }
+ else
+ {
+ // SensorState() defaults to 0s.
+ // ss.Pose.Orientation = Quatf();
+ // ..
+
+ // John:
+ // We still want valid times so frames will get a delta-time
+ // and allow operation with a joypad when the sensor isn't
+ // connected.
+ ss.Recorded.TimeInSeconds = absTime;
+ ss.Predicted.TimeInSeconds = absTime;
+ }
+
+ ss.StatusFlags |= ovrStatus_HmdConnected;
+ return ss;
+}
+
+
+bool HMDState::checkCreateSensor()
+{
+ if (!(SensorStarted && !SensorCreated && AddSensorCount))
+ return false;
+
+ Lock::Locker lockScope(&DevicesLock);
+
+ // Re-check condition once in the lock, in case the state changed.
+ if (SensorStarted && !SensorCreated && AddSensorCount)
+ {
+ if (pHMD)
+ {
+ AddSensorCount = 0;
+ pSensor = *pHMD->GetSensor();
+ }
+
+ if (pSensor)
+ {
+ pSensor->SetReportRate(500);
+ SFusion.AttachToSensor(pSensor);
+ SFusion.SetYawCorrectionEnabled((SensorCaps & ovrHmdCap_YawCorrection) != 0);
+ applyProfileToSensorFusion();
+
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (SensorCaps & ovrHmdCap_Position)
+ {
+ pPoseTracker = new Vision::PoseTracker(SFusion);
+ if (pPoseTracker)
+ {
+ pPoseTracker->AssociateHMD(pSensor);
+ }
+ LogText("Sensor Pose tracker created.\n");
+ }
+#endif // OVR_CAPI_VISION_CODE
+
+ LogText("Sensor created.\n");
+
+ SensorCreated = true;
+ return true;
+ }
+ }
+
+ return SensorCreated;
+}
+
+bool HMDState::GetSensorDesc(ovrSensorDesc* descOut)
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ if (SensorCreated)
+ {
+ OVR_ASSERT(pSensor);
+ OVR::SensorInfo si;
+ pSensor->GetDeviceInfo(&si);
+ descOut->VendorId = si.VendorId;
+ descOut->ProductId = si.ProductId;
+ OVR_ASSERT(si.SerialNumber.GetSize() <= sizeof(descOut->SerialNumber));
+ OVR_strcpy(descOut->SerialNumber, sizeof(descOut->SerialNumber), si.SerialNumber.ToCStr());
+ return true;
+ }
+ return false;
+}
+
+
+void HMDState::applyProfileToSensorFusion()
+{
+ Profile* profile = pHMD ? pHMD->GetProfile() : 0;
+ SFusion.SetUserHeadDimensions ( profile, RenderState.RenderInfo );
+}
+
+void HMDState::updateLowPersistenceMode(bool lowPersistence) const
+{
+ OVR_ASSERT(pSensor);
+ DisplayReport dr;
+ pSensor->GetDisplayReport(&dr);
+
+ dr.Persistence = (UInt16) (dr.TotalRows * (lowPersistence ? 0.18f : 1.0f));
+ dr.Brightness = lowPersistence ? 255 : 0;
+
+ pSensor->SetDisplayReport(dr);
+}
+
+void HMDState::updateLatencyTestForHmd(bool latencyTesting)
+{
+ if (pSensor.GetPtr())
+ {
+ DisplayReport dr;
+ pSensor->GetDisplayReport(&dr);
+
+ dr.ReadPixel = latencyTesting;
+
+ pSensor->SetDisplayReport(dr);
+ }
+
+ if (latencyTesting)
+ {
+ LatencyUtil2.SetSensorDevice(pSensor.GetPtr());
+ }
+ else
+ {
+ LatencyUtil2.SetSensorDevice(NULL);
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// ***** Property Access
+
+// TBD: This all needs to be cleaned up and organized into namespaces.
+
+float HMDState::getFloatValue(const char* propertyName, float defaultVal)
+{
+ if (OVR_strcmp(propertyName, "LensSeparation") == 0)
+ {
+ return HMDInfo.LensSeparationInMeters;
+ }
+ else if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0)
+ {
+ return SFusion.GetCenterPupilDepth();
+ }
+ else if (pHMD)
+ {
+ Profile* p = pHMD->GetProfile();
+ if (p)
+ {
+ return p->GetFloatValue(propertyName, defaultVal);
+ }
+ }
+ return defaultVal;
+}
+
+bool HMDState::setFloatValue(const char* propertyName, float value)
+{
+ if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0)
+ {
+ SFusion.SetCenterPupilDepth(value);
+ return true;
+ }
+ return false;
+}
+
+
+static unsigned CopyFloatArrayWithLimit(float dest[], unsigned destSize,
+ float source[], unsigned sourceSize)
+{
+ unsigned count = Alg::Min(destSize, sourceSize);
+ for (unsigned i = 0; i < count; i++)
+ dest[i] = source[i];
+ return count;
+}
+
+
+unsigned HMDState::getFloatArray(const char* propertyName, float values[], unsigned arraySize)
+{
+ if (arraySize)
+ {
+ if (OVR_strcmp(propertyName, "ScreenSize") == 0)
+ {
+ float data[2] = { HMDInfo.ScreenSizeInMeters.w, HMDInfo.ScreenSizeInMeters.h };
+
+ return CopyFloatArrayWithLimit(values, arraySize, data, 2);
+ }
+ else if (OVR_strcmp(propertyName, "DistortionClearColor") == 0)
+ {
+ return CopyFloatArrayWithLimit(values, arraySize, RenderState.ClearColor, 4);
+ }
+ else if (OVR_strcmp(propertyName, "DK2Latency") == 0)
+ {
+ if (HMDInfo.HmdType != HmdType_DK2)
+ return 0;
+
+ float data[3];
+ TimeManager.GetLatencyTimings(data);
+
+ return CopyFloatArrayWithLimit(values, arraySize, data, 3);
+ }
+
+ /*
+ else if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0)
+ {
+ if (arraySize >= 1)
+ {
+ values[0] = SFusion.GetCenterPupilDepth();
+ return 1;
+ }
+ return 0;
+ } */
+ else if (pHMD)
+ {
+ Profile* p = pHMD->GetProfile();
+
+ // TBD: Not quite right. Should update profile interface, so that
+ // we can return 0 in all conditions if property doesn't exist.
+ if (p)
+ {
+ unsigned count = p->GetFloatValues(propertyName, values, arraySize);
+ return count;
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool HMDState::setFloatArray(const char* propertyName, float values[], unsigned arraySize)
+{
+ if (!arraySize)
+ return false;
+
+ if (OVR_strcmp(propertyName, "DistortionClearColor") == 0)
+ {
+ CopyFloatArrayWithLimit(RenderState.ClearColor, 4, values, arraySize);
+ return true;
+ }
+ return false;
+}
+
+
+const char* HMDState::getString(const char* propertyName, const char* defaultVal)
+{
+ if (pHMD)
+ {
+ // For now, just access the profile.
+ Profile* p = pHMD->GetProfile();
+
+ LastGetStringValue[0] = 0;
+ if (p && p->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue)))
+ {
+ return LastGetStringValue;
+ }
+ }
+
+ return defaultVal;
+}
+
+//-------------------------------------------------------------------------------------
+// *** Latency Test
+
+bool HMDState::ProcessLatencyTest(unsigned char rgbColorOut[3])
+{
+ bool result = false;
+
+ // Check create.
+ if (pLatencyTester)
+ {
+ if (pLatencyTester->IsConnected())
+ {
+ Color colorToDisplay;
+
+ LatencyUtil.ProcessInputs();
+ result = LatencyUtil.DisplayScreenColor(colorToDisplay);
+ rgbColorOut[0] = colorToDisplay.R;
+ rgbColorOut[1] = colorToDisplay.G;
+ rgbColorOut[2] = colorToDisplay.B;
+ }
+ else
+ {
+ // Disconnect.
+ LatencyUtil.SetDevice(NULL);
+ pLatencyTester = 0;
+ LogText("LATENCY SENSOR disconnected.\n");
+ }
+ }
+ else if (AddLatencyTestCount > 0)
+ {
+ // This might have some unlikely race condition issue which could cause us to miss a device...
+ AddLatencyTestCount = 0;
+
+ pLatencyTester = *GlobalState::pInstance->GetManager()->
+ EnumerateDevices<LatencyTestDevice>().CreateDevice();
+ if (pLatencyTester)
+ {
+ LatencyUtil.SetDevice(pLatencyTester);
+ LogText("LATENCY TESTER connected\n");
+ }
+ }
+
+ return result;
+}
+
+void HMDState::ProcessLatencyTest2(unsigned char rgbColorOut[3], double startTime)
+{
+ // Check create.
+ if (!(SensorCaps & ovrHmdCap_LatencyTest))
+ return;
+
+ if (pLatencyTesterDisplay && !LatencyUtil2.HasDisplayDevice())
+ {
+ if (!pLatencyTesterDisplay->IsConnected())
+ {
+ LatencyUtil2.SetDisplayDevice(NULL);
+ }
+ }
+ else if (AddLatencyTestDisplayCount > 0)
+ {
+ // This might have some unlikely race condition issue
+ // which could cause us to miss a device...
+ AddLatencyTestDisplayCount = 0;
+
+ pLatencyTesterDisplay = *GlobalState::pInstance->GetManager()->
+ EnumerateDevices<LatencyTestDevice>().CreateDevice();
+ if (pLatencyTesterDisplay)
+ {
+ LatencyUtil2.SetDisplayDevice(pLatencyTesterDisplay);
+ }
+ }
+
+ if (LatencyUtil2.HasDevice() && pSensor && pSensor->IsConnected())
+ {
+ LatencyUtil2.BeginTest(startTime);
+
+ Color colorToDisplay;
+ LatencyTest2Active = LatencyUtil2.DisplayScreenColor(colorToDisplay);
+ rgbColorOut[0] = colorToDisplay.R;
+ rgbColorOut[1] = colorToDisplay.G;
+ rgbColorOut[2] = colorToDisplay.B;
+ }
+ else
+ {
+ LatencyTest2Active = false;
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// *** Rendering
+
+bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrEyeDesc eyeDescIn[2],
+ const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps,
+ unsigned distortionCaps)
+{
+ ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_ConfigureRendering");
+
+ // null -> shut down.
+ if (!apiConfig)
+ {
+ if (pRenderer)
+ pRenderer.Clear();
+ RenderingConfigured = false;
+ return true;
+ }
+
+ if (pRenderer &&
+ (apiConfig->Header.API != pRenderer->GetRenderAPI()))
+ {
+ // Shutdown old renderer.
+ if (pRenderer)
+ pRenderer.Clear();
+ }
+
+
+ // Step 1: do basic setup configuration
+ RenderState.setupRenderDesc(eyeRenderDescOut, eyeDescIn);
+ RenderState.HMDCaps = hmdCaps; // Any cleaner way?
+ RenderState.DistortionCaps = distortionCaps;
+
+ TimeManager.ResetFrameTiming(0,
+ (hmdCaps & ovrHmdCap_NoVSync) ? false : true,
+ (hmdCaps & ovrHmdCap_DynamicPrediction) ? true : false,
+ true);
+
+ LastFrameTimeSeconds = 0.0f;
+
+ // Set RenderingConfigured early to avoid ASSERTs in renderer initialization.
+ RenderingConfigured = true;
+
+ if (!pRenderer)
+ {
+ pRenderer = *DistortionRenderer::APICreateRegistry
+ [apiConfig->Header.API](this, TimeManager, RenderState);
+ }
+
+ if (!pRenderer ||
+ !pRenderer->Initialize(apiConfig, hmdCaps, distortionCaps))
+ {
+ RenderingConfigured = false;
+ return false;
+ }
+
+ return true;
+}
+
+
+
+ovrPosef HMDState::BeginEyeRender(ovrEyeType eye)
+{
+ // Debug checks.
+ checkBeginFrameScope("ovrHmd_BeginEyeRender");
+ ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_BeginEyeRender");
+
+ // Unknown eyeId provided in ovrHmd_BeginEyeRender
+ OVR_ASSERT_LOG(eye == ovrEye_Left || eye == ovrEye_Right,
+ ("ovrHmd_BeginEyeRender eyeId out of range."));
+ OVR_ASSERT_LOG(EyeRenderActive[eye] == false,
+ ("Multiple calls to ovrHmd_BeginEyeRender for the same eye."));
+
+ EyeRenderActive[eye] = true;
+
+ // Only process latency tester for drawing the left eye (assumes left eye is drawn first)
+ if (pRenderer && eye == 0)
+ {
+ LatencyTestActive = ProcessLatencyTest(LatencyTestDrawColor);
+ }
+
+ return ovrHmd_GetEyePose(this, eye);
+}
+
+
+void HMDState::EndEyeRender(ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture)
+{
+ // Debug checks.
+ checkBeginFrameScope("ovrHmd_EndEyeRender");
+ ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_EndEyeRender");
+
+ if (!EyeRenderActive[eye])
+ {
+ OVR_ASSERT_LOG(false,
+ ("ovrHmd_EndEyeRender called without ovrHmd_BeginEyeRender."));
+ return;
+ }
+
+ RenderState.EyeRenderPoses[eye] = renderPose;
+
+ if (pRenderer)
+ pRenderer->SubmitEye(eye, eyeTexture);
+
+ EyeRenderActive[eye] = false;
+}
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDState.h b/LibOVR/Src/CAPI/CAPI_HMDState.h
new file mode 100644
index 0000000..d178042
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDState.h
@@ -0,0 +1,334 @@
+/************************************************************************************
+
+Filename : CAPI_HMDState.h
+Content : State associated with a single HMD
+Created : January 24, 2014
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_HMDState_h
+#define OVR_CAPI_HMDState_h
+
+#include "../Kernel/OVR_Math.h"
+#include "../Kernel/OVR_List.h"
+#include "../Kernel/OVR_Log.h"
+#include "../OVR_CAPI.h"
+#include "../OVR_SensorFusion.h"
+#include "../Util/Util_LatencyTest.h"
+#include "../Util/Util_LatencyTest2.h"
+
+#include "CAPI_FrameTimeManager.h"
+#include "CAPI_HMDRenderState.h"
+#include "CAPI_DistortionRenderer.h"
+
+// Define OVR_CAPI_VISIONSUPPORT to compile in vision support
+#ifdef OVR_CAPI_VISIONSUPPORT
+ #define OVR_CAPI_VISION_CODE(c) c
+ #include "../Vision/Vision_PoseTracker.h"
+#else
+ #define OVR_CAPI_VISION_CODE(c)
+#endif
+
+
+struct ovrHmdStruct { };
+
+namespace OVR { namespace CAPI {
+
+using namespace OVR::Util::Render;
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadChecker
+
+// This helper class is used to verify that the API is used according to supported
+// thread safety constraints (is not re-entrant for this and related functions).
+class ThreadChecker
+{
+public:
+
+#ifndef OVR_BUILD_DEBUG
+
+ // In release build, thread checks are disabled.
+ ThreadChecker() { }
+ void Begin(const char* functionName) { OVR_UNUSED1(functionName); }
+ void End() { }
+
+ // Add thread-re-entrancy check for function scope
+ struct Scope
+ {
+ Scope(ThreadChecker*, const char *) { }
+ ~Scope() { }
+ };
+
+
+#else // OVR_BUILD_DEBUG
+ ThreadChecker() : pFunctionName(0), FirstThread(0)
+ { }
+
+ void Begin(const char* functionName)
+ {
+ if (!pFunctionName)
+ {
+ pFunctionName = functionName;
+ FirstThread = GetCurrentThreadId();
+ }
+ else
+ {
+ // pFunctionName may be not null here if function is called internally on the same thread.
+ OVR_ASSERT_LOG((FirstThread == GetCurrentThreadId()),
+ ("%s (threadId=%d) called at the same times as %s (threadId=%d)\n",
+ functionName, GetCurrentThreadId(), pFunctionName, FirstThread) );
+ }
+ }
+ void End()
+ {
+ pFunctionName = 0;
+ FirstThread = 0;
+ }
+
+ // Add thread-re-entrancy check for function scope.
+ struct Scope
+ {
+ Scope(ThreadChecker* threadChecker, const char *functionName) : pChecker(threadChecker)
+ { pChecker->Begin(functionName); }
+ ~Scope()
+ { pChecker->End(); }
+ private:
+ ThreadChecker* pChecker;
+ };
+
+private:
+ // If not 0, contains the name of the function that first entered the scope.
+ const char * pFunctionName;
+ ThreadId FirstThread;
+
+#endif // OVR_BUILD_DEBUG
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDState
+
+// Describes a single HMD.
+class HMDState : public ListNode<HMDState>,
+ public ovrHmdStruct, public NewOverrideBase
+{
+public:
+
+ HMDState(HMDDevice* device);
+ HMDState(ovrHmdType hmdType);
+ virtual ~HMDState();
+
+
+ // *** Sensor Setup
+
+ bool StartSensor(unsigned supportedCaps, unsigned requiredCaps);
+ void StopSensor();
+ void ResetSensor();
+ ovrSensorState PredictedSensorState(double absTime);
+ bool GetSensorDesc(ovrSensorDesc* descOut);
+
+ bool ProcessLatencyTest(unsigned char rgbColorOut[3]);
+ void ProcessLatencyTest2(unsigned char rgbColorOut[3], double startTime);
+
+
+ // *** Rendering Setup
+
+ bool ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrEyeDesc eyeDescIn[2],
+ const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps,
+ unsigned distortionCaps);
+
+ ovrPosef BeginEyeRender(ovrEyeType eye);
+ void EndEyeRender(ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture);
+
+
+ const char* GetLastError()
+ {
+ const char* p = pLastError;
+ pLastError = 0;
+ return p;
+ }
+
+ void NotifyAddDevice(DeviceType deviceType)
+ {
+ if (deviceType == Device_Sensor)
+ AddSensorCount++;
+ else if (deviceType == Device_LatencyTester)
+ {
+ AddLatencyTestCount++;
+ AddLatencyTestDisplayCount++;
+ }
+ }
+
+ bool checkCreateSensor();
+
+ void applyProfileToSensorFusion();
+
+ // INlines so that they can be easily compiled out.
+ // Does debug ASSERT checks for functions that require BeginFrame.
+ // Also verifies that we are on the right thread.
+ void checkBeginFrameScope(const char* functionName)
+ {
+ OVR_UNUSED1(functionName); // for Release build.
+ OVR_ASSERT_LOG(BeginFrameCalled == true,
+ ("%s called outside ovrHmd_BeginFrame."));
+ OVR_ASSERT_LOG(BeginFrameThreadId == OVR::GetCurrentThreadId(),
+ ("%s called on a different thread then ovrHmd_BeginFrame."));
+ }
+
+ void checkRenderingConfigured(const char* functionName)
+ {
+ OVR_UNUSED1(functionName); // for Release build.
+ OVR_ASSERT_LOG(RenderingConfigured == true,
+ ("%s called without ovrHmd_ConfigureRendering."));
+ }
+
+ void checkBeginFrameTimingScope(const char* functionName)
+ {
+ OVR_UNUSED1(functionName); // for Release build.
+ OVR_ASSERT_LOG(BeginFrameTimingCalled == true,
+ ("%s called outside ovrHmd_BeginFrameTiming."));
+ }
+
+
+ HMDState* getThis() { return this; }
+
+ void updateLowPersistenceMode(bool lowPersistence) const;
+ void updateLatencyTestForHmd(bool latencyTesting);
+
+ // Get properties by name.
+ float getFloatValue(const char* propertyName, float defaultVal);
+ bool setFloatValue(const char* propertyName, float value);
+ unsigned getFloatArray(const char* propertyName, float values[], unsigned arraySize);
+ bool setFloatArray(const char* propertyName, float values[], unsigned arraySize);
+ const char* getString(const char* propertyName, const char* defaultVal);
+public:
+
+ // Wrapper to support 'const'
+ struct HMDInfoWrapper
+ {
+ HMDInfoWrapper(ovrHmdType hmdType)
+ {
+ HmdTypeEnum t = HmdType_None;
+ if (hmdType == ovrHmd_DK1)
+ t = HmdType_DK1;
+ else if (hmdType == ovrHmd_CrystalCoveProto)
+ t = HmdType_CrystalCoveProto;
+ else if (hmdType == ovrHmd_DK2)
+ t = HmdType_DK2;
+ h = CreateDebugHMDInfo(t);
+ }
+ HMDInfoWrapper(HMDDevice* device) { if (device) device->GetDeviceInfo(&h); }
+ OVR::HMDInfo h;
+ };
+
+ // Note: pHMD can be null if we are representing a virtualized debug HMD.
+ Ptr<HMDDevice> pHMD;
+
+ // HMDInfo shouldn't change, as its string pointers are passed out.
+ const HMDInfoWrapper HMDInfoW;
+ const OVR::HMDInfo& HMDInfo;
+
+ const char* pLastError;
+
+
+ // *** Sensor
+
+ // Lock used to support thread-safe lifetime access to sensor.
+ Lock DevicesLock;
+
+ // Atomic integer used as a flag that we should check the sensor device.
+ AtomicInt<int> AddSensorCount;
+
+ // All of Sensor variables may be modified/used with DevicesLock, with exception that
+ // the {SensorStarted, SensorCreated} can be read outside the lock to see
+ // if device creation check is necessary.
+ // Whether we called StartSensor() and requested sensor caps.
+ volatile bool SensorStarted;
+ volatile bool SensorCreated;
+ // pSensor may still be null or non-running after start if it wasn't yet available
+ Ptr<SensorDevice> pSensor; // Head
+ unsigned SensorCaps;
+
+ // SensorFusion state may be accessible without a lock.
+ SensorFusion SFusion;
+
+
+ // Vision pose tracker is currently new-allocated
+ OVR_CAPI_VISION_CODE(
+ Vision::PoseTracker* pPoseTracker;
+ )
+
+ // Latency tester
+ Ptr<LatencyTestDevice> pLatencyTester;
+ Util::LatencyTest LatencyUtil;
+ AtomicInt<int> AddLatencyTestCount;
+
+ bool LatencyTestActive;
+ unsigned char LatencyTestDrawColor[3];
+
+ // Using latency tester as debug display
+ Ptr<LatencyTestDevice> pLatencyTesterDisplay;
+ AtomicInt<int> AddLatencyTestDisplayCount;
+ Util::LatencyTest2 LatencyUtil2;
+
+ bool LatencyTest2Active;
+ unsigned char LatencyTest2DrawColor[3];
+ //bool ReadbackColor;
+
+ // Rendering part
+ FrameTimeManager TimeManager;
+ HMDRenderState RenderState;
+ Ptr<DistortionRenderer> pRenderer;
+
+ // Last timing value reported by BeginFrame.
+ double LastFrameTimeSeconds;
+ // Last timing value reported by GetFrameTime. These are separate since the intended
+ // use is from different threads. TBD: Move to FrameTimeManager? Make atomic?
+ double LastGetFrameTimeSeconds;
+
+ // Last cached value returned by ovrHmd_GetString/ovrHmd_GetStringArray.
+ char LastGetStringValue[256];
+
+
+ // Debug flag set after ovrHmd_ConfigureRendering succeeds.
+ bool RenderingConfigured;
+ // Set after BeginFrame succeeds, and its corresponding thread id for debug checks.
+ bool BeginFrameCalled;
+ ThreadId BeginFrameThreadId;
+ // Graphics functions are not re-entrant from other threads.
+ ThreadChecker RenderAPIThreadChecker;
+ //
+ bool BeginFrameTimingCalled;
+
+ // Flags set when we've called BeginEyeRender on a given eye.
+ bool EyeRenderActive[2];
+};
+
+
+}} // namespace OVR::CAPI
+
+
+#endif // OVR_CAPI_HMDState_h
+
+
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.cpp b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.cpp
new file mode 100644
index 0000000..d1ea4dc
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.cpp
@@ -0,0 +1,29 @@
+/************************************************************************************
+
+Filename : CAPI_D3D10_DistortionRenderer.cpp
+Content : Distortion renderer instantiation for D3D10
+Created : November 11, 2013
+Authors : Volga Aksoy, Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#define OVR_D3D_VERSION 10
+#include "CAPI_D3D1X_Util.cpp"
+#include "CAPI_D3D1X_DistortionRenderer.cpp"
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.h b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.h
new file mode 100644
index 0000000..bb56cb4
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.h
@@ -0,0 +1,34 @@
+/************************************************************************************
+
+Filename : CAPI_D3D10_DistortionRenderer.h
+Content : Distortion renderer header for D3D10
+Created : November 11, 2013
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef INC_CAPI_D3D10_DistortionRenderer_h
+#define INC_CAPI_D3D10_DistortionRenderer_h
+
+#define OVR_D3D_VERSION 10
+#include "CAPI_D3D1X_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#endif \ No newline at end of file
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.cpp b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.cpp
new file mode 100644
index 0000000..1184df8
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.cpp
@@ -0,0 +1,30 @@
+/************************************************************************************
+
+Filename : CAPI_D3D11_DistortionRenderer.cpp
+Content : Distortion renderer instantiation for D3D11
+Created : November 11, 2013
+Authors : Volga Aksoy, Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#define OVR_D3D_VERSION 11
+#include "CAPI_D3D1X_Util.cpp"
+#include "CAPI_D3D1X_DistortionRenderer.cpp"
+
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.h b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.h
new file mode 100644
index 0000000..8b9863b
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.h
@@ -0,0 +1,34 @@
+/************************************************************************************
+
+Filename : CAPI_D3D11_DistortionRenderer.h
+Content : Distortion renderer header for D3D11
+Created : November 11, 2013
+Authors : Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef INC_CAPI_D3D11_DistortionRenderer_h
+#define INC_CAPI_D3D11_DistortionRenderer_h
+
+#define OVR_D3D_VERSION 11
+#include "CAPI_D3D1X_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#endif \ No newline at end of file
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.cpp b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.cpp
new file mode 100644
index 0000000..53f8948
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.cpp
@@ -0,0 +1,773 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_DistortionRenderer.cpp
+Content : Experimental distortion renderer
+Created : November 11, 2013
+Authors : Volga Aksoy, Michael Antonov
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_D3D1X_DistortionRenderer.h"
+
+#include "../../OVR_CAPI_D3D.h"
+
+namespace OVR { namespace CAPI { namespace D3D_NS {
+
+#include "../Shaders/Distortion_vs.h"
+#include "../Shaders/Distortion_vs_refl.h"
+#include "../Shaders/Distortion_ps.h"
+#include "../Shaders/Distortion_ps_refl.h"
+#include "../Shaders/DistortionChroma_vs.h"
+#include "../Shaders/DistortionChroma_vs_refl.h"
+#include "../Shaders/DistortionChroma_ps.h"
+#include "../Shaders/DistortionChroma_ps_refl.h"
+#include "../Shaders/DistortionTimewarp_vs.h"
+#include "../Shaders/DistortionTimewarp_vs_refl.h"
+#include "../Shaders/DistortionTimewarpChroma_vs.h"
+#include "../Shaders/DistortionTimewarpChroma_vs_refl.h"
+
+#include "../Shaders/SimpleQuad_vs.h"
+#include "../Shaders/SimpleQuad_vs_refl.h"
+#include "../Shaders/SimpleQuad_ps.h"
+#include "../Shaders/SimpleQuad_ps_refl.h"
+
+// Distortion pixel shader lookup.
+// Bit 0: Chroma Correction
+// Bit 1: Timewarp
+
+enum {
+ DistortionVertexShaderBitMask = 3,
+ DistortionVertexShaderCount = DistortionVertexShaderBitMask + 1,
+ DistortionPixelShaderBitMask = 1,
+ DistortionPixelShaderCount = DistortionPixelShaderBitMask + 1
+};
+
+struct PrecompiledShader
+{
+ const unsigned char* ShaderData;
+ size_t ShaderSize;
+ const ShaderBase::Uniform* ReflectionData;
+ size_t ReflectionSize;
+};
+
+// Do add a new distortion shader use these macros (with or w/o reflection)
+#define PCS_NOREFL(shader) { shader, sizeof(shader), NULL, 0 }
+#define PCS_REFL__(shader) { shader, sizeof(shader), shader ## _refl, sizeof( shader ## _refl )/sizeof(*(shader ## _refl)) }
+
+
+static PrecompiledShader DistortionVertexShaderLookup[DistortionVertexShaderCount] =
+{
+ PCS_REFL__(Distortion_vs),
+ PCS_REFL__(DistortionChroma_vs),
+ PCS_REFL__(DistortionTimewarp_vs),
+ PCS_REFL__(DistortionTimewarpChroma_vs),
+};
+
+static PrecompiledShader DistortionPixelShaderLookup[DistortionPixelShaderCount] =
+{
+ PCS_NOREFL(Distortion_ps),
+ PCS_NOREFL(DistortionChroma_ps)
+};
+
+void DistortionShaderBitIndexCheck()
+{
+ OVR_COMPILER_ASSERT(ovrDistortion_Chromatic == 1);
+ OVR_COMPILER_ASSERT(ovrDistortion_TimeWarp == 2);
+}
+
+
+
+struct DistortionVertex
+{
+ Vector2f Pos;
+ Vector2f TexR;
+ Vector2f TexG;
+ Vector2f TexB;
+ Color Col;
+};
+
+
+// Vertex type; same format is used for all shapes for simplicity.
+// Shapes are built by adding vertices to Model.
+struct Vertex
+{
+ Vector3f Pos;
+ Color C;
+ float U, V;
+ Vector3f Norm;
+
+ Vertex (const Vector3f& p, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0, Vector3f n = Vector3f(1,0,0))
+ : Pos(p), C(c), U(u), V(v), Norm(n)
+ {}
+ Vertex(float x, float y, float z, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0) : Pos(x,y,z), C(c), U(u), V(v)
+ { }
+
+ bool operator==(const Vertex& b) const
+ {
+ return Pos == b.Pos && C == b.C && U == b.U && V == b.V;
+ }
+};
+
+
+//----------------------------------------------------------------------------
+// ***** D3D1X::DistortionRenderer
+
+DistortionRenderer::DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+ : CAPI::DistortionRenderer(ovrRenderAPI_D3D11, hmd, timeManager, renderState)
+{
+}
+
+DistortionRenderer::~DistortionRenderer()
+{
+ destroy();
+}
+
+// static
+CAPI::DistortionRenderer* DistortionRenderer::Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+{
+ return new DistortionRenderer(hmd, timeManager, renderState);
+}
+
+
+bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned distortionCaps)
+{
+ // TBD: Decide if hmdCaps are needed here or are a part of RenderState
+ OVR_UNUSED(hmdCaps);
+
+ const ovrD3D1X(Config)* config = (const ovrD3D1X(Config)*)apiConfig;
+
+ if (!config)
+ {
+ // Cleanup
+ pEyeTextures[0].Clear();
+ pEyeTextures[1].Clear();
+ memset(&RParams, 0, sizeof(RParams));
+ return true;
+ }
+
+ if (!config->D3D_NS.pDevice || !config->D3D_NS.pBackBufferRT)
+ return false;
+
+ RParams.pDevice = config->D3D_NS.pDevice;
+ RParams.pContext = D3DSELECT_10_11(config->D3D_NS.pDevice, config->D3D_NS.pDeviceContext);
+ RParams.pBackBufferRT = config->D3D_NS.pBackBufferRT;
+ RParams.pSwapChain = config->D3D_NS.pSwapChain;
+ RParams.RTSize = config->D3D_NS.Header.RTSize;
+ RParams.Multisample = config->D3D_NS.Header.Multisample;
+
+ DistortionCaps = distortionCaps;
+
+ //DistortionWarper.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true);
+
+ pEyeTextures[0] = *new Texture(&RParams, Texture_RGBA, Sizei(0),
+ getSamplerState(Sample_Linear|Sample_ClampBorder));
+ pEyeTextures[1] = *new Texture(&RParams, Texture_RGBA, Sizei(0),
+ getSamplerState(Sample_Linear|Sample_ClampBorder));
+
+ initBuffersAndShaders();
+
+ // Rasterizer state
+ D3D1X_(RASTERIZER_DESC) rs;
+ memset(&rs, 0, sizeof(rs));
+ rs.AntialiasedLineEnable = true;
+ rs.CullMode = D3D1X_(CULL_BACK);
+ rs.DepthClipEnable = true;
+ rs.FillMode = D3D1X_(FILL_SOLID);
+ RParams.pDevice->CreateRasterizerState(&rs, &Rasterizer.GetRawRef());
+
+ // TBD: Blend state.. not used?
+ // We'll want to turn off blending
+
+#if (OVR_D3D_VERSION == 11)
+ GpuProfiler.Init(RParams.pDevice, RParams.pContext);
+#endif
+
+ return true;
+}
+
+
+void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture)
+{
+ const ovrD3D1X(Texture)* tex = (const ovrD3D1X(Texture)*)eyeTexture;
+
+ if (eyeTexture)
+ {
+ // Use tex->D3D_NS.Header.RenderViewport to update UVs for rendering in case they changed.
+ // TBD: This may be optimized through some caching.
+ ovrEyeDesc ed = RState.EyeRenderDesc[eyeId].Desc;
+ ed.TextureSize = tex->D3D_NS.Header.TextureSize;
+ ed.RenderViewport = tex->D3D_NS.Header.RenderViewport;
+
+ ovrHmd_GetRenderScaleAndOffset(HMD, ed, DistortionCaps, UVScaleOffset[eyeId]);
+
+ pEyeTextures[eyeId]->UpdatePlaceholderTexture(tex->D3D_NS.pTexture, tex->D3D_NS.pSRView,
+ tex->D3D_NS.Header.TextureSize);
+ }
+}
+
+void DistortionRenderer::EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor,
+ unsigned char* latencyTester2DrawColor)
+{
+
+#if 0
+
+ // MA: This causes orientation and positional stutter!! NOT USABLE.
+ if (!TimeManager.NeedDistortionTimeMeasurement() &&
+ (RState.DistortionCaps & ovrDistortion_TimeWarp))
+ {
+ // Wait for timewarp distortion if it is time
+ FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime);
+ }
+
+ // Always measure distortion time so that TimeManager can better
+ // estimate latency-reducing time-warp wait timing.
+ {
+ GpuProfiler.BeginQuery();
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+
+ GpuProfiler.EndQuery();
+ TimeManager.AddDistortionTimeMeasurement(GpuProfiler.GetTiming(false));
+ }
+#else
+
+ if (!TimeManager.NeedDistortionTimeMeasurement())
+ {
+ if (RState.DistortionCaps & ovrDistortion_TimeWarp)
+ {
+ // Wait for timewarp distortion if it is time and Gpu idle
+ FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime);
+ }
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+ }
+ else
+ {
+ // If needed, measure distortion time so that TimeManager can better estimate
+ // latency-reducing time-warp wait timing.
+ WaitUntilGpuIdle();
+ double distortionStartTime = ovr_GetTimeInSeconds();
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+
+ WaitUntilGpuIdle();
+ TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime);
+ }
+#endif
+
+ if(latencyTesterDrawColor)
+ {
+ renderLatencyQuad(latencyTesterDrawColor);
+ }
+ else if(latencyTester2DrawColor)
+ {
+ renderLatencyPixel(latencyTester2DrawColor);
+ }
+
+ if (swapBuffers)
+ {
+ if (RParams.pSwapChain)
+ {
+ UINT swapInterval = (RState.HMDCaps & ovrHmdCap_NoVSync) ? 0 : 1;
+ RParams.pSwapChain->Present(swapInterval, 0);
+
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ // It's critical that this flush is *after* present.
+ WaitUntilGpuIdle();
+ }
+ else
+ {
+ // TBD: Generate error - swapbuffer option used with null swapchain.
+ }
+ }
+}
+
+
+void DistortionRenderer::WaitUntilGpuIdle()
+{
+ // Flush and Stall CPU while waiting for GPU to complete rendering all of the queued draw calls
+ D3D1x_QUERY_DESC queryDesc = { D3D1X_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+
+ if (RParams.pDevice->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+ D3DSELECT_10_11(query->End(),
+ RParams.pContext->End(query));
+
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Exit on failure to avoid infinite loop.
+ do { }
+ while(!done &&
+ !FAILED(D3DSELECT_10_11(query->GetData(&done, sizeof(BOOL), 0),
+ RParams.pContext->GetData(query, &done, sizeof(BOOL), 0)))
+ );
+ }
+}
+
+double DistortionRenderer::FlushGpuAndWaitTillTime(double absTime)
+{
+ double initialTime = ovr_GetTimeInSeconds();
+ if (initialTime >= absTime)
+ return 0.0;
+
+ // Flush and Stall CPU while waiting for GPU to complete rendering all of the queued draw calls
+ D3D1x_QUERY_DESC queryDesc = { D3D1X_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+ bool callGetData = false;
+
+ if (RParams.pDevice->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+ D3DSELECT_10_11(query->End(),
+ RParams.pContext->End(query));
+ callGetData = true;
+ }
+
+ double newTime = initialTime;
+ volatile int i;
+
+ while (newTime < absTime)
+ {
+ if (callGetData)
+ {
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Stop calling GetData on failure.
+ callGetData = !FAILED(D3DSELECT_10_11(query->GetData(&done, sizeof(BOOL), 0),
+ RParams.pContext->GetData(query, &done, sizeof(BOOL), 0))) && !done;
+ }
+ else
+ {
+ for (int j = 0; j < 50; j++)
+ i = 0;
+ }
+ newTime = ovr_GetTimeInSeconds();
+ }
+
+ // How long we waited
+ return newTime - initialTime;
+}
+
+void DistortionRenderer::initBuffersAndShaders()
+{
+ for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
+ {
+ // Allocate & generate distortion mesh vertices.
+ ovrDistortionMesh meshData;
+
+// double startT = ovr_GetTimeInSeconds();
+
+ if (!ovrHmd_CreateDistortionMesh( HMD, RState.EyeRenderDesc[eyeNum].Desc,
+ RState.DistortionCaps,
+ UVScaleOffset[eyeNum], &meshData) )
+ {
+ OVR_ASSERT(false);
+ continue;
+ }
+
+// double deltaT = ovr_GetTimeInSeconds() - startT;
+// LogText("GenerateDistortion time = %f\n", deltaT);
+
+ // Now parse the vertex data and create a render ready vertex buffer from it
+ DistortionVertex * pVBVerts = (DistortionVertex*)OVR_ALLOC ( sizeof(DistortionVertex) * meshData.VertexCount );
+ DistortionVertex * pCurVBVert = pVBVerts;
+ ovrDistortionVertex* pCurOvrVert = meshData.pVertexData;
+
+ for ( unsigned vertNum = 0; vertNum < meshData.VertexCount; vertNum++ )
+ {
+ pCurVBVert->Pos.x = pCurOvrVert->Pos.x;
+ pCurVBVert->Pos.y = pCurOvrVert->Pos.y;
+ pCurVBVert->TexR = (*(Vector2f*)&pCurOvrVert->TexR);
+ pCurVBVert->TexG = (*(Vector2f*)&pCurOvrVert->TexG);
+ pCurVBVert->TexB = (*(Vector2f*)&pCurOvrVert->TexB);
+ // Convert [0.0f,1.0f] to [0,255]
+ pCurVBVert->Col.R = (OVR::UByte)( pCurOvrVert->VignetteFactor * 255.99f );
+ pCurVBVert->Col.G = pCurVBVert->Col.R;
+ pCurVBVert->Col.B = pCurVBVert->Col.R;
+ pCurVBVert->Col.A = (OVR::UByte)( pCurOvrVert->TimeWarpFactor * 255.99f );;
+ pCurOvrVert++;
+ pCurVBVert++;
+ }
+
+ DistortionMeshVBs[eyeNum] = *new Buffer(&RParams);
+ DistortionMeshVBs[eyeNum]->Data ( Buffer_Vertex, pVBVerts, sizeof(DistortionVertex) * meshData.VertexCount );
+ DistortionMeshIBs[eyeNum] = *new Buffer(&RParams);
+ DistortionMeshIBs[eyeNum]->Data ( Buffer_Index, meshData.pIndexData, ( sizeof(INT16) * meshData.IndexCount ) );
+
+ OVR_FREE ( pVBVerts );
+ ovrHmd_DestroyDistortionMesh( &meshData );
+ }
+
+ // Uniform buffers
+ for(int i = 0; i < Shader_Count; i++)
+ {
+ UniformBuffers[i] = *new Buffer(&RParams);
+ //MaxTextureSet[i] = 0;
+ }
+
+ initShaders();
+}
+
+void DistortionRenderer::renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture)
+{
+ RParams.pContext->RSSetState(Rasterizer);
+
+ RParams.pContext->OMSetRenderTargets(1, &RParams.pBackBufferRT, 0);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ // Not affected by viewport.
+ RParams.pContext->ClearRenderTargetView(RParams.pBackBufferRT, RState.ClearColor);
+
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ ShaderFill distortionShaderFill(DistortionShader);
+ distortionShaderFill.SetTexture(0, eyeNum == 0 ? leftEyeTexture : rightEyeTexture);
+ distortionShaderFill.SetInputLayout(DistortionVertexIL);
+
+ DistortionShader->SetUniform2f("EyeToSourceUVScale", UVScaleOffset[eyeNum][0].x, UVScaleOffset[eyeNum][0].y);
+ DistortionShader->SetUniform2f("EyeToSourceUVOffset", UVScaleOffset[eyeNum][1].x, UVScaleOffset[eyeNum][1].y);
+
+ if (DistortionCaps & ovrDistortion_TimeWarp)
+ {
+ ovrMatrix4f timeWarpMatrices[2];
+ ovrHmd_GetEyeTimewarpMatrices(HMD, (ovrEyeType)eyeNum,
+ RState.EyeRenderPoses[eyeNum], timeWarpMatrices);
+
+ // Feed identity like matrices in until we get proper timewarp calculation going on
+ DistortionShader->SetUniform4x4f("EyeRotationStart", Matrix4f(timeWarpMatrices[0]));
+ DistortionShader->SetUniform4x4f("EyeRotationEnd", Matrix4f(timeWarpMatrices[1]));
+
+ renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum],
+ NULL, 0, (int)DistortionMeshVBs[eyeNum]->GetSize(), Prim_Triangles);
+ }
+ else
+ {
+ renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum],
+ NULL, 0, (int)DistortionMeshVBs[eyeNum]->GetSize(), Prim_Triangles);
+ }
+ }
+}
+
+void DistortionRenderer::createDrawQuad()
+{
+ const int numQuadVerts = 4;
+ LatencyTesterQuadVB = *new Buffer(&RParams);
+ if(!LatencyTesterQuadVB)
+ {
+ return;
+ }
+
+ LatencyTesterQuadVB->Data(Buffer_Vertex, NULL, numQuadVerts * sizeof(Vertex));
+ Vertex* vertices = (Vertex*)LatencyTesterQuadVB->Map(0, numQuadVerts * sizeof(Vertex), Map_Discard);
+ if(!vertices)
+ {
+ OVR_ASSERT(false); // failed to lock vertex buffer
+ return;
+ }
+
+ const float left = -1.0f;
+ const float top = -1.0f;
+ const float right = 1.0f;
+ const float bottom = 1.0f;
+
+ vertices[0] = Vertex(Vector3f(left, top, 0.0f), Color(255, 255, 255, 255));
+ vertices[1] = Vertex(Vector3f(left, bottom, 0.0f), Color(255, 255, 255, 255));
+ vertices[2] = Vertex(Vector3f(right, top, 0.0f), Color(255, 255, 255, 255));
+ vertices[3] = Vertex(Vector3f(right, bottom, 0.0f), Color(255, 255, 255, 255));
+
+ LatencyTesterQuadVB->Unmap(vertices);
+}
+
+void DistortionRenderer::renderLatencyQuad(unsigned char* latencyTesterDrawColor)
+{
+ const int numQuadVerts = 4;
+
+ if(!LatencyTesterQuadVB)
+ {
+ createDrawQuad();
+ }
+
+ ShaderFill quadFill(SimpleQuadShader);
+ quadFill.SetInputLayout(SimpleQuadVertexIL);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ SimpleQuadShader->SetUniform2f("Scale", 0.2f, 0.2f);
+ SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterDrawColor[0] / 255.99f,
+ (float)latencyTesterDrawColor[0] / 255.99f,
+ (float)latencyTesterDrawColor[0] / 255.99f,
+ 1.0f);
+
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ SimpleQuadShader->SetUniform2f("PositionOffset", eyeNum == 0 ? -0.4f : 0.4f, 0.0f);
+ renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, NULL, 0, numQuadVerts, Prim_TriangleStrip);
+ }
+}
+
+void DistortionRenderer::renderLatencyPixel(unsigned char* latencyTesterPixelColor)
+{
+ const int numQuadVerts = 4;
+
+ if(!LatencyTesterQuadVB)
+ {
+ createDrawQuad();
+ }
+
+ ShaderFill quadFill(SimpleQuadShader);
+ quadFill.SetInputLayout(SimpleQuadVertexIL);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterPixelColor[0] / 255.99f,
+ (float)latencyTesterPixelColor[0] / 255.99f,
+ (float)latencyTesterPixelColor[0] / 255.99f,
+ 1.0f);
+
+ Vector2f scale(2.0f / RParams.RTSize.w, 2.0f / RParams.RTSize.h);
+ SimpleQuadShader->SetUniform2f("Scale", scale.x, scale.y);
+ SimpleQuadShader->SetUniform2f("PositionOffset", 1.0f, 1.0f);
+ renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, NULL, 0, numQuadVerts, Prim_TriangleStrip);
+}
+
+void DistortionRenderer::renderPrimitives(
+ const ShaderFill* fill,
+ Buffer* vertices, Buffer* indices,
+ Matrix4f* viewMatrix, int offset, int count,
+ PrimitiveType rprim)
+{
+ OVR_ASSERT(fill->GetInputLayout() != 0);
+ RParams.pContext->IASetInputLayout((ID3D1xInputLayout*)fill->GetInputLayout());
+
+ if (indices)
+ {
+ RParams.pContext->IASetIndexBuffer(indices->GetBuffer(), DXGI_FORMAT_R16_UINT, 0);
+ }
+
+ ID3D1xBuffer* vertexBuffer = vertices->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = offset;
+ RParams.pContext->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ ShaderSet* shaders = ((ShaderFill*)fill)->GetShaders();
+
+ ShaderBase* vshader = ((ShaderBase*)shaders->GetShader(Shader_Vertex));
+ unsigned char* vertexData = vshader->UniformData;
+ if (vertexData)
+ {
+ // TODO: some VSes don't start with StandardUniformData!
+ if ( viewMatrix )
+ {
+ StandardUniformData* stdUniforms = (StandardUniformData*) vertexData;
+ stdUniforms->View = viewMatrix->Transposed();
+ stdUniforms->Proj = StdUniforms.Proj;
+ }
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, vertexData, vshader->UniformsSize);
+ vshader->SetUniformBuffer(UniformBuffers[Shader_Vertex]);
+ }
+
+ for(int i = Shader_Vertex + 1; i < Shader_Count; i++)
+ {
+ if (shaders->GetShader(i))
+ {
+ ((ShaderBase*)shaders->GetShader(i))->UpdateBuffer(UniformBuffers[i]);
+ ((ShaderBase*)shaders->GetShader(i))->SetUniformBuffer(UniformBuffers[i]);
+ }
+ }
+
+ D3D1X_(PRIMITIVE_TOPOLOGY) prim;
+ switch(rprim)
+ {
+ case Prim_Triangles:
+ prim = D3D1X_(PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ break;
+ case Prim_Lines:
+ prim = D3D1X_(PRIMITIVE_TOPOLOGY_LINELIST);
+ break;
+ case Prim_TriangleStrip:
+ prim = D3D1X_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ break;
+ default:
+ OVR_ASSERT(0);
+ return;
+ }
+ RParams.pContext->IASetPrimitiveTopology(prim);
+
+ fill->Set(rprim);
+
+ if (indices)
+ {
+ RParams.pContext->DrawIndexed(count, 0, 0);
+ }
+ else
+ {
+ RParams.pContext->Draw(count, 0);
+ }
+}
+
+void DistortionRenderer::setViewport(const Recti& vp)
+{
+ D3D1x_VIEWPORT d3dvp;
+
+ d3dvp.Width = D3DSELECT_10_11(vp.w, (float)vp.w);
+ d3dvp.Height = D3DSELECT_10_11(vp.h, (float)vp.h);
+ d3dvp.TopLeftX = D3DSELECT_10_11(vp.x, (float)vp.x);
+ d3dvp.TopLeftY = D3DSELECT_10_11(vp.y, (float)vp.y);
+ d3dvp.MinDepth = 0;
+ d3dvp.MaxDepth = 1;
+ RParams.pContext->RSSetViewports(1, &d3dvp);
+}
+
+
+
+
+static D3D1X_(INPUT_ELEMENT_DESC) DistortionMeshVertexDesc[] =
+{
+ {"Position", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D1X_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D1X_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D1X_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 2, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D1X_(INPUT_PER_VERTEX_DATA), 0},
+ {"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 32, D3D1X_(INPUT_PER_VERTEX_DATA), 0},
+};
+
+static D3D1X_(INPUT_ELEMENT_DESC) SimpleQuadMeshVertexDesc[] =
+{
+ {"Position", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D1X_(INPUT_PER_VERTEX_DATA), 0},
+};
+
+// TODO: this is D3D specific
+void DistortionRenderer::initShaders()
+{
+ {
+ PrecompiledShader vsShaderByteCode = DistortionVertexShaderLookup[DistortionVertexShaderBitMask & DistortionCaps];
+ Ptr<D3D_NS::VertexShader> vtxShader = *new D3D_NS::VertexShader(
+ &RParams,
+ (void*)vsShaderByteCode.ShaderData, vsShaderByteCode.ShaderSize,
+ vsShaderByteCode.ReflectionData, vsShaderByteCode.ReflectionSize);
+
+ ID3D1xInputLayout** objRef = &DistortionVertexIL.GetRawRef();
+
+ HRESULT validate = RParams.pDevice->CreateInputLayout(
+ DistortionMeshVertexDesc, sizeof(DistortionMeshVertexDesc) / sizeof(DistortionMeshVertexDesc[0]),
+ vsShaderByteCode.ShaderData, vsShaderByteCode.ShaderSize, objRef);
+ OVR_UNUSED(validate);
+
+ DistortionShader = *new ShaderSet;
+ DistortionShader->SetShader(vtxShader);
+
+ PrecompiledShader psShaderByteCode = DistortionPixelShaderLookup[DistortionPixelShaderBitMask & DistortionCaps];
+
+ Ptr<D3D_NS::PixelShader> ps = *new D3D_NS::PixelShader(
+ &RParams,
+ (void*)psShaderByteCode.ShaderData, psShaderByteCode.ShaderSize,
+ psShaderByteCode.ReflectionData, psShaderByteCode.ReflectionSize);
+
+ DistortionShader->SetShader(ps);
+ }
+
+ {
+ Ptr<D3D_NS::VertexShader> vtxShader = *new D3D_NS::VertexShader(
+ &RParams,
+ (void*)SimpleQuad_vs, sizeof(SimpleQuad_vs),
+ SimpleQuad_vs_refl, sizeof(SimpleQuad_vs_refl) / sizeof(SimpleQuad_vs_refl[0]));
+ //NULL, 0);
+
+ ID3D1xInputLayout** objRef = &SimpleQuadVertexIL.GetRawRef();
+
+ HRESULT validate = RParams.pDevice->CreateInputLayout(
+ SimpleQuadMeshVertexDesc, sizeof(SimpleQuadMeshVertexDesc) / sizeof(SimpleQuadMeshVertexDesc[0]),
+ (void*)SimpleQuad_vs, sizeof(SimpleQuad_vs), objRef);
+ OVR_UNUSED(validate);
+
+ SimpleQuadShader = *new ShaderSet;
+ SimpleQuadShader->SetShader(vtxShader);
+
+ Ptr<D3D_NS::PixelShader> ps = *new D3D_NS::PixelShader(
+ &RParams,
+ (void*)SimpleQuad_ps, sizeof(SimpleQuad_ps),
+ SimpleQuad_ps_refl, sizeof(SimpleQuad_ps_refl) / sizeof(SimpleQuad_ps_refl[0]));
+
+ SimpleQuadShader->SetShader(ps);
+ }
+}
+
+
+
+ID3D1xSamplerState* DistortionRenderer::getSamplerState(int sm)
+{
+ if (SamplerStates[sm])
+ return SamplerStates[sm];
+
+ D3D1X_(SAMPLER_DESC) ss;
+ memset(&ss, 0, sizeof(ss));
+ if (sm & Sample_Clamp)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1X_(TEXTURE_ADDRESS_CLAMP);
+ else if (sm & Sample_ClampBorder)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1X_(TEXTURE_ADDRESS_BORDER);
+ else
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1X_(TEXTURE_ADDRESS_WRAP);
+
+ if (sm & Sample_Nearest)
+ {
+ ss.Filter = D3D1X_(FILTER_MIN_MAG_MIP_POINT);
+ }
+ else if (sm & Sample_Anisotropic)
+ {
+ ss.Filter = D3D1X_(FILTER_ANISOTROPIC);
+ ss.MaxAnisotropy = 8;
+ }
+ else
+ {
+ ss.Filter = D3D1X_(FILTER_MIN_MAG_MIP_LINEAR);
+ }
+ ss.MaxLOD = 15;
+ RParams.pDevice->CreateSamplerState(&ss, &SamplerStates[sm].GetRawRef());
+ return SamplerStates[sm];
+}
+
+
+void DistortionRenderer::destroy()
+{
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ DistortionMeshVBs[eyeNum].Clear();
+ DistortionMeshIBs[eyeNum].Clear();
+ }
+
+ DistortionVertexIL.Clear();
+
+ if (DistortionShader)
+ {
+ DistortionShader->UnsetShader(Shader_Vertex);
+ DistortionShader->UnsetShader(Shader_Pixel);
+ DistortionShader.Clear();
+ }
+
+ LatencyTesterQuadVB.Clear();
+}
+
+}}} // OVR::CAPI::D3D1X
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.h b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.h
new file mode 100644
index 0000000..f151d73
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.h
@@ -0,0 +1,131 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_DistortionRenderer.h
+Content : Experimental distortion renderer
+Created : November 11, 2013
+Authors : Volga Aksoy
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+// No include guard, since this fill will be multiply-included.
+//#ifndef OVR_CAPI_D3D1X_DistortionRenderer_h
+
+#include "CAPI_D3D1X_Util.h"
+#include "../CAPI_DistortionRenderer.h"
+
+#include "../../Kernel/OVR_Log.h"
+
+namespace OVR { namespace CAPI { namespace D3D_NS {
+
+
+// ***** D3D1X::DistortionRenderer
+
+// Implementation of DistortionRenderer for D3D10/11.
+
+class DistortionRenderer : public CAPI::DistortionRenderer
+{
+public:
+ DistortionRenderer(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+ ~DistortionRenderer();
+
+
+ // Creation function for the device.
+ static CAPI::DistortionRenderer* Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+
+
+ // ***** Public DistortionRenderer interface
+
+ virtual bool Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned distortionCaps);
+
+ virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture);
+
+ virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor);
+
+ // TBD: Make public?
+ void WaitUntilGpuIdle();
+
+ // Similar to ovr_WaitTillTime but it also flushes GPU.
+ // Note, it exits when time expires, even if GPU is not in idle state yet.
+ double FlushGpuAndWaitTillTime(double absTime);
+
+private:
+ // Helpers
+ void initBuffersAndShaders();
+ void initShaders();
+ void initFullscreenQuad();
+ void destroy();
+
+ void setViewport(const Recti& vp);
+
+ void renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture);
+
+ void renderPrimitives(const ShaderFill* fill, Buffer* vertices, Buffer* indices,
+ Matrix4f* viewMatrix, int offset, int count,
+ PrimitiveType rprim);
+
+ void createDrawQuad();
+ void renderLatencyQuad(unsigned char* latencyTesterDrawColor);
+ void renderLatencyPixel(unsigned char* latencyTesterPixelColor);
+
+ // Create or get cached D3D sampler based on flags.
+ ID3D1xSamplerState* getSamplerState(int sm);
+
+
+ // TBD: Should we be using oe from RState instead?
+ unsigned DistortionCaps;
+
+ // D3DX device and utility variables.
+ RenderParams RParams;
+ Ptr<Texture> pEyeTextures[2];
+
+ // U,V scale and offset needed for timewarp.
+ ovrVector2f UVScaleOffset[2][2];
+
+ //Ptr<Buffer> mpFullScreenVertexBuffer;
+
+ Ptr<Buffer> DistortionMeshVBs[2]; // one per-eye
+ Ptr<Buffer> DistortionMeshIBs[2]; // one per-eye
+
+ Ptr<ShaderSet> DistortionShader;
+ Ptr<ID3D1xInputLayout> DistortionVertexIL;
+
+ struct StandardUniformData
+ {
+ Matrix4f Proj;
+ Matrix4f View;
+ } StdUniforms;
+ Ptr<Buffer> UniformBuffers[Shader_Count];
+
+ Ptr<ID3D1xSamplerState> SamplerStates[Sample_Count];
+ Ptr<ID3D1xRasterizerState> Rasterizer;
+
+ Ptr<Buffer> LatencyTesterQuadVB;
+ Ptr<ShaderSet> SimpleQuadShader;
+ Ptr<ID3D1xInputLayout> SimpleQuadVertexIL;
+
+ GpuTimer GpuProfiler;
+};
+
+}}} // OVR::CAPI::D3D1X
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.cpp b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.cpp
new file mode 100644
index 0000000..501a8e3
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.cpp
@@ -0,0 +1,416 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_Util.cpp
+Content : D3DX10 utility classes for rendering
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_D3D1X_Util.h"
+
+#include <d3dcompiler.h>
+
+namespace OVR { namespace CAPI { namespace D3D_NS {
+
+
+//-------------------------------------------------------------------------------------
+// ***** ShaderFill
+
+void ShaderFill::Set(PrimitiveType prim) const
+{
+ Shaders->Set(prim);
+ for(int i = 0; i < 8; i++)
+ {
+ if(Textures[i])
+ {
+ Textures[i]->Set(i);
+ }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Buffer
+
+Buffer::~Buffer()
+{
+}
+
+bool Buffer::Data(int use, const void *buffer, size_t size)
+{
+ if (D3DBuffer && Size >= size)
+ {
+ if (Dynamic)
+ {
+ if (!buffer)
+ return true;
+
+ void* v = Map(0, size, Map_Discard);
+ if (v)
+ {
+ memcpy(v, buffer, size);
+ Unmap(v);
+ return true;
+ }
+ }
+ else
+ {
+ pParams->pContext->UpdateSubresource(D3DBuffer, 0, NULL, buffer, 0, 0);
+ return true;
+ }
+ }
+ if (D3DBuffer)
+ {
+ D3DBuffer = NULL;
+ Size = 0;
+ Use = 0;
+ Dynamic = 0;
+ }
+
+ D3D1X_(BUFFER_DESC) desc;
+ memset(&desc, 0, sizeof(desc));
+ if (use & Buffer_ReadOnly)
+ {
+ desc.Usage = D3D1X_(USAGE_IMMUTABLE);
+ desc.CPUAccessFlags = 0;
+ }
+ else
+ {
+ desc.Usage = D3D1X_(USAGE_DYNAMIC);
+ desc.CPUAccessFlags = D3D1X_(CPU_ACCESS_WRITE);
+ Dynamic = 1;
+ }
+
+ switch(use & Buffer_TypeMask)
+ {
+ case Buffer_Vertex: desc.BindFlags = D3D1X_(BIND_VERTEX_BUFFER); break;
+ case Buffer_Index: desc.BindFlags = D3D1X_(BIND_INDEX_BUFFER); break;
+ case Buffer_Uniform:
+ desc.BindFlags = D3D1X_(BIND_CONSTANT_BUFFER);
+ size += ((size + 15) & ~15) - size;
+ break;
+ }
+
+ desc.ByteWidth = (unsigned)size;
+
+ D3D1X_(SUBRESOURCE_DATA) sr;
+ sr.pSysMem = buffer;
+ sr.SysMemPitch = 0;
+ sr.SysMemSlicePitch = 0;
+
+ HRESULT hr = pParams->pDevice->CreateBuffer(&desc, buffer ? &sr : NULL, &D3DBuffer.GetRawRef());
+ if (SUCCEEDED(hr))
+ {
+ Use = use;
+ Size = desc.ByteWidth;
+ return 1;
+ }
+ return 0;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ OVR_UNUSED(size);
+
+ D3D1X_(MAP) mapFlags = D3D1X_(MAP_WRITE);
+ if (flags & Map_Discard)
+ mapFlags = D3D1X_(MAP_WRITE_DISCARD);
+ if (flags & Map_Unsynchronized)
+ mapFlags = D3D1X_(MAP_WRITE_NO_OVERWRITE);
+
+#if (OVR_D3D_VERSION == 10)
+ void* map;
+ if (SUCCEEDED(D3DBuffer->Map(mapFlags, 0, &map)))
+ return ((char*)map) + start;
+#else
+ D3D11_MAPPED_SUBRESOURCE map;
+ if (SUCCEEDED(pParams->pContext->Map(D3DBuffer, 0, mapFlags, 0, &map)))
+ return ((char*)map.pData) + start;
+#endif
+
+ return NULL;
+}
+
+bool Buffer::Unmap(void *m)
+{
+ OVR_UNUSED(m);
+
+ D3DSELECT_10_11( D3DBuffer->Unmap(),
+ pParams->pContext->Unmap(D3DBuffer, 0) );
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Shaders
+
+template<> bool ShaderImpl<Shader_Vertex, ID3D1xVertexShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(pParams->pDevice->CreateVertexShader(shader, size D3D11_COMMA_0, &D3DShader));
+}
+template<> bool ShaderImpl<Shader_Pixel, ID3D1xPixelShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(pParams->pDevice->CreatePixelShader(shader, size D3D11_COMMA_0, &D3DShader));
+}
+
+template<> void ShaderImpl<Shader_Vertex, ID3D1xVertexShader>::Set(PrimitiveType) const
+{
+ pParams->pContext->VSSetShader(D3DShader D3D11_COMMA_0 D3D11_COMMA_0 );
+}
+template<> void ShaderImpl<Shader_Pixel, ID3D1xPixelShader>::Set(PrimitiveType) const
+{
+ pParams->pContext->PSSetShader(D3DShader D3D11_COMMA_0 D3D11_COMMA_0 ) ;
+}
+
+template<> void ShaderImpl<Shader_Vertex, ID3D1xVertexShader>::SetUniformBuffer(Buffer* buffer, int i)
+{
+ pParams->pContext->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+template<> void ShaderImpl<Shader_Pixel, ID3D1xPixelShader>::SetUniformBuffer(Buffer* buffer, int i)
+{
+ pParams->pContext->PSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Shader Base
+
+ShaderBase::ShaderBase(RenderParams* rp, ShaderStage stage)
+ : Shader(stage), pParams(rp), UniformData(0)
+{
+}
+ShaderBase::~ShaderBase()
+{
+ if (UniformData)
+ OVR_FREE(UniformData);
+}
+
+bool ShaderBase::SetUniform(const char* name, int n, const float* v)
+{
+ for(unsigned i = 0; i < UniformReflSize; i++)
+ {
+ if (!strcmp(UniformRefl[i].Name, name))
+ {
+ memcpy(UniformData + UniformRefl[i].Offset, v, n * sizeof(float));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+bool ShaderBase::SetUniformBool(const char* name, int n, const bool* v)
+{
+ OVR_UNUSED(n);
+ for(unsigned i = 0; i < UniformReflSize; i++)
+ {
+ if (!strcmp(UniformRefl[i].Name, name))
+ {
+ memcpy(UniformData + UniformRefl[i].Offset, v, UniformRefl[i].Size);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ShaderBase::InitUniforms(const Uniform* refl, size_t reflSize)
+{
+ if(!refl)
+ {
+ UniformRefl = NULL;
+ UniformReflSize = 0;
+
+ UniformsSize = 0;
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ UniformData = 0;
+ }
+ return; // no reflection data
+ }
+
+ UniformRefl = refl;
+ UniformReflSize = reflSize;
+
+ UniformsSize = UniformRefl[UniformReflSize-1].Offset + UniformRefl[UniformReflSize-1].Size;
+ UniformData = (unsigned char*)OVR_ALLOC(UniformsSize);
+}
+
+void ShaderBase::UpdateBuffer(Buffer* buf)
+{
+ if (UniformsSize)
+ {
+ buf->Data(Buffer_Uniform, UniformData, UniformsSize);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Texture
+//
+Texture::Texture(RenderParams* rp, int fmt, const Sizei texSize,
+ ID3D1xSamplerState* sampler, int samples)
+ : pParams(rp), Tex(NULL), TexSv(NULL), TexRtv(NULL), TexDsv(NULL),
+ TextureSize(texSize),
+ Sampler(sampler),
+ Samples(samples)
+{
+ OVR_UNUSED(fmt);
+}
+
+Texture::~Texture()
+{
+}
+
+void Texture::Set(int slot, ShaderStage stage) const
+{
+ ID3D1xShaderResourceView* texSv = TexSv.GetPtr();
+
+ switch(stage)
+ {
+ case Shader_Fragment:
+ pParams->pContext->PSSetShaderResources(slot, 1, &texSv);
+ pParams->pContext->PSSetSamplers(slot, 1, &Sampler.GetRawRef());
+ break;
+
+ case Shader_Vertex:
+ pParams->pContext->VSSetShaderResources(slot, 1, &texSv);
+ break;
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** GpuTimer
+//
+#if (OVR_D3D_VERSION == 11)
+#define D3DQUERY_EXEC(_context_, _query_, _command_, ...) _context_->_command_(_query_, __VA_ARGS__)
+#else
+#define D3DQUERY_EXEC(_context_, _query_, _command_, ...) _query_->_command_(__VA_ARGS__)
+#endif
+
+
+void GpuTimer::Init(ID3D1xDevice* device, ID3D1xDeviceContext* content)
+{
+ D3dDevice = device;
+ Context = content;
+}
+
+void GpuTimer::BeginQuery()
+{
+ if(GotoNextFrame(LastQueuedFrame) == LastTimedFrame)
+ {
+ OVR_ASSERT(false); // too many queries queued
+ return;
+ }
+
+ LastQueuedFrame = GotoNextFrame(LastQueuedFrame);
+
+ GpuQuerySets& newQuerySet = QuerySets[LastQueuedFrame];
+ if(newQuerySet.DisjointQuery == NULL)
+ {
+ // Create the queries
+ D3D1x_QUERY_DESC desc;
+ desc.Query = D3D1X_(QUERY_TIMESTAMP_DISJOINT);
+ desc.MiscFlags = 0;
+ VERIFY_HRESULT(D3dDevice->CreateQuery(&desc, &newQuerySet.DisjointQuery));
+
+ desc.Query = D3D1X_(QUERY_TIMESTAMP);
+ VERIFY_HRESULT(D3dDevice->CreateQuery(&desc, &newQuerySet.TimeStartQuery));
+ VERIFY_HRESULT(D3dDevice->CreateQuery(&desc, &newQuerySet.TimeEndQuery));
+ }
+
+ OVR_ASSERT(!newQuerySet.QueryStarted);
+ OVR_ASSERT(!newQuerySet.QueryAwaitingTiming);
+
+
+ D3DQUERY_EXEC(Context, QuerySets[LastQueuedFrame].DisjointQuery, Begin, ); // First start a disjoint query
+ D3DQUERY_EXEC(Context, QuerySets[LastQueuedFrame].TimeStartQuery, End, ); // Insert start timestamp
+
+ newQuerySet.QueryStarted = true;
+ newQuerySet.QueryAwaitingTiming = false;
+ //newQuerySet.QueryTimed = false;
+}
+
+void GpuTimer::EndQuery()
+{
+ if(LastQueuedFrame > 0 && !QuerySets[LastQueuedFrame].QueryStarted)
+ return;
+
+ GpuQuerySets& doneQuerySet = QuerySets[LastQueuedFrame];
+ OVR_ASSERT(doneQuerySet.QueryStarted);
+ OVR_ASSERT(!doneQuerySet.QueryAwaitingTiming);
+
+ // Insert the end timestamp
+ D3DQUERY_EXEC(Context, doneQuerySet.TimeEndQuery, End, );
+
+ // End the disjoint query
+ D3DQUERY_EXEC(Context, doneQuerySet.DisjointQuery, End, );
+
+ doneQuerySet.QueryStarted = false;
+ doneQuerySet.QueryAwaitingTiming = true;
+}
+
+float GpuTimer::GetTiming(bool blockUntilValid)
+{
+ float time = -1.0f;
+
+ // loop until we hit a query that is not ready yet, or we have read all queued queries
+ while(LastTimedFrame != LastQueuedFrame)
+ {
+ int timeTestFrame = GotoNextFrame(LastTimedFrame);
+
+ GpuQuerySets& querySet = QuerySets[timeTestFrame];
+
+ OVR_ASSERT(!querySet.QueryStarted && querySet.QueryAwaitingTiming);
+
+ UINT64 startTime = 0;
+ UINT64 endTime = 0;
+ D3D1X_(QUERY_DATA_TIMESTAMP_DISJOINT) disjointData;
+
+ if(blockUntilValid)
+ {
+ while(D3DQUERY_EXEC(Context, querySet.TimeStartQuery, GetData, &startTime, sizeof(startTime), 0) != S_OK);
+ while(D3DQUERY_EXEC(Context, querySet.TimeEndQuery, GetData, &endTime, sizeof(endTime), 0) != S_OK);
+ while(D3DQUERY_EXEC(Context, querySet.DisjointQuery, GetData, &disjointData, sizeof(disjointData), 0) != S_OK);
+ }
+ else
+ {
+// Early return if we fail to get data for any of these
+ if(D3DQUERY_EXEC(Context, querySet.TimeStartQuery, GetData, &startTime, sizeof(startTime), 0) != S_OK) return time;
+ if(D3DQUERY_EXEC(Context, querySet.TimeEndQuery, GetData, &endTime, sizeof(endTime), 0) != S_OK) return time;
+ if(D3DQUERY_EXEC(Context, querySet.DisjointQuery, GetData, &disjointData, sizeof(disjointData), 0) != S_OK) return time;
+ }
+
+ querySet.QueryAwaitingTiming = false;
+ LastTimedFrame = timeTestFrame; // successfully retrieved the timing data
+
+ if(disjointData.Disjoint == false)
+ {
+ UINT64 delta = endTime - startTime;
+ float frequency = (float)(disjointData.Frequency);
+ time = (delta / frequency);
+ }
+ }
+
+ return time;
+}
+
+}}} // OVR::CAPI::D3DX
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.h b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.h
new file mode 100644
index 0000000..f8d7bd3
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.h
@@ -0,0 +1,505 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_Util.h
+Content : D3DX 10/11 utility classes for rendering
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+// ***** IMPORTANT:
+// This file can be included twice, once with OVR_D3D_VERSION=10 and
+// once with OVR_D3D_VERSION=11.
+
+
+#ifndef OVR_D3D_VERSION
+#error define OVR_D3D_VERSION to 10 or 11
+#endif
+
+// Custom include guard, allowing one of each D3D10/11.
+#if (OVR_D3D_VERSION == 10 && !defined(INC_OVR_CAPI_D3D10_Util_h)) || \
+ (OVR_D3D_VERSION == 11 && !defined(INC_OVR_CAPI_D3D11_Util_h))
+
+#include "../../Kernel/OVR_String.h"
+#include "../../Kernel/OVR_Array.h"
+#include "../../Kernel/OVR_Math.h"
+
+#include <Windows.h>
+#include <comdef.h> // for _COM_SMARTPTR_TYPEDEF()
+
+#undef D3D_NS // namespace
+#undef D3D1X_
+#undef ID3D1X // interface prefix
+#undef ovrD3D1X // ovrD3D10Config, etc.
+#undef D3D11_COMMA_0 // Injects on ", 0" for D3D11 only
+#undef D3DSELECT_10_11
+#undef IID_ID3D1xShaderReflection
+
+#if (OVR_D3D_VERSION == 10)
+
+ #define INC_OVR_CAPI_D3D10_Util_h
+ #define D3D_NS D3D10
+ #define D3D1X_(x) D3D10_##x
+ #define ID3D1X(x) ID3D10##x
+ #define ovrD3D1X(x) ovrD3D10##x
+ #define D3DSELECT_10_11(a10, a11) a10
+ #define D3D11_COMMA_0
+ #define IID_ID3D1xShaderReflection IID_ID3D10ShaderReflection
+ #include <d3d10_1.h> // avoids warning?
+ #include <d3d10.h>
+
+#else // (OVR_D3D_VERSION == 11)
+
+ #define INC_OVR_CAPI_D3D11_Util_h
+ #define D3D_NS D3D11
+ #define D3D1X_(x) D3D11_##x
+ #define ID3D1X(x) ID3D11##x
+ #define ovrD3D1X(x) ovrD3D11##x
+ #define D3DSELECT_10_11(a10, a11) a11
+ #define D3D11_COMMA_0 , 0
+ #define IID_ID3D1xShaderReflection IID_ID3D11ShaderReflection
+ #include <d3d11.h>
+ #include <D3D11Shader.h>
+#endif
+
+
+namespace OVR { namespace CAPI { namespace D3D_NS {
+
+// D3D Namespace-local types.
+typedef ID3D1X(Device) ID3D1xDevice;
+typedef ID3D1X(RenderTargetView) ID3D1xRenderTargetView;
+typedef ID3D1X(Texture2D) ID3D1xTexture2D;
+typedef ID3D1X(ShaderResourceView) ID3D1xShaderResourceView;
+typedef ID3D1X(DepthStencilView) ID3D1xDepthStencilView;
+typedef ID3D1X(DepthStencilState) ID3D1xDepthStencilState;
+typedef ID3D1X(InputLayout) ID3D1xInputLayout;
+typedef ID3D1X(Buffer) ID3D1xBuffer;
+typedef ID3D1X(VertexShader) ID3D1xVertexShader;
+typedef ID3D1X(PixelShader) ID3D1xPixelShader;
+typedef ID3D1X(GeometryShader) ID3D1xGeometryShader;
+typedef ID3D1X(BlendState) ID3D1xBlendState;
+typedef ID3D1X(RasterizerState) ID3D1xRasterizerState;
+typedef ID3D1X(SamplerState) ID3D1xSamplerState;
+typedef ID3D1X(Query) ID3D1xQuery;
+typedef ID3D1X(ShaderReflection) ID3D1xShaderReflection;
+typedef ID3D1X(ShaderReflectionVariable) ID3D1xShaderReflectionVariable;
+typedef ID3D1X(ShaderReflectionConstantBuffer) ID3D1xShaderReflectionConstantBuffer;
+typedef D3D1X_(VIEWPORT) D3D1x_VIEWPORT;
+typedef D3D1X_(QUERY_DESC) D3D1x_QUERY_DESC;
+typedef D3D1X_(SHADER_BUFFER_DESC) D3D1x_SHADER_BUFFER_DESC;
+typedef D3D1X_(SHADER_VARIABLE_DESC) D3D1x_SHADER_VARIABLE_DESC;
+// Blob is the same
+typedef ID3D10Blob ID3D1xBlob;
+
+#if (OVR_D3D_VERSION == 10)
+ typedef ID3D10Device ID3D1xDeviceContext;
+#else
+ typedef ID3D11DeviceContext ID3D1xDeviceContext;
+#endif
+
+
+// Assert on HRESULT failure
+inline void VERIFY_HRESULT(HRESULT hr)
+{
+ if (FAILED(hr))
+ OVR_ASSERT(false);
+}
+
+class Buffer;
+
+// Rendering parameters/pointers describing D3DX rendering setup.
+struct RenderParams
+{
+ ID3D1xDevice* pDevice;
+ ID3D1xDeviceContext* pContext;
+ ID3D1xRenderTargetView* pBackBufferRT;
+ IDXGISwapChain* pSwapChain;
+ Sizei RTSize;
+ int Multisample;
+};
+
+
+// Rendering primitive type used to render Model.
+enum PrimitiveType
+{
+ Prim_Triangles,
+ Prim_Lines,
+ Prim_TriangleStrip,
+ Prim_Unknown,
+ Prim_Count
+};
+
+// Types of shaders that can be stored together in a ShaderSet.
+enum ShaderStage
+{
+ Shader_Vertex = 0,
+ Shader_Fragment = 2,
+ Shader_Pixel = 2,
+ Shader_Count = 3,
+};
+
+enum MapFlags
+{
+ Map_Discard = 1,
+ Map_Read = 2, // do not use
+ Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE
+};
+
+
+// Buffer types used for uploading geometry & constants.
+enum BufferUsage
+{
+ Buffer_Unknown = 0,
+ Buffer_Vertex = 1,
+ Buffer_Index = 2,
+ Buffer_Uniform = 4,
+ Buffer_TypeMask = 0xff,
+ Buffer_ReadOnly = 0x100, // Buffer must be created with Data().
+};
+
+enum TextureFormat
+{
+ Texture_RGBA = 0x0100,
+ Texture_Depth = 0x8000,
+ Texture_TypeMask = 0xff00,
+ Texture_SamplesMask = 0x00ff,
+ Texture_RenderTarget = 0x10000,
+ Texture_GenMipmaps = 0x20000,
+};
+
+// Texture sampling modes.
+enum SampleMode
+{
+ Sample_Linear = 0,
+ Sample_Nearest = 1,
+ Sample_Anisotropic = 2,
+ Sample_FilterMask = 3,
+
+ Sample_Repeat = 0,
+ Sample_Clamp = 4,
+ Sample_ClampBorder = 8, // If unsupported Clamp is used instead.
+ Sample_AddressMask =12,
+
+ Sample_Count =13,
+};
+
+// Base class for vertex and pixel shaders. Stored in ShaderSet.
+class Shader : public RefCountBase<Shader>
+{
+ friend class ShaderSet;
+
+protected:
+ ShaderStage Stage;
+
+public:
+ Shader(ShaderStage s) : Stage(s) {}
+ virtual ~Shader() {}
+
+ ShaderStage GetStage() const { return Stage; }
+
+ virtual void Set(PrimitiveType) const { }
+ virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); }
+
+protected:
+ virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; }
+ virtual bool SetUniformBool(const char* name, int n, const bool* v) { OVR_UNUSED3(name, n, v); return false; }
+};
+
+
+
+// A group of shaders, one per stage.
+// A ShaderSet is applied to a RenderDevice for rendering with a given fill.
+class ShaderSet : public RefCountBase<ShaderSet>
+{
+protected:
+ Ptr<Shader> Shaders[Shader_Count];
+
+public:
+ ShaderSet() { }
+ ~ShaderSet() { }
+
+ virtual void SetShader(Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shaders[stage] = NULL;
+ }
+ Shader* GetShader(int stage) { return Shaders[stage]; }
+
+ virtual void Set(PrimitiveType prim) const
+ {
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ Shaders[i]->Set(prim);
+ }
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v)
+ {
+ bool result = 0;
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ result |= Shaders[i]->SetUniform(name, n, v);
+
+ return result;
+ }
+ bool SetUniform1f(const char* name, float x)
+ {
+ const float v[] = {x};
+ return SetUniform(name, 1, v);
+ }
+ bool SetUniform2f(const char* name, float x, float y)
+ {
+ const float v[] = {x,y};
+ return SetUniform(name, 2, v);
+ }
+ bool SetUniform3f(const char* name, float x, float y, float z)
+ {
+ const float v[] = {x,y,z};
+ return SetUniform(name, 3, v);
+ }
+ bool SetUniform4f(const char* name, float x, float y, float z, float w = 1)
+ {
+ const float v[] = {x,y,z,w};
+ return SetUniform(name, 4, v);
+ }
+
+ bool SetUniformv(const char* name, const Vector3f& v)
+ {
+ const float a[] = {v.x,v.y,v.z,1};
+ return SetUniform(name, 4, a);
+ }
+
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ Matrix4f mt = m.Transposed();
+ return SetUniform(name, 16, &mt.M[0][0]);
+ }
+};
+
+
+// Fill combines a ShaderSet (vertex, pixel) with textures, if any.
+// Every model has a fill.
+class ShaderFill : public RefCountBase<ShaderFill>
+{
+ Ptr<ShaderSet> Shaders;
+ Ptr<class Texture> Textures[8];
+ void* InputLayout; // HACK this should be abstracted
+
+public:
+ ShaderFill(ShaderSet* sh) : Shaders(sh) { InputLayout = NULL; }
+ ShaderFill(ShaderSet& sh) : Shaders(sh) { InputLayout = NULL; }
+
+ ShaderSet* GetShaders() const { return Shaders; }
+ void* GetInputLayout() const { return InputLayout; }
+
+ virtual void Set(PrimitiveType prim = Prim_Unknown) const;
+ virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; }
+ void SetInputLayout(void* newIL) { InputLayout = (void*)newIL; }
+};
+
+
+class ShaderBase : public Shader
+{
+public:
+ RenderParams* pParams;
+ unsigned char* UniformData;
+ int UniformsSize;
+
+ enum VarType
+ {
+ VARTYPE_FLOAT,
+ VARTYPE_INT,
+ VARTYPE_BOOL,
+ };
+
+ struct Uniform
+ {
+ const char* Name;
+ VarType Type;
+ int Offset, Size;
+ };
+ const Uniform* UniformRefl;
+ size_t UniformReflSize;
+
+ ShaderBase(RenderParams* rp, ShaderStage stage);
+ ~ShaderBase();
+
+ ShaderStage GetStage() const { return Stage; }
+
+ void InitUniforms(const Uniform* refl, size_t reflSize);
+ bool SetUniform(const char* name, int n, const float* v);
+ bool SetUniformBool(const char* name, int n, const bool* v);
+
+ void UpdateBuffer(Buffer* b);
+};
+
+
+template<ShaderStage SStage, class D3DShaderType>
+class ShaderImpl : public ShaderBase
+{
+public:
+ D3DShaderType* D3DShader;
+
+ ShaderImpl(RenderParams* rp, void* s, size_t size, const Uniform* refl, size_t reflSize) : ShaderBase(rp, SStage)
+ {
+ Load(s, size);
+ InitUniforms(refl, reflSize);
+ }
+ ~ShaderImpl()
+ {
+ if (D3DShader)
+ D3DShader->Release();
+ }
+
+ // These functions have specializations.
+ bool Load(void* shader, size_t size);
+ void Set(PrimitiveType prim) const;
+ void SetUniformBuffer(Buffer* buffers, int i = 0);
+};
+
+typedef ShaderImpl<Shader_Vertex, ID3D1xVertexShader> VertexShader;
+typedef ShaderImpl<Shader_Fragment, ID3D1xPixelShader> PixelShader;
+
+
+class Buffer : public RefCountBase<Buffer>
+{
+public:
+ RenderParams* pParams;
+ Ptr<ID3D1xBuffer> D3DBuffer;
+ size_t Size;
+ int Use;
+ bool Dynamic;
+
+public:
+ Buffer(RenderParams* rp) : pParams(rp), Size(0), Use(0) {}
+ ~Buffer();
+
+ ID3D1xBuffer* GetBuffer() const { return D3DBuffer; }
+
+ virtual size_t GetSize() { return Size; }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+
+class Texture : public RefCountBase<Texture>
+{
+public:
+ RenderParams* pParams;
+ Ptr<ID3D1xTexture2D> Tex;
+ Ptr<ID3D1xShaderResourceView> TexSv;
+ Ptr<ID3D1xRenderTargetView> TexRtv;
+ Ptr<ID3D1xDepthStencilView> TexDsv;
+ mutable Ptr<ID3D1xSamplerState> Sampler;
+ Sizei TextureSize;
+ int Samples;
+
+ Texture(RenderParams* rp, int fmt, const Sizei texSize,
+ ID3D1xSamplerState* sampler, int samples = 1);
+ ~Texture();
+
+ virtual Sizei GetSize() const { return TextureSize; }
+ virtual int GetSamples() const { return Samples; }
+
+ // virtual void SetSampleMode(int sm);
+
+ // Updates texture to point to specified resources
+ // - used for slave rendering.
+ void UpdatePlaceholderTexture(ID3D1xTexture2D* texture,
+ ID3D1xShaderResourceView* psrv,
+ const Sizei& textureSize)
+ {
+ Tex = texture;
+ TexSv = psrv;
+ TexRtv.Clear();
+ TexDsv.Clear();
+
+ TextureSize = textureSize;
+
+#ifdef OVR_BUILD_DEBUG
+ D3D1X_(TEXTURE2D_DESC) desc;
+ texture->GetDesc(&desc);
+ OVR_ASSERT(TextureSize == Sizei(desc.Width, desc.Height));
+#endif
+ }
+
+
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const;
+
+};
+
+
+class GpuTimer : public RefCountBase<GpuTimer>
+{
+public:
+ GpuTimer()
+ : QuerySets(MaxNumQueryFrames)
+ , D3dDevice(NULL)
+ , Context(NULL)
+ , LastQueuedFrame(-1)
+ , LastTimedFrame(-1)
+ { }
+
+ void Init(ID3D1xDevice* device, ID3D1xDeviceContext* content);
+
+ void BeginQuery();
+ void EndQuery();
+
+ // Returns -1 if timing is invalid
+ float GetTiming(bool blockUntilValid);
+
+protected:
+ static const unsigned MaxNumQueryFrames = 10;
+
+ int GotoNextFrame(int frame)
+ {
+ return (frame + 1) % MaxNumQueryFrames;
+ }
+
+ _COM_SMARTPTR_TYPEDEF(ID3D1xQuery, __uuidof(ID3D1xQuery));
+
+ struct GpuQuerySets
+ {
+ ID3D1xQueryPtr DisjointQuery;
+ ID3D1xQueryPtr TimeStartQuery;
+ ID3D1xQueryPtr TimeEndQuery;
+ bool QueryStarted;
+ bool QueryAwaitingTiming;
+
+ GpuQuerySets() : QueryStarted(false), QueryAwaitingTiming(false) {}
+ };
+ Array<GpuQuerySets> QuerySets;
+
+ int LastQueuedFrame;
+ int LastTimedFrame;
+
+ Ptr<ID3D1xDevice> D3dDevice;
+ Ptr<ID3D1xDeviceContext> Context;
+};
+
+}}} // OVR::CAPI::D3D1X
+
+#endif // INC_OVR_CAPI_D3D10/11_Util_h
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.cpp b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.cpp
new file mode 100644
index 0000000..21b885e
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.cpp
@@ -0,0 +1,251 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_DistortionRenderer.cpp
+Content : Experimental distortion renderer
+Created : March 7th, 2014
+Authors : Tom Heath
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_D3D9_DistortionRenderer.h"
+#define OVR_D3D_VERSION 9
+#include "../../OVR_CAPI_D3D.h"
+
+namespace OVR { namespace CAPI { namespace D3D9 {
+
+
+///QUESTION : Why not just a normal constructor?
+CAPI::DistortionRenderer* DistortionRenderer::Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+{
+ return new DistortionRenderer(hmd, timeManager, renderState);
+}
+
+DistortionRenderer::DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+ : CAPI::DistortionRenderer(ovrRenderAPI_D3D9, hmd, timeManager, renderState)
+{
+}
+/**********************************************/
+DistortionRenderer::~DistortionRenderer()
+{
+ //Release any memory
+ eachEye[0].dxIndices->Release();
+ eachEye[0].dxVerts->Release();
+ eachEye[1].dxIndices->Release();
+ eachEye[1].dxVerts->Release();
+}
+
+
+/******************************************************************************/
+bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned arg_distortionCaps)
+{
+ // TBD: Decide if hmdCaps are needed here or are a part of RenderState
+ OVR_UNUSED(hmdCaps);
+
+ ///QUESTION - what is returned bool for??? Are we happy with this true, if not config.
+ const ovrD3D9Config * config = (const ovrD3D9Config*)apiConfig;
+ if (!config) return true;
+ if (!config->D3D9.pDevice) return false;
+
+ //Glean all the required variables from the input structures
+ device = config->D3D9.pDevice;
+ screenSize = config->D3D9.Header.RTSize;
+ distortionCaps = arg_distortionCaps;
+
+ CreateVertexDeclaration();
+ CreateDistortionShaders();
+ Create_Distortion_Models();
+
+ return (true);
+}
+
+
+/**************************************************************/
+void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture)
+{
+ //Doesn't do a lot in here??
+ const ovrD3D9Texture* tex = (const ovrD3D9Texture*)eyeTexture;
+
+ //Write in values
+ eachEye[eyeId].texture = tex->D3D9.pTexture;
+
+ //Its only at this point we discover what the viewport of the texture is.
+ //because presumably we allow users to realtime adjust the resolution.
+ //Which begs the question - why did we ask them what viewport they were
+ //using before, which gave them a set of UV offsets. In fact, our
+ //asking for eye mesh must be entirely independed of these viewports,
+ //presumably only to get the parameters.
+
+ ovrEyeDesc ed = RState.EyeRenderDesc[eyeId].Desc;
+ ed.TextureSize = tex->D3D9.Header.TextureSize;
+ ed.RenderViewport = tex->D3D9.Header.RenderViewport;
+
+ ovrHmd_GetRenderScaleAndOffset(HMD, ed, distortionCaps, eachEye[eyeId].UVScaleOffset);
+}
+
+
+/******************************************************************/
+void DistortionRenderer::EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor)
+{
+ OVR_UNUSED(swapBuffers);
+ OVR_UNUSED(latencyTesterDrawColor);
+
+ ///QUESTION : Should I be clearing the screen?
+ ///QUESTION : Should I be ensuring the screen is the render target
+
+ if (!TimeManager.NeedDistortionTimeMeasurement())
+ {
+ if (RState.DistortionCaps & ovrDistortion_TimeWarp)
+ {
+ // Wait for timewarp distortion if it is time and Gpu idle
+ WaitTillTimeAndFlushGpu(TimeManager.GetFrameTiming().TimewarpPointTime);
+ }
+
+ RenderBothDistortionMeshes();
+ }
+ else
+ {
+ // If needed, measure distortion time so that TimeManager can better estimate
+ // latency-reducing time-warp wait timing.
+ WaitUntilGpuIdle();
+ double distortionStartTime = ovr_GetTimeInSeconds();
+
+ RenderBothDistortionMeshes();
+ WaitUntilGpuIdle();
+
+ TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime);
+ }
+
+ if(latencyTesterDrawColor)
+ {
+ ///QUESTION : Is this still to be supported?
+ ///renderLatencyQuad(latencyTesterDrawColor);
+ }
+
+ if(latencyTester2DrawColor)
+ {
+ // TODO:
+ }
+
+ if (swapBuffers)
+ {
+ device->Present( NULL, NULL, NULL, NULL );
+
+ /// if (RParams.pSwapChain)
+ {
+ /// UINT swapInterval = (RState.HMDCaps & ovrHmdCap_NoVSync) ? 0 : 1;
+ /// RParams.pSwapChain->Present(swapInterval, 0);
+
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ // It's critical that this flush is *after* present.
+ /// WaitUntilGpuIdle();
+ }
+ /// else
+ {
+ // TBD: Generate error - swapbuffer option used with null swapchain.
+ }
+ }
+}
+
+
+void DistortionRenderer::WaitUntilGpuIdle()
+{
+#if 0
+ // Flush and Stall CPU while waiting for GPU to complete rendering all of the queued draw calls
+ D3D1x_QUERY_DESC queryDesc = { D3D1X_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+
+ if (RParams.pDevice->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+ D3DSELECT_10_11(query->End(),
+ RParams.pContext->End(query));
+
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Exit on failure to avoid infinite loop.
+ do { }
+ while(!done &&
+ !FAILED(D3DSELECT_10_11(query->GetData(&done, sizeof(BOOL), 0),
+ RParams.pContext->GetData(query, &done, sizeof(BOOL), 0)))
+ );
+ }
+#endif
+}
+
+double DistortionRenderer::WaitTillTimeAndFlushGpu(double absTime)
+{
+
+OVR_UNUSED(absTime);
+#if 0
+ double initialTime = ovr_GetTimeInSeconds();
+ if (initialTime >= absTime)
+ return 0.0;
+
+ // Flush and Stall CPU while waiting for GPU to complete rendering all of the queued draw calls
+ D3D1x_QUERY_DESC queryDesc = { D3D1X_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+ bool callGetData = false;
+
+ if (RParams.pDevice->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+ D3DSELECT_10_11(query->End(),
+ RParams.pContext->End(query));
+ callGetData = true;
+ }
+
+ double newTime = initialTime;
+ volatile int i;
+
+ while (newTime < absTime)
+ {
+ if (callGetData)
+ {
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Stop calling GetData on failure.
+ callGetData = !FAILED(D3DSELECT_10_11(query->GetData(&done, sizeof(BOOL), 0),
+ RParams.pContext->GetData(query, &done, sizeof(BOOL), 0))) && !done;
+ }
+ else
+ {
+ for (int j = 0; j < 50; j++)
+ i = 0;
+ }
+ newTime = ovr_GetTimeInSeconds();
+ }
+
+ // How long we waited
+ return newTime - initialTime;
+#endif
+ return 0; //dummy
+}
+
+
+
+
+
+
+
+}}} // OVR::CAPI::D3D1X
+
+
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.h b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.h
new file mode 100644
index 0000000..9332b83
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.h
@@ -0,0 +1,120 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_DistortionRenderer.h
+Content : Experimental distortion renderer
+Created : March 7, 2014
+Authors : Tom Heath
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#undef new
+
+#if _MSC_VER < 1700
+#include <d3dx9.h>
+#else
+#include <d3d9.h>
+#endif
+
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+#include "../CAPI_DistortionRenderer.h"
+
+
+namespace OVR { namespace CAPI { namespace D3D9 {
+
+
+//Implementation of DistortionRenderer for D3D9.
+/***************************************************/
+class DistortionRenderer : public CAPI::DistortionRenderer
+{
+public:
+ DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState);
+ ~DistortionRenderer();
+
+ // Creation function for the device.
+ static CAPI::DistortionRenderer* Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+
+ // ***** Public DistortionRenderer interface
+ virtual bool Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned distortionCaps);
+
+ virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture);
+
+ virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor);
+
+ // TBD: Make public?
+ void WaitUntilGpuIdle();
+
+ // Similar to ovr_WaitTillTime but it also flushes GPU.
+ // Note, it exits when time expires, even if GPU is not in idle state yet.
+ double WaitTillTimeAndFlushGpu(double absTime);
+
+private:
+
+ //Functions
+ void CreateDistortionShaders(void);
+ void Create_Distortion_Models(void);
+ void CreateVertexDeclaration(void);
+ void RenderBothDistortionMeshes();
+ void RecordAndSetState(int which, int type, DWORD newValue);
+ void RevertAllStates(void);
+
+
+ //Data, structures and pointers
+ IDirect3DDevice9 * device;
+ IDirect3DVertexDeclaration9 * vertexDecl;
+ IDirect3DPixelShader9 * pixelShader;
+ IDirect3DVertexShader9 * vertexShader;
+ IDirect3DVertexShader9 * vertexShaderTimewarp;
+ ovrSizei screenSize;
+ unsigned distortionCaps;
+
+ struct FOR_EACH_EYE
+ {
+ IDirect3DVertexBuffer9 * dxVerts;
+ IDirect3DIndexBuffer9 * dxIndices;
+ int numVerts;
+ int numIndices;
+ IDirect3DTexture9 * texture;
+ ovrVector2f UVScaleOffset[2];
+ } eachEye[2];
+
+
+ //Structure to store our state changes
+ #define MAX_SAVED_STATES 100
+ struct SavedStateType
+ {
+ int which; //0 for samplerstate, 1 for renderstate
+ int type;
+ DWORD valueToRevertTo;
+ } savedState[MAX_SAVED_STATES];
+
+ //Keep track of how many we've done, for reverting
+ int numSavedStates;
+
+
+
+};
+
+}}} // OVR::CAPI::D3D9
diff --git a/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_Util.cpp b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_Util.cpp
new file mode 100644
index 0000000..ea36100
--- /dev/null
+++ b/LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_Util.cpp
@@ -0,0 +1,317 @@
+/************************************************************************************
+
+Filename : CAPI_D3D1X_Util.cpp
+Content : D3D9 utility functions for rendering
+Created : March 7 , 2014
+Authors : Tom Heath
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_D3D9_DistortionRenderer.h"
+#define OVR_D3D_VERSION 9
+#include "../../OVR_CAPI_D3D.h"
+
+
+namespace OVR { namespace CAPI { namespace D3D9 {
+
+
+
+#define PRECOMPILE_FLAG 0
+#if !PRECOMPILE_FLAG
+//To make these, you need to run it with PRECOMPILE_FLAG, which also uses them, so good for debugging.
+//Then cut and paste these from the output window.
+//Then turn off the flag.
+DWORD precompiledVertexShaderSrc[95] = {4294836736,3014654,1111577667,28,127,4294836736,2,28,33024,120,68,131074,655361,88,0,104,2,131073,88,0,1415936325,1668436847,1716475477,1952805734,2880154368,196609,131073,1,0,1415936325,1668436847,1666405973,6646881,845116278,1291858015,1869767529,1952870259,693250080,1397508128,1750278220,1919247457,1836008224,1701603696,775495794,959330610,858665525,3223857,83886161,2685337601,1065353216,0,1056964608,0,33554463,2147483648,2416902144,33554463,2147614720,2416902145,33554463,2147483653,2416902146,33554463,2147549189,2416902147,33554463,2147614725,2416902148,33554433,2147680256,2699296768,67108868,3758292992,2162425856,2430861314,2699296770,67108868,3758292993,2162425856,2430861315,2699296770,67108868,3758292994,2162425856,2430861316,2699296770,67108868,3222208512,2416181248,2689597441,2686779393,33554433,3758161923,2415919105,65535,};
+DWORD precompiledVertexShaderTimewarpSrc[293] = {4294836736,4456446,1111577667,28,215,4294836736,4,28,33024,208,108,1310722,5373956,124,0,140,262146,1179652,124,0,157,131074,655361,176,0,192,2,131073,176,0,1382381893,1952543855,1164865385,2868929646,196611,262148,1,0,1382381893,1952543855,1399746409,1953653108,1702446336,1918070612,1331058019,1702061670,2880110708,196609,131073,1,0,1415936325,1668436847,1666405973,6646881,845116278,1291858015,1869767529,1952870259,693250080,1397508128,1750278220,1919247457,1836008224,1701603696,775495794,959330610,858665525,3223857,83886161,2685337601,1065353216,0,1056964608,0,33554463,2147483648,2416902144,33554463,2147549184,2416902145,33554463,2147614720,2416902146,33554463,2147483653,2416902147,33554463,2147549189,2416902148,33554463,2147614725,2416902149,33554433,2147549184,2695495684,50331650,2147549185,2164260864,2695495700,33554433,2147614720,2695495685,50331650,2147614721,2169831424,2695495701,33554433,2147745792,2695495686,50331650,2147745793,2175401984,2695495702,33554433,2148007936,2695495687,50331650,2148007937,2180972544,2695495703,67108868,2148466688,2415919105,2162425857,2162425856,67108868,2148466689,2416181251,2689597441,2684682241,50331657,2147549186,2162425856,2162425857,33554438,2147549186,2147483650,33554433,2147680259,2699296772,50331650,2147876866,2177892355,2697986068,67108868,2147549187,2415919105,2158624770,2689925124,67108868,2147549188,2415919105,2153054210,2684354564,33554433,2147680261,2699296773,50331650,2147876866,2177105925,2697199637,67108868,2147614723,2415919105,2153054210,2689925125,67108868,2147614724,2415919105,2158624770,2684354565,33554433,2147680261,2699296774,50331650,2147811333,2177171461,2697265174,67108868,2147745795,2415919105,2147483653,2689925126,67108868,2147745796,2415919105,2158624773,2684354566,33554433,2147680261,2699296775,50331650,2148073477,2166685701,2686779415,67108868,2148007939,2415919105,2147483653,2689925127,67108868,2148007940,2415919105,2164195333,2684354567,50331657,2147549189,2162425860,2162425857,50331657,2147614725,2162425859,2162425857,50331653,2147680257,2147483650,2162425861,33554433,2147680258,2699296768,67108868,3758292992,2162425858,2162425857,2699296770,67108868,2148466689,2416181252,2689597441,2684682241,50331657,2147549189,2162425860,2162425857,50331657,2147614725,2162425859,2162425857,50331657,2147549185,2162425856,2162425857,33554438,2147549185,2147483649,50331653,2147680257,2147483649,2162425861,67108868,3758292993,2162425858,2162425857,2699296770,67108868,2148466689,2416181253,2689597441,2684682241,50331657,2147549188,2162425860,2162425857,50331657,2147614724,2162425859,2162425857,50331657,2147549184,2162425856,2162425857,33554438,2147549184,2147483648,50331653,2147680256,2147483648,2162425860,67108868,3758292994,2162425858,2162425856,2699296770,67108868,3222208512,2416181248,2689597441,2686779393,33554433,3758161923,2415919106,65535,};
+DWORD precompiledPixelShaderSrc[84] = {4294902528,2228222,1111577667,28,79,4294902528,1,28,33024,72,48,3,131073,56,0,1954047316,6648437,786436,65537,1,0,861893488,1291858015,1869767529,1952870259,693250080,1397508128,1750278220,1919247457,1836008224,1701603696,775495794,959330610,858665525,3223857,83886161,2685337600,1065353216,0,0,0,33554463,2147483653,2416115712,33554463,2147549189,2416115713,33554463,2147614725,2416115714,33554463,2147680261,2415984643,33554463,2415919104,2685339648,50331714,2148466688,2430861312,2699298816,67108868,2148073472,2147483648,2690908160,2686779392,50331714,2148466689,2430861313,2699298816,33554433,2147614720,2153054209,50331714,2148466689,2430861314,2699298816,33554433,2147745792,2158624769,50331653,2148468736,2162425856,2415919107,65535,};
+
+#else
+#include "d3dcompiler.h"
+/***************************************************************************/
+const char* VertexShaderSrc =
+
+ "float2 EyeToSourceUVScale : register(c0); \n"
+ "float2 EyeToSourceUVOffset : register(c2); \n"
+
+ "void main(in float2 Position : POSITION, in float TimeWarp : POSITION1, \n"
+ " in float Vignette : POSITION2, in float2 TexCoord0 : TEXCOORD0, \n"
+ " in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2, \n"
+ " out float4 oPosition : SV_Position, out float2 oTexCoord0 : TEXCOORD0, \n"
+ " out float2 oTexCoord1 : TEXCOORD1, out float2 oTexCoord2 : TEXCOORD2, \n"
+ " out float oVignette : TEXCOORD3) \n"
+ "{ \n"
+ " oTexCoord0 = EyeToSourceUVScale * TexCoord0 + EyeToSourceUVOffset; \n"
+ " oTexCoord1 = EyeToSourceUVScale * TexCoord1 + EyeToSourceUVOffset; \n"
+ " oTexCoord2 = EyeToSourceUVScale * TexCoord2 + EyeToSourceUVOffset; \n"
+ " oVignette = Vignette; \n"
+ " oPosition = float4(Position.xy, 0.5, 1.0); \n"
+ "}";
+
+/***************************************************************************/
+const char* VertexShaderTimewarpSrc =
+
+ "float2 EyeToSourceUVScale : register(c0); \n"
+ "float2 EyeToSourceUVOffset : register(c2); \n"
+ "float4x4 EyeRotationStart : register(c4); \n"
+ "float4x4 EyeRotationEnd : register(c20); \n"
+
+ "float2 TimewarpTexCoord(float2 TexCoord, float4x4 rotMat) \n"
+ "{ \n"
+ " float3 transformed = float3( mul ( rotMat, float4(TexCoord.xy, 1, 1) ).xyz); \n"
+ " float2 flattened = (transformed.xy / transformed.z); \n"
+ " return(EyeToSourceUVScale * flattened + EyeToSourceUVOffset); \n"
+ "} \n"
+ "void main(in float2 Position : POSITION, in float TimeWarp : POSITION1, \n"
+ " in float Vignette : POSITION2, in float2 TexCoord0 : TEXCOORD0, \n"
+ " in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2, \n"
+ " out float4 oPosition : SV_Position, out float2 oTexCoord0 : TEXCOORD0, \n"
+ " out float2 oTexCoord1 : TEXCOORD1, out float2 oTexCoord2 : TEXCOORD2, \n"
+ " out float oVignette : TEXCOORD3) \n"
+ "{ \n"
+ " float4x4 lerpedEyeRot = lerp(EyeRotationStart, EyeRotationEnd, TimeWarp); \n"
+ " oTexCoord0 = TimewarpTexCoord(TexCoord0,lerpedEyeRot); \n"
+ " oTexCoord1 = TimewarpTexCoord(TexCoord1,lerpedEyeRot); \n"
+ " oTexCoord2 = TimewarpTexCoord(TexCoord2,lerpedEyeRot); \n"
+ " oVignette = Vignette; \n"
+ " oPosition = float4(Position.xy, 0.5, 1.0); \n"
+ "}";
+
+/***************************************************************************/
+const char* PixelShaderSrc =
+
+ " sampler2D Texture : register(s0); \n"
+
+ "float4 main(in float4 oPosition : SV_Position, in float2 oTexCoord0 : TEXCOORD0, \n"
+ " in float2 oTexCoord1 : TEXCOORD1, in float2 oTexCoord2 : TEXCOORD2, \n"
+ " in float oVignette : TEXCOORD3) \n"
+ " : SV_Target \n"
+ "{ \n"
+ " float R = tex2D(Texture,oTexCoord0).r; \n"
+ " float G = tex2D(Texture,oTexCoord1).g; \n"
+ " float B = tex2D(Texture,oTexCoord2).b; \n"
+ " return (oVignette*float4(R,G,B,1)); \n"
+ "}";
+
+/*************************************************************/
+ID3DBlob* ShaderCompile(char * shaderName, const char * shaderSrcString, const char * profile)
+{
+ ID3DBlob* pShaderCode = NULL;
+ ID3DBlob* pErrorMsg = NULL;
+
+ if (FAILED(D3DCompile(shaderSrcString, strlen(shaderSrcString),NULL,NULL,NULL,
+ "main",profile,D3DCOMPILE_OPTIMIZATION_LEVEL3,0,
+ &pShaderCode,&pErrorMsg)))
+ MessageBoxA(NULL,(char *) pErrorMsg->GetBufferPointer(),"", MB_OK);
+ if (pErrorMsg) pErrorMsg->Release();
+
+ //Now write out blob
+ char tempString[1000];
+ int numDWORDs = ((int)pShaderCode->GetBufferSize())/4;
+ DWORD * ptr = (DWORD *)pShaderCode->GetBufferPointer();
+ sprintf_s(tempString,"DWORD %s[%d] = {",shaderName,numDWORDs);
+ OutputDebugStringA(tempString);
+ for (int i = 0;i < numDWORDs; i++)
+ {
+ sprintf_s(tempString,"%lu,",ptr[i]);
+ OutputDebugStringA(tempString);
+ }
+ OutputDebugStringA("};\n");
+
+ return(pShaderCode);
+}
+#endif
+
+/***********************************************************/
+void DistortionRenderer::CreateDistortionShaders(void)
+{
+#if PRECOMPILE_FLAG
+ ID3DBlob * pShaderCode;
+ pShaderCode = ShaderCompile("precompiledVertexShaderSrc",VertexShaderSrc,"vs_2_0");
+ device->CreateVertexShader( ( DWORD* )pShaderCode->GetBufferPointer(), &vertexShader );
+ pShaderCode->Release();
+
+ pShaderCode = ShaderCompile("precompiledVertexShaderTimewarpSrc",VertexShaderTimewarpSrc,"vs_2_0");
+ device->CreateVertexShader( ( DWORD* )pShaderCode->GetBufferPointer(), &vertexShaderTimewarp );
+ pShaderCode->Release();
+
+ pShaderCode = ShaderCompile("precompiledPixelShaderSrc",PixelShaderSrc,"ps_3_0");
+ device->CreatePixelShader( ( DWORD* )pShaderCode->GetBufferPointer(), &pixelShader );
+ pShaderCode->Release();
+#else
+ device->CreateVertexShader( precompiledVertexShaderSrc, &vertexShader );
+ device->CreateVertexShader( precompiledVertexShaderTimewarpSrc, &vertexShaderTimewarp );
+ device->CreatePixelShader( precompiledPixelShaderSrc, &pixelShader );
+#endif
+}
+
+
+/***************************************************/
+void DistortionRenderer::CreateVertexDeclaration(void)
+{
+ static const D3DVERTEXELEMENT9 VertexElements[7] = {
+ { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 1 },
+ { 0, 12, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 2 },
+ { 0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ { 0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },
+ D3DDECL_END() };
+ device->CreateVertexDeclaration( VertexElements, &vertexDecl );
+}
+
+
+/******************************************************/
+void DistortionRenderer::Create_Distortion_Models(void)
+{
+ //Make the distortion models
+ for (int eye=0;eye<2;eye++)
+ {
+ ovrVector2f dummy_UVScaleOffset[2]; //because it needs to be updated by a later call
+ FOR_EACH_EYE * e = &eachEye[eye];
+ ovrDistortionMesh meshData;
+ ovrHmd_CreateDistortionMesh(HMD, RState.EyeRenderDesc[eye].Desc, distortionCaps,
+ dummy_UVScaleOffset, &meshData);
+
+ e->numVerts = meshData.VertexCount;
+ e->numIndices = meshData.IndexCount;
+
+ device->CreateVertexBuffer( (e->numVerts)*sizeof(ovrDistortionVertex),0, 0,D3DPOOL_MANAGED, &e->dxVerts, NULL );
+ ovrDistortionVertex * dxv; e->dxVerts->Lock( 0, 0, (void**)&dxv, 0 );
+ for (int v=0;v<e->numVerts;v++) dxv[v] = meshData.pVertexData[v];
+
+ device->CreateIndexBuffer( (e->numIndices)*sizeof(u_short),0, D3DFMT_INDEX16,D3DPOOL_MANAGED, &e->dxIndices, NULL );
+ unsigned short* dxi; e->dxIndices->Lock( 0, 0, (void**)&dxi, 0 );
+ for (int i=0;i<e->numIndices;i++) dxi[i] = meshData.pIndexData[i];
+
+ ovrHmd_DestroyDistortionMesh( &meshData );
+ }
+}
+
+/**********************************************************/
+void DistortionRenderer::RenderBothDistortionMeshes(void)
+{
+ //Record and set render state
+ numSavedStates=0;
+ RecordAndSetState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
+ RecordAndSetState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
+ RecordAndSetState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
+ RecordAndSetState(0, D3DSAMP_BORDERCOLOR, 0x000000 );
+ RecordAndSetState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER );
+ RecordAndSetState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER );
+ RecordAndSetState(1, D3DRS_MULTISAMPLEANTIALIAS, FALSE );
+ RecordAndSetState(1, D3DRS_DITHERENABLE, FALSE );
+ RecordAndSetState(1, D3DRS_ZENABLE, FALSE );
+ RecordAndSetState(1, D3DRS_ZWRITEENABLE, TRUE );
+ RecordAndSetState(1, D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
+ RecordAndSetState(1, D3DRS_CULLMODE , D3DCULL_CCW );
+ RecordAndSetState(1, D3DRS_ALPHABLENDENABLE , FALSE );
+ RecordAndSetState(1, D3DRS_DEPTHBIAS , 0 );
+ RecordAndSetState(1, D3DRS_SRCBLEND , D3DBLEND_SRCALPHA );
+ RecordAndSetState(1, D3DRS_DESTBLEND , D3DBLEND_INVSRCALPHA );
+ RecordAndSetState(1, D3DRS_FILLMODE, D3DFILL_SOLID );
+ RecordAndSetState(1, D3DRS_ALPHATESTENABLE, FALSE);
+ RecordAndSetState(1, D3DRS_DEPTHBIAS , 0 );
+ RecordAndSetState(1, D3DRS_LIGHTING, FALSE );
+ RecordAndSetState(1, D3DRS_FOGENABLE, FALSE );
+
+ for (int eye=0; eye<2; eye++)
+ {
+ FOR_EACH_EYE * e = &eachEye[eye];
+ D3DVIEWPORT9 vp; vp.X=0; vp.Y=0; vp.Width=screenSize.w; vp.Height=screenSize.h; vp.MinZ=0; vp.MaxZ = 1;
+ device->SetViewport(&vp);
+ device->SetStreamSource( 0, e->dxVerts,0, sizeof(ovrDistortionVertex) );
+ device->SetVertexDeclaration( vertexDecl );
+ device->SetIndices( e->dxIndices );
+ device->SetPixelShader( pixelShader );
+ device->SetTexture( 0, e->texture);
+
+ //Choose which vertex shader, with associated additional inputs
+ if (distortionCaps & ovrDistortion_TimeWarp)
+ {
+ device->SetVertexShader( vertexShaderTimewarp );
+
+ ovrMatrix4f timeWarpMatrices[2];
+ ovrHmd_GetEyeTimewarpMatrices(HMD, (ovrEyeType)eye,
+ RState.EyeRenderPoses[eye], timeWarpMatrices);
+
+ // Feed identity like matrices in until we get proper timewarp calculation going on
+ device->SetVertexShaderConstantF(4, (float *) &timeWarpMatrices[0],4);
+ device->SetVertexShaderConstantF(20,(float *) &timeWarpMatrices[1],4);
+ }
+ else
+ {
+ device->SetVertexShader( vertexShader );
+ }
+
+
+ //Set up vertex shader constants
+ device->SetVertexShaderConstantF( 0, ( FLOAT* )&(e->UVScaleOffset[0]), 1 );
+ device->SetVertexShaderConstantF( 2, ( FLOAT* )&(e->UVScaleOffset[1]), 1 );
+
+ device->DrawIndexedPrimitive( D3DPT_TRIANGLELIST,0,0,e->numVerts,0,e->numIndices/3);
+ }
+
+ //Revert render state
+ RevertAllStates();
+
+}
+
+/*********************************************************************************/
+void DistortionRenderer::RecordAndSetState(int which, int type, DWORD newValue)
+{
+ SavedStateType * sst = &savedState[numSavedStates++];
+ sst->which = which;
+ sst->type = type;
+ if (which==0)
+ {
+ device->GetSamplerState( 0, (D3DSAMPLERSTATETYPE)type, &sst->valueToRevertTo);
+ device->SetSamplerState( 0, (D3DSAMPLERSTATETYPE)type, newValue);
+ }
+ else
+ {
+ device->GetRenderState( (D3DRENDERSTATETYPE)type, &sst->valueToRevertTo);
+ device->SetRenderState( (D3DRENDERSTATETYPE)type, newValue);
+ }
+}
+/*********************************************************************************/
+void DistortionRenderer::RevertAllStates(void)
+{
+ for (int i=0;i<numSavedStates;i++)
+ {
+ SavedStateType * sst = &savedState[i];
+ if (sst->which==0)
+ {
+ device->SetSamplerState( 0, (D3DSAMPLERSTATETYPE)sst->type, sst->valueToRevertTo);
+ }
+ else
+ {
+ device->SetRenderState( (D3DRENDERSTATETYPE)sst->type, sst->valueToRevertTo);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+}}} \ No newline at end of file
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp
new file mode 100644
index 0000000..a953d73
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp
@@ -0,0 +1,1006 @@
+/************************************************************************************
+
+Filename : CAPI_GL_DistortionRenderer.h
+Content : Distortion renderer header for GL
+Created : November 11, 2013
+Authors : David Borel, Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "CAPI_GL_DistortionRenderer.h"
+
+#include "../../OVR_CAPI_GL.h"
+
+namespace OVR { namespace CAPI { namespace GL {
+
+
+static const char SimpleQuad_vs[] =
+ "uniform vec2 PositionOffset;\n"
+ "uniform vec2 Scale;\n"
+
+ "attribute vec3 Position;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(Position.xy * Scale + PositionOffset, 0.5, 1.0);\n"
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuad_vs_refl[] =
+{
+ { "PositionOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "Scale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+};
+
+static const char SimpleQuad_fs[] =
+ "uniform vec4 Color;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = Color;\n"
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuad_fs_refl[] =
+{
+ { "Color", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 16 },
+};
+
+
+static const char Distortion_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+
+ "attribute vec2 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord0;\n"
+
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord0;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.5;\n"
+ " gl_Position.w = 1.0;\n"
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " oTexCoord0 = TexCoord0 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord0.y = 1-oTexCoord0.y;\n"
+ " oColor = Color;\n" // Used for vignette fade.
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform Distortion_vs_refl[] =
+{
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+};
+
+static const char Distortion_fs[] =
+ "uniform sampler2D Texture0;\n"
+
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord0;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(Texture0, oTexCoord0);\n"
+ " gl_FragColor.a = 1.0;\n"
+ "}\n";
+
+
+static const char DistortionTimewarp_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+ "uniform mat4 EyeRotationStart;\n"
+ "uniform mat4 EyeRotationEnd;\n"
+
+ "attribute vec2 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord0;\n"
+
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord0;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.0;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ " vec3 TanEyeAngle = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );\n"
+
+ // Accurate time warp lerp vs. faster
+#if 1
+ // Apply the two 3x3 timewarp rotations to these vectors.
+ " vec3 TransformedStart = (EyeRotationStart * vec4(TanEyeAngle, 0)).xyz;\n"
+ " vec3 TransformedEnd = (EyeRotationEnd * vec4(TanEyeAngle, 0)).xyz;\n"
+ // And blend between them.
+ " vec3 Transformed = mix ( TransformedStart, TransformedEnd, Color.a );\n"
+#else
+ " mat3 EyeRotation = mix ( EyeRotationStart, EyeRotationEnd, Color.a );\n"
+ " vec3 Transformed = EyeRotation * TanEyeAngle;\n"
+#endif
+
+ // Project them back onto the Z=1 plane of the rendered images.
+ " float RecipZ = 1.0 / Transformed.z;\n"
+ " vec2 Flattened = vec2 ( Transformed.x * RecipZ, Transformed.y * RecipZ );\n"
+
+ // These are now still in TanEyeAngle space.
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " vec2 SrcCoord = Flattened * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord0 = SrcCoord;\n"
+ " oTexCoord0.y = 1-oTexCoord0.y;\n"
+ " oColor = Color.r;\n" // Used for vignette fade.
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform DistortionTimewarp_vs_refl[] =
+{
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+};
+
+
+static const char DistortionPositionalTimewarp_vs[] =
+ "#version 150\n"
+
+ "uniform sampler2D Texture0;\n"
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+ "uniform vec2 DepthProjector;\n"
+ "uniform vec2 DepthDimSize;\n"
+ "uniform mat4 EyeRotationStart;\n"
+ "uniform mat4 EyeRotationEnd;\n"
+
+ "in vec2 Position;\n"
+ "in vec4 Color;\n"
+ "in vec2 TexCoord0;\n"
+ "in vec2 TexCoord1;\n"
+ "in vec2 TexCoord2;\n"
+
+ "out vec4 oColor;\n"
+ "out vec2 oTexCoord0;\n"
+
+ "vec4 PositionFromDepth(vec2 inTexCoord)\n"
+ "{\n"
+ " vec2 eyeToSourceTexCoord = inTexCoord * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " eyeToSourceTexCoord.y = 1 - eyeToSourceTexCoord.y;\n"
+ " float depth = texelFetch(Texture0, ivec2(eyeToSourceTexCoord * DepthDimSize), 0).x;\n"
+ " float linearDepth = DepthProjector.y / (depth - DepthProjector.x);\n"
+ " vec4 retVal = vec4(inTexCoord, 1, 1);\n"
+ " retVal.xyz *= linearDepth;\n"
+ " return retVal;\n"
+ "}\n"
+
+ "vec2 TimewarpTexCoordToWarpedPos(vec2 inTexCoord, float a)\n"
+ "{\n"
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ // Apply the 4x4 timewarp rotation to these vectors.
+ " vec4 inputPos = PositionFromDepth(inTexCoord);\n"
+ " vec3 transformed = mix ( EyeRotationStart * inputPos, EyeRotationEnd * inputPos, a ).xyz;\n"
+ // Project them back onto the Z=1 plane of the rendered images.
+ " vec2 flattened = transformed.xy / transformed.z;\n"
+ // Scale them into ([0,0.5],[0,1]) or ([0.5,0],[0,1]) UV lookup space (depending on eye)
+ " vec2 noDepthUV = flattened * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ //" float depth = texture2DLod(Texture0, noDepthUV, 0).r;\n"
+ " return noDepthUV.xy;\n"
+ "}\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.0;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // warped positions are a bit more involved, hence a separate function
+ " oTexCoord0 = TimewarpTexCoordToWarpedPos(TexCoord0, Color.a);\n"
+ " oTexCoord0.y = 1-oTexCoord0.y;\n"
+
+ " oColor = vec4(Color.r); // Used for vignette fade.\n"
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform DistortionPositionalTimewarp_vs_refl[] =
+{
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+};
+
+
+static const char DistortionChroma_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+
+ "attribute vec2 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord0;\n"
+ "attribute vec2 TexCoord1;\n"
+ "attribute vec2 TexCoord2;\n"
+
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord0;\n"
+ "varying vec2 oTexCoord1;\n"
+ "varying vec2 oTexCoord2;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.5;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " oTexCoord0 = TexCoord0 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord0.y = 1-oTexCoord0.y;\n"
+ " oTexCoord1 = TexCoord1 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord1.y = 1-oTexCoord1.y;\n"
+ " oTexCoord2 = TexCoord2 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord2.y = 1-oTexCoord2.y;\n"
+
+ " oColor = Color;\n" // Used for vignette fade.
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform DistortionChroma_vs_refl[] =
+{
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+};
+
+static const char DistortionChroma_fs[] =
+ "uniform sampler2D Texture0;\n"
+
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord0;\n"
+ "varying vec2 oTexCoord1;\n"
+ "varying vec2 oTexCoord2;\n"
+
+ "void main()\n"
+ "{\n"
+ " float ResultR = texture2D(Texture0, oTexCoord0).r;\n"
+ " float ResultG = texture2D(Texture0, oTexCoord1).g;\n"
+ " float ResultB = texture2D(Texture0, oTexCoord2).b;\n"
+
+ " gl_FragColor = vec4(ResultR * oColor.r, ResultG * oColor.g, ResultB * oColor.b, 1.0);\n"
+ "}\n";
+
+
+static const char DistortionTimewarpChroma_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+ "uniform mat4 EyeRotationStart;\n"
+ "uniform mat4 EyeRotationEnd;\n"
+
+ "attribute vec2 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord0;\n"
+ "attribute vec2 TexCoord1;\n"
+ "attribute vec2 TexCoord2;\n"
+
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord0;\n"
+ "varying vec2 oTexCoord1;\n"
+ "varying vec2 oTexCoord2;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.0;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ " vec3 TanEyeAngleR = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );\n"
+ " vec3 TanEyeAngleG = vec3 ( TexCoord1.x, TexCoord1.y, 1.0 );\n"
+ " vec3 TanEyeAngleB = vec3 ( TexCoord2.x, TexCoord2.y, 1.0 );\n"
+
+ // Accurate time warp lerp vs. faster
+#if 1
+ // Apply the two 3x3 timewarp rotations to these vectors.
+ " vec3 TransformedRStart = (EyeRotationStart * vec4(TanEyeAngleR, 0)).xyz;\n"
+ " vec3 TransformedGStart = (EyeRotationStart * vec4(TanEyeAngleG, 0)).xyz;\n"
+ " vec3 TransformedBStart = (EyeRotationStart * vec4(TanEyeAngleB, 0)).xyz;\n"
+ " vec3 TransformedREnd = (EyeRotationEnd * vec4(TanEyeAngleR, 0)).xyz;\n"
+ " vec3 TransformedGEnd = (EyeRotationEnd * vec4(TanEyeAngleG, 0)).xyz;\n"
+ " vec3 TransformedBEnd = (EyeRotationEnd * vec4(TanEyeAngleB, 0)).xyz;\n"
+
+ // And blend between them.
+ " vec3 TransformedR = mix ( TransformedRStart, TransformedREnd, Color.a );\n"
+ " vec3 TransformedG = mix ( TransformedGStart, TransformedGEnd, Color.a );\n"
+ " vec3 TransformedB = mix ( TransformedBStart, TransformedBEnd, Color.a );\n"
+#else
+ " mat3 EyeRotation = mix ( EyeRotationStart, EyeRotationEnd, Color.a );\n"
+ " vec3 TransformedR = EyeRotation * TanEyeAngleR;\n"
+ " vec3 TransformedG = EyeRotation * TanEyeAngleG;\n"
+ " vec3 TransformedB = EyeRotation * TanEyeAngleB;\n"
+#endif
+
+ // Project them back onto the Z=1 plane of the rendered images.
+ " float RecipZR = 1.0 / TransformedR.z;\n"
+ " float RecipZG = 1.0 / TransformedG.z;\n"
+ " float RecipZB = 1.0 / TransformedB.z;\n"
+ " vec2 FlattenedR = vec2 ( TransformedR.x * RecipZR, TransformedR.y * RecipZR );\n"
+ " vec2 FlattenedG = vec2 ( TransformedG.x * RecipZG, TransformedG.y * RecipZG );\n"
+ " vec2 FlattenedB = vec2 ( TransformedB.x * RecipZB, TransformedB.y * RecipZB );\n"
+
+ // These are now still in TanEyeAngle space.
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " vec2 SrcCoordR = FlattenedR * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " vec2 SrcCoordG = FlattenedG * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " vec2 SrcCoordB = FlattenedB * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+
+ " oTexCoord0 = SrcCoordR;\n"
+ " oTexCoord0.y = 1-oTexCoord0.y;\n"
+ " oTexCoord1 = SrcCoordG;\n"
+ " oTexCoord1.y = 1-oTexCoord1.y;\n"
+ " oTexCoord2 = SrcCoordB;\n"
+ " oTexCoord2.y = 1-oTexCoord2.y;\n"
+
+ " oColor = Color.r;\n" // Used for vignette fade.
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform DistortionTimewarpChroma_vs_refl[] =
+{
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+ { "EyeRotationStart", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 16, 64 },
+ { "EyeRotationEnd", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 80, 64 },
+};
+
+
+static const char DistortionPositionalTimewarpChroma_vs[] =
+ "#version 150\n"
+ "uniform sampler2D Texture0;\n"
+ "uniform sampler2D Texture1;\n"
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+ "uniform vec2 DepthProjector;\n"
+ "uniform vec2 DepthDimSize;\n"
+ "uniform mat4 EyeRotationStart;\n"
+ "uniform mat4 EyeRotationEnd;\n"
+
+ "in vec2 Position;\n"
+ "in vec4 Color;\n"
+ "in vec2 TexCoord0;\n"
+ "in vec2 TexCoord1;\n"
+ "in vec2 TexCoord2;\n"
+
+ "out vec4 oColor;\n"
+ "out vec2 oTexCoord0;\n"
+ "out vec2 oTexCoord1;\n"
+ "out vec2 oTexCoord2;\n"
+
+ "vec4 PositionFromDepth(vec2 inTexCoord)\n"
+ "{\n"
+ " vec2 eyeToSourceTexCoord = inTexCoord * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " eyeToSourceTexCoord.y = 1 - eyeToSourceTexCoord.y;\n"
+ " float depth = texelFetch(Texture1, ivec2(eyeToSourceTexCoord * DepthDimSize), 0).x;\n"
+ " float linearDepth = DepthProjector.y / (depth - DepthProjector.x);\n"
+ " vec4 retVal = vec4(inTexCoord, 1, 1);\n"
+ " retVal.xyz *= linearDepth;\n"
+ " return retVal;\n"
+ "}\n"
+
+ "vec2 TimewarpTexCoordToWarpedPos(vec2 inTexCoord, float a)\n"
+ "{\n"
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ // Apply the 4x4 timewarp rotation to these vectors.
+ " vec4 inputPos = PositionFromDepth(inTexCoord);\n"
+ " vec3 transformed = mix ( EyeRotationStart * inputPos, EyeRotationEnd * inputPos, a ).xyz;\n"
+ // Project them back onto the Z=1 plane of the rendered images.
+ " vec2 flattened = transformed.xy / transformed.z;\n"
+ // Scale them into ([0,0.5],[0,1]) or ([0.5,0],[0,1]) UV lookup space (depending on eye)
+ " vec2 noDepthUV = flattened * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ //" float depth = texture2DLod(Texture1, noDepthUV, 0).r;\n"
+ " return noDepthUV.xy;\n"
+ "}\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.0;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // warped positions are a bit more involved, hence a separate function
+ " oTexCoord0 = TimewarpTexCoordToWarpedPos(TexCoord0, Color.a);\n"
+ " oTexCoord0.y = 1-oTexCoord0.y;\n"
+ " oTexCoord1 = TimewarpTexCoordToWarpedPos(TexCoord1, Color.a);\n"
+ " oTexCoord1.y = 1-oTexCoord1.y;\n"
+ " oTexCoord2 = TimewarpTexCoordToWarpedPos(TexCoord2, Color.a);\n"
+ " oTexCoord2.y = 1-oTexCoord2.y;\n"
+
+ " oColor = vec4(Color.r); // Used for vignette fade.\n"
+ "}\n";
+
+const OVR::CAPI::GL::ShaderBase::Uniform DistortionPositionalTimewarpChroma_vs_refl[] =
+{
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+};
+
+
+// Distortion pixel shader lookup.
+// Bit 0: Chroma Correction
+// Bit 1: Timewarp
+
+enum {
+ DistortionVertexShaderBitMask = 3,
+ DistortionVertexShaderCount = DistortionVertexShaderBitMask + 1,
+ DistortionPixelShaderBitMask = 1,
+ DistortionPixelShaderCount = DistortionPixelShaderBitMask + 1
+};
+
+struct ShaderInfo
+{
+ const char* ShaderData;
+ size_t ShaderSize;
+ const ShaderBase::Uniform* ReflectionData;
+ size_t ReflectionSize;
+};
+
+// Do add a new distortion shader use these macros (with or w/o reflection)
+#define SI_NOREFL(shader) { shader, sizeof(shader), NULL, 0 }
+#define SI_REFL__(shader) { shader, sizeof(shader), shader ## _refl, sizeof( shader ## _refl )/sizeof(*(shader ## _refl)) }
+
+
+static ShaderInfo DistortionVertexShaderLookup[DistortionVertexShaderCount] =
+{
+ SI_REFL__(Distortion_vs),
+ SI_REFL__(DistortionChroma_vs),
+ SI_REFL__(DistortionTimewarp_vs),
+ SI_REFL__(DistortionTimewarpChroma_vs)
+ //SI_REFL__(DistortionPositionalTimewarp_vs),
+ //SI_REFL__(DistortionPositionalTimewarpChroma_vs)
+};
+
+static ShaderInfo DistortionPixelShaderLookup[DistortionPixelShaderCount] =
+{
+ SI_NOREFL(Distortion_fs),
+ SI_NOREFL(DistortionChroma_fs)
+};
+
+void DistortionShaderBitIndexCheck()
+{
+ OVR_COMPILER_ASSERT(ovrDistortion_Chromatic == 1);
+ OVR_COMPILER_ASSERT(ovrDistortion_TimeWarp == 2);
+}
+
+
+
+struct DistortionVertex
+{
+ Vector2f Pos;
+ Vector2f TexR;
+ Vector2f TexG;
+ Vector2f TexB;
+ Color Col;
+};
+
+
+// Vertex type; same format is used for all shapes for simplicity.
+// Shapes are built by adding vertices to Model.
+struct LatencyVertex
+{
+ Vector3f Pos;
+ LatencyVertex (const Vector3f& p) : Pos(p) {}
+};
+
+
+//----------------------------------------------------------------------------
+// ***** GL::DistortionRenderer
+
+DistortionRenderer::DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+ : CAPI::DistortionRenderer(ovrRenderAPI_OpenGL, hmd, timeManager, renderState)
+{
+}
+
+DistortionRenderer::~DistortionRenderer()
+{
+ destroy();
+}
+
+// static
+CAPI::DistortionRenderer* DistortionRenderer::Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+{
+ InitGLExtensions();
+
+ return new DistortionRenderer(hmd, timeManager, renderState);
+}
+
+
+bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned distortionCaps)
+{
+ // TBD: Decide if hmdCaps are needed here or are a part of RenderState
+ OVR_UNUSED(hmdCaps);
+
+ const ovrGLConfig* config = (const ovrGLConfig*)apiConfig;
+
+ if (!config)
+ {
+ // Cleanup
+ pEyeTextures[0].Clear();
+ pEyeTextures[1].Clear();
+ memset(&RParams, 0, sizeof(RParams));
+ return true;
+ }
+
+ if (!config->OGL.WglContext || !config->OGL.GdiDc)
+ return false;
+
+ RParams.GdiDc = config->OGL.GdiDc;
+ RParams.Multisample = config->OGL.Header.Multisample;
+ RParams.RTSize = config->OGL.Header.RTSize;
+ RParams.WglContext = config->OGL.WglContext;
+ RParams.Window = config->OGL.Window;
+
+ DistortionCaps = distortionCaps;
+
+ //DistortionWarper.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true);
+
+ pEyeTextures[0] = *new Texture(&RParams, 0, 0);
+ pEyeTextures[1] = *new Texture(&RParams, 0, 0);
+
+ initBuffersAndShaders();
+
+ return true;
+}
+
+
+void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture)
+{
+ //Doesn't do a lot in here??
+ const ovrGLTexture* tex = (const ovrGLTexture*)eyeTexture;
+
+ //Write in values
+ eachEye[eyeId].texture = tex->OGL.TexId;
+
+ if (tex)
+ {
+ //Its only at this point we discover what the viewport of the texture is.
+ //because presumably we allow users to realtime adjust the resolution.
+ //Which begs the question - why did we ask them what viewport they were
+ //using before, which gave them a set of UV offsets. In fact, our
+ //asking for eye mesh must be entirely independed of these viewports,
+ //presumably only to get the parameters.
+
+ ovrEyeDesc ed = RState.EyeRenderDesc[eyeId].Desc;
+ ed.TextureSize = tex->OGL.Header.TextureSize;
+ ed.RenderViewport = tex->OGL.Header.RenderViewport;
+
+ ovrHmd_GetRenderScaleAndOffset(HMD, ed, DistortionCaps, eachEye[eyeId].UVScaleOffset);
+
+ pEyeTextures[eyeId]->UpdatePlaceholderTexture(tex->OGL.TexId,
+ tex->OGL.Header.TextureSize);
+ }
+}
+
+void DistortionRenderer::EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor)
+{
+ if (!TimeManager.NeedDistortionTimeMeasurement())
+ {
+ if (RState.DistortionCaps & ovrDistortion_TimeWarp)
+ {
+ // Wait for timewarp distortion if it is time and Gpu idle
+ FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime);
+ }
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+ }
+ else
+ {
+ // If needed, measure distortion time so that TimeManager can better estimate
+ // latency-reducing time-warp wait timing.
+ WaitUntilGpuIdle();
+ double distortionStartTime = ovr_GetTimeInSeconds();
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+
+ WaitUntilGpuIdle();
+ TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime);
+ }
+
+ if(latencyTesterDrawColor)
+ {
+ renderLatencyQuad(latencyTesterDrawColor);
+ }
+ else if(latencyTester2DrawColor)
+ {
+ renderLatencyPixel(latencyTester2DrawColor);
+ }
+
+ if (swapBuffers)
+ {
+ bool useVsync = ((RState.HMDCaps & ovrHmdCap_NoVSync) == 0);
+ BOOL success;
+ int swapInterval = (useVsync) ? 1 : 0;
+ if (wglGetSwapIntervalEXT() != swapInterval)
+ wglSwapIntervalEXT(swapInterval);
+
+ success = SwapBuffers(RParams.GdiDc);
+ OVR_ASSERT(success);
+
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ // It's critical that this flush is *after* present.
+ WaitUntilGpuIdle();
+ }
+}
+
+void DistortionRenderer::WaitUntilGpuIdle()
+{
+ glFlush();
+ glFinish();
+}
+
+double DistortionRenderer::FlushGpuAndWaitTillTime(double absTime)
+{
+ double initialTime = ovr_GetTimeInSeconds();
+ if (initialTime >= absTime)
+ return 0.0;
+
+ glFlush();
+ glFinish();
+
+ double newTime = initialTime;
+ volatile int i;
+
+ while (newTime < absTime)
+ {
+ for (int j = 0; j < 50; j++)
+ i = 0;
+
+ newTime = ovr_GetTimeInSeconds();
+ }
+
+ // How long we waited
+ return newTime - initialTime;
+}
+
+
+void DistortionRenderer::initBuffersAndShaders()
+{
+ for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
+ {
+ // Allocate & generate distortion mesh vertices.
+ ovrDistortionMesh meshData;
+
+// double startT = ovr_GetTimeInSeconds();
+
+ if (!ovrHmd_CreateDistortionMesh( HMD, RState.EyeRenderDesc[eyeNum].Desc,
+ RState.DistortionCaps,
+ UVScaleOffset[eyeNum], &meshData) )
+ {
+ OVR_ASSERT(false);
+ continue;
+ }
+
+ // Now parse the vertex data and create a render ready vertex buffer from it
+ DistortionVertex * pVBVerts = (DistortionVertex*)OVR_ALLOC ( sizeof(DistortionVertex) * meshData.VertexCount );
+ DistortionVertex * pCurVBVert = pVBVerts;
+ ovrDistortionVertex* pCurOvrVert = meshData.pVertexData;
+
+ for ( unsigned vertNum = 0; vertNum < meshData.VertexCount; vertNum++ )
+ {
+ pCurVBVert->Pos.x = pCurOvrVert->Pos.x;
+ pCurVBVert->Pos.y = pCurOvrVert->Pos.y;
+ pCurVBVert->TexR = (*(Vector2f*)&pCurOvrVert->TexR);
+ pCurVBVert->TexG = (*(Vector2f*)&pCurOvrVert->TexG);
+ pCurVBVert->TexB = (*(Vector2f*)&pCurOvrVert->TexB);
+ // Convert [0.0f,1.0f] to [0,255]
+ pCurVBVert->Col.R = (OVR::UByte)( pCurOvrVert->VignetteFactor * 255.99f );
+ pCurVBVert->Col.G = pCurVBVert->Col.R;
+ pCurVBVert->Col.B = pCurVBVert->Col.R;
+ pCurVBVert->Col.A = (OVR::UByte)( pCurOvrVert->TimeWarpFactor * 255.99f );;
+ pCurOvrVert++;
+ pCurVBVert++;
+ }
+
+ DistortionMeshVBs[eyeNum] = *new Buffer(&RParams);
+ DistortionMeshVBs[eyeNum]->Data ( Buffer_Vertex, pVBVerts, sizeof(DistortionVertex) * meshData.VertexCount );
+ DistortionMeshIBs[eyeNum] = *new Buffer(&RParams);
+ DistortionMeshIBs[eyeNum]->Data ( Buffer_Index, meshData.pIndexData, ( sizeof(INT16) * meshData.IndexCount ) );
+
+ OVR_FREE ( pVBVerts );
+ ovrHmd_DestroyDistortionMesh( &meshData );
+ }
+
+ initShaders();
+}
+
+void DistortionRenderer::renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture)
+{
+ setViewport( Recti(0,0, RParams.RTSize.w, RParams.RTSize.h) );
+
+ glClearColor(
+ RState.ClearColor[0],
+ RState.ClearColor[1],
+ RState.ClearColor[2],
+ RState.ClearColor[3] );
+
+ glClearDepth(0);
+
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+
+ for (int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ ShaderFill distortionShaderFill(DistortionShader);
+ distortionShaderFill.SetTexture(0, eyeNum == 0 ? leftEyeTexture : rightEyeTexture);
+
+ DistortionShader->SetUniform2f("EyeToSourceUVScale", UVScaleOffset[eyeNum][0].x, UVScaleOffset[eyeNum][0].y);
+ DistortionShader->SetUniform2f("EyeToSourceUVOffset", UVScaleOffset[eyeNum][1].x, UVScaleOffset[eyeNum][1].y);
+
+ if (DistortionCaps & ovrDistortion_TimeWarp)
+ {
+ ovrMatrix4f timeWarpMatrices[2];
+ ovrHmd_GetEyeTimewarpMatrices(HMD, (ovrEyeType)eyeNum,
+ RState.EyeRenderPoses[eyeNum], timeWarpMatrices);
+
+ // Feed identity like matrices in until we get proper timewarp calculation going on
+ DistortionShader->SetUniform4x4f("EyeRotationStart", Matrix4f(timeWarpMatrices[0]).Transposed());
+ DistortionShader->SetUniform4x4f("EyeRotationEnd", Matrix4f(timeWarpMatrices[1]).Transposed());
+
+ renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum],
+ NULL, 0, (int)DistortionMeshVBs[eyeNum]->GetSize(), Prim_Triangles, true);
+ }
+ else
+ {
+ renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum],
+ NULL, 0, (int)DistortionMeshVBs[eyeNum]->GetSize(), Prim_Triangles, true);
+ }
+ }
+}
+
+void DistortionRenderer::createDrawQuad()
+{
+ const int numQuadVerts = 4;
+ LatencyTesterQuadVB = *new Buffer(&RParams);
+ if(!LatencyTesterQuadVB)
+ {
+ return;
+ }
+
+ LatencyTesterQuadVB->Data(Buffer_Vertex, NULL, numQuadVerts * sizeof(LatencyVertex));
+ LatencyVertex* vertices = (LatencyVertex*)LatencyTesterQuadVB->Map(0, numQuadVerts * sizeof(LatencyVertex), Map_Discard);
+ if(!vertices)
+ {
+ OVR_ASSERT(false); // failed to lock vertex buffer
+ return;
+ }
+
+ const float left = -1.0f;
+ const float top = -1.0f;
+ const float right = 1.0f;
+ const float bottom = 1.0f;
+
+ vertices[0] = LatencyVertex(Vector3f(left, top, 0.0f));
+ vertices[1] = LatencyVertex(Vector3f(left, bottom, 0.0f));
+ vertices[2] = LatencyVertex(Vector3f(right, top, 0.0f));
+ vertices[3] = LatencyVertex(Vector3f(right, bottom, 0.0f));
+
+ LatencyTesterQuadVB->Unmap(vertices);
+}
+
+void DistortionRenderer::renderLatencyQuad(unsigned char* latencyTesterDrawColor)
+{
+ const int numQuadVerts = 4;
+
+ if(!LatencyTesterQuadVB)
+ {
+ createDrawQuad();
+ }
+
+ ShaderFill quadFill(SimpleQuadShader);
+ //quadFill.SetInputLayout(SimpleQuadVertexIL);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ SimpleQuadShader->SetUniform2f("Scale", 0.2f, 0.2f);
+ SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterDrawColor[0] / 255.99f,
+ (float)latencyTesterDrawColor[0] / 255.99f,
+ (float)latencyTesterDrawColor[0] / 255.99f,
+ 1.0f);
+
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ SimpleQuadShader->SetUniform2f("PositionOffset", eyeNum == 0 ? -0.4f : 0.4f, 0.0f);
+ renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, NULL, 0, numQuadVerts, Prim_TriangleStrip, false);
+ }
+}
+
+void DistortionRenderer::renderLatencyPixel(unsigned char* latencyTesterPixelColor)
+{
+ const int numQuadVerts = 4;
+
+ if(!LatencyTesterQuadVB)
+ {
+ createDrawQuad();
+ }
+
+ ShaderFill quadFill(SimpleQuadShader);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterPixelColor[0] / 255.99f,
+ (float)latencyTesterPixelColor[0] / 255.99f,
+ (float)latencyTesterPixelColor[0] / 255.99f,
+ 1.0f);
+
+ Vector2f scale(2.0f / RParams.RTSize.w, 2.0f / RParams.RTSize.h);
+ SimpleQuadShader->SetUniform2f("Scale", scale.x, scale.y);
+ SimpleQuadShader->SetUniform2f("PositionOffset", 1.0f, 1.0f);
+ renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, NULL, 0, numQuadVerts, Prim_TriangleStrip, false);
+}
+
+void DistortionRenderer::renderPrimitives(
+ const ShaderFill* fill,
+ Buffer* vertices, Buffer* indices,
+ Matrix4f* viewMatrix, int offset, int count,
+ PrimitiveType rprim, bool useDistortionVertex)
+{
+ ShaderSet* shaders = (ShaderSet*) ((ShaderFill*)fill)->GetShaders();
+
+ GLenum prim;
+ switch (rprim)
+ {
+ case Prim_Triangles:
+ prim = GL_TRIANGLES;
+ break;
+ case Prim_Lines:
+ prim = GL_LINES;
+ break;
+ case Prim_TriangleStrip:
+ prim = GL_TRIANGLE_STRIP;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ fill->Set();
+ if (shaders->ProjLoc >= 0)
+ glUniformMatrix4fv(shaders->ProjLoc, 1, 0, &StdUniforms.Proj.M[0][0]);
+ if (shaders->ViewLoc >= 0 && viewMatrix != NULL)
+ glUniformMatrix4fv(shaders->ViewLoc, 1, 0, &viewMatrix->Transposed().M[0][0]);
+
+ //if (shaders->UsesLighting && Lighting->Version != shaders->LightingVer)
+ //{
+ // shaders->LightingVer = Lighting->Version;
+ // Lighting->Set(shaders);
+ //}
+
+ glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer);
+ for (int i = 0; i < 5; i++)
+ glEnableVertexAttribArray(i);
+
+ GLuint prog = fill->GetShaders()->Prog;
+
+ if (useDistortionVertex)
+ {
+ GLint posLoc = glGetAttribLocation(prog, "Position");
+ GLint colLoc = glGetAttribLocation(prog, "Color");
+ GLint tc0Loc = glGetAttribLocation(prog, "TexCoord0");
+ GLint tc1Loc = glGetAttribLocation(prog, "TexCoord1");
+ GLint tc2Loc = glGetAttribLocation(prog, "TexCoord2");
+
+ glVertexAttribPointer(posLoc, 2, GL_FLOAT, false, sizeof(DistortionVertex), (char*)offset + offsetof(DistortionVertex, Pos));
+ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, true, sizeof(DistortionVertex), (char*)offset + offsetof(DistortionVertex, Col));
+ glVertexAttribPointer(tc0Loc, 2, GL_FLOAT, false, sizeof(DistortionVertex), (char*)offset + offsetof(DistortionVertex, TexR));
+ glVertexAttribPointer(tc1Loc, 2, GL_FLOAT, false, sizeof(DistortionVertex), (char*)offset + offsetof(DistortionVertex, TexG));
+ glVertexAttribPointer(tc2Loc, 2, GL_FLOAT, false, sizeof(DistortionVertex), (char*)offset + offsetof(DistortionVertex, TexB));
+ }
+ else
+ {
+ GLint posLoc = glGetAttribLocation(prog, "Position");
+
+ glVertexAttribPointer(posLoc, 3, GL_FLOAT, false, sizeof(LatencyVertex), (char*)offset + offsetof(LatencyVertex, Pos));
+ }
+
+ if (indices)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer);
+ glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ else
+ {
+ glDrawArrays(prim, 0, count);
+ }
+
+ for (int i = 0; i < 5; i++)
+ glDisableVertexAttribArray(i);
+}
+
+void DistortionRenderer::setViewport(const Recti& vp)
+{
+ int wh;
+ if (CurRenderTarget)
+ wh = CurRenderTarget->Height;
+ else
+ {
+ RECT rect;
+ BOOL success = GetWindowRect(RParams.Window, &rect);
+ OVR_ASSERT(success);
+ OVR_UNUSED(success);
+ wh = rect.bottom - rect.top;
+ }
+ glViewport(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+
+ //glEnable(GL_SCISSOR_TEST);
+ //glScissor(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+}
+
+
+void DistortionRenderer::initShaders()
+{
+ {
+ ShaderInfo vsShaderByteCode = DistortionVertexShaderLookup[DistortionVertexShaderBitMask & DistortionCaps];
+ Ptr<GL::VertexShader> vtxShader = *new GL::VertexShader(
+ &RParams,
+ (void*)vsShaderByteCode.ShaderData, vsShaderByteCode.ShaderSize,
+ vsShaderByteCode.ReflectionData, vsShaderByteCode.ReflectionSize);
+
+ DistortionShader = *new ShaderSet;
+ DistortionShader->SetShader(vtxShader);
+
+ ShaderInfo psShaderByteCode = DistortionPixelShaderLookup[DistortionPixelShaderBitMask & DistortionCaps];
+
+ Ptr<GL::FragmentShader> ps = *new GL::FragmentShader(
+ &RParams,
+ (void*)psShaderByteCode.ShaderData, psShaderByteCode.ShaderSize,
+ psShaderByteCode.ReflectionData, psShaderByteCode.ReflectionSize);
+
+ DistortionShader->SetShader(ps);
+ }
+ {
+ Ptr<GL::VertexShader> vtxShader = *new GL::VertexShader(
+ &RParams,
+ (void*)SimpleQuad_vs, sizeof(SimpleQuad_vs),
+ SimpleQuad_vs_refl, sizeof(SimpleQuad_vs_refl) / sizeof(SimpleQuad_vs_refl[0]));
+
+ SimpleQuadShader = *new ShaderSet;
+ SimpleQuadShader->SetShader(vtxShader);
+
+ Ptr<GL::FragmentShader> ps = *new GL::FragmentShader(
+ &RParams,
+ (void*)SimpleQuad_fs, sizeof(SimpleQuad_fs),
+ SimpleQuad_fs_refl, sizeof(SimpleQuad_fs_refl) / sizeof(SimpleQuad_fs_refl[0]));
+
+ SimpleQuadShader->SetShader(ps);
+ }
+}
+
+
+void DistortionRenderer::destroy()
+{
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ DistortionMeshVBs[eyeNum].Clear();
+ DistortionMeshIBs[eyeNum].Clear();
+ }
+
+ if (DistortionShader)
+ {
+ DistortionShader->UnsetShader(Shader_Vertex);
+ DistortionShader->UnsetShader(Shader_Pixel);
+ DistortionShader.Clear();
+ }
+
+ LatencyTesterQuadVB.Clear();
+}
+
+}}} // OVR::CAPI::GL
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h
new file mode 100644
index 0000000..8e0b72e
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h
@@ -0,0 +1,125 @@
+/************************************************************************************
+
+Filename : CAPI_GL_DistortionRenderer.h
+Content : Distortion renderer header for GL
+Created : November 11, 2013
+Authors : David Borel, Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_GL_DistortionRenderer_h
+#define OVR_CAPI_GL_DistortionRenderer_h
+
+#include "../CAPI_DistortionRenderer.h"
+
+#include "../../Kernel/OVR_Log.h"
+#include "CAPI_GL_Util.h"
+
+namespace OVR { namespace CAPI { namespace GL {
+
+// ***** GL::DistortionRenderer
+
+// Implementation of DistortionRenderer for GL.
+
+class DistortionRenderer : public CAPI::DistortionRenderer
+{
+public:
+ DistortionRenderer(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+ ~DistortionRenderer();
+
+
+ // Creation function for the device.
+ static CAPI::DistortionRenderer* Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+
+
+ // ***** Public DistortionRenderer interface
+
+ virtual bool Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned hmdCaps, unsigned distortionCaps);
+
+ virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture);
+
+ virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor);
+
+ void WaitUntilGpuIdle();
+
+ // Similar to ovr_WaitTillTime but it also flushes GPU.
+ // Note, it exits when time expires, even if GPU is not in idle state yet.
+ double FlushGpuAndWaitTillTime(double absTime);
+
+private:
+ // TBD: Should we be using oe from RState instead?
+ unsigned DistortionCaps;
+
+ struct FOR_EACH_EYE
+ {
+#if 0
+ IDirect3DVertexBuffer9 * dxVerts;
+ IDirect3DIndexBuffer9 * dxIndices;
+#endif
+ int numVerts;
+ int numIndices;
+
+ GLuint texture;
+
+ ovrVector2f UVScaleOffset[2];
+ } eachEye[2];
+
+ // GL context and utility variables.
+ RenderParams RParams;
+
+ // Helpers
+ void initBuffersAndShaders();
+ void initShaders();
+ void initFullscreenQuad();
+ void destroy();
+
+ void setViewport(const Recti& vp);
+
+ void renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture);
+
+ void renderPrimitives(const ShaderFill* fill, Buffer* vertices, Buffer* indices,
+ Matrix4f* viewMatrix, int offset, int count,
+ PrimitiveType rprim, bool useDistortionVertex);
+
+ void createDrawQuad();
+ void renderLatencyQuad(unsigned char* latencyTesterDrawColor);
+ void renderLatencyPixel(unsigned char* latencyTesterPixelColor);
+
+ Ptr<Texture> pEyeTextures[2];
+
+ // U,V scale and offset needed for timewarp.
+ ovrVector2f UVScaleOffset[2][2];
+
+ Ptr<Buffer> DistortionMeshVBs[2]; // one per-eye
+ Ptr<Buffer> DistortionMeshIBs[2]; // one per-eye
+
+ Ptr<ShaderSet> DistortionShader;
+
+ struct StandardUniformData
+ {
+ Matrix4f Proj;
+ Matrix4f View;
+ } StdUniforms;
+
+ Ptr<Buffer> LatencyTesterQuadVB;
+ Ptr<ShaderSet> SimpleQuadShader;
+
+ Ptr<Texture> CurRenderTarget;
+ Array<Ptr<Texture> > DepthBuffers;
+ GLuint CurrentFbo;
+};
+
+}}} // OVR::CAPI::GL
+
+#endif // OVR_CAPI_GL_DistortionRenderer_h \ No newline at end of file
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp
new file mode 100644
index 0000000..b82939a
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp
@@ -0,0 +1,516 @@
+/************************************************************************************
+
+Filename : Render_GL_Device.cpp
+Content : RenderDevice implementation for OpenGL
+Created : September 10, 2012
+Authors : David Borel, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "CAPI_GL_Util.h"
+#include "../../Kernel/OVR_Log.h"
+
+namespace OVR { namespace CAPI { namespace GL {
+
+
+
+// GL Hooks for PC.
+#if defined(OVR_OS_WIN32)
+
+PFNWGLGETPROCADDRESS wglGetProcAddress;
+
+PFNGLCLEARPROC glClear;
+PFNGLCLEARCOLORPROC glClearColor;
+PFNGLCLEARDEPTHPROC glClearDepth;
+PFNGLVIEWPORTPROC glViewport;
+PFNGLDRAWELEMENTSPROC glDrawElements;
+PFNGLTEXPARAMETERIPROC glTexParameteri;
+PFNGLFLUSHPROC glFlush;
+PFNGLFINISHPROC glFinish;
+PFNGLDRAWARRAYSPROC glDrawArrays;
+PFNGLGENTEXTURESPROC glGenTextures;
+PFNGLDELETETEXTURESPROC glDeleteTextures;
+PFNGLBINDTEXTUREPROC glBindTexture;
+
+PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
+PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
+PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
+PFNGLDELETESHADERPROC glDeleteShader;
+PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
+PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT;
+PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
+PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
+PFNGLACTIVETEXTUREPROC glActiveTexture;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
+PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+PFNGLBINDBUFFERPROC glBindBuffer;
+PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv;
+PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+PFNGLDELETEBUFFERSPROC glDeleteBuffers;
+PFNGLBUFFERDATAPROC glBufferData;
+PFNGLGENBUFFERSPROC glGenBuffers;
+PFNGLMAPBUFFERPROC glMapBuffer;
+PFNGLUNMAPBUFFERPROC glUnmapBuffer;
+PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
+PFNGLGETSHADERIVPROC glGetShaderiv;
+PFNGLCOMPILESHADERPROC glCompileShader;
+PFNGLSHADERSOURCEPROC glShaderSource;
+PFNGLCREATESHADERPROC glCreateShader;
+PFNGLCREATEPROGRAMPROC glCreateProgram;
+PFNGLATTACHSHADERPROC glAttachShader;
+PFNGLDETACHSHADERPROC glDetachShader;
+PFNGLDELETEPROGRAMPROC glDeleteProgram;
+PFNGLUNIFORM1IPROC glUniform1i;
+PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
+PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
+PFNGLUSEPROGRAMPROC glUseProgram;
+PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
+PFNGLGETPROGRAMIVPROC glGetProgramiv;
+PFNGLLINKPROGRAMPROC glLinkProgram;
+PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
+PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
+PFNGLUNIFORM4FVPROC glUniform4fv;
+PFNGLUNIFORM3FVPROC glUniform3fv;
+PFNGLUNIFORM2FVPROC glUniform2fv;
+PFNGLUNIFORM1FVPROC glUniform1fv;
+PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
+PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT;
+PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
+PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT;
+PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT;
+
+PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
+
+void InitGLExtensions()
+{
+ HINSTANCE hInst = LoadLibrary( L"Opengl32.dll" );
+ if (!hInst)
+ return;
+
+ glClear = (PFNGLCLEARPROC)GetProcAddress( hInst, "glClear" );
+ glClearColor = (PFNGLCLEARCOLORPROC)GetProcAddress( hInst, "glClearColor" );
+ glClearDepth = (PFNGLCLEARDEPTHPROC)GetProcAddress( hInst, "glClearDepth" );
+ glViewport = (PFNGLVIEWPORTPROC)GetProcAddress( hInst, "glViewport" );
+ glDrawElements = (PFNGLDRAWELEMENTSPROC)GetProcAddress( hInst, "glDrawElements" );
+ glTexParameteri = (PFNGLTEXPARAMETERIPROC)GetProcAddress( hInst, "glTexParameteri" );
+ glFlush = (PFNGLFLUSHPROC)GetProcAddress( hInst, "glFlush" );
+ glFinish = (PFNGLFINISHPROC)GetProcAddress( hInst, "glFinish" );
+ glDrawArrays = (PFNGLDRAWARRAYSPROC)GetProcAddress( hInst, "glDrawArrays" );
+ glGenTextures = (PFNGLGENTEXTURESPROC)GetProcAddress( hInst,"glGenTextures" );
+ glDeleteTextures = (PFNGLDELETETEXTURESPROC)GetProcAddress( hInst,"glDeleteTextures" );
+ glBindTexture = (PFNGLBINDTEXTUREPROC)GetProcAddress( hInst,"glBindTexture" );
+
+ wglGetProcAddress = (PFNWGLGETPROCADDRESS)GetProcAddress( hInst, "wglGetProcAddress" );
+
+ if (glGenFramebuffersEXT)
+ return;
+
+ wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT");
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
+ glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) wglGetProcAddress("glGenFramebuffersEXT");
+ glDeleteShader = (PFNGLDELETESHADERPROC) wglGetProcAddress("glDeleteShader");
+ glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) wglGetProcAddress("glCheckFramebufferStatusEXT");
+ glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) wglGetProcAddress("glFramebufferRenderbufferEXT");
+ glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) wglGetProcAddress("glFramebufferTexture2DEXT");
+ glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) wglGetProcAddress("glBindFramebufferEXT");
+ glActiveTexture = (PFNGLACTIVETEXTUREPROC) wglGetProcAddress("glActiveTexture");
+ glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) wglGetProcAddress("glDisableVertexAttribArray");
+ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) wglGetProcAddress("glVertexAttribPointer");
+ glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) wglGetProcAddress("glEnableVertexAttribArray");
+ glBindBuffer = (PFNGLBINDBUFFERPROC) wglGetProcAddress("glBindBuffer");
+ glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) wglGetProcAddress("glUniformMatrix3fv");
+ glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) wglGetProcAddress("glUniformMatrix4fv");
+ glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) wglGetProcAddress("glDeleteBuffers");
+ glBufferData = (PFNGLBUFFERDATAPROC) wglGetProcAddress("glBufferData");
+ glGenBuffers = (PFNGLGENBUFFERSPROC) wglGetProcAddress("glGenBuffers");
+ glMapBuffer = (PFNGLMAPBUFFERPROC) wglGetProcAddress("glMapBuffer");
+ glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) wglGetProcAddress("glUnmapBuffer");
+ glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) wglGetProcAddress("glGetShaderInfoLog");
+ glGetShaderiv = (PFNGLGETSHADERIVPROC) wglGetProcAddress("glGetShaderiv");
+ glCompileShader = (PFNGLCOMPILESHADERPROC) wglGetProcAddress("glCompileShader");
+ glShaderSource = (PFNGLSHADERSOURCEPROC) wglGetProcAddress("glShaderSource");
+ glCreateShader = (PFNGLCREATESHADERPROC) wglGetProcAddress("glCreateShader");
+ glCreateProgram = (PFNGLCREATEPROGRAMPROC) wglGetProcAddress("glCreateProgram");
+ glAttachShader = (PFNGLATTACHSHADERPROC) wglGetProcAddress("glAttachShader");
+ glDetachShader = (PFNGLDETACHSHADERPROC) wglGetProcAddress("glDetachShader");
+ glDeleteProgram = (PFNGLDELETEPROGRAMPROC) wglGetProcAddress("glDeleteProgram");
+ glUniform1i = (PFNGLUNIFORM1IPROC) wglGetProcAddress("glUniform1i");
+ glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) wglGetProcAddress("glGetUniformLocation");
+ glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) wglGetProcAddress("glGetActiveUniform");
+ glUseProgram = (PFNGLUSEPROGRAMPROC) wglGetProcAddress("glUseProgram");
+ glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) wglGetProcAddress("glGetProgramInfoLog");
+ glGetProgramiv = (PFNGLGETPROGRAMIVPROC) wglGetProcAddress("glGetProgramiv");
+ glLinkProgram = (PFNGLLINKPROGRAMPROC) wglGetProcAddress("glLinkProgram");
+ glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) wglGetProcAddress("glBindAttribLocation");
+ glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) wglGetProcAddress("glGetAttribLocation");
+ glUniform4fv = (PFNGLUNIFORM4FVPROC) wglGetProcAddress("glUniform4fv");
+ glUniform3fv = (PFNGLUNIFORM3FVPROC) wglGetProcAddress("glUniform3fv");
+ glUniform2fv = (PFNGLUNIFORM2FVPROC) wglGetProcAddress("glUniform2fv");
+ glUniform1fv = (PFNGLUNIFORM1FVPROC) wglGetProcAddress("glUniform1fv");
+ glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) wglGetProcAddress("glCompressedTexImage2D");
+ glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) wglGetProcAddress("glRenderbufferStorageEXT");
+ glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) wglGetProcAddress("glBindRenderbufferEXT");
+ glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) wglGetProcAddress("glGenRenderbuffersEXT");
+ glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) wglGetProcAddress("glDeleteRenderbuffersEXT");
+
+
+ glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) wglGetProcAddress("glGenVertexArrays");
+}
+
+#endif
+
+Buffer::Buffer(RenderParams* rp) : pParams(rp), Size(0), Use(0), GLBuffer(0)
+{
+}
+
+Buffer::~Buffer()
+{
+ if (GLBuffer)
+ glDeleteBuffers(1, &GLBuffer);
+}
+
+bool Buffer::Data(int use, const void* buffer, size_t size)
+{
+ Size = size;
+
+ switch (use & Buffer_TypeMask)
+ {
+ case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break;
+ default: Use = GL_ARRAY_BUFFER; break;
+ }
+
+ if (!GLBuffer)
+ glGenBuffers(1, &GLBuffer);
+
+ int mode = GL_DYNAMIC_DRAW;
+ if (use & Buffer_ReadOnly)
+ mode = GL_STATIC_DRAW;
+
+ glBindBuffer(Use, GLBuffer);
+ glBufferData(Use, size, buffer, mode);
+ glBindBuffer(Use, 0);
+ return 1;
+}
+
+void* Buffer::Map(size_t, size_t, int)
+{
+ int mode = GL_WRITE_ONLY;
+ //if (flags & Map_Unsynchronized)
+ // mode |= GL_MAP_UNSYNCHRONIZED;
+
+ glBindBuffer(Use, GLBuffer);
+ void* v = glMapBuffer(Use, mode);
+ glBindBuffer(Use, 0);
+ return v;
+}
+
+bool Buffer::Unmap(void*)
+{
+ glBindBuffer(Use, GLBuffer);
+ int r = glUnmapBuffer(Use);
+ glBindBuffer(Use, 0);
+ return r != 0;
+}
+
+ShaderSet::ShaderSet()
+{
+ Prog = glCreateProgram();
+}
+ShaderSet::~ShaderSet()
+{
+ glDeleteProgram(Prog);
+}
+
+GLint ShaderSet::GetGLShader(Shader* s)
+{
+ switch (s->Stage)
+ {
+ case Shader_Vertex: {
+ ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER>* gls = (ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER>*)s;
+ return gls->GLShader;
+ } break;
+ case Shader_Fragment: {
+ ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER>* gls = (ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER>*)s;
+ return gls->GLShader;
+ } break;
+ }
+
+ return -1;
+}
+
+void ShaderSet::SetShader(Shader *s)
+{
+ Shaders[s->Stage] = s;
+ GLint GLShader = GetGLShader(s);
+ glAttachShader(Prog, GLShader);
+ if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment])
+ Link();
+}
+
+void ShaderSet::UnsetShader(int stage)
+{
+ if (Shaders[stage] == NULL)
+ return;
+
+ GLint GLShader = GetGLShader(Shaders[stage]);
+ glDetachShader(Prog, GLShader);
+
+ Shaders[stage] = NULL;
+
+ //Link();
+}
+
+bool ShaderSet::SetUniform(const char* name, int n, const float* v)
+{
+ for (unsigned int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ OVR_ASSERT(UniformInfo[i].Location >= 0);
+ glUseProgram(Prog);
+ switch (UniformInfo[i].Type)
+ {
+ case 1: glUniform1fv(UniformInfo[i].Location, n, v); break;
+ case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break;
+ case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break;
+ case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break;
+ case 12: glUniformMatrix3fv(UniformInfo[i].Location, 1, 1, v); break;
+ case 16: glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, v); break;
+ default: OVR_ASSERT(0);
+ }
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+bool ShaderSet::Link()
+{
+ glLinkProgram(Prog);
+ GLint r;
+ glGetProgramiv(Prog, GL_LINK_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetProgramInfoLog(Prog, sizeof(msg), 0, msg);
+ OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg));
+ if (!r)
+ return 0;
+ }
+ glUseProgram(Prog);
+
+ UniformInfo.Clear();
+ LightingVer = 0;
+ UsesLighting = 0;
+
+ GLint uniformCount = 0;
+ glGetProgramiv(Prog, GL_ACTIVE_UNIFORMS, &uniformCount);
+ OVR_ASSERT(uniformCount >= 0);
+
+ for(GLuint i = 0; i < (GLuint)uniformCount; i++)
+ {
+ GLsizei namelen;
+ GLint size = 0;
+ GLenum type;
+ GLchar name[32];
+ glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name);
+
+ if (size)
+ {
+ int l = glGetUniformLocation(Prog, name);
+ char *np = name;
+ while (*np)
+ {
+ if (*np == '[')
+ *np = 0;
+ np++;
+ }
+ Uniform u;
+ u.Name = name;
+ u.Location = l;
+ u.Size = size;
+ switch (type)
+ {
+ case GL_FLOAT: u.Type = 1; break;
+ case GL_FLOAT_VEC2: u.Type = 2; break;
+ case GL_FLOAT_VEC3: u.Type = 3; break;
+ case GL_FLOAT_VEC4: u.Type = 4; break;
+ case GL_FLOAT_MAT3: u.Type = 12; break;
+ case GL_FLOAT_MAT4: u.Type = 16; break;
+ default:
+ continue;
+ }
+ UniformInfo.PushBack(u);
+ if (!strcmp(name, "LightCount"))
+ UsesLighting = 1;
+ }
+ else
+ break;
+ }
+
+ ProjLoc = glGetUniformLocation(Prog, "Proj");
+ ViewLoc = glGetUniformLocation(Prog, "View");
+ for (int i = 0; i < 8; i++)
+ {
+ char texv[32];
+ OVR_sprintf(texv, 10, "Texture%d", i);
+ TexLoc[i] = glGetUniformLocation(Prog, texv);
+ if (TexLoc[i] < 0)
+ break;
+
+ glUniform1i(TexLoc[i], i);
+ }
+ if (UsesLighting)
+ OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0);
+ return 1;
+}
+
+bool ShaderBase::SetUniform(const char* name, int n, const float* v)
+{
+ for(unsigned i = 0; i < UniformReflSize; i++)
+ {
+ if (!strcmp(UniformRefl[i].Name, name))
+ {
+ memcpy(UniformData + UniformRefl[i].Offset, v, n * sizeof(float));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+bool ShaderBase::SetUniformBool(const char* name, int n, const bool* v)
+{
+ OVR_UNUSED(n);
+ for(unsigned i = 0; i < UniformReflSize; i++)
+ {
+ if (!strcmp(UniformRefl[i].Name, name))
+ {
+ memcpy(UniformData + UniformRefl[i].Offset, v, UniformRefl[i].Size);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ShaderBase::InitUniforms(const Uniform* refl, size_t reflSize)
+{
+ if(!refl)
+ {
+ UniformRefl = NULL;
+ UniformReflSize = 0;
+
+ UniformsSize = 0;
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ UniformData = 0;
+ }
+ return; // no reflection data
+ }
+
+ UniformRefl = refl;
+ UniformReflSize = reflSize;
+
+ UniformsSize = UniformRefl[UniformReflSize-1].Offset + UniformRefl[UniformReflSize-1].Size;
+ UniformData = (unsigned char*)OVR_ALLOC(UniformsSize);
+}
+
+void ShaderBase::UpdateBuffer(Buffer* buf)
+{
+ if (UniformsSize)
+ {
+ buf->Data(Buffer_Uniform, UniformData, UniformsSize);
+ }
+}
+
+Texture::Texture(RenderParams* rp, int w, int h) : IsUserAllocated(true), pParams(rp), TexId(0), Width(w), Height(h)
+{
+ if (w && h)
+ glGenTextures(1, &TexId);
+}
+
+Texture::~Texture()
+{
+ if (TexId && !IsUserAllocated)
+ glDeleteTextures(1, &TexId);
+}
+
+void Texture::Set(int slot, ShaderStage) const
+{
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, TexId);
+ glActiveTexture(GL_TEXTURE0);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ glBindTexture(GL_TEXTURE_2D, TexId);
+ switch (sm & Sample_FilterMask)
+ {
+ case Sample_Linear:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
+ break;
+
+ case Sample_Anisotropic:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
+ break;
+
+ case Sample_Nearest:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
+ break;
+ }
+
+ switch (sm & Sample_AddressMask)
+ {
+ case Sample_Repeat:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+
+ case Sample_Clamp:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ break;
+
+ case Sample_ClampBorder:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ break;
+ }
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void Texture::UpdatePlaceholderTexture(GLuint texId, const Sizei& textureSize)
+{
+ if (!IsUserAllocated && TexId && texId != TexId)
+ glDeleteTextures(1, &TexId);
+
+ TexId = texId;
+ Width = textureSize.w;
+ Height = textureSize.h;
+
+ IsUserAllocated = true;
+}
+
+}}}
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h
new file mode 100644
index 0000000..5e694cc
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h
@@ -0,0 +1,522 @@
+/************************************************************************************
+
+Filename : CAPI_GL_Util.h
+Content : Utility header for OpenGL
+Created : March 27, 2014
+Authors : Andrew Reisse, David Borel
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef INC_OVR_CAPI_GL_Util_h
+#define INC_OVR_CAPI_GL_Util_h
+
+#include "../../OVR_CAPI.h"
+#include "../../Kernel/OVR_Array.h"
+#include "../../Kernel/OVR_Math.h"
+#include "../../Kernel/OVR_RefCount.h"
+#include "../../Kernel/OVR_String.h"
+#include "../../Kernel/OVR_Types.h"
+
+#if defined(OVR_OS_WIN32)
+#include <Windows.h>
+#endif
+
+#if defined(OVR_OS_MAC)
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#else
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+#include <GL/gl.h>
+#include <GL/glext.h>
+#if defined(OVR_OS_WIN32)
+#include <GL/wglext.h>
+#endif
+#endif
+
+namespace OVR { namespace CAPI { namespace GL {
+
+// GL extension Hooks for PC.
+#if defined(OVR_OS_WIN32)
+
+typedef PROC (__stdcall *PFNWGLGETPROCADDRESS) (LPCSTR);
+typedef void (__stdcall *PFNGLFLUSHPROC) ();
+typedef void (__stdcall *PFNGLFINISHPROC) ();
+typedef void (__stdcall *PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count);
+typedef void (__stdcall *PFNGLCLEARPROC) (GLbitfield);
+typedef void (__stdcall *PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+typedef void (__stdcall *PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
+typedef void (__stdcall *PFNGLDELETETEXTURESPROC) (GLsizei n, GLuint *textures);
+typedef void (__stdcall *PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
+typedef void (__stdcall *PFNGLCLEARCOLORPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+typedef void (__stdcall *PFNGLCLEARDEPTHPROC) (GLclampd depth);
+typedef void (__stdcall *PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (__stdcall *PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
+
+extern PFNWGLGETPROCADDRESS wglGetProcAddress;
+extern PFNGLCLEARPROC glClear;
+extern PFNGLCLEARCOLORPROC glClearColor;
+extern PFNGLCLEARDEPTHPROC glClearDepth;
+extern PFNGLVIEWPORTPROC glViewport;
+extern PFNGLDRAWARRAYSPROC glDrawArrays;
+extern PFNGLDRAWELEMENTSPROC glDrawElements;
+extern PFNGLGENTEXTURESPROC glGenTextures;
+extern PFNGLDELETETEXTURESPROC glDeleteTextures;
+extern PFNGLBINDTEXTUREPROC glBindTexture;
+extern PFNGLTEXPARAMETERIPROC glTexParameteri;
+extern PFNGLFLUSHPROC glFlush;
+extern PFNGLFINISHPROC glFinish;
+
+extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
+extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
+extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
+extern PFNGLDELETESHADERPROC glDeleteShader;
+extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
+extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT;
+extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
+extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
+extern PFNGLACTIVETEXTUREPROC glActiveTexture;
+extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
+extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+extern PFNGLBINDBUFFERPROC glBindBuffer;
+extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
+extern PFNGLBUFFERDATAPROC glBufferData;
+extern PFNGLGENBUFFERSPROC glGenBuffers;
+extern PFNGLMAPBUFFERPROC glMapBuffer;
+extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
+extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
+extern PFNGLGETSHADERIVPROC glGetShaderiv;
+extern PFNGLCOMPILESHADERPROC glCompileShader;
+extern PFNGLSHADERSOURCEPROC glShaderSource;
+extern PFNGLCREATESHADERPROC glCreateShader;
+extern PFNGLCREATEPROGRAMPROC glCreateProgram;
+extern PFNGLATTACHSHADERPROC glAttachShader;
+extern PFNGLDETACHSHADERPROC glDetachShader;
+extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
+extern PFNGLUNIFORM1IPROC glUniform1i;
+extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
+extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
+extern PFNGLUSEPROGRAMPROC glUseProgram;
+extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
+extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
+extern PFNGLLINKPROGRAMPROC glLinkProgram;
+extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
+extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
+extern PFNGLUNIFORM4FVPROC glUniform4fv;
+extern PFNGLUNIFORM3FVPROC glUniform3fv;
+extern PFNGLUNIFORM2FVPROC glUniform2fv;
+extern PFNGLUNIFORM1FVPROC glUniform1fv;
+extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
+extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT;
+extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT;
+extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT;
+extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT;
+
+// For testing
+extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
+
+extern void InitGLExtensions();
+
+#endif
+
+
+// Rendering primitive type used to render Model.
+enum PrimitiveType
+{
+ Prim_Triangles,
+ Prim_Lines,
+ Prim_TriangleStrip,
+ Prim_Unknown,
+ Prim_Count
+};
+
+// Types of shaders that can be stored together in a ShaderSet.
+enum ShaderStage
+{
+ Shader_Vertex = 0,
+ Shader_Fragment = 2,
+ Shader_Pixel = 2,
+ Shader_Count = 3,
+};
+
+enum MapFlags
+{
+ Map_Discard = 1,
+ Map_Read = 2, // do not use
+ Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE
+};
+
+
+// Buffer types used for uploading geometry & constants.
+enum BufferUsage
+{
+ Buffer_Unknown = 0,
+ Buffer_Vertex = 1,
+ Buffer_Index = 2,
+ Buffer_Uniform = 4,
+ Buffer_TypeMask = 0xff,
+ Buffer_ReadOnly = 0x100, // Buffer must be created with Data().
+};
+
+enum TextureFormat
+{
+ Texture_RGBA = 0x0100,
+ Texture_Depth = 0x8000,
+ Texture_TypeMask = 0xff00,
+ Texture_SamplesMask = 0x00ff,
+ Texture_RenderTarget = 0x10000,
+ Texture_GenMipmaps = 0x20000,
+};
+
+// Texture sampling modes.
+enum SampleMode
+{
+ Sample_Linear = 0,
+ Sample_Nearest = 1,
+ Sample_Anisotropic = 2,
+ Sample_FilterMask = 3,
+
+ Sample_Repeat = 0,
+ Sample_Clamp = 4,
+ Sample_ClampBorder = 8, // If unsupported Clamp is used instead.
+ Sample_AddressMask =12,
+
+ Sample_Count =13,
+};
+
+
+// Rendering parameters/pointers describing GL rendering setup.
+struct RenderParams
+{
+#ifdef OVR_OS_WIN32
+ HWND Window;
+ HGLRC WglContext;
+ HDC GdiDc;
+#endif
+
+ ovrSizei RTSize;
+ int Multisample;
+};
+
+
+class Buffer : public RefCountBase<Buffer>
+{
+public:
+ RenderParams* pParams;
+ size_t Size;
+ GLenum Use;
+ GLuint GLBuffer;
+
+public:
+ Buffer(RenderParams* r);
+ ~Buffer();
+
+ GLuint GetBuffer() { return GLBuffer; }
+
+ virtual size_t GetSize() { return Size; }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public RefCountBase<Texture>
+{
+ bool IsUserAllocated;
+
+public:
+ RenderParams* pParams;
+ GLuint TexId;
+ int Width, Height;
+
+ Texture(RenderParams* rp, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const { return Width; }
+ virtual int GetHeight() const { return Height; }
+
+ virtual void SetSampleMode(int sm);
+
+ // Updates texture to point to specified resources
+ // - used for slave rendering.
+ void UpdatePlaceholderTexture(GLuint texId,
+ const Sizei& textureSize);
+
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const;
+};
+
+// Base class for vertex and pixel shaders. Stored in ShaderSet.
+class Shader : public RefCountBase<Shader>
+{
+ friend class ShaderSet;
+
+protected:
+ ShaderStage Stage;
+
+public:
+ Shader(ShaderStage s) : Stage(s) {}
+ virtual ~Shader() {}
+
+ ShaderStage GetStage() const { return Stage; }
+
+ virtual void Set(PrimitiveType) const { }
+ virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); }
+
+protected:
+ virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; }
+ virtual bool SetUniformBool(const char* name, int n, const bool* v) { OVR_UNUSED3(name, n, v); return false; }
+};
+
+
+
+// A group of shaders, one per stage.
+// A ShaderSet is applied for rendering with a given fill.
+class ShaderSet : public RefCountBase<ShaderSet>
+{
+protected:
+ Ptr<Shader> Shaders[Shader_Count];
+
+ struct Uniform
+ {
+ String Name;
+ int Location, Size;
+ int Type; // currently number of floats in vector
+ };
+ Array<Uniform> UniformInfo;
+
+public:
+ GLuint Prog;
+ GLint ProjLoc, ViewLoc;
+ GLint TexLoc[8];
+ bool UsesLighting;
+ int LightingVer;
+
+ ShaderSet();
+ ~ShaderSet();
+
+ virtual void SetShader(Shader *s);
+ virtual void UnsetShader(int stage);
+ Shader* GetShader(int stage) { return Shaders[stage]; }
+
+ virtual void Set(PrimitiveType prim) const
+ {
+ glUseProgram(Prog);
+
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ Shaders[i]->Set(prim);
+ }
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v);
+ bool SetUniform1f(const char* name, float x)
+ {
+ const float v[] = {x};
+ return SetUniform(name, 1, v);
+ }
+ bool SetUniform2f(const char* name, float x, float y)
+ {
+ const float v[] = {x,y};
+ return SetUniform(name, 2, v);
+ }
+ bool SetUniform3f(const char* name, float x, float y, float z)
+ {
+ const float v[] = {x,y,z};
+ return SetUniform(name, 3, v);
+ }
+ bool SetUniform4f(const char* name, float x, float y, float z, float w = 1)
+ {
+ const float v[] = {x,y,z,w};
+ return SetUniform(name, 4, v);
+ }
+
+ bool SetUniformv(const char* name, const Vector3f& v)
+ {
+ const float a[] = {v.x,v.y,v.z,1};
+ return SetUniform(name, 4, a);
+ }
+
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ Matrix4f mt = m.Transposed();
+ return SetUniform(name, 16, &mt.M[0][0]);
+ }
+
+protected:
+ GLint GetGLShader(Shader* s);
+ bool Link();
+};
+
+
+// Fill combines a ShaderSet (vertex, pixel) with textures, if any.
+// Every model has a fill.
+class ShaderFill : public RefCountBase<ShaderFill>
+{
+ Ptr<ShaderSet> Shaders;
+ Ptr<class Texture> Textures[8];
+ void* InputLayout; // HACK this should be abstracted
+
+public:
+ ShaderFill(ShaderSet* sh) : Shaders(sh) { InputLayout = NULL; }
+ ShaderFill(ShaderSet& sh) : Shaders(sh) { InputLayout = NULL; }
+
+ ShaderSet* GetShaders() const { return Shaders; }
+ void* GetInputLayout() const { return InputLayout; }
+
+ virtual void Set(PrimitiveType prim = Prim_Unknown) const {
+ Shaders->Set(prim);
+ for(int i = 0; i < 8; i++)
+ {
+ if(Textures[i])
+ {
+ Textures[i]->Set(i);
+ }
+ }
+ }
+
+ virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; }
+};
+
+
+struct DisplayId
+{
+ // Windows
+ String MonitorName; // Monitor name for fullscreen mode
+
+ // MacOS
+ long CgDisplayId; // CGDirectDisplayID
+
+ DisplayId() : CgDisplayId(0) {}
+ DisplayId(long id) : CgDisplayId(id) {}
+ DisplayId(String m, long id=0) : MonitorName(m), CgDisplayId(id) {}
+
+ operator bool () const
+ {
+ return MonitorName.GetLength() || CgDisplayId;
+ }
+
+ bool operator== (const DisplayId& b) const
+ {
+ return CgDisplayId == b.CgDisplayId &&
+ (strstr(MonitorName.ToCStr(), b.MonitorName.ToCStr()) ||
+ strstr(b.MonitorName.ToCStr(), MonitorName.ToCStr()));
+ }
+};
+
+
+class ShaderBase : public Shader
+{
+public:
+ RenderParams* pParams;
+ unsigned char* UniformData;
+ int UniformsSize;
+
+ enum VarType
+ {
+ VARTYPE_FLOAT,
+ VARTYPE_INT,
+ VARTYPE_BOOL,
+ };
+
+ struct Uniform
+ {
+ const char* Name;
+ VarType Type;
+ int Offset, Size;
+ };
+ const Uniform* UniformRefl;
+ size_t UniformReflSize;
+
+ ShaderBase(RenderParams* rp, ShaderStage stage) : Shader(stage), pParams(rp), UniformData(0), UniformsSize(0) {}
+ ~ShaderBase()
+ {
+ if (UniformData)
+ OVR_FREE(UniformData);
+ }
+
+ void InitUniforms(const Uniform* refl, size_t reflSize);
+ bool SetUniform(const char* name, int n, const float* v);
+ bool SetUniformBool(const char* name, int n, const bool* v);
+
+ void UpdateBuffer(Buffer* b);
+};
+
+
+template<ShaderStage SStage, GLenum SType>
+class ShaderImpl : public ShaderBase
+{
+ friend class ShaderSet;
+
+public:
+ ShaderImpl(RenderParams* rp, void* s, size_t size, const Uniform* refl, size_t reflSize)
+ : ShaderBase(rp, SStage)
+ , GLShader(0)
+ {
+ BOOL success;
+ OVR_UNUSED(size);
+ success = Compile((const char*) s);
+ OVR_ASSERT(success);
+ InitUniforms(refl, reflSize);
+ }
+ ~ShaderImpl()
+ {
+ if (GLShader)
+ {
+ glDeleteShader(GLShader);
+ GLShader = 0;
+ }
+ }
+ bool Compile(const char* src)
+ {
+ if (!GLShader)
+ GLShader = glCreateShader(GLStage());
+
+ glShaderSource(GLShader, 1, &src, 0);
+ glCompileShader(GLShader);
+ GLint r;
+ glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg);
+ if (msg[0])
+ OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg));
+
+ return 0;
+ }
+ return 1;
+ }
+
+ GLenum GLStage() const
+ {
+ return SType;
+ }
+
+private:
+ GLuint GLShader;
+};
+
+typedef ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER> VertexShader;
+typedef ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER> FragmentShader;
+
+}}}
+
+#endif // INC_OVR_CAPI_GL_Util_h
diff --git a/LibOVR/Src/CAPI/Shaders/DistortionChroma_ps.psh b/LibOVR/Src/CAPI/Shaders/DistortionChroma_ps.psh
new file mode 100644
index 0000000..5c95ade
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/DistortionChroma_ps.psh
@@ -0,0 +1,12 @@
+Texture2D Texture : register(t0);
+SamplerState Linear : register(s0);
+
+float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,
+ in float3 oTexCoord0 : TEXCOORD0, in float3 oTexCoord1 : TEXCOORD1, in float3 oTexCoord2 : TEXCOORD2) : SV_Target
+{
+ float ResultR = Texture.Sample(Linear, oTexCoord0.xy).r;
+ float ResultG = Texture.Sample(Linear, oTexCoord1.xy).g;
+ float ResultB = Texture.Sample(Linear, oTexCoord2.xy).b;
+ return float4(ResultR * oColor.r, ResultG * oColor.g, ResultB * oColor.b, 1.0);
+ //" return oColor.rrrr;
+}
diff --git a/LibOVR/Src/CAPI/Shaders/DistortionChroma_vs.vsh b/LibOVR/Src/CAPI/Shaders/DistortionChroma_vs.vsh
new file mode 100644
index 0000000..6e11647
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/DistortionChroma_vs.vsh
@@ -0,0 +1,24 @@
+float2 EyeToSourceUVScale;
+float2 EyeToSourceUVOffset;
+
+void main(in float2 Position : POSITION, in float4 Color : COLOR0,
+ in float2 TexCoord0 : TEXCOORD0, in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2,
+ out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float3 oTexCoord0 : TEXCOORD0,
+ out float3 oTexCoord1 : TEXCOORD1, out float3 oTexCoord2 : TEXCOORD2)
+{
+ oPosition.x = Position.x;
+ oPosition.y = Position.y;
+ oPosition.z = 0.5;
+ oPosition.w = 1.0;
+
+ // Scale them into UV lookup space
+ float2 tc0scaled = EyeToSourceUVScale * TexCoord0 + EyeToSourceUVOffset;
+ float2 tc1scaled = EyeToSourceUVScale * TexCoord1 + EyeToSourceUVOffset;
+ float2 tc2scaled = EyeToSourceUVScale * TexCoord2 + EyeToSourceUVOffset;
+
+ oTexCoord0 = float3(tc0scaled, 1); // R sample.
+ oTexCoord1 = float3(tc1scaled, 1); // G sample.
+ oTexCoord2 = float3(tc2scaled, 1); // B sample.
+ oColor = Color; // Used for vignette fade.
+}
+
diff --git a/LibOVR/Src/CAPI/Shaders/DistortionTimewarpChroma_vs.vsh b/LibOVR/Src/CAPI/Shaders/DistortionTimewarpChroma_vs.vsh
new file mode 100644
index 0000000..d629ddd
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/DistortionTimewarpChroma_vs.vsh
@@ -0,0 +1,40 @@
+float2 EyeToSourceUVScale;
+float2 EyeToSourceUVOffset;
+float4x4 EyeRotationStart;
+float4x4 EyeRotationEnd;
+
+float2 TimewarpTexCoordToWarpedPos(float2 inTexCoord, float4x4 rotMat)
+{
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ // Apply the 3x3 timewarp rotation to these vectors.
+ float3 transformed = float3( mul ( rotMat, float4(inTexCoord.xy, 1, 1) ).xyz);
+ // Project them back onto the Z=1 plane of the rendered images.
+ float2 flattened = transformed.xy / transformed.z;
+ // Scale them into ([0,0.5],[0,1]) or ([0.5,0],[0,1]) UV lookup space (depending on eye)
+ return flattened * EyeToSourceUVScale + EyeToSourceUVOffset;
+
+}
+
+void main(in float2 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord0 : TEXCOORD0,
+ in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2,
+ out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float3 oTexCoord0 : TEXCOORD0,
+ out float3 oTexCoord1 : TEXCOORD1, out float3 oTexCoord2 : TEXCOORD2)
+{
+
+ oPosition.x = Position.x;
+ oPosition.y = Position.y;
+ oPosition.z = 0.5;
+ oPosition.w = 1.0;
+
+ float timewarpLerpFactor = Color.a;
+ float4x4 lerpedEyeRot = lerp(EyeRotationStart, EyeRotationEnd, timewarpLerpFactor);
+ //" float4x4 lerpedEyeRot = EyeRotationStart;
+
+ // warped positions are a bit more involved, hence a separate function
+ oTexCoord0 = float3(TimewarpTexCoordToWarpedPos(TexCoord0, lerpedEyeRot), 1);
+ oTexCoord1 = float3(TimewarpTexCoordToWarpedPos(TexCoord1, lerpedEyeRot), 1);
+ oTexCoord2 = float3(TimewarpTexCoordToWarpedPos(TexCoord2, lerpedEyeRot), 1);
+
+ oColor = Color.r; // Used for vignette fade.
+}
diff --git a/LibOVR/Src/CAPI/Shaders/DistortionTimewarp_vs.vsh b/LibOVR/Src/CAPI/Shaders/DistortionTimewarp_vs.vsh
new file mode 100644
index 0000000..627970f
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/DistortionTimewarp_vs.vsh
@@ -0,0 +1,36 @@
+float2 EyeToSourceUVScale;
+float2 EyeToSourceUVOffset;
+float4x4 EyeRotationStart;
+float4x4 EyeRotationEnd;
+
+float2 TimewarpTexCoordToWarpedPos(float2 inTexCoord, float4x4 rotMat)
+{
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ // Apply the 3x3 timewarp rotation to these vectors.
+ float3 transformed = float3( mul ( rotMat, float4(inTexCoord,1,1) ).xyz);
+ // Project them back onto the Z=1 plane of the rendered images.
+ float2 flattened = transformed.xy / transformed.z;
+ // Scale them into ([0,0.5],[0,1]) or ([0.5,0],[0,1]) UV lookup space (depending on eye)
+ return flattened * EyeToSourceUVScale + EyeToSourceUVOffset;
+
+}
+
+void main(in float2 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord0 : TEXCOORD0,
+ out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float3 oTexCoord0 : TEXCOORD0)
+{
+
+ oPosition.x = Position.x;
+ oPosition.y = Position.y;
+ oPosition.z = 0.5;
+ oPosition.w = 1.0;
+
+ float timewarpLerpFactor = Color.a;
+ float4x4 lerpedEyeRot = lerp(EyeRotationStart, EyeRotationEnd, timewarpLerpFactor);
+
+ // Warped positions are a bit more involved, hence a separate function
+ oTexCoord0 = float3(TimewarpTexCoordToWarpedPos(TexCoord0, lerpedEyeRot), 1);
+ oColor = Color.r; // Used for vignette fade.
+}
+
+
diff --git a/LibOVR/Src/CAPI/Shaders/Distortion_ps.psh b/LibOVR/Src/CAPI/Shaders/Distortion_ps.psh
new file mode 100644
index 0000000..4a33de5
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/Distortion_ps.psh
@@ -0,0 +1,9 @@
+Texture2D Texture : register(t0);
+SamplerState Linear : register(s0);
+
+float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,
+ in float3 oTexCoord0 : TEXCOORD0) : SV_Target
+{
+ float3 Result = Texture.Sample(Linear, oTexCoord0.xy).rgb;
+ return float4(Result.r * oColor.r, Result.g * oColor.g, Result.b * oColor.b, 1.0);
+}
diff --git a/LibOVR/Src/CAPI/Shaders/Distortion_vs.vsh b/LibOVR/Src/CAPI/Shaders/Distortion_vs.vsh
new file mode 100644
index 0000000..d22ea02
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/Distortion_vs.vsh
@@ -0,0 +1,14 @@
+float2 EyeToSourceUVScale;
+float2 EyeToSourceUVOffset;
+
+void main(in float2 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord0 : TEXCOORD0,
+ out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float3 oTexCoord0 : TEXCOORD0)
+{
+ oPosition.x = Position.x;
+ oPosition.y = Position.y;
+ oPosition.z = 0.5;
+ oPosition.w = 1.0;
+ oTexCoord0 = float3(EyeToSourceUVScale * TexCoord0 + EyeToSourceUVOffset, 1);
+ oColor = Color; // Used for vignette fade.
+}
+
diff --git a/LibOVR/Src/CAPI/Shaders/SimpleQuad_ps.psh b/LibOVR/Src/CAPI/Shaders/SimpleQuad_ps.psh
new file mode 100644
index 0000000..9ea10cd
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/SimpleQuad_ps.psh
@@ -0,0 +1,6 @@
+float4 Color;
+
+float4 main() : SV_Target
+{
+ return Color;
+}
diff --git a/LibOVR/Src/CAPI/Shaders/SimpleQuad_vs.vsh b/LibOVR/Src/CAPI/Shaders/SimpleQuad_vs.vsh
new file mode 100644
index 0000000..4625371
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/SimpleQuad_vs.vsh
@@ -0,0 +1,8 @@
+float2 PositionOffset = float2(0, 0);
+float2 Scale = float2(1, 1);
+
+void main( in float3 Position : POSITION,
+out float4 oPosition : SV_Position)
+{
+ oPosition = float4(Position.xy * Scale + PositionOffset, 0.5, 1.0);
+} \ No newline at end of file
diff --git a/LibOVR/Src/CAPI/Shaders/genPixelShaderHeader.bat b/LibOVR/Src/CAPI/Shaders/genPixelShaderHeader.bat
new file mode 100644
index 0000000..a1311b2
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/genPixelShaderHeader.bat
@@ -0,0 +1,15 @@
+@echo off
+pushd %~dp0
+echo Compiling shader and packing into header: %~2
+setlocal
+
+set PATH=%PATH%;"%DXSDK_DIR%Utilities\bin\x86\"
+fxc.exe /nologo /E main /T ps_4_0 /Fo "%1" %2
+bin2header.exe "%1"
+
+echo Generating shader reflection data for %1
+ShaderReflector "%1" "%1_refl.h"
+
+del "%1"
+endlocal
+popd
diff --git a/LibOVR/Src/CAPI/Shaders/genVertexShaderHeader.bat b/LibOVR/Src/CAPI/Shaders/genVertexShaderHeader.bat
new file mode 100644
index 0000000..4591d20
--- /dev/null
+++ b/LibOVR/Src/CAPI/Shaders/genVertexShaderHeader.bat
@@ -0,0 +1,15 @@
+@echo off
+pushd %~dp0
+echo Compiling shader and packing into header: %~2
+setlocal
+
+set PATH=%PATH%;"%DXSDK_DIR%Utilities\bin\x86\"
+fxc.exe /nologo /E main /T vs_4_0 /Fo "%1" %2
+bin2header.exe "%1"
+
+echo Generating shader reflection data for %1
+ShaderReflector "%1" "%1_refl.h"
+
+del "%1"
+endlocal
+popd