Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5e9f544

Browse filesBrowse files
committed
GPU VRAM and system RAM freed for all graphics
1 parent 247b44b commit 5e9f544
Copy full SHA for 5e9f544

File tree

Expand file treeCollapse file tree

5 files changed

+97
-41
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+97
-41
lines changed

‎fastplotlib/graphics/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_base.py
+31-11Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ def __init_subclass__(cls, **kwargs):
5050

5151
class Graphic(BaseGraphic):
5252
def __init__(
53-
self, name: str = None):
53+
self, name: str = None
54+
):
5455
"""
5556
5657
Parameters
@@ -64,15 +65,16 @@ def __init__(
6465
self.registered_callbacks = dict()
6566
self.present = PresentFeature(parent=self)
6667

67-
self._world_objects = WORLD_OBJECTS
68+
# store hex id str of Graphic instance mem location
69+
self.loc: str = hex(id(self))
6870

6971
@property
7072
def world_object(self) -> WorldObject:
7173
"""Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly."""
72-
return weakref.proxy(self._world_objects[hex(id(self))])
74+
return weakref.proxy(WORLD_OBJECTS[hex(id(self))])
7375

7476
def _set_world_object(self, wo: WorldObject):
75-
self._world_objects[hex(id(self))] = wo
77+
WORLD_OBJECTS[hex(id(self))] = wo
7678

7779
@property
7880
def position(self) -> Vector3:
@@ -112,7 +114,7 @@ def __repr__(self):
112114
return rval
113115

114116
def __del__(self):
115-
del self._world_objects[hex(id(self))]
117+
del WORLD_OBJECTS[self.loc]
116118

117119

118120
class Interaction(ABC):
@@ -278,17 +280,21 @@ class PreviouslyModifiedData:
278280
indices: Any
279281

280282

283+
COLLECTION_GRAPHICS: dict[str, Graphic] = dict()
284+
285+
281286
class GraphicCollection(Graphic):
282287
"""Graphic Collection base class"""
283288

284289
def __init__(self, name: str = None):
285290
super(GraphicCollection, self).__init__(name)
286-
self._graphics: List[Graphic] = list()
291+
self._graphics: List[str] = list()
287292

288293
@property
289294
def graphics(self) -> Tuple[Graphic]:
290-
"""returns the Graphics within this collection"""
291-
return tuple(self._graphics)
295+
"""The Graphics within this collection. Always returns a proxy to the Graphics."""
296+
proxies = [weakref.proxy(COLLECTION_GRAPHICS[loc]) for loc in self._graphics]
297+
return tuple(proxies)
292298

293299
def add_graphic(self, graphic: Graphic, reset_index: True):
294300
"""Add a graphic to the collection"""
@@ -298,17 +304,31 @@ def add_graphic(self, graphic: Graphic, reset_index: True):
298304
f"You can only add {self.child_type} to a {self.__class__.__name__}, "
299305
f"you are trying to add a {graphic.__class__.__name__}."
300306
)
301-
self._graphics.append(graphic)
307+
308+
loc = hex(id(graphic))
309+
COLLECTION_GRAPHICS[loc] = graphic
310+
311+
self._graphics.append(loc)
302312
if reset_index:
303313
self._reset_index()
304314
self.world_object.add(graphic.world_object)
305315

306316
def remove_graphic(self, graphic: Graphic, reset_index: True):
307317
"""Remove a graphic from the collection"""
308318
self._graphics.remove(graphic)
319+
309320
if reset_index:
310321
self._reset_index()
311-
self.world_object.remove(graphic)
322+
323+
self.world_object.remove(graphic.world_object)
324+
325+
def __del__(self):
326+
self.world_object.clear()
327+
328+
for loc in self._graphics:
329+
del COLLECTION_GRAPHICS[loc]
330+
331+
super().__del__()
312332

313333
def _reset_index(self):
314334
for new_index, graphic in enumerate(self._graphics):
@@ -374,7 +394,7 @@ def __init__(
374394
selection_indices: Union[list, range]
375395
the corresponding indices from the parent GraphicCollection that were selected
376396
"""
377-
self._parent = parent
397+
self._parent = weakref.proxy(parent)
378398
self._selection = selection
379399
self._selection_indices = selection_indices
380400

‎fastplotlib/graphics/features/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/features/_base.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from inspect import getfullargspec
33
from warnings import warn
44
from typing import *
5+
import weakref
56

