GTK: Swap glFenceSync for sync control extension.

This commit is contained in:
Brandon Wright 2019-03-02 15:25:59 -06:00
parent 70484ba612
commit 7b310a0cf4
11 changed files with 174 additions and 126 deletions

View File

@ -151,8 +151,8 @@ int Snes9xConfig::load_defaults ()
npot_textures = false; npot_textures = false;
use_shaders = false; use_shaders = false;
shader_filename.clear (); shader_filename.clear ();
sync_every_frame = false; use_glfinish = false;
use_fences = false; use_sync_control = false;
#endif #endif
/* Snes9x Variables */ /* Snes9x Variables */
@ -267,8 +267,8 @@ int Snes9xConfig::save_config_file ()
#undef z #undef z
#define z "OpenGL::" #define z "OpenGL::"
outbool (cf, z"VSync", sync_to_vblank); outbool (cf, z"VSync", sync_to_vblank);
outbool (cf, z"glFinish", sync_every_frame); outbool (cf, z"glFinish", use_glfinish);
outbool (cf, z"glFenceSync", use_fences); outbool (cf, z"SyncControl", use_sync_control);
outbool (cf, z"UsePixelBufferObjects", use_pbos); outbool (cf, z"UsePixelBufferObjects", use_pbos);
cf.SetInt (z"PixelBufferObjectBitDepth", pbo_format); cf.SetInt (z"PixelBufferObjectBitDepth", pbo_format);
outbool (cf, z"UseNonPowerOfTwoTextures", npot_textures); outbool (cf, z"UseNonPowerOfTwoTextures", npot_textures);
@ -497,8 +497,8 @@ int Snes9xConfig::load_config_file ()
#undef z #undef z
#define z "OpenGL::" #define z "OpenGL::"
inbool (z"VSync", sync_to_vblank); inbool (z"VSync", sync_to_vblank);
inbool (z"glFinish", sync_every_frame); inbool (z"glFinish", use_glfinish);
inbool (z"glFenceSync", use_fences); inbool (z"SyncControl", use_sync_control);
inbool (z"UsePixelBufferObjects", use_pbos); inbool (z"UsePixelBufferObjects", use_pbos);
inint (z"PixelBufferObjectBitDepth", pbo_format); inint (z"PixelBufferObjectBitDepth", pbo_format);
inbool (z"UseNonPowerOfTwoTextures", npot_textures); inbool (z"UseNonPowerOfTwoTextures", npot_textures);

View File

@ -151,8 +151,8 @@ class Snes9xConfig
bool npot_textures; bool npot_textures;
bool use_shaders; bool use_shaders;
std::string shader_filename; std::string shader_filename;
bool sync_every_frame; bool use_glfinish;
bool use_fences; bool use_sync_control;
#endif #endif
JoyDevice **joystick; JoyDevice **joystick;

View File

