Skip to content

Commit

Permalink
libgui|GLFramebuffer: Stereo left/right swap mode
Browse files Browse the repository at this point in the history
Even though rendering always happens in Canvas’s GL frame buffer,
VR mode 13 should now theoretically work as GLFramebuffer can be
told to use a different swap mode for the left/right frames.

In practice, the “left” swap mode draws the contents of the Canvas
frame buffer into the window’s BACK_LEFT buffer; same goes for the
“right” swap mode. The “stereo” swap will then simply do a regular
back-to-front swap with the finished contents.
  • Loading branch information
skyjake committed Dec 16, 2013
1 parent 771e85b commit ff52397
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 51 deletions.
5 changes: 4 additions & 1 deletion doomsday/libgui/include/de/gui/canvas.h
Expand Up @@ -28,6 +28,7 @@
#include "../KeyEventSource"
#include "../MouseEventSource"
#include "../GLTarget"
#include "../GLFramebuffer"

namespace de {

Expand Down Expand Up @@ -160,10 +161,12 @@ class LIBGUI_PUBLIC Canvas : public QGLWidget, public KeyEventSource, public Mou
*/
GLTarget &renderTarget() const;

GLFramebuffer &framebuffer();

/**
* Copies or swaps the back buffer to the front, making it visible.
*/
void swapBuffers();
void swapBuffers(gl::SwapBufferMode swapMode = gl::SwapMonoBuffer);

protected:
void initializeGL();
Expand Down
4 changes: 3 additions & 1 deletion doomsday/libgui/include/de/gui/canvaswindow.h
Expand Up @@ -27,6 +27,8 @@
#include <de/Rectangle>
#include <de/NativePath>

#include "../GLFramebuffer"

#ifdef WIN32
# undef min
# undef max
Expand Down Expand Up @@ -147,7 +149,7 @@ class LIBGUI_PUBLIC CanvasWindow : public QMainWindow,
*/
bool grabToFile(NativePath const &path) const;

void swapBuffers() const;
void swapBuffers(gl::SwapBufferMode swapMode = gl::SwapMonoBuffer) const;

/**
* Activates the window's GL context so that OpenGL API calls can be made.
Expand Down
20 changes: 19 additions & 1 deletion doomsday/libgui/include/de/gui/glframebuffer.h
Expand Up @@ -28,6 +28,15 @@

namespace de {

namespace gl {
enum SwapBufferMode {
SwapMonoBuffer,
SwapStereoLeftBuffer,
SwapStereoRightBuffer,
SwapStereoBuffers
};
}

class Canvas;

/**
Expand Down Expand Up @@ -70,7 +79,16 @@ class LIBGUI_PUBLIC GLFramebuffer : public Asset
GLTexture &colorTexture() const;
GLTexture &depthStencilTexture() const;

void swapBuffers(Canvas &canvas);
/**
* Swaps buffers.
*
* @param canvas Canvas where to swap.
* @param swapMode Stereo swapping mode:
* - gl::SwapMonoBuffer: swap is done normally into the Canvas's framebuffer
* - gl::SwapStereoLeftBuffer: swap updates the back/left stereo buffer
* - gl::SwapStereoRightBuffer: swap updates the back/right stereo buffer
*/
void swapBuffers(Canvas &canvas, gl::SwapBufferMode swapMode = gl::SwapMonoBuffer);

public:
/**
Expand Down
19 changes: 15 additions & 4 deletions doomsday/libgui/src/canvas.cpp
Expand Up @@ -200,10 +200,16 @@ DENG2_PIMPL(Canvas)
framebuf.glDeinit();
}

void swapBuffers()
void swapBuffers(gl::SwapBufferMode mode)
{
if(mode == gl::SwapStereoBuffers && !self.format().stereo())
{
// The canvas is not using a stereo format, must do a normal swap.
mode = gl::SwapMonoBuffer;
}

/// @todo Double buffering is not really needed in manual FB mode.
framebuf.swapBuffers(self);
framebuf.swapBuffers(self, mode);
}
};

Expand Down Expand Up @@ -306,9 +312,14 @@ GLTarget &Canvas::renderTarget() const
return d->framebuf.target();
}

void Canvas::swapBuffers()
GLFramebuffer &Canvas::framebuffer()
{
return d->framebuf;
}

void Canvas::swapBuffers(gl::SwapBufferMode swapMode)
{
d->swapBuffers();
d->swapBuffers(swapMode);
}

void Canvas::initializeGL()
Expand Down
4 changes: 2 additions & 2 deletions doomsday/libgui/src/canvaswindow.cpp
Expand Up @@ -246,10 +246,10 @@ bool CanvasWindow::grabToFile(NativePath const &path) const
return d->canvas->grabImage().save(path.toString());
}

void CanvasWindow::swapBuffers() const
void CanvasWindow::swapBuffers(gl::SwapBufferMode swapMode) const
{
// Force a swapbuffers right now.
d->canvas->swapBuffers();
d->canvas->swapBuffers(swapMode);
}

void CanvasWindow::glActivate()
Expand Down
110 changes: 68 additions & 42 deletions doomsday/libgui/src/glframebuffer.cpp
Expand Up @@ -96,34 +96,31 @@ DENG2_PIMPL(GLFramebuffer)

void alloc()
{
if(!GLInfo::extensions().EXT_framebuffer_blit)
{
// Prepare the fallback blit method.
VBuf *buf = new VBuf;
bufSwap.addBuffer(buf);
bufSwap.program().build(// Vertex shader:
Block("uniform highp mat4 uMvpMatrix; "
"attribute highp vec4 aVertex; "
"attribute highp vec2 aUV; "
"varying highp vec2 vUV; "
"void main(void) {"
"gl_Position = uMvpMatrix * aVertex; "
"vUV = aUV; }"),
// Fragment shader:
Block("uniform sampler2D uTex; "
"varying highp vec2 vUV; "
"void main(void) { "
"gl_FragColor = texture2D(uTex, vUV); }"))
<< uMvpMatrix
<< uBufTex;

buf->setVertices(gl::TriangleStrip,
VBuf::Builder().makeQuad(Rectanglef(0, 0, 1, 1), Rectanglef(0, 1, 1, -1)),
gl::Static);

uMvpMatrix = Matrix4f::ortho(0, 1, 0, 1);
uBufTex = color;
}
// Prepare the fallback blit method.
VBuf *buf = new VBuf;
bufSwap.addBuffer(buf);
bufSwap.program().build(// Vertex shader:
Block("uniform highp mat4 uMvpMatrix; "
"attribute highp vec4 aVertex; "
"attribute highp vec2 aUV; "
"varying highp vec2 vUV; "
"void main(void) {"
"gl_Position = uMvpMatrix * aVertex; "
"vUV = aUV; }"),
// Fragment shader:
Block("uniform sampler2D uTex; "
"varying highp vec2 vUV; "
"void main(void) { "
"gl_FragColor = texture2D(uTex, vUV); }"))
<< uMvpMatrix
<< uBufTex;

buf->setVertices(gl::TriangleStrip,
VBuf::Builder().makeQuad(Rectanglef(0, 0, 1, 1), Rectanglef(0, 1, 1, -1)),
gl::Static);

uMvpMatrix = Matrix4f::ortho(0, 1, 0, 1);
uBufTex = color;
}

void release()
Expand Down Expand Up @@ -175,7 +172,16 @@ DENG2_PIMPL(GLFramebuffer)
}
}

void swapBuffers(Canvas &canvas)
void drawSwap()
{
if(isMultisampled())
{
target.updateFromProxy();
}
bufSwap.draw();
}

void swapBuffers(Canvas &canvas, gl::SwapBufferMode swapMode)
{
GLTarget defaultTarget;

Expand All @@ -184,24 +190,44 @@ DENG2_PIMPL(GLFramebuffer)
.setViewport(Rectangleui::fromSize(size))
.apply();

if(GLInfo::extensions().EXT_framebuffer_blit)
switch(swapMode)
{
if(isMultisampled())
case gl::SwapMonoBuffer:
if(GLInfo::extensions().EXT_framebuffer_blit)
{
multisampleTarget.blit(defaultTarget); // resolve multisampling to system backbuffer
if(isMultisampled())
{
multisampleTarget.blit(defaultTarget); // resolve multisampling to system backbuffer
}
else
{
target.blit(defaultTarget); // copy to system backbuffer
}
}
else
{
target.blit(defaultTarget); // copy to system backbuffer
// Fallback: draw the back buffer texture to the main framebuffer.
bufSwap.draw();
}
canvas.QGLWidget::swapBuffers();
break;

case gl::SwapStereoLeftBuffer:
glDrawBuffer(GL_BACK_LEFT);
drawSwap();
glDrawBuffer(GL_BACK);
break;

case gl::SwapStereoRightBuffer:
glDrawBuffer(GL_BACK_RIGHT);
drawSwap();
glDrawBuffer(GL_BACK);
break;

case gl::SwapStereoBuffers:
canvas.QGLWidget::swapBuffers();
break;
}
else
{
// Fallback: draw the back buffer texture to the main framebuffer.
bufSwap.draw();
}

canvas.QGLWidget::swapBuffers();

GLState::pop().apply();
}
Expand Down Expand Up @@ -281,9 +307,9 @@ GLTexture &GLFramebuffer::depthStencilTexture() const
return d->depthStencil;
}

void GLFramebuffer::swapBuffers(Canvas &canvas)
void GLFramebuffer::swapBuffers(Canvas &canvas, gl::SwapBufferMode swapMode)
{
d->swapBuffers(canvas);
d->swapBuffers(canvas, swapMode);
}

bool GLFramebuffer::setDefaultMultisampling(int sampleCount)
Expand Down

0 comments on commit ff52397

Please sign in to comment.