BGE: Adding vsync control. Users can enable vsync, disable vsync, or use adaptive...
authorMitchell Stokes <mogurijin@gmail.com>
Mon, 29 Jul 2013 22:31:32 +0000 (22:31 +0000)
committerMitchell Stokes <mogurijin@gmail.com>
Mon, 29 Jul 2013 22:31:32 +0000 (22:31 +0000)
22 files changed:
doc/python_api/rst/bge.render.rst
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_IWindow.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_Window.h
intern/ghost/intern/GHOST_WindowWin32.cpp
intern/ghost/intern/GHOST_WindowWin32.h
intern/ghost/intern/GHOST_WindowX11.cpp
intern/ghost/intern/GHOST_WindowX11.h
release/scripts/startup/bl_ui/properties_game.py
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/wm_window.h
source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp
source/gameengine/BlenderRoutines/KX_BlenderCanvas.h
source/gameengine/BlenderRoutines/KX_BlenderGL.cpp
source/gameengine/BlenderRoutines/KX_BlenderGL.h
source/gameengine/GamePlayer/ghost/GPG_Application.cpp
source/gameengine/GamePlayer/ghost/GPG_Canvas.cpp
source/gameengine/GamePlayer/ghost/GPG_Canvas.h
source/gameengine/Ketsji/KX_PythonInit.cpp
source/gameengine/Rasterizer/RAS_ICanvas.h

index c3bcf19c01b2a9e4b5bf0aa5d4cefd7125d35145..5a80b7fa5c1c55c231916a98db75b5210660f7df 100644 (file)
@@ -62,6 +62,18 @@ Constants
 .. data:: KX_BLENDER_GLSL_MATERIAL
 
    Materials approximating blender materials with GLSL.
+   
+.. DATA:: VSYNC_OFF
+
+   Disables vsync
+
+.. DATA:: VSYNC_ON
+
+   Enables vsync
+
+.. DATA:: VSYNC_ADAPTIVE
+
+   Enables adaptive vsync if supported. Adaptive vsync enables vsync if the framerate is above the monitors refresh rate. Otherwise, vsync is diabled if the framerate is too low.
 
 *********
 Functions
@@ -289,3 +301,15 @@ Functions
 
    Disable the motion blur effect.
 
+.. function:: setVsync(value)
+
+   Set the vsync value
+
+   :arg value: One of VSYNC_OFF, VSYNC_ON, VSYNC_ADAPTIVE
+   :type value: integer
+
+.. function:: getVsync()
+
+   Get the current vsync value
+
+   :rtype: One of VSYNC_OFF, VSYNC_ON, VSYNC_ADAPTIVE
index a92d0d33b652ede9c4b0a017bc7d7f9fed4091fc..aae90179be5cd74365ddb8500316d6e760b139cd 100644 (file)
@@ -665,6 +665,19 @@ extern GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle,
  */
 extern GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle);
 
+/**
+ * Sets the swap interval for swapBuffers.
+ * \param interval The swap interval to use.
+ * \return A boolean success indicator.
+ */
+extern GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interval);
+
+/**
+ * Gets the current swap interval for swapBuffers.
+ * \return An integer.
+ */
+extern int GHOST_GetSwapInterval(GHOST_WindowHandle windowhandle);
+
 /**
  * Activates the drawing context of this window.
  * \param windowhandle The handle to the window
index a2d3e9b91fbb884822450548a0a888c605d865b8..355770752634dd00a7317886825c9ff8973327d4 100644 (file)
@@ -204,6 +204,19 @@ public:
         */
        virtual GHOST_TSuccess swapBuffers() = 0;
 
