Merge pull request #2159 from 3b1b/video-work

Energy savings
This commit is contained in:
Grant Sanderson
2024-08-15 12:08:51 -07:00
committed by GitHub
4 changed files with 52 additions and 15 deletions

View File

@ -124,6 +124,8 @@ class Camera(object):
def clear(self) -> None:
self.fbo.clear(*self.background_rgba)
if self.window:
self.window.clear()
def blit(self, src_fbo, dst_fbo):
"""
@ -227,8 +229,11 @@ class Camera(object):
self.fbo.use()
for mobject in mobjects:
mobject.render(self.ctx, self.uniforms)
if self.window is not None and self.fbo is not self.window_fbo:
self.blit(self.fbo, self.window_fbo)
if self.window:
self.window.swap_buffers()
if self.fbo is not self.window_fbo:
self.blit(self.fbo, self.window_fbo)
def refresh_uniforms(self) -> None:
frame = self.frame

View File

@ -214,7 +214,7 @@ class Scene(object):
) -> None:
if not self.preview:
# Embed is only relevant with a preview
return
return
self.stop_skipping()
self.update_frame()
self.save_state()
@ -269,7 +269,7 @@ class Scene(object):
# Operation to run after each ipython command
def post_cell_func(*args, **kwargs):
if not self.is_window_closing():
self.update_frame(dt=0, ignore_skipping=True)
self.update_frame(dt=0, force_draw=True)
shell.events.register("post_run_cell", post_cell_func)
@ -313,28 +313,30 @@ class Scene(object):
return image
def show(self) -> None:
self.update_frame(ignore_skipping=True)
self.update_frame(force_draw=True)
self.get_image().show()
def update_frame(self, dt: float = 0, ignore_skipping: bool = False) -> None:
def update_frame(self, dt: float = 0, force_draw: bool = False) -> None:
self.increment_time(dt)
self.update_mobjects(dt)
if self.skip_animations and not ignore_skipping:
if self.skip_animations and not force_draw:
return
if self.is_window_closing():
raise EndScene()
if self.window:
self.window.clear()
if self.window and dt == 0 and not self.window.has_undrawn_event() and not force_draw:
# In this case, there's no need for new rendering, but we
# shoudl still listen for new events
self.window._window.dispatch_events()
return
self.camera.capture(*self.render_groups)
if self.window:
self.window.swap_buffers()
vt = self.time - self.virtual_animation_start_time
rt = time.time() - self.real_animation_start_time
if rt < vt:
self.update_frame(0)
time.sleep(max(vt - rt, 0))
def emit_frame(self) -> None:
if not self.skip_animations:
@ -530,6 +532,7 @@ class Scene(object):
def stop_skipping(self) -> None:
self.virtual_animation_start_time = self.time
self.real_animation_start_time = time.time()
self.skip_animations = False
# Methods associated with running animations
@ -596,8 +599,8 @@ class Scene(object):
self.file_writer.begin_animation()
if self.window:
self.real_animation_start_time = time.time()
self.virtual_animation_start_time = self.time
self.real_animation_start_time = time.time()
def post_play(self):
if not self.skip_animations:
@ -605,7 +608,7 @@ class Scene(object):
if self.skip_animations and self.window is not None:
# Show some quick frames along the way
self.update_frame(dt=0, ignore_skipping=True)
self.update_frame(dt=0, force_draw=True)
self.num_plays += 1

View File

@ -182,7 +182,7 @@ def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray]:
if(color.a == 0) discard;
// Counteract scaling in fill frag
color.a *= 1.06;
color *= 1.06;
gl_FragDepth = texture(DepthTexture, uv)[0];
}

View File

@ -6,6 +6,7 @@ import moderngl_window as mglw
from moderngl_window.context.pyglet.window import Window as PygletWindow
from moderngl_window.timers.clock import Timer
from screeninfo import get_monitors
from functools import wraps
from manimlib.constants import FRAME_SHAPE
from manimlib.utils.customization import get_customization
@ -39,6 +40,8 @@ class Window(PygletWindow):
self.title = str(scene)
self.size = size
self._has_undrawn_event = True
mglw.activate_context(window=self)
self.timer = Timer()
self.config = mglw.WindowConfig(ctx=self.ctx, wnd=self, timer=self.timer)
@ -95,56 +98,82 @@ class Window(PygletWindow):
coords[:2] -= 0.5 * fixed_frame_shape
return frame.from_fixed_frame_point(coords, relative)
def has_undrawn_event(self) -> bool:
return self._has_undrawn_event
def swap_buffers(self):
super().swap_buffers()
self._has_undrawn_event = False
@staticmethod
def note_undrawn_event(func: Callable[..., T]) -> Callable[..., T]:
@wraps(func)
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
self._has_undrawn_event = True
return wrapper
@note_undrawn_event
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> None:
super().on_mouse_motion(x, y, dx, dy)
point = self.pixel_coords_to_space_coords(x, y)
d_point = self.pixel_coords_to_space_coords(dx, dy, relative=True)
self.scene.on_mouse_motion(point, d_point)
@note_undrawn_event
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int) -> None:
super().on_mouse_drag(x, y, dx, dy, buttons, modifiers)
point = self.pixel_coords_to_space_coords(x, y)
d_point = self.pixel_coords_to_space_coords(dx, dy, relative=True)
self.scene.on_mouse_drag(point, d_point, buttons, modifiers)
@note_undrawn_event
def on_mouse_press(self, x: int, y: int, button: int, mods: int) -> None:
super().on_mouse_press(x, y, button, mods)
point = self.pixel_coords_to_space_coords(x, y)
self.scene.on_mouse_press(point, button, mods)
@note_undrawn_event
def on_mouse_release(self, x: int, y: int, button: int, mods: int) -> None:
super().on_mouse_release(x, y, button, mods)
point = self.pixel_coords_to_space_coords(x, y)
self.scene.on_mouse_release(point, button, mods)
@note_undrawn_event
def on_mouse_scroll(self, x: int, y: int, x_offset: float, y_offset: float) -> None:
super().on_mouse_scroll(x, y, x_offset, y_offset)
point = self.pixel_coords_to_space_coords(x, y)
offset = self.pixel_coords_to_space_coords(x_offset, y_offset, relative=True)
self.scene.on_mouse_scroll(point, offset, x_offset, y_offset)
@note_undrawn_event
def on_key_press(self, symbol: int, modifiers: int) -> None:
self.pressed_keys.add(symbol) # Modifiers?
super().on_key_press(symbol, modifiers)
self.scene.on_key_press(symbol, modifiers)
@note_undrawn_event
def on_key_release(self, symbol: int, modifiers: int) -> None:
self.pressed_keys.difference_update({symbol}) # Modifiers?
super().on_key_release(symbol, modifiers)
self.scene.on_key_release(symbol, modifiers)
@note_undrawn_event
def on_resize(self, width: int, height: int) -> None:
super().on_resize(width, height)
self.scene.on_resize(width, height)
@note_undrawn_event
def on_show(self) -> None:
super().on_show()
self.scene.on_show()
@note_undrawn_event
def on_hide(self) -> None:
super().on_hide()
self.scene.on_hide()
@note_undrawn_event
def on_close(self) -> None:
super().on_close()
self.scene.on_close()