clock: implement rfps (real frame displayed on screen)

base: display and flip only when needed (might be have some bugs.)
This commit is contained in:
Mathieu Virbel 2010-11-07 20:43:49 -05:00
parent dd91a1f940
commit e838e13e96
7 changed files with 52 additions and 26 deletions

View File

@ -6,7 +6,8 @@ from kivy.lang import Builder
class KvApp(App): class KvApp(App):
def _print_fps(self, *largs): def _print_fps(self, *largs):
print 'FPS:', Clock.get_fps() print 'FPS: %2.4f (real draw: %d)' % (
Clock.get_fps(), Clock.get_rfps())
def build(self): def build(self):
Clock.schedule_interval(self._print_fps, 1) Clock.schedule_interval(self._print_fps, 1)
return Builder.load_file(self.options['filename']) return Builder.load_file(self.options['filename'])

View File

@ -12,6 +12,7 @@ from kivy.logger import Logger
from kivy.exceptions import ExceptionManager from kivy.exceptions import ExceptionManager
from kivy.clock import Clock from kivy.clock import Clock
from kivy.input import TouchFactory, kivy_postproc_modules from kivy.input import TouchFactory, kivy_postproc_modules
from kivy.graphics import GraphicContext
# private vars # private vars
EventLoop = None EventLoop = None
@ -211,7 +212,9 @@ class EventLoopBase(object):
self.dispatch_input() self.dispatch_input()
window = self.window window = self.window
if window: need_redraw = GraphicContext.instance().need_redraw
if window and need_redraw:
Clock.tick_draw()
window.dispatch('on_draw') window.dispatch('on_draw')
window.dispatch('on_flip') window.dispatch('on_flip')

View File

@ -7,7 +7,7 @@ cdef class GraphicContext:
cdef readonly int need_flush cdef readonly int need_flush
cdef Shader _default_shader cdef Shader _default_shader
cdef object _default_texture cdef object _default_texture
cdef int need_redraw cdef int _need_redraw
cpdef post_update(self) cpdef post_update(self)
cpdef finish_frame(self) cpdef finish_frame(self)

View File

@ -45,18 +45,22 @@ cdef class GraphicContext:
self.journal = set() self.journal = set()
self.need_flush = 0 self.need_flush = 0
self._default_shader = None self._default_shader = None
self._need_redraw = 1
def __init__(self): def __init__(self):
# create initial state # create initial state
self.reset() self.reset()
self.save() self.save()
self.need_redraw = 1
property need_redraw:
def __get__(self):
return self._need_redraw
cpdef post_update(self): cpdef post_update(self):
self.need_redraw = 1 self._need_redraw = 1
cpdef finish_frame(self): cpdef finish_frame(self):
self.need_redraw = 0 self._need_redraw = 0
err = glGetError() err = glGetError()
if err: if err:
Logger.warning('GContext: GL Error while drawing frame: %d' % err) Logger.warning('GContext: GL Error while drawing frame: %d' % err)

View File