+       /**
+        * Sets the swap interval for swapBuffers.
+        * \param interval The swap interval to use.
+        * \return A boolean success indicator.
+        */
+       virtual GHOST_TSuccess setSwapInterval(int interval) = 0;
+
+       /**
+        * Gets the current swap interval for swapBuffers.
+        * \return An integer.
+        */
+       virtual int getSwapInterval() = 0;
+
        /**
         * Activates the drawing context of this window.
         * \return  A boolean success indicator.
index b73ff26c259918cacec4e31ba31fd4e46259aa67..8d4498ed759b11951bed50a4790f611857b7f665 100644 (file)
@@ -691,6 +691,19 @@ GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle)
        return window->swapBuffers();
 }
 
+GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interval)
+{
+       GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
+
+       return window->setSwapInterval(interval);
+}
+
+int GHOST_GetSwapInterval(GHOST_WindowHandle windowhandle)
+{
+       GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
+
+       return window->getSwapInterval();
+}
 
 
 GHOST_TSuccess GHOST_ActivateWindowDrawingContext(GHOST_WindowHandle windowhandle)
index 588de0911e302576c7d616d3cc06ec162e175146..77ee4db8543e487e26ff672c9ce4f840ee8362ac 100644 (file)
@@ -67,6 +67,8 @@ public:
         * virtual GHOST_TWindowOrder getOrder(void) = 0;
         * virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0;
         * virtual GHOST_TSuccess swapBuffers() = 0;
+        * virtual GHOST_TSuccess setSwapInterval() = 0;
+        * virtual int getSwapInterval() = 0;
         * virtual GHOST_TSuccess activateDrawingContext() = 0;
         * virtual GHOST_TSuccess invalidate() = 0;
         */
@@ -110,6 +112,8 @@ public:
         * virtual GHOST_TSuccess setState(GHOST_TWindowState state) = 0;
         * virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0;
         * virtual GHOST_TSuccess swapBuffers() = 0;
+        * virtual GHOST_TSuccess setSwapInterval() = 0;
+        * virtual int getSwapInterval() = 0;
         * virtual GHOST_TSuccess activateDrawingContext() = 0;
         * virtual GHOST_TSuccess invalidate() = 0;
         */
@@ -204,6 +208,23 @@ public:
                return GHOST_kFailure;
        }
        
+       /**
+        * Sets the swap interval for swapBuffers.
+        * \param interval The swap interval to use.
+        * \return A boolean success indicator.
+        */
+       virtual GHOST_TSuccess setSwapInterval(int interval) {
+               return GHOST_kFailure;
+       }
+       
+       /**
+        * Gets the current swap interval for swapBuffers.
+        * \return An integer.
+        */
+       virtual int getSwapInterval() {
+               return 0;
+       }
+       
        /**
         * Tells if the ongoing drag'n'drop object can be accepted upon mouse drop
         */
index b0d0b1a5b5dbed78e68a17d5cef4cf38eea4e437..c264686fbb152a96ada6fd8cee5c82c29bfd86d5 100644 (file)
@@ -648,6 +648,20 @@ GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
        return ::SwapBuffers(hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
 }
 
+GHOST_TSuccess GHOST_WindowWin32::setSwapInterval(int interval)
+{
+       if (!WGL_EXT_swap_control)
+               return GHOST_kFailure;
+       return wglSwapIntervalEXT(interval) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
+}
+
+int GHOST_WindowWin32::getSwapInterval()
+{
+       if (WGL_EXT_swap_control)
+               return wglGetSwapIntervalEXT();
+
+       return 0;
+}
 
 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
 {
index 2fd1f5b37f4eb845f943a9b15415aed3a2449fd4..6fdc963f30ae8a1c1328b5f506ce661cc17ba0f1 100644 (file)
@@ -211,6 +211,19 @@ public:
         */
        virtual GHOST_TSuccess swapBuffers();
 
+       /**
+        * Sets the swap interval for swapBuffers.
+        * \param interval The swap interval to use.
+        * \return A boolean success indicator.
+        */
+       virtual GHOST_TSuccess setSwapInterval(int interval);
+
+       /**
+        * Gets the current swap interval for swapBuffers.
+        * \return An integer.
+        */
+       virtual int getSwapInterval();
+
        /**
         * Activates the drawing context of this window.
         * \return Indication of success.
index 25d7c181b68b0e0152914cbc4a27f061030c6e72..3173736c2a5b4410a1b6493a720a12c90d7788dc 100644 (file)
@@ -30,6 +30,8 @@
  */
 
 
+#include <GL/glxew.h>
+
 #include "GHOST_WindowX11.h"
 #include "GHOST_SystemX11.h"
 #include "STR_String.h"
@@ -1513,3 +1515,23 @@ endFullScreen() const
 
        return GHOST_kSuccess;
 }