@ -6,6 +6,7 @@
#include "gtk_2_3_compat.h" #include "gtk_2_3_compat.h"
#include <dlfcn.h> #include <dlfcn.h>
#include <epoxy/glx_generated.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/time.h> #include <sys/time.h>
@ -88,7 +89,6 @@ S9xOpenGLDisplayDriver::S9xOpenGLDisplayDriver (Snes9xWindow *window,
this->window = window; this->window = window;
this->config = config; this->config = config;
this->drawing_area = GTK_WIDGET (window->drawing_area); this->drawing_area = GTK_WIDGET (window->drawing_area);
fence = NULL;
} }
void S9xOpenGLDisplayDriver::update (int width, int height, int yoffset) void S9xOpenGLDisplayDriver::update (int width, int height, int yoffset)
@ -609,9 +609,6 @@ bool S9xOpenGLDisplayDriver::create_context()
else else
core = false; core = false;
if (version >= 31 || epoxy_has_gl_extension ("GL_ARB_sync"))
fences = true;
return true; return true;
} }
@ -666,17 +663,11 @@ void S9xOpenGLDisplayDriver::swap_buffers ()
{ {
context->swap_buffers (); context->swap_buffers ();
if (config->sync_every_frame && !config->use_fences) if (config->use_glfinish && !config->use_sync_control)
{ {
usleep (0); usleep (0);
glFinish (); glFinish ();
} }
else if (config->use_fences && fences)
{
if (fence)
glDeleteSync (fence);
fence = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
} }
void S9xOpenGLDisplayDriver::deinit () void S9xOpenGLDisplayDriver::deinit ()
@ -740,14 +731,10 @@ int S9xOpenGLDisplayDriver::query_availability ()
bool S9xOpenGLDisplayDriver::is_ready () bool S9xOpenGLDisplayDriver::is_ready ()
{ {
if (!fence) if (context->ready())
{
return true; return true;
}
if (glClientWaitSync (fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0) == GL_TIMEOUT_EXPIRED) return false;
return false;
glDeleteSync (fence);
fence = NULL;
return true;
} }

View File

@ -75,8 +75,6 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver
OpenGLContext *context; OpenGLContext *context;
GLsync fence;
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
GTKGLXContext glx; GTKGLXContext glx;
#endif #endif

View File

@ -4,6 +4,7 @@
For further information, consult the LICENSE file in the root directory. For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/ \*****************************************************************************/
#include <epoxy/glx_generated.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -22,6 +23,8 @@ GTKGLXContext::GTKGLXContext ()
version_major = -1; version_major = -1;
version_minor = -1; version_minor = -1;
use_oml_sync_control = false;
ust = msc = sbc = 0;
} }
GTKGLXContext::~GTKGLXContext () GTKGLXContext::~GTKGLXContext ()
@ -120,6 +123,9 @@ bool GTKGLXContext::create_context ()
return false; return false;
} }
if (strstr(extensions, "GLX_OML_sync_control") && gui_config->use_sync_control)
use_oml_sync_control = true;
return true; return true;
} }
@ -144,9 +150,27 @@ void GTKGLXContext::resize ()
void GTKGLXContext::swap_buffers () void GTKGLXContext::swap_buffers ()
{ {
if (use_oml_sync_control)
glXGetSyncValuesOML(display, xid, &ust, &msc, &sbc);
glXSwapBuffers (display, xid); glXSwapBuffers (display, xid);
} }
bool GTKGLXContext::ready()
{
if (use_oml_sync_control)
{
int64 ust, msc, sbc;
glXGetSyncValuesOML(display, xid, &ust, &msc, &sbc);
if (sbc != this->sbc || msc - this->msc > 2)
return true;
return false;
}
return true;
}
void GTKGLXContext::make_current () void GTKGLXContext::make_current ()
{ {
glXMakeCurrent (display, xid, context); glXMakeCurrent (display, xid, context);

View File

@ -23,6 +23,7 @@ class GTKGLXContext : public OpenGLContext
void swap_buffers (); void swap_buffers ();
void swap_interval (int frames); void swap_interval (int frames);
void make_current (); void make_current ();
bool ready();
GtkWidget *widget; GtkWidget *widget;
@ -40,6 +41,9 @@ class GTKGLXContext : public OpenGLContext
int version_major; int version_major;
int version_minor; int version_minor;
bool use_oml_sync_control;
int64_t ust, msc, sbc;
}; };
#endif #endif

View File

@ -19,6 +19,7 @@ class OpenGLContext
virtual void swap_buffers () = 0; virtual void swap_buffers () = 0;
virtual void swap_interval (int frames) = 0; virtual void swap_interval (int frames) = 0;
virtual void make_current () = 0; virtual void make_current () = 0;
virtual bool ready() { return true; };
int x; int x;
int y; int y;

View File

