Skip to content

Commit

Permalink
Added: Variables "vid-fsaa" and "vid-vsync"
Browse files Browse the repository at this point in the history
FSAA and vsync can be toggled at runtime with the new vid-fsaa and
vid-vsync variables. The command line options -nofsaa and -novsync
can be used to permanently disable FSAA and vsync, respectively.

Also, when the mouse is trapped in a window, all GL updates will be
timed manually to match the requested maximum refresh rate. This
will only have an effect if refresh-rate-maximum is less than the vsync
rate.
  • Loading branch information
skyjake committed Apr 7, 2012
1 parent 19193e1 commit 7fa141f
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 24 deletions.
6 changes: 5 additions & 1 deletion doomsday/engine/portable/include/canvas.h
Expand Up @@ -38,7 +38,7 @@ class Canvas : public QGLWidget
Q_OBJECT

public:
explicit Canvas(QWidget *parent = 0);
explicit Canvas(QWidget *parent = 0, QGLWidget* shared = 0);
~Canvas();

/**
Expand Down Expand Up @@ -69,6 +69,8 @@ class Canvas : public QGLWidget

void setFocusFunc(void (*canvasFocusChanged)(Canvas&, bool));

void useCallbacksFrom(Canvas& other);

/**
* Grabs the contents of the canvas framebuffer.
*
Expand Down Expand Up @@ -102,6 +104,8 @@ class Canvas : public QGLWidget

void trapMouse(bool trap = true);

bool isMouseTrapped() const;

protected:
void initializeGL();
void resizeGL(int w, int h);
Expand Down
10 changes: 10 additions & 0 deletions doomsday/engine/portable/include/canvaswindow.h
Expand Up @@ -38,7 +38,14 @@ class CanvasWindow : public QMainWindow
explicit CanvasWindow(QWidget *parent = 0);
~CanvasWindow();

/**
* Recreates the contained Canvas with an update GL format.
* The context is shared with the old Canvas.
*/
void recreateCanvas();

Canvas& canvas();
bool ownsCanvas(Canvas* c) const;

/**
* Sets a callback function for notifying about window movement.
Expand All @@ -60,6 +67,9 @@ class CanvasWindow : public QMainWindow

public slots:

protected:
static void initCanvasAfterRecreation(Canvas& canvas);

private:
struct Instance;
Instance* d;
Expand Down
4 changes: 4 additions & 0 deletions doomsday/engine/portable/include/dd_loop.h
Expand Up @@ -59,6 +59,10 @@ void DD_GameLoopDrawer(void);
* ready before this is called. Ideally the updates would appear at a fixed
* frequency; in practice, inaccuracies due to time measurement and background
* processes may result in varying update intervals.
*
* Note that if the maximum refresh rate has been set to a value higher than
* the vsync rate, this function does nothing but update the statistisc on
* frame timing.
*/
void DD_WaitForOptimalUpdateTime(void);

Expand Down
13 changes: 13 additions & 0 deletions doomsday/engine/portable/include/window.h
Expand Up @@ -299,6 +299,19 @@ void Window_RestoreState(Window* wnd);
*/
void Window_TrapMouse(const Window* wnd, boolean enable);

boolean Window_IsMouseTrapped(const Window* wnd);

/**
* Determines whether the contents of a window should be drawn during the
* execution of the main loop callback, or should we wait for an update event
* from the windowing system.
*
* @param wnd Window instance.
*/
boolean Window_ShouldRepaintManually(const Window* wnd);

void Window_UpdateCanvasFormat(Window* wnd);

void* Window_NativeHandle(const Window* wnd);

#ifdef __cplusplus
Expand Down
18 changes: 17 additions & 1 deletion doomsday/engine/portable/src/canvas.cpp
Expand Up @@ -110,10 +110,14 @@ struct Canvas::Instance
}
};

Canvas::Canvas(QWidget *parent) : QGLWidget(parent)
Canvas::Canvas(QWidget* parent, QGLWidget* shared) : QGLWidget(parent, shared)
{
d = new Instance(this);

LOG_AS("Canvas");
LOG_DEBUG("swap interval: ") << format().swapInterval();
LOG_DEBUG("multisample: %b") << format().sampleBuffers();

setFocusPolicy(Qt::StrongFocus);
setMouseTracking(true); // receive moves always

Expand Down Expand Up @@ -146,6 +150,13 @@ void Canvas::setFocusFunc(void (*canvasFocusChanged)(Canvas&, bool))
d->focusCallback = canvasFocusChanged;
}

void Canvas::useCallbacksFrom(Canvas &other)
{
d->drawCallback = other.d->drawCallback;
d->focusCallback = other.d->focusCallback;
d->resizedCallback = other.d->resizedCallback;
}