+
+GHOST_TSuccess
+GHOST_WindowX11::
+setSwapInterval(int interval) {
+       if (!GLX_EXT_swap_control)
+               return GHOST_kFailure;
+       glXSwapIntervalEXT(m_display, m_window, interval);
+       return GHOST_kSuccess;
+}
+
+int
+GHOST_WindowX11::
+getSwapInterval() {
+       if (GLX_EXT_swap_control) {
+               unsigned int value;
+               glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &value);
+               return (int)value;
+       }
+       return 0;
+}
index b8471b41a111601dcb8b9cf54837be7cf31d8586..7cbdcdeec2188a1201784535ffe6e7b25e5eda7b 100644 (file)
@@ -235,6 +235,19 @@ public:
 
        GHOST_TSuccess endFullScreen() const;
 
+       /**
+        * Sets the swap interval for swapBuffers.
+        * \param interval The swap interval to use.
+        * \return A boolean success indicator.
+        */
+       virtual GHOST_TSuccess setSwapInterval(int interval);
+
+       /**
+        * Gets the current swap interval for swapBuffers.
+        * \return An integer.
+        */
+       virtual int getSwapInterval();
+
 protected:
        /**
         * Tries to install a rendering context in this window.
index 38e49853454abcd7cec178fc3e2e0727b59ef857..025d86204f21d3d30d272a2fdfca8f6aae9ad75f 100644 (file)
@@ -413,6 +413,9 @@ class RENDER_PT_game_system(RenderButtonsPanel, Panel):
         col.prop(gs, "use_display_lists")
         col.active = gs.raster_storage != 'VERTEX_BUFFER_OBJECT'
 
+        row = layout.row()
+        row.prop(gs, "vsync")
+
         row = layout.row()
         row.prop(gs, "raster_storage")
 
index 70541f7cec5b8055ead5113444af71bdb970cb87..062107f834ecc7384e39b4021985d65ed631f03b 100644 (file)
@@ -1324,6 +1324,16 @@ void wm_window_swap_buffers(wmWindow *win)
 #endif
 }
 
+void wm_window_set_swap_interval (wmWindow *win, int interval)
+{
+       GHOST_SetSwapInterval(win->ghostwin, interval);
+}
+
+int wm_window_get_swap_interval (wmWindow *win)
+{
+       return GHOST_GetSwapInterval(win->ghostwin);
+}
+
 
 /* ******************* exported api ***************** */
 
index 22fa2423f6117866424ec4a6d23d318c1c189e16..d7e938fec7c9a8da5ea7eb1d48b4cb4a31cfd302 100644 (file)
@@ -58,6 +58,8 @@ void          wm_window_lower                 (wmWindow *win);
 void           wm_window_set_size              (wmWindow *win, int width, int height);
 void           wm_window_get_position  (wmWindow *win, int *posx_r, int *posy_r);
 void           wm_window_swap_buffers  (wmWindow *win);
+void           wm_window_set_swap_interval     (wmWindow *win, int interval);
+int                    wm_window_get_swap_interval     (wmWindow *win);
 
 void           wm_get_cursor_position  (wmWindow *win, int *x, int *y);
 
index bb4c3fd2cbc087f1eb76f79ed0a248d907f09efc..5703527eae0c0040408f1256378aeefbf1a5a01f 100644 (file)
@@ -284,6 +284,14 @@ extern "C" void StartKetsjiShell(struct bContext *C, struct ARegion *ar, rcti *c
                        canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
                else
                        canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE);
+
+               // Setup vsync
+               int previous_vsync = canvas->GetSwapInterval();
+               if (startscene->gm.vsync == VSYNC_ADAPTIVE)
+                       canvas->SetSwapInterval(-1);
+               else
+                       canvas->SetSwapInterval(startscene->gm.vsync); // VSYNC_OFF == 0, VSYNC_ON == 1, so this works
+
                RAS_IRenderTools* rendertools = new KX_BlenderRenderTools();
                RAS_IRasterizer* rasterizer = NULL;
                //Don't use displaylists with VBOs