@ -17,8 +17,9 @@ If the callback return False, the schedule will be removed.
__all__ = ('Clock', ) __all__ = ('Clock', )
import time from time import time, sleep
from kivy.weakmethod import WeakMethod from kivy.weakmethod import WeakMethod
from kivy.config import Config
class _Event(object): class _Event(object):
@ -63,16 +64,19 @@ class _Event(object):
class ClockBase(object): class ClockBase(object):
'''A clock object, that support events''' '''A clock object, that support events'''
__slots__ = ('_dt', '_last_fps_tick', '_last_tick', '_fps', __slots__ = ('_dt', '_last_fps_tick', '_last_tick', '_fps', '_rfps',
'_fps_counter', '_events') '_fps_counter', '_rfps_counter', '_events', '_max_fps')
def __init__(self): def __init__(self):
self._dt = 0.0001 self._dt = 0.0001
self._last_tick = time.time() self._last_tick = time()
self._fps = 0 self._fps = 0
self._rfps = 0
self._fps_counter = 0 self._fps_counter = 0
self._rfps_counter = 0
self._last_fps_tick = None self._last_fps_tick = None
self._events = [] self._events = []
self._max_fps = float(Config.getint('graphics', 'fps'))
@property @property
def frametime(self): def frametime(self):
@ -83,8 +87,16 @@ class ClockBase(object):
def tick(self): def tick(self):
'''Advance clock to the next step. Must be called every frame. '''Advance clock to the next step. Must be called every frame.
The default clock have the tick() function called by Kivy''' The default clock have the tick() function called by Kivy'''
# do we need to sleep ?
if self._max_fps > 0:
fps = self._max_fps
s = 1 / fps - (time() - self._last_tick)
if s > 0:
sleep(s)
# tick the current time # tick the current time
current = time.time() current = time()
self._dt = current - self._last_tick self._dt = current - self._last_tick
self._fps_counter += 1 self._fps_counter += 1
self._last_tick = current self._last_tick = current
@ -93,19 +105,37 @@ class ClockBase(object):
if self._last_fps_tick == None: if self._last_fps_tick == None:
self._last_fps_tick = current self._last_fps_tick = current
elif current - self._last_fps_tick > 1: elif current - self._last_fps_tick > 1:
self._fps = self._fps_counter / float(current - self._last_fps_tick) d = float(current - self._last_fps_tick)
self._fps = self._fps_counter / d
self._rfps = self._rfps_counter
self._last_fps_tick = current self._last_fps_tick = current
self._fps_counter = 0 self._fps_counter = 0
self._rfps_counter = 0
# process event # process event
self._process_events() self._process_events()
return self._dt return self._dt
def tick_draw(self):
'''Tick the drawing counter
'''
self._rfps_counter += 1
def get_fps(self): def get_fps(self):
'''Get the current FPS calculated by the clock''' '''Get the current FPS calculated by the clock
'''
return self._fps return self._fps
def get_rfps(self):
'''Get the current "real" FPS calculated by the clock.
This counter reflect the real frame displayed on the screen.
In contrary to get_fps(), this function return a counter of the number
of frame, not a average of frame per seconds
'''
return self._rfps
def get_time(self): def get_time(self):
'''Get the last tick made by the clock''' '''Get the last tick made by the clock'''
return self._last_tick return self._last_tick

View File

@ -7,7 +7,6 @@ __all__ = ('WindowPygame', )
from . import WindowBase from . import WindowBase
import os import os
from time import sleep, time
from kivy.config import Config from kivy.config import Config
from kivy.clock import Clock from kivy.clock import Clock
from kivy.exceptions import ExceptionManager from kivy.exceptions import ExceptionManager
@ -138,17 +137,6 @@ class WindowPygame(WindowBase):
pygame.display.flip() pygame.display.flip()
super(WindowPygame, self).flip() super(WindowPygame, self).flip()
# do software vsync if asked
# FIXME: vsync is surely not 60 for everyone
# this is not a real vsync. this must be done by driver...
# but pygame can't do vsync on X11, and some people
# use hack to make it work under darwin...
fps = self._fps
if fps > 0:
s = 1 / fps - (time() - Clock.get_time())
if s > 0:
sleep(s)
def toggle_fullscreen(self): def toggle_fullscreen(self):
if self.flags & pygame.FULLSCREEN: if self.flags & pygame.FULLSCREEN:
self.flags &= ~pygame.FULLSCREEN self.flags &= ~pygame.FULLSCREEN

View File

@ -8,7 +8,7 @@
source: 'data/images/button.png' source: 'data/images/button.png'
Label: Label:
text: root.text text: root.text
font_size: 18 font_size: 12
canvas: canvas:
Color: Color:
rgb: (1, 1, 1) rgb: (1, 1, 1)