diff options
author | Brad Davis <[email protected]> | 2014-07-24 16:47:31 -0700 |
---|---|---|
committer | Brad Davis <[email protected]> | 2014-07-24 16:47:31 -0700 |
commit | 0f49ce8fc6aa54224e4c0d6fda8c4527ad39cce1 (patch) | |
tree | da07ebc6a7f75185bda857dd5f1c34710b416a93 /LibOVR/Src/CAPI/GL | |
parent | ca79271759ff7eecd22ec5c4db438370fe51d687 (diff) |
0.4 Win-Beta0.4.0
Diffstat (limited to 'LibOVR/Src/CAPI/GL')
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp | 404 | ||||
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h | 17 | ||||
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h | 77 | ||||
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp | 460 | ||||
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.h | 74 | ||||
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp | 40 | ||||
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_Util.h | 29 |
7 files changed, 960 insertions, 141 deletions
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp index 21b6509..0bf0aa4 100644 --- a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp @@ -18,9 +18,11 @@ otherwise accompanies this software in either electronic or hard copy form. #include "CAPI_GL_DistortionShaders.h" #include "../../OVR_CAPI_GL.h" +#include "../../Kernel/OVR_Color.h" namespace OVR { namespace CAPI { namespace GL { + // Distortion pixel shader lookup. // Bit 0: Chroma Correction // Bit 1: Timewarp @@ -69,10 +71,10 @@ void DistortionShaderBitIndexCheck() struct DistortionVertex { - Vector2f Pos; - Vector2f TexR; - Vector2f TexG; - Vector2f TexB; + Vector2f ScreenPosNDC; + Vector2f TanEyeAnglesR; + Vector2f TanEyeAnglesG; + Vector2f TanEyeAnglesB; Color Col; }; @@ -120,6 +122,8 @@ bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig, { GfxState = *new GraphicsState(); + SaveGraphicsState(); + const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; if (!config) @@ -135,18 +139,39 @@ bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig, RParams.RTSize = config->OGL.Header.RTSize; #if defined(OVR_OS_WIN32) RParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow(); + RParams.DC = config->OGL.DC; #elif defined(OVR_OS_LINUX) - RParams.Disp = (config->OGL.Disp) ? config->OGL.Disp : XOpenDisplay(NULL); - RParams.Win = config->OGL.Win; + if (config->OGL.Disp) + RParams.Disp = config->OGL.Disp; + if (!RParams.Disp) + RParams.Disp = XOpenDisplay(NULL); + if (!RParams.Disp) + { + OVR_DEBUG_LOG(("XOpenDisplay failed.")); + return false; + } + + if (config->OGL.Win) + RParams.Win = config->OGL.Win; if (!RParams.Win) { int unused; - XGetInputFocus(RParams.Disp, &RParams.Win, &unused); + RParams.Win = glXGetCurrentDrawable(); + } + if (!RParams.Win) + { + OVR_DEBUG_LOG(("XGetInputFocus failed.")); + return false; } #endif DistortionCaps = distortionCaps; + DistortionMeshVAOs[0] = 0; + DistortionMeshVAOs[1] = 0; + + LatencyVAO = 0; + //DistortionWarper.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true); pEyeTextures[0] = *new Texture(&RParams, 0, 0); @@ -154,13 +179,15 @@ bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig, initBuffersAndShaders(); + RestoreGraphicsState(); + return true; } -void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture) +void DistortionRenderer::SubmitEye(int eyeId, const ovrTexture* eyeTexture) { - // Doesn't do a lot in here?? + // Doesn't do a lot in here?? const ovrGLTexture* tex = (const ovrGLTexture*)eyeTexture; // Write in values @@ -179,44 +206,65 @@ void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture) eachEye[eyeId].TextureSize, eachEye[eyeId].RenderViewport, eachEye[eyeId].UVScaleOffset ); + if (!(RState.DistortionCaps & ovrDistortionCap_FlipInput)) + { + eachEye[eyeId].UVScaleOffset[0].y = -eachEye[eyeId].UVScaleOffset[0].y; + eachEye[eyeId].UVScaleOffset[1].y = 1.0f - eachEye[eyeId].UVScaleOffset[1].y; + } + pEyeTextures[eyeId]->UpdatePlaceholderTexture(tex->OGL.TexId, tex->OGL.Header.TextureSize); } } -void DistortionRenderer::EndFrame(bool swapBuffers, - unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor) +void DistortionRenderer::renderEndFrame() { - if (!TimeManager.NeedDistortionTimeMeasurement()) - { - if (RState.DistortionCaps & ovrDistortionCap_TimeWarp) - { - // Wait for timewarp distortion if it is time and Gpu idle - FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime); - } + renderDistortion(pEyeTextures[0], pEyeTextures[1]); + + // TODO: Add rendering context to callback. + if(RegisteredPostDistortionCallback) + RegisteredPostDistortionCallback(NULL); - renderDistortion(pEyeTextures[0], pEyeTextures[1]); + if(LatencyTest2Active) + { + renderLatencyPixel(LatencyTest2DrawColor); } - else +} + +void DistortionRenderer::EndFrame(bool swapBuffers) +{ + // Don't spin if we are explicitly asked not to + if (RState.DistortionCaps & ovrDistortionCap_TimeWarp && + !(RState.DistortionCaps & ovrDistortionCap_ProfileNoTimewarpSpinWaits)) { - // If needed, measure distortion time so that TimeManager can better estimate - // latency-reducing time-warp wait timing. - WaitUntilGpuIdle(); - double distortionStartTime = ovr_GetTimeInSeconds(); + if (!TimeManager.NeedDistortionTimeMeasurement()) + { + // Wait for timewarp distortion if it is time and Gpu idle + FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime); - renderDistortion(pEyeTextures[0], pEyeTextures[1]); + renderEndFrame(); + } + else + { + // If needed, measure distortion time so that TimeManager can better estimate + // latency-reducing time-warp wait timing. + WaitUntilGpuIdle(); + double distortionStartTime = ovr_GetTimeInSeconds(); - WaitUntilGpuIdle(); - TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime); - } + renderEndFrame(); - if(latencyTesterDrawColor) + WaitUntilGpuIdle(); + TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime); + } + } + else { - renderLatencyQuad(latencyTesterDrawColor); + renderEndFrame(); } - else if(latencyTester2DrawColor) + + if(LatencyTestActive) { - renderLatencyPixel(latencyTester2DrawColor); + renderLatencyQuad(LatencyTestDrawColor); } if (swapBuffers) @@ -227,11 +275,13 @@ void DistortionRenderer::EndFrame(bool swapBuffers, if (wglGetSwapIntervalEXT() != swapInterval) wglSwapIntervalEXT(swapInterval); - HDC dc = GetDC(RParams.Window); + HDC dc = (RParams.DC != NULL) ? RParams.DC : GetDC(RParams.Window); BOOL success = SwapBuffers(dc); - ReleaseDC(RParams.Window, dc); - OVR_ASSERT(success); + OVR_ASSERT(success); OVR_UNUSED(success); + + if (RParams.DC == NULL) + ReleaseDC(RParams.Window, dc); #elif defined(OVR_OS_MAC) CGLContextObj context = CGLGetCurrentContext(); GLint currentSwapInterval = 0; @@ -253,58 +303,58 @@ void DistortionRenderer::EndFrame(bool swapBuffers, glXSwapBuffers(RParams.Disp, RParams.Win); #endif + // Force GPU to flush the scene, resulting in the lowest possible latency. + // It's critical that this flush is *after* present. + // With the display driver this flush is obsolete and theoretically should + // be a no-op. + // Doesn't need to be done if running through the Oculus driver. + if (RState.OurHMDInfo.InCompatibilityMode && + !(RState.DistortionCaps & ovrDistortionCap_ProfileNoTimewarpSpinWaits)) + WaitUntilGpuIdle(); } } void DistortionRenderer::WaitUntilGpuIdle() { - glFlush(); glFinish(); } double DistortionRenderer::FlushGpuAndWaitTillTime(double absTime) { - double initialTime = ovr_GetTimeInSeconds(); - if (initialTime >= absTime) - return 0.0; - - glFlush(); - glFinish(); + // because glFlush() is not strict enough certain GL drivers + // we do a glFinish(), but before doing so, we make sure we're not + // running late + double initialTime = ovr_GetTimeInSeconds(); + if (initialTime >= absTime) + return 0.0; - double newTime = initialTime; - volatile int i; + glFinish(); - while (newTime < absTime) - { - for (int j = 0; j < 50; j++) - i = 0; - - newTime = ovr_GetTimeInSeconds(); - } - - // How long we waited - return newTime - initialTime; + return WaitTillTime(absTime); } DistortionRenderer::GraphicsState::GraphicsState() { - const char* glVersionString = (const char*)glGetString(GL_VERSION); - OVR_DEBUG_LOG(("GL_VERSION STRING: %s", (const char*)glVersionString)); - char prefix[64]; bool foundVersion = false; - - for (int i = 10; i < 30; ++i) + const char* glVersionString = (const char*)glGetString(GL_VERSION); + if (glVersionString) { - int major = i / 10; - int minor = i % 10; - OVR_sprintf(prefix, 64, "%d.%d", major, minor); - if (strstr(glVersionString, prefix) == glVersionString) + OVR_DEBUG_LOG(("GL_VERSION STRING: %s", (const char*)glVersionString)); + char prefix[64]; + + for (int i = 10; i < 30; ++i) { - GlMajorVersion = major; - GlMinorVersion = minor; - foundVersion = true; - break; + int major = i / 10; + int minor = i % 10; + OVR_sprintf(prefix, 64, "%d.%d", major, minor); + if (strstr(glVersionString, prefix) == glVersionString) + { + GlMajorVersion = major; + GlMinorVersion = minor; + foundVersion = true; + break; + } } } @@ -319,21 +369,34 @@ DistortionRenderer::GraphicsState::GraphicsState() if (GlMajorVersion >= 3) { SupportsVao = true; + SupportsDrawBuffers = true; } else { const char* extensions = (const char*)glGetString(GL_EXTENSIONS); - SupportsVao = (strstr("GL_ARB_vertex_array_object", extensions) != NULL); + SupportsVao = (strstr(extensions, "GL_ARB_vertex_array_object") != NULL + || strstr(extensions, "GL_APPLE_vertex_array_object") != NULL); + SupportsDrawBuffers = (strstr(extensions, "GL_EXT_draw_buffers2") != NULL); } } -void DistortionRenderer::GraphicsState::ApplyBool(GLenum Name, GLint Value) +void DistortionRenderer::GraphicsState::ApplyBool(GLenum Name, GLint Value, GLint index) { - if (Value != 0) - glEnable(Name); - else - glDisable(Name); + if (SupportsDrawBuffers && index != -1) + { + if (Value != 0) + glEnablei(Name, index); + else + glDisablei(Name, index); + } + else + { + if (Value != 0) + glEnable(Name); + else + glDisable(Name); + } } @@ -343,18 +406,34 @@ void DistortionRenderer::GraphicsState::Save() glGetFloatv(GL_COLOR_CLEAR_VALUE, ClearColor); glGetIntegerv(GL_DEPTH_TEST, &DepthTest); glGetIntegerv(GL_CULL_FACE, &CullFace); + glGetIntegerv(GL_FRAMEBUFFER_SRGB, &SRGB); glGetIntegerv(GL_CURRENT_PROGRAM, &Program); glGetIntegerv(GL_ACTIVE_TEXTURE, &ActiveTexture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &TextureBinding); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &VertexArray); + if (SupportsVao) + { + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &VertexArrayBinding); + } + else + { + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ElementArrayBufferBinding); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &ArrayBufferBinding); + } glGetIntegerv(GL_FRAMEBUFFER_BINDING, &FrameBufferBinding); - glGetIntegerv(GL_BLEND, &Blend); - glGetIntegerv(GL_COLOR_WRITEMASK, ColorWritemask); + if (SupportsDrawBuffers) + { + glGetIntegeri_v(GL_BLEND, 0, &Blend); + glGetIntegeri_v(GL_COLOR_WRITEMASK, 0, ColorWritemask); + } + else + { + glGetIntegerv(GL_BLEND, &Blend); + glGetIntegerv(GL_COLOR_WRITEMASK, ColorWritemask); + } glGetIntegerv(GL_DITHER, &Dither); glGetIntegerv(GL_RASTERIZER_DISCARD, &RasterizerDiscard); - if (GlMajorVersion >= 3 && GlMajorVersion >= 2) + if ((GlMajorVersion == 3 && GlMinorVersion >= 2) || GlMajorVersion >= 4) glGetIntegerv(GL_SAMPLE_MASK, &SampleMask); - glGetIntegerv(GL_SCISSOR_TEST, &ScissorTest); IsValid = true; } @@ -371,22 +450,37 @@ void DistortionRenderer::GraphicsState::Restore() ApplyBool(GL_DEPTH_TEST, DepthTest); ApplyBool(GL_CULL_FACE, CullFace); + ApplyBool(GL_FRAMEBUFFER_SRGB, SRGB); glUseProgram(Program); glActiveTexture(ActiveTexture); glBindTexture(GL_TEXTURE_2D, TextureBinding); if (SupportsVao) - glBindVertexArray(VertexArray); + { +#ifdef OVR_OS_MAC + glBindVertexArrayAPPLE(VertexArrayBinding); +#else + glBindVertexArray(VertexArrayBinding); +#endif + } + else + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementArrayBufferBinding); + glBindBuffer(GL_ARRAY_BUFFER, ArrayBufferBinding); + } glBindFramebuffer(GL_FRAMEBUFFER, FrameBufferBinding); - ApplyBool(GL_BLEND, Blend); + ApplyBool(GL_BLEND, Blend, 0); + + if (SupportsDrawBuffers) + glColorMaski(0, (GLboolean)ColorWritemask[0], (GLboolean)ColorWritemask[1], (GLboolean)ColorWritemask[2], (GLboolean)ColorWritemask[3]); + else + glColorMask((GLboolean)ColorWritemask[0], (GLboolean)ColorWritemask[1], (GLboolean)ColorWritemask[2], (GLboolean)ColorWritemask[3]); - glColorMask((GLboolean)ColorWritemask[0], (GLboolean)ColorWritemask[1], (GLboolean)ColorWritemask[2], (GLboolean)ColorWritemask[3]); ApplyBool(GL_DITHER, Dither); ApplyBool(GL_RASTERIZER_DISCARD, RasterizerDiscard); - if (GlMajorVersion >= 3 && GlMajorVersion >= 2) + if ((GlMajorVersion == 3 && GlMinorVersion >= 2) || GlMajorVersion >= 4) ApplyBool(GL_SAMPLE_MASK, SampleMask); - ApplyBool(GL_SCISSOR_TEST, ScissorTest); } @@ -416,16 +510,21 @@ void DistortionRenderer::initBuffersAndShaders() 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); + pCurVBVert->ScreenPosNDC.x = pCurOvrVert->ScreenPosNDC.x; + pCurVBVert->ScreenPosNDC.y = pCurOvrVert->ScreenPosNDC.y; + pCurVBVert->TanEyeAnglesR = (*(Vector2f*)&pCurOvrVert->TanEyeAnglesR); + pCurVBVert->TanEyeAnglesG = (*(Vector2f*)&pCurOvrVert->TanEyeAnglesG); + pCurVBVert->TanEyeAnglesB = (*(Vector2f*)&pCurOvrVert->TanEyeAnglesB); + // Convert [0.0f,1.0f] to [0,255] - pCurVBVert->Col.R = (OVR::UByte)( pCurOvrVert->VignetteFactor * 255.99f ); + if (DistortionCaps & ovrDistortionCap_Vignette) + pCurVBVert->Col.R = (uint8_t)( pCurOvrVert->VignetteFactor * 255.99f ); + else + pCurVBVert->Col.R = 255; + pCurVBVert->Col.G = pCurVBVert->Col.R; pCurVBVert->Col.B = pCurVBVert->Col.R; - pCurVBVert->Col.A = (OVR::UByte)( pCurOvrVert->TimeWarpFactor * 255.99f );; + pCurVBVert->Col.A = (uint8_t)( pCurOvrVert->TimeWarpFactor * 255.99f );; pCurOvrVert++; pCurVBVert++; } @@ -433,7 +532,7 @@ void DistortionRenderer::initBuffersAndShaders() DistortionMeshVBs[eyeNum] = *new Buffer(&RParams); DistortionMeshVBs[eyeNum]->Data ( Buffer_Vertex | Buffer_ReadOnly, pVBVerts, sizeof(DistortionVertex) * meshData.VertexCount ); DistortionMeshIBs[eyeNum] = *new Buffer(&RParams); - DistortionMeshIBs[eyeNum]->Data ( Buffer_Index | Buffer_ReadOnly, meshData.pIndexData, ( sizeof(SInt16) * meshData.IndexCount ) ); + DistortionMeshIBs[eyeNum]->Data ( Buffer_Index | Buffer_ReadOnly, meshData.pIndexData, ( sizeof(int16_t) * meshData.IndexCount ) ); OVR_FREE ( pVBVerts ); ovrHmd_DestroyDistortionMesh( &meshData ); @@ -449,16 +548,29 @@ void DistortionRenderer::renderDistortion(Texture* leftEyeTexture, Texture* righ glBindFramebuffer(GL_FRAMEBUFFER, 0); setViewport( Recti(0,0, RParams.RTSize.w, RParams.RTSize.h) ); + if (DistortionCaps & ovrDistortionCap_SRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else + glDisable(GL_FRAMEBUFFER_SRGB); + glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + if (glState->SupportsDrawBuffers) + { + glDisablei(GL_BLEND, 0); + glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + } + else + { + glDisable(GL_BLEND); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + } + glDisable(GL_DITHER); glDisable(GL_RASTERIZER_DISCARD); - if (glState->GlMajorVersion >= 3 && glState->GlMajorVersion >= 2) + if ((glState->GlMajorVersion >= 3 && glState->GlMinorVersion >= 2) || glState->GlMajorVersion >= 4) glDisable(GL_SAMPLE_MASK); - glDisable(GL_SCISSOR_TEST); glClearColor( RState.ClearColor[0], @@ -536,20 +648,21 @@ void DistortionRenderer::renderLatencyQuad(unsigned char* latencyTesterDrawColor createDrawQuad(); } - ShaderFill quadFill(SimpleQuadShader); + Ptr<ShaderSet> quadShader = (DistortionCaps & ovrDistortionCap_SRGB) ? SimpleQuadGammaShader : SimpleQuadShader; + ShaderFill quadFill(quadShader); //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); + quadShader->SetUniform2f("Scale", 0.3f, 0.3f); + quadShader->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); + quadShader->SetUniform2f("PositionOffset", eyeNum == 0 ? -0.5f : 0.5f, 0.0f); renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false); } } @@ -563,18 +676,28 @@ void DistortionRenderer::renderLatencyPixel(unsigned char* latencyTesterPixelCol createDrawQuad(); } - ShaderFill quadFill(SimpleQuadShader); + Ptr<ShaderSet> quadShader = (DistortionCaps & ovrDistortionCap_SRGB) ? SimpleQuadGammaShader : SimpleQuadShader; + ShaderFill quadFill(quadShader); 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); +#ifdef OVR_BUILD_DEBUG + quadShader->SetUniform4f("Color", (float)latencyTesterPixelColor[0] / 255.99f, + (float)latencyTesterPixelColor[1] / 255.99f, + (float)latencyTesterPixelColor[2] / 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); + Vector2f scale(20.0f / RParams.RTSize.w, 20.0f / RParams.RTSize.h); +#else + quadShader->SetUniform4f("Color", (float)latencyTesterPixelColor[0] / 255.99f, + (float)latencyTesterPixelColor[0] / 255.99f, + (float)latencyTesterPixelColor[0] / 255.99f, + 1.0f); + + Vector2f scale(1.0f / RParams.RTSize.w, 1.0f / RParams.RTSize.h); +#endif + quadShader->SetUniform2f("Scale", scale.x, scale.y); + quadShader->SetUniform2f("PositionOffset", 1.0f-scale.x, 1.0f-scale.y); renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false); } @@ -611,7 +734,11 @@ void DistortionRenderer::renderPrimitives( { if (*vao != 0) { +#ifdef OVR_OS_MAC + glBindVertexArrayAPPLE(*vao); +#else glBindVertexArray(*vao); +#endif if (isDistortionMesh) glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); @@ -622,8 +749,13 @@ void DistortionRenderer::renderPrimitives( { if (glState->SupportsVao) { +#ifdef OVR_OS_MAC + glGenVertexArraysAPPLE(1, vao); + glBindVertexArrayAPPLE(*vao); +#else glGenVertexArrays(1, vao); glBindVertexArray(*vao); +#endif } int attributeCount = (isDistortionMesh) ? 5 : 1; @@ -641,11 +773,11 @@ void DistortionRenderer::renderPrimitives( locs[3] = glGetAttribLocation(prog, "TexCoord1"); locs[4] = glGetAttribLocation(prog, "TexCoord2"); - glVertexAttribPointer(locs[0], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, Pos)); + glVertexAttribPointer(locs[0], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, ScreenPosNDC)); glVertexAttribPointer(locs[1], 4, GL_UNSIGNED_BYTE, true, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, Col)); - glVertexAttribPointer(locs[2], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TexR)); - glVertexAttribPointer(locs[3], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TexG)); - glVertexAttribPointer(locs[4], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TexB)); + glVertexAttribPointer(locs[2], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TanEyeAnglesR)); + glVertexAttribPointer(locs[3], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TanEyeAnglesG)); + glVertexAttribPointer(locs[4], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TanEyeAnglesB)); } else { @@ -751,12 +883,44 @@ void DistortionRenderer::initShaders() SimpleQuadShader->SetShader(ps); delete[](psSource); - } + } + { + size_t vsSize = strlen(shaderPrefix)+sizeof(SimpleQuad_vs); + char* vsSource = new char[vsSize]; + OVR_strcpy(vsSource, vsSize, shaderPrefix); + OVR_strcat(vsSource, vsSize, SimpleQuad_vs); + + Ptr<GL::VertexShader> vs = *new GL::VertexShader( + &RParams, + (void*)vsSource, vsSize, + SimpleQuad_vs_refl, sizeof(SimpleQuad_vs_refl) / sizeof(SimpleQuad_vs_refl[0])); + + SimpleQuadGammaShader = *new ShaderSet; + SimpleQuadGammaShader->SetShader(vs); + + delete[](vsSource); + + size_t psSize = strlen(shaderPrefix)+sizeof(SimpleQuadGamma_fs); + char* psSource = new char[psSize]; + OVR_strcpy(psSource, psSize, shaderPrefix); + OVR_strcat(psSource, psSize, SimpleQuadGamma_fs); + + Ptr<GL::FragmentShader> ps = *new GL::FragmentShader( + &RParams, + (void*)psSource, psSize, + SimpleQuadGamma_fs_refl, sizeof(SimpleQuadGamma_fs_refl) / sizeof(SimpleQuadGamma_fs_refl[0])); + + SimpleQuadGammaShader->SetShader(ps); + + delete[](psSource); + } } void DistortionRenderer::destroy() { + SaveGraphicsState(); + GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); for(int eyeNum = 0; eyeNum < 2; eyeNum++) @@ -778,7 +942,15 @@ void DistortionRenderer::destroy() } LatencyTesterQuadVB.Clear(); - LatencyVAO = 0; + + if(LatencyVAO != 0) + { + glDeleteVertexArrays(1, &LatencyVAO); + LatencyVAO = 0; + } + + RestoreGraphicsState(); } + }}} // OVR::CAPI::GL diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h index 60f1a9f..24dbbd6 100644 --- a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h @@ -47,9 +47,9 @@ public: virtual bool Initialize(const ovrRenderAPIConfig* apiConfig, unsigned distortionCaps); - virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture); + virtual void SubmitEye(int eyeId, const ovrTexture* eyeTexture); - virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor); + virtual void EndFrame(bool swapBuffers); void WaitUntilGpuIdle(); @@ -68,21 +68,25 @@ protected: virtual void Restore(); protected: - void ApplyBool(GLenum Name, GLint Value); + void ApplyBool(GLenum Name, GLint Value, GLint index = -1); public: GLint GlMajorVersion; GLint GlMinorVersion; bool SupportsVao; + bool SupportsDrawBuffers; GLint Viewport[4]; GLfloat ClearColor[4]; GLint DepthTest; GLint CullFace; + GLint SRGB; GLint Program; GLint ActiveTexture; GLint TextureBinding; - GLint VertexArray; + GLint VertexArrayBinding; + GLint ElementArrayBufferBinding; + GLint ArrayBufferBinding; GLint FrameBufferBinding; GLint Blend; @@ -140,6 +144,8 @@ protected: void renderLatencyQuad(unsigned char* latencyTesterDrawColor); void renderLatencyPixel(unsigned char* latencyTesterPixelColor); + void renderEndFrame(); + Ptr<Texture> pEyeTextures[2]; Ptr<Buffer> DistortionMeshVBs[2]; // one per-eye @@ -157,6 +163,7 @@ protected: GLuint LatencyVAO; Ptr<Buffer> LatencyTesterQuadVB; Ptr<ShaderSet> SimpleQuadShader; + Ptr<ShaderSet> SimpleQuadGammaShader; Ptr<Texture> CurRenderTarget; Array<Ptr<Texture> > DepthBuffers; @@ -175,4 +182,4 @@ protected: }}} // OVR::CAPI::GL -#endif // OVR_CAPI_GL_DistortionRenderer_h
\ No newline at end of file +#endif // OVR_CAPI_GL_DistortionRenderer_h diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h index 03fd174..ce7e2c5 100644 --- a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h @@ -72,7 +72,70 @@ namespace OVR { namespace CAPI { namespace GL { { { "Color", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 16 }, }; - + + static const char SimpleQuadGamma_fs[] = + "uniform vec4 Color;\n" + + "_FRAGCOLOR_DECLARATION\n" + + "void main()\n" + "{\n" + " _FRAGCOLOR.rgb = pow(Color.rgb, vec3(2.2));\n" + " _FRAGCOLOR.a = Color.a;\n" + "}\n"; + + const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuadGamma_fs_refl[] = + { + { "Color", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 16 }, + }; + + // This must be prefixed with glsl2Prefix or glsl3Prefix before being compiled. + static const char SimpleTexturedQuad_vs[] = + "uniform vec2 PositionOffset;\n" + "uniform vec2 Scale;\n" + + "_VS_IN vec3 Position;\n" + "_VS_IN vec4 Color;\n" + "_VS_IN vec2 TexCoord;\n" + + "_VS_OUT vec4 oColor;\n" + "_VS_OUT vec2 oTexCoord;\n" + + "void main()\n" + "{\n" + " gl_Position = vec4(Position.xy * Scale + PositionOffset, 0.5, 1.0);\n" + " oColor = Color;\n" + " oTexCoord = TexCoord;\n" + "}\n"; + + // The following declaration is copied from the generated D3D SimpleTexturedQuad_vs_refl.h file, with D3D_NS renamed to GL. + const OVR::CAPI::GL::ShaderBase::Uniform SimpleTexturedQuad_vs_refl[] = + { + { "PositionOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 }, + { "Scale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 }, + }; + + + // This must be prefixed with glsl2Prefix or glsl3Prefix before being compiled. + static const char SimpleTexturedQuad_ps[] = + "uniform sampler2D Texture0;\n" + + "_FS_IN vec4 oColor;\n" + "_FS_IN vec2 oTexCoord;\n" + + "void main()\n" + "{\n" + " gl_FragColor = texture2D(Texture0, oTexCoord);\n" + " if(oColor.a < 0.02)\n" + " gl_FragColor.a = 0.0;\n" + "}\n"; + + // The following is copied from the generated D3D SimpleTexturedQuad_ps_refl.h file, with D3D_NS renamed to GL. + const OVR::CAPI::GL::ShaderBase::Uniform SimpleTexturedQuad_ps_refl[] = + { + { "Color", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 16 }, + }; + static const char Distortion_vs[] = "uniform vec2 EyeToSourceUVScale;\n" @@ -94,7 +157,6 @@ namespace OVR { namespace CAPI { namespace GL { // 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.0 - oTexCoord0.y;\n" " oColor = Color;\n" // Used for vignette fade. "}\n"; @@ -129,8 +191,8 @@ namespace OVR { namespace CAPI { namespace GL { "_VS_IN vec4 Color;\n" "_VS_IN vec2 TexCoord0;\n" - "_FS_IN vec4 oColor;\n" - "_FS_IN vec2 oTexCoord0;\n" + "_VS_OUT vec4 oColor;\n" + "_VS_OUT vec2 oTexCoord0;\n" "void main()\n" "{\n" @@ -163,7 +225,6 @@ namespace OVR { namespace CAPI { namespace GL { // 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.0-oTexCoord0.y;\n" " oColor = vec4(Color.r, Color.r, Color.r, Color.r);\n" // Used for vignette fade. "}\n"; @@ -199,11 +260,8 @@ namespace OVR { namespace CAPI { namespace GL { // 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.0-oTexCoord0.y;\n" " oTexCoord1 = TexCoord1 * EyeToSourceUVScale + EyeToSourceUVOffset;\n" - " oTexCoord1.y = 1.0-oTexCoord1.y;\n" " oTexCoord2 = TexCoord2 * EyeToSourceUVScale + EyeToSourceUVOffset;\n" - " oTexCoord2.y = 1.0-oTexCoord2.y;\n" " oColor = Color;\n" // Used for vignette fade. "}\n"; @@ -303,11 +361,8 @@ namespace OVR { namespace CAPI { namespace GL { " vec2 SrcCoordB = FlattenedB * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord0 = SrcCoordR;\n" - " oTexCoord0.y = 1.0-oTexCoord0.y;\n" " oTexCoord1 = SrcCoordG;\n" - " oTexCoord1.y = 1.0-oTexCoord1.y;\n" " oTexCoord2 = SrcCoordB;\n" - " oTexCoord2.y = 1.0-oTexCoord2.y;\n" " oColor = vec4(Color.r, Color.r, Color.r, Color.r);\n" // Used for vignette fade. "}\n"; diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp new file mode 100644 index 0000000..36830ca --- /dev/null +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp @@ -0,0 +1,460 @@ +/************************************************************************************ + +Filename : CAPI_GL_HSWDisplay.cpp +Content : Implements Health and Safety Warning system. +Created : July 7, 2014 +Authors : Paul Pedriana + +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_GL_HSWDisplay.h" +#include "CAPI_GL_DistortionShaders.h" +#include "../../OVR_CAPI_GL.h" +#include "../../Kernel/OVR_File.h" +#include "../../Kernel/OVR_Math.h" +#include "../../Kernel/OVR_Allocator.h" +#include "../../Kernel/OVR_Color.h" + +#include "../Textures/healthAndSafety.tga.h" + +OVR_DISABLE_MSVC_WARNING(4996) // "This function or variable may be unsafe..." + + +namespace OVR { namespace CAPI { + + +// Loads the TGA data from the File as an array of width * height 32 bit Texture_RGBA values. +// Returned pointer must be freed with OVR_FREE. +uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height) +{ + // See http://www.fileformat.info/format/tga/egff.htm for format details. + // The TGA file must be exported with compression disabled and with the origin set to the top-left. + // To do: Support at least RLE formats. + // TGA files are stored with little-endian data. + uint8_t* pRGBA = NULL; + + f->SeekToBegin(); + + const int desclen = f->ReadUByte(); + const int palette = f->ReadUByte(); + OVR_UNUSED(palette); + const int imgtype = f->ReadUByte(); + f->ReadUInt16(); // Skip bytes + int palCount = f->ReadUInt16(); + int palSize = f->ReadUByte(); + f->ReadUInt16(); + f->ReadUInt16(); + width = f->ReadUInt16(); + height = f->ReadUInt16(); + int bpp = f->ReadUByte(); + f->ReadUByte(); + + OVR_ASSERT((imgtype == 2) && ((bpp == 24) || (bpp == 32))); + + if((imgtype == 2) && ((bpp == 24) || (bpp == 32))) + { + int imgsize = width * height * 4; + pRGBA = (uint8_t*) OVR_ALLOC(imgsize); + f->Skip(desclen); + f->Skip(palCount * (palSize + 7) >> 3); + int strideBytes = width * 4; // This is the number of bytes between successive rows. + + + unsigned char buf[16]; + + switch (imgtype) + { + case 2: // uncompressed true-color image -- the only image type we support. + switch (bpp) + { + case 24: + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + f->Read(buf, 3); // Data is stored as B, G, R + pRGBA[y*strideBytes + x*4 + 0] = buf[2]; + pRGBA[y*strideBytes + x*4 + 1] = buf[1]; + pRGBA[y*strideBytes + x*4 + 2] = buf[0]; + pRGBA[y*strideBytes + x*4 + 3] = alpha; + } + } + break; + case 32: + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + f->Read(buf, 4); // Data is stored as B, G, R, A + pRGBA[y*strideBytes + x*4 + 0] = buf[2]; + pRGBA[y*strideBytes + x*4 + 1] = buf[1]; + pRGBA[y*strideBytes + x*4 + 2] = buf[0]; + pRGBA[y*strideBytes + x*4 + 3] = buf[3]; + } + } + break; + } + break; + } + } + + return pRGBA; +} // LoadTextureTgaData + + + +namespace GL { + + +// To do: This needs to be promoted to a central version, possibly in CAPI_HSWDisplay.h +struct HASWVertex +{ + Vector3f Pos; + Color C; + float U, V; + + HASWVertex(const Vector3f& p, const Color& c = Color(64,0,0,255), float u = 0, float v = 0) + : Pos(p), C(c), U(u), V(v) + {} + + HASWVertex(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 HASWVertex& b) const + { + return (Pos == b.Pos) && (C == b.C) && (U == b.U) && (V == b.V); + } +}; + + + +// This is a temporary function implementation, and it functionality needs to be implemented in a more generic way. +Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, OVR::File* f, uint8_t alpha) +{ + OVR::CAPI::GL::Texture* pTexture = NULL; + + int width, height; + const uint8_t* pRGBA = LoadTextureTgaData(f, alpha, width, height); + + if (pRGBA) + { + pTexture = new OVR::CAPI::GL::Texture(&rParams, width, height); + + // SetSampleMode forces the use of mipmaps through GL_LINEAR_MIPMAP_LINEAR. + pTexture->SetSampleMode(samplerMode); // Calls glBindTexture internally. + + // We are intentionally not using mipmaps. We need to use this because Texture::SetSampleMode unilaterally uses GL_LINEAR_MIPMAP_LINEAR. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); + OVR_ASSERT(glGetError() == 0); + + // With OpenGL 4.2+ we can use this instead of glTexImage2D: + // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); + // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); + + OVR_FREE(const_cast<uint8_t*>(pRGBA)); + } + + return pTexture; +} + + +// Loads a texture from a memory image of a TGA file. +Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, const uint8_t* pData, int dataSize, uint8_t alpha) +{ + MemoryFile memoryFile("", pData, dataSize); + + return LoadTextureTga(rParams, samplerMode, &memoryFile, alpha); +} + + + + +// The texture below may conceivably be shared between HSWDisplay instances. However, +// beware that sharing may not be possible if two HMDs are using different locales +// simultaneously. As of this writing it's not clear if that can occur in practice. + +HSWDisplay::HSWDisplay(ovrRenderAPIType api, ovrHmd hmd, const HMDRenderState& renderState) + : OVR::CAPI::HSWDisplay(api, hmd, renderState), + RenderParams(), + FrameBuffer(0) +{ +} + +bool HSWDisplay::Initialize(const ovrRenderAPIConfig* apiConfig) +{ + const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; + + if(config) + { + // The following is essentially copied from CAPI_GL_DistortionRender.cpp's + // Initialize function. To do: Merge this to a central location. + RenderParams.Multisample = config->OGL.Header.Multisample; + RenderParams.RTSize = config->OGL.Header.RTSize; + + #if defined(OVR_OS_WIN32) + RenderParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow(); + RenderParams.DC = config->OGL.DC; + #elif defined(OVR_OS_LINUX) + if (config->OGL.Disp) + RenderParams.Disp = config->OGL.Disp; + if (!RenderParams.Disp) + RenderParams.Disp = XOpenDisplay(NULL); + if (!RenderParams.Disp) + { + OVR_DEBUG_LOG(("XOpenDisplay failed.")); + return false; + } + + if (config->OGL.Win) + RenderParams.Win= config->OGL.Win; + if (!RenderParams.Win) + { + int unused; + RenderParams.Win = glXGetCurrentDrawable(); + } + if (!RenderParams.Win) + { + OVR_DEBUG_LOG(("XGetInputFocus failed.")); + return false; + } + #endif + } + else + { + UnloadGraphics(); + } + + return true; +} + + +void HSWDisplay::Shutdown() +{ + UnloadGraphics(); +} + + +void HSWDisplay::DisplayInternal() +{ + HSWDISPLAY_LOG(("[HSWDisplay GL] DisplayInternal()")); + // We may want to call LoadGraphics here instead of within Render. +} + + +void HSWDisplay::DismissInternal() +{ + HSWDISPLAY_LOG(("[HSWDisplay GL] DismissInternal()")); + UnloadGraphics(); +} + + +void HSWDisplay::UnloadGraphics() +{ + // RenderParams: No need to clear. + if(FrameBuffer != 0) + { + glDeleteFramebuffers(1, &FrameBuffer); + FrameBuffer = 0; + } + pTexture.Clear(); + pShaderSet.Clear(); + pVertexShader.Clear(); + pFragmentShader.Clear(); + pVB.Clear(); + // OrthoProjection: No need to clear. +} + + +void HSWDisplay::LoadGraphics() +{ + int glVersionMajor = 0; + int glVersionMinor = 0; + const char* glVersionString = (const char*)glGetString(GL_VERSION); + + OVR_ASSERT(glVersionString); + if (glVersionString) + { + int fieldCount = sscanf(glVersionString, isdigit(*glVersionString) ? "%d.%d" : "%*[^0-9]%d.%d", &glVersionMajor, &glVersionMinor); // Skip all leading non-digits before reading %d. Example glVersionStrings: "1.5 ATI-1.4.18", "OpenGL ES-CM 3.2" + + if(fieldCount != 2) + { + static_assert(sizeof(glVersionMajor) == sizeof(GLint), "type mis-match"); + glGetIntegerv(GL_MAJOR_VERSION, &glVersionMajor); + } + } + + if (FrameBuffer == 0) + glGenFramebuffers(1, &FrameBuffer); + + if (!pTexture) // To do: Add support for .dds files, which would be significantly smaller than the size of the tga. + pTexture = *LoadTextureTga(RenderParams, Sample_Linear | Sample_Clamp, healthAndSafety_tga, (int)sizeof(healthAndSafety_tga), 255); + + if(!pShaderSet) + pShaderSet = *new ShaderSet(); + + if(!pVertexShader) + { + OVR::String strShader((glVersionMajor >= 3) ? glsl3Prefix : glsl2Prefix); + strShader += SimpleTexturedQuad_vs; + + pVertexShader = *new VertexShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_vs_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_vs_refl)); + pShaderSet->SetShader(pVertexShader); + } + + if(!pFragmentShader) + { + OVR::String strShader((glVersionMajor >= 3) ? glsl3Prefix : glsl2Prefix); + strShader += SimpleTexturedQuad_ps; + + pFragmentShader = *new FragmentShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_ps_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_ps_refl)); + pShaderSet->SetShader(pFragmentShader); + } + + if(!pVB) + { + pVB = *new Buffer(&RenderParams); + + pVB->Data(Buffer_Vertex, NULL, 4 * sizeof(HASWVertex)); + HASWVertex* pVertices = (HASWVertex*)pVB->Map(0, 4 * sizeof(HASWVertex), Map_Discard); + OVR_ASSERT(pVertices); + + if(pVertices) + { + const bool flip = ((RenderState.DistortionCaps & ovrDistortionCap_FlipInput) != 0); + const float left = -1.0f; // We currently draw this in normalized device coordinates with an stereo translation + const float top = -1.1f; // applied as a vertex shader uniform. In the future when we have a more formal graphics + const float right = 1.0f; // API abstraction we may move this draw to an overlay layer or to a more formal + const float bottom = 0.9f; // model/mesh scheme with a perspective projection. + + pVertices[0] = HASWVertex(left, top, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 1.f : 0.f); // To do: Make this branchless + pVertices[1] = HASWVertex(left, bottom, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 0.f : 1.f); + pVertices[2] = HASWVertex(right, top, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 1.f : 0.f); + pVertices[3] = HASWVertex(right, bottom, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 0.f : 1.f); + + pVB->Unmap(pVertices); + } + } + + // Calculate ortho projection. + GetOrthoProjection(RenderState, OrthoProjection); +} + + +void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) +{ + if(RenderEnabled && eyeTexture) + { + // We need to render to the eyeTexture with the texture viewport. + // Setup rendering to the texture. + ovrGLTexture* eyeTextureGL = const_cast<ovrGLTexture*>(reinterpret_cast<const ovrGLTexture*>(eyeTexture)); + OVR_ASSERT(eyeTextureGL->Texture.Header.API == ovrRenderAPI_OpenGL); + + // Load the graphics if not loaded already. + if (!pTexture) + LoadGraphics(); + + // Set the rendering to be to the eye texture. + glBindFramebuffer(GL_FRAMEBUFFER, FrameBuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, eyeTextureGL->OGL.TexId, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); // We aren't using depth, as we currently want this to overwrite everything. + // GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + // glDrawBuffers(OVR_ARRAY_COUNT(DrawBuffers), DrawBuffers); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + OVR_ASSERT(status == GL_FRAMEBUFFER_COMPLETE); OVR_UNUSED(status); + + // Set up the viewport + const GLint x = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.x; + const GLint y = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.y; // Note that GL uses bottom-up coordinates. + const GLsizei w = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.w; + const GLsizei h = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.h; + glViewport(x, y, w, h); + + // Set fixed-function render states + glDisable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); // Irrelevant to our case here. + glFrontFace(GL_CW); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + OVR_ASSERT(glGetError() == 0); + + // Enable the buffer and shaders we use. + glBindBuffer(GL_ARRAY_BUFFER, pVB->GLBuffer); + OVR_ASSERT(glGetError() == 0); + + ShaderFill fill(pShaderSet); + if(pTexture) + { + fill.SetTexture(0, pTexture); + OVR_ASSERT(glGetError() == 0); + } + + // Set shader uniforms. + const float scale = HSWDISPLAY_SCALE * ((RenderState.OurHMDInfo.HmdType == HmdType_DK1) ? 0.70f : 1.f); + pShaderSet->SetUniform2f("Scale", scale, scale / 2.f); // X and Y scale. Y is a fixed proportion to X in order to give a certain aspect ratio. + pShaderSet->SetUniform2f("PositionOffset", OrthoProjection[eye].GetTranslation().x, 0.0f); + OVR_ASSERT(glGetError() == 0); + + // Set vertex attributes + // To consider: We can use glGenVertexArrays + glBindVertexArray here to tell GL to store the attrib values below in + // a vertex array object so later we can simply call glBindVertexArray(VertexArrayObject) to enable them instead + // of doing all the calls below again. glBindVertexArray(0) to unbind, glDeleteVertexArrays to destory. Requires + // OpenGL v3+ or the GL_ARB_vertex_array_object extension. + + const GLuint shaderProgram = pShaderSet->Prog; + int attributeLocationArray[3]; + + attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position"); + glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, Pos))); + + attributeLocationArray[1] = glGetAttribLocation(shaderProgram, "Color"); + glVertexAttribPointer(attributeLocationArray[1], sizeof(Color)/sizeof(uint8_t), GL_UNSIGNED_BYTE, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, C))); + + attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord"); + glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, U))); + + for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++) + glEnableVertexAttribArray((GLuint)i); + OVR_ASSERT(glGetError() == 0); + + fill.Set(Prim_TriangleStrip); + OVR_ASSERT(glGetError() == 0); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + OVR_ASSERT(glGetError() == 0); + + for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++) + glDisableVertexAttribArray(i); + } +} + + +}}} // namespace OVR::CAPI::GL + + + + + + + diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.h b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.h new file mode 100644 index 0000000..c799e5d --- /dev/null +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.h @@ -0,0 +1,74 @@ +/************************************************************************************ + +Filename : CAPI_GL_HSWDisplay.h +Content : Implements Health and Safety Warning system. +Created : July 7, 2014 +Authors : Paul Pedriana + +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_GL_HSWDisplay_h +#define OVR_CAPI_GL_HSWDisplay_h + + +#include "../CAPI_HSWDisplay.h" +#include "CAPI_GL_Util.h" + + +namespace OVR { namespace CAPI { namespace GL { + + class HSWDisplay : public CAPI::HSWDisplay + { + public: + HSWDisplay(ovrRenderAPIType api, ovrHmd hmd, const HMDRenderState& renderState); + + // Must be called before use. apiConfig is such that: + // const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; or + bool Initialize(const ovrRenderAPIConfig* apiConfig); + void Shutdown(); + void DisplayInternal(); + void DismissInternal(); + + // Draws the warning to the eye texture(s). This must be done at the end of a + // frame but prior to executing the distortion rendering of the eye textures. + void RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture); + + protected: + void UnloadGraphics(); + void LoadGraphics(); + + OVR::CAPI::GL::RenderParams RenderParams; + GLuint FrameBuffer; // This is a container for a texture, depth buffer, stencil buffer to be rendered to. To consider: Make a wrapper class, like the OculusWorldDemo RBuffer class. + Ptr<OVR::CAPI::GL::Texture> pTexture; + Ptr<OVR::CAPI::GL::ShaderSet> pShaderSet; + Ptr<OVR::CAPI::GL::VertexShader> pVertexShader; + Ptr<OVR::CAPI::GL::FragmentShader> pFragmentShader; + Ptr<OVR::CAPI::GL::Buffer> pVB; + Matrix4f OrthoProjection[2]; // Projection for 2D. + + private: + OVR_NON_COPYABLE(HSWDisplay) + }; + +}}} // namespace OVR::CAPI::GL + + +#endif // OVR_CAPI_GL_HSWDisplay_h + diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp index 910e28c..7c0b46b 100644 --- a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp @@ -36,6 +36,7 @@ namespace OVR { namespace CAPI { namespace GL { PFNWGLGETPROCADDRESS wglGetProcAddress; +PFNGLGETERRORPROC glGetError; PFNGLENABLEPROC glEnable; PFNGLDISABLEPROC glDisable; PFNGLGETFLOATVPROC glGetFloatv; @@ -54,6 +55,9 @@ PFNGLDRAWARRAYSPROC glDrawArrays; PFNGLGENTEXTURESPROC glGenTextures; PFNGLDELETETEXTURESPROC glDeleteTextures; PFNGLBINDTEXTUREPROC glBindTexture; +PFNGLTEXIMAGE2DPROC glTexImage2D; +PFNGLBLENDFUNCPROC glBlendFunc; +PFNGLFRONTFACEPROC glFrontFace; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; @@ -64,7 +68,16 @@ PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; #endif +PFNGLENABLEIPROC glEnablei; +PFNGLDISABLEIPROC glDisablei; +PFNGLCOLORMASKIPROC glColorMaski; +PFNGLGETINTEGERI_VPROC glGetIntegeri_v; +PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; PFNGLDELETESHADERPROC glDeleteShader; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; PFNGLACTIVETEXTUREPROC glActiveTexture; PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; @@ -131,6 +144,7 @@ void InitGLExtensions() if (!hInst) return; + glGetError = (PFNGLGETERRORPROC) GetProcAddress(hInst, "glGetError"); glGetFloatv = (PFNGLGETFLOATVPROC) GetProcAddress(hInst, "glGetFloatv"); glGetIntegerv = (PFNGLGETINTEGERVPROC) GetProcAddress(hInst, "glGetIntegerv"); glGetString = (PFNGLGETSTRINGPROC) GetProcAddress(hInst, "glGetString"); @@ -145,19 +159,32 @@ void InitGLExtensions() glFinish = (PFNGLFINISHPROC) GetProcAddress(hInst, "glFinish"); glDrawArrays = (PFNGLDRAWARRAYSPROC) GetProcAddress(hInst, "glDrawArrays"); glDrawElements = (PFNGLDRAWELEMENTSPROC) GetProcAddress(hInst, "glDrawElements"); - glGenTextures = (PFNGLGENTEXTURESPROC) GetProcAddress(hInst,"glGenTextures"); - glDeleteTextures = (PFNGLDELETETEXTURESPROC) GetProcAddress(hInst,"glDeleteTextures"); - glBindTexture = (PFNGLBINDTEXTUREPROC) GetProcAddress(hInst,"glBindTexture"); + glGenTextures = (PFNGLGENTEXTURESPROC) GetProcAddress(hInst, "glGenTextures"); + glDeleteTextures = (PFNGLDELETETEXTURESPROC) GetProcAddress(hInst, "glDeleteTextures"); + glBindTexture = (PFNGLBINDTEXTUREPROC) GetProcAddress(hInst, "glBindTexture"); + glTexImage2D = (PFNGLTEXIMAGE2DPROC) GetProcAddress(hInst, "glTexImage2D"); glTexParameteri = (PFNGLTEXPARAMETERIPROC) GetProcAddress(hInst, "glTexParameteri"); + glBlendFunc = (PFNGLBLENDFUNCPROC) GetProcAddress(hInst, "glBlendFunc"); + glFrontFace = (PFNGLFRONTFACEPROC) GetProcAddress(hInst, "glFrontFace"); wglGetProcAddress = (PFNWGLGETPROCADDRESS) GetProcAddress(hInst, "wglGetProcAddress"); wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) GetFunction("wglGetSwapIntervalEXT"); wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) GetFunction("wglSwapIntervalEXT"); #elif defined(OVR_OS_LINUX) + glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) GetFunction("glXSwapIntervalEXT"); #endif - + + glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) GetFunction("glGenFramebuffersEXT"); + glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) GetFunction("glDeleteFramebuffersEXT"); + glEnablei = (PFNGLENABLEIPROC) GetFunction("glEnableIndexedEXT"); + glDisablei = (PFNGLDISABLEIPROC) GetFunction("glDisableIndexedEXT"); + glColorMaski = (PFNGLCOLORMASKIPROC) GetFunction("glColorMaskIndexedEXT"); + glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) GetFunction("glGetIntegerIndexedvEXT"); + glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) GetFunction("glCheckFramebufferStatusEXT"); + glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) GetFunction("glFramebufferRenderbufferEXT"); + glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) GetFunction("glFramebufferTexture2DEXT"); glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) GetFunction("glBindFramebufferEXT"); glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) GetFunction("glGenVertexArrays"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) GetFunction("glDeleteVertexArrays"); @@ -490,7 +517,7 @@ void Texture::SetSampleMode(int sm) break; case Sample_Nearest: - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); break; @@ -527,4 +554,7 @@ void Texture::UpdatePlaceholderTexture(GLuint texId, const Sizei& textureSize) IsUserAllocated = true; } + }}} + + diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h index a410f17..e9320d2 100644 --- a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h @@ -33,12 +33,13 @@ limitations under the License. #include "../../Kernel/OVR_Log.h" #if defined(OVR_OS_WIN32) +#define WIN32_LEAN_AND_MEAN #include <Windows.h> #endif #if defined(OVR_OS_MAC) -#include <OpenGL/gl3.h> -#include <OpenGL/gl3ext.h> +#include <OpenGL/gl.h> +#include <OpenGL/glext.h> #else #ifndef GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES @@ -60,6 +61,7 @@ namespace OVR { namespace CAPI { namespace GL { // Let Windows apps build without linking GL. #if defined(OVR_OS_WIN32) +typedef GLenum (__stdcall *PFNGLGETERRORPROC) (); typedef void (__stdcall *PFNGLENABLEPROC) (GLenum); typedef void (__stdcall *PFNGLDISABLEPROC) (GLenum); typedef void (__stdcall *PFNGLGETFLOATVPROC) (GLenum, GLfloat*); @@ -75,21 +77,29 @@ typedef void (__stdcall *PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLe 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 *PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLint format, GLenum type, const GLvoid *pixels); 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); +typedef void (__stdcall *PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); +typedef void (__stdcall *PFNGLFRONTFACEPROC) (GLenum mode); extern PFNWGLGETPROCADDRESS wglGetProcAddress; extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; +extern PFNGLGETERRORPROC glGetError; extern PFNGLENABLEPROC glEnable; +extern PFNGLENABLEIPROC glEnablei; extern PFNGLDISABLEPROC glDisable; +extern PFNGLDISABLEIPROC glDisablei; extern PFNGLCOLORMASKPROC glColorMask; +extern PFNGLCOLORMASKIPROC glColorMaski; extern PFNGLGETFLOATVPROC glGetFloatv; extern PFNGLGETSTRINGPROC glGetString; extern PFNGLGETINTEGERVPROC glGetIntegerv; +extern PFNGLGETINTEGERI_VPROC glGetIntegeri_v; extern PFNGLCLEARPROC glClear; extern PFNGLCLEARCOLORPROC glClearColor; extern PFNGLCLEARDEPTHPROC glClearDepth; @@ -99,9 +109,12 @@ extern PFNGLDRAWELEMENTSPROC glDrawElements; extern PFNGLGENTEXTURESPROC glGenTextures; extern PFNGLDELETETEXTURESPROC glDeleteTextures; extern PFNGLBINDTEXTUREPROC glBindTexture; +extern PFNGLTEXIMAGE2DPROC glTexImage2D; extern PFNGLTEXPARAMETERIPROC glTexParameteri; extern PFNGLFLUSHPROC glFlush; extern PFNGLFINISHPROC glFinish; +extern PFNGLBLENDFUNCPROC glBlendFunc; +extern PFNGLFRONTFACEPROC glFrontFace; #elif defined(OVR_OS_LINUX) @@ -109,7 +122,12 @@ extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; #endif // defined(OVR_OS_WIN32) +extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; extern PFNGLDELETESHADERPROC glDeleteShader; +extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; extern PFNGLACTIVETEXTUREPROC glActiveTexture; extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; @@ -223,9 +241,10 @@ struct RenderParams { #if defined(OVR_OS_WIN32) HWND Window; + HDC DC; #elif defined(OVR_OS_LINUX) - Display* Disp; - Window Win; + _XDisplay* Disp; + Window Win; #endif ovrSizei RTSize; @@ -532,6 +551,8 @@ private: typedef ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER> VertexShader; typedef ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER> FragmentShader; + }}} + #endif // INC_OVR_CAPI_GL_Util_h |