Provide a check that shader uniforms really need updating before setting value

This commit is contained in:
Grant Sanderson 2023-01-26 20:01:59 -08:00
parent acdc2654d3
commit 258bc2256a
4 changed files with 23 additions and 25 deletions

View file

@ -50,7 +50,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Callable, Iterable, Union, Tuple from typing import Callable, Iterable, Union, Tuple
import numpy.typing as npt import numpy.typing as npt
from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array, UniformDict
from moderngl.context import Context from moderngl.context import Context
TimeBasedUpdater = Callable[["Mobject", float], "Mobject" | None] TimeBasedUpdater = Callable[["Mobject", float], "Mobject" | None]
@ -131,7 +131,7 @@ class Mobject(object):
self.data = np.zeros(length, dtype=self.shader_dtype) self.data = np.zeros(length, dtype=self.shader_dtype)
def init_uniforms(self): def init_uniforms(self):
self.uniforms: dict[str, float | np.ndarray] = { self.uniforms: UniformDict = {
"is_fixed_in_frame": float(self.is_fixed_in_frame), "is_fixed_in_frame": float(self.is_fixed_in_frame),
"shading": np.array(self.shading, dtype=float), "shading": np.array(self.shading, dtype=float),
} }
@ -1894,7 +1894,7 @@ class Mobject(object):
self.shader_wrapper.vert_data = self.get_shader_data() self.shader_wrapper.vert_data = self.get_shader_data()
self.shader_wrapper.vert_indices = self.get_shader_vert_indices() self.shader_wrapper.vert_indices = self.get_shader_vert_indices()
self.shader_wrapper.uniforms.update(self.get_uniforms()) self.shader_wrapper.update_program_uniforms(self.get_uniforms())
self.shader_wrapper.depth_test = self.depth_test self.shader_wrapper.depth_test = self.depth_test
return self.shader_wrapper return self.shader_wrapper
@ -1931,8 +1931,8 @@ class Mobject(object):
shader_wrapper.generate_vao() shader_wrapper.generate_vao()
self._data_has_changed = False self._data_has_changed = False
for shader_wrapper in self.shader_wrappers: for shader_wrapper in self.shader_wrappers:
shader_wrapper.uniforms.update(self.get_uniforms()) shader_wrapper.update_program_uniforms(self.get_uniforms())
shader_wrapper.uniforms.update(camera_uniforms) shader_wrapper.update_program_uniforms(camera_uniforms, universal=True)
shader_wrapper.pre_render() shader_wrapper.pre_render()
shader_wrapper.render() shader_wrapper.render()

View file

@ -1264,11 +1264,6 @@ class VMobject(Mobject):
self.fill_shader_wrapper.read_in(fill_datas, fill_indices or None), self.fill_shader_wrapper.read_in(fill_datas, fill_indices or None),
self.stroke_shader_wrapper.read_in(stroke_datas), self.stroke_shader_wrapper.read_in(stroke_datas),
] ]
for sw in shader_wrappers:
# Assume uniforms of the first family member
sw.uniforms.update(family[0].get_uniforms())
sw.depth_test = family[0].depth_test
return [sw for sw in shader_wrappers if len(sw.vert_data) > 0] return [sw for sw in shader_wrappers if len(sw.vert_data) > 0]

View file

