3b1b / manim

Animation engine for explanatory math videos
MIT License
60.28k stars 5.7k forks source link

New Manim version that supports GPU #936

Open qo4on opened 4 years ago

qo4on commented 4 years ago

I get an error in Colab trying to run Manim from "shaders" branch. I see tqdm for each animation, that means they are successful. But then it gets into a loop and I have to stop it with the error:

Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/content/manim/manim.py", line 5, in <module>
    manimlib.main()
  File "/content/manim/manimlib/__init__.py", line 14, in main
    scene.run()
  File "/content/manim/manimlib/scene/scene.py", line 77, in run
    self.tear_down()
  File "/content/manim/manimlib/scene/scene.py", line 96, in tear_down
    self.interact()
  File "/content/manim/manimlib/scene/scene.py", line 104, in interact
    self.update_frame()
  File "/content/manim/manimlib/scene/scene.py", line 139, in update_frame
    self.camera.clear()
  File "/content/manim/manimlib/camera/camera.py", line 114, in clear
    rgba = (*Color(self.background_color).get_rgb(), self.background_opacity)
  File "/usr/local/lib/python3.6/dist-packages/colour.py", line 988, in __init__
    self.web = color if color else 'black'
  File "/usr/local/lib/python3.6/dist-packages/colour.py", line 1006, in __setattr__
    fc(value)
  File "/usr/local/lib/python3.6/dist-packages/colour.py", line 1084, in set_web
    self.hex = web2hex(value)
  File "/usr/local/lib/python3.6/dist-packages/colour.py", line 1005, in __setattr__
    fc = getattr(self, 'set_' + label)
KeyboardInterrupt

UPDATE: If you want to try it in Colab, the code is here.

timalive commented 4 years ago

I am getting the same issue when running in docker. Something to do with the fact that pyglet needs a display to run. There may be a solution using Xvfb

qo4on commented 4 years ago

xvfb doesn't work either. We need to change manim window.py backend from pyglet to headless.

from moderngl_window.context.headless.window import Window as HeadlessWindow

But I didn't managed to make it work.

t-wolfeadam commented 4 years ago

Running Win10, I couldn't get it to work when using the preview setting (-p) due to no configs for the window:

