Skip to content

Commit

Permalink
Fixed issue #333: Moved frame limiter from open_gl and sdl2 window to…
Browse files Browse the repository at this point in the history
… PyBoyWindowPlugin
  • Loading branch information
Daivik Bhatia authored and Baekalfen committed Sep 12, 2024
1 parent cfef332 commit b52a84a
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 47 deletions.
19 changes: 6 additions & 13 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,8 @@ <h2 id="kwargs">Kwargs</h2>

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
Expand All @@ -1055,11 +1056,6 @@ <h2 id="kwargs">Kwargs</h2>
Args:
target_speed (int): Target emulation speed as multiplier of real-time.
&#34;&#34;&#34;
if self.initialized and self._plugin_manager.window_null_enabled:
logger.warning(
&#39;This window type does not support frame-limiting. `pyboy.set_emulation_speed(...)` will have no effect, as it\&#39;s always running at full speed.&#39;
)

if target_speed &gt; 5:
logger.warning(&#34;The emulation speed might not be accurate when speed-target is higher than 5&#34;)
self.target_emulationspeed = target_speed
Expand Down Expand Up @@ -2343,7 +2339,8 @@ <h2 id="returns">Returns</h2>
<code>target_speed</code>.</p>
<p>The speed is defined as a multiple of real-time. I.e <code>target_speed=2</code> is double speed.</p>
<p>A <code>target_speed</code> of <code>0</code> means unlimited. I.e. fastest possible execution.</p>
<p>Some window types do not implement a frame-limiter, and will always run at full speed.</p>
<p>Due to backwards compatibility, the null window starts at unlimited speed (i.e. <code>target_speed=0</code>), while
others start at realtime (i.e. <code>target_speed=1</code>).</p>
<p>Example:</p>
<pre><code class="language-python">&gt;&gt;&gt; pyboy.tick() # Delays 16.67ms
True
Expand All @@ -2369,7 +2366,8 @@ <h2 id="args">Args</h2>

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
Expand All @@ -2383,11 +2381,6 @@ <h2 id="args">Args</h2>
Args:
target_speed (int): Target emulation speed as multiplier of real-time.
&#34;&#34;&#34;
if self.initialized and self._plugin_manager.window_null_enabled:
logger.warning(
&#39;This window type does not support frame-limiting. `pyboy.set_emulation_speed(...)` will have no effect, as it\&#39;s always running at full speed.&#39;
)

if target_speed &gt; 5:
logger.warning(&#34;The emulation speed might not be accurate when speed-target is higher than 5&#34;)
self.target_emulationspeed = target_speed</code></pre>
Expand Down
12 changes: 11 additions & 1 deletion docs/plugins/base_plugin.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ <h1 class="title">Module <code>pyboy.plugins.base_plugin</code></h1>

import io
import random
import time
from array import array

import numpy as np
Expand Down Expand Up @@ -92,6 +93,8 @@ <h1 class="title">Module <code>pyboy.plugins.base_plugin</code></h1>
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

Expand All @@ -110,7 +113,14 @@ <h1 class="title">Module <code>pyboy.plugins.base_plugin</code></h1>
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 &gt; now):
delay = (self._ftime - now) // 1_000_000
time.sleep(delay / 1000)
else:
self._ftime = now
return True

def set_title(self, title):
pass
Expand Down
3 changes: 2 additions & 1 deletion pyboy/plugins/base_plugin.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
12 changes: 11 additions & 1 deletion pyboy/plugins/base_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import io
import random
import time
from array import array

import numpy as np
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions pyboy/plugins/window_null.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
1 change: 0 additions & 1 deletion pyboy/plugins/window_open_gl.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
12 changes: 1 addition & 11 deletions pyboy/plugins/window_open_gl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down
1 change: 0 additions & 1 deletion pyboy/plugins/window_sdl2.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 0 additions & 12 deletions pyboy/plugins/window_sdl2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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
Expand Down
8 changes: 2 additions & 6 deletions pyboy/pyboy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit b52a84a

Please sign in to comment.