Skip to content

Commit

Permalink
GRAPHICS: Add a new frame locking mechanism
Browse files Browse the repository at this point in the history
The problem with our current LOCK_FRAME() method that locks Ogre's
SceneMutex is that it blocks in the render loop, therefore producing
a deadlock when locking the frame and then forcing a function into
the main thread (since those functions are handled in the same thread
that's then waiting on the SceneMutex).

To prevent that, we now implement a simple frame lock using atomics
that doesn't block the main render loop. Instead, if the render
method can't aquire the frame lock, it will simply jump over the
rendering and continue with processing the event queue.

To prevent that, we now implement a simple frame lock using atomics
that doesn't block the main render loop. Instead, if the render
method can't aquire the frame lock, it will simply jump over the
rendering and continue with processing the event queue.
  • Loading branch information
DrMcCoy committed Mar 20, 2014
1 parent d9015d6 commit c5a3462
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/graphics/graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ GraphicsManager::GraphicsManager() {

_width = 800;
_height = 600;

_frameLock.store(0);
}

GraphicsManager::~GraphicsManager() {
Expand Down Expand Up @@ -344,12 +346,34 @@ double GraphicsManager::getFPS() const {
return _renderer->getFPS();
}

void GraphicsManager::lockFrame() {
while (true) {
// Mask out the render bit (so that comparison will always fail if it is set)
uint32 oldValue = _frameLock.load() & 0x7FFFFFFF;
uint32 newValue = oldValue + 1;

if (_frameLock.compare_exchange_weak(oldValue, newValue))
return;
}
}

void GraphicsManager::unlockFrame() {
_frameLock.fetch_sub(1);
}

void GraphicsManager::renderScene() {
Common::enforceMainThread();

// Make sure the frame is not lock and set the render bit, otherwise skip rendering
uint32 frameLock = 0;
if (!_frameLock.compare_exchange_weak(frameLock, 0x80000000))
return;

if (_renderer)
_renderer->render();

_frameLock.store(0);

SDL_GL_SwapWindow(_screen);
}

Expand Down
16 changes: 16 additions & 0 deletions src/graphics/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#ifndef GRAPHICS_GRAPHICS_H
#define GRAPHICS_GRAPHICS_H

#include <boost/atomic.hpp>

#include <OgreRoot.h>
#include <OgreSceneManager.h>
#include <OgreThreadHeaders.h>
Expand Down Expand Up @@ -123,6 +125,11 @@ class GraphicsManager : public Common::Singleton<GraphicsManager> {

double getFPS() const;

/** Lock the frame blockingly. No further rendering will be done while the frame is locked. */
void lockFrame();
/** Unlock the frame again. */
void unlockFrame();

/** Render one complete frame of the scene. */
void renderScene();

Expand Down Expand Up @@ -150,6 +157,8 @@ class GraphicsManager : public Common::Singleton<GraphicsManager> {
SDL_Window *_screen; ///< The SDL screen surface.
Renderer *_renderer; ///< The OpenGL renderer.

boost::atomic<uint32> _frameLock;


void initScreen(int width, int height, bool fullscreen, bool vsync, int fsaa);
void getDefaultDisplayMode();
Expand All @@ -163,6 +172,13 @@ class GraphicsManager : public Common::Singleton<GraphicsManager> {
void checkCapabilities();
};

/** A simple class allowing for scopingly locking/unlocking the frame. */
class FrameLocker {
public:
FrameLocker() { GraphicsManager::instance().lockFrame(); }
~FrameLocker() { GraphicsManager::instance().unlockFrame(); }
};

} // End of namespace Graphics

/** Shortcut for accessing the graphics manager. */
Expand Down

0 comments on commit c5a3462

Please sign in to comment.