From b52a84a5ece9b66295340b97540bb672ebd0b637 Mon Sep 17 00:00:00 2001
From: Daivik Bhatia
Date: Sat, 17 Aug 2024 19:21:18 +0530
Subject: [PATCH] Fixed issue #333: Moved frame limiter from open_gl and sdl2
window to PyBoyWindowPlugin
---
docs/index.html | 19 ++++++-------------
docs/plugins/base_plugin.html | 12 +++++++++++-
pyboy/plugins/base_plugin.pxd | 3 ++-
pyboy/plugins/base_plugin.py | 12 +++++++++++-
pyboy/plugins/window_null.py | 2 ++
pyboy/plugins/window_open_gl.pxd | 1 -
pyboy/plugins/window_open_gl.py | 12 +-----------
pyboy/plugins/window_sdl2.pxd | 1 -
pyboy/plugins/window_sdl2.py | 12 ------------
pyboy/pyboy.py | 8 ++------
10 files changed, 35 insertions(+), 47 deletions(-)
diff --git a/docs/index.html b/docs/index.html
index 175e11905..30dc01166 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1041,7 +1041,8 @@ Kwargs
A `target_speed` of `0` means unlimited. I.e. fastest possible execution.
- Some window types do not implement a frame-limiter, and will always run at full speed.
+ Due to backwards compatibility, the null window starts at unlimited speed (i.e. `target_speed=0`), while
+ others start at realtime (i.e. `target_speed=1`).
Example:
```python
@@ -1055,11 +1056,6 @@ Kwargs
Args:
target_speed (int): Target emulation speed as multiplier of real-time.
"""
- if self.initialized and self._plugin_manager.window_null_enabled:
- logger.warning(
- 'This window type does not support frame-limiting. `pyboy.set_emulation_speed(...)` will have no effect, as it\'s always running at full speed.'
- )
-
if target_speed > 5:
logger.warning("The emulation speed might not be accurate when speed-target is higher than 5")
self.target_emulationspeed = target_speed
@@ -2343,7 +2339,8 @@ Returns
target_speed
.
The speed is defined as a multiple of real-time. I.e target_speed=2
is double speed.
A target_speed
of 0
means unlimited. I.e. fastest possible execution.
-Some window types do not implement a frame-limiter, and will always run at full speed.
+Due to backwards compatibility, the null window starts at unlimited speed (i.e. target_speed=0
), while
+others start at realtime (i.e. target_speed=1
).
Example:
>>> pyboy.tick() # Delays 16.67ms
True
@@ -2369,7 +2366,8 @@ Args
A `target_speed` of `0` means unlimited. I.e. fastest possible execution.
- Some window types do not implement a frame-limiter, and will always run at full speed.
+ Due to backwards compatibility, the null window starts at unlimited speed (i.e. `target_speed=0`), while
+ others start at realtime (i.e. `target_speed=1`).
Example:
```python
@@ -2383,11 +2381,6 @@ Args
Args:
target_speed (int): Target emulation speed as multiplier of real-time.
"""
- if self.initialized and self._plugin_manager.window_null_enabled:
- logger.warning(
- 'This window type does not support frame-limiting. `pyboy.set_emulation_speed(...)` will have no effect, as it\'s always running at full speed.'
- )
-
if target_speed > 5:
logger.warning("The emulation speed might not be accurate when speed-target is higher than 5")
self.target_emulationspeed = target_speed
diff --git a/docs/plugins/base_plugin.html b/docs/plugins/base_plugin.html
index f10207b0c..26e59efb6 100644
--- a/docs/plugins/base_plugin.html
+++ b/docs/plugins/base_plugin.html
@@ -40,6 +40,7 @@ Module pyboy.plugins.base_plugin
import io
import random
+import time
from array import array
import numpy as np
@@ -92,6 +93,8 @@ Module pyboy.plugins.base_plugin
def __init__(self, pyboy, mb, pyboy_argv, *args, **kwargs):
super().__init__(pyboy, mb, pyboy_argv, *args, **kwargs)
+ self._ftime = time.perf_counter_ns()
+
if not self.enabled():
return
@@ -110,7 +113,14 @@ Module pyboy.plugins.base_plugin
self.renderer = self.mb.lcd.renderer
def frame_limiter(self, speed):
- return False
+ self._ftime += int((1.0 / (60.0*speed)) * 1_000_000_000)
+ now = time.perf_counter_ns()
+ if (self._ftime > now):
+ delay = (self._ftime - now) // 1_000_000
+ time.sleep(delay / 1000)
+ else:
+ self._ftime = now
+ return True
def set_title(self, title):
pass
diff --git a/pyboy/plugins/base_plugin.pxd b/pyboy/plugins/base_plugin.pxd
index 6f555480c..cc65f0ca3 100644
--- a/pyboy/plugins/base_plugin.pxd
+++ b/pyboy/plugins/base_plugin.pxd
@@ -6,7 +6,7 @@
cimport cython
cimport numpy as cnp
from cpython.array cimport array
-from libc.stdint cimport uint8_t, uint16_t, uint32_t
+from libc.stdint cimport uint8_t, uint16_t, uint32_t, int64_t
from pyboy.core.lcd cimport Renderer
from pyboy.core.mb cimport Motherboard
@@ -38,6 +38,7 @@ cdef class PyBoyWindowPlugin(PyBoyPlugin):
cdef bint enable_title
cdef Renderer renderer
+ cdef int64_t _ftime
cdef bint frame_limiter(self, int) noexcept
cdef void set_title(self, str) noexcept
diff --git a/pyboy/plugins/base_plugin.py b/pyboy/plugins/base_plugin.py
index 71d737bf1..295285266 100644
--- a/pyboy/plugins/base_plugin.py
+++ b/pyboy/plugins/base_plugin.py
@@ -13,6 +13,7 @@
import io
import random
+import time
from array import array
import numpy as np
@@ -65,6 +66,8 @@ class PyBoyWindowPlugin(PyBoyPlugin):
def __init__(self, pyboy, mb, pyboy_argv, *args, **kwargs):
super().__init__(pyboy, mb, pyboy_argv, *args, **kwargs)
+ self._ftime = time.perf_counter_ns()
+
if not self.enabled():
return
@@ -83,7 +86,14 @@ def __cinit__(self, *args, **kwargs):
self.renderer = self.mb.lcd.renderer
def frame_limiter(self, speed):
- return False
+ self._ftime += int((1.0 / (60.0*speed)) * 1_000_000_000)
+ now = time.perf_counter_ns()
+ if (self._ftime > now):
+ delay = (self._ftime - now) // 1_000_000
+ time.sleep(delay / 1000)
+ else:
+ self._ftime = now
+ return True
def set_title(self, title):
pass
diff --git a/pyboy/plugins/window_null.py b/pyboy/plugins/window_null.py
index 2257db97d..783dd20d8 100644
--- a/pyboy/plugins/window_null.py
+++ b/pyboy/plugins/window_null.py
@@ -22,6 +22,8 @@ def __init__(self, pyboy, mb, pyboy_argv):
'Deprecated use of "headless" or "dummy" window. Change to "null" window instead. https://github.com/Baekalfen/PyBoy/wiki/Migrating-from-v1.x.x-to-v2.0.0'
)
+ pyboy.set_emulation_speed(0)
+
def enabled(self):
return self.pyboy_argv.get("window") in ["null", "headless", "dummy"]
diff --git a/pyboy/plugins/window_open_gl.pxd b/pyboy/plugins/window_open_gl.pxd
index 3d5c9203f..e13dd9101 100644
--- a/pyboy/plugins/window_open_gl.pxd
+++ b/pyboy/plugins/window_open_gl.pxd
@@ -22,7 +22,6 @@ cdef int ROWS, COLS
cdef class WindowOpenGL(PyBoyWindowPlugin):
cdef list events
- cdef int64_t _ftime
cdef void _glkeyboard(self, str, int, int, bint) noexcept
cdef void _glkeyboardspecial(self, char, int, int, bint) noexcept
diff --git a/pyboy/plugins/window_open_gl.py b/pyboy/plugins/window_open_gl.py
index 7374c3b40..fae5c887d 100644
--- a/pyboy/plugins/window_open_gl.py
+++ b/pyboy/plugins/window_open_gl.py
@@ -50,7 +50,7 @@ def __init__(self, pyboy, mb, pyboy_argv):
glPixelZoom(self.scale, self.scale)
glutReshapeFunc(self._glreshape)
glutDisplayFunc(self._gldraw)
- self._ftime = time.perf_counter_ns()
+
# Cython does not cooperate with lambdas
def _key(self, c, x, y):
@@ -139,16 +139,6 @@ def _gldraw(self):
glDrawPixels(COLS, ROWS, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, buf)
glFlush()
- def frame_limiter(self, speed):
- self._ftime += int((1.0 / (60.0*speed)) * 1_000_000_000)
- now = time.perf_counter_ns()
- if (self._ftime > now):
- delay = (self._ftime - now) // 1_000_000
- time.sleep(delay / 1000)
- else:
- self._ftime = now
- return True
-
def enabled(self):
if self.pyboy_argv.get("window") == "OpenGL":
if opengl_enabled:
diff --git a/pyboy/plugins/window_sdl2.pxd b/pyboy/plugins/window_sdl2.pxd
index 558d54c0d..4d6cb843b 100644
--- a/pyboy/plugins/window_sdl2.pxd
+++ b/pyboy/plugins/window_sdl2.pxd
@@ -25,7 +25,6 @@ cpdef list sdl2_event_pump(list) noexcept
cdef class WindowSDL2(PyBoyWindowPlugin):
- cdef int64_t _ftime
cdef dict _key_down
cdef dict _key_up
cdef bint fullscreen
diff --git a/pyboy/plugins/window_sdl2.py b/pyboy/plugins/window_sdl2.py
index 7919a45af..6ae03add4 100644
--- a/pyboy/plugins/window_sdl2.py
+++ b/pyboy/plugins/window_sdl2.py
@@ -157,9 +157,7 @@ def __init__(self, pyboy, mb, pyboy_argv):
if not self.enabled():
return
-
sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_GAMECONTROLLER)
- self._ftime = time.perf_counter_ns()
self._window = sdl2.SDL_CreateWindow(
b"PyBoy", sdl2.SDL_WINDOWPOS_CENTERED, sdl2.SDL_WINDOWPOS_CENTERED, self._scaledresolution[0],
@@ -205,16 +203,6 @@ def enabled(self):
else:
return False
- def frame_limiter(self, speed):
- self._ftime += int((1.0 / (60.0*speed)) * 1_000_000_000)
- now = time.perf_counter_ns()
- if (self._ftime > now):
- delay = (self._ftime - now) // 1_000_000
- sdl2.SDL_Delay(delay)
- else:
- self._ftime = now
- return True
-
def stop(self):
sdl2.SDL_DestroyWindow(self._window)
for _ in range(10): # At least 2 to close
diff --git a/pyboy/pyboy.py b/pyboy/pyboy.py
index f2c969134..cf09628b2 100644
--- a/pyboy/pyboy.py
+++ b/pyboy/pyboy.py
@@ -970,7 +970,8 @@ def set_emulation_speed(self, target_speed):
A `target_speed` of `0` means unlimited. I.e. fastest possible execution.
- Some window types do not implement a frame-limiter, and will always run at full speed.
+ Due to backwards compatibility, the null window starts at unlimited speed (i.e. `target_speed=0`), while
+ others start at realtime (i.e. `target_speed=1`).
Example:
```python
@@ -984,11 +985,6 @@ def set_emulation_speed(self, target_speed):
Args:
target_speed (int): Target emulation speed as multiplier of real-time.
"""
- if self.initialized and self._plugin_manager.window_null_enabled:
- logger.warning(
- 'This window type does not support frame-limiting. `pyboy.set_emulation_speed(...)` will have no effect, as it\'s always running at full speed.'
- )
-
if target_speed > 5:
logger.warning("The emulation speed might not be accurate when speed-target is higher than 5")
self.target_emulationspeed = target_speed