Traceback (most recent call last):
  File "C:\Users\Tyler Wolfe-Adam\Documents\Python\manim-shaders\manim.py", line 5, in <module>
    manimlib.main()
  File "C:\Users\Tyler Wolfe-Adam\Documents\Python\manim-shaders\manimlib\__init__.py", line 11, in main
    scenes = manimlib.extract_scene.main(config)
  File "C:\Users\Tyler Wolfe-Adam\Documents\Python\manim-shaders\manimlib\extract_scene.py", line 104, in main
    scenes = get_scenes_to_render(all_scene_classes, config)
  File "C:\Users\Tyler Wolfe-Adam\Documents\Python\manim-shaders\manimlib\extract_scene.py", line 72, in get_scenes_to_render
    scene = scene_class(**scene_kwargs)
  File "C:\Users\Tyler Wolfe-Adam\Documents\Python\manim-shaders\manimlib\scene\scene.py", line 44, in __init__
    self.window = Window(self, **self.window_config)
  File "C:\Users\Tyler Wolfe-Adam\Documents\Python\manim-shaders\manimlib\window.py", line 21, in __init__
    super().__init__(**kwargs)
  File "C:\Program Files\Python37\lib\site-packages\moderngl_window\context\pyglet\window.py", line 54, in __init__
    config=config,
  File "C:\Program Files\Python37\lib\site-packages\pyglet\window\win32\__init__.py", line 130, in __init__
    super(Win32Window, self).__init__(*args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\pyglet\window\__init__.py", line 591, in __init__
    config = screen.get_best_config(config)
  File "C:\Program Files\Python37\lib\site-packages\pyglet\canvas\base.py", line 194, in get_best_config
    raise window.NoSuchConfigException()
pyglet.window.NoSuchConfigException

It does work when just writing to a file (-w). However, just for the shape examples. Anything with text doesn't work due to (unrelated) issues with dvisvgm not converting the dvi to svg.

qo4on commented 4 years ago

What text doesn't work? tex_mobject or text_mobject?

t-wolfeadam commented 4 years ago

What text doesn't work? tex_mobject or text_mobject?

Both, unfortunately; I also tried modifying the example scenes with just using one or the other. Both result in OSError: No file matching ./media\Tex\ab47de5016aed0d4.svg in image directory.

I dug into it and it stems from the dvisvgm command (called from the dvi_to_svg function inside \manimlib\utils\tex_file_writing.py) - the command executes successfully but nothing gets outputted (no files or messages, even with the highest level of verbosity). I even tried searching my entire file system for the file and still nothing. It's really weird.

I'm currently getting it setup to try on WSL. Hopefully that works better. No luck on googling the problem either. I'll probably open a new thread somewhere if it doesn't work with WSL :/

Edit: The problem with dvisvgm seems to be a Windows thing. I found out it's returning exit code -1073740791, which is apparently a stack buffer overflow. dvisvgm works fine on WSL, so for now I've implemented a hacky solution where Windows waits until Ubuntu has converted the necessary files.

Edit 2: Found the fix! For future readers: Running dvisvgm --version reported 2.7. I ran where dvisvgm and it reported two versions: the v2.7 (installed in Program Files) showed before the other, which was part of MikTex and version 2.8.2. Removing the v2.7 copy fixed this issue. I am now able to render videos in this branch, as long as I'm writing to file (w flag) and not showing preview (p flag).

t-wolfeadam commented 4 years ago

Update: Ran into the same problems with the display. Tried using xvfb like so: xvfb-run -s "-screen 0 640x360x24" python3.7 -m manim example_scenes.py SquareToCircle -ql That returned the same error for me as when I tried to use the q flag on Windows:

Traceback (most recent call last):
  File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/tyler/Documents/manim/manim.py", line 5, in <module>
    manimlib.main()
  File "/home/tyler/Documents/manim/manimlib/__init__.py", line 11, in main
    scenes = manimlib.extract_scene.main(config)
  File "/home/tyler/Documents/manim/manimlib/extract_scene.py", line 104, in main
    scenes = get_scenes_to_render(all_scene_classes, config)
  File "/home/tyler/Documents/manim/manimlib/extract_scene.py", line 72, in get_scenes_to_render
    scene = scene_class(**scene_kwargs)
  File "/home/tyler/Documents/manim/manimlib/scene/scene.py", line 44, in __init__
    self.window = Window(self, **self.window_config)
  File "/home/tyler/Documents/manim/manimlib/window.py", line 21, in __init__
    super().__init__(**kwargs)
  File "/home/tyler/.local/lib/python3.7/site-packages/moderngl_window/context/pyglet/window.py", line 54, in __init__
    config=config,
  File "/home/tyler/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 171, in __init__
    super(XlibWindow, self).__init__(*args, **kwargs)
  File "/home/tyler/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 591, in __init__
    config = screen.get_best_config(config)
  File "/home/tyler/.local/lib/python3.7/site-packages/pyglet/canvas/base.py", line 194, in get_best_config
    raise window.NoSuchConfigException()
pyglet.window.NoSuchConfigException

Tried again with the w flag, but then got the following error:


Media will be written to ./media/. You can change this behavior with the --media_dir flag.
Traceback (most recent call last):
  File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/tyler/Documents/manim/manim.py", line 5, in <module>
    manimlib.main()
  File "/home/tyler/Documents/manim/manimlib/__init__.py", line 14, in main
    scene.run()
  File "/home/tyler/Documents/manim/manimlib/scene/scene.py", line 74, in run
    self.construct()
  File "example_scenes.py", line 84, in construct
    self.play(Transform(square, circle))
  File "/home/tyler/Documents/manim/manimlib/scene/scene.py", line 357, in wrapper
    func(self, *args, **kwargs)
  File "/home/tyler/Documents/manim/manimlib/scene/scene.py", line 425, in play
    self.progress_through_animations(animations)
  File "/home/tyler/Documents/manim/manimlib/scene/scene.py", line 402, in progress_through_animations
    self.update_frame(dt)
  File "/home/tyler/Documents/manim/manimlib/scene/scene.py", line 140, in update_frame
    self.camera.capture(*self.mobjects)
  File "/home/tyler/Documents/manim/manimlib/camera/camera.py", line 228, in capture
    shader = self.get_shader(info_group[0])
  File "/home/tyler/Documents/manim/manimlib/camera/camera.py", line 253, in get_shader
    fragment_shader=get_shader_code_from_file(info["frag"]),
  File "/home/tyler/.local/lib/python3.7/site-packages/moderngl/context.py", line 1037, in program
    varyings
moderngl.error.Error: GLSL Compiler failed

fragment_shader
===============
0:179(24): error: `return' with wrong type int, in function `sdf' returning float
``
qo4on commented 4 years ago

You can't get hardware acceleration with virtual displays such as xvfb. Can you render SquareToCircle in shaders version correctly?

timalive commented 4 years ago

I was able to do it on mac - not in docker

qo4on commented 4 years ago

Can you try to run it in Colab? I had no success there.

einarf commented 4 years ago

Lengthy issue about this in the modengl project: https://github.com/moderngl/moderngl/issues/392 We do actually get it running in Google Colab with EGL, but it gets stuck in an infinite loop. That I think is a flow issue in the Manim code itself.. or at least it needs to be debugged properly to make a conclusion.

Manim is using moderngl and moderngl-window. I think the main issues is that it extends the pyglet window backend directly. If headless rendering is needed it should use the headless.Window instead.

The BaseWindow.init_mgl_context should be probably also be overridden to create the correct context type or I can add some features for this in the moderngl-window project if we figure out what is needed. Right now it tries to detect the context pyglet creates**, but in headless we just need to make our own context with no window (X11 or EGL).

It's not that complicated to fix thankfully.

I am willing to help out giving pointers for the moderngl specific things, but I don't really know much about Manim (at least yet).

t-wolfeadam commented 4 years ago

You can't get hardware acceleration with virtual displays such as xvfb. Can you render SquareToCircle in shaders version correctly?

Good to know! I can in Windows, but not in the Ubuntu subsystem. For both the q and w flags I get the pyglet.canvas.xlib.NoSuchDisplayException: Cannot connect to "None" exception.

einarf commented 4 years ago

I released a new version of moderngl-window what should at least solve the NoSuchConfig for a lot of people. You maye also have to go into the window.py module and set samples = 0.

@t-wolfeadam The shader error you are getting is an error in the glsl code here : https://github.com/3b1b/manim/blob/2ce0b72c440fadbfba990bdac8bc9c2ea8bff4e7/manimlib/shaders/quadratic_bezier_fill_frag.glsl#L41

It should return a float, not an int. Not all GLSL compilers are nice and convert constants for you automatically. I would fix that line and test it again. Make a PR as well while you are at it (if it works) πŸ˜„

You may see more of those, but they are quick and easy to fix if you search for the variable or function names reported by the GLSL compiler.

t-wolfeadam commented 4 years ago

I released a new version of moderngl-window what should at least solve the NoSuchConfig for a lot of people. You may also have to go into the window.py module and set samples = 0.

Fantastic, thanks! I can confirm for Windows that updating + setting samples to 0 fixed it!

@t-wolfeadam The shader error you are getting is an error in the glsl code here :

https://github.com/3b1b/manim/blob/2ce0b72c440fadbfba990bdac8bc9c2ea8bff4e7/manimlib/shaders/quadratic_bezier_fill_frag.glsl#L41

It should return a float, not an int. Not all GLSL compilers are nice and convert constants for you automatically. I would fix that line and test it again. Make a PR as well while you are at it (if it works) πŸ˜„

Ahh, so that's what sdf was referring to. That also did the trick, so thank you for that as well! (and fortunately, it was the only instance).

Edit: looks like someone already made the PR for that issue

einarf commented 4 years ago

@t-wolfeadam Yep. We just sorted out a PR after discussing the issue on the manim discord server πŸ‘

kolibril13 commented 4 years ago

And here it is: https://github.com/3b1b/manim/pull/950

pythonian1027 commented 4 years ago

I tried running the project for SIR simulation (https://www.youtube.com/watch?v=gxAaO2rsdIs) and got this error: "~\Anaconda3\envs\manim\lib\site-packages\pyglet\canvas\base.py", line 194, in get_best_config raise window.NoSuchConfigException() pyglet.window.NoSuchConfigException "

I was able to run the Test case but basically everything else crashed

einarf commented 4 years ago

NoSuchConfigException is related to a hardcoded samples = 1 in the window. That needs to be removed or be configurable. The branch is super alpha and needs a bit of hacking to work.

pythonian1027 commented 4 years ago

@einarf

NoSuchConfigException is related to a hardcoded samples = 1 in the window. That needs to be removed or be configurable. The branch is super alpha and needs a bit of hacking to work.

I got it to work by removing the samples = 1 line, but it is sluggish, could you explain how to make it configurable?

qo4on commented 4 years ago

@einarf I found that we can skip the loop when running -w option to write directly to a file. I set HeadlessWindow and backend='egl'. But got an exception with XOpenDisplay. Do you know how to fix that?

!python3 -m manim example_scenes.py SquareToCircle -w

Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/content/manim/manim.py", line 5, in <module>
    manimlib.main()
  File "/content/manim/manimlib/__init__.py", line 11, in main
    scenes = manimlib.extract_scene.main(config)
  File "/content/manim/manimlib/extract_scene.py", line 104, in main
    scenes = get_scenes_to_render(all_scene_classes, config)
  File "/content/manim/manimlib/extract_scene.py", line 72, in get_scenes_to_render
    scene = scene_class(**scene_kwargs)
  File "/content/manim/manimlib/scene/scene.py", line 51, in __init__
    self.camera = self.camera_class(**self.camera_config)
  File "/content/manim/manimlib/camera/camera.py", line 65, in __init__
    self.init_context(ctx)
  File "/content/manim/manimlib/camera/camera.py", line 77, in init_context
    self.ctx = moderngl.create_standalone_context()
  File "/usr/local/lib/python3.6/dist-packages/moderngl/context.py", line 1466, in create_standalone_context
    ctx.mglo, ctx.version_code = mgl.create_context(glversion=require, mode=mode, **settings)
  File "/usr/local/lib/python3.6/dist-packages/glcontext/__init__.py", line 76, in create
    return x11.create_context(**kwargs)
Exception: (standalone) XOpenDisplay: cannot open display
einarf commented 4 years ago

The stack trace above is from another context creation. Maybe that's the one that is used when no preview is used and the HeadlessWindow thing is not needed at all? : https://github.com/3b1b/manim/blob/ec4a70ad3511805c3b5ec55b43ac548be85555e8/manimlib/camera/camera.py#L77

XOpenDisplay means it's and x11 headless context and not egl, and clearly backend='egl' is not passed in there.

qo4on commented 4 years ago

Thanks. I replaced it with

self.ctx = moderngl.create_standalone_context(require=460, backend='egl')

ran

!python3 -m manim example_scenes.py SquareToCircle -w

and got rendered file

/content/manim/media/videos/example_scenes/1440p60/SquareToCircle.mp4

One problem: there is no GPU acceleration. On CPU this SquareToCircle example finishes in 17 seconds. On GPU it took 18 seconds. When GPU works the rendering time should be around 3 seconds. Is there anything we can do with it?

Whole context function:

def init_context(self, ctx=None):
    if ctx is not None:
        self.ctx = ctx
        self.fbo = self.ctx.detect_framebuffer()
    else:
        self.ctx = moderngl.create_standalone_context(require=460, backend='egl')
        self.fbo = self.get_fbo()
        self.fbo.use()

    self.ctx.enable(moderngl.BLEND)
    self.ctx.blend_func = (
        moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
        moderngl.ONE, moderngl.ONE
    )
    self.background_fbo = None
einarf commented 4 years ago

hmm. Grab the self.ctx.info and paste here.

qo4on commented 4 years ago
{'GL_ALIASED_LINE_WIDTH_RANGE': (1.0, 10.0),
 'GL_CONTEXT_PROFILE_MASK': 1,
 'GL_DOUBLEBUFFER': False,
 'GL_MAJOR_VERSION': 4,
 'GL_MAX_3D_TEXTURE_SIZE': 16384,
 'GL_MAX_ARRAY_TEXTURE_LAYERS': 2048,
 'GL_MAX_CLIP_DISTANCES': 8,
 'GL_MAX_COLOR_ATTACHMENTS': 8,
 'GL_MAX_COLOR_TEXTURE_SAMPLES': 32,
 'GL_MAX_COMBINED_ATOMIC_COUNTERS': 98304,
 'GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS': 231424,
 'GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS': 233472,
 'GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS': 231424,
 'GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS': 96,
 'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS': 192,
 'GL_MAX_COMBINED_UNIFORM_BLOCKS': 84,
 'GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS': 233472,
 'GL_MAX_COMPUTE_ATOMIC_COUNTERS': 16384,
 'GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS': 8,
 'GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS': 16,
 'GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS': 32,
 'GL_MAX_COMPUTE_UNIFORM_BLOCKS': 14,
 'GL_MAX_COMPUTE_UNIFORM_COMPONENTS': 2048,
 'GL_MAX_COMPUTE_WORK_GROUP_COUNT': (2147483647, 65535, 65535),
 'GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS': 1536,
 'GL_MAX_COMPUTE_WORK_GROUP_SIZE': (1536, 1024, 64),
 'GL_MAX_CUBE_MAP_TEXTURE_SIZE': 32768,
 'GL_MAX_DEPTH_TEXTURE_SAMPLES': 32,
 'GL_MAX_DRAW_BUFFERS': 8,
 'GL_MAX_DUAL_SOURCE_DRAW_BUFFERS': 1,
 'GL_MAX_ELEMENTS_INDICES': 1048576,
 'GL_MAX_ELEMENTS_VERTICES': 1048576,
 'GL_MAX_ELEMENT_INDEX': 4294967295,
 'GL_MAX_FRAGMENT_ATOMIC_COUNTERS': 16384,
 'GL_MAX_FRAGMENT_INPUT_COMPONENTS': 128,
 'GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS': 16,
 'GL_MAX_FRAGMENT_UNIFORM_BLOCKS': 14,
 'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS': 4096,
 'GL_MAX_FRAGMENT_UNIFORM_VECTORS': 1024,
 'GL_MAX_FRAMEBUFFER_HEIGHT': 32768,
 'GL_MAX_FRAMEBUFFER_LAYERS': 2048,
 'GL_MAX_FRAMEBUFFER_SAMPLES': 32,
 'GL_MAX_FRAMEBUFFER_WIDTH': 32768,
 'GL_MAX_GEOMETRY_ATOMIC_COUNTERS': 16384,
 'GL_MAX_GEOMETRY_INPUT_COMPONENTS': 128,
 'GL_MAX_GEOMETRY_OUTPUT_COMPONENTS': 128,
 'GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS': 16,
 'GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS': 32,
 'GL_MAX_GEOMETRY_UNIFORM_BLOCKS': 14,
 'GL_MAX_GEOMETRY_UNIFORM_COMPONENTS': 2048,
 'GL_MAX_INTEGER_SAMPLES': 32,
 'GL_MAX_PROGRAM_TEXEL_OFFSET': 7.0,
 'GL_MAX_RECTANGLE_TEXTURE_SIZE': 32768,
 'GL_MAX_RENDERBUFFER_SIZE': 32768,
 'GL_MAX_SAMPLES': 32,
 'GL_MAX_SAMPLE_MASK_WORDS': 2,
 'GL_MAX_SERVER_WAIT_TIMEOUT': -1,
 'GL_MAX_SHADER_STORAGE_BLOCK_SIZE': 2147483647,
 'GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS': 96,
 'GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS': 16384,
 'GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS': 16,
 'GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS': 16384,
 'GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS': 16,
 'GL_MAX_TEXTURE_BUFFER_SIZE': 134217728,
 'GL_MAX_TEXTURE_IMAGE_UNITS': 32,
 'GL_MAX_TEXTURE_LOD_BIAS': 15,
 'GL_MAX_TEXTURE_SIZE': 32768,
 'GL_MAX_UNIFORM_BLOCK_SIZE': 65536,
 'GL_MAX_UNIFORM_BUFFER_BINDINGS': 84,
 'GL_MAX_UNIFORM_LOCATIONS': 65536,
 'GL_MAX_VARYING_VECTORS': 31,
 'GL_MAX_VERTEX_ATOMIC_COUNTERS': 16384,
 'GL_MAX_VERTEX_ATTRIBS': 16,
 'GL_MAX_VERTEX_ATTRIB_BINDINGS': 16,
 'GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET': 2047,
 'GL_MAX_VERTEX_OUTPUT_COMPONENTS': 128,
 'GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS': 16,
 'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS': 32,
 'GL_MAX_VERTEX_UNIFORM_BLOCKS': 14,
 'GL_MAX_VERTEX_UNIFORM_COMPONENTS': 4096,
 'GL_MAX_VERTEX_UNIFORM_VECTORS': 1024,
 'GL_MAX_VIEWPORTS': 16,
 'GL_MAX_VIEWPORT_DIMS': (32768, 32768),
 'GL_MINOR_VERSION': 6,
 'GL_MIN_MAP_BUFFER_ALIGNMENT': 64,
 'GL_MIN_PROGRAM_TEXEL_OFFSET': -8.0,
 'GL_POINT_FADE_THRESHOLD_SIZE': 1.0,
 'GL_POINT_SIZE_GRANULARITY': 0.125,
 'GL_POINT_SIZE_RANGE': (1.0, 189.875),
 'GL_RENDERER': 'Tesla P100-PCIE-16GB/PCIe/SSE2',
 'GL_SAMPLE_BUFFERS': 0,
 'GL_SMOOTH_LINE_WIDTH_GRANULARITY': 0.125,
 'GL_SMOOTH_LINE_WIDTH_RANGE': (0.5, 10.0),
 'GL_STEREO': False,
 'GL_SUBPIXEL_BITS': 8,
 'GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT': 256,
 'GL_VENDOR': 'NVIDIA Corporation',
 'GL_VERSION': '4.6.0 NVIDIA 418.67',
 'GL_VIEWPORT_BOUNDS_RANGE': (-65536, 65536),
 'GL_VIEWPORT_SUBPIXEL_BITS': 8}

It is from the end of init_context function.

qo4on commented 4 years ago

When we replaced manim window.py with the code below it redered faster, but we've got into a loop.

import moderngl_window as mglw
from moderngl_window.context.headless.window import Window as HeadlessWindow
from moderngl_window.timers.clock import Timer

from manimlib.constants import DEFAULT_PIXEL_WIDTH
from manimlib.constants import DEFAULT_PIXEL_HEIGHT
from manimlib.utils.config_ops import digest_config

class Window(HeadlessWindow):
    size = (DEFAULT_PIXEL_WIDTH, DEFAULT_PIXEL_HEIGHT)
    fullscreen = False
    resizable = False
    gl_version = (4, 6)
    vsync = False
    samples = 1
    cursor = False

    def __init__(self, scene, **kwargs):
        digest_config(self, kwargs)
        super().__init__(**kwargs)
        self.scene = scene
        self.title = str(scene)
        # Put at the top of the screen
        self.position = (self.position[0], 0)

        mglw.activate_context(window=self)
        self.timer = Timer()
        self.config = mglw.WindowConfig(ctx=self.ctx, wnd=self, timer=self.timer)
        self.timer.start()

    # Delegate event handling to scene
    def pixel_coords_to_space_coords(self, px, py, relative=False):
        return self.scene.camera.pixel_coords_to_space_coords(px, py, relative)""")
einarf commented 4 years ago

Hard to say about performance to be honest. Manim in shader branch is a mix of gpu and cpu rendering as far as I have seen, so that needs to be profiled locally. If this is Google Colab still I would guess you only won't get the performance of that entire card. Also I don't know if there's any setup or teardown cost.

einarf commented 4 years ago

So does it create two opengl contexts when not using a preview?

qo4on commented 4 years ago

I don't know. How can I check that?

einarf commented 4 years ago

Add some prints before every context creation or open and append something to file if you cannot see output.

qo4on commented 4 years ago

It creates context in /content/manim/manimlib/camera/camera.py And does not in /usr/local/lib/python3.6/dist-packages/moderngl_window/context/headless/window.py

qo4on commented 4 years ago

There are different steps after context creation. In camera.py he does:

self.ctx = moderngl.create_standalone_context(require=460, backend='egl')
self.fbo = self.get_fbo()
self.fbo.use()

self.ctx.enable(moderngl.BLEND)
self.ctx.blend_func = (
    moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
    moderngl.ONE, moderngl.ONE
)
self.background_fbo = None

But in your window.py:

self._ctx = moderngl.create_standalone_context(require=self.gl_version_code, backend='egl')
self._fbo = self.ctx.framebuffer(
    color_attachments=self.ctx.texture(self.size, 4, samples=self._samples),
    depth_attachment=self.ctx.depth_texture(self.size, samples=self._samples),
)
self.use()
einarf commented 4 years ago

The camera.py one is slower I guess? .. and you only saw one single context creation I hope?

You can try commenting out blending and see if that affects anything

self.ctx.enable(moderngl.BLEND)
self.ctx.blend_func = (
    moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
    moderngl.ONE, moderngl.ONE
)

The second thing you can test is to use same framebuffer configuration as the window.

qo4on commented 4 years ago

The camera.py one is slower

Yes

you only saw one single context creation I hope?

Yes. The conext creates in camera.py.

You can try commenting out blending and see if that affects anything

Rendering took 17 seconds

einarf commented 4 years ago

Modify get_fbo() to make the same framebuffer format? I don't understand why changing the window class affects anything in the first place πŸ˜„

qo4on commented 4 years ago

I tried this:

def init_context(self, ctx=None):
    if ctx is not None:
        self.ctx = ctx
        self.fbo = self.ctx.detect_framebuffer()
    else:
        self.ctx = moderngl.create_standalone_context(require=460, backend='egl')
        size = (DEFAULT_PIXEL_WIDTH, DEFAULT_PIXEL_HEIGHT)
        samples = 1
        self.fbo = self.ctx.framebuffer(
          color_attachments=self.ctx.texture(size, 4, samples=samples),
          depth_attachment=self.ctx.depth_texture(size, samples=samples),
        )
    self.background_fbo = None

Rendering time 15 seconds, but the resulting file is a black window.

Modify get_fbo() to make the same framebuffer format?

I'm not sure what you mean.

I don't understand why changing the window class affects anything in the first place πŸ˜„

It does not affect anything when writing to a file enabled !python3 -m manim example_scenes.py SquareToCircle -w. But it affects when running headless widow: !python3 -m manim example_scenes.py SquareToCircle.

I think we need to recreate steps you did in window.py inside manim camera.py.

einarf commented 4 years ago

I think we need to recreate steps you did in window.py inside manim camera.py.

Probably a good idea. It might just even be a fluke. I don't really know how much gpu resources is allocated per user or if the tesla have a much lower latency. I'm guessing the setup is configured to do longer running gppgu and machine learning tasks.

I'd try to experiment over several runs. Try to replicate the speedup you saw.

One difference I see is in scene.py/update_frame. If you have a window it will call swap_buffers. That's just doing a ctx.finish() that will wait for all rendering operations to complete. There is no equivalent for swap_buffers when camera.py creates the context.

qo4on commented 4 years ago

I believe when you are connected to Colab GPU you are getting all its power.

Do you know how to setup antialiasing samples in /content/manim/manimlib/camera/camera.py?

When sapples = 0 it creates a video in 13 seconds:

!python3 -m manim example_scenes.py SquareToCircle -w

def init_context(self, ctx=None):
    if ctx is not None:
        self.ctx = ctx
        self.fbo = self.ctx.detect_framebuffer()
    else:
        self.ctx = moderngl.create_standalone_context(require=460, backend='egl')
        self.fbo = self.get_fbo()
        self.fbo.use()

    self.ctx.enable(moderngl.BLEND)
    self.ctx.blend_func = (
        moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
        moderngl.ONE, moderngl.ONE
    )
    self.background_fbo = None
    from pprint import pprint
    pprint(self.__dict__)

# Methods associated with the frame buffer
def get_fbo(self):
    samples = 0
    return self.ctx.framebuffer(
        color_attachments=self.ctx.texture((self.pixel_width, self.pixel_height), 4, samples=samples),
        depth_attachment=self.ctx.depth_texture((self.pixel_width, self.pixel_height), samples=samples)
    )

{'background_color': '#000000',
 'background_fbo': None,
 'background_image': None,
 'background_opacity': 1,
 'ctx': <Context 140226494281160 version_code=460>,
 'fbo': <Framebuffer: 1>,
 'frame': <manimlib.camera.camera.CameraFrame object at 0x7f8911b19080>,
 'frame_config': {'center': array([0., 0., 0.]),
                  'height': 8.0,
                  'width': 14.222222222222221},
 'frame_rate': 60,
 'image_mode': 'RGBA',
 'line_width_multiple': 0.01,
 'max_allowable_norm': 14.222222222222221,
 'n_channels': 4,
 'pixel_array_dtype': 'uint8',
 'pixel_height': 1440,
 'pixel_width': 2560,
 'rgb_max_val': 255}

But when I change it to samples = 1 the video is a black screen.

einarf commented 4 years ago

Do it in get_fbo ..

    # Methods associated with the frame buffer
    def get_fbo(self):
        return self.ctx.framebuffer(
            color_attachments=self.ctx.texture(self.size, 4, samples=8),
            depth_attachment=self.ctx.depth_texture(self.size, samples=8),
        )

Restore init_context to the original state except the backend part.

qo4on commented 4 years ago

This returns a black video with colored artifacts at the bottom edge. Your example shows white screen with this settings:

import numpy as np
from PIL import Image
import moderngl

ctx = moderngl.create_standalone_context(require=460, backend='egl')
samples = 8
fbo = ctx.framebuffer(
        color_attachments=ctx.texture((512, 512), 4, samples=samples),
        depth_attachment=ctx.depth_texture((512, 512), samples=samples)
    )
fbo.use()

vertices = np.array([
    -1.0,  -1.0,   1.0, 0.0, 0.0,
     1.0,  -1.0,   0.0, 1.0, 0.0,
     0.0,   1.0,   0.0, 0.0, 1.0],
    dtype='f4',
)

prog = ctx.program(vertex_shader="""
#version 330
in vec2 in_vert;
in vec3 in_color;
out vec3 color;
void main() {
    gl_Position = vec4(in_vert, 0.0, 1.0);
    color = in_color;
}
""",
fragment_shader="""
#version 330
out vec4 fragColor;
in vec3 color;
void main() {
    fragColor = vec4(color, 1.0);
}
""",
)
vao = ctx.simple_vertex_array(prog, ctx.buffer(vertices), 'in_vert', 'in_color')
vao.render(mode=moderngl.TRIANGLES)

image = Image.frombytes('RGBA', (512, 512), fbo.read(components=4))
image = image.transpose(Image.FLIP_TOP_BOTTOM)
image.save('triangle.png', format='png')

with samples = 0 it renders colored triangle.

There is only one change in ctx.info: samples = 0; 'GL_SAMPLE_BUFFERS': 0 and when samples>0: samples = 8; 'GL_SAMPLE_BUFFERS': 1.

qo4on commented 4 years ago

@einarf Investigating this further I found that it is possibly a bug of the Moderngl.

einarf commented 4 years ago

Oh. I'll have to look into that. Thanks!

qo4on commented 4 years ago

@einarf Do you have any comments on this?

einarf commented 4 years ago

Not anything that I've already said in the post above.

I'm wondering if manim even needs a depth buffer, so you can try dropping it.

qo4on commented 4 years ago

Can you tell when a depth buffer is needed?

einarf commented 4 years ago

Search for DEPTH_TEST in the source. That`s the enum name for the flag that enables or disable depth testing.

qo4on commented 4 years ago

I found that for now manim has gpu accleration only when it works with pyglet window:

!python3 -m manim example_scenes.py SquareToCircle -p

What I didn't find is where pyglet window defines a framebuffer and where it uses samples=1.