Skip to content

Commit

Permalink
Enable CI testing for OpenGL (3b1b#1160)
Browse files Browse the repository at this point in the history
* add simple circle test for opengl

* literally a comma

* add rgb tolerance to graphical unit tester

* start up xvfb on ubuntu?

* black style

* reduce tolerance to pixel ?

* add msys2 action

* add mesa install

* ci: windows add msys2 to path
Compile moderngl from source
add a init tests for CI to work
add colour to pytest

* minor edits

* mild refactor of scene / camera / renderer relationship

* flake-d

* reverted refactor

* reverted opengl split at the level of graphical unit tester

* remove unnecessary config in definition

* CI: remove python3 from MSYS2 also

Co-authored-by: Naveen M K <[email protected]>
  • Loading branch information
markromanmiller and naveen521kk authored Mar 28, 2021
1 parent cdd0dde commit d8d9a01
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 14 deletions.
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
runs-on: ${{ matrix.os }}
env:
POETRY_VIRTUALENVS_CREATE: false
DISPLAY: :0
PYTEST_ADDOPTS: "--color=yes" # colors in pytest
strategy:
fail-fast: false
matrix:
Expand All @@ -37,8 +39,10 @@ jobs:
run: |
sudo apt update
sudo apt install -y ffmpeg
sudo apt-get -y install texlive texlive-latex-extra texlive-fonts-extra texlive-latex-recommended texlive-science texlive-fonts-extra tipa python-opengl libpango1.0-dev
sudo apt-get -y install texlive texlive-latex-extra texlive-fonts-extra texlive-latex-recommended texlive-science texlive-fonts-extra tipa python-opengl libpango1.0-dev xvfb
echo "$HOME/.poetry/bin" >> $GITHUB_PATH
# start xvfb in the background
sudo /usr/bin/Xvfb $DISPLAY -screen 0 1280x1024x24 &
- name: Install system dependencies (MacOS)
if: runner.os == 'macOS'
Expand Down Expand Up @@ -77,6 +81,16 @@ jobs:
path: ${{ github.workspace }}\ManimCache
key: ${{ runner.os }}-dependencies-ffmpeg-tinytex-${{ hashFiles('.github/manimdependency.json') }}-${{ steps.pip-cache-and-time.outputs.date }}

- name: Setup MSYS2
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
release: false
msystem: MINGW64
path-type: inherit
install: >-
mingw-w64-x86_64-mesa
- name: Download system dependencies (Windows)
if: runner.os == 'Windows' && steps.cache-windows.outputs.cache-hit != 'true'
run: |
Expand Down Expand Up @@ -105,10 +119,13 @@ jobs:
- name: Add Windows dependecies to path
if: runner.os == 'Windows'
run: |
Remove-Item C:\msys64\mingw64\bin\python.exe -Force
Remove-Item C:\msys64\mingw64\bin\python3.exe -Force
$env:Path += ";" + "$($PWD)\ManimCache\FFmpeg\bin"
$env:Path += ";" + "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\win32"
$env:Path += ";" + "$($PWD)\ManimCache\Pango\pango"
$env:Path = "$env:USERPROFILE\.poetry\bin;$($env:PATH)"
$env:PATH = "C:\msys64\mingw64\bin;$($env:PATH)"
echo "$env:Path" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install manim
Expand Down
7 changes: 7 additions & 0 deletions manim/renderer/opengl_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,13 @@ def get_raw_frame_buffer_object_data(self, dtype="f1"):
)
return ret

def get_frame(self):
# get current pixel values as numpy data in order to test output
raw = self.get_raw_frame_buffer_object_data(dtype="f1")
result_dimensions = (config["pixel_height"], config["pixel_width"], 4)
np_buf = np.frombuffer(raw, dtype="uint8").reshape(result_dimensions)
return np_buf

# Returns offset from the bottom left corner in pixels.
def pixel_coords_to_space_coords(self, px, py, relative=False):
pw, ph = config["pixel_width"], config["pixel_height"]
Expand Down
10 changes: 10 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os

