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.
- yta_editor_pools-0.0.1/LICENSE +19 -0
- yta_editor_pools-0.0.1/PKG-INFO +17 -0
- yta_editor_pools-0.0.1/README.md +3 -0
- yta_editor_pools-0.0.1/pyproject.toml +31 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/__init__.py +5 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/render_target/__init__.py +159 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/render_target/pool.py +123 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/render_target/provider.py +85 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/texture/__init__.py +0 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/texture/pool.py +92 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/texture/provider.py +84 -0
- yta_editor_pools-0.0.1/src/yta_editor_pools/texture/utils.py +29 -0
|
@@ -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,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,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)
|
|
File without changes
|
|
@@ -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
|
+
)
|