@ -7,7 +7,6 @@ import re
import OpenGL.GL as gl import OpenGL.GL as gl
import moderngl import moderngl
import numpy as np import numpy as np
from functools import lru_cache
from manimlib.utils.iterables import resize_array from manimlib.utils.iterables import resize_array
from manimlib.utils.shaders import get_shader_code_from_file from manimlib.utils.shaders import get_shader_code_from_file
@ -20,7 +19,8 @@ from manimlib.utils.shaders import release_texture
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import List, Optional from typing import List, Optional, Dict
from manimlib.typing import UniformDict
# Mobjects that should be rendered with # Mobjects that should be rendered with
@ -37,7 +37,7 @@ class ShaderWrapper(object):
vert_data: np.ndarray, vert_data: np.ndarray,
vert_indices: Optional[np.ndarray] = None, vert_indices: Optional[np.ndarray] = None,
shader_folder: Optional[str] = None, shader_folder: Optional[str] = None,
uniforms: Optional[dict[str, float | np.ndarray]] = None, # A dictionary mapping names of uniform variables uniforms: Optional[UniformDict] = None, # A dictionary mapping names of uniform variables
texture_paths: Optional[dict[str, str]] = None, # A dictionary mapping names to filepaths for textures. texture_paths: Optional[dict[str, str]] = None, # A dictionary mapping names to filepaths for textures.
depth_test: bool = False, depth_test: bool = False,
render_primitive: int = moderngl.TRIANGLE_STRIP, render_primitive: int = moderngl.TRIANGLE_STRIP,
@ -47,12 +47,13 @@ class ShaderWrapper(object):
self.vert_indices = (vert_indices or np.zeros(0)).astype(int) self.vert_indices = (vert_indices or np.zeros(0)).astype(int)
self.vert_attributes = vert_data.dtype.names self.vert_attributes = vert_data.dtype.names
self.shader_folder = shader_folder self.shader_folder = shader_folder
self.uniforms = dict(uniforms or {}) self.uniforms: UniformDict = dict()
self.depth_test = depth_test self.depth_test = depth_test
self.render_primitive = render_primitive self.render_primitive = render_primitive
self.init_program_code() self.init_program_code()
self.init_program() self.init_program()
self.update_program_uniforms(uniforms or dict())
if texture_paths is not None: if texture_paths is not None:
self.init_textures(texture_paths) self.init_textures(texture_paths)
self.refresh_id() self.refresh_id()
@ -93,7 +94,7 @@ class ShaderWrapper(object):
np.all(self.vert_indices == shader_wrapper.vert_indices), np.all(self.vert_indices == shader_wrapper.vert_indices),
self.shader_folder == shader_wrapper.shader_folder, self.shader_folder == shader_wrapper.shader_folder,
all( all(
np.all(self.uniforms[key] == shader_wrapper.uniforms[key]) self.uniforms[key] == shader_wrapper.uniforms[key]
for key in self.uniforms for key in self.uniforms
), ),
self.depth_test == shader_wrapper.depth_test, self.depth_test == shader_wrapper.depth_test,
@ -105,8 +106,6 @@ class ShaderWrapper(object):
result.ctx = self.ctx result.ctx = self.ctx
result.vert_data = self.vert_data.copy() result.vert_data = self.vert_data.copy()
result.vert_indices = self.vert_indices.copy() result.vert_indices = self.vert_indices.copy()
if self.uniforms:
result.uniforms = {key: np.array(value) for key, value in self.uniforms.items()}
result.vao = None result.vao = None
result.vbo = None result.vbo = None
result.ibo = None result.ibo = None
@ -217,20 +216,23 @@ class ShaderWrapper(object):
def pre_render(self): def pre_render(self):
self.set_ctx_depth_test(self.depth_test) self.set_ctx_depth_test(self.depth_test)
self.set_ctx_clip_plane(self.use_clip_plane()) self.set_ctx_clip_plane(self.use_clip_plane())
self.update_program_uniforms()
def render(self): def render(self):
assert(self.vao is not None) assert(self.vao is not None)
self.vao.render() self.vao.render()
def update_program_uniforms(self): def update_program_uniforms(self, uniforms: UniformDict, universal: bool = False):
if self.program is None: if self.program is None:
return return
for name, value in self.uniforms.items(): for name, value in uniforms.items():
if name in self.program: if name not in self.program:
continue
if isinstance(value, np.ndarray) and value.ndim > 0: if isinstance(value, np.ndarray) and value.ndim > 0:
value = tuple(value) value = tuple(value)
if universal and self.uniforms.get(name, None) == value:
continue
self.program[name].value = value self.program[name].value = value
self.uniforms[name] = value
def get_vertex_buffer_object(self, refresh: bool = True): def get_vertex_buffer_object(self, refresh: bool = True):
if refresh: if refresh:
@ -274,7 +276,6 @@ class FillShaderWrapper(ShaderWrapper):
): ):
super().__init__(ctx, *args, **kwargs) super().__init__(ctx, *args, **kwargs)
def render(self): def render(self):
vao = self.vao vao = self.vao
assert(vao is not None) assert(vao is not None)

View file

@ -19,6 +19,8 @@ if TYPE_CHECKING:
] ]
Selector = Union[SingleSelector, Iterable[SingleSelector]] Selector = Union[SingleSelector, Iterable[SingleSelector]]
UniformDict = Dict[str, float | bool | np.ndarray | tuple]
# These are various alternate names for np.ndarray meant to specify # These are various alternate names for np.ndarray meant to specify
# certain shapes. # certain shapes.
# #