turbopipe 1.0.5__tar.gz → 1.2.0__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.
Potentially problematic release.
This version of turbopipe might be problematic. Click here for more details.
- {turbopipe-1.0.5 → turbopipe-1.2.0}/.github/workflows/release.yaml +20 -2
- {turbopipe-1.0.5 → turbopipe-1.2.0}/PKG-INFO +24 -20
- {turbopipe-1.0.5 → turbopipe-1.2.0}/Readme.md +22 -18
- {turbopipe-1.0.5 → turbopipe-1.2.0}/meson.build +0 -2
- {turbopipe-1.0.5 → turbopipe-1.2.0}/pyproject.toml +1 -1
- turbopipe-1.2.0/turbopipe/__init__.py +47 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/turbopipe/_turbopipe.cpp +49 -92
- turbopipe-1.2.0/turbopipe/version.py +5 -0
- turbopipe-1.0.5/.github/workflows/debug.yaml +0 -42
- turbopipe-1.0.5/turbopipe/__init__.py +0 -41
- turbopipe-1.0.5/turbopipe/include/gl_methods.hpp +0 -2591
- turbopipe-1.0.5/turbopipe/include/glcorearb.h +0 -5991
- turbopipe-1.0.5/turbopipe/include/khrplatform.h +0 -311
- turbopipe-1.0.5/turbopipe/version.py +0 -3
- {turbopipe-1.0.5 → turbopipe-1.2.0}/.gitattributes +0 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/.gitignore +0 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/License.md +0 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/examples/basic.py +0 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/examples/benchmark.py +0 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/turbopipe/resources/images/turbopipe.png +0 -0
- {turbopipe-1.0.5 → turbopipe-1.2.0}/turbopipe/resources/images/turbopipe.svg +0 -0
|
@@ -48,8 +48,6 @@ jobs:
|
|
|
48
48
|
- name: Install MSVC
|
|
49
49
|
if: matrix.os == 'windows-latest'
|
|
50
50
|
uses: bus1/cabuild/action/msdevshell@v1
|
|
51
|
-
with:
|
|
52
|
-
architecture: x64
|
|
53
51
|
|
|
54
52
|
- name: deps
|
|
55
53
|
run: python -m pip install cibuildwheel==2.19.2
|
|
@@ -86,3 +84,23 @@ jobs:
|
|
|
86
84
|
TWINE_USERNAME: __token__
|
|
87
85
|
TWINE_PASSWORD: ${{secrets.PYPI_TOKEN}}
|
|
88
86
|
run: twine upload package/*
|
|
87
|
+
|
|
88
|
+
tag:
|
|
89
|
+
needs: publish
|
|
90
|
+
name: Create Release Tag
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v4
|
|
95
|
+
- uses: actions/setup-python@v5
|
|
96
|
+
|
|
97
|
+
- name: Get version
|
|
98
|
+
run: echo VERSION=$(python turbopipe/version.py) >> $GITHUB_ENV
|
|
99
|
+
shell: bash
|
|
100
|
+
|
|
101
|
+
- name: Create Release Tag
|
|
102
|
+
run: |
|
|
103
|
+
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
104
|
+
git config --local user.name "github-actions[bot]"
|
|
105
|
+
git tag -a v$VERSION -m "Release v$VERSION"
|
|
106
|
+
git push origin v$VERSION
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: turbopipe
|
|
3
|
-
Version: 1.0
|
|
4
|
-
Summary: 🌀 Faster
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: 🌀 Faster MemoryView inter-process data transfers for subprocesses
|
|
5
5
|
Home-page: https://brokensrc.dev
|
|
6
6
|
Author-Email: Tremeschin <29046864+Tremeschin@users.noreply.github.com>
|
|
7
7
|
License: MIT License
|
|
@@ -35,12 +35,18 @@ Description-Content-Type: text/markdown
|
|
|
35
35
|
|
|
36
36
|
> [!IMPORTANT]
|
|
37
37
|
> <sub>Also check out [**ShaderFlow**](https://github.com/BrokenSource/ShaderFlow), where **TurboPipe** shines! 😉</sub>
|
|
38
|
-
|
|
38
|
+
<!-- PyPI -->
|
|
39
39
|
<div align="center">
|
|
40
40
|
<a href="https://brokensrc.dev/"><img src="https://raw.githubusercontent.com/BrokenSource/TurboPipe/main/turbopipe/resources/images/turbopipe.png" width="200"></a>
|
|
41
41
|
<h1>TurboPipe</h1>
|
|
42
|
+
Faster <a href="https://github.com/moderngl/moderngl"><b>ModernGL Buffers</b></a> inter-process data transfers for subprocesses
|
|
43
|
+
<br>
|
|
42
44
|
<br>
|
|
43
|
-
|
|
45
|
+
<a href="https://pypi.org/project/turbopipe/"><img src="https://img.shields.io/pypi/v/turbopipe?label=PyPI&color=blue"></a>
|
|
46
|
+
<a href="https://pypi.org/project/turbopipe/"><img src="https://img.shields.io/pypi/dw/turbopipe?label=Installs&color=blue"></a>
|
|
47
|
+
<a href="https://github.com/BrokenSource/TurboPipe"><img src="https://img.shields.io/github/v/tag/BrokenSource/TurboPipe?label=GitHub&color=orange"></a>
|
|
48
|
+
<a href="https://github.com/BrokenSource/TurboPipe/stargazers"><img src="https://img.shields.io/github/stars/BrokenSource/TurboPipe?label=Stars&style=flat&color=orange"></a>
|
|
49
|
+
<a href="https://discord.gg/KjqvcYwRHm"><img src="https://img.shields.io/discord/1184696441298485370?label=Discord&style=flat&color=purple"></a>
|
|
44
50
|
</div>
|
|
45
51
|
|
|
46
52
|
<br>
|
|
@@ -53,7 +59,7 @@ The **optimizations** involved are:
|
|
|
53
59
|
|
|
54
60
|
- **Zero-copy**: Avoid unnecessary memory copies or allocation (intermediate `buffer.read()`)
|
|
55
61
|
- **C++**: The core of TurboPipe is written in C++ for speed, efficiency and low-level control
|
|
56
|
-
- **Chunks**: Write in chunks of 4096 bytes (RAM page size), so the hardware is happy
|
|
62
|
+
- **Chunks**: Write in chunks of 4096 bytes (RAM page size), so the hardware is happy (Unix)
|
|
57
63
|
- **Threaded**:
|
|
58
64
|
- Doesn't block Python code execution, allows to render next frame
|
|
59
65
|
- Decouples the main thread from the I/O thread for performance
|
|
@@ -68,7 +74,7 @@ It couldn't be easier! Just install the [**`turbopipe`**](https://pypi.org/proje
|
|
|
68
74
|
|
|
69
75
|
```bash
|
|
70
76
|
# With pip (https://pip.pypa.io/)
|
|
71
|
-
|
|
77
|
+
pip install turbopipe
|
|
72
78
|
|
|
73
79
|
# With Poetry (https://python-poetry.org/)
|
|
74
80
|
poetry add turbopipe
|
|
@@ -84,7 +90,7 @@ rye add turbopipe
|
|
|
84
90
|
|
|
85
91
|
# 🚀 Usage
|
|
86
92
|
|
|
87
|
-
See also the [**Examples**](https://github.com/BrokenSource/TurboPipe/tree/main/examples) folder for
|
|
93
|
+
See also the [**Examples**](https://github.com/BrokenSource/TurboPipe/tree/main/examples) folder for comparisons, and [**ShaderFlow**](https://github.com/BrokenSource/ShaderFlow/blob/main/ShaderFlow/Scene.py) usage of it!
|
|
88
94
|
|
|
89
95
|
```python
|
|
90
96
|
import subprocess
|
|
@@ -106,7 +112,7 @@ ffmpeg = subprocess.Popen(
|
|
|
106
112
|
for _ in range(60 * 60):
|
|
107
113
|
turbopipe.pipe(buffer, ffmpeg.stdin.fileno())
|
|
108
114
|
|
|
109
|
-
# Finalize writing
|
|
115
|
+
# Finalize writing, encoding
|
|
110
116
|
turbopipe.sync()
|
|
111
117
|
ffmpeg.stdin.close()
|
|
112
118
|
ffmpeg.wait()
|
|
@@ -148,9 +154,9 @@ ffmpeg.wait()
|
|
|
148
154
|
| 🐢 | Null | 1 | 882 fps | 2.44 GB/s | |
|
|
149
155
|
| 🚀 | Null | 1 | 793 fps | 2.19 GB/s | -10.04% |
|
|
150
156
|
| 🌀 | Null | 1 | 1911 fps | 5.28 GB/s | 116.70% |
|
|
151
|
-
| 🐢 | Null | 4 |
|
|
152
|
-
| 🚀 | Null | 4 |
|
|
153
|
-
| 🌀 | Null | 4 |
|
|
157
|
+
| 🐢 | Null | 4 | 857 fps | 2.37 GB/s | |
|
|
158
|
+
| 🚀 | Null | 4 | 891 fps | 2.47 GB/s | 4.05% |
|
|
159
|
+
| 🌀 | Null | 4 | 2309 fps | 6.38 GB/s | 169.45% |
|
|
154
160
|
| 🐢 | ultrafast | 4 | 714 fps | 1.98 GB/s | |
|
|
155
161
|
| 🚀 | ultrafast | 4 | 670 fps | 1.85 GB/s | -6.10% |
|
|
156
162
|
| 🌀 | ultrafast | 4 | 1093 fps | 3.02 GB/s | 53.13% |
|
|
@@ -166,9 +172,9 @@ ffmpeg.wait()
|
|
|
166
172
|
| 🐢 | Null | 4 | 390 fps | 2.43 GB/s | |
|
|
167
173
|
| 🚀 | Null | 4 | 391 fps | 2.43 GB/s | 0.26% |
|
|
168
174
|
| 🌀 | Null | 4 | 756 fps | 4.71 GB/s | 94.01% |
|
|
169
|
-
| 🐢 | ultrafast | 4 |
|
|
170
|
-
| 🚀 | ultrafast | 4 |
|
|
171
|
-
| 🌀 | ultrafast | 4 |
|
|
175
|
+
| 🐢 | ultrafast | 4 | 269 fps | 1.68 GB/s | |
|
|
176
|
+
| 🚀 | ultrafast | 4 | 272 fps | 1.70 GB/s | 1.48% |
|
|
177
|
+
| 🌀 | ultrafast | 4 | 409 fps | 2.55 GB/s | 52.29% |
|
|
172
178
|
| 🐢 | slow | 4 | 115 fps | 0.72 GB/s | |
|
|
173
179
|
| 🚀 | slow | 4 | 118 fps | 0.74 GB/s | 3.40% |
|
|
174
180
|
| 🌀 | slow | 4 | 119 fps | 0.75 GB/s | 4.34% |
|
|
@@ -178,9 +184,9 @@ ffmpeg.wait()
|
|
|
178
184
|
| 🐢 | Null | 1 | 210 fps | 2.33 GB/s | |
|
|
179
185
|
| 🚀 | Null | 1 | 239 fps | 2.64 GB/s | 13.84% |
|
|
180
186
|
| 🌀 | Null | 1 | 534 fps | 5.91 GB/s | 154.32% |
|
|
181
|
-
| 🐢 | Null | 4 |
|
|
182
|
-
| 🚀 | Null | 4 |
|
|
183
|
-
| 🌀 | Null | 4 |
|
|
187
|
+
| 🐢 | Null | 4 | 219 fps | 2.43 GB/s | |
|
|
188
|
+
| 🚀 | Null | 4 | 231 fps | 2.56 GB/s | 5.64% |
|
|
189
|
+
| 🌀 | Null | 4 | 503 fps | 5.56 GB/s | 129.75% |
|
|
184
190
|
| 🐢 | ultrafast | 4 | 141 fps | 1.56 GB/s | |
|
|
185
191
|
| 🚀 | ultrafast | 4 | 150 fps | 1.67 GB/s | 6.92% |
|
|
186
192
|
| 🌀 | ultrafast | 4 | 226 fps | 2.50 GB/s | 60.37% |
|
|
@@ -360,9 +366,7 @@ On realistically loads, like [**ShaderFlow**](https://github.com/BrokenSource/Sh
|
|
|
360
366
|
|
|
361
367
|
# 📚 Future work
|
|
362
368
|
|
|
363
|
-
- Add support for NumPy arrays, memoryviews, and byte-like objects
|
|
364
369
|
- Disable/investigate performance degradation on Windows iGPUs
|
|
365
370
|
- Improve the thread synchronization and/or use a ThreadPool
|
|
366
|
-
- Stabler way for finding mglo struct offsets (moderngl.h?)
|
|
367
371
|
- Maybe use `mmap` instead of chunks writing on Linux
|
|
368
|
-
- Test on
|
|
372
|
+
- Test on macOS 🙈
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
> [!IMPORTANT]
|
|
2
2
|
> <sub>Also check out [**ShaderFlow**](https://github.com/BrokenSource/ShaderFlow), where **TurboPipe** shines! 😉</sub>
|
|
3
|
-
|
|
3
|
+
<!-- PyPI -->
|
|
4
4
|
<div align="center">
|
|
5
5
|
<a href="https://brokensrc.dev/"><img src="https://raw.githubusercontent.com/BrokenSource/TurboPipe/main/turbopipe/resources/images/turbopipe.png" width="200"></a>
|
|
6
6
|
<h1>TurboPipe</h1>
|
|
7
|
+
Faster <a href="https://github.com/moderngl/moderngl"><b>ModernGL Buffers</b></a> inter-process data transfers for subprocesses
|
|
8
|
+
<br>
|
|
7
9
|
<br>
|
|
8
|
-
|
|
10
|
+
<a href="https://pypi.org/project/turbopipe/"><img src="https://img.shields.io/pypi/v/turbopipe?label=PyPI&color=blue"></a>
|
|
11
|
+
<a href="https://pypi.org/project/turbopipe/"><img src="https://img.shields.io/pypi/dw/turbopipe?label=Installs&color=blue"></a>
|
|
12
|
+
<a href="https://github.com/BrokenSource/TurboPipe"><img src="https://img.shields.io/github/v/tag/BrokenSource/TurboPipe?label=GitHub&color=orange"></a>
|
|
13
|
+
<a href="https://github.com/BrokenSource/TurboPipe/stargazers"><img src="https://img.shields.io/github/stars/BrokenSource/TurboPipe?label=Stars&style=flat&color=orange"></a>
|
|
14
|
+
<a href="https://discord.gg/KjqvcYwRHm"><img src="https://img.shields.io/discord/1184696441298485370?label=Discord&style=flat&color=purple"></a>
|
|
9
15
|
</div>
|
|
10
16
|
|
|
11
17
|
<br>
|
|
@@ -18,7 +24,7 @@ The **optimizations** involved are:
|
|
|
18
24
|
|
|
19
25
|
- **Zero-copy**: Avoid unnecessary memory copies or allocation (intermediate `buffer.read()`)
|
|
20
26
|
- **C++**: The core of TurboPipe is written in C++ for speed, efficiency and low-level control
|
|
21
|
-
- **Chunks**: Write in chunks of 4096 bytes (RAM page size), so the hardware is happy
|
|
27
|
+
- **Chunks**: Write in chunks of 4096 bytes (RAM page size), so the hardware is happy (Unix)
|
|
22
28
|
- **Threaded**:
|
|
23
29
|
- Doesn't block Python code execution, allows to render next frame
|
|
24
30
|
- Decouples the main thread from the I/O thread for performance
|
|
@@ -33,7 +39,7 @@ It couldn't be easier! Just install the [**`turbopipe`**](https://pypi.org/proje
|
|
|
33
39
|
|
|
34
40
|
```bash
|
|
35
41
|
# With pip (https://pip.pypa.io/)
|
|
36
|
-
|
|
42
|
+
pip install turbopipe
|
|
37
43
|
|
|
38
44
|
# With Poetry (https://python-poetry.org/)
|
|
39
45
|
poetry add turbopipe
|
|
@@ -49,7 +55,7 @@ rye add turbopipe
|
|
|
49
55
|
|
|
50
56
|
# 🚀 Usage
|
|
51
57
|
|
|
52
|
-
See also the [**Examples**](https://github.com/BrokenSource/TurboPipe/tree/main/examples) folder for
|
|
58
|
+
See also the [**Examples**](https://github.com/BrokenSource/TurboPipe/tree/main/examples) folder for comparisons, and [**ShaderFlow**](https://github.com/BrokenSource/ShaderFlow/blob/main/ShaderFlow/Scene.py) usage of it!
|
|
53
59
|
|
|
54
60
|
```python
|
|
55
61
|
import subprocess
|
|
@@ -71,7 +77,7 @@ ffmpeg = subprocess.Popen(
|
|
|
71
77
|
for _ in range(60 * 60):
|
|
72
78
|
turbopipe.pipe(buffer, ffmpeg.stdin.fileno())
|
|
73
79
|
|
|
74
|
-
# Finalize writing
|
|
80
|
+
# Finalize writing, encoding
|
|
75
81
|
turbopipe.sync()
|
|
76
82
|
ffmpeg.stdin.close()
|
|
77
83
|
ffmpeg.wait()
|
|
@@ -113,9 +119,9 @@ ffmpeg.wait()
|
|
|
113
119
|
| 🐢 | Null | 1 | 882 fps | 2.44 GB/s | |
|
|
114
120
|
| 🚀 | Null | 1 | 793 fps | 2.19 GB/s | -10.04% |
|
|
115
121
|
| 🌀 | Null | 1 | 1911 fps | 5.28 GB/s | 116.70% |
|
|
116
|
-
| 🐢 | Null | 4 |
|
|
117
|
-
| 🚀 | Null | 4 |
|
|
118
|
-
| 🌀 | Null | 4 |
|
|
122
|
+
| 🐢 | Null | 4 | 857 fps | 2.37 GB/s | |
|
|
123
|
+
| 🚀 | Null | 4 | 891 fps | 2.47 GB/s | 4.05% |
|
|
124
|
+
| 🌀 | Null | 4 | 2309 fps | 6.38 GB/s | 169.45% |
|
|
119
125
|
| 🐢 | ultrafast | 4 | 714 fps | 1.98 GB/s | |
|
|
120
126
|
| 🚀 | ultrafast | 4 | 670 fps | 1.85 GB/s | -6.10% |
|
|
121
127
|
| 🌀 | ultrafast | 4 | 1093 fps | 3.02 GB/s | 53.13% |
|
|
@@ -131,9 +137,9 @@ ffmpeg.wait()
|
|
|
131
137
|
| 🐢 | Null | 4 | 390 fps | 2.43 GB/s | |
|
|
132
138
|
| 🚀 | Null | 4 | 391 fps | 2.43 GB/s | 0.26% |
|
|
133
139
|
| 🌀 | Null | 4 | 756 fps | 4.71 GB/s | 94.01% |
|
|
134
|
-
| 🐢 | ultrafast | 4 |
|
|
135
|
-
| 🚀 | ultrafast | 4 |
|
|
136
|
-
| 🌀 | ultrafast | 4 |
|
|
140
|
+
| 🐢 | ultrafast | 4 | 269 fps | 1.68 GB/s | |
|
|
141
|
+
| 🚀 | ultrafast | 4 | 272 fps | 1.70 GB/s | 1.48% |
|
|
142
|
+
| 🌀 | ultrafast | 4 | 409 fps | 2.55 GB/s | 52.29% |
|
|
137
143
|
| 🐢 | slow | 4 | 115 fps | 0.72 GB/s | |
|
|
138
144
|
| 🚀 | slow | 4 | 118 fps | 0.74 GB/s | 3.40% |
|
|
139
145
|
| 🌀 | slow | 4 | 119 fps | 0.75 GB/s | 4.34% |
|
|
@@ -143,9 +149,9 @@ ffmpeg.wait()
|
|
|
143
149
|
| 🐢 | Null | 1 | 210 fps | 2.33 GB/s | |
|
|
144
150
|
| 🚀 | Null | 1 | 239 fps | 2.64 GB/s | 13.84% |
|
|
145
151
|
| 🌀 | Null | 1 | 534 fps | 5.91 GB/s | 154.32% |
|
|
146
|
-
| 🐢 | Null | 4 |
|
|
147
|
-
| 🚀 | Null | 4 |
|
|
148
|
-
| 🌀 | Null | 4 |
|
|
152
|
+
| 🐢 | Null | 4 | 219 fps | 2.43 GB/s | |
|
|
153
|
+
| 🚀 | Null | 4 | 231 fps | 2.56 GB/s | 5.64% |
|
|
154
|
+
| 🌀 | Null | 4 | 503 fps | 5.56 GB/s | 129.75% |
|
|
149
155
|
| 🐢 | ultrafast | 4 | 141 fps | 1.56 GB/s | |
|
|
150
156
|
| 🚀 | ultrafast | 4 | 150 fps | 1.67 GB/s | 6.92% |
|
|
151
157
|
| 🌀 | ultrafast | 4 | 226 fps | 2.50 GB/s | 60.37% |
|
|
@@ -325,9 +331,7 @@ On realistically loads, like [**ShaderFlow**](https://github.com/BrokenSource/Sh
|
|
|
325
331
|
|
|
326
332
|
# 📚 Future work
|
|
327
333
|
|
|
328
|
-
- Add support for NumPy arrays, memoryviews, and byte-like objects
|
|
329
334
|
- Disable/investigate performance degradation on Windows iGPUs
|
|
330
335
|
- Improve the thread synchronization and/or use a ThreadPool
|
|
331
|
-
- Stabler way for finding mglo struct offsets (moderngl.h?)
|
|
332
336
|
- Maybe use `mmap` instead of chunks writing on Linux
|
|
333
|
-
- Test on
|
|
337
|
+
- Test on macOS 🙈
|
|
@@ -28,7 +28,6 @@ endif
|
|
|
28
28
|
# ----------------------------------------------|
|
|
29
29
|
# Source files
|
|
30
30
|
|
|
31
|
-
incdir = include_directories('turbopipe/include')
|
|
32
31
|
source = files('turbopipe/_turbopipe.cpp')
|
|
33
32
|
|
|
34
33
|
# ----------------------------------------------|
|
|
@@ -37,7 +36,6 @@ python = import('python').find_installation()
|
|
|
37
36
|
|
|
38
37
|
python.extension_module(
|
|
39
38
|
'_turbopipe', source,
|
|
40
|
-
include_directories: incdir,
|
|
41
39
|
cpp_args: cpp_args,
|
|
42
40
|
install: true,
|
|
43
41
|
subdir: 'turbopipe'
|
|
@@ -7,7 +7,7 @@ homepage = "https://brokensrc.dev"
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "turbopipe"
|
|
9
9
|
dynamic = ["version"]
|
|
10
|
-
description = "🌀 Faster
|
|
10
|
+
description = "🌀 Faster MemoryView inter-process data transfers for subprocesses"
|
|
11
11
|
authors = [{name="Tremeschin", email="29046864+Tremeschin@users.noreply.github.com"}]
|
|
12
12
|
readme = "Readme.md"
|
|
13
13
|
license = {file="License.md"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
3
|
+
from moderngl import Buffer
|
|
4
|
+
|
|
5
|
+
from turbopipe import _turbopipe
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def pipe(buffer: Union[Buffer, memoryview], fileno: int) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Pipe the content of a moderngl.Buffer or memoryview to a file descriptor, fast, threaded and
|
|
11
|
+
blocking when needed. Call `sync(buffer)` before this, and `sync()` when done for
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
```python
|
|
15
|
+
# Assuming `buffer = ctx.buffer(...)`
|
|
16
|
+
# Note: Use as `fbo.read_into(buffer)`
|
|
17
|
+
|
|
18
|
+
# As a open() file
|
|
19
|
+
with open("file.bin", "wb") as file:
|
|
20
|
+
turbopipe.pipe(buffer, file)
|
|
21
|
+
|
|
22
|
+
# As a subprocess
|
|
23
|
+
child = subprocess.Popen(..., stdin=subprocess.PIPE)
|
|
24
|
+
turbopipe.pipe(buffer, child.stdin.fileno())
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(buffer, Buffer):
|
|
28
|
+
buffer = memoryview(buffer.mglo)
|
|
29
|
+
_turbopipe.pipe(buffer, fileno)
|
|
30
|
+
del buffer
|
|
31
|
+
|
|
32
|
+
def sync(buffer: Optional[Union[Buffer, memoryview]]=None) -> None:
|
|
33
|
+
"""Waits for any pending write operation on a buffer, or 'all buffers' if None, to finish"""
|
|
34
|
+
if isinstance(buffer, Buffer):
|
|
35
|
+
buffer = memoryview(buffer.mglo)
|
|
36
|
+
_turbopipe.sync(buffer)
|
|
37
|
+
del buffer
|
|
38
|
+
|
|
39
|
+
def close() -> None:
|
|
40
|
+
"""Syncs and deletes objects"""
|
|
41
|
+
_turbopipe.close()
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"pipe",
|
|
45
|
+
"sync",
|
|
46
|
+
"close"
|
|
47
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ------------------------------------------------------------------------------------------------|
|
|
2
2
|
//
|
|
3
|
-
// TurboPipe - Faster ModernGL
|
|
3
|
+
// TurboPipe - Faster ModernGL Buffers inter-process data transfers for subprocesses
|
|
4
4
|
//
|
|
5
5
|
// (c) 2024, Tremeschin, MIT License
|
|
6
6
|
//
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
// Standard library
|
|
13
13
|
#include <functional>
|
|
14
|
-
#include <iostream>
|
|
15
14
|
#include <chrono>
|
|
16
15
|
|
|
17
16
|
// Threading
|
|
@@ -24,71 +23,16 @@
|
|
|
24
23
|
#include <unordered_map>
|
|
25
24
|
#include <deque>
|
|
26
25
|
|
|
27
|
-
// Third party
|
|
28
|
-
#include "gl_methods.hpp"
|
|
29
|
-
|
|
30
26
|
#define dict std::unordered_map
|
|
31
27
|
using namespace std;
|
|
32
28
|
|
|
33
|
-
// ------------------------------------------------------------------------------------------------|
|
|
34
|
-
// ModernGL Types - Courtesy of the moderngl package developers (MIT)
|
|
35
|
-
|
|
36
|
-
static PyTypeObject* MGLBuffer_type = nullptr;
|
|
37
|
-
|
|
38
|
-
struct MGLContext;
|
|
39
|
-
struct MGLFramebuffer;
|
|
40
|
-
|
|
41
|
-
struct MGLBuffer {
|
|
42
|
-
PyObject_HEAD
|
|
43
|
-
MGLContext* context;
|
|
44
|
-
int buffer;
|
|
45
|
-
Py_ssize_t size;
|
|
46
|
-
bool dynamic;
|
|
47
|
-
bool released;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
struct MGLContext {
|
|
51
|
-
PyObject_HEAD
|
|
52
|
-
PyObject * ctx;
|
|
53
|
-
PyObject * extensions;
|
|
54
|
-
MGLFramebuffer * default_framebuffer;
|
|
55
|
-
MGLFramebuffer * bound_framebuffer;
|
|
56
|
-
PyObject * includes;
|
|
57
|
-
int version_code;
|
|
58
|
-
int max_samples;
|
|
59
|
-
int max_integer_samples;
|
|
60
|
-
int max_color_attachments;
|
|
61
|
-
int max_texture_units;
|
|
62
|
-
int default_texture_unit;
|
|
63
|
-
float max_anisotropy;
|
|
64
|
-
int enable_flags;
|
|
65
|
-
int front_face;
|
|
66
|
-
int cull_face;
|
|
67
|
-
int depth_func;
|
|
68
|
-
bool depth_clamp;
|
|
69
|
-
double depth_range[2];
|
|
70
|
-
int blend_func_src;
|
|
71
|
-
int blend_func_dst;
|
|
72
|
-
bool wireframe;
|
|
73
|
-
bool multisample;
|
|
74
|
-
int provoking_vertex;
|
|
75
|
-
float polygon_offset_factor;
|
|
76
|
-
float polygon_offset_units;
|
|
77
|
-
GLMethods gl;
|
|
78
|
-
bool released;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
29
|
// ------------------------------------------------------------------------------------------------|
|
|
82
30
|
// TurboPipe internals
|
|
83
31
|
|
|
84
32
|
struct Work {
|
|
85
|
-
void*
|
|
33
|
+
void* data;
|
|
86
34
|
int file;
|
|
87
35
|
size_t size;
|
|
88
|
-
|
|
89
|
-
int hash() {
|
|
90
|
-
return std::hash<int>()(file) ^ std::hash<void*>()(map);
|
|
91
|
-
}
|
|
92
36
|
};
|
|
93
37
|
|
|
94
38
|
class TurboPipe {
|
|
@@ -96,22 +40,28 @@ public:
|
|
|
96
40
|
TurboPipe(): running(true) {}
|
|
97
41
|
~TurboPipe() {close();}
|
|
98
42
|
|
|
99
|
-
void pipe(
|
|
100
|
-
|
|
43
|
+
void pipe(PyObject* memoryview, int file) {
|
|
44
|
+
Py_buffer view = *PyMemoryView_GET_BUFFER(memoryview);
|
|
45
|
+
this->_pipe(view.buf, view.len, file);
|
|
46
|
+
}
|
|
101
47
|
|
|
102
|
-
|
|
103
|
-
void* data =
|
|
104
|
-
gl.UnmapBuffer(GL_ARRAY_BUFFER);
|
|
48
|
+
void sync(PyObject* memoryview=nullptr) {
|
|
49
|
+
void* data = nullptr;
|
|
105
50
|
|
|
106
|
-
|
|
107
|
-
|
|
51
|
+
if (memoryview != nullptr) {
|
|
52
|
+
Py_buffer view = *PyMemoryView_GET_BUFFER(memoryview);
|
|
53
|
+
data = view.buf;
|
|
54
|
+
}
|
|
108
55
|
|
|
109
|
-
|
|
110
|
-
// Wait for all queues to be empty, as they are erased when
|
|
56
|
+
// Wait for some or all queues to be empty, as they are erased when
|
|
111
57
|
// each thread's writing loop is done, guaranteeing finish
|
|
112
58
|
for (auto& values: queue) {
|
|
113
|
-
while (
|
|
114
|
-
|
|
59
|
+
while (true) {
|
|
60
|
+
if (data != nullptr && values.second.find(data) == values.second.end())
|
|
61
|
+
break;
|
|
62
|
+
if (data == nullptr && values.second.empty())
|
|
63
|
+
break;
|
|
64
|
+
this_thread::sleep_for(chrono::microseconds(200));
|
|
115
65
|
}
|
|
116
66
|
}
|
|
117
67
|
}
|
|
@@ -126,8 +76,8 @@ public:
|
|
|
126
76
|
}
|
|
127
77
|
|
|
128
78
|
private:
|
|
129
|
-
dict<int, dict<
|
|
130
|
-
dict<int, unordered_set<
|
|
79
|
+
dict<int, dict<void*, condition_variable>> pending;
|
|
80
|
+
dict<int, unordered_set<void*>> queue;
|
|
131
81
|
dict<int, deque<Work>> stream;
|
|
132
82
|
dict<int, thread> threads;
|
|
133
83
|
dict<int, mutex> mutexes;
|
|
@@ -136,20 +86,18 @@ private:
|
|
|
136
86
|
|
|
137
87
|
void _pipe(void* data, size_t size, int file) {
|
|
138
88
|
Work work = {data, file, size};
|
|
139
|
-
int hash = work.hash();
|
|
140
|
-
|
|
141
89
|
unique_lock<mutex> lock(mutexes[file]);
|
|
142
90
|
|
|
143
|
-
// Notify this
|
|
144
|
-
if (!queue[file].insert(
|
|
145
|
-
pending[file][
|
|
146
|
-
return queue[file].find(
|
|
91
|
+
// Notify this memory is queued, wait if pending
|
|
92
|
+
if (!queue[file].insert(data).second) {
|
|
93
|
+
pending[file][data].wait(lock, [this, file, data] {
|
|
94
|
+
return queue[file].find(data) == queue[file].end();
|
|
147
95
|
});
|
|
148
96
|
}
|
|
149
97
|
|
|
150
98
|
// Add another job to the queue
|
|
151
99
|
stream[file].push_back(work);
|
|
152
|
-
queue[file].insert(
|
|
100
|
+
queue[file].insert(data);
|
|
153
101
|
this->running = true;
|
|
154
102
|
lock.unlock();
|
|
155
103
|
|
|
@@ -179,13 +127,13 @@ private:
|
|
|
179
127
|
|
|
180
128
|
#ifdef _WIN32
|
|
181
129
|
// Windows doesn't like chunked writes ??
|
|
182
|
-
write(work.file, (char*) work.
|
|
130
|
+
write(work.file, (char*) work.data, work.size);
|
|
183
131
|
#else
|
|
184
132
|
// Optimization: Write in chunks of 4096 (RAM page size)
|
|
185
133
|
size_t tell = 0;
|
|
186
134
|
while (tell < work.size) {
|
|
187
135
|
size_t chunk = min(work.size - tell, static_cast<size_t>(4096));
|
|
188
|
-
size_t written = write(work.file, (char*) work.
|
|
136
|
+
size_t written = write(work.file, (char*) work.data + tell, chunk);
|
|
189
137
|
if (written == -1) break;
|
|
190
138
|
tell += written;
|
|
191
139
|
}
|
|
@@ -193,9 +141,8 @@ private:
|
|
|
193
141
|
|
|
194
142
|
// Signal work is done
|
|
195
143
|
lock.lock();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
queue[file].erase(hash);
|
|
144
|
+
pending[file][work.data].notify_all();
|
|
145
|
+
queue[file].erase(work.data);
|
|
199
146
|
signal.notify_all();
|
|
200
147
|
}
|
|
201
148
|
}
|
|
@@ -211,19 +158,30 @@ static PyObject* turbopipe_pipe(
|
|
|
211
158
|
PyObject* Py_UNUSED(self),
|
|
212
159
|
PyObject* args
|
|
213
160
|
) {
|
|
214
|
-
PyObject*
|
|
161
|
+
PyObject* memoryview;
|
|
215
162
|
PyObject* file;
|
|
216
|
-
if (!PyArg_ParseTuple(args, "OO", &
|
|
163
|
+
if (!PyArg_ParseTuple(args, "OO", &memoryview, &file))
|
|
217
164
|
return NULL;
|
|
218
|
-
|
|
165
|
+
if (!PyMemoryView_Check(memoryview)) {
|
|
166
|
+
PyErr_SetString(PyExc_TypeError, "Expected a memoryview object");
|
|
167
|
+
return NULL;
|
|
168
|
+
}
|
|
169
|
+
turbopipe->pipe(memoryview, PyLong_AsLong(file));
|
|
219
170
|
Py_RETURN_NONE;
|
|
220
171
|
}
|
|
221
172
|
|
|
222
173
|
static PyObject* turbopipe_sync(
|
|
223
174
|
PyObject* Py_UNUSED(self),
|
|
224
|
-
PyObject*
|
|
175
|
+
PyObject* args
|
|
225
176
|
) {
|
|
226
|
-
|
|
177
|
+
PyObject* memoryview;
|
|
178
|
+
if (!PyArg_ParseTuple(args, "|O", &memoryview))
|
|
179
|
+
return NULL;
|
|
180
|
+
if (memoryview != nullptr && !PyMemoryView_Check(memoryview)) {
|
|
181
|
+
PyErr_SetString(PyExc_TypeError, "Expected a memoryview object or None");
|
|
182
|
+
return NULL;
|
|
183
|
+
}
|
|
184
|
+
turbopipe->sync(memoryview);
|
|
227
185
|
Py_RETURN_NONE;
|
|
228
186
|
}
|
|
229
187
|
|
|
@@ -244,7 +202,7 @@ static void turbopipe_exit() {
|
|
|
244
202
|
|
|
245
203
|
static PyMethodDef TurboPipeMethods[] = {
|
|
246
204
|
{"pipe", (PyCFunction) turbopipe_pipe, METH_VARARGS, ""},
|
|
247
|
-
{"sync", (PyCFunction) turbopipe_sync,
|
|
205
|
+
{"sync", (PyCFunction) turbopipe_sync, METH_VARARGS, ""},
|
|
248
206
|
{"close", (PyCFunction) turbopipe_close, METH_NOARGS, ""},
|
|
249
207
|
{NULL, NULL, 0, NULL}
|
|
250
208
|
};
|
|
@@ -259,9 +217,8 @@ static struct PyModuleDef turbopipe_module = {
|
|
|
259
217
|
|
|
260
218
|
PyMODINIT_FUNC PyInit__turbopipe(void) {
|
|
261
219
|
PyObject* module = PyModule_Create(&turbopipe_module);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
MGLBuffer_type = (PyTypeObject*) buffer;
|
|
220
|
+
if (module == NULL)
|
|
221
|
+
return NULL;
|
|
265
222
|
turbopipe = new TurboPipe();
|
|
266
223
|
Py_AtExit(turbopipe_exit);
|
|
267
224
|
return module;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
name: debug
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
|
|
6
|
-
jobs:
|
|
7
|
-
wheels:
|
|
8
|
-
name: Build ${{matrix.pyver}} wheels on ${{matrix.os}}
|
|
9
|
-
runs-on: ${{matrix.os}}
|
|
10
|
-
strategy:
|
|
11
|
-
matrix:
|
|
12
|
-
os: [windows-latest]
|
|
13
|
-
pyver: [cp311]
|
|
14
|
-
|
|
15
|
-
env:
|
|
16
|
-
CIBW_BUILD: ${{matrix.pyver}}-*
|
|
17
|
-
CIBW_ARCHS_LINUX: auto
|
|
18
|
-
CIBW_ARCHS_MACOS: arm64
|
|
19
|
-
CIBW_ARCHS_WINDOWS: auto
|
|
20
|
-
CIBW_SKIP: '*musllinux* *i686* *-win32'
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- uses: actions/checkout@v4
|
|
24
|
-
- uses: actions/setup-python@v5
|
|
25
|
-
|
|
26
|
-
- name: Prepare MSVC
|
|
27
|
-
if: matrix.os == 'windows-latest'
|
|
28
|
-
uses: bus1/cabuild/action/msdevshell@v1
|
|
29
|
-
with:
|
|
30
|
-
architecture: x64
|
|
31
|
-
|
|
32
|
-
- name: deps
|
|
33
|
-
run: python -m pip install build
|
|
34
|
-
|
|
35
|
-
- name: wheels
|
|
36
|
-
run: python -m build
|
|
37
|
-
|
|
38
|
-
- name: upload
|
|
39
|
-
uses: actions/upload-artifact@v3
|
|
40
|
-
with:
|
|
41
|
-
name: package
|
|
42
|
-
path: dist/*.whl
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
from io import IOBase
|
|
2
|
-
|
|
3
|
-
from moderngl import Buffer
|
|
4
|
-
|
|
5
|
-
from turbopipe import _turbopipe
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def pipe(buffer: Buffer, file: IOBase) -> None:
|
|
9
|
-
"""
|
|
10
|
-
Pipe the content of a moderngl.Buffer to a file descriptor,
|
|
11
|
-
Fast, threaded and non-blocking. Call `sync()` when done!
|
|
12
|
-
|
|
13
|
-
Usage:
|
|
14
|
-
```python
|
|
15
|
-
# Assuming `buffer = ctx.buffer(...)`
|
|
16
|
-
# Note: Use as `fbo.read_into(buffer)`
|
|
17
|
-
|
|
18
|
-
# As a open() file
|
|
19
|
-
with open("file.bin", "wb") as file:
|
|
20
|
-
turbopipe.pipe(buffer, file)
|
|
21
|
-
|
|
22
|
-
# As a subprocess
|
|
23
|
-
child = subprocess.Popen(..., stdin=subprocess.PIPE)
|
|
24
|
-
turbopipe.pipe(buffer, child.stdin.fileno())
|
|
25
|
-
```
|
|
26
|
-
"""
|
|
27
|
-
_turbopipe.pipe(buffer.mglo, file)
|
|
28
|
-
|
|
29
|
-
def sync() -> None:
|
|
30
|
-
"""Waits for all jobs to finish"""
|
|
31
|
-
_turbopipe.sync()
|
|
32
|
-
|
|
33
|
-
def close() -> None:
|
|
34
|
-
"""Syncs and deletes objects"""
|
|
35
|
-
_turbopipe.close()
|
|
36
|
-
|
|
37
|
-
__all__ = [
|
|
38
|
-
"pipe",
|
|
39
|
-
"sync",
|
|
40
|
-
"close"
|
|
41
|
-
]
|