@ -715,8 +715,8 @@ Snes9xPreferences::move_settings_to_dialog ()
#ifdef USE_OPENGL #ifdef USE_OPENGL
set_check ("sync_to_vblank", config->sync_to_vblank); set_check ("sync_to_vblank", config->sync_to_vblank);
set_check ("sync_every_frame", config->sync_every_frame); set_check ("use_glfinish", config->use_glfinish);
set_check ("use_fences", config->use_fences); set_check ("use_sync_control", config->use_sync_control);
set_check ("use_pbos", config->use_pbos); set_check ("use_pbos", config->use_pbos);
set_combo ("pixel_format", config->pbo_format == 16 ? 0 : 1); set_combo ("pixel_format", config->pbo_format == 16 ? 0 : 1);
set_check ("npot_textures", config->npot_textures); set_check ("npot_textures", config->npot_textures);
@ -859,6 +859,7 @@ Snes9xPreferences::get_settings_from_dialog ()
int pbo_format = get_combo ("pixel_format") == 1 ? 32 : 16; int pbo_format = get_combo ("pixel_format") == 1 ? 32 : 16;
if (config->sync_to_vblank != get_check ("sync_to_vblank") || if (config->sync_to_vblank != get_check ("sync_to_vblank") ||
config->use_sync_control != get_check ("use_sync_control") ||
config->npot_textures != get_check ("npot_textures") || config->npot_textures != get_check ("npot_textures") ||
config->use_pbos != get_check ("use_pbos") || config->use_pbos != get_check ("use_pbos") ||
config->pbo_format != pbo_format || config->pbo_format != pbo_format ||
@ -872,8 +873,8 @@ Snes9xPreferences::get_settings_from_dialog ()
config->use_pbos = get_check ("use_pbos"); config->use_pbos = get_check ("use_pbos");
config->npot_textures = get_check ("npot_textures"); config->npot_textures = get_check ("npot_textures");
config->use_shaders = get_check ("use_shaders"); config->use_shaders = get_check ("use_shaders");
config->sync_every_frame = get_check ("sync_every_frame"); config->use_glfinish = get_check ("use_glfinish");
config->use_fences = get_check ("use_fences"); config->use_sync_control = get_check ("use_sync_control");
config->shader_filename = get_entry_text ("fragment_shader"); config->shader_filename = get_entry_text ("fragment_shader");

View File

@ -4,28 +4,30 @@
For further information, consult the LICENSE file in the root directory. For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/ \*****************************************************************************/
#include <epoxy/egl_generated.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "gtk_s9x.h"
#include "gtk_wayland_egl_context.h" #include "gtk_wayland_egl_context.h"
static void wl_global (void *data, static void wl_global(void *data,
struct wl_registry *wl_registry, struct wl_registry *wl_registry,
uint32_t name, uint32_t name,
const char *interface, const char *interface,
uint32_t version) uint32_t version)
{ {
WaylandEGLContext *wl = (WaylandEGLContext *) data; WaylandEGLContext *wl = (WaylandEGLContext *)data;
if (!strcmp (interface, "wl_compositor")) if (!strcmp(interface, "wl_compositor"))
wl->compositor = (struct wl_compositor *) wl_registry_bind (wl_registry, name, &wl_compositor_interface, 3); wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3);
else if (!strcmp (interface, "wl_subcompositor")) else if (!strcmp(interface, "wl_subcompositor"))
wl->subcompositor = (struct wl_subcompositor *) wl_registry_bind (wl_registry, name, &wl_subcompositor_interface, 1); wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
} }
static void wl_global_remove (void *data, static void wl_global_remove(void *data,
struct wl_registry *wl_registry, struct wl_registry *wl_registry,
uint32_t name) uint32_t name)
{ {
} }
@ -34,79 +36,81 @@ static const struct wl_registry_listener wl_registry_listener = {
wl_global_remove wl_global_remove
}; };
WaylandEGLContext::WaylandEGLContext () WaylandEGLContext::WaylandEGLContext()
{ {
display = NULL; display = NULL;
registry = NULL; registry = NULL;
compositor = NULL; compositor = NULL;
subcompositor = NULL; subcompositor = NULL;
parent = NULL; parent = NULL;
child = NULL; child = NULL;
region = NULL; region = NULL;
subsurface = NULL; subsurface = NULL;
egl_display = NULL; egl_display = NULL;
egl_surface = NULL; egl_surface = NULL;
egl_context = NULL; egl_context = NULL;
egl_config = NULL; egl_config = NULL;
egl_window = NULL; egl_window = NULL;
use_sync_control = false;
ust = msc = sbc = 0;
} }
WaylandEGLContext::~WaylandEGLContext () WaylandEGLContext::~WaylandEGLContext()
{ {
if (subsurface) if (subsurface)
wl_subsurface_destroy (subsurface); wl_subsurface_destroy(subsurface);
if (region) if (region)
wl_region_destroy (region); wl_region_destroy(region);
if (child) if (child)
wl_surface_destroy (child); wl_surface_destroy(child);
if (egl_context) if (egl_context)
eglDestroyContext (egl_display, egl_context); eglDestroyContext(egl_display, egl_context);
if (egl_surface) if (egl_surface)
eglDestroySurface (egl_display, egl_surface); eglDestroySurface(egl_display, egl_surface);
if (egl_window) if (egl_window)
wl_egl_window_destroy (egl_window); wl_egl_window_destroy(egl_window);
} }
bool WaylandEGLContext::attach (GtkWidget *widget) bool WaylandEGLContext::attach(GtkWidget *widget)
{ {
GdkWindow *window = gtk_widget_get_window (widget); GdkWindow *window = gtk_widget_get_window(widget);
if (!GDK_IS_WAYLAND_WINDOW (window)) if (!GDK_IS_WAYLAND_WINDOW(window))
return false; return false;
gdk_window = window; gdk_window = window;
gdk_window_get_geometry (gdk_window, &x, &y, &width, &height); gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
display = gdk_wayland_display_get_wl_display (gdk_window_get_display (gdk_window)); display = gdk_wayland_display_get_wl_display(gdk_window_get_display(gdk_window));
parent = gdk_wayland_window_get_wl_surface (gdk_window); parent = gdk_wayland_window_get_wl_surface(gdk_window);
registry = wl_display_get_registry (display); registry = wl_display_get_registry(display);
wl_registry_add_listener (registry, &wl_registry_listener, this); wl_registry_add_listener(registry, &wl_registry_listener, this);
wl_display_roundtrip (display); wl_display_roundtrip(display);
if (!compositor || !subcompositor) if (!compositor || !subcompositor)
return false; return false;
child = wl_compositor_create_surface (compositor); child = wl_compositor_create_surface(compositor);
region = wl_compositor_create_region (compositor); region = wl_compositor_create_region(compositor);
subsurface = wl_subcompositor_get_subsurface (subcompositor, child, parent); subsurface = wl_subcompositor_get_subsurface(subcompositor, child, parent);
wl_surface_set_input_region (child, region); wl_surface_set_input_region(child, region);
wl_subsurface_set_desync (subsurface); wl_subsurface_set_desync(subsurface);
wl_subsurface_set_position (subsurface, x, y); wl_subsurface_set_position(subsurface, x, y);
return true; return true;
} }
bool WaylandEGLContext::create_context () bool WaylandEGLContext::create_context()
{ {
int scale = gdk_window_get_scale_factor (gdk_window); int scale = gdk_window_get_scale_factor(gdk_window);
gdk_window_get_geometry (gdk_window, &x, &y, &width, &height); gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
EGLint surface_attribs[] = { EGLint surface_attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
@ -133,74 +137,96 @@ bool WaylandEGLContext::create_context ()
if (!subsurface) if (!subsurface)
return false; return false;
egl_display = eglGetDisplay ((EGLNativeDisplayType) display); egl_display = eglGetDisplay((EGLNativeDisplayType)display);
eglInitialize (egl_display, NULL, NULL); eglInitialize(egl_display, NULL, NULL);
if (!eglChooseConfig (egl_display, surface_attribs, &egl_config, 1, &num_configs)) if (!eglChooseConfig(egl_display, surface_attribs, &egl_config, 1, &num_configs))
{ {
printf ("Couldn't find matching config.\n"); printf("Couldn't find matching config.\n");
return false; return false;
} }
eglBindAPI (EGL_OPENGL_API); eglBindAPI(EGL_OPENGL_API);
egl_window = wl_egl_window_create (child, width * scale, height * scale); egl_window = wl_egl_window_create(child, width * scale, height * scale);
if (!egl_window) if (!egl_window)
{ {
printf ("Couldn't create window.\n"); printf("Couldn't create window.\n");
return false; return false;
} }
egl_surface = eglCreateWindowSurface (egl_display, egl_config, (EGLNativeWindowType) egl_window, NULL); egl_surface = eglCreateWindowSurface(egl_display, egl_config, (EGLNativeWindowType)egl_window, NULL);
if (!egl_surface) if (!egl_surface)
{ {
printf ("Couldn't create surface.\n"); printf("Couldn't create surface.\n");
return false; return false;
} }
egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, core_context_attribs); egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, core_context_attribs);
if (!egl_context) if (!egl_context)
{ {
egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, compatibility_context_attribs); egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, compatibility_context_attribs);
if (!egl_context) if (!egl_context)
{ {
printf ("Couldn't create context.\n"); printf("Couldn't create context.\n");
return false; return false;
} }
} }
wl_surface_set_buffer_scale (child, scale); if (gui_config->use_sync_control && epoxy_has_egl_extension(egl_display, "EGL_CHROMIUM_sync_control"))
gdk_window_invalidate_rect (gdk_window, NULL, false); {
eglGetSyncValuesCHROMIUM = (PEGLGETSYNCVALUESCHROMIUM)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
if (eglGetSyncValuesCHROMIUM)
use_sync_control = true;
}
wl_surface_set_buffer_scale(child, scale);
gdk_window_invalidate_rect(gdk_window, NULL, false);
return true; return true;
} }
void WaylandEGLContext::resize () void WaylandEGLContext::resize()
{ {
int scale; int scale;
gdk_window_get_geometry (gdk_window, &x, &y, &width, &height); gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
scale = gdk_window_get_scale_factor (gdk_window); scale = gdk_window_get_scale_factor(gdk_window);
wl_egl_window_resize (egl_window, width * scale, height * scale, 0, 0); wl_egl_window_resize(egl_window, width * scale, height * scale, 0, 0);
wl_subsurface_set_position (subsurface, x, y); wl_subsurface_set_position(subsurface, x, y);
make_current (); make_current();
} }
void WaylandEGLContext::swap_buffers () void WaylandEGLContext::swap_buffers()
{ {
eglSwapBuffers (egl_display, egl_surface); if (use_sync_control)
wl_surface_commit (child); eglGetSyncValuesCHROMIUM(egl_display, egl_surface, &ust, &msc, &sbc);
eglSwapBuffers(egl_display, egl_surface);
wl_surface_commit(child);
} }
void WaylandEGLContext::make_current () bool WaylandEGLContext::ready()
{ {
eglMakeCurrent (egl_display, egl_surface, egl_surface, egl_context); if (use_sync_control)
{
EGLuint64KHR ust, msc, sbc;
eglGetSyncValuesCHROMIUM(egl_display, egl_surface, &ust, &msc, &sbc);
if (sbc != this->sbc || msc - this->msc > 2)
return true;
return false;
}
return true;
} }
void WaylandEGLContext::swap_interval (int frames) void WaylandEGLContext::make_current()
{ {
eglSwapInterval (egl_display, frames); eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
} }
void WaylandEGLContext::swap_interval(int frames)
{
eglSwapInterval(egl_display, frames);
}

