ovr_sdk

view 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 source
1 /************************************************************************************
3 Filename : CAPI_GL_HSWDisplay.cpp
4 Content : Implements Health and Safety Warning system.
5 Created : July 7, 2014
6 Authors : Paul Pedriana
8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
12 which is provided at the time of installation or download, or which
13 otherwise accompanies this software in either electronic or hard copy form.
15 You may obtain a copy of the License at
17 http://www.oculusvr.com/licenses/LICENSE-3.2
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
25 ************************************************************************************/
28 #include "CAPI_GL_HSWDisplay.h"
29 #include "CAPI_GL_DistortionShaders.h"
30 #include "../../OVR_CAPI_GL.h"
31 #include "../../Kernel/OVR_File.h"
32 #include "../../Kernel/OVR_Math.h"
33 #include "../../Kernel/OVR_Allocator.h"
34 #include "../../Kernel/OVR_Color.h"
37 OVR_DISABLE_MSVC_WARNING(4996) // "This function or variable may be unsafe..."
40 namespace OVR { namespace CAPI {
43 // Loads the TGA data from the File as an array of width * height 32 bit Texture_RGBA values.
44 // Returned pointer must be freed with OVR_FREE.
45 uint8_t* LoadTextureTgaData(OVR::File* f, uint8_t alpha, int& width, int& height)
46 {
47 // See http://www.fileformat.info/format/tga/egff.htm for format details.
48 // TGA files are stored with little-endian data.
49 uint8_t* pRGBA = NULL;
51 f->SeekToBegin();
53 const int desclen = f->ReadUByte();
54 const int palette = f->ReadUByte();
55 OVR_UNUSED(palette);
56 const int imgtype = f->ReadUByte();
57 f->ReadUInt16(); // Skip bytes
58 int palCount = f->ReadUInt16();
59 int palSize = f->ReadUByte();
60 f->ReadUInt16();
61 f->ReadUInt16();
62 width = f->ReadUInt16();
63 height = f->ReadUInt16();
64 int bpp = f->ReadUByte();
65 f->ReadUByte();
67 const int ImgTypeBGRAUncompressed = 2;
68 const int ImgTypeBGRARLECompressed = 10;
70 OVR_ASSERT(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32)));
72 // imgType 2 is uncompressed true-color image.
73 // imgType 10 is run-length encoded true-color image.
74 if(((imgtype == ImgTypeBGRAUncompressed) || (imgtype == ImgTypeBGRARLECompressed)) && ((bpp == 24) || (bpp == 32)))
75 {
76 int imgsize = width * height * 4;
77 pRGBA = (uint8_t*) OVR_ALLOC(imgsize);
78 f->Skip(desclen);
79 f->Skip(palCount * (palSize + 7) >> 3);
80 int strideBytes = width * 4; // This is the number of bytes between successive rows.
82 unsigned char buf[4] = { 0, 0, 0, alpha }; // If bpp is 24 then this alpha will be unmodified below.
84 switch (imgtype)
85 {
86 case ImgTypeBGRAUncompressed:
87 switch (bpp)
88 {
89 case 24:
90 case 32:
91 for (int y = 0; y < height; y++)
92 {
93 for (int x = 0; x < width; x++)
94 {
95 f->Read(buf, bpp / 8); // Data is stored as B, G, R
96 pRGBA[y*strideBytes + x*4 + 0] = buf[2];
97 pRGBA[y*strideBytes + x*4 + 1] = buf[1];
98 pRGBA[y*strideBytes + x*4 + 2] = buf[0];
99 pRGBA[y*strideBytes + x*4 + 3] = buf[3];
100 }
101 }
102 break;
103 }
104 break;
106 case ImgTypeBGRARLECompressed:
107 switch (bpp)
108 {
109 case 24:
110 case 32:
111 for (int y = 0; y < height; y++) // RLE spans don't cross successive rows.
112 {
113 int x = 0;
115 while(x < width)
116 {
117 uint8_t rleByte;
118 f->Read(&rleByte, 1);
120 if(rleByte & 0x80) // If the high byte is set then what follows are RLE bytes.
121 {
122 size_t rleCount = ((rleByte & 0x7f) + 1);
123 f->Read(buf, bpp / 8); // Data is stored as B, G, R, A
125 for (; rleCount; --rleCount, ++x)
126 {
127 pRGBA[y*strideBytes + x*4 + 0] = buf[2];
128 pRGBA[y*strideBytes + x*4 + 1] = buf[1];
129 pRGBA[y*strideBytes + x*4 + 2] = buf[0];
130 pRGBA[y*strideBytes + x*4 + 3] = buf[3];
131 }
132 }
133 else // Else what follows are regular bytes of a count indicated by rleByte
134 {
135 for (size_t rleCount = (rleByte + 1); rleCount; --rleCount, ++x)
136 {
137 f->Read(buf, bpp / 8); // Data is stored as B, G, R, A
138 pRGBA[y*strideBytes + x*4 + 0] = buf[2];
139 pRGBA[y*strideBytes + x*4 + 1] = buf[1];
140 pRGBA[y*strideBytes + x*4 + 2] = buf[0];
141 pRGBA[y*strideBytes + x*4 + 3] = buf[3];
142 }
143 }
144 }
145 }
146 break;
147 }
148 break;
149 }
150 }
152 return pRGBA;
153 } // LoadTextureTgaData
157 namespace GL {
160 // To do: This needs to be promoted to a central version, possibly in CAPI_HSWDisplay.h
161 struct HASWVertex
162 {
163 Vector3f Pos;
164 Color C;
165 float U, V;
167 HASWVertex(const Vector3f& p, const Color& c = Color(64,0,0,255), float u = 0, float v = 0)
168 : Pos(p), C(c), U(u), V(v)
169 {}
171 HASWVertex(float x, float y, float z, const Color& c = Color(64,0,0,255), float u = 0, float v = 0)
172 : Pos(x,y,z), C(c), U(u), V(v)
173 {}
175 bool operator==(const HASWVertex& b) const
176 {
177 return (Pos == b.Pos) && (C == b.C) && (U == b.U) && (V == b.V);
178 }
179 };
183 // This is a temporary function implementation, and it functionality needs to be implemented in a more generic way.
184 Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, OVR::File* f, uint8_t alpha)
185 {
186 OVR::CAPI::GL::Texture* pTexture = NULL;
188 int width, height;
189 const uint8_t* pRGBA = LoadTextureTgaData(f, alpha, width, height);
191 if (pRGBA)
192 {
193 pTexture = new OVR::CAPI::GL::Texture(&rParams, width, height);
195 // SetSampleMode forces the use of mipmaps through GL_LINEAR_MIPMAP_LINEAR.
196 pTexture->SetSampleMode(samplerMode); // Calls glBindTexture internally.
198 // We are intentionally not using mipmaps. We need to use this because Texture::SetSampleMode unilaterally uses GL_LINEAR_MIPMAP_LINEAR.
199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
201 OVR_ASSERT(glGetError() == 0);
203 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA);
204 OVR_ASSERT(glGetError() == 0);
206 // With OpenGL 4.2+ we can use this instead of glTexImage2D:
207 // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
208 // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pRGBA);
210 OVR_FREE(const_cast<uint8_t*>(pRGBA));
211 }
213 return pTexture;
214 }
217 // Loads a texture from a memory image of a TGA file.
218 Texture* LoadTextureTga(RenderParams& rParams, int samplerMode, const uint8_t* pData, int dataSize, uint8_t alpha)
219 {
220 MemoryFile memoryFile("", pData, dataSize);
222 return LoadTextureTga(rParams, samplerMode, &memoryFile, alpha);
223 }
228 // The texture below may conceivably be shared between HSWDisplay instances. However,
229 // beware that sharing may not be possible if two HMDs are using different locales
230 // simultaneously. As of this writing it's not clear if that can occur in practice.
232 HSWDisplay::HSWDisplay(ovrRenderAPIType api, ovrHmd hmd, const HMDRenderState& renderState)
233 : OVR::CAPI::HSWDisplay(api, hmd, renderState)
234 , RenderParams()
235 , GLContext()
236 , FrameBuffer(0)
237 , pTexture()
238 , pShaderSet()
239 , pVertexShader()
240 , pFragmentShader()
241 , pVB()
242 , VAO(0)
243 , VAOInitialized(false)
244 , OrthoProjection()
245 {
246 }
249 bool HSWDisplay::Initialize(const ovrRenderAPIConfig* apiConfig)
250 {
251 const ovrGLConfig* config = (const ovrGLConfig*)apiConfig;
253 if(config)
254 {
255 // The following is essentially copied from CAPI_GL_DistortionRender.cpp's
256 // Initialize function. To do: Merge this to a central location.
257 RenderParams.Multisample = config->OGL.Header.Multisample;
258 RenderParams.BackBufferSize = config->OGL.Header.BackBufferSize;
260 #if defined(OVR_OS_WIN32)
261 RenderParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow();
262 RenderParams.DC = config->OGL.DC;
263 #elif defined(OVR_OS_LINUX)
264 if (config->OGL.Disp)
265 {
266 RenderParams.Disp = config->OGL.Disp;
267 }
268 if (!RenderParams.Disp)
269 {
270 RenderParams.Disp = glXGetCurrentDisplay();
271 }
272 if (!RenderParams.Disp)
273 {
274 OVR_DEBUG_LOG(("glXGetCurrentDisplay failed."));
275 return false;
276 }
277 #endif
278 }
279 else
280 {
281 UnloadGraphics();
282 }
284 return true;
285 }
288 void HSWDisplay::Shutdown()
289 {
290 UnloadGraphics();
291 }
294 void HSWDisplay::DisplayInternal()
295 {
296 HSWDISPLAY_LOG(("[HSWDisplay GL] DisplayInternal()"));
297 // We may want to call LoadGraphics here instead of within Render.
298 }
301 void HSWDisplay::DismissInternal()
302 {
303 HSWDISPLAY_LOG(("[HSWDisplay GL] DismissInternal()"));
304 UnloadGraphicsRequested = true; // We don't directly call UnloadGraphics here because this may be executed within a different thread.
305 }
308 void HSWDisplay::UnloadGraphics()
309 {
310 if(pTexture) // If initialized...
311 {
312 Context currentGLContext;
313 currentGLContext.InitFromCurrent();
314 GLContext.Bind();
316 // RenderParams: No need to clear.
317 if(FrameBuffer != 0)
318 {
319 glDeleteFramebuffers(1, &FrameBuffer);
320 FrameBuffer = 0;
321 }
322 pTexture.Clear();
323 pShaderSet.Clear();
324 pVertexShader.Clear();
325 pFragmentShader.Clear();
326 pVB.Clear();
327 if(VAO)
328 {
329 glDeleteVertexArrays(1, &VAO);
330 currentGLContext.Bind();
331 GLContext.Destroy();
332 }
333 }
334 }
337 void HSWDisplay::LoadGraphics()
338 {
339 // We assume here that the current GL context is the one our resources will be associated with.
341 if (FrameBuffer == 0)
342 {
343 glGenFramebuffers(1, &FrameBuffer);
344 }
346 if (!pTexture) // To do: Add support for .dds files, which would be significantly smaller than the size of the tga.
347 {
348 size_t textureSize;
349 const uint8_t* TextureData = GetDefaultTexture(textureSize);
350 pTexture = *LoadTextureTga(RenderParams, Sample_Linear | Sample_Clamp, TextureData, (int)textureSize, 255);
351 }
353 if (!pShaderSet)
354 {
355 pShaderSet = *new ShaderSet();
356 }
358 if(!pVertexShader)
359 {
360 OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix);
361 strShader += SimpleTexturedQuad_vs;
363 pVertexShader = *new VertexShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_vs_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_vs_refl));
364 pShaderSet->SetShader(pVertexShader);
365 }
367 if(!pFragmentShader)
368 {
369 OVR::String strShader((GLEContext::GetCurrentContext()->WholeVersion >= 302) ? glsl3Prefix : glsl2Prefix);
370 strShader += SimpleTexturedQuad_ps;
372 pFragmentShader = *new FragmentShader(&RenderParams, const_cast<char*>(strShader.ToCStr()), strShader.GetLength(), SimpleTexturedQuad_ps_refl, OVR_ARRAY_COUNT(SimpleTexturedQuad_ps_refl));
373 pShaderSet->SetShader(pFragmentShader);
374 }
376 if(!pVB)
377 {
378 pVB = *new Buffer(&RenderParams);
380 pVB->Data(Buffer_Vertex, NULL, 4 * sizeof(HASWVertex));
381 HASWVertex* pVertices = (HASWVertex*)pVB->Map(0, 4 * sizeof(HASWVertex), Map_Discard);
382 OVR_ASSERT(pVertices);
384 if(pVertices)
385 {
386 const bool flip = ((RenderState.DistortionCaps & ovrDistortionCap_FlipInput) != 0);
387 const float left = -1.0f; // We currently draw this in normalized device coordinates with an stereo translation
388 const float top = -1.1f; // applied as a vertex shader uniform. In the future when we have a more formal graphics
389 const float right = 1.0f; // API abstraction we may move this draw to an overlay layer or to a more formal
390 const float bottom = 0.9f; // model/mesh scheme with a perspective projection.
392 pVertices[0] = HASWVertex(left, top, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 1.f : 0.f);
393 pVertices[1] = HASWVertex(left, bottom, 0.f, Color(255, 255, 255, 255), 0.f, flip ? 0.f : 1.f);
394 pVertices[2] = HASWVertex(right, top, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 1.f : 0.f);
395 pVertices[3] = HASWVertex(right, bottom, 0.f, Color(255, 255, 255, 255), 1.f, flip ? 0.f : 1.f);
397 pVB->Unmap(pVertices);
398 }
399 }
401 // We don't bind or initialize the vertex arrays here.
402 if (!VAO && GLE_ARB_vertex_array_object)
403 {
404 OVR_ASSERT(!VAOInitialized);
405 glGenVertexArrays(1, &VAO);
406 }
407 }
410 void HSWDisplay::RenderInternal(ovrEyeType eye, const ovrTexture* eyeTexture)
411 {
412 if(RenderEnabled && eyeTexture)
413 {
414 // We need to render to the eyeTexture with the texture viewport.
415 // Setup rendering to the texture.
416 ovrGLTexture* eyeTextureGL = const_cast<ovrGLTexture*>(reinterpret_cast<const ovrGLTexture*>(eyeTexture));
417 OVR_ASSERT(eyeTextureGL->Texture.Header.API == ovrRenderAPI_OpenGL);
419 GL::AutoContext autoGLContext(GLContext); // Saves the current GL context, binds our GLContext, then at the end of scope re-binds the current GL context.
421 // Load the graphics if not loaded already.
422 if (!pTexture)
423 LoadGraphics();
425 // Calculate ortho projection.
426 GetOrthoProjection(RenderState, OrthoProjection);
428 // Set the rendering to be to the eye texture.
429 glBindFramebuffer(GL_FRAMEBUFFER, FrameBuffer);
430 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, eyeTextureGL->OGL.TexId, 0);
431 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); // We aren't using depth, as we currently want this to overwrite everything.
432 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
433 OVR_ASSERT(status == GL_FRAMEBUFFER_COMPLETE); OVR_UNUSED(status);
435 // Set up the viewport
436 const GLint x = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.x;
437 const GLint y = (GLint)eyeTextureGL->Texture.Header.RenderViewport.Pos.y; // Note that GL uses bottom-up coordinates.
438 const GLsizei w = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.w;
439 const GLsizei h = (GLsizei)eyeTextureGL->Texture.Header.RenderViewport.Size.h;
440 glViewport(x, y, w, h);
442 // Set fixed-function render states.
443 //glDepthRange(0.0, 1.0); // This is the default
444 glDepthMask(GL_FALSE);
445 glDisable(GL_DEPTH_TEST);
446 glFrontFace(GL_CW);
447 glEnable(GL_BLEND);
448 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
450 // Enable the buffer and shaders we use.
451 ShaderFill fill(pShaderSet);
452 if (pTexture)
453 fill.SetTexture(0, pTexture);
455 // Set shader uniforms.
456 const float scale = HSWDISPLAY_SCALE * ((RenderState.OurHMDInfo.HmdType == HmdType_DK1) ? 0.70f : 1.f);
457 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.
458 pShaderSet->SetUniform2f("PositionOffset", OrthoProjection[eye].GetTranslation().x, 0.0f);
460 // Set vertex attributes
461 if (GLE_ARB_vertex_array_object)
462 {
463 OVR_ASSERT(VAO != 0);
464 glBindVertexArray(VAO);
465 }
467 if(!VAOInitialized) // This executes for the case that VAO isn't supported.
468 {
469 glBindBuffer(GL_ARRAY_BUFFER, pVB->GLBuffer); // This must be called before glVertexAttribPointer is called below.
471 const GLuint shaderProgram = pShaderSet->Prog;
472 GLint attributeLocationArray[3];
474 attributeLocationArray[0] = glGetAttribLocation(shaderProgram, "Position");
475 glVertexAttribPointer(attributeLocationArray[0], sizeof(Vector3f)/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, Pos)));
477 attributeLocationArray[1] = glGetAttribLocation(shaderProgram, "Color");
478 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.
480 attributeLocationArray[2] = glGetAttribLocation(shaderProgram, "TexCoord");
481 glVertexAttribPointer(attributeLocationArray[2], sizeof(float[2])/sizeof(float), GL_FLOAT, false, sizeof(HASWVertex), reinterpret_cast<char*>(offsetof(HASWVertex, U)));
483 for (size_t i = 0; i < OVR_ARRAY_COUNT(attributeLocationArray); i++)
484 glEnableVertexAttribArray((GLuint)i);
485 }
487 fill.Set(Prim_TriangleStrip);
489 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
491 if (GLE_ARB_vertex_array_object)
492 {
493 VAOInitialized = true;
494 glBindVertexArray(0);
495 }
496 }
497 }
500 }}} // namespace OVR::CAPI::GL