yta-editor-pools 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: yta-editor-pools
3
+ Version: 0.0.1
4
+ Summary: Youtube Autonomous Main Editor Pools Module
5
+ License-File: LICENSE
6
+ Author: danialcala94
7
+ Author-email: danielalcalavalera@gmail.com
8
+ Requires-Python: ==3.9
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Requires-Dist: moderngl (>=0.0.1,<9.0.0)
12
+ Requires-Dist: yta_validation (>=0.0.1,<1.0.0)
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Youtube Autonomous Main Editor Pools Module
16
+
17
+ The pools we need to use in our editor, related to the `moderngl.Texture` instances and to our own-designed `RenderTarget` class.
@@ -0,0 +1,3 @@
1
+ # Youtube Autonomous Main Editor Pools Module
2
+
3
+ The pools we need to use in our editor, related to the `moderngl.Texture` instances and to our own-designed `RenderTarget` class.
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "yta-editor-pools"
3
+ version = "0.0.1"
4
+ description = "Youtube Autonomous Main Editor Pools Module"
5
+ authors = [
6
+ {name = "danialcala94", email = "danielalcalavalera@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = "==3.9"
10
+ dependencies = [
11
+ "yta_validation (>=0.0.1,<1.0.0)",
12
+ "moderngl (>=0.0.1,<9.0.0)",
13
+ ]
14
+
15
+ [tool.poetry]
16
+ packages = [{include = "yta_editor_pools", from = "src"}]
17
+
18
+ [tool.poetry.group.dev.dependencies]
19
+ pytest = "^8.3.5"
20
+ yta_testing = ">=0.0.1"
21
+ yta_video_opengl = ">=0.0.1"
22
+
23
+ [tool.pytest.ini_options]
24
+ markers = [
25
+ "mandatory: mandatory tests for release",
26
+ "additional: exhaustive and demanding tests"
27
+ ]
28
+
29
+ [build-system]
30
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
31
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,5 @@
1
+ """
2
+ Library to include the pools we need to operate
3
+ properly, being able to avoid recreating
4
+ expensive objects.
5
+ """
@@ -0,0 +1,159 @@
1
+ from yta_validation.parameter import ParameterValidator
2
+
3
+ import moderngl
4
+
5
+
6
+ class RenderTarget:
7
+ """
8
+ An object that acts as a unit the system uses
9
+ to render content on it, attached to a texture
10
+ and an FBO.
11
+
12
+ The purpose of this class is to have an object
13
+ in which our nodes are able to write. It
14
+ includes a texture but it is always attached to
15
+ it and it doesn't come from any pool.
16
+
17
+ The `RenderTarget` itself belongs to the pool
18
+ while it is not being used and to the consumer
19
+ after the `.acquire()` method is called.
20
+ """
21
+
22
+ __slots__ = (
23
+ 'context',
24
+ 'texture',
25
+ '_fbo',
26
+ 'width',
27
+ 'height',
28
+ 'number_of_components',
29
+ 'dtype',
30
+ '_is_in_use'
31
+ )
32
+
33
+ @property
34
+ def fbo(
35
+ self
36
+ ) -> moderngl.Framebuffer:
37
+ """
38
+ Lazy FBO.
39
+ """
40
+ if self._fbo is None:
41
+ self._fbo = self.context.framebuffer(
42
+ color_attachments = [self.texture]
43
+ )
44
+
45
+ return self._fbo
46
+
47
+ def __init__(
48
+ self,
49
+ context: moderngl.Context,
50
+ texture: moderngl.Texture,
51
+ width: int,
52
+ height: int,
53
+ number_of_components: int,
54
+ dtype: str
55
+ ):
56
+ # TODO: Add docu
57
+ self.context = context
58
+ self.texture: moderngl.Texture = texture
59
+ """
60
+ The texture of the render target, that is always
61
+ attached to this `RenderTarget` instance.
62
+ """
63
+ self.width = width
64
+ self.height = height
65
+ self.number_of_components = number_of_components
66
+ self.dtype = dtype
67
+
68
+ self._fbo: moderngl.Framebuffer | None = None
69
+ self._is_in_use: bool = False
70
+
71
+ def reset_state(
72
+ self
73
+ ):
74
+ """
75
+ TODO: Document
76
+ """
77
+ self.texture.filter = (moderngl.LINEAR, moderngl.LINEAR)
78
+
79
+ def use(
80
+ self
81
+ ):
82
+ """
83
+ Bind FBO and prepare viewport.
84
+ """
85
+ self.fbo.use()
86
+ self.context.viewport = (0, 0, self.width, self.height)
87
+
88
+ def clear(
89
+ self
90
+ ):
91
+ """
92
+ Clear the context.
93
+ """
94
+ self.context.clear(0.0, 0.0, 0.0, 0.0)
95
+
96
+ def write(
97
+ self,
98
+ data: 'np.ndarray'
99
+ ) -> 'RenderTarget':
100
+ """
101
+ Write the `data` numpy array provided directly in
102
+ the texture, which is uploading from CPU to GPU.
103
+ """
104
+ ParameterValidator.validate_mandatory_numpy_array('data', data)
105
+
106
+ """
107
+ The `.write()` method accepts a numpy array only,
108
+ if no we have to do `.tobytes()` to whatever we
109
+ are trying to write.
110
+ """
111
+ self.texture.write(data)
112
+
113
+ return self
114
+
115
+ def read(
116
+ self
117
+ ) -> bytes:
118
+ """
119
+ Read the information existing in the `texture`
120
+ inside this `RenderTarget` instance, which is
121
+ downloading from GPU to CPU.
122
+ """
123
+ return self.texture.read()
124
+
125
+ def mark_in_use(
126
+ self
127
+ ) -> 'RenderTarget':
128
+ """
129
+ Set this `RenderTarget` instance as in use.
130
+ """
131
+ assert not self._is_in_use, 'RenderTarget already in use'
132
+ self._is_in_use = True
133
+
134
+ return self
135
+
136
+ def mark_free(
137
+ self
138
+ ) -> 'RenderTarget':
139
+ """
140
+ Set this `RenderTarget` instance as not in use.
141
+ """
142
+ assert self._is_in_use, 'RenderTarget already free'
143
+ self._is_in_use = False
144
+
145
+ return self
146
+
147
+ def release_gpu(
148
+ self
149
+ ):
150
+ """
151
+ Free the real GPU resources (not back to the pool).
152
+ """
153
+ if self._fbo:
154
+ self._fbo.release()
155
+ self._fbo = None
156
+
157
+ if self.texture:
158
+ self.texture.release()
159
+ self.texture = None
@@ -0,0 +1,123 @@
1
+ from yta_editor_pools.render_target import RenderTarget
2
+ from yta_editor_pools.texture.utils import get_texture_key
3
+ from collections import defaultdict
4
+
5
+ import moderngl
6
+
7
+
8
+ class RenderTargetPool:
9
+ """
10
+ A pool of `RenderTarget` instances.
11
+
12
+ These instances are built to be used by our
13
+ nodes that will be using the shaders, uniforms
14
+ and inputs to write with an specific FBO and a
15
+ single texture that is attached to it.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ context: moderngl.Context
21
+ ):
22
+ self.context: moderngl.Context = context
23
+ """
24
+ The `moderngl` context we need to work with.
25
+ """
26
+ self.pool = defaultdict(list)
27
+ """
28
+ The internal pool to store the `RenderTarget`
29
+ instances.
30
+ """
31
+
32
+ def acquire(
33
+ self,
34
+ width: int = 1920,
35
+ height: int = 1080,
36
+ number_of_components: int = 4,
37
+ dtype: str = 'f1'
38
+ ) -> RenderTarget:
39
+ """
40
+ Get a `RenderTarget` from the pool with the given
41
+ parameters, or create a new one if non existing
42
+ and mark in use.
43
+ """
44
+ key = get_texture_key(
45
+ width = width,
46
+ height = height,
47
+ number_of_components = number_of_components,
48
+ dtype = dtype
49
+ )
50
+
51
+ if self.pool[key]:
52
+ render_target = self.pool[key].pop()
53
+ else:
54
+ texture = self.context.texture(
55
+ (width, height),
56
+ components = number_of_components,
57
+ dtype = dtype
58
+ )
59
+
60
+ #texture.filter = (moderngl.LINEAR, moderngl.LINEAR)
61
+
62
+ render_target = RenderTarget(
63
+ context = self.context,
64
+ texture = texture,
65
+ width = width,
66
+ height = height,
67
+ number_of_components = number_of_components,
68
+ dtype = dtype
69
+ )
70
+
71
+ render_target.reset_state()
72
+ render_target.mark_in_use()
73
+
74
+ return render_target
75
+
76
+ def acquire_like(
77
+ self,
78
+ render_target: 'RenderTarget'
79
+ ) -> 'RenderTarget':
80
+ """
81
+ Get a `RenderTarget` from the pool with the same
82
+ parameters than the `render_target` provided, or
83
+ create a new one if non existing and mark in use.
84
+ """
85
+ return self.acquire(
86
+ width = render_target.width,
87
+ height = render_target.height,
88
+ number_of_components = render_target.number_of_components,
89
+ dtype = render_target.dtype
90
+ )
91
+
92
+ def release(
93
+ self,
94
+ render_target: RenderTarget
95
+ ) -> None:
96
+ """
97
+ Mark the `render_target` provided as free and
98
+ return it to the pool.
99
+ """
100
+ render_target.mark_free()
101
+
102
+ key = get_texture_key(
103
+ width = render_target.width,
104
+ height = render_target.height,
105
+ number_of_components = render_target.number_of_components,
106
+ dtype = render_target.dtype
107
+ )
108
+
109
+ self.pool[key].append(render_target)
110
+
111
+ # TODO: Is it ok like this (?)
112
+ def clear(
113
+ self
114
+ ):
115
+ """
116
+ Empty the whole pool by also freeing the resources
117
+ of each `RenderTarget` instance.
118
+ """
119
+ for render_targets in self.pool.values():
120
+ for render_target in render_targets:
121
+ render_target.release_gpu()
122
+
123
+ self.pool.clear()
@@ -0,0 +1,85 @@
1
+ from yta_editor_pools.render_target.pool import RenderTargetPool
2
+ from yta_validation.parameter import ParameterValidator
3
+
4
+ import moderngl
5
+
6
+
7
+ class RenderTargetProvider:
8
+ """
9
+ Class to provide `RenderTarget` instances by using
10
+ a pool internally that is unique for the context
11
+ provided.
12
+ """
13
+
14
+ _instances: dict[int, 'RenderTargetProvider'] = {}
15
+
16
+ @classmethod
17
+ def get(
18
+ cls,
19
+ opengl_context: moderngl.Context
20
+ ) -> 'RenderTargetProvider':
21
+ """
22
+ Get the singleton instance for the specific
23
+ `opengl_context` provided as parameter.
24
+ """
25
+ key = id(opengl_context)
26
+ if key not in cls._instances:
27
+ cls._instances[key] = cls(opengl_context)
28
+
29
+ return cls._instances[key]
30
+
31
+ def __init__(
32
+ self,
33
+ moderngl_context: 'moderngl.Context'
34
+ ):
35
+ ParameterValidator.validate_mandatory_instance_of('moderngl_context', moderngl_context, 'Context')
36
+
37
+ self._pool: RenderTargetPool = RenderTargetPool(moderngl_context)
38
+ """
39
+ *For internal use only*
40
+
41
+ The pool of `RenderTarget` instances.
42
+ """
43
+
44
+ def acquire(
45
+ self,
46
+ width: int = 1920,
47
+ height: int = 1080,
48
+ number_of_components: int = 4,
49
+ dtype: str = 'f1'
50
+ ) -> 'RenderTarget':
51
+ """
52
+ Get a `RenderTarget` instance with the given `width`,
53
+ `height`, `number_of_components` and `dtype` from the
54
+ pool, that will mark it as in use.
55
+ """
56
+ return self._pool.acquire(
57
+ width = width,
58
+ height = height,
59
+ number_of_components = number_of_components,
60
+ dtype = dtype
61
+ )
62
+
63
+ def acquire_like(
64
+ self,
65
+ render_target: 'RenderTarget'
66
+ ) -> 'RenderTarget':
67
+ """
68
+ Get the `RenderTarget` instance with the same
69
+ properties than the `render_target` provided as
70
+ parameter.
71
+ """
72
+ return self._pool.acquire_like(
73
+ render_target = render_target
74
+ )
75
+
76
+ def release(
77
+ self,
78
+ render_target: 'RenderTarget'
79
+ ):
80
+ """
81
+ Release the `render_target` provided, making it
82
+ available again through the pool and marking it
83
+ as free.
84
+ """
85
+ self._pool.release(render_target)
@@ -0,0 +1,92 @@
1
+ from yta_editor_pools.texture.utils import get_texture_key
2
+ from collections import defaultdict
3
+
4
+ import moderngl
5
+
6
+
7
+ class TexturePool:
8
+ """
9
+ A pool of `TexturePool` instances.
10
+
11
+ These instances will be used to directly write
12
+ information on them and use, most of the times,
13
+ for CPU to GPU uploading.
14
+ """
15
+
16
+ def __init__(
17
+ self,
18
+ context: moderngl.Context
19
+ ):
20
+ self.context: moderngl.Context = context
21
+ """
22
+ The `moderngl` context we need to work with.
23
+ """
24
+ self.pool = defaultdict(list)
25
+ """
26
+ The internal pool to store the `moderngl.Texture`
27
+ instances.
28
+ """
29
+
30
+ def acquire(
31
+ self,
32
+ width: int = 1920,
33
+ height: int = 1080,
34
+ number_of_components: int = 4,
35
+ dtype: str = 'f1'
36
+ ) -> moderngl.Texture:
37
+ """
38
+ Get a `moderngl.Texture` from the pool with the
39
+ given parameters, or create a new one if non
40
+ existing and mark in use.
41
+ """
42
+ key = get_texture_key(
43
+ width = width,
44
+ height = height,
45
+ number_of_components = number_of_components,
46
+ dtype = dtype
47
+ )
48
+
49
+ return (
50
+ # Obtained from pool
51
+ self.pool[key].pop()
52
+ if self.pool[key] else
53
+ # Create new one
54
+ self.context.texture(
55
+ (width, height),
56
+ components = number_of_components,
57
+ dtype = dtype
58
+ )
59
+ )
60
+
61
+ def acquire_like(
62
+ self,
63
+ texture: moderngl.Texture
64
+ ) -> moderngl.Texture:
65
+ """
66
+ Get a `moderngl.Texture` from the pool with the same
67
+ parameters than the `texture` provided, or
68
+ create a new one if non existing and mark in use.
69
+ """
70
+ return self.acquire(
71
+ width = texture.width,
72
+ height = texture.height,
73
+ number_of_components = texture.components,
74
+ dtype = texture.dtype
75
+ )
76
+
77
+ def release(
78
+ self,
79
+ texture: moderngl.Texture
80
+ ) -> None:
81
+ """
82
+ Mark the `texture` provided as free and return
83
+ it to the pool.
84
+ """
85
+ key = get_texture_key(
86
+ width = texture.width,
87
+ height = texture.height,
88
+ number_of_components = texture.components,
89
+ dtype = texture.dtype
90
+ )
91
+
92
+ self.pool[key].append(texture)
@@ -0,0 +1,84 @@
1
+ from yta_editor_pools.texture.pool import TexturePool
2
+ from yta_validation.parameter import ParameterValidator
3
+
4
+ import moderngl
5
+
6
+
7
+ class TextureProvider:
8
+ """
9
+ Class to provide `moderngl.Texture` instances by
10
+ using a pool internally that is unique for the
11
+ context provided.
12
+ """
13
+
14
+ _instances: dict[int, 'TextureProvider'] = {}
15
+
16
+ @classmethod
17
+ def get(
18
+ cls,
19
+ opengl_context: moderngl.Context
20
+ ) -> 'TextureProvider':
21
+ """
22
+ Get the singleton instance for the specific
23
+ `opengl_context` provided as parameter.
24
+ """
25
+ key = id(opengl_context)
26
+ if key not in cls._instances:
27
+ cls._instances[key] = cls(opengl_context)
28
+
29
+ return cls._instances[key]
30
+
31
+ def __init__(
32
+ self,
33
+ moderngl_context: 'moderngl.Context'
34
+ ):
35
+ ParameterValidator.validate_mandatory_instance_of('moderngl_context', moderngl_context, 'Context')
36
+
37
+ self._pool: TexturePool = TexturePool(moderngl_context)
38
+ """
39
+ *For internal use only*
40
+
41
+ The pool of `moderngl.Texture` instances.
42
+ """
43
+
44
+ def acquire(
45
+ self,
46
+ width: int = 1920,
47
+ height: int = 1080,
48
+ number_of_components: int = 4,
49
+ dtype: str = 'f1'
50
+ ) -> moderngl.Texture:
51
+ """
52
+ Get a `moderngl.Texture` instance with the given `width`,
53
+ `height`, `number_of_components` and `dtype` from the
54
+ pool.
55
+ """
56
+ return self._pool.acquire(
57
+ width = width,
58
+ height = height,
59
+ number_of_components = number_of_components,
60
+ dtype = dtype
61
+ )
62
+
63
+ def acquire_like(
64
+ self,
65
+ texture: moderngl.Texture
66
+ ) -> moderngl.Texture:
67
+ """
68
+ Get the `moderngl.Texture` instance with the same
69
+ properties than the `texture` provided as
70
+ parameter.
71
+ """
72
+ return self._pool.acquire_like(
73
+ texture = texture
74
+ )
75
+
76
+ def release(
77
+ self,
78
+ texture: 'moderngl.Texture'
79
+ ):
80
+ """
81
+ Release the `texture` provided, making it available
82
+ again through the pool.
83
+ """
84
+ self._pool.release(texture)
@@ -0,0 +1,29 @@
1
+ def get_texture_key(
2
+ width: int = 1920,
3
+ height: int = 1080,
4
+ number_of_components: int = 4,
5
+ dtype: str = 'f1'
6
+ ) -> tuple[int, int, int, str]:
7
+ """
8
+ *For internal use only*
9
+
10
+ Get the key for the texture with the provided
11
+ specifications.
12
+ """
13
+ return (width, height, number_of_components, dtype)
14
+
15
+ def get_texture_key_from_texture(
16
+ texture: 'moderngl.Texture'
17
+ ) -> tuple[int, int, int, str]:
18
+ """
19
+ *For internal use only*
20
+
21
+ Get the key for the texture based on the data
22
+ that the `texture` provided has.
23
+ """
24
+ return get_texture_key(
25
+ width = texture.width,
26
+ height = texture.height,
27
+ number_of_components = texture.components,
28
+ dtype = texture.dtype
29
+ )