Skip to content

Commit d76b905

Browse files
authored
Merge pull request #37 from Contraz/resource-loading
Resource loading
2 parents 0a512af + 50264e7 commit d76b905

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1207
-416
lines changed

demosys/core/finders.py

Lines changed: 14 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""
22
Base finders
33
"""
4-
import os
54
from collections import namedtuple
5+
from pathlib import Path
6+
67
from demosys.conf import settings
78
from demosys.core.exceptions import ImproperlyConfigured
89

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

24-
self._cache = {}
25-
26-
def find(self, path):
26+
def find(self, path: Path):
2727
"""
28-
Find a file in the path.
29-
When creating a custom finder, this is the method you override.
28+
Find a file in the path. The file may exist in multiple
29+
paths. The last found file will be returned.
3030
3131
:param path: The path to find
3232
:return: The absolute path to the file or None if not found
3333
"""
34-
return self._find(path)
35-
36-
def _find(self, path):
37-
"""
38-
Similar to ``find()``, but it caches each result to speed things.
34+
path_found = None
3935

40-
:param path: The path to find
41-
:return: The absolute path to the file or None if not found
42-
"""
4336
for entry in self.paths:
44-
abspath = os.path.join(entry, path)
45-
if os.path.exists(abspath):
46-
self.cache(abspath, abspath)
47-
return abspath
48-
else:
49-
self.cache(abspath, abspath, exists=False)
50-
51-
return None
37+
abspath = entry / path
38+
if abspath.exists():
39+
path_found = abspath
5240

53-
def find_cached(self, path):
54-
"""
55-
Check if the path is already cached.
56-
This method should normally not be overridden.
57-
58-
:param path: The path to the file
59-
:return: The absolute path to the file or None
60-
"""
61-
entry = self._cache.get(path)
62-
if entry.exists:
63-
return entry.abspath
64-
65-
return None
66-
67-
def cache(self, path, abspath, exists=True):
68-
"""
69-
Caches an entry.
70-
Should ideally not be overridden.
71-
72-
:param path: The path
73-
:param abspath: The absolute path
74-
:param exists: Did the file exist? (bool)
75-
"""
76-
self._cache[path] = FinderEntry(path=path, abspath=abspath, exists=exists)
41+
return path_found
7742

7843

7944
class BaseEffectDirectoriesFinder(BaseFileSystemFinder):
@@ -83,7 +48,7 @@ class BaseEffectDirectoriesFinder(BaseFileSystemFinder):
8348
def __init__(self):
8449
from demosys.effects.registry import effects
8550
self.paths = list(effects.get_dirs())
86-
self._cache = {}
8751

88-
def find(self, path):
89-
return self._find(os.path.join(self.directory, path))
52+
def find(self, path: Path):
53+
path = Path(self.directory) / Path(path)
54+
return super().find(path)

demosys/effects/effect.py

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,9 @@ class Effect:
7676
_ctx = None # type: moderngl.Context
7777
_sys_camera = None # type: camera.SystemCamera
7878

79-
def __init__(self, *args, **kwargs):
80-
self.on_resouces_loaded(self.post_load)
81-
8279
def post_load(self):
8380
"""
84-
Called when all resources are loaded before effects start running.
85-
This assumes you have called Effect.__init__()
81+
Called when all effects are initialized before effects start running.
8682
"""
8783
pass
8884

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

218-
# Register callbacks
219-
220-
def on_resouces_loaded(self, func):
221-
"""Register callback function when all resources are loaded"""
222-
resources.on_loaded(func)
223-
224-
def on_shaders_loaded(self, func):
225-
"""Register callback function when shaders are loaded"""
226-
resources.shaders.on_loaded(func)
227-
228-
def on_textures_loaded(self, func):
229-
"""Register callback function when textures are loaded"""
230-
resources.textures.on_loaded(func)
231-
232-
def on_scenes_loaded(self, func):
233-
"""Register callback function when scenes are loaded"""
234-
resources.scenes.on_loaded(func)
235-
236-
def on_data_loaded(self, func):
237-
"""Register callback function when data files are loaded"""
238-
resources.data.on_loaded(func)
239-
240214
# Utility methods for matrices
241215

242216
def create_projection(self, fov=75.0, near=1.0, far=100.0, ratio=None):

demosys/effects/managers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def pre_load(self):
8686
return True
8787

8888
def post_load(self):
89+
self.active_effect.post_load()
8990
return True
9091

9192
def draw(self, time, frametime, target):

demosys/opengl/shader.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ def __init__(self, path=None, name=None):
2020
:param path: Full file path to the shader
2121
:param name: Name of the shader (debug purposes)
2222
"""
23-
self.ctx = context.ctx()
2423
if not path and not name:
2524
raise ShaderError("Shader must have a path or a name")
2625

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