QImage Canvas::grabImage(const QSize& outputSize)
{
QImage grabbed = grabFrameBuffer(); // no alpha
Expand Down Expand Up @@ -194,6 +205,11 @@ void Canvas::trapMouse(bool trap)
}
}

bool Canvas::isMouseTrapped() const
{
return d->mouseGrabbed;
}

void Canvas::initializeGL()
{
Sys_GLConfigureDefaultState();
Expand Down
83 changes: 80 additions & 3 deletions doomsday/engine/portable/src/canvaswindow.cpp
Expand Up @@ -26,15 +26,19 @@

#include "de_platform.h"
#include "con_main.h"
#include "gl_main.h"
#include "m_args.h"
#include "canvaswindow.h"
#include <assert.h>

struct CanvasWindow::Instance
{
Canvas* canvas;
Canvas* recreated;
void (*moveFunc)(CanvasWindow&);
bool mouseWasTrapped;

Instance() : canvas(0), moveFunc(0) {}
Instance() : canvas(0), moveFunc(0), mouseWasTrapped(false) {}
};

CanvasWindow::CanvasWindow(QWidget *parent)
Expand All @@ -54,12 +58,67 @@ CanvasWindow::~CanvasWindow()
delete d;
}

void CanvasWindow::initCanvasAfterRecreation(Canvas& canvas)
{
CanvasWindow* self = dynamic_cast<CanvasWindow*>(canvas.parentWidget());
assert(self);

LOG_DEBUG("About to replace Canvas %p with %p")
<< de::dintptr(self->d->canvas) << de::dintptr(self->d->recreated);

// Take the existing callbacks of the old canvas.
self->d->recreated->useCallbacksFrom(*self->d->canvas);

// Switch the central widget. This will delete the old canvas automatically.
self->setCentralWidget(self->d->recreated);
self->d->canvas = self->d->recreated;
self->d->recreated = 0;

// Set up the basic GL state for the new canvas.
GL_Init2DState();

// Reacquire the focus.
self->d->canvas->setFocus();
if(self->d->mouseWasTrapped)
{
self->d->canvas->trapMouse();
}

LOG_DEBUG("Canvas replaced with %p") << de::dintptr(self->d->canvas);
}

void CanvasWindow::recreateCanvas()
{
// We'll re-trap the mouse after the new canvas is ready.
d->mouseWasTrapped = canvas().isMouseTrapped();
canvas().trapMouse(false);

// Update the GL format for subsequently created Canvases.
setDefaultGLFormat();

// Create the replacement Canvas. Once it's created and visible, we'll
// finish the switch-over.
d->recreated = new Canvas(this, d->canvas);
d->recreated->setInitFunc(initCanvasAfterRecreation);

d->recreated->setGeometry(d->canvas->geometry());
d->recreated->show();

LOG_DEBUG("Canvas recreated, old one still exists");
}

Canvas& CanvasWindow::canvas()
{
assert(d->canvas != 0);
return *d->canvas;
}

bool CanvasWindow::ownsCanvas(Canvas *c) const
{
if(!c) return false;
return (d->canvas == c || d->recreated == c);
}

void CanvasWindow::setMoveFunc(void (*func)(CanvasWindow&))
{
d->moveFunc = func;
Expand Down Expand Up @@ -96,7 +155,25 @@ void CanvasWindow::setDefaultGLFormat() // static
fmt.setDepthBufferSize(16);
fmt.setStencilBufferSize(8);
fmt.setDoubleBuffer(true);
fmt.setSwapInterval(1); // vsync on
fmt.setSampleBuffers(true); // multisampling on (default: highest available)

if(ArgExists("-novsync") || !Con_GetByte("vid-vsync"))
{
fmt.setSwapInterval(0); // vsync off
}
else
{
fmt.setSwapInterval(1);
}

if(ArgExists("-nofsaa") || !Con_GetByte("vid-fsaa"))
{
fmt.setSampleBuffers(false);
}
else
{
fmt.setSampleBuffers(true); // multisampling on (default: highest available)
//fmt.setSamples(4);
}

QGLFormat::setDefaultFormat(fmt);
}
2 changes: 2 additions & 0 deletions doomsday/engine/portable/src/con_main.c
Expand Up @@ -2047,6 +2047,8 @@ void Con_Error(const char* error, ...)
char buff[2048], err[256];
va_list argptr;

Window_TrapMouse(Window_Main(), false);

