ovr_sdk

diff LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/LibOVR/Src/CAPI/GL/CAPI_GL_HSWDisplay.cpp	Wed Jan 14 06:51:16 2015 +0200
     1.3 @@ -0,0 +1,500 @@
     1.4 +/************************************************************************************
     1.5 +
     1.6 +Filename    :   CAPI_GL_HSWDisplay.cpp
     1.7 +Content     :   Implements Health and Safety Warning system.
     1.8 +Created     :   July 7, 2014
     1.9 +Authors     :   Paul Pedriana
    1.10 +
    1.11 +Copyright   :   Copyright 2014 Oculus VR, LLC All Rights reserved.
    1.12 +
    1.13 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 
    1.14 +you may not use the Oculus VR Rift SDK except in compliance with the License, 
    1.15 +which is provided at the time of installation or download, or which 
    1.16 +otherwise accompanies this software in either electronic or hard copy form.
    1.17 +
    1.18 +You may obtain a copy of the License at
    1.19 +
    1.20 +http://www.oculusvr.com/licenses/LICENSE-3.2 
    1.21 +
    1.22 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 
    1.23 +distributed under the License is distributed on an "AS IS" BASIS,
    1.24 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.25 +See the License for the specific language governing permissions and
    1.26 +limitations under the License.
    1.27 +
    1.28 +************************************************************************************/
    1.29 +
    1.30 +
    1.31 +#include "CAPI_GL_HSWDisplay.h"
    1.32 +#include "CAPI_GL_DistortionShaders.h"
    1.33 +#include "../../OVR_CAPI_GL.h"
    1.34 +#include "../../Kernel/OVR_File.h"
    1.35 +#include "../../Kernel/OVR_Math.h"
    1.36 +#include "../../Kernel/OVR_Allocator.h"
    1.37 +#include "../../Kernel/OVR_Color.h"
    1.38 +
    1.39 +
    1.40 +OVR_DISABLE_MSVC_WARNING(4996) // "This function or variable may be unsafe..."
    1.41 +
    1.42 +
    1.43 +namespace OVR { namespace CAPI { 
    1.44 +
    1.45 +
    1.46 +// Loads the TGA data from the File as an array of width * height 32 bit Texture_RGBA values.
    1.47 +// Returned pointer must be freed with OVR_FREE.
    1.48 +uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height)
    1.49 +{
    1.50 +    // See http://www.fileformat.info/format/tga/egff.htm for format details.
    1.51 +    // TGA files are stored with little-endian data.
    1.52 +    uint8_t* pRGBA  = NULL;
    1.53 +
    1.54 +    f->SeekToBegin();
    1.55 +    
    1.56 +    const int desclen = f->ReadUByte();
    1.57 +    const int palette = f->ReadUByte();
    1.58 +    OVR_UNUSED(palette);
    1.59 +    const int imgtype = f->ReadUByte();
    1.60 +    f->ReadUInt16(); // Skip bytes
    1.61 +    int palCount = f->ReadUInt16();
    1.62 +    int palSize = f->ReadUByte();
    1.63 +    f->ReadUInt16();
    1.64 +    f->ReadUInt16();
    1.65 +    width = f->ReadUInt16();
    1.66 +    height = f->ReadUInt16();
    1.67 +    int bpp = f->ReadUByte();
    1.68 +    f->ReadUByte();
    1.69 +
    1.70 +    const int ImgTypeBGRAUncompressed = 2;
    1.71 +    const int ImgTypeBGRARLECompressed = 10;
    1.72 +
    1.73 +    OVR_ASSERT(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32)));
    1.74 +
    1.75 +    // imgType 2 is uncompressed true-color image.
    1.76 +    // imgType 10 is run-length encoded true-color image.
    1.77 +    if(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32)))
    1.78 +    {
    1.79 +        int imgsize = width * height * 4;
    1.80 +        pRGBA = (uint8_t*) OVR_ALLOC(imgsize);
    1.81 +        f->Skip(desclen);
    1.82 +        f->Skip(palCount * (palSize + 7) >> 3);
    1.83 +        int strideBytes = width * 4; // This is the number of bytes between successive rows.
    1.84 +
    1.85 +        unsigned char buf[4] = { 0, 0, 0, alpha }; // If bpp is 24 then this alpha will be unmodified below.
    1.86 +
    1.87 +        switch (imgtype)
    1.88 +        {
    1.89 +        case ImgTypeBGRAUncompressed:
    1.90 +            switch (bpp)
    1.91 +            {
    1.92 +            case 24:
    1.93 +            case 32:
    1.94 +                for (int y = 0; y < height; y++)
    1.95 +                {
    1.96 +                    for (int x = 0; x < width; x++)
    1.97 +                    {
    1.98 +                        f->Read(buf, bpp / 8); // Data is stored as B, G, R
    1.99 +                        pRGBA[y*strideBytes + x*4 + 0] = buf[2];
   1.100 +                        pRGBA[y*strideBytes + x*4 + 1] = buf[1];
   1.101 +                        pRGBA[y*strideBytes + x*4 + 2] = buf[0];
   1.102 +                        pRGBA[y*strideBytes + x*4 + 3] = buf[3];
   1.103 +                    }
   1.104 +                }
   1.105 +                break;
   1.106 +            }
   1.107 +            break;
   1.108 +
   1.109 +        case ImgTypeBGRARLECompressed:
   1.110 +            switch (bpp)
   1.111 +            {
   1.112 +            case 24:
   1.113 +            case 32:
   1.114 +                for (int y = 0; y < height; y++) // RLE spans don't cross successive rows.
   1.115 +                {
   1.116 +                    int x = 0;
   1.117 +
   1.118 +                    while(x < width)
   1.119 +                    {
   1.120 +                        uint8_t rleByte;
   1.121 +                        f->Read(&rleByte, 1);
   1.122 +
   1.123 +                        if(rleByte & 0x80) // If the high byte is set then what follows are RLE bytes.
   1.124 +                        {
   1.125 +                            size_t rleCount = ((rleByte & 0x7f) + 1);
   1.126 +                            f->Read(buf, bpp / 8); // Data is stored as B, G, R, A
   1.127 +
   1.128 +                            for (; rleCount; --rleCount, ++x)
   1.129 +                            {
   1.130 +                                pRGBA[y*strideBytes + x*4 + 0] = buf[2];
   1.131 +                                pRGBA[y*strideBytes + x*4 + 1] = buf[1];
   1.132 +                                pRGBA[y*strideBytes + x*4 + 2] = buf[0];
   1.133 +                                pRGBA[y*strideBytes + x*4 + 3] = buf[3];
   1.134 +                            }   
   1.135 +                        }
   1.136 +                        else // Else what follows are regular bytes of a count indicated by rleByte
   1.137 +                        {
   1.138 +                            for (size_t rleCount = (rleByte + 1); rleCount; --rleCount, ++x)
   1.139 +                            {
   1.140 +                                f->Read(buf, bpp / 8); // Data is stored as B, G, R, A
   1.141 +                                pRGBA[y*strideBytes + x*4 + 0] = buf[2];
   1.142 +                                pRGBA[y*strideBytes + x*4 + 1] = buf[1];
   1.143 +                                pRGBA[y*strideBytes + x*4 + 2] = buf[0];
   1.144 +                                pRGBA[y*strideBytes + x*4 + 3] = buf[3];
   1.145 +                            }
   1.146 +                        }
   1.147 +                    }
   1.148 +                }
   1.149 +                break;
   1.150 +            }
   1.151 +            break;
   1.152 +        }
   1.153 +    }
   1.154 +
   1.155 +    return pRGBA;
   1.156 +} // LoadTextureTgaData
   1.157 +
   1.158 +
   1.159 +
   1.160 +namespace GL {
   1.161 +
   1.162 +
   1.163 +// To do: This needs to be promoted to a central version, possibly in CAPI_HSWDisplay.h
   1.164 +struct HASWVertex
   1.165 +{
   1.166 +    Vector3f  Pos;
   1.167 +    Color     C;
   1.168 +    float     U, V;    
   1.169 +
   1.170 +    HASWVertex(const Vector3f& p, const Color& c = Color(64,0,0,255), float u = 0, float v = 0)
   1.171 +        : Pos(p), C(c), U(u), V(v)
   1.172 +    {}
   1.173 +
   1.174 +    HASWVertex(float x, float y, float z, const Color& c = Color(64,0,0,255), float u = 0, float v = 0) 
   1.175 +        : Pos(x,y,z), C(c), U(u), V(v)
   1.176 +    {}
   1.177 +
   1.178 +    bool operator==(const HASWVertex& b) const
   1.179 +    {
   1.180 +        return (Pos == b.Pos) && (C == b.C) && (U == b.U) && (V == b.V);
   1.181 +    }
   1.182 +};
   1.183 +
   1.184 +
   1.185 +
   1.186 +// This is a temporary function implementation, and it functionality needs to be implemented in a more generic way.
   1.187 +Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, OVR::File* f, uint8_t alpha)
   1.188 +{
   1.189 +    OVR::CAPI::GL::Texture* pTexture = NULL;
   1.190 +
   1.191 +    int width, height;
   1.192 +    const uint8_t* pRGBA = LoadTextureTgaData(f, alpha, width, height);
   1.193 +
   1.194 +    if (pRGBA)
   1.195 +    {
   1.196 +        pTexture = new OVR::CAPI::GL::Texture(&rParams, width, height);
   1.197 +
   1.198 +        // SetSampleMode forces the use of mipmaps through GL_LINEAR_MIPMAP_LINEAR.
   1.199 +        pTexture->SetSampleMode(samplerMode); // Calls glBindTexture internally.
   1.200 +
   1.201 +        // We are intentionally not using mipmaps. We need to use this because Texture::SetSampleMode unilaterally uses GL_LINEAR_MIPMAP_LINEAR.
   1.202 +        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
   1.203 +        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
   1.204 +        OVR_ASSERT(glGetError() == 0);
   1.205 +
   1.206 +        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA);
   1.207 +        OVR_ASSERT(glGetError() == 0);
   1.208 +
   1.209 +        // With OpenGL 4.2+ we can use this instead of glTexImage2D:
   1.210 +        // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
   1.211 +        // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA);
   1.212 +
   1.213 +        OVR_FREE(const_cast<uint8_t*>(pRGBA));
   1.214 +    }
   1.215 +
   1.216 +    return pTexture;
   1.217 +}
   1.218 +
   1.219 +
   1.220 +// Loads a texture from a memory image of a TGA file.
   1.221 +Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, const uint8_t* pData, int dataSize, uint8_t alpha)
   1.222 +{
   1.223 +    MemoryFile memoryFile("", pData, dataSize);
   1.224 +
   1.225 +    return LoadTextureTga(rParams, samplerMode, &memoryFile, alpha);
   1.226 +}
   1.227 +
   1.228 +
   1.229 +
   1.230 +
   1.231 +// The texture below may conceivably be shared between HSWDisplay instances. However,  
   1.232 +// beware that sharing may not be possible if two HMDs are using different locales  
   1.233 +// simultaneously. As of this writing it's not clear if that can occur in practice.
   1.234 +
   1.235 +HSWDisplay::HSWDisplay(ovrRenderAPIType api, ovrHmd hmd, const HMDRenderState& renderState)
   1.236 +  : OVR::CAPI::HSWDisplay(api, hmd, renderState)
   1.237 +  , RenderParams()
   1.238 +  , GLContext()
   1.239 +  , FrameBuffer(0)
   1.240 +  , pTexture()
   1.241 +  , pShaderSet()
   1.242 +  , pVertexShader()
   1.243 +  , pFragmentShader()
   1.244 +  , pVB()
   1.245 +  , VAO(0)
   1.246 +  , VAOInitialized(false)
   1.247 +  , OrthoProjection()
   1.248 +{
   1.249 +}
   1.250 +
   1.251 +
   1.252 +bool HSWDisplay::Initialize(const ovrRenderAPIConfig* apiConfig)
   1.253 +{
   1.254 +    const ovrGLConfig* config = (const ovrGLConfig*)apiConfig;
   1.255 +
   1.256 +    if(config)
   1.257 +    {
   1.258 +        // The following is essentially copied from CAPI_GL_DistortionRender.cpp's 
   1.259 +        // Initialize function. To do: Merge this to a central location.
   1.260 +        RenderParams.Multisample = config->OGL.Header.Multisample;
   1.261 +        RenderParams.BackBufferSize     = config->OGL.Header.BackBufferSize;
   1.262 +
   1.263 +        #if defined(OVR_OS_WIN32)
   1.264 +            RenderParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow();
   1.265 +            RenderParams.DC     = config->OGL.DC;
   1.266 +        #elif defined(OVR_OS_LINUX)
   1.267 +            if (config->OGL.Disp)
   1.268 +            {
   1.269 +                RenderParams.Disp = config->OGL.Disp;
   1.270 +            }
   1.271 +            if (!RenderParams.Disp)
   1.272 +            {
   1.273 +                RenderParams.Disp = glXGetCurrentDisplay();
   1.274 +            }
   1.275 +            if (!RenderParams.Disp)
   1.276 +            {
   1.277 +                OVR_DEBUG_LOG(("glXGetCurrentDisplay failed."));
   1.278 +                return false;
   1.279 +            }
   1.280 +        #endif
   1.281 +    }
   1.282 +    else
   1.283 +    {
   1.284 +        UnloadGraphics();
   1.285 +    }
   1.286 +
   1.287 +    return true;
   1.288 +}
   1.289 +
   1.290 +
   1.291 +void HSWDisplay::Shutdown()
   1.292 +{
   1.293 +    UnloadGraphics();
   1.294 +}
   1.295 +
   1.296 +
   1.297 +void HSWDisplay::DisplayInternal()
   1.298 +{
   1.299 +    HSWDISPLAY_LOG(("[HSWDisplay GL] DisplayInternal()"));
   1.300 +    // We may want to call LoadGraphics here instead of within Render.
   1.301 +}
   1.302 +
   1.303 +
   1.304 +void HSWDisplay::DismissInternal()
   1.305 +{
   1.306 +    HSWDISPLAY_LOG(("[HSWDisplay GL] DismissInternal()"));
   1.307 +    UnloadGraphicsRequested = true; // We don't directly call UnloadGraphics here because this may be executed within a different thread.
   1.308 +}
   1.309 +
   1.310 +
   1.311 +void HSWDisplay::UnloadGraphics()
   1.312 +{
   1.313 +    if(pTexture) // If initialized...
   1.314 +    {
   1.315 +        Context currentGLContext;
   1.316 +        currentGLContext.InitFromCurrent();
   1.317 +        GLContext.Bind();
   1.318 +
   1.319 +    // RenderParams: No need to clear.
   1.320 +    if(FrameBuffer != 0)
   1.321 +    {
   1.322 +        glDeleteFramebuffers(1, &FrameBuffer);
   1.323 +        FrameBuffer = 0;
   1.324 +    }
   1.325 +    pTexture.Clear();
   1.326 +    pShaderSet.Clear();
   1.327 +    pVertexShader.Clear();
   1.328 +    pFragmentShader.Clear();
   1.329 +    pVB.Clear();
   1.330 +    if(VAO)
   1.331 +    {
   1.332 +            glDeleteVertexArrays(1, &VAO);
   1.333 +        currentGLContext.Bind();
   1.334 +        GLContext.Destroy();
   1.335 +    }
   1.336 +}
   1.337 +}
   1.338 +
   1.339 +
   1.340 +void HSWDisplay::LoadGraphics()
   1.341 +{
   1.342 +    // We assume here that the current GL context is the one our resources will be associated with.
   1.343 +
   1.344 +    if (FrameBuffer == 0)
   1.345 +    {
   1.346 +        glGenFramebuffers(1, &FrameBuffer);
   1.347 +    }
   1.348 +
   1.349 +    if (!pTexture) // To do: Add support for .dds files, which would be significantly smaller than the size of the tga.
   1.350 +    {
   1.351 +        size_t textureSize;
   1.352 +        const uint8_t* TextureData = GetDefaultTexture(textureSize);
   1.353 +        pTexture = *LoadTextureTga(RenderParams, Sample_Linear | Sample_Clamp, TextureData, (int)textureSize, 255);
   1.354 +    }
   1.355 +
   1.356 +    if (!pShaderSet)
   1.357 +    {
   1.358 +        pShaderSet = *new ShaderSet();
   1.359 +    }
   1.360 +
   1.361 +    if(!pVertexShader)
   1.362 +    {
   1.363 +        OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix);
   1.364 +        strShader += SimpleTexturedQuad_vs;
   1.365 +
   1.366 +        pVertexShader = *new VertexShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_vs_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_vs_refl));
   1.367 +        pShaderSet->SetShader(pVertexShader);
   1.368 +    }
   1.369 +
   1.370 +    if(!pFragmentShader)
   1.371 +    {
   1.372 +        OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix);
   1.373 +        strShader += SimpleTexturedQuad_ps;
   1.374 +
   1.375 +        pFragmentShader = *new FragmentShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_ps_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_ps_refl));
   1.376 +        pShaderSet->SetShader(pFragmentShader);
   1.377 +    }
   1.378 +
   1.379 +    if(!pVB)
   1.380 +    {
   1.381 +        pVB = *new Buffer(&RenderParams);
   1.382 +
   1.383 +        pVB->Data(Buffer_Vertex, NULL, 4 * sizeof(HASWVertex));
   1.384 +        HASWVertex* pVertices = (HASWVertex*)pVB->Map(0, 4 * sizeof(HASWVertex), Map_Discard);
   1.385 +        OVR_ASSERT(pVertices);
   1.386 +
   1.387 +        if(pVertices)
   1.388 +        {
   1.389 +            const bool  flip   = ((RenderState.DistortionCaps & ovrDistortionCap_FlipInput) != 0);
   1.390 +            const float left   = -1.0f; // We currently draw this in normalized device coordinates with an stereo translation
   1.391 +            const float top    = -1.1f; // applied as a vertex shader uniform. In the future when we have a more formal graphics
   1.392 +            const float right  =  1.0f; // API abstraction we may move this draw to an overlay layer or to a more formal 
   1.393 +            const float bottom =  0.9f; // model/mesh scheme with a perspective projection.
   1.394 +
   1.395 +            pVertices[0] = HASWVertex(left,  top,    0.f, Color(255, 255, 255, 255), 0.f, flip ? 1.f : 0.f);
   1.396 +            pVertices[1] = HASWVertex(left,  bottom, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 0.f : 1.f);
   1.397 +            pVertices[2] = HASWVertex(right, top,    0.f, Color(255, 255, 255, 255), 1.f, flip ? 1.f : 0.f); 
   1.398 +            pVertices[3] = HASWVertex(right, bottom, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 0.f : 1.f);
   1.399 +
   1.400 +            pVB->Unmap(pVertices);
   1.401 +        }
   1.402 +    }
   1.403 +
   1.404 +    // We don't bind or initialize the vertex arrays here.
   1.405 +    if (!VAO && GLE_ARB_vertex_array_object)
   1.406 +    {
   1.407 +        OVR_ASSERT(!VAOInitialized);
   1.408 +            glGenVertexArrays(1, &VAO);
   1.409 +    }
   1.410 +}
   1.411 +
   1.412 +
   1.413 +void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture)
   1.414 +{
   1.415 +    if(RenderEnabled && eyeTexture)
   1.416 +    {        
   1.417 +        // We need to render to the eyeTexture with the texture viewport.
   1.418 +        // Setup rendering to the texture.
   1.419 +        ovrGLTexture* eyeTextureGL = const_cast<ovrGLTexture*>(reinterpret_cast<const ovrGLTexture*>(eyeTexture));
   1.420 +        OVR_ASSERT(eyeTextureGL->Texture.Header.API == ovrRenderAPI_OpenGL);
   1.421 +
   1.422 +        GL::AutoContext autoGLContext(GLContext); // Saves the current GL context, binds our GLContext, then at the end of scope re-binds the current GL context.
   1.423 +
   1.424 +        // Load the graphics if not loaded already.
   1.425 +        if (!pTexture)
   1.426 +            LoadGraphics();
   1.427 +
   1.428 +        // Calculate ortho projection.
   1.429 +        GetOrthoProjection(RenderState, OrthoProjection);
   1.430 +
   1.431 +        // Set the rendering to be to the eye texture.
   1.432 +        glBindFramebuffer(GL_FRAMEBUFFER, FrameBuffer);
   1.433 +        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, eyeTextureGL->OGL.TexId, 0);
   1.434 +        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); // We aren't using depth, as we currently want this to overwrite everything.
   1.435 +        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
   1.436 +        OVR_ASSERT(status == GL_FRAMEBUFFER_COMPLETE); OVR_UNUSED(status);
   1.437 +
   1.438 +        // Set up the viewport
   1.439 +        const GLint   x = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.x;
   1.440 +        const GLint   y = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.y; // Note that GL uses bottom-up coordinates.
   1.441 +        const GLsizei w = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.w;
   1.442 +        const GLsizei h = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.h;
   1.443 +        glViewport(x, y, w, h);
   1.444 +
   1.445 +        // Set fixed-function render states.
   1.446 +        //glDepthRange(0.0,  1.0); // This is the default
   1.447 +        glDepthMask(GL_FALSE);
   1.448 +        glDisable(GL_DEPTH_TEST);
   1.449 +        glFrontFace(GL_CW);
   1.450 +        glEnable(GL_BLEND);
   1.451 +        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   1.452 +
   1.453 +        // Enable the buffer and shaders we use.
   1.454 +        ShaderFill fill(pShaderSet);
   1.455 +        if (pTexture)
   1.456 +            fill.SetTexture(0, pTexture);
   1.457 +
   1.458 +        // Set shader uniforms.
   1.459 +        const float scale  = HSWDISPLAY_SCALE * ((RenderState.OurHMDInfo.HmdType == HmdType_DK1) ? 0.70f : 1.f);
   1.460 +        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.
   1.461 +        pShaderSet->SetUniform2f("PositionOffset", OrthoProjection[eye].GetTranslation().x, 0.0f);
   1.462 +
   1.463 +        // Set vertex attributes
   1.464 +        if (GLE_ARB_vertex_array_object)
   1.465 +        {
   1.466 +            OVR_ASSERT(VAO != 0);
   1.467 +            glBindVertexArray(VAO);
   1.468 +        }
   1.469 +
   1.470 +        if(!VAOInitialized) // This executes for the case that VAO isn't supported.
   1.471 +        {
   1.472 +            glBindBuffer(GL_ARRAY_BUFFER, pVB->GLBuffer); // This must be called before glVertexAttribPointer is called below.
   1.473 +
   1.474 +            const GLuint shaderProgram = pShaderSet->Prog;
   1.475 +            GLint attributeLocationArray[3];
   1.476 +
   1.477 +            attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position");
   1.478 +            glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT,         false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, Pos)));
   1.479 +
   1.480 +            attributeLocationArray[1] = glGetAttribLocation(shaderProgram, "Color");
   1.481 +            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.
   1.482 +
   1.483 +            attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord");
   1.484 +            glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT,         false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, U)));
   1.485 +
   1.486 +            for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++)
   1.487 +                glEnableVertexAttribArray((GLuint)i);
   1.488 +        }
   1.489 +
   1.490 +        fill.Set(Prim_TriangleStrip);
   1.491 +
   1.492 +        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   1.493 +
   1.494 +        if (GLE_ARB_vertex_array_object)
   1.495 +        {
   1.496 +            VAOInitialized = true;
   1.497 +            glBindVertexArray(0);
   1.498 +        }
   1.499 +    }
   1.500 +}
   1.501 +
   1.502 + 
   1.503 +}}} // namespace OVR::CAPI::GL