@@ -663,6 +671,7 @@ extern "C" void StartKetsjiShell(struct bContext *C, struct ARegion *ar, rcti *c
                }
                if (canvas)
                {
+                       canvas->SetSwapInterval(previous_vsync); // Set the swap interval back
                        delete canvas;
                        canvas = NULL;
                }
index 3bd1c02f12e5dc3d558de0c0d9370ae3087198ce..3089b3fd44d465f7d390f7ec07e546386ac10213 100644 (file)
@@ -66,6 +66,16 @@ void KX_BlenderCanvas::SwapBuffers()
        BL_SwapBuffers(m_win);
 }
 
+void KX_BlenderCanvas::SetSwapInterval(int interval)
+{
+       BL_SetSwapInterval(m_win, interval);
+}
+
+int    KX_BlenderCanvas::GetSwapInterval()
+{
+       return BL_GetSwapInterval(m_win);
+}
+
 void KX_BlenderCanvas::ResizeWindow(int width, int height)
 {
        // Not implemented for the embedded player
index c201d866efecb49a526015d52add3d639aa704a7..c5318b882fa8a75c900eb7c983c9a90ab12d88da 100644 (file)
@@ -80,6 +80,16 @@ public:
                void 
        SwapBuffers(
        );
+
+               void
+       SetSwapInterval(
+               int interval
+       );
+
+               int
+       GetSwapInterval(
+       );
+
                void 
        ResizeWindow(
                int width,
index 61598995040c8c1e24891bd5ee1be3017be4d0f9..6ed4866579cccc66337e89e9be3c06100a0e4fe0 100644 (file)
@@ -98,6 +98,16 @@ void BL_MakeDrawable(wmWindowManager *wm, wmWindow *win)
        wm_window_make_drawable(wm, win);
 }
 
+void BL_SetSwapInterval(struct wmWindow *win, int interval)
+{
+       wm_window_set_swap_interval(win, interval);
+}
+
+int BL_GetSwapInterval(struct wmWindow *win)
+{
+       return wm_window_get_swap_interval(win);
+}
+
 static void DisableForText()
 {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); /* needed for texture fonts otherwise they render as wireframe */
index 54e76ff6489f2d38c8083a1a9549ff4d5a1b6477..8032d9a594abb4820ac4457853491db9567a39cc 100644 (file)
@@ -43,6 +43,8 @@ struct wmWindowManager;
 
 // special swapbuffers, that takes care of which area (viewport) needs to be swapped
 void   BL_SwapBuffers(struct wmWindow *win);
+void   BL_SetSwapInterval(struct wmWindow *win, int interval);
+int            BL_GetSwapInterval(struct wmWindow *win);
 
 void   BL_MakeDrawable(struct wmWindowManager *wm, struct wmWindow *win);
 
index bedee5d9a4757d1e250b1b01c6b9c6edf2822131..6f7a87804dcf73dd23771131d8ffaf7a3179efaa 100644 (file)
@@ -582,7 +582,12 @@ bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
                m_canvas = new GPG_Canvas(window);
                if (!m_canvas)
                        return false;
-                               
+
+               if (gm->vsync == VSYNC_ADAPTIVE)
+                       m_canvas->SetSwapInterval(-1);
+               else
+                       m_canvas->SetSwapInterval(gm->vsync); // VSYNC_OFF == 0, VSYNC_ON == 1, so this works
+
                m_canvas->Init();
                if (gm->flag & GAME_SHOW_MOUSE)
                        m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
index a1d00dad0e1335040a7a7a93062a1ccaebf3c879..e0559385ee64ab81ff649833ca493fa46c27fca9 100644 (file)
@@ -107,6 +107,20 @@ void GPG_Canvas::SwapBuffers()
        }
 }
 
