Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 14 additions & 49 deletions demosys/core/finders.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""
Base finders
"""
import os
from collections import namedtuple
from pathlib import Path

from demosys.conf import settings
from demosys.core.exceptions import ImproperlyConfigured

Expand All @@ -20,60 +21,24 @@ def __init__(self):
"This is required when using a FileSystemFinder."
)
self.paths = getattr(settings, self.settings_attr)
self._cached_paths = {}

self._cache = {}

def find(self, path):
def find(self, path: Path):
"""
Find a file in the path.
When creating a custom finder, this is the method you override.
Find a file in the path. The file may exist in multiple
paths. The last found file will be returned.

:param path: The path to find
:return: The absolute path to the file or None if not found
"""
return self._find(path)

def _find(self, path):
"""
Similar to ``find()``, but it caches each result to speed things.
path_found = None

:param path: The path to find
:return: The absolute path to the file or None if not found
"""
for entry in self.paths:
abspath = os.path.join(entry, path)
if os.path.exists(abspath):
self.cache(abspath, abspath)
return abspath
else:
self.cache(abspath, abspath, exists=False)

return None
abspath = entry / path
if abspath.exists():
path_found = abspath

def find_cached(self, path):
"""
Check if the path is already cached.
This method should normally not be overridden.

:param path: The path to the file
:return: The absolute path to the file or None
"""
entry = self._cache.get(path)
if entry.exists:
return entry.abspath

return None

def cache(self, path, abspath, exists=True):
"""
Caches an entry.
Should ideally not be overridden.

:param path: The path
:param abspath: The absolute path
:param exists: Did the file exist? (bool)
"""
self._cache[path] = FinderEntry(path=path, abspath=abspath, exists=exists)
return path_found


class BaseEffectDirectoriesFinder(BaseFileSystemFinder):
Expand All @@ -83,7 +48,7 @@ class BaseEffectDirectoriesFinder(BaseFileSystemFinder):
def __init__(self):
from demosys.effects.registry import effects
self.paths = list(effects.get_dirs())
self._cache = {}

def find(self, path):
return self._find(os.path.join(self.directory, path))
def find(self, path: Path):
path = Path(self.directory) / Path(path)
return super().find(path)
28 changes: 1 addition & 27 deletions demosys/effects/effect.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,9 @@ class Effect:
_ctx = None # type: moderngl.Context
_sys_camera = None # type: camera.SystemCamera

def __init__(self, *args, **kwargs):
self.on_resouces_loaded(self.post_load)

def post_load(self):
"""
Called when all resources are loaded before effects start running.
This assumes you have called Effect.__init__()
Called when all effects are initialized before effects start running.
"""
pass

Expand Down Expand Up @@ -215,28 +211,6 @@ def get_data(self, path, local=False, **kwargs) -> Data:
"""
return resources.data.get(path, create=True, **kwargs)

# Register callbacks

def on_resouces_loaded(self, func):
"""Register callback function when all resources are loaded"""
resources.on_loaded(func)

def on_shaders_loaded(self, func):
"""Register callback function when shaders are loaded"""
resources.shaders.on_loaded(func)

def on_textures_loaded(self, func):
"""Register callback function when textures are loaded"""
resources.textures.on_loaded(func)

def on_scenes_loaded(self, func):
"""Register callback function when scenes are loaded"""
resources.scenes.on_loaded(func)

def on_data_loaded(self, func):
"""Register callback function when data files are loaded"""
resources.data.on_loaded(func)

# Utility methods for matrices

def create_projection(self, fov=75.0, near=1.0, far=100.0, ratio=None):
Expand Down
1 change: 1 addition & 0 deletions demosys/effects/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def pre_load(self):
return True

def post_load(self):
self.active_effect.post_load()
return True

