mtlpy 0.1.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.
- mtlpy-0.1.0/.github/workflows/ci.yml +38 -0
- mtlpy-0.1.0/.github/workflows/release.yml +141 -0
- mtlpy-0.1.0/.gitignore +32 -0
- mtlpy-0.1.0/.gitmodules +3 -0
- mtlpy-0.1.0/CMakeLists.txt +37 -0
- mtlpy-0.1.0/LICENSE +21 -0
- mtlpy-0.1.0/PKG-INFO +295 -0
- mtlpy-0.1.0/README.md +266 -0
- mtlpy-0.1.0/benchmarks/bench.py +208 -0
- mtlpy-0.1.0/csrc/bindings.cpp +46 -0
- mtlpy-0.1.0/csrc/buffer.cpp +22 -0
- mtlpy-0.1.0/csrc/buffer.h +21 -0
- mtlpy-0.1.0/csrc/device.cpp +75 -0
- mtlpy-0.1.0/csrc/device.h +35 -0
- mtlpy-0.1.0/csrc/metal_impl.mm +7 -0
- mtlpy-0.1.0/csrc/pipeline.cpp +109 -0
- mtlpy-0.1.0/csrc/pipeline.h +32 -0
- mtlpy-0.1.0/csrc/pipeline_cache.cpp +173 -0
- mtlpy-0.1.0/csrc/pipeline_cache.h +47 -0
- mtlpy-0.1.0/examples/adder.py +14 -0
- mtlpy-0.1.0/examples/async_dispatch.py +28 -0
- mtlpy-0.1.0/examples/reuse_buffers.py +37 -0
- mtlpy-0.1.0/metal-cpp/Foundation/Foundation.hpp +47 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSArray.hpp +124 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSAutoreleasePool.hpp +83 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSBundle.hpp +374 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSData.hpp +54 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSDate.hpp +53 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSDefines.hpp +45 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSDictionary.hpp +128 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSEnumerator.hpp +78 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSError.hpp +173 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSLock.hpp +118 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSNotification.hpp +110 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSNumber.hpp +501 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSObjCRuntime.hpp +43 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSObject.hpp +302 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSPrivate.hpp +531 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSProcessInfo.hpp +386 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSRange.hpp +83 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSSet.hpp +87 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSSharedPtr.hpp +324 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSString.hpp +255 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSTypes.hpp +51 -0
- mtlpy-0.1.0/metal-cpp/Foundation/NSURL.hpp +90 -0
- mtlpy-0.1.0/metal-cpp/LICENSE.txt +202 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4AccelerationStructure.hpp +1395 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4Archive.hpp +93 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4ArgumentTable.hpp +187 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4BinaryFunction.hpp +50 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4BinaryFunctionDescriptor.hpp +97 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4CommandAllocator.hpp +100 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4CommandBuffer.hpp +193 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4CommandEncoder.hpp +134 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4CommandQueue.hpp +283 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4CommitFeedback.hpp +62 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4Compiler.hpp +345 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4CompilerTask.hpp +63 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4ComputeCommandEncoder.hpp +307 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4ComputePipeline.hpp +158 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4Counters.hpp +138 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4FunctionDescriptor.hpp +49 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4LibraryDescriptor.hpp +98 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4LibraryFunctionDescriptor.hpp +86 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4LinkingDescriptor.hpp +204 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4MachineLearningCommandEncoder.hpp +66 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4MachineLearningPipeline.hpp +172 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4MeshRenderPipeline.hpp +413 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4PipelineDataSetSerializer.hpp +85 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4PipelineState.hpp +150 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4RenderCommandEncoder.hpp +340 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4RenderPass.hpp +280 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4RenderPipeline.hpp +587 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4SpecializedFunctionDescriptor.hpp +100 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4StitchedFunctionDescriptor.hpp +86 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTL4TileRenderPipeline.hpp +173 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLAccelerationStructure.hpp +1887 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLAccelerationStructureCommandEncoder.hpp +260 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLAccelerationStructureTypes.hpp +292 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLAllocation.hpp +40 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLArgument.hpp +840 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLArgumentEncoder.hpp +235 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLBinaryArchive.hpp +152 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLBlitCommandEncoder.hpp +233 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLBlitPass.hpp +154 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLBuffer.hpp +119 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLCaptureManager.hpp +217 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLCaptureScope.hpp +91 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLCommandBuffer.hpp +464 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLCommandEncoder.hpp +117 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLCommandQueue.hpp +158 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLComputeCommandEncoder.hpp +324 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLComputePass.hpp +169 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLComputePipeline.hpp +439 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLCounters.hpp +243 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLDataType.hpp +129 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLDefines.hpp +41 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLDepthStencil.hpp +277 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLDevice.hpp +1512 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLDrawable.hpp +90 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLDynamicLibrary.hpp +78 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLEvent.hpp +170 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLFence.hpp +55 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLFunctionConstantValues.hpp +76 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLFunctionDescriptor.hpp +153 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLFunctionHandle.hpp +65 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLFunctionLog.hpp +101 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLFunctionStitching.hpp +319 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLGPUAddress.hpp +36 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLHeaderBridge.hpp +3161 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLHeap.hpp +318 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLIOCommandBuffer.hpp +182 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLIOCommandQueue.hpp +211 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLIOCompressor.hpp +94 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLIndirectCommandBuffer.hpp +376 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLIndirectCommandEncoder.hpp +273 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLIntersectionFunctionTable.hpp +173 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLLibrary.hpp +813 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLLinkedFunctions.hpp +110 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLLogState.hpp +111 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLParallelRenderCommandEncoder.hpp +83 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLPipeline.hpp +104 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLPixelFormat.hpp +173 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLPrivate.hpp +158 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLRasterizationRate.hpp +337 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLRenderCommandEncoder.hpp +1022 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLRenderPass.hpp +792 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLRenderPipeline.hpp +1876 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLResidencySet.hpp +178 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLResource.hpp +190 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLResourceStateCommandEncoder.hpp +98 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLResourceStatePass.hpp +154 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLResourceViewPool.hpp +118 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLSampler.hpp +345 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLStageInputOutputDescriptor.hpp +356 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLTensor.hpp +511 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLTexture.hpp +803 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLTextureViewPool.hpp +59 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLTypes.hpp +164 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLVersion.hpp +32 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLVertexDescriptor.hpp +326 -0
- mtlpy-0.1.0/metal-cpp/Metal/MTLVisibleFunctionTable.hpp +96 -0
- mtlpy-0.1.0/metal-cpp/Metal/Metal.hpp +120 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTL4FXFrameInterpolator.hpp +47 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTL4FXSpatialScaler.hpp +49 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTL4FXTemporalDenoisedScaler.hpp +49 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTL4FXTemporalScaler.hpp +49 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTLFXDefines.hpp +41 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTLFXFrameInterpolator.hpp +953 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTLFXPrivate.hpp +560 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTLFXSpatialScaler.hpp +397 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTLFXTemporalDenoisedScaler.hpp +1217 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MTLFXTemporalScaler.hpp +994 -0
- mtlpy-0.1.0/metal-cpp/MetalFX/MetalFX.hpp +35 -0
- mtlpy-0.1.0/metal-cpp/QuartzCore/CADefines.hpp +41 -0
- mtlpy-0.1.0/metal-cpp/QuartzCore/CAMetalDrawable.hpp +57 -0
- mtlpy-0.1.0/metal-cpp/QuartzCore/CAMetalLayer.hpp +234 -0
- mtlpy-0.1.0/metal-cpp/QuartzCore/CAPrivate.hpp +154 -0
- mtlpy-0.1.0/metal-cpp/QuartzCore/QuartzCore.hpp +28 -0
- mtlpy-0.1.0/metal-cpp/README.md +318 -0
- mtlpy-0.1.0/metal-cpp/SingleHeader/MakeSingleHeader.py +271 -0
- mtlpy-0.1.0/pyproject.toml +41 -0
- mtlpy-0.1.0/src/mtlpy/__init__.py +11 -0
- mtlpy-0.1.0/src/mtlpy/buffer.py +137 -0
- mtlpy-0.1.0/src/mtlpy/device.py +122 -0
- mtlpy-0.1.0/src/mtlpy/operators.py +50 -0
- mtlpy-0.1.0/src/mtlpy/pipeline.py +19 -0
- mtlpy-0.1.0/src/mtlpy/shader.py +165 -0
- mtlpy-0.1.0/src/mtlpy/utils.py +40 -0
- mtlpy-0.1.0/tests/conftest.py +12 -0
- mtlpy-0.1.0/tests/test_async.py +54 -0
- mtlpy-0.1.0/tests/test_basic.py +140 -0
- mtlpy-0.1.0/tests/test_buffer_reuse.py +66 -0
- mtlpy-0.1.0/tests/test_operators.py +257 -0
- mtlpy-0.1.0/tests/test_pipeline_persistence.py +89 -0
- mtlpy-0.1.0/tests/test_stability.py +124 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# Validates every PR targeting main (and main itself, as a sanity check
|
|
4
|
+
# after merge) before the tag-release workflow can ever fire on main --
|
|
5
|
+
# this is what stands between a broken PR and an automatic PyPI publish.
|
|
6
|
+
on:
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
# Matches publish.yml, so the packaging smoke test below produces the
|
|
14
|
+
# same wheel tag (macosx_14_0_arm64) the real release build would.
|
|
15
|
+
MACOSX_DEPLOYMENT_TARGET: "14.0"
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
test:
|
|
19
|
+
runs-on: macos-14
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
submodules: recursive
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
|
|
29
|
+
- name: Install mtlpy (editable, with dev extras)
|
|
30
|
+
run: pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Run test suite
|
|
33
|
+
run: pytest tests/ -v
|
|
34
|
+
|
|
35
|
+
- name: Build sdist + wheel (packaging smoke test)
|
|
36
|
+
run: |
|
|
37
|
+
pip install build
|
|
38
|
+
python -m build
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Runs on every push to main (i.e. every merged PR). If pyproject.toml's
|
|
4
|
+
# [project].version was bumped and isn't already tagged: tags it, creates a
|
|
5
|
+
# GitHub Release, builds the sdist + a wheel per supported Python version,
|
|
6
|
+
# and publishes all of it to PyPI -- all in one workflow run.
|
|
7
|
+
#
|
|
8
|
+
# Deliberately one workflow, not tag-then-trigger-a-second-workflow: GitHub
|
|
9
|
+
# doesn't let events caused by the default GITHUB_TOKEN (e.g. a release
|
|
10
|
+
# created by an Actions step) trigger other workflows, as an anti-recursion
|
|
11
|
+
# safeguard. That meant a separate "publish on release" workflow silently
|
|
12
|
+
# never ran. Using `needs:` to sequence jobs within a single workflow run
|
|
13
|
+
# has no such restriction, so this version doesn't need a PAT either.
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [main]
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
# Wheels only need building on the OLDEST macOS we support (14): the
|
|
23
|
+
# resulting binary installs and runs on every later macOS too, since
|
|
24
|
+
# macOS is backward- (not forward-) compatible. Building on the floor
|
|
25
|
+
# OS also means its older SDK can't let us accidentally call a
|
|
26
|
+
# macOS-15/26-only Metal API while still claiming macOS 14 support --
|
|
27
|
+
# see CMakeLists.txt for the matching CMAKE_OSX_DEPLOYMENT_TARGET.
|
|
28
|
+
# scikit-build-core reads this env var (not the CMake cache variable)
|
|
29
|
+
# to decide the wheel's platform tag.
|
|
30
|
+
MACOSX_DEPLOYMENT_TARGET: "14.0"
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
tag-release:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
outputs:
|
|
36
|
+
tag: ${{ steps.version.outputs.tag }}
|
|
37
|
+
released: ${{ steps.check_tag.outputs.exists == 'false' }}
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
with:
|
|
41
|
+
fetch-depth: 0 # full history + tags, needed to check for an existing tag
|
|
42
|
+
|
|
43
|
+
- uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: "3.12" # need 3.11+ for stdlib tomllib below
|
|
46
|
+
|
|
47
|
+
- name: Read version from pyproject.toml
|
|
48
|
+
id: version
|
|
49
|
+
run: |
|
|
50
|
+
version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
51
|
+
echo "tag=v$version" >> "$GITHUB_OUTPUT"
|
|
52
|
+
|
|
53
|
+
- name: Check whether this version is already tagged
|
|
54
|
+
id: check_tag
|
|
55
|
+
run: |
|
|
56
|
+
if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
|
|
57
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
58
|
+
else
|
|
59
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
- name: Create tag and GitHub Release
|
|
63
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
64
|
+
env:
|
|
65
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
66
|
+
run: |
|
|
67
|
+
tag="${{ steps.version.outputs.tag }}"
|
|
68
|
+
git tag "$tag"
|
|
69
|
+
git push origin "$tag"
|
|
70
|
+
gh release create "$tag" --title "$tag" --generate-notes
|
|
71
|
+
|
|
72
|
+
build-sdist:
|
|
73
|
+
needs: tag-release
|
|
74
|
+
if: needs.tag-release.outputs.released == 'true'
|
|
75
|
+
runs-on: macos-14
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
submodules: recursive
|
|
80
|
+
ref: ${{ needs.tag-release.outputs.tag }}
|
|
81
|
+
|
|
82
|
+
- uses: actions/setup-python@v5
|
|
83
|
+
with:
|
|
84
|
+
python-version: "3.12"
|
|
85
|
+
|
|
86
|
+
- name: Build sdist
|
|
87
|
+
run: |
|
|
88
|
+
pip install build
|
|
89
|
+
python -m build --sdist
|
|
90
|
+
|
|
91
|
+
- uses: actions/upload-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: dist-sdist
|
|
94
|
+
path: dist/*.tar.gz
|
|
95
|
+
|
|
96
|
+
build-wheels:
|
|
97
|
+
needs: tag-release
|
|
98
|
+
if: needs.tag-release.outputs.released == 'true'
|
|
99
|
+
runs-on: macos-14
|
|
100
|
+
strategy:
|
|
101
|
+
matrix:
|
|
102
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v4
|
|
105
|
+
with:
|
|
106
|
+
submodules: recursive
|
|
107
|
+
ref: ${{ needs.tag-release.outputs.tag }}
|
|
108
|
+
|
|
109
|
+
- uses: actions/setup-python@v5
|
|
110
|
+
with:
|
|
111
|
+
python-version: ${{ matrix.python-version }}
|
|
112
|
+
|
|
113
|
+
- name: Build wheel
|
|
114
|
+
run: |
|
|
115
|
+
pip install build
|
|
116
|
+
python -m build --wheel
|
|
117
|
+
|
|
118
|
+
- uses: actions/upload-artifact@v4
|
|
119
|
+
with:
|
|
120
|
+
name: dist-wheel-cp${{ matrix.python-version }}
|
|
121
|
+
path: dist/*.whl
|
|
122
|
+
|
|
123
|
+
publish:
|
|
124
|
+
needs: [build-sdist, build-wheels]
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
environment: pypi # optional extra gate: add required reviewers to this
|
|
127
|
+
# GitHub Environment (Settings -> Environments) if you
|
|
128
|
+
# want a manual approval click before every publish.
|
|
129
|
+
permissions:
|
|
130
|
+
id-token: write # required for PyPI Trusted Publishing
|
|
131
|
+
steps:
|
|
132
|
+
- uses: actions/download-artifact@v4
|
|
133
|
+
with:
|
|
134
|
+
path: dist
|
|
135
|
+
pattern: "dist-*"
|
|
136
|
+
merge-multiple: true
|
|
137
|
+
|
|
138
|
+
- name: Publish to PyPI
|
|
139
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
140
|
+
with:
|
|
141
|
+
packages-dir: dist/
|
mtlpy-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
*.egg
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# C++ build artifacts
|
|
13
|
+
*.o
|
|
14
|
+
*.so
|
|
15
|
+
*.dylib
|
|
16
|
+
CMakeCache.txt
|
|
17
|
+
CMakeFiles/
|
|
18
|
+
cmake_install.cmake
|
|
19
|
+
Makefile
|
|
20
|
+
_skbuild/
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
*.swp
|
|
26
|
+
|
|
27
|
+
# Test artifacts
|
|
28
|
+
.pytest_cache/
|
|
29
|
+
.coverage
|
|
30
|
+
|
|
31
|
+
# Benchmark output (local baselines, not source)
|
|
32
|
+
benchmarks/results/
|
mtlpy-0.1.0/.gitmodules
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
|
|
3
|
+
set(CMAKE_OSX_DEPLOYMENT_TARGET "14.0" CACHE STRING "Minimum macOS version")
|
|
4
|
+
|
|
5
|
+
project(mtlpy LANGUAGES CXX OBJCXX)
|
|
6
|
+
|
|
7
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
8
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
9
|
+
set(CMAKE_OBJCXX_STANDARD 17)
|
|
10
|
+
set(CMAKE_OBJCXX_STANDARD_REQUIRED ON)
|
|
11
|
+
|
|
12
|
+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
|
13
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
14
|
+
|
|
15
|
+
set(METAL_CPP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/metal-cpp)
|
|
16
|
+
|
|
17
|
+
pybind11_add_module(_mtlpy
|
|
18
|
+
csrc/metal_impl.mm
|
|
19
|
+
csrc/device.cpp
|
|
20
|
+
csrc/pipeline_cache.cpp
|
|
21
|
+
csrc/buffer.cpp
|
|
22
|
+
csrc/pipeline.cpp
|
|
23
|
+
csrc/bindings.cpp
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
target_include_directories(_mtlpy PRIVATE
|
|
27
|
+
${METAL_CPP_DIR}
|
|
28
|
+
${CMAKE_CURRENT_SOURCE_DIR}/csrc
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
target_link_libraries(_mtlpy PRIVATE
|
|
32
|
+
"-framework Metal"
|
|
33
|
+
"-framework Foundation"
|
|
34
|
+
"-framework QuartzCore"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
install(TARGETS _mtlpy LIBRARY DESTINATION mtlpy)
|
mtlpy-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peyton Howe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mtlpy-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mtlpy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bindings for Apple Metal GPU compute
|
|
5
|
+
Keywords: metal,gpu,compute,apple,macos,mtl
|
|
6
|
+
Author: Peyton Howe
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: C++
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Project-URL: Homepage, https://github.com/peyton-howe/mtlpy
|
|
22
|
+
Project-URL: Repository, https://github.com/peyton-howe/mtlpy
|
|
23
|
+
Project-URL: Issues, https://github.com/peyton-howe/mtlpy/issues
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: numpy>=1.20
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# mtlpy
|
|
31
|
+
|
|
32
|
+
Python bindings for GPU compute on Apple Metal, built on [pybind11](https://github.com/pybind/pybind11)
|
|
33
|
+
and Apple's [metal-cpp](https://developer.apple.com/metal/cpp/). Write a Metal
|
|
34
|
+
compute kernel as a string, dispatch it over NumPy arrays, get the result back
|
|
35
|
+
as a NumPy array — no separate build step, no manual buffer plumbing.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import numpy as np
|
|
39
|
+
import mtlpy
|
|
40
|
+
|
|
41
|
+
device = mtlpy.Device()
|
|
42
|
+
a = device.buffer(np.array([1.0, 2.0, 3.0], dtype=np.float32))
|
|
43
|
+
b = device.buffer(np.array([10.0, 20.0, 30.0], dtype=np.float32))
|
|
44
|
+
|
|
45
|
+
print((a + b).contents) # [11. 22. 33.]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Why this exists
|
|
49
|
+
|
|
50
|
+
mtlpy is a from-scratch rewrite of the`metalgpu`
|
|
51
|
+
project. Rather than build on top of that codebase's ctypes-based bindings
|
|
52
|
+
and global singleton state, mtlpy starts over with a few deliberate
|
|
53
|
+
improvements:
|
|
54
|
+
|
|
55
|
+
- **pybind11 instead of ctypes** — real type safety across the Python/C++
|
|
56
|
+
boundary, and Metal errors propagate as Python exceptions instead of
|
|
57
|
+
silent failures.
|
|
58
|
+
- **No global singleton state** — each `Device` owns its own command queue
|
|
59
|
+
and pipeline cache; nothing is saved/restored behind your back.
|
|
60
|
+
- **Pipeline compile caching** — a compute pipeline is compiled once per
|
|
61
|
+
(shader source, function name) and reused, both within a process and
|
|
62
|
+
(via an on-disk Metal binary archive) across process launches.
|
|
63
|
+
- **Async dispatch** — `Pipeline.run(..., wait=False)` lets you batch work
|
|
64
|
+
without stalling on every call.
|
|
65
|
+
|
|
66
|
+
## Status
|
|
67
|
+
|
|
68
|
+
Alpha, but built, tested, and benchmarked on real Apple Silicon hardware —
|
|
69
|
+
see [Building from source](#building-from-source) and the test suite for
|
|
70
|
+
current coverage.
|
|
71
|
+
|
|
72
|
+
## Architecture
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
metal-cpp/ Apple's C++ Metal headers (git submodule)
|
|
76
|
+
csrc/ C++ extension (pybind11 + metal-cpp)
|
|
77
|
+
device.{h,cpp} MTL::Device + MTL::CommandQueue owner
|
|
78
|
+
buffer.{h,cpp} MTL::Buffer wrapper (shared-storage, CPU/GPU unified memory)
|
|
79
|
+
pipeline.{h,cpp} Dispatches a compiled MTL::ComputePipelineState
|
|
80
|
+
pipeline_cache.{h,cpp} Compiles-once cache, keyed on (source, function name),
|
|
81
|
+
backed by an on-disk MTL::BinaryArchive
|
|
82
|
+
metal_impl.mm Single Obj-C++ translation unit providing the
|
|
83
|
+
NS::/CA::/MTL:: private implementations
|
|
84
|
+
bindings.cpp pybind11 module definition (`_mtlpy`)
|
|
85
|
+
src/mtlpy/ Python package (src layout, for PyPI)
|
|
86
|
+
device.py Device: buffer/empty/compile, list_devices(), wraps _mtlpy.Device
|
|
87
|
+
buffer.py Buffer: NumPy-backed contents, arithmetic/comparison/in-place operators
|
|
88
|
+
pipeline.py Pipeline: thin wrapper over _mtlpy.Pipeline
|
|
89
|
+
operators.py sqrt/cos/sin/tan/exp/log, sum/max/min/mean reductions
|
|
90
|
+
shader.py Generates Metal Shading Language source per dtype
|
|
91
|
+
utils.py NumPy dtype <-> Metal type name mapping
|
|
92
|
+
tests/ pytest suite
|
|
93
|
+
benchmarks/ Standalone performance baseline scripts
|
|
94
|
+
examples/ Runnable usage examples
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Each `Device` in Python owns exactly one `MTL::Device`, one `MTL::CommandQueue`,
|
|
98
|
+
and one `PipelineCache`. Buffers use `MTL::ResourceStorageModeShared`, so on
|
|
99
|
+
Apple Silicon's unified memory there's no copy between CPU and GPU views of
|
|
100
|
+
the same allocation — `Buffer.contents` is a NumPy array backed directly by
|
|
101
|
+
GPU-visible memory (accessing `.contents` is a true zero-copy view; writing
|
|
102
|
+
new data into it via `buf.contents[:] = arr` is still a real memcpy from
|
|
103
|
+
`arr`'s own memory, same as it would be for any destination).
|
|
104
|
+
|
|
105
|
+
## Features
|
|
106
|
+
|
|
107
|
+
- **Elementwise operators**: `+`, `-`, `*`, `/`, unary `-`, and in-place
|
|
108
|
+
`+=`/`-=`/`*=`/`/=` (which dispatch in-place, into the same `Buffer`, with
|
|
109
|
+
no extra allocation) on `Buffer` — each also works with a NumPy/Python
|
|
110
|
+
scalar on either side (`buf + 5.0`, `5.0 - buf`), not just `Buffer op
|
|
111
|
+
Buffer`. Plus `sqrt`, `cos`, `sin`, `tan`, `exp`, `log`, and `astype` for
|
|
112
|
+
dtype conversion.
|
|
113
|
+
- **Comparisons**: `==`, `!=`, `<`, `<=`, `>`, `>=` (against another `Buffer`
|
|
114
|
+
or a scalar) return a `bool` `Buffer`, matching NumPy's `ndarray`
|
|
115
|
+
convention — which also makes `Buffer` unhashable, same tradeoff NumPy
|
|
116
|
+
makes.
|
|
117
|
+
- **Reductions**: `operators.sum`/`max`/`min`/`mean` — an O(log n) multi-pass
|
|
118
|
+
tree reduction returning a plain Python scalar.
|
|
119
|
+
- **Custom kernels**: compile and dispatch arbitrary Metal Shading Language
|
|
120
|
+
source directly (see [Custom kernels](#custom-kernels) below).
|
|
121
|
+
`Pipeline.run` validates the buffer count against the kernel's own
|
|
122
|
+
argument reflection, so passing too few buffers raises a clear Python
|
|
123
|
+
exception instead of leaving a Metal buffer argument unbound (undefined
|
|
124
|
+
behavior).
|
|
125
|
+
- **Dtype support**: `float32`, `float16`, `int32`, `uint32`,
|
|
126
|
+
`int16`, `uint16`, `int64`, `uint64`, `bool` — mapped to their Metal
|
|
127
|
+
equivalents (`float`, `half`, `int`, `uint`, `short`, `ushort`,
|
|
128
|
+
`long`, `ulong`, `bool`) in `src/mtlpy/utils.py`. `float64` has no Metal
|
|
129
|
+
equivalent (no Apple GPU supports double precision), so it's silently
|
|
130
|
+
downcast to `float32` at buffer creation. Note that `Buffer / Buffer` uses
|
|
131
|
+
Metal's native `/` for the shared dtype (truncating for integers), not
|
|
132
|
+
NumPy's always-promote-to-float64 semantics.
|
|
133
|
+
- **Pipeline caching**: identical (source, function name) pairs are compiled
|
|
134
|
+
once per process and reused; a binary archive on disk
|
|
135
|
+
(`~/Library/Caches/mtlpy/pipelines.metallib`) carries compiled pipelines
|
|
136
|
+
across process launches too. `Device.flush_cache()` (or using `Device` as
|
|
137
|
+
a context manager: `with mtlpy.Device() as d:`) serializes it on demand,
|
|
138
|
+
rather than only when the `Device` is garbage collected.
|
|
139
|
+
- **Async dispatch**: `wait=False` commits work without blocking; Metal
|
|
140
|
+
retires command buffers on a queue in commit order, so a later `wait=True`
|
|
141
|
+
dispatch that reads the result is enough to synchronize (see
|
|
142
|
+
`examples/async_dispatch.py`). `Pipeline.run` releases the GIL for the
|
|
143
|
+
whole call, so other Python threads keep running during the GPU wait
|
|
144
|
+
instead of being blocked for its full duration.
|
|
145
|
+
- **Multi-GPU support**: `mtlpy.list_devices()` lists every Metal-capable GPU
|
|
146
|
+
on the machine; `mtlpy.Device(index=...)` selects one (the default targets
|
|
147
|
+
the system default GPU).
|
|
148
|
+
- **Errors as exceptions**: shader compile failures, missing kernel
|
|
149
|
+
functions, mismatched buffer counts, mismatched-`Device` operands, and GPU
|
|
150
|
+
execution errors all raise Python exceptions with a clear message, instead
|
|
151
|
+
of failing silently or invoking undefined behavior.
|
|
152
|
+
|
|
153
|
+
## Building from source
|
|
154
|
+
|
|
155
|
+
Requires macOS with Metal support, Xcode (for the Metal/Objective-C++
|
|
156
|
+
toolchain), CMake, and Python 3.9+.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git clone --recursive git@github.com:peyton-howe/mtlpy.git
|
|
160
|
+
cd mtlpy
|
|
161
|
+
pip install -e ".[dev]"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If you already cloned without `--recursive`:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git submodule update --init
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Quick start
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
import numpy as np
|
|
174
|
+
import mtlpy
|
|
175
|
+
|
|
176
|
+
device = mtlpy.Device()
|
|
177
|
+
|
|
178
|
+
a = device.buffer(np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32))
|
|
179
|
+
b = device.buffer(np.array([10.0, 20.0, 30.0, 40.0], dtype=np.float32))
|
|
180
|
+
|
|
181
|
+
c = a + b
|
|
182
|
+
print(c.contents) # numpy.ndarray([11. 22. 33. 44.])
|
|
183
|
+
|
|
184
|
+
d = mtlpy.operators.sqrt(a)
|
|
185
|
+
print(d.contents)
|
|
186
|
+
|
|
187
|
+
e = a.astype(np.int32)
|
|
188
|
+
print(e.dtype, e.contents)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Custom kernels
|
|
192
|
+
|
|
193
|
+
`Device.compile(source, function_name)` compiles arbitrary Metal Shading
|
|
194
|
+
Language and returns a `Pipeline` you can dispatch directly:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
source = """
|
|
198
|
+
#include <metal_stdlib>
|
|
199
|
+
using namespace metal;
|
|
200
|
+
kernel void square(
|
|
201
|
+
device const float *a [[buffer(0)]],
|
|
202
|
+
device float *b [[buffer(1)]],
|
|
203
|
+
uint id [[thread_position_in_grid]])
|
|
204
|
+
{
|
|
205
|
+
b[id] = a[id] * a[id];
|
|
206
|
+
}
|
|
207
|
+
"""
|
|
208
|
+
pipeline = device.compile(source, "square")
|
|
209
|
+
|
|
210
|
+
a = device.buffer(np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32))
|
|
211
|
+
b = device.empty(4, np.float32)
|
|
212
|
+
pipeline.run([a, b], grid=4)
|
|
213
|
+
|
|
214
|
+
print(b.contents) # [1. 4. 9. 16.]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`grid` may be an int (1D dispatch) or a 3-tuple/list for 2D/3D dispatch.
|
|
218
|
+
Threadgroup sizing is computed automatically from the pipeline's
|
|
219
|
+
`thread_execution_width` and `max_threads_per_threadgroup`.
|
|
220
|
+
|
|
221
|
+
## Reusing buffers in a hot loop
|
|
222
|
+
|
|
223
|
+
`Buffer.contents` is a live NumPy view over the same underlying Metal
|
|
224
|
+
allocation, not a copy — writing `buf.contents[:] = ...` updates GPU-visible
|
|
225
|
+
memory in place, and reading it back after a `wait=True` dispatch needs no
|
|
226
|
+
reallocation either. For a kernel dispatched repeatedly (e.g. in a `while`
|
|
227
|
+
loop), compile the pipeline and allocate buffers once, then just write/read
|
|
228
|
+
`.contents` each iteration:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
pipeline = device.compile(source, "square")
|
|
232
|
+
|
|
233
|
+
a = device.buffer(np.zeros(4, dtype=np.float32)) # allocated once
|
|
234
|
+
out = device.empty(4, np.float32) # allocated once
|
|
235
|
+
|
|
236
|
+
while running:
|
|
237
|
+
a.contents[:] = get_next_input() # in-place write, no realloc
|
|
238
|
+
pipeline.run([a, out], grid=4) # wait=True by default
|
|
239
|
+
consume(out.contents) # in-place read, no realloc
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The out-of-place convenience operators (`a + b`, `operators.sqrt(a)`,
|
|
243
|
+
`astype`, etc.) don't follow this pattern — each call allocates a fresh
|
|
244
|
+
output `Buffer` internally, which is fine for one-off use but wasteful in a
|
|
245
|
+
tight loop. The in-place operators (`a += b`, `a *= 2.0`, ...) do reuse `a`'s
|
|
246
|
+
own buffer with no extra allocation, if that fits your loop. See
|
|
247
|
+
`examples/reuse_buffers.py`.
|
|
248
|
+
|
|
249
|
+
## Testing
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
pytest tests/
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- `test_basic.py` / `test_operators.py` — correctness for every operator
|
|
256
|
+
(arithmetic, scalar broadcasting, comparisons, in-place, reductions),
|
|
257
|
+
dtype, and `astype` conversion, plus error handling for mismatched buffer
|
|
258
|
+
sizes/dtypes/devices and wrong kernel argument counts.
|
|
259
|
+
- `test_async.py` — `wait=False` dispatch ordering.
|
|
260
|
+
- `test_buffer_reuse.py` — in-place `.contents` writes and repeated dispatch
|
|
261
|
+
against the same buffers, without reallocation.
|
|
262
|
+
- `test_stability.py` — repeated-dispatch and object-lifetime stress tests
|
|
263
|
+
(regression coverage for the Metal object-ownership rules in `csrc/`),
|
|
264
|
+
plus multi-threaded dispatch/compilation tests (`Pipeline.run` releases
|
|
265
|
+
the GIL, so this exercises genuinely concurrent Metal calls).
|
|
266
|
+
- `test_pipeline_persistence.py` — spawns separate processes to verify the
|
|
267
|
+
on-disk pipeline binary archive is actually written and read back, and
|
|
268
|
+
that `Device.flush_cache()` writes it on demand.
|
|
269
|
+
|
|
270
|
+
## Benchmarking
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
python benchmarks/bench.py
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Measures first-dispatch (compile-included) and steady-state warm-dispatch
|
|
277
|
+
latency/throughput for every operator across a range of buffer sizes, with
|
|
278
|
+
NumPy CPU timings alongside for context. Each run is saved as JSON
|
|
279
|
+
(timestamped, tagged with the git commit) under `benchmarks/results/` so you
|
|
280
|
+
can baseline future changes:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
python benchmarks/bench.py --baseline benchmarks/results/<earlier-run>.json
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`benchmarks/demosaic_bench.py` is a separate, more involved benchmark
|
|
287
|
+
comparing the edge-aware Bayer demosaicing kernel
|
|
288
|
+
(`benchmarks/bayer2rgb_ea_kernel.txt`) against OpenCV's own
|
|
289
|
+
`COLOR_Bayer*2BGR_EA` (requires `pip install -e ".[bench]"`), covering both
|
|
290
|
+
single-shot dispatch latency and realistic streaming throughput (a rotating
|
|
291
|
+
buffer pool pipelining dispatches instead of waiting on every frame).
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
MIT
|