+void GPG_Canvas::SetSwapInterval(int interval)
+{
+       if (m_window)
+               m_window->setSwapInterval(interval);
+}
+
+int GPG_Canvas::GetSwapInterval()
+{
+       if (m_window)
+               return m_window->getSwapInterval();
+
+       return 0;
+}
+
 void GPG_Canvas::ResizeWindow(int width, int height)
 {
        if (m_window->getState() == GHOST_kWindowStateFullScreen)
index 6168d96b3371cade1e64dce91e9abcf01514fad2..6e1f86cac0e365a015549628006c5c8107083f7e 100644 (file)
@@ -55,6 +55,9 @@ public:
        virtual void SetMousePosition(int x, int y);
        virtual void SetMouseState(RAS_MouseState mousestate);
        virtual void SwapBuffers();
+       virtual void SetSwapInterval(int interval);
+       virtual int     GetSwapInterval();
+
        virtual int GetMouseX(int x) { return x; }
        virtual int GetMouseY(int y) { return y; }
        virtual float GetMouseNormalizedX(int x);
index 7845c113643d26748756a9dfa5f7eac142f9fbe1..e595f24a6624578d7f524c059375f58bad0e5170 100644 (file)
@@ -1372,6 +1372,29 @@ static PyObject *gPyGetMipmapping(PyObject *)
        return PyLong_FromLong(gp_Rasterizer->GetMipmapping());
 }
 
+static PyObject *gPySetVsync(PyObject *, PyObject *args)
+{
+       int interval;
+
+       if (!PyArg_ParseTuple(args, "i:setVsync", &interval))
+               return NULL;
+
+       if (interval < VSYNC_OFF || interval > VSYNC_ADAPTIVE) {
+               PyErr_SetString(PyExc_ValueError, "Rasterizer.setVsync(value): value must be VSYNC_OFF, VSYNC_ON, or VSYNC_ADAPTIVE");
+               return NULL;
+       }
+
+       if (interval == VSYNC_ADAPTIVE)
+               interval = -1;
+       gp_Canvas->SetSwapInterval(interval);
+       Py_RETURN_NONE;
+}
+
+static PyObject *gPyGetVsync(PyObject *)
+{
+       return PyLong_FromLong(gp_Canvas->GetSwapInterval());
+}
+
 static struct PyMethodDef rasterizer_methods[] = {
        {"getWindowWidth",(PyCFunction) gPyGetWindowWidth,
         METH_VARARGS, "getWindowWidth doc"},
@@ -1417,6 +1440,8 @@ static struct PyMethodDef rasterizer_methods[] = {
        {"getFullScreen", (PyCFunction) gPyGetFullScreen, METH_NOARGS, ""},
        {"setMipmapping", (PyCFunction) gPySetMipmapping, METH_VARARGS, ""},
        {"getMipmapping", (PyCFunction) gPyGetMipmapping, METH_NOARGS, ""},
+       {"setVsync", (PyCFunction) gPySetVsync, METH_VARARGS, ""},
+       {"getVsync", (PyCFunction) gPyGetVsync, METH_NOARGS, ""},
        { NULL, (PyCFunction) NULL, 0, NULL }
 };
 
@@ -2189,6 +2214,11 @@ PyObject *initRasterizer(RAS_IRasterizer* rasty,RAS_ICanvas* canvas)
        KX_MACRO_addTypesToDict(d, RAS_MIPMAP_NEAREST, RAS_IRasterizer::RAS_MIPMAP_NEAREST);
        KX_MACRO_addTypesToDict(d, RAS_MIPMAP_LINEAR, RAS_IRasterizer::RAS_MIPMAP_LINEAR);
 
+       /* for get/setVsync */
+       KX_MACRO_addTypesToDict(d, VSYNC_OFF, VSYNC_OFF);
+       KX_MACRO_addTypesToDict(d, VSYNC_ON, VSYNC_ON);
+       KX_MACRO_addTypesToDict(d, VSYNC_ADAPTIVE, VSYNC_ADAPTIVE);
+
        // XXXX Add constants here
 
        // Check for errors
index 1b1e43a52572ed0b9009f76f8f20005405c94ab2..9e8a6e8ccf64d970627785e6d2146240dab5d2d3 100644 (file)
@@ -105,6 +105,17 @@ public:
                void 
        SwapBuffers(
        )=0;
+       
+       virtual
+               void
+       SetSwapInterval(
+               int interval
+       )=0;
+
+       virtual
+               int
+       GetSwapInterval(
+       )=0;
  
        virtual 
                void