def draw(self, time, frametime, target):
Expand Down
10 changes: 7 additions & 3 deletions demosys/opengl/shader.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ def __init__(self, path=None, name=None):
:param path: Full file path to the shader
:param name: Name of the shader (debug purposes)
"""
self.ctx = context.ctx()
if not path and not name:
raise ShaderError("Shader must have a path or a name")

Expand All @@ -47,6 +46,11 @@ def __init__(self, path=None, name=None):
# Unique key for VAO instances containing shader id and attributes
self.vao_key = None

@property
def ctx(self) -> moderngl.Context:
"""The moderngl context"""
return context.ctx()

def __getitem__(self, key) -> Union[moderngl.Uniform, moderngl.UniformBlock, moderngl.Subroutine,
moderngl.Attribute, moderngl.Varying]:
return self.mglo[key]
Expand Down Expand Up @@ -149,15 +153,15 @@ def prepare(self, reload=False):
program = self.ctx.program(**params)

if reload:
self.program.release()
self.release()

self.program = program

# Build internal lookups
self._build_uniform_map()
self._build_attribute_map()

def _delete(self):
def release(self):
"""Frees the memory and invalidates the name associated with the program"""
if self.program:
self.program.release()
Expand Down
7 changes: 4 additions & 3 deletions demosys/opengl/texture/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from demosys import context

from .base import BaseTexture
from .base import BaseTexture, image_data


class TextureArray(BaseTexture):
Expand Down Expand Up @@ -66,11 +66,12 @@ def set_image(self, image, flip=True):
image = image.transpose(Image.FLIP_TOP_BOTTOM)

width, height, depth = image.size[0], image.size[1] // self.layers, self.layers
components, data = image_data(image)

self.mglo = self.ctx.texture_array(
(width, height, depth),
4,
image.convert("RGBA").tobytes(),
components,
data,
)

if self.mipmap:
Expand Down
21 changes: 15 additions & 6 deletions demosys/opengl/texture/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,27 @@
from demosys import context


def image_data(image):
"""Get components and bytes for an image"""
# NOTE: We might want to check the actual image.mode
# and convert to an acceptable format.
# At the moment we load the data as is.
data = image.tobytes()
components = len(data) // (image.size[0] * image.size[1])
return components, data


class BaseTexture:
"""
Wraps the basic functionality of the ModernGL methods
"""
def __init__(self):
self.mglo = None # Type: Union[moderngl.Texture, moderngl.TextureArray]
self._ctx = context.ctx()

@property
def ctx(self) -> moderngl.Context:
"""ModernGL context"""
return context.ctx()

def use(self, location=0):
"""
Expand Down Expand Up @@ -65,11 +79,6 @@ def release(self):
"""Release/free the ModernGL object"""
self.mglo.release()

@property
def ctx(self) -> moderngl.Context:
"""ModernGL context"""
return self._ctx

@property
def size(self) -> Tuple:
"""The size of the texture"""
Expand Down
8 changes: 5 additions & 3 deletions demosys/opengl/texture/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import moderngl
from demosys import context

from .base import BaseTexture
from .base import BaseTexture, image_data


class Texture2D(BaseTexture):
Expand Down Expand Up @@ -91,10 +91,12 @@ def set_image(self, image, flip=True):
if flip:
image = image.transpose(Image.FLIP_TOP_BOTTOM)

components, data = image_data(image)

self.mglo = self.ctx.texture(
image.size,
4,
image.convert("RGBA").tobytes(),
components,
data,
)

if self.mipmap:
Expand Down
11 changes: 11 additions & 0 deletions demosys/opengl/vao.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,17 @@ def _create_vao_instance(self, shader):

return vao

def release(self):
"""Destroy the vao object and its buffers"""
for key, vao in self.vaos:
vao.release()

for buff in self.buffers:
buff.buffer.release()

if self._index_buffer:
self._index_buffer.release()


class VAOError(Exception):
pass
47 changes: 5 additions & 42 deletions demosys/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,49 +20,12 @@
]


class States:
on_loaded_funcs = []
on_load_funcs = []
loaded = False

@classmethod
def sort_callbacks(cls):
cls.on_load_funcs = sorted(cls.on_load_funcs, key=lambda x: x[0])
cls.on_loaded_funcs = sorted(cls.on_loaded_funcs, key=lambda x: x[0])


def load():
States.sort_callbacks()

for func in reversed(States.on_load_funcs):
func[1]()

scenes.load()
shaders.load()
textures.load()
tracks.load()
data.load()

States.loaded = True
for func in reversed(States.on_loaded_funcs):
func[1]()
scenes.load_pool()
shaders.load_pool()
textures.load_pool()
data.load_pool()


def count():
return shaders.count + textures.count


def loading_complete():
return States.loaded


def on_load(func, priority=0):
States.on_load_funcs.append((priority, func))


def on_loaded(func, priority=0):
if loading_complete():
func()
return

States.on_loaded_funcs.append((priority, func))
return scenes.count + shaders.count + textures.count + data.count
Loading