From 07d0f4d0bbf3477ac6a9584f726e8ec6ab285707 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 14 Apr 2014 21:25:09 -0700 Subject: Adding windows 0.3.1 SDK --- LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp | 63 ++ LibOVR/Src/CAPI/CAPI_DistortionRenderer.h | 100 ++ LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp | 674 +++++++++++++ LibOVR/Src/CAPI/CAPI_FrameTimeManager.h | 264 +++++ LibOVR/Src/CAPI/CAPI_GlobalState.cpp | 142 +++ LibOVR/Src/CAPI/CAPI_GlobalState.h | 84 ++ LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp | 147 +++ LibOVR/Src/CAPI/CAPI_HMDRenderState.h | 93 ++ LibOVR/Src/CAPI/CAPI_HMDState.cpp | 774 +++++++++++++++ LibOVR/Src/CAPI/CAPI_HMDState.h | 334 +++++++ .../CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.cpp | 29 + .../Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.h | 34 + .../CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.cpp | 30 + .../Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.h | 34 + .../CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.cpp | 773 +++++++++++++++ .../Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.h | 131 +++ LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.cpp | 416 ++++++++ LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.h | 505 ++++++++++ .../CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.cpp | 251 +++++ .../Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.h | 120 +++ LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_Util.cpp | 317 ++++++ LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp | 1006 ++++++++++++++++++++ LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h | 125 +++ LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp | 516 ++++++++++ LibOVR/Src/CAPI/GL/CAPI_GL_Util.h | 522 ++++++++++ LibOVR/Src/CAPI/Shaders/DistortionChroma_ps.psh | 12 + LibOVR/Src/CAPI/Shaders/DistortionChroma_vs.vsh | 24 + .../CAPI/Shaders/DistortionTimewarpChroma_vs.vsh | 40 + LibOVR/Src/CAPI/Shaders/DistortionTimewarp_vs.vsh | 36 + LibOVR/Src/CAPI/Shaders/Distortion_ps.psh | 9 + LibOVR/Src/CAPI/Shaders/Distortion_vs.vsh | 14 + LibOVR/Src/CAPI/Shaders/SimpleQuad_ps.psh | 6 + LibOVR/Src/CAPI/Shaders/SimpleQuad_vs.vsh | 8 + LibOVR/Src/CAPI/Shaders/genPixelShaderHeader.bat | 15 + LibOVR/Src/CAPI/Shaders/genVertexShaderHeader.bat | 15 + 35 files changed, 7663 insertions(+) create mode 100644 LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp create mode 100644 LibOVR/Src/CAPI/CAPI_DistortionRenderer.h create mode 100644 LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp create mode 100644 LibOVR/Src/CAPI/CAPI_FrameTimeManager.h create mode 100644 LibOVR/Src/CAPI/CAPI_GlobalState.cpp create mode 100644 LibOVR/Src/CAPI/CAPI_GlobalState.h create mode 100644 LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp create mode 100644 LibOVR/Src/CAPI/CAPI_HMDRenderState.h create mode 100644 LibOVR/Src/CAPI/CAPI_HMDState.cpp create mode 100644 LibOVR/Src/CAPI/CAPI_HMDState.h create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.cpp create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D10_DistortionRenderer.h create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.cpp create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D11_DistortionRenderer.h create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.cpp create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_DistortionRenderer.h create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.cpp create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D1X_Util.h create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.cpp create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_DistortionRenderer.h create mode 100644 LibOVR/Src/CAPI/D3D1X/CAPI_D3D9_Util.cpp create mode 100644 LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp create mode 100644 LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h create mode 100644 LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp create mode 100644 LibOVR/Src/CAPI/GL/CAPI_GL_Util.h create mode 100644 LibOVR/Src/CAPI/Shaders/DistortionChroma_ps.psh create mode 100644 LibOVR/Src/CAPI/Shaders/DistortionChroma_vs.vsh create mode 100644 LibOVR/Src/CAPI/Shaders/DistortionTimewarpChroma_vs.vsh create mode 100644 LibOVR/Src/CAPI/Shaders/DistortionTimewarp_vs.vsh create mode 100644 LibOVR/Src/CAPI/Shaders/Distortion_ps.psh create mode 100644 LibOVR/Src/CAPI/Shaders/Distortion_vs.vsh create mode 100644 LibOVR/Src/CAPI/Shaders/SimpleQuad_ps.psh create mode 100644 LibOVR/Src/CAPI/Shaders/SimpleQuad_vs.vsh create mode 100644 LibOVR/Src/CAPI/Shaders/genPixelShaderHeader.bat create mode 100644 LibOVR/Src/CAPI/Shaders/genVertexShaderHeader.bat (limited to 'LibOVR/Src/CAPI') 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 +{ + // 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= 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 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 e = pManager->EnumerateDevices(); + 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(); +} + + +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(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 pManager; + Lock EnumerationLock; + Array EnumeratedDevices; + + // Currently created hmds; protected by Manager lock. + List 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().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().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, + 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 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 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 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 pLatencyTester; + Util::LatencyTest LatencyUtil; + AtomicInt AddLatencyTestCount; + + bool LatencyTestActive; + unsigned char LatencyTestDrawColor[3]; + + // Using latency tester as debug display + Ptr pLatencyTesterDisplay; + AtomicInt AddLatencyTestDisplayCount; + Util::LatencyTest2 LatencyUtil2; + + bool LatencyTest2Active; + unsigned char LatencyTest2DrawColor[3]; + //bool ReadbackColor; + + // Rendering part + FrameTimeManager TimeManager; + HMDRenderState RenderState; + Ptr 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 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 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 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 ps = *new D3D_NS::PixelShader( + &RParams, + (void*)psShaderByteCode.ShaderData, psShaderByteCode.ShaderSize, + psShaderByteCode.ReflectionData, psShaderByteCode.ReflectionSize); + + DistortionShader->SetShader(ps); + } + + { + Ptr 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 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 pEyeTextures[2]; + + // U,V scale and offset needed for timewarp. + ovrVector2f UVScaleOffset[2][2]; + + //Ptr mpFullScreenVertexBuffer; + + Ptr DistortionMeshVBs[2]; // one per-eye + Ptr DistortionMeshIBs[2]; // one per-eye + + Ptr DistortionShader; + Ptr DistortionVertexIL; + + struct StandardUniformData + { + Matrix4f Proj; + Matrix4f View; + } StdUniforms; + Ptr UniformBuffers[Shader_Count]; + + Ptr SamplerStates[Sample_Count]; + Ptr Rasterizer; + + Ptr LatencyTesterQuadVB; + Ptr SimpleQuadShader; + Ptr 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 + +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::Load(void* shader, size_t size) +{ + return SUCCEEDED(pParams->pDevice->CreateVertexShader(shader, size D3D11_COMMA_0, &D3DShader)); +} +template<> bool ShaderImpl::Load(void* shader, size_t size) +{ + return SUCCEEDED(pParams->pDevice->CreatePixelShader(shader, size D3D11_COMMA_0, &D3DShader)); +} + +template<> void ShaderImpl::Set(PrimitiveType) const +{ + pParams->pContext->VSSetShader(D3DShader D3D11_COMMA_0 D3D11_COMMA_0 ); +} +template<> void ShaderImpl::Set(PrimitiveType) const +{ + pParams->pContext->PSSetShader(D3DShader D3D11_COMMA_0 D3D11_COMMA_0 ) ; +} + +template<> void ShaderImpl::SetUniformBuffer(Buffer* buffer, int i) +{ + pParams->pContext->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef()); +} +template<> void ShaderImpl::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 +#include // 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 // avoids warning? + #include + +#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 + #include +#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 +{ + 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 +{ +protected: + Ptr 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 +{ + Ptr Shaders; + Ptr 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 +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 VertexShader; +typedef ShaderImpl PixelShader; + + +class Buffer : public RefCountBase +{ +public: + RenderParams* pParams; + Ptr 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 +{ +public: + RenderParams* pParams; + Ptr Tex; + Ptr TexSv; + Ptr TexRtv; + Ptr TexDsv; + mutable Ptr 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 +{ +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 QuerySets; + + int LastQueuedFrame; + int LastTimedFrame; + + Ptr D3dDevice; + Ptr 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 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 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 +#else +#include +#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;vnumVerts;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;inumIndices;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;iwhich==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 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 ps = *new GL::FragmentShader( + &RParams, + (void*)psShaderByteCode.ShaderData, psShaderByteCode.ShaderSize, + psShaderByteCode.ReflectionData, psShaderByteCode.ReflectionSize); + + DistortionShader->SetShader(ps); + } + { + Ptr 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 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 pEyeTextures[2]; + + // U,V scale and offset needed for timewarp. + ovrVector2f UVScaleOffset[2][2]; + + Ptr DistortionMeshVBs[2]; // one per-eye + Ptr DistortionMeshIBs[2]; // one per-eye + + Ptr DistortionShader; + + struct StandardUniformData + { + Matrix4f Proj; + Matrix4f View; + } StdUniforms; + + Ptr LatencyTesterQuadVB; + Ptr SimpleQuadShader; + + Ptr CurRenderTarget; + Array > 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* gls = (ShaderImpl*)s; + return gls->GLShader; + } break; + case Shader_Fragment: { + ShaderImpl* gls = (ShaderImpl*)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 +#endif + +#if defined(OVR_OS_MAC) +#include +#include +#else +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif +#include +#include +#if defined(OVR_OS_WIN32) +#include +#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 +{ +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 +{ + 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 +{ + 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 +{ +protected: + Ptr Shaders[Shader_Count]; + + struct Uniform + { + String Name; + int Location, Size; + int Type; // currently number of floats in vector + }; + Array 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 +{ + Ptr Shaders; + Ptr 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 +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 VertexShader; +typedef ShaderImpl 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 -- cgit v1.2.3