49+
@property
50+
def ctx(self) -> moderngl.Context:
51+
"""The moderngl context"""
52+
return context.ctx()
53+
5054
def __getitem__(self, key) -> Union[moderngl.Uniform, moderngl.UniformBlock, moderngl.Subroutine,
5155
moderngl.Attribute, moderngl.Varying]:
5256
return self.mglo[key]
@@ -149,15 +153,15 @@ def prepare(self, reload=False):
149153
program = self.ctx.program(**params)
150154

151155
if reload:
152-
self.program.release()
156+
self.release()
153157

154158
self.program = program
155159

156160
# Build internal lookups
157161
self._build_uniform_map()
158162
self._build_attribute_map()
159163

160-
def _delete(self):
164+
def release(self):
161165
"""Frees the memory and invalidates the name associated with the program"""
162166
if self.program:
163167
self.program.release()

demosys/opengl/texture/array.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from demosys import context
44

5-
from .base import BaseTexture
5+
from .base import BaseTexture, image_data
66

77

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

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

7071
self.mglo = self.ctx.texture_array(
7172
(width, height, depth),
72-
4,
73-
image.convert("RGBA").tobytes(),
73+
components,
74+
data,
7475
)
7576

7677
if self.mipmap:

demosys/opengl/texture/base.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,27 @@
44
from demosys import context
55

66

7+
def image_data(image):
8+
"""Get components and bytes for an image"""
9+
# NOTE: We might want to check the actual image.mode
10+
# and convert to an acceptable format.
11+
# At the moment we load the data as is.
12+
data = image.tobytes()
13+
components = len(data) // (image.size[0] * image.size[1])
14+
return components, data
15+
16+
717
class BaseTexture:
818
"""
919
Wraps the basic functionality of the ModernGL methods
1020
"""
1121
def __init__(self):
1222
self.mglo = None # Type: Union[moderngl.Texture, moderngl.TextureArray]
13-
self._ctx = context.ctx()
23+
24+
@property
25+
def ctx(self) -> moderngl.Context:
26+
"""ModernGL context"""
27+
return context.ctx()
1428

1529
def use(self, location=0):
1630
"""
@@ -65,11 +79,6 @@ def release(self):
6579
"""Release/free the ModernGL object"""
6680
self.mglo.release()
6781

68-
@property
69-
def ctx(self) -> moderngl.Context:
70-
"""ModernGL context"""
71-
return self._ctx
72-
7382
@property
7483
def size(self) -> Tuple:
7584
"""The size of the texture"""

demosys/opengl/texture/texture.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import moderngl
44
from demosys import context
55

6-
from .base import BaseTexture
6+
from .base import BaseTexture, image_data
77

88

99
class Texture2D(BaseTexture):
@@ -91,10 +91,12 @@ def set_image(self, image, flip=True):
9191
if flip:
9292
image = image.transpose(Image.FLIP_TOP_BOTTOM)
9393

94+
components, data = image_data(image)
95+
9496
self.mglo = self.ctx.texture(
9597
image.size,
96-
4,
97-
image.convert("RGBA").tobytes(),
98+
components,
99+
data,
98100
)
99101

100102
if self.mipmap:

demosys/opengl/vao.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,17 @@ def _create_vao_instance(self, shader):
239239

240240
return vao
241241

242+
def release(self):
243+
"""Destroy the vao object and its buffers"""
244+
for key, vao in self.vaos:
245+
vao.release()
246+
247+
for buff in self.buffers:
248+
buff.buffer.release()
249+
250+
if self._index_buffer:
251+
self._index_buffer.release()
252+
242253

243254
class VAOError(Exception):
244255
pass

demosys/resources/__init__.py

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,12 @@
2020
]
2121

2222

23-
class States:
24-
on_loaded_funcs = []
25-
on_load_funcs = []
26-
loaded = False
27-
28-
@classmethod
29-
def sort_callbacks(cls):
30-
cls.on_load_funcs = sorted(cls.on_load_funcs, key=lambda x: x[0])
31-
cls.on_loaded_funcs = sorted(cls.on_loaded_funcs, key=lambda x: x[0])
32-
33-
3423
def load():
35-
States.sort_callbacks()
36-
37-
for func in reversed(States.on_load_funcs):
38-
func[1]()
39-
40-
scenes.load()
41-
shaders.load()
42-
textures.load()
43-
tracks.load()
44-
data.load()
45-
46-
States.loaded = True
47-
for func in reversed(States.on_loaded_funcs):
48-
func[1]()
24+
scenes.load_pool()
25+
shaders.load_pool()
26+
textures.load_pool()
27+
data.load_pool()
4928

5029

5130
def count():
52-
return shaders.count + textures.count
53-
54-
55-
def loading_complete():
56-
return States.loaded
57-
58-
59-
def on_load(func, priority=0):
60-
States.on_load_funcs.append((priority, func))
61-
62-
63-
def on_loaded(func, priority=0):
64-
if loading_complete():
65-
func()
66-
return
67-
68-
States.on_loaded_funcs.append((priority, func))
31+
return scenes.count + shaders.count + textures.count + data.count

0 commit comments

Comments
 (0)