// Already in an error?
if(!ConsoleInited || errorInProgress)
{
Expand Down
20 changes: 14 additions & 6 deletions doomsday/engine/portable/src/dd_loop.c
Expand Up @@ -90,7 +90,6 @@ static boolean waitingForDraw = false;
static void startFrame(void);
static void endFrame(void);
static void runTics(void);
static void drawAndUpdate(void);

void DD_RegisterLoop(void)
{
Expand Down Expand Up @@ -238,21 +237,32 @@ void DD_GameLoopDrawer(void)
endFrame();
}

//static uint frameStartAt;

static void startFrame(void)
{
//frameStartAt = Sys_GetRealTime();

S_StartFrame();
if(gx.BeginFrame)
{
gx.BeginFrame();
}
}

static uint lastShowAt;

static void endFrame(void)
{
static uint lastFpsTime = 0;

uint nowTime = Sys_GetRealTime();

/*
Con_Message("endFrame with %i ms (%i render)\n", nowTime - lastShowAt, nowTime - frameStartAt);
lastShowAt = nowTime;
*/

// Increment the (local) frame counter.
rFrameCount++;

Expand Down Expand Up @@ -475,8 +485,6 @@ static void timeDeltaStatistics(int deltaMs)

void DD_WaitForOptimalUpdateTime(void)
{
/// @todo This would benefit from microsecond-accurate timing.

// All times are in milliseconds.
static uint prevUpdateTime = 0;
uint nowTime, elapsed = 0;
Expand All @@ -488,9 +496,9 @@ void DD_WaitForOptimalUpdateTime(void)

// If vsync is on, this is unnecessary.
/// @todo check the rend-vsync cvar
#if defined(MACOSX) || defined(WIN32)
return;
#endif
//#if defined(MACOSX) || defined(WIN32)
// return;
//#endif

if(Sys_IsShuttingDown()) return; // No need for finesse.

Expand Down
26 changes: 20 additions & 6 deletions doomsday/engine/portable/src/gl_main.c
Expand Up @@ -123,16 +123,27 @@ static boolean initGLOk = false;
static boolean gamma_support = false;
static float oldgamma, oldcontrast, oldbright;
static int fogModeDefault = 0;
static byte fsaaEnabled = true;
static byte vsyncEnabled = true;

static viewport_t currentView;

// CODE --------------------------------------------------------------------

static void videoSettingsChanged(void)
{
if(!novideo && Window_Main())
{
Window_UpdateCanvasFormat(Window_Main());
}
}

void GL_Register(void)
{
// Cvars
C_VAR_INT("rend-dev-wireframe", &renderWireframe, 0, 0, 2);
C_VAR_INT("rend-fog-default", &fogModeDefault, 0, 0, 2);

// * Render-HUD
C_VAR_FLOAT("rend-hud-offset-scale", &weaponOffsetScale, CVF_NO_MAX,
0, 0);
Expand All @@ -144,6 +155,8 @@ void GL_Register(void)
C_VAR_INT("rend-mobj-smooth-turn", &useSRVOAngle, 0, 0, 1);

// * video
C_VAR_BYTE2("vid-vsync", &vsyncEnabled, 0, 0, 1, videoSettingsChanged);
C_VAR_BYTE2("vid-fsaa", &fsaaEnabled, 0, 0, 1, videoSettingsChanged);
C_VAR_INT("vid-res-x", &defResX, CVF_NO_MAX|CVF_READ_ONLY|CVF_NO_ARCHIVE, 320, 0);
C_VAR_INT("vid-res-y", &defResY, CVF_NO_MAX|CVF_READ_ONLY|CVF_NO_ARCHIVE, 240, 0);
C_VAR_INT("vid-bpp", &defBPP, CVF_READ_ONLY|CVF_NO_ARCHIVE, 16, 32);
Expand Down Expand Up @@ -184,15 +197,16 @@ void GL_DoUpdate(void)

LIBDENG_ASSERT_IN_MAIN_THREAD();

// Wait until the right time to show the frame so that the realized
// frame rate is exactly right.
//DD_WaitForOptimalUpdateTime();
if(Window_ShouldRepaintManually(Window_Main()))
{
// Wait until the right time to show the frame so that the realized
// frame rate is exactly right.
glFlush();
DD_WaitForOptimalUpdateTime();
}

// Blit screen to video.
Window_SwapBuffers(theWindow);

// Increment frame counter.
//r_framecounter++;
}

void GL_GetGammaRamp(displaycolortransfer_t *ramp)
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/portable/src/sys_opengl.c
Expand Up @@ -704,7 +704,7 @@ boolean Sys_GLCheckError(void)
{
GLenum error = glGetError();
if(error != GL_NO_ERROR)
Con_Error("OpenGL error: 0x%x\n", error);
Con_Message("OpenGL error: 0x%x\n", error);
}
#endif
return false;
Expand Down
3 changes: 2 additions & 1 deletion doomsday/engine/portable/src/sys_system.c
Expand Up @@ -246,7 +246,8 @@ void Sys_BlockUntilRealTime(uint realTimeMs)

while(Sys_GetRealTime() < realTimeMs)
{
// Do nothing; don't yield execution.
// Do nothing; don't yield execution. We want to exit here at the
// precise right moment.
}
}

Expand Down

0 comments on commit 7fa141f

Please sign in to comment.