67
import numpy as np
78
from pygfx import Buffer, Texture
@@ -71,7 +72,7 @@ def __init__(self, parent, data: Any, collection_index: int = None):
7172
if part of a collection, index of this graphic within the collection
7273
7374
"""
74-
self._parent = parent
75+
self._parent = weakref.proxy(parent)
7576

7677
self._data = to_gpu_supported_dtype(data)
7778

‎fastplotlib/graphics/text.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/text.py
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ def __init__(
3838
"""
3939
super(TextGraphic, self).__init__(name=name)
4040

41-
self._world_object = pygfx.Text(
41+
world_object = pygfx.Text(
4242
pygfx.TextGeometry(text=text, font_size=size, screen_space=False),
4343
pygfx.TextMaterial(color=face_color, outline_color=outline_color, outline_thickness=outline_thickness)
4444
)
4545

46+
self._set_world_object(world_object)
47+
4648
self.world_object.position.set(*position)
4749

4850
self.name = None

‎fastplotlib/layouts/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_base.py
+57-26Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1+
from warnings import warn
2+
from typing import *
3+
import weakref
4+
15
import numpy as np
6+
27
from pygfx import Scene, OrthographicCamera, PerspectiveCamera, PanZoomController, OrbitController, \
38
Viewport, WgpuRenderer
49
from wgpu.gui.auto import WgpuCanvas
5-
from warnings import warn
6-
from ..graphics._base import Graphic, GraphicCollection, WORLD_OBJECTS
10+
11+
from ..graphics._base import Graphic, GraphicCollection
712
from ..graphics.line_slider import LineSlider
8-
from typing import *
13+
14+
15+
# dict to store Graphic instances
16+
# this is the only place where the real references to Graphics are stored in a Python session
17+
# {hex id str: Graphic}
18+
GRAPHICS: Dict[str, Graphic] = dict()
919

1020

1121
class PlotArea:
@@ -74,7 +84,9 @@ def __init__(
7484

7585
self.renderer.add_event_handler(self.set_viewport_rect, "resize")
7686

77-
self._graphics: List[Graphic] = list()
87+
# list of hex id strings for all graphics managed by this PlotArea
88+
# the real Graphic instances are stored in the ``GRAPHICS`` dict
89+
self._graphics: List[str] = list()
7890

7991
# hacky workaround for now to exclude from bbox calculations
8092
self._sliders: List[LineSlider] = list()
@@ -129,8 +141,13 @@ def controller(self) -> Union[PanZoomController, OrbitController]:
129141

130142
@property
131143
def graphics(self) -> Tuple[Graphic]:
132-
"""returns the Graphics in the plot area"""
133-
return tuple(self._graphics)
144+
"""Graphics in the plot area. Always returns a proxy to the Graphic instances."""
145+
proxies = list()
146+
for loc in self._graphics:
147+
p = weakref.proxy(GRAPHICS[loc])
148+
proxies.append(p)
149+
150+
return tuple(proxies)
134151

135152
def get_rect(self) -> Tuple[float, float, float, float]:
136153
"""allows setting the region occupied by the viewport w.r.t. the parent"""
@@ -154,7 +171,8 @@ def add_graphic(self, graphic: Graphic, center: bool = True):
154171
Parameters
155172
----------
156173
graphic: Graphic or GraphicCollection
157-
Add a Graphic or a GraphicCollection to the plot area
174+
Add a Graphic or a GraphicCollection to the plot area.
175+
Note: this must be a real Graphic instance and not a proxy
158176
159177
center: bool, default True
160178
Center the camera on the newly added Graphic
@@ -168,12 +186,17 @@ def add_graphic(self, graphic: Graphic, center: bool = True):
168186
if graphic.name is not None: # skip for those that have no name
169187
self._check_graphic_name_exists(graphic.name)
170188

189+
# store in GRAPHICS dict
190+
loc = graphic.loc
191+
GRAPHICS[loc] = graphic
192+
171193
# TODO: need to refactor LineSlider entirely
172194
if isinstance(graphic, LineSlider):
173-
self._sliders.append(graphic)
195+
self._sliders.append(graphic) # don't manage garbage collection of LineSliders for now
174196
else:
175-
self._graphics.append(graphic)
197+
self._graphics.append(loc) # add hex id string for referencing this graphic instance
176198

199+
# add world object to scene
177200
self.scene.add(graphic.world_object)
178201

179202
if center:
@@ -185,7 +208,7 @@ def add_graphic(self, graphic: Graphic, center: bool = True):
185208
def _check_graphic_name_exists(self, name):
186209
graphic_names = list()
187210

188-
for g in self._graphics:
211+
for g in self.graphics:
189212
graphic_names.append(g.name)
190213

191214
if name in graphic_names:
@@ -311,45 +334,53 @@ def delete_graphic(self, graphic: Graphic):
311334
312335
"""
313336

314-
if graphic not in self._graphics:
315-
raise KeyError
337+
# graphic_loc = hex(id(graphic.__repr__.__self__))
338+
339+
# get location
340+
graphic_loc = graphic.loc
341+
342+
if graphic_loc not in self._graphics:
343+
raise KeyError(f"Graphic with following address not found in plot area: {graphic_loc}")
316344

345+
# remove from scene if necessary
317346
if graphic.world_object in self.scene.children:
318347
self.scene.remove(graphic.world_object)
319348

320-
self._graphics.remove(graphic)
349+
# remove from list of addresses
350+
self._graphics.remove(graphic_loc)
321351

322352
# for GraphicCollection objects
323-
if isinstance(graphic, GraphicCollection):
324-
# clear Group
325-
graphic.world_object.clear()
353+
# if isinstance(graphic, GraphicCollection):
354+
# # clear Group
355+
# graphic.world_object.clear()
356+
# graphic.clear()
326357
# delete all child world objects in the collection
327-
for g in graphic.graphics:
328-
subloc = hex(id(g))
329-
del WORLD_OBJECTS[subloc]
358+
# for g in graphic.graphics:
359+
# subloc = hex(id(g))
360+
# del WORLD_OBJECTS[subloc]
330361

331362
# get mem location of graphic
332-
loc = hex(id(graphic))
363+
# loc = hex(id(graphic))
333364
# delete world object
334-
del WORLD_OBJECTS[loc]
365+
#del WORLD_OBJECTS[graphic_loc]
335366

336-
del graphic
367+
del GRAPHICS[graphic_loc]
337368

338369
def clear(self):
339370
"""
340371
Clear the Plot or Subplot. Also performs garbage collection, i.e. runs ``delete_graphic`` on all graphics.
341372
"""
342373

343-
for g in self._graphics:
374+
for g in self.graphics:
344375
self.delete_graphic(g)
345376

346377
def __getitem__(self, name: str):
347-
for graphic in self._graphics:
378+
for graphic in self.graphics:
348379
if graphic.name == name:
349380
return graphic
350381

351382
graphic_names = list()
352-
for g in self._graphics:
383+
for g in self.graphics:
353384
graphic_names.append(g.name)
354385
raise IndexError(f"no graphic of given name, the current graphics are:\n {graphic_names}")
355386

@@ -367,5 +398,5 @@ def __repr__(self):
367398
return f"{self}\n" \
368399
f" parent: {self.parent}\n" \
369400
f" Graphics:\n" \
370-
f"\t{newline.join(graphic.__repr__() for graphic in self._graphics)}" \
401+
f"\t{newline.join(graphic.__repr__() for graphic in self.graphics)}" \
371402
f"\n"

‎fastplotlib/layouts/_subplot.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_subplot.py
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import numpy as np
33
from math import copysign
44
from functools import partial
5+
import weakref
56
from inspect import signature, getfullargspec
67
from warnings import warn
78

@@ -112,7 +113,7 @@ def __init__(
112113
if self.name is not None:
113114
self.set_title(self.name)
114115

115-
def _create_graphic(self, graphic_class, *args, **kwargs):
116+
def _create_graphic(self, graphic_class, *args, **kwargs) -> weakref.proxy:
116117
if "center" in kwargs.keys():
117118
center = kwargs.pop("center")
118119
else:
@@ -124,7 +125,8 @@ def _create_graphic(self, graphic_class, *args, **kwargs):
124125
graphic = graphic_class(*args, **kwargs)
125126
self.add_graphic(graphic, center=center)
126127

127-
return graphic
128+
# only return a proxy to the real graphic
129+
return weakref.proxy(graphic)
128130

129131
def set_title(self, text: Any):
130132
"""Sets the name of a subplot to 'top' viewport if defined."""

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.