/************************************************************************************ 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 "CAPI_GL_DistortionShaders.h" #include "../../OVR_CAPI_GL.h" namespace OVR { namespace CAPI { namespace GL { // 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) }; static ShaderInfo DistortionPixelShaderLookup[DistortionPixelShaderCount] = { SI_NOREFL(Distortion_fs), SI_NOREFL(DistortionChroma_fs) }; void DistortionShaderBitIndexCheck() { OVR_COMPILER_ASSERT(ovrDistortionCap_Chromatic == 1); OVR_COMPILER_ASSERT(ovrDistortionCap_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) , LatencyVAO(0) { DistortionMeshVAOs[0] = 0; DistortionMeshVAOs[1] = 0; } DistortionRenderer::~DistortionRenderer() { destroy(); } // static CAPI::DistortionRenderer* DistortionRenderer::Create(ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState) { #if !defined(OVR_OS_MAC) InitGLExtensions(); #endif return new DistortionRenderer(hmd, timeManager, renderState); } bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig, unsigned distortionCaps) { GfxState = *new GraphicsState(); const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; if (!config) { // Cleanup pEyeTextures[0].Clear(); pEyeTextures[1].Clear(); memset(&RParams, 0, sizeof(RParams)); return true; } RParams.Multisample = config->OGL.Header.Multisample; RParams.RTSize = config->OGL.Header.RTSize; #if defined(OVR_OS_WIN32) RParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow(); #elif defined(OVR_OS_LINUX) RParams.Disp = (config->OGL.Disp) ? config->OGL.Disp : XOpenDisplay(NULL); RParams.Win = config->OGL.Win; if (!RParams.Win) { int unused; XGetInputFocus(RParams.Disp, &RParams.Win, &unused); } #endif 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. eachEye[eyeId].TextureSize = tex->OGL.Header.TextureSize; eachEye[eyeId].RenderViewport = tex->OGL.Header.RenderViewport; const ovrEyeRenderDesc& erd = RState.EyeRenderDesc[eyeId]; ovrHmd_GetRenderScaleAndOffset( erd.Fov, eachEye[eyeId].TextureSize, eachEye[eyeId].RenderViewport, 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 & ovrDistortionCap_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.EnabledHmdCaps & ovrHmdCap_NoVSync) == 0); int swapInterval = (useVsync) ? 1 : 0; #if defined(OVR_OS_WIN32) if (wglGetSwapIntervalEXT() != swapInterval) wglSwapIntervalEXT(swapInterval); HDC dc = GetDC(RParams.Window); BOOL success = SwapBuffers(dc); ReleaseDC(RParams.Window, dc); OVR_ASSERT(success); OVR_UNUSED(success); #elif defined(OVR_OS_MAC) CGLContextObj context = CGLGetCurrentContext(); GLint currentSwapInterval = 0; CGLGetParameter(context, kCGLCPSwapInterval, ¤tSwapInterval); if (currentSwapInterval != swapInterval) CGLSetParameter(context, kCGLCPSwapInterval, &swapInterval); CGLFlushDrawable(context); #elif defined(OVR_OS_LINUX) static const char* extensions = glXQueryExtensionsString(RParams.Disp, 0); static bool supportsVSync = (extensions != NULL && strstr(extensions, "GLX_EXT_swap_control")); if (supportsVSync) { GLuint currentSwapInterval = 0; glXQueryDrawable(RParams.Disp, RParams.Win, GLX_SWAP_INTERVAL_EXT, ¤tSwapInterval); if (currentSwapInterval != swapInterval) glXSwapIntervalEXT(RParams.Disp, RParams.Win, swapInterval); } glXSwapBuffers(RParams.Disp, RParams.Win); #endif } } 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; } 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) { 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; } } if (!foundVersion) { glGetIntegerv(GL_MAJOR_VERSION, &GlMajorVersion); glGetIntegerv(GL_MAJOR_VERSION, &GlMinorVersion); } OVR_ASSERT(GlMajorVersion >= 2); if (GlMajorVersion >= 3) { SupportsVao = true; } else { const char* extensions = (const char*)glGetString(GL_EXTENSIONS); SupportsVao = (strstr("GL_ARB_vertex_array_object", extensions) != NULL); } } void DistortionRenderer::GraphicsState::ApplyBool(GLenum Name, GLint Value) { if (Value != 0) glEnable(Name); else glDisable(Name); } void DistortionRenderer::GraphicsState::Save() { glGetIntegerv(GL_VIEWPORT, Viewport); glGetFloatv(GL_COLOR_CLEAR_VALUE, ClearColor); glGetIntegerv(GL_DEPTH_TEST, &DepthTest); glGetIntegerv(GL_CULL_FACE, &CullFace); glGetIntegerv(GL_CURRENT_PROGRAM, &Program); glGetIntegerv(GL_ACTIVE_TEXTURE, &ActiveTexture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &TextureBinding); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &VertexArray); glGetIntegerv(GL_FRAMEBUFFER_BINDING, &FrameBufferBinding); glGetIntegerv(GL_BLEND, &Blend); glGetIntegerv(GL_COLOR_WRITEMASK, ColorWritemask); glGetIntegerv(GL_DITHER, &Dither); glGetIntegerv(GL_RASTERIZER_DISCARD, &RasterizerDiscard); if (GlMajorVersion >= 3 && GlMajorVersion >= 2) glGetIntegerv(GL_SAMPLE_MASK, &SampleMask); glGetIntegerv(GL_SCISSOR_TEST, &ScissorTest); IsValid = true; } void DistortionRenderer::GraphicsState::Restore() { // Don't allow restore-before-save. if (!IsValid) return; glViewport(Viewport[0], Viewport[1], Viewport[2], Viewport[3]); glClearColor(ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]); ApplyBool(GL_DEPTH_TEST, DepthTest); ApplyBool(GL_CULL_FACE, CullFace); glUseProgram(Program); glActiveTexture(ActiveTexture); glBindTexture(GL_TEXTURE_2D, TextureBinding); if (SupportsVao) glBindVertexArray(VertexArray); glBindFramebuffer(GL_FRAMEBUFFER, FrameBufferBinding); ApplyBool(GL_BLEND, Blend); 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) ApplyBool(GL_SAMPLE_MASK, SampleMask); ApplyBool(GL_SCISSOR_TEST, ScissorTest); } 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].Eye, RState.EyeRenderDesc[eyeNum].Fov, RState.DistortionCaps, &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 | 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 ) ); OVR_FREE ( pVBVerts ); ovrHmd_DestroyDistortionMesh( &meshData ); } initShaders(); } void DistortionRenderer::renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture) { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); glBindFramebuffer(GL_FRAMEBUFFER, 0); setViewport( Recti(0,0, RParams.RTSize.w, RParams.RTSize.h) ); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); 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) glDisable(GL_SAMPLE_MASK); glDisable(GL_SCISSOR_TEST); glClearColor( RState.ClearColor[0], RState.ClearColor[1], RState.ClearColor[2], RState.ClearColor[3] ); glClear(GL_COLOR_BUFFER_BIT); for (int eyeNum = 0; eyeNum < 2; eyeNum++) { ShaderFill distortionShaderFill(DistortionShader); distortionShaderFill.SetTexture(0, eyeNum == 0 ? leftEyeTexture : rightEyeTexture); DistortionShader->SetUniform2f("EyeToSourceUVScale", eachEye[eyeNum].UVScaleOffset[0].x, eachEye[eyeNum].UVScaleOffset[0].y); DistortionShader->SetUniform2f("EyeToSourceUVOffset", eachEye[eyeNum].UVScaleOffset[1].x, eachEye[eyeNum].UVScaleOffset[1].y); if (DistortionCaps & ovrDistortionCap_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], 0, (int)DistortionMeshIBs[eyeNum]->GetSize()/2, Prim_Triangles, &DistortionMeshVAOs[eyeNum], true); } else { renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum], 0, (int)DistortionMeshIBs[eyeNum]->GetSize()/2, Prim_Triangles, &DistortionMeshVAOs[eyeNum], 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, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, 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, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false); } void DistortionRenderer::renderPrimitives( const ShaderFill* fill, Buffer* vertices, Buffer* indices, int offset, int count, PrimitiveType rprim, GLuint* vao, bool isDistortionMesh) { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); 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: OVR_ASSERT(false); return; } fill->Set(); GLuint prog = fill->GetShaders()->Prog; if (vao != NULL) { if (*vao != 0) { glBindVertexArray(*vao); if (isDistortionMesh) glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); else glDrawArrays(prim, 0, count); } else { if (glState->SupportsVao) { glGenVertexArrays(1, vao); glBindVertexArray(*vao); } int attributeCount = (isDistortionMesh) ? 5 : 1; int* locs = new int[attributeCount]; glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer); if (isDistortionMesh) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer); locs[0] = glGetAttribLocation(prog, "Position"); locs[1] = glGetAttribLocation(prog, "Color"); locs[2] = glGetAttribLocation(prog, "TexCoord0"); locs[3] = glGetAttribLocation(prog, "TexCoord1"); locs[4] = glGetAttribLocation(prog, "TexCoord2"); glVertexAttribPointer(locs[0], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, Pos)); glVertexAttribPointer(locs[1], 4, GL_UNSIGNED_BYTE, true, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, Col)); glVertexAttribPointer(locs[2], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, TexR)); glVertexAttribPointer(locs[3], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, TexG)); glVertexAttribPointer(locs[4], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, TexB)); } else { locs[0] = glGetAttribLocation(prog, "Position"); glVertexAttribPointer(locs[0], 3, GL_FLOAT, false, sizeof(LatencyVertex), reinterpret_cast(offset)+offsetof(LatencyVertex, Pos)); } for (int i = 0; i < attributeCount; ++i) glEnableVertexAttribArray(locs[i]); if (isDistortionMesh) glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); else glDrawArrays(prim, 0, count); if (!glState->SupportsVao) { for (int i = 0; i < attributeCount; ++i) glDisableVertexAttribArray(locs[i]); } delete[] locs; } } } void DistortionRenderer::setViewport(const Recti& vp) { glViewport(vp.x, vp.y, vp.w, vp.h); } void DistortionRenderer::initShaders() { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); const char* shaderPrefix = (glState->GlMajorVersion < 3 || (glState->GlMajorVersion == 3 && glState->GlMinorVersion < 2)) ? glsl2Prefix : glsl3Prefix; { ShaderInfo vsInfo = DistortionVertexShaderLookup[DistortionVertexShaderBitMask & DistortionCaps]; size_t vsSize = strlen(shaderPrefix)+vsInfo.ShaderSize; char* vsSource = new char[vsSize]; OVR_strcpy(vsSource, vsSize, shaderPrefix); OVR_strcat(vsSource, vsSize, vsInfo.ShaderData); Ptr vs = *new GL::VertexShader( &RParams, (void*)vsSource, vsSize, vsInfo.ReflectionData, vsInfo.ReflectionSize); DistortionShader = *new ShaderSet; DistortionShader->SetShader(vs); delete[](vsSource); ShaderInfo psInfo = DistortionPixelShaderLookup[DistortionPixelShaderBitMask & DistortionCaps]; size_t psSize = strlen(shaderPrefix)+psInfo.ShaderSize; char* psSource = new char[psSize]; OVR_strcpy(psSource, psSize, shaderPrefix); OVR_strcat(psSource, psSize, psInfo.ShaderData); Ptr ps = *new GL::FragmentShader( &RParams, (void*)psSource, psSize, psInfo.ReflectionData, psInfo.ReflectionSize); DistortionShader->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 vs = *new GL::VertexShader( &RParams, (void*)vsSource, vsSize, SimpleQuad_vs_refl, sizeof(SimpleQuad_vs_refl) / sizeof(SimpleQuad_vs_refl[0])); SimpleQuadShader = *new ShaderSet; SimpleQuadShader->SetShader(vs); delete[](vsSource); size_t psSize = strlen(shaderPrefix)+sizeof(SimpleQuad_fs); char* psSource = new char[psSize]; OVR_strcpy(psSource, psSize, shaderPrefix); OVR_strcat(psSource, psSize, SimpleQuad_fs); Ptr ps = *new GL::FragmentShader( &RParams, (void*)psSource, psSize, SimpleQuad_fs_refl, sizeof(SimpleQuad_fs_refl) / sizeof(SimpleQuad_fs_refl[0])); SimpleQuadShader->SetShader(ps); delete[](psSource); } } void DistortionRenderer::destroy() { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); for(int eyeNum = 0; eyeNum < 2; eyeNum++) { if (glState->SupportsVao) glDeleteVertexArrays(1, &DistortionMeshVAOs[eyeNum]); DistortionMeshVAOs[eyeNum] = 0; DistortionMeshVBs[eyeNum].Clear(); DistortionMeshIBs[eyeNum].Clear(); } if (DistortionShader) { DistortionShader->UnsetShader(Shader_Vertex); DistortionShader->UnsetShader(Shader_Pixel); DistortionShader.Clear(); } LatencyTesterQuadVB.Clear(); LatencyVAO = 0; } }}} // OVR::CAPI::GL