Skip to content

Commit

Permalink
fix jitter behaviour of skia due to double buffers swapping (#446)
Browse files Browse the repository at this point in the history
* fix jitter behaviour of skia due to double buffers swapping

* fix: fix pylint errors

* fix: add exit method and fix size calls in setup

* Update requirements.txt

* 🎨 Python code fromated with psf/black (#447)

Co-authored-by: tushar5526 <[email protected]>

* update mouse pos logic

* 🎨 Python code fromated with psf/black (#448)

Co-authored-by: tushar5526 <[email protected]>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: tushar5526 <[email protected]>
  • Loading branch information
3 people authored Nov 17, 2023
1 parent 544ed95 commit 495fe09
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 39 deletions.
69 changes: 37 additions & 32 deletions p5/sketch/Skia2DRenderer/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import builtins
from p5.core import p5

import contextlib, glfw, skia
import skia
from OpenGL import GL
from time import time

import copy
from ..events import handler_names
Expand Down Expand Up @@ -76,20 +74,14 @@ def clean_up(self):
def glfw_window(self):
if not glfw.init():
raise RuntimeError("glfw.init() failed")

window = glfw.create_window(*self._size, "p5py", None, None)
glfw.make_context_current(window)
return window

def skia_surface(self, window=None, size=None):
def skia_surface(self):
self.context = skia.GrDirectContext.MakeGL()
if size:
width, height = size
elif window:
width, height = glfw.get_framebuffer_size(window)
else:
raise ValueError(
"Both window and size can't be None, This is probably an error within p5 instead of the sketch"
)
width, height = glfw.get_framebuffer_size(self.window)
backend_render_target = skia.GrBackendRenderTarget(
width,
height,
Expand All @@ -108,12 +100,10 @@ def skia_surface(self, window=None, size=None):
return surface

# create a new surface everytime
def create_surface(self, size=None):
if not size:
size = self._size
self._size = size
builtins.width, builtins.height = size
self.surface = self.skia_surface(self.window, size)
def create_surface(self):
self._size = glfw.get_framebuffer_size(self.window)
builtins.width, builtins.height = self._size
self.surface = self.skia_surface()
self.canvas = self.surface.getCanvas()
p5.renderer.initialize_renderer(self.canvas, self.paint, self.path)

Expand All @@ -133,8 +123,11 @@ def main_loop(self):
builtins.frame_count += 1
with self.surface as self.canvas:
self.draw_method()

p5.renderer._store_surface_state()
self.surface.flushAndSubmit()
glfw.swap_buffers(self.window)
p5.renderer._restore_surface_state()
last_render_call_time = time()

# If redraw == True, we have rendered the frame once
Expand All @@ -157,27 +150,39 @@ def start(self):
self.window = self.glfw_window()
self.create_surface()
self.assign_callbacks()

p5.renderer.initialize_renderer(self.canvas, self.paint, self.path)

# We don't draw the buffer from scratch each time, instead store the current state of surface
# and restore it after swapping the buffer
self.setup_method()
self.poll_events()
p5.renderer.render()

# Get snapshot of surface
p5.renderer._store_surface_state()

# Write to secondary buffer
self.surface.flushAndSubmit()
glfw.swap_buffers(self.window)

p5.renderer._restore_surface_state()
self.surface.flushAndSubmit()

# Buffers are swapped twice so that both buffers have the same initial surface state
glfw.swap_buffers(self.window)

self.main_loop()
self.clean_up()

def resize(self):
# when glfw changes the framebuffer size, we will be resized completely
# until then hold the rendering calls
self.resized = False

# call change the window size(), this will not be done instantly
# but after some time and a frame_buffer_changed callback will occur on
# on a different thread
glfw.set_window_size(self.window, *self.size)

# when glfw changes the framebuffer size, we will be resized completely
# until then hold the rendering calls
self.resized = False

def poll_events(self):
glfw.poll_events()
if glfw.get_key(
Expand Down Expand Up @@ -216,20 +221,16 @@ def frame_buffer_resize_callback_handler(self, window, width, height):
# Creates an Image of current surface and a copy of current style configurations
# For the purpose of handling setup_method() re-call
# Ref: Issue #419
old_image = self.surface.makeImageSnapshot()
old_image = old_image.resize(old_image.width(), old_image.height())
old_style = copy.deepcopy(p5.renderer.style)

GL.glViewport(0, 0, width, height)
self.create_surface(size=(width, height))
self.create_surface()
self.setup_method()
p5.renderer._store_surface_state()
self.surface.flushAndSubmit()
glfw.swap_buffers(self.window)

# Redraws Image on the canvas/ new frame buffer
# Previously stored style configurations are restored for
# discarding setup_method() style changes
p5.renderer.style = old_style
with self.surface as self.canvas:
self.canvas.drawImage(old_image, 0, 0)

# Tell the program, we have resized the frame buffer
# and do not rewind/clear the path
Expand All @@ -238,3 +239,7 @@ def frame_buffer_resize_callback_handler(self, window, width, height):

def _enqueue_event(self, handler_name, event):
self.handler_queue.append((self.handlers[handler_name], event))

def exit(self):
self.clean_up()
exit()
19 changes: 15 additions & 4 deletions p5/sketch/Skia2DRenderer/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


import builtins
import platform
import glfw
from p5.core import p5
from p5.sketch.events import KeyEvent, MouseEvent
Expand Down Expand Up @@ -130,8 +131,7 @@ def __init__(


def on_mouse_button(window, button, action, mod):
pos = glfw.get_cursor_pos(window)

pos = _adjust_mouse_pos(window, glfw.get_cursor_pos(window))
if button < 3:
button = BUTTONMAP.get(button, 0)

Expand All @@ -152,7 +152,7 @@ def on_mouse_button(window, button, action, mod):


def on_mouse_scroll(window, x_off, y_off):
pos = glfw.get_cursor_pos(window)
pos = _adjust_mouse_pos(window, glfw.get_cursor_pos(window))
delta = (float(x_off), float(y_off))
event = PseudoMouseEvent(pos=pos, delta=delta, modifiers=input_state.modifiers)
mev = MouseEvent(event, active=builtins.mouse_is_pressed)
Expand All @@ -161,7 +161,9 @@ def on_mouse_scroll(window, x_off, y_off):


def on_mouse_motion(window, x, y):
event = PseudoMouseEvent(pos=(x, y), modifiers=input_state.modifiers)
event = PseudoMouseEvent(
_adjust_mouse_pos(window, (x, y)), modifiers=input_state.modifiers
)
mev = MouseEvent(event, active=builtins.mouse_is_pressed)

# Queue a 'mouse_dragged` or `mouse_moved` event, not both similar to p5.js
Expand Down Expand Up @@ -220,3 +222,12 @@ def on_window_focus(window, focused):

def on_close(window):
pass


def _adjust_mouse_pos(window, pos):
if platform.system() != "Darwin":
return pos
glfw.get_window_content_scale(window)
pos_x, pos_y = pos
mul_x, mul_y = glfw.get_window_content_scale(window)
return pos_x * mul_x, pos_y * mul_y
9 changes: 9 additions & 0 deletions p5/sketch/Skia2DRenderer/renderer2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ def __init__(self):
self.path = None
self.curve_tightness = 0
self.pimage = None
# used to store the surface state before swapping with secondary buffer
self._surface_image = None

# TODO: Optimise it using bitmap or pixmap
def _store_surface_state(self):
self._surface_image = self.canvas.getSurface().makeImageSnapshot()

def _restore_surface_state(self):
self.canvas.drawImage(self._surface_image, 0, 0)

# Transforms functions
def push_matrix(self):
Expand Down
3 changes: 1 addition & 2 deletions p5/sketch/userspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,7 @@ def exit():
before exiting the sketch.
"""
if p5.sketch is not None and builtins.current_renderer == "vispy":
p5.sketch.exit()
p5.sketch.exit()


def no_cursor():
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ glfw>=2.5.9
numpy
Pillow==9.0.1
vispy==0.10.0
PyOpenGL-accelerate==3.1.6
PyOpenGL-accelerate==3.1.7
PyOpenGL==3.1.6
requests>=2.25.0
dataclasses;python_version=="3.6"
Expand Down

0 comments on commit 495fe09

Please sign in to comment.