View File

@ -7,22 +7,24 @@
#ifndef __GTK_WAYLAND_EGL_CONTEXT_H #ifndef __GTK_WAYLAND_EGL_CONTEXT_H
#define __GTK_WAYLAND_EGL_CONTEXT_H #define __GTK_WAYLAND_EGL_CONTEXT_H
#include <wayland-egl.h>
#include <epoxy/egl.h> #include <epoxy/egl.h>
#include <epoxy/egl_generated.h>
#include <wayland-egl.h>
#include "gtk_opengl_context.h" #include "gtk_opengl_context.h"
class WaylandEGLContext : public OpenGLContext class WaylandEGLContext : public OpenGLContext
{ {
public: public:
WaylandEGLContext (); WaylandEGLContext();
~WaylandEGLContext (); ~WaylandEGLContext();
bool attach (GtkWidget *widget); bool attach(GtkWidget *widget);
bool create_context (); bool create_context();
void resize (); void resize();
void swap_buffers (); void swap_buffers();
void swap_interval (int frames); void swap_interval(int frames);
void make_current (); void make_current();
bool ready();
GdkWindow *gdk_window; GdkWindow *gdk_window;
@ -39,9 +41,14 @@ class WaylandEGLContext : public OpenGLContext
EGLDisplay egl_display; EGLDisplay egl_display;
EGLSurface egl_surface; EGLSurface egl_surface;
EGLContext egl_context; EGLContext egl_context;
EGLConfig egl_config; EGLConfig egl_config;
wl_egl_window *egl_window; wl_egl_window *egl_window;
typedef EGLBoolean (*PEGLGETSYNCVALUESCHROMIUM)(EGLDisplay, EGLSurface, EGLuint64KHR *, EGLuint64KHR *, EGLuint64KHR *);
PEGLGETSYNCVALUESCHROMIUM eglGetSyncValuesCHROMIUM;
bool use_sync_control;
EGLuint64KHR ust, msc, sbc;
}; };
#endif #endif

View File

@ -3777,7 +3777,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="sync_every_frame"> <object class="GtkCheckButton" id="use_glfinish">
<property name="label" translatable="yes">Reduce input lag with glFinish</property> <property name="label" translatable="yes">Reduce input lag with glFinish</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
@ -3792,12 +3792,12 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="use_fences"> <object class="GtkCheckButton" id="use_sync_control">
<property name="label" translatable="yes">Reduce input lag with glFenceSync</property> <property name="label" translatable="yes">Reduce input lag with sync control</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Sync the program with the video output after every displayed frame to reduce input latency, but allow GUI events to occur in the meantime</property> <property name="tooltip_text" translatable="yes">More modern method for syncing the program with the video output to reduce input latency. Allows GUI events to occur in the meantime</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>