diff options
Diffstat (limited to 'LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp')
-rw-r--r-- | LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp | 507 |
1 files changed, 453 insertions, 54 deletions
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp index 36830ca..6c0106d 100644 --- a/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp @@ -33,7 +33,6 @@ limitations under the License. #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..." @@ -46,8 +45,6 @@ namespace OVR { namespace CAPI { 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; @@ -67,9 +64,14 @@ uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height int bpp = f->ReadUByte(); f->ReadUByte(); - OVR_ASSERT((imgtype == 2) && ((bpp == 24) || (bpp == 32))); + const int ImgTypeBGRAUncompressed = 2; + const int ImgTypeBGRARLECompressed = 10; - if((imgtype == 2) && ((bpp == 24) || (bpp == 32))) + OVR_ASSERT(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32))); + + // imgType 2 is uncompressed true-color image. + // imgType 10 is run-length encoded true-color image. + if(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32))) { int imgsize = width * height * 4; pRGBA = (uint8_t*) OVR_ALLOC(imgsize); @@ -77,37 +79,68 @@ uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height f->Skip(palCount * (palSize + 7) >> 3); int strideBytes = width * 4; // This is the number of bytes between successive rows. - - unsigned char buf[16]; + unsigned char buf[4] = { 0, 0, 0, alpha }; // If bpp is 24 then this alpha will be unmodified below. switch (imgtype) { - case 2: // uncompressed true-color image -- the only image type we support. + case ImgTypeBGRAUncompressed: switch (bpp) { case 24: + case 32: 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 + f->Read(buf, bpp / 8); // 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; + pRGBA[y*strideBytes + x*4 + 3] = buf[3]; } } break; + } + break; + + case ImgTypeBGRARLECompressed: + switch (bpp) + { + case 24: case 32: - for (int y = 0; y < height; y++) + for (int y = 0; y < height; y++) // RLE spans don't cross successive rows. { - for (int x = 0; x < width; x++) + int x = 0; + + while(x < width) { - 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]; + uint8_t rleByte; + f->Read(&rleByte, 1); + + if(rleByte & 0x80) // If the high byte is set then what follows are RLE bytes. + { + size_t rleCount = ((rleByte & 0x7f) + 1); + f->Read(buf, bpp / 8); // Data is stored as B, G, R, A + + for (; rleCount; --rleCount, ++x) + { + 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]; + } + } + else // Else what follows are regular bytes of a count indicated by rleByte + { + for (size_t rleCount = (rleByte + 1); rleCount; --rleCount, ++x) + { + f->Read(buf, bpp / 8); // 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; @@ -165,6 +198,7 @@ Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, OVR::File* f, ui // 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); + OVR_ASSERT(glGetError() == 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA); OVR_ASSERT(glGetError() == 0); @@ -196,12 +230,24 @@ Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, const uint8_t* p // 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) + : OVR::CAPI::HSWDisplay(api, hmd, renderState) + , RenderParams() + , GLMajorVersion(0) + , GLMinorVersion(0) + , SupportsVao(false) + , FrameBuffer(0) + , pTexture() + , pShaderSet() + , pVertexShader() + , pFragmentShader() + , pVB() + , VAO(0) + , VAOInitialized(false) + , OrthoProjection() { } - + + bool HSWDisplay::Initialize(const ovrRenderAPIConfig* apiConfig) { const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; @@ -283,40 +329,68 @@ void HSWDisplay::UnloadGraphics() pVertexShader.Clear(); pFragmentShader.Clear(); pVB.Clear(); + if(VAO) + { + #ifdef OVR_OS_MAC + if(GLMajorVersion >= 3) + { + glDeleteVertexArrays(1, &VAO); + } + else + { + glDeleteVertexArraysAPPLE(1, &VAO); + } + #else + glDeleteVertexArrays(1, &VAO); + #endif + VAO = 0; + VAOInitialized = false; + } // 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" + int fieldCount = sscanf(glVersionString, isdigit(*glVersionString) ? "%d.%d" : "%*[^0-9]%d.%d", &GLMajorVersion, &GLMinorVersion); // 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); + static_assert(sizeof(GLMajorVersion) == sizeof(GLint), "type mis-match"); + glGetIntegerv(GL_MAJOR_VERSION, &GLMajorVersion); } } + // SupportsVao + if(GLMajorVersion >= 3) + SupportsVao = true; + else + { + const char* extensions = (const char*)glGetString(GL_EXTENSIONS); + SupportsVao = (strstr(extensions, "GL_ARB_vertex_array_object") || strstr(extensions, "GL_APPLE_vertex_array_object")); + } + 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); + { + size_t textureSize; + const uint8_t* TextureData = GetDefaultTexture(textureSize); + pTexture = *LoadTextureTga(RenderParams, Sample_Linear | Sample_Clamp, TextureData, (int)textureSize, 255); + } if(!pShaderSet) pShaderSet = *new ShaderSet(); if(!pVertexShader) { - OVR::String strShader((glVersionMajor >= 3) ? glsl3Prefix : glsl2Prefix); + OVR::String strShader((GLMajorVersion >= 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)); @@ -325,7 +399,7 @@ void HSWDisplay::LoadGraphics() if(!pFragmentShader) { - OVR::String strShader((glVersionMajor >= 3) ? glsl3Prefix : glsl2Prefix); + OVR::String strShader((GLMajorVersion >= 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)); @@ -348,7 +422,7 @@ void HSWDisplay::LoadGraphics() 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[0] = HASWVertex(left, top, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 1.f : 0.f); 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); @@ -357,8 +431,23 @@ void HSWDisplay::LoadGraphics() } } - // Calculate ortho projection. - GetOrthoProjection(RenderState, OrthoProjection); + // We don't generate the vertex arrays here + if(!VAO && SupportsVao) + { + OVR_ASSERT(!VAOInitialized); + #ifdef OVR_OS_MAC + if(GLMajorVersion >= 3) + { + glGenVertexArrays(1, &VAO); + } + else + { + glGenVertexArraysAPPLE(1, &VAO); + } + #else + glGenVertexArrays(1, &VAO); + #endif + } } @@ -366,15 +455,158 @@ void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) { if(RenderEnabled && eyeTexture) { + // Hack - Clear previous errors. + glGetError(); + // 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); + const GLuint kVertexAttribCount = 3; + const GLuint kSavedVertexAttribCount = 8; + + // Save state + // To do: Converge this with the state setting/restoring functionality present in the distortion renderer. + // Note that the glGet functions below will block until command buffer has completed. + // glPushAttrib is deprecated, so we use glGet* to save/restore fixed-function settings. + // https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGet.xml + // + GLint RenderModeSaved; + glGetIntegerv(GL_RENDER_MODE, &RenderModeSaved); + OVR_ASSERT(glGetError() == 0); + OVR_ASSERT(RenderModeSaved == GL_RENDER); // Make sure it's not GL_SELECT or GL_FEEDBACK. + + GLint FrameBufferBindingSaved; // OpenGL renamed GL_FRAMEBUFFER_BINDING to GL_DRAW_FRAMEBUFFER_BINDING and adds GL_READ_FRAMEBUFFER_BINDING. + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &FrameBufferBindingSaved); + OVR_ASSERT(glGetError() == 0); + + GLint TextureBinding2DSaved; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &TextureBinding2DSaved); + OVR_ASSERT(glGetError() == 0); + + GLint ViewportSaved[4]; + glGetIntegerv(GL_VIEWPORT, ViewportSaved); + OVR_ASSERT(glGetError() == 0); + + GLint PolygonModeSaved[2]; // Will be two (e.g. GL_FILL) values, one for the front mode and one for the mode. + glGetIntegerv(GL_POLYGON_MODE, PolygonModeSaved); + OVR_ASSERT(glGetError() == 0); + + GLdouble DepthRangeSaved[2]; + #if defined(OVR_OS_MAC) + // Using glDepthRange as a conditional will always evaluate to true on Mac. + glGetDoublev(GL_DEPTH_RANGE, DepthRangeSaved); + #else + GLfloat DepthRangefSaved[2]; + if(glDepthRange) // If we can use the double version (glDepthRangef may not be available)... + glGetDoublev(GL_DEPTH_RANGE, DepthRangeSaved); + else + glGetFloatv(GL_DEPTH_RANGE, DepthRangefSaved); + #endif + OVR_ASSERT(glGetError() == 0); + + GLint DepthWriteMaskSaved; + glGetIntegerv(GL_DEPTH_WRITEMASK, &DepthWriteMaskSaved); + OVR_ASSERT(glGetError() == 0); + + GLint DepthTestSaved; + glGetIntegerv(GL_DEPTH_TEST, &DepthTestSaved); + OVR_ASSERT(glGetError() == 0); + + // No need to save/restore depth offset because we are neither testing nor writing depth. + + GLint CullFaceSaved; + glGetIntegerv(GL_CULL_FACE, &CullFaceSaved); + OVR_ASSERT(glGetError() == 0); + + GLint FrontFaceSaved; + glGetIntegerv(GL_FRONT_FACE, &FrontFaceSaved); + OVR_ASSERT(glGetError() == 0); + + GLint BlendSaved; + glGetIntegerv(GL_BLEND, &BlendSaved); + OVR_ASSERT(glGetError() == 0); + + GLint BlendSrcRGBSaved, BlendSrcAlphaSaved, BlendDstRGBSaved, BlendDstAlphaSaved; + glGetIntegerv(GL_BLEND_SRC_RGB, &BlendSrcRGBSaved); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &BlendSrcAlphaSaved); + glGetIntegerv(GL_BLEND_DST_RGB, &BlendDstRGBSaved); + glGetIntegerv(GL_BLEND_DST_ALPHA, &BlendDstAlphaSaved); + + GLint DitherSaved; + glGetIntegerv(GL_DITHER, &DitherSaved); + OVR_ASSERT(glGetError() == 0); + + GLint RasterizerDiscardSaved; + glGetIntegerv(GL_RASTERIZER_DISCARD, &RasterizerDiscardSaved); + OVR_ASSERT(glGetError() == 0); + + GLint ScissorTestSaved; + glGetIntegerv(GL_SCISSOR_TEST, &ScissorTestSaved); + OVR_ASSERT(glGetError() == 0); + + GLint SampleMaskSaved = 0; + if(((GLMajorVersion * 100) + GLMinorVersion) >= 302) // OpenGL 3.2 or later + { + glGetIntegerv(GL_SAMPLE_MASK, &SampleMaskSaved); + OVR_ASSERT(glGetError() == 0); + } + + GLint ColorWriteMaskSaved[4]; + glGetIntegerv(GL_COLOR_WRITEMASK, ColorWriteMaskSaved); + + GLint ArrayBufferBindingSaved; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &ArrayBufferBindingSaved); + OVR_ASSERT(glGetError() == 0); + + GLint ProgramSaved; + glGetIntegerv(GL_CURRENT_PROGRAM, &ProgramSaved); + OVR_ASSERT(glGetError() == 0); + + GLint ActiveTextureSaved; + glGetIntegerv(GL_ACTIVE_TEXTURE, &ActiveTextureSaved); + OVR_ASSERT(glGetError() == 0); + + GLint TextureBindingSaved; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &TextureBindingSaved); + OVR_ASSERT(glGetError() == 0); + + // https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glVertexAttribPointer.xml + GLint VertexAttribEnabledSaved[kSavedVertexAttribCount]; + GLint VertexAttribSizeSaved[kSavedVertexAttribCount]; + GLint VertexAttribTypeSaved[kSavedVertexAttribCount]; + GLint VertexAttribNormalizedSaved[kSavedVertexAttribCount]; + GLint VertexAttribStrideSaved[kSavedVertexAttribCount]; + GLvoid* VertexAttribPointerSaved[kSavedVertexAttribCount]; + for(GLuint i = 0; i < kSavedVertexAttribCount; i++) + { + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &VertexAttribEnabledSaved[i]); + + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &VertexAttribSizeSaved[i]); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &VertexAttribTypeSaved[i]); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &VertexAttribNormalizedSaved[i]); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &VertexAttribStrideSaved[i]); + glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &VertexAttribPointerSaved[i]); + + OVR_ASSERT(glGetError() == 0); + } + + GLint VertexArrayBindingSaved = 0; + if (SupportsVao) + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &VertexArrayBindingSaved); + + // + // End of save state + + // Load the graphics if not loaded already. if (!pTexture) LoadGraphics(); + // Calculate ortho projection. + GetOrthoProjection(RenderState, OrthoProjection); + // 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); @@ -383,6 +615,7 @@ void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) // glDrawBuffers(OVR_ARRAY_COUNT(DrawBuffers), DrawBuffers); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); OVR_ASSERT(status == GL_FRAMEBUFFER_COMPLETE); OVR_UNUSED(status); + OVR_ASSERT(glGetError() == 0); // Set up the viewport const GLint x = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.x; @@ -390,19 +623,34 @@ void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) 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); + OVR_ASSERT(glGetError() == 0); // Set fixed-function render states - glDisable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); // Irrelevant to our case here. + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + OVR_ASSERT(glGetError() == 0); + #if defined(OVR_OS_MAC) // On Mac we are directly using OpenGL functions instead of function pointers. + glDepthRange(0.0, 1.0); + #else + if(glDepthRange) // If we can use the double version (glDepthRangef may not be available)... + glDepthRange(0.0, 1.0); + else + glDepthRangef(0.f, 1.f); + #endif + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); // Disabling depth test should also have the effect of glDepthMask(GL_FALSE). + glDisable(GL_CULL_FACE); glFrontFace(GL_CW); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DITHER); + glDisable(GL_RASTERIZER_DISCARD); + glDisable(GL_SCISSOR_TEST); + if(((GLMajorVersion * 100) + GLMinorVersion) >= 302) // OpenGL 3.2 or later + glDisable(GL_SAMPLE_MASK); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); 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) { @@ -417,26 +665,52 @@ void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) 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. + // To do: We must add support for vertext array objects (VAOs) here. When using an OpenGL 3.2+ core profile, + // the application is required to use vertex array objects and glVertexAttribPointer will fail otherwise. - const GLuint shaderProgram = pShaderSet->Prog; - int attributeLocationArray[3]; + if(SupportsVao) + { + OVR_ASSERT(VAO != 0); + #ifdef OVR_OS_MAC + if(GLMajorVersion >= 3) + { + glBindVertexArray(VAO); + } + else + { + glBindVertexArrayAPPLE(VAO); + } + #else + glBindVertexArray(VAO); + #endif + } - attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position"); - glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, Pos))); + if(!VAOInitialized) // This executes for the case that VAO isn't supported. + { + glBindBuffer(GL_ARRAY_BUFFER, pVB->GLBuffer); // This must be called before glVertexAttribPointer is called below. + OVR_ASSERT(glGetError() == 0); - 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))); + const GLuint shaderProgram = pShaderSet->Prog; + GLint attributeLocationArray[kVertexAttribCount]; - attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord"); - glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, U))); + attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position"); + glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, Pos))); - for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++) - glEnableVertexAttribArray((GLuint)i); - OVR_ASSERT(glGetError() == 0); + attributeLocationArray[1] = glGetAttribLocation(shaderProgram, "Color"); + glVertexAttribPointer(attributeLocationArray[1], sizeof(Color)/sizeof(uint8_t), GL_UNSIGNED_BYTE, true, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, C))); // True because we want it to convert [0,255] to [0,1] for us. + + attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord"); + glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, U))); + OVR_ASSERT(glGetError() == 0); + + for (size_t i = 0; i < kVertexAttribCount; i++) + glEnableVertexAttribArray((GLuint)i); + OVR_ASSERT(glGetError() == 0); + + for (size_t i = kVertexAttribCount; i < kSavedVertexAttribCount; i++) + glDisableVertexAttribArray((GLuint)i); + OVR_ASSERT(glGetError() == 0); + } fill.Set(Prim_TriangleStrip); OVR_ASSERT(glGetError() == 0); @@ -444,10 +718,135 @@ void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture) glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); OVR_ASSERT(glGetError() == 0); - for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++) - glDisableVertexAttribArray(i); + if(SupportsVao) + { + VAOInitialized = true; + + #ifdef OVR_OS_MAC + if(GLMajorVersion >= 3) + { + glBindVertexArray(0); + } + else + { + glBindVertexArrayAPPLE(0); + } + #else + glBindVertexArray(0); + #endif + } + + // Restore state + // We restore the state in the reverse order that we saved it. + // To do: Make the code below avoid changes that are effectively no-ops. + // + if (SupportsVao) + { +#ifdef OVR_OS_MAC + if(GLMajorVersion >= 3) + { + glBindVertexArray(VertexArrayBindingSaved); + } + else + { + glBindVertexArrayAPPLE(VertexArrayBindingSaved); + } +#else + glBindVertexArray(VertexArrayBindingSaved); +#endif + } + + for (GLuint i = 0; i < kSavedVertexAttribCount; i++) + { + // We have a problem here: if the GL context was initialized with a core profile version 3.x or later, calls to glVertexAttribPointer can fail when there is no Vertex Array Object + // in place. That case is possible here, and we don't have an easy means to detect that a core profile was specified and thus that the glVertexAttribPointer call below could fail. + // Our current solution is to call glVertexAttribPointer only if vertex array objects are not supported. We cannot simply decide based on whether the given vertex attrib was enabled + // or if there was a vertex array object installed. With our solution below a problem can occur when using OpenGL 3.x+ which supports VAOs, the user has vertex attrib pointers installed, + // the currently installed VAO is 0, and the user is somehow dependent on us returning to the user with those vertex attrib pointers reinstalled. + + if (!SupportsVao || (VertexArrayBindingSaved != 0)) // If the OpenGL version is older or in core profile compatibility mode, or if there's a VAO currently installed... + { + glVertexAttribPointer(i, VertexAttribSizeSaved[i], VertexAttribTypeSaved[i], (GLboolean)VertexAttribNormalizedSaved[i], VertexAttribStrideSaved[i], VertexAttribPointerSaved[i]); + + if(VertexAttribEnabledSaved[i]) + glEnableVertexAttribArray(i); + else + glDisableVertexAttribArray(i); + + OVR_ASSERT(glGetError() == 0); + } + } + + glBindTexture(GL_TEXTURE_2D, TextureBindingSaved); + glActiveTexture(ActiveTextureSaved); + glUseProgram(ProgramSaved); + glBindBuffer(GL_ARRAY_BUFFER, ArrayBufferBindingSaved); + glColorMask((GLboolean)ColorWriteMaskSaved[0], (GLboolean)ColorWriteMaskSaved[1], (GLboolean)ColorWriteMaskSaved[2], (GLboolean)ColorWriteMaskSaved[3]); + + if(((GLMajorVersion * 100) + GLMinorVersion) >= 302) // OpenGL 3.2 or later + { + if(SampleMaskSaved) + glEnable(GL_SAMPLE_MASK); + else + glDisable(GL_SAMPLE_MASK); + } + + if(ScissorTestSaved) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + + if(RasterizerDiscardSaved) + glEnable(GL_RASTERIZER_DISCARD); + else + glDisable(GL_RASTERIZER_DISCARD); + + if(DitherSaved) + glEnable(GL_DITHER); + else + glDisable(GL_DITHER); + + glBlendFunc(BlendSrcRGBSaved, BlendDstRGBSaved); // What about BlendSrcAlphaSaved / BlendDstAlphaSaved? + + if(BlendSaved) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + glFrontFace(FrontFaceSaved); + + if(CullFaceSaved) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + if(DepthTestSaved) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + glDepthMask(DepthWriteMaskSaved ? GL_TRUE : GL_FALSE); + #if defined(OVR_OS_MAC) // On Mac we are directly using OpenGL functions instead of function pointers. + glDepthRange(DepthRangeSaved[0], DepthRangeSaved[1]); + #else + if(glDepthRange) // If we can use the double version (glDepthRangef may not be available)... + glDepthRange(DepthRangeSaved[0], DepthRangeSaved[1]); + else + glDepthRangef(DepthRangefSaved[0], DepthRangefSaved[1]); + #endif + // For OpenGL 3.x+ core profile mode, glPolygonMode allows only GL_FRONT_AND_BACK and not separate GL_FRONT and GL_BACK. + glPolygonMode(GL_FRONT_AND_BACK, PolygonModeSaved[0]); + glViewport(ViewportSaved[0], ViewportSaved[1], ViewportSaved[2], ViewportSaved[3]); + glBindTexture(GL_TEXTURE_2D, TextureBinding2DSaved); + glBindFramebuffer(GL_FRAMEBUFFER, FrameBufferBindingSaved); + //glRenderMode(RenderModeSaved); + + OVR_ASSERT(glGetError() == 0); + // + // End of restore state } } + }}} // namespace OVR::CAPI::GL |