if os.getenv("CI") and os.name == "nt":
location = r"C:\msys64\mingw64\bin"
os.environ["PATH"] = location + os.pathsep + os.getenv("PATH")
import ctypes

ctypes.CDLL(r"C:\msys64\mingw64\bin\OPENGL32.dll")
if hasattr(os, "add_dll_directory"):
os.add_dll_directory(location)
Binary file not shown.
25 changes: 25 additions & 0 deletions tests/test_graphical_units/test_opengl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from manim import *
from manim.opengl import *
from ..utils.testing_utils import get_scenes_to_test
from ..utils.GraphicalUnitTester import GraphicalUnitTester


class CircleTest(Scene):
def construct(self):
circle = OpenGLCircle().set_color(RED)
self.add(circle)
self.wait()


MODULE_NAME = "opengl"


@pytest.mark.parametrize("scene_to_test", get_scenes_to_test(__name__), indirect=False)
def test_scene(scene_to_test, tmpdir, show_diff):
with tempconfig({"use_opengl_renderer": True}):
# allow 1/255 RGB value differences with opengl tests because of differences across platforms
GraphicalUnitTester(scene_to_test[1], MODULE_NAME, tmpdir, rgb_atol=1.01).test(
show_diff=show_diff
)
28 changes: 15 additions & 13 deletions tests/utils/GraphicalUnitTester.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import numpy as np

from manim import config, tempconfig
from manim.renderer.opengl_renderer import OpenGLRenderer


class GraphicalUnitTester:
"""Class used to test the animations.
Parameters
----------
scene_object : :class:`~.Scene`
scene_class : :class:`~.Scene`
The scene to be tested
config_scene : :class:`dict`
The configuration of the scene
Expand All @@ -27,12 +28,7 @@ class GraphicalUnitTester:
The scene tested
"""

def __init__(
self,
scene_object,
module_tested,
tmpdir,
):
def __init__(self, scene_class, module_tested, tmpdir, rgb_atol=0):
# Disable the the logs, (--quiet is broken) TODO
logging.disable(logging.CRITICAL)
tests_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand All @@ -41,11 +37,12 @@ def __init__(
"test_graphical_units",
"tests_cache",
module_tested,
scene_object.__name__,
scene_class.__name__,
)
self.path_control_data = os.path.join(
tests_directory, "control_data", "graphical_units_data", module_tested
)
self.rgb_atol = rgb_atol

# IMPORTANT NOTE : The graphical units tests don't use for now any
# custom manim.cfg, since it is impossible to manually select a
Expand All @@ -64,7 +61,10 @@ def __init__(
os.makedirs(dir_temp)

with tempconfig({"dry_run": True}):
self.scene = scene_object(skip_animations=True)
if config["use_opengl_renderer"]:
self.scene = scene_class(renderer=OpenGLRenderer())
else:
self.scene = scene_class(skip_animations=True)
self.scene.render()

def _load_data(self):
Expand Down Expand Up @@ -126,15 +126,17 @@ def test(self, show_diff=False):
+ f"\nframe_data.shape = {frame_data.shape}"
)

test_result = np.array_equal(frame_data, expected_frame_data)
if not test_result:
incorrect_indices = np.argwhere(frame_data != expected_frame_data)
mismatches = np.logical_not(
np.isclose(frame_data, expected_frame_data, atol=self.rgb_atol, rtol=0)
)
if mismatches.any():
incorrect_indices = np.argwhere(mismatches)
first_incorrect_index = incorrect_indices[0][:2]
first_incorrect_point = frame_data[tuple(first_incorrect_index)]
expected_point = expected_frame_data[tuple(first_incorrect_index)]
if show_diff:
self._show_diff_helper(frame_data, expected_frame_data)
assert test_result, (
assert not mismatches.any(), (
f"The frames don't match. {str(self.scene).replace('Test', '')} has been modified."
+ "\nPlease ignore if it was intended."
+ f"\nFirst unmatched index is at {first_incorrect_index}: {first_incorrect_point} != {expected_point}"
Expand Down

0 comments on commit d8d9a